First major part of the REST query resdesign.
This commit is contained in:
parent
bfd8007dfa
commit
4e9d60b446
|
@ -0,0 +1,59 @@
|
||||||
|
<script>
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
let dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
export let type = "info"
|
||||||
|
export let icon = "Info"
|
||||||
|
export let size = "S"
|
||||||
|
export let extraButtonText
|
||||||
|
export let extraButtonAction
|
||||||
|
|
||||||
|
let show = true
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
show = false
|
||||||
|
dispatch("change")
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if show}
|
||||||
|
<div class="spectrum-Toast spectrum-Toast--{type}">
|
||||||
|
<svg
|
||||||
|
class="spectrum-Icon spectrum-Icon--size{size} spectrum-Toast-typeIcon"
|
||||||
|
focusable="false"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||||
|
</svg>
|
||||||
|
<div class="spectrum-Toast-body">
|
||||||
|
<div class="spectrum-Toast-content">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
{#if extraButtonText && extraButtonAction}
|
||||||
|
<button
|
||||||
|
class="spectrum-Button spectrum-Button--sizeM spectrum-Button--overBackground spectrum-Button--quiet"
|
||||||
|
on:click={extraButtonAction}
|
||||||
|
>
|
||||||
|
<span class="spectrum-Button-label">{extraButtonText}</span>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="spectrum-Toast-buttons">
|
||||||
|
<button
|
||||||
|
class="spectrum-ClearButton spectrum-ClearButton--overBackground spectrum-ClearButton--size{size}"
|
||||||
|
on:click={clear}
|
||||||
|
>
|
||||||
|
<div class="spectrum-ClearButton-fill">
|
||||||
|
<svg
|
||||||
|
class="spectrum-ClearButton-icon spectrum-Icon spectrum-UIIcon-Cross100"
|
||||||
|
focusable="false"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<use xlink:href="#spectrum-css-icon-Cross100" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
|
@ -5,7 +5,7 @@
|
||||||
export let icon = ""
|
export let icon = ""
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const selected = getContext("tab")
|
let selected = getContext("tab")
|
||||||
let tab
|
let tab
|
||||||
let tabInfo
|
let tabInfo
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@
|
||||||
// We just need to get this off the main thread to fix this, by using
|
// We just need to get this off the main thread to fix this, by using
|
||||||
// a 0ms timeout.
|
// a 0ms timeout.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
tabInfo = tab.getBoundingClientRect()
|
tabInfo = tab?.getBoundingClientRect()
|
||||||
if ($selected.title === title) {
|
if (tabInfo && $selected.title === title) {
|
||||||
$selected.info = tabInfo
|
$selected.info = tabInfo
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0)
|
||||||
|
|
|
@ -59,6 +59,7 @@ export { default as Badge } from "./Badge/Badge.svelte"
|
||||||
export { default as StatusLight } from "./StatusLight/StatusLight.svelte"
|
export { default as StatusLight } from "./StatusLight/StatusLight.svelte"
|
||||||
export { default as ColorPicker } from "./ColorPicker/ColorPicker.svelte"
|
export { default as ColorPicker } from "./ColorPicker/ColorPicker.svelte"
|
||||||
export { default as InlineAlert } from "./InlineAlert/InlineAlert.svelte"
|
export { default as InlineAlert } from "./InlineAlert/InlineAlert.svelte"
|
||||||
|
export { default as Banner } from "./Banner/Banner.svelte"
|
||||||
|
|
||||||
// Typography
|
// Typography
|
||||||
export { default as Body } from "./Typography/Body.svelte"
|
export { default as Body } from "./Typography/Body.svelte"
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script>
|
||||||
|
import { Heading, Icon, Input, Label, Body } from "@budibase/bbui"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
let dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
export let defaultValue = ""
|
||||||
|
export let value
|
||||||
|
export let type = "label"
|
||||||
|
export let size = "M"
|
||||||
|
|
||||||
|
let editing = false
|
||||||
|
|
||||||
|
function setEditing(state) {
|
||||||
|
editing = state
|
||||||
|
if (editing) {
|
||||||
|
dispatch("change")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="parent">
|
||||||
|
{#if !editing}
|
||||||
|
{#if type === "heading"}
|
||||||
|
<Heading {size}>{value || defaultValue}</Heading>
|
||||||
|
{:else if type === "body"}
|
||||||
|
<Body {size}>{value || defaultValue}</Body>
|
||||||
|
{:else}
|
||||||
|
<Label {size}>{value || defaultValue}</Label>
|
||||||
|
{/if}
|
||||||
|
<div class="hide">
|
||||||
|
<Icon name="Edit" hoverable size="S" on:click={() => setEditing(true)} />
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="input">
|
||||||
|
<Input placeholder={defaultValue} bind:value on:change />
|
||||||
|
</div>
|
||||||
|
<Icon
|
||||||
|
name="SaveFloppy"
|
||||||
|
hoverable
|
||||||
|
size="S"
|
||||||
|
on:click={() => setEditing(false)}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.parent {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.parent:hover .hide {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -19,6 +19,7 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
export function addEntry() {
|
export function addEntry() {
|
||||||
|
console.log(fields)
|
||||||
fields = [...fields, {}]
|
fields = [...fields, {}]
|
||||||
changed()
|
changed()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { params } from "@roxi/routify"
|
import { params } from "@roxi/routify"
|
||||||
import { queries } from "stores/backend"
|
import { queries, datasources } from "stores/backend"
|
||||||
|
import { IntegrationTypes } from "constants"
|
||||||
|
import { goto } from "@roxi/routify"
|
||||||
|
|
||||||
if ($params.query) {
|
if ($params.query) {
|
||||||
const query = $queries.list.find(q => q._id === $params.query)
|
const query = $queries.list.find(q => q._id === $params.query)
|
||||||
|
@ -8,6 +10,12 @@
|
||||||
queries.select(query)
|
queries.select(query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const datasource = $datasources.list.find(
|
||||||
|
ds => ds._id === $datasources.selected
|
||||||
|
)
|
||||||
|
if (datasource.source === IntegrationTypes.REST) {
|
||||||
|
$goto("../rest")
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<slot />
|
|
@ -0,0 +1,173 @@
|
||||||
|
<script>
|
||||||
|
import { params } from "@roxi/routify"
|
||||||
|
import { datasources, integrations, queries } from "stores/backend"
|
||||||
|
import {
|
||||||
|
Layout,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
Tabs,
|
||||||
|
Tab,
|
||||||
|
Banner,
|
||||||
|
Divider,
|
||||||
|
Button,
|
||||||
|
Heading,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||||
|
import EditableLabel from "components/common/inputs/EditableLabel.svelte"
|
||||||
|
import CodeMirrorEditor from "components/common/CodeMirrorEditor.svelte"
|
||||||
|
import { capitalise } from "helpers"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
let query
|
||||||
|
let breakQs = {}
|
||||||
|
|
||||||
|
$: datasource = $datasources.list.find(ds => ds._id === query?.datasourceId)
|
||||||
|
$: datasourceType = datasource?.source
|
||||||
|
$: integrationInfo = $integrations[datasourceType]
|
||||||
|
$: queryConfig = integrationInfo?.query
|
||||||
|
|
||||||
|
function getSelectedQuery() {
|
||||||
|
return (
|
||||||
|
$queries.list.find(q => q._id === $queries.selected) || {
|
||||||
|
datasourceId: $params.selectedDatasource,
|
||||||
|
parameters: [],
|
||||||
|
fields: {},
|
||||||
|
queryVerb: "read",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function breakQueryString(qs) {
|
||||||
|
if (!qs) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
const params = qs.split("&")
|
||||||
|
let paramObj = {}
|
||||||
|
for (let param of params) {
|
||||||
|
const [key, value] = param.split("=")
|
||||||
|
paramObj[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// function buildQueryString(obj) {
|
||||||
|
// let str = ""
|
||||||
|
// for (let [key, value] of Object.entries(obj)) {
|
||||||
|
// if (str !== "") {
|
||||||
|
// str += "&"
|
||||||
|
// }
|
||||||
|
// str += `${key}=${value}`
|
||||||
|
// }
|
||||||
|
// return str
|
||||||
|
// }
|
||||||
|
|
||||||
|
function checkQueryName(queryToCheck, url = null) {
|
||||||
|
if (queryToCheck && (!queryToCheck.name || queryToCheck.flags.urlName)) {
|
||||||
|
queryToCheck.flags.urlName = true
|
||||||
|
queryToCheck.name = url || queryToCheck.fields.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function learnMoreBanner() {}
|
||||||
|
|
||||||
|
function saveQuery() {}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
query = getSelectedQuery()
|
||||||
|
breakQs = breakQueryString(query?.fields.queryString)
|
||||||
|
if (query && !query.transformer) {
|
||||||
|
query.transformer = "return data"
|
||||||
|
}
|
||||||
|
if (query && !query.flags) {
|
||||||
|
query.flags = {
|
||||||
|
urlName: false,
|
||||||
|
bannerCleared: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if query}
|
||||||
|
<div class="inner">
|
||||||
|
<div class="top">
|
||||||
|
<Layout gap="S">
|
||||||
|
<EditableLabel
|
||||||
|
type="heading"
|
||||||
|
bind:value={query.name}
|
||||||
|
defaultValue="Untitled"
|
||||||
|
on:change={() => (query.flags.urlName = false)}
|
||||||
|
/>
|
||||||
|
<div class="url-block">
|
||||||
|
<div class="verb">
|
||||||
|
<Select
|
||||||
|
bind:value={query.queryVerb}
|
||||||
|
on:change={() => {}}
|
||||||
|
options={Object.keys(queryConfig)}
|
||||||
|
getOptionLabel={verb =>
|
||||||
|
queryConfig[verb]?.displayName || capitalise(verb)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="url">
|
||||||
|
<Input
|
||||||
|
bind:value={query.fields.path}
|
||||||
|
on:change={({ detail }) => checkQueryName(query, detail)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button cta disabled={!query.fields.path} on:click={saveQuery}
|
||||||
|
>Send</Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<Tabs selected="Params">
|
||||||
|
<Tab title="Params">
|
||||||
|
<KeyValueBuilder bind:object={breakQs} name="param" />
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Headers" />
|
||||||
|
<Tab title="Body" />
|
||||||
|
<Tab title="Transformer">
|
||||||
|
<Layout noPadding>
|
||||||
|
{#if !query.flags.bannerCleared}
|
||||||
|
<Banner
|
||||||
|
extraButtonText="Learn more"
|
||||||
|
extraButtonAction={learnMoreBanner}
|
||||||
|
on:change={() => (query.flags.bannerCleared = true)}
|
||||||
|
>
|
||||||
|
Add a JavaScript function to transform the query result.
|
||||||
|
</Banner>
|
||||||
|
{/if}
|
||||||
|
<CodeMirrorEditor
|
||||||
|
height={200}
|
||||||
|
value={query.transformer}
|
||||||
|
resize="vertical"
|
||||||
|
on:change={e => (query.transformer = e.detail)}
|
||||||
|
/>
|
||||||
|
</Layout>
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
|
<Layout paddingY="L">
|
||||||
|
<Divider size="S" />
|
||||||
|
<Heading size="M">Response</Heading>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.inner {
|
||||||
|
width: 840px;
|
||||||
|
margin: 0 auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.url-block {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
}
|
||||||
|
.verb {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.url {
|
||||||
|
flex: 4;
|
||||||
|
}
|
||||||
|
.top {
|
||||||
|
min-height: 50%;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue