Merge pull request #1124 from Budibase/url-context
Bindable URL parameters
This commit is contained in:
commit
85caa27855
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "0.7.7",
|
"version": "0.7.8",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "0.7.7",
|
"version": "0.7.8",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -64,9 +64,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.58.5",
|
"@budibase/bbui": "^1.58.5",
|
||||||
"@budibase/client": "^0.7.7",
|
"@budibase/client": "^0.7.8",
|
||||||
"@budibase/colorpicker": "1.0.1",
|
"@budibase/colorpicker": "1.0.1",
|
||||||
"@budibase/string-templates": "^0.7.7",
|
"@budibase/string-templates": "^0.7.8",
|
||||||
"@budibase/svelte-ag-grid": "^0.0.16",
|
"@budibase/svelte-ag-grid": "^0.0.16",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@svelteschool/svelte-forms": "0.7.0",
|
"@svelteschool/svelte-forms": "0.7.0",
|
||||||
|
|
|
@ -11,21 +11,24 @@ const CAPTURE_VAR_INSIDE_TEMPLATE = /{{([^}]+)}}/g
|
||||||
/**
|
/**
|
||||||
* Gets all bindable data context fields and instance fields.
|
* Gets all bindable data context fields and instance fields.
|
||||||
*/
|
*/
|
||||||
export const getBindableProperties = (rootComponent, componentId) => {
|
export const getBindableProperties = (asset, componentId) => {
|
||||||
return getContextBindings(rootComponent, componentId)
|
const contextBindings = getContextBindings(asset, componentId)
|
||||||
|
const userBindings = getUserBindings()
|
||||||
|
const urlBindings = getUrlBindings(asset, componentId)
|
||||||
|
return [...contextBindings, ...userBindings, ...urlBindings]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all data provider components above a component.
|
* Gets all data provider components above a component.
|
||||||
*/
|
*/
|
||||||
export const getDataProviderComponents = (rootComponent, componentId) => {
|
export const getDataProviderComponents = (asset, componentId) => {
|
||||||
if (!rootComponent || !componentId) {
|
if (!asset || !componentId) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the component tree leading up to this component, ignoring the component
|
// Get the component tree leading up to this component, ignoring the component
|
||||||
// itself
|
// itself
|
||||||
const path = findComponentPath(rootComponent, componentId)
|
const path = findComponentPath(asset.props, componentId)
|
||||||
path.pop()
|
path.pop()
|
||||||
|
|
||||||
// Filter by only data provider components
|
// Filter by only data provider components
|
||||||
|
@ -38,18 +41,14 @@ export const getDataProviderComponents = (rootComponent, componentId) => {
|
||||||
/**
|
/**
|
||||||
* Gets all data provider components above a component.
|
* Gets all data provider components above a component.
|
||||||
*/
|
*/
|
||||||
export const getActionProviderComponents = (
|
export const getActionProviderComponents = (asset, componentId, actionType) => {
|
||||||
rootComponent,
|
if (!asset || !componentId) {
|
||||||
componentId,
|
|
||||||
actionType
|
|
||||||
) => {
|
|
||||||
if (!rootComponent || !componentId) {
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the component tree leading up to this component, ignoring the component
|
// Get the component tree leading up to this component, ignoring the component
|
||||||
// itself
|
// itself
|
||||||
const path = findComponentPath(rootComponent, componentId)
|
const path = findComponentPath(asset.props, componentId)
|
||||||
path.pop()
|
path.pop()
|
||||||
|
|
||||||
// Filter by only data provider components
|
// Filter by only data provider components
|
||||||
|
@ -92,13 +91,12 @@ export const getDatasourceForProvider = component => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all bindable data contexts. These are fields of schemas of data contexts
|
* Gets all bindable data properties from component data contexts.
|
||||||
* provided by data provider components, such as lists or row detail components.
|
|
||||||
*/
|
*/
|
||||||
export const getContextBindings = (rootComponent, componentId) => {
|
const getContextBindings = (asset, componentId) => {
|
||||||
// Extract any components which provide data contexts
|
// Extract any components which provide data contexts
|
||||||
const dataProviders = getDataProviderComponents(rootComponent, componentId)
|
const dataProviders = getDataProviderComponents(asset, componentId)
|
||||||
let contextBindings = []
|
let bindings = []
|
||||||
|
|
||||||
// Create bindings for each data provider
|
// Create bindings for each data provider
|
||||||
dataProviders.forEach(component => {
|
dataProviders.forEach(component => {
|
||||||
|
@ -109,7 +107,7 @@ export const getContextBindings = (rootComponent, componentId) => {
|
||||||
// Forms are an edge case which do not need table schemas
|
// Forms are an edge case which do not need table schemas
|
||||||
if (isForm) {
|
if (isForm) {
|
||||||
schema = buildFormSchema(component)
|
schema = buildFormSchema(component)
|
||||||
tableName = "Schema"
|
tableName = "Fields"
|
||||||
} else {
|
} else {
|
||||||
if (!datasource) {
|
if (!datasource) {
|
||||||
return
|
return
|
||||||
|
@ -143,7 +141,7 @@ export const getContextBindings = (rootComponent, componentId) => {
|
||||||
runtimeBoundKey = `${key}_first`
|
runtimeBoundKey = `${key}_first`
|
||||||
}
|
}
|
||||||
|
|
||||||
contextBindings.push({
|
bindings.push({
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: `${makePropSafe(component._id)}.${makePropSafe(
|
runtimeBinding: `${makePropSafe(component._id)}.${makePropSafe(
|
||||||
runtimeBoundKey
|
runtimeBoundKey
|
||||||
|
@ -157,7 +155,14 @@ export const getContextBindings = (rootComponent, componentId) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add logged in user bindings
|
return bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all bindable properties from the logged in user.
|
||||||
|
*/
|
||||||
|
const getUserBindings = () => {
|
||||||
|
let bindings = []
|
||||||
const tables = get(backendUiStore).tables
|
const tables = get(backendUiStore).tables
|
||||||
const userTable = tables.find(table => table._id === TableNames.USERS)
|
const userTable = tables.find(table => table._id === TableNames.USERS)
|
||||||
const schema = {
|
const schema = {
|
||||||
|
@ -176,7 +181,7 @@ export const getContextBindings = (rootComponent, componentId) => {
|
||||||
runtimeBoundKey = `${key}_first`
|
runtimeBoundKey = `${key}_first`
|
||||||
}
|
}
|
||||||
|
|
||||||
contextBindings.push({
|
bindings.push({
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: `user.${runtimeBoundKey}`,
|
runtimeBinding: `user.${runtimeBoundKey}`,
|
||||||
readableBinding: `Current User.${key}`,
|
readableBinding: `Current User.${key}`,
|
||||||
|
@ -187,7 +192,26 @@ export const getContextBindings = (rootComponent, componentId) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return contextBindings
|
return bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all bindable properties from URL parameters.
|
||||||
|
*/
|
||||||
|
const getUrlBindings = asset => {
|
||||||
|
const url = asset?.routing?.route ?? ""
|
||||||
|
const split = url.split("/")
|
||||||
|
let params = []
|
||||||
|
split.forEach(part => {
|
||||||
|
if (part.startsWith(":") && part.length > 1) {
|
||||||
|
params.push(part.replace(/:/g, "").replace(/\?/g, ""))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return params.map(param => ({
|
||||||
|
type: "context",
|
||||||
|
runtimeBinding: `url.${param}`,
|
||||||
|
readableBinding: `URL.${param}`,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<script>
|
||||||
|
import { Icon, Input, Drawer, Body, Button } from "@budibase/bbui"
|
||||||
|
import {
|
||||||
|
readableToRuntimeBinding,
|
||||||
|
runtimeToReadableBinding,
|
||||||
|
} from "builderStore/dataBinding"
|
||||||
|
import BindingPanel from "components/design/PropertiesPanel/BindingPanel.svelte"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
export let value = ""
|
||||||
|
export let bindings = []
|
||||||
|
|
||||||
|
let bindingDrawer
|
||||||
|
let tempValue = value
|
||||||
|
|
||||||
|
$: readableValue = runtimeToReadableBinding(bindings, value)
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
onChange(tempValue)
|
||||||
|
bindingDrawer.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChange = value => {
|
||||||
|
dispatch("change", readableToRuntimeBinding(bindings, value))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<Input
|
||||||
|
thin
|
||||||
|
value={readableValue}
|
||||||
|
on:change={event => onChange(event.target.value)}
|
||||||
|
placeholder="/screen" />
|
||||||
|
<div class="icon" on:click={bindingDrawer.show}>
|
||||||
|
<Icon name="lightning" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Drawer bind:this={bindingDrawer} title="Bindings">
|
||||||
|
<div slot="description">
|
||||||
|
<Body extraSmall grey>
|
||||||
|
Add the objects on the left to enrich your text.
|
||||||
|
</Body>
|
||||||
|
</div>
|
||||||
|
<heading slot="buttons">
|
||||||
|
<Button thin blue on:click={handleClose}>Save</Button>
|
||||||
|
</heading>
|
||||||
|
<div slot="body">
|
||||||
|
<BindingPanel
|
||||||
|
value={readableValue}
|
||||||
|
close={handleClose}
|
||||||
|
on:update={event => (tempValue = event.detail)}
|
||||||
|
bindableProperties={bindings} />
|
||||||
|
</div>
|
||||||
|
</Drawer>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.control {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: var(--spacing-l);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
right: 2px;
|
||||||
|
top: 2px;
|
||||||
|
bottom: 2px;
|
||||||
|
position: absolute;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-left: 7px;
|
||||||
|
border-left: 1px solid var(--grey-4);
|
||||||
|
background-color: var(--grey-2);
|
||||||
|
border-top-right-radius: var(--border-radius-m);
|
||||||
|
border-bottom-right-radius: var(--border-radius-m);
|
||||||
|
color: var(--grey-7);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.icon:hover {
|
||||||
|
color: var(--ink);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
$: value && checkValid()
|
$: value && checkValid()
|
||||||
$: bindableProperties = getBindableProperties(
|
$: bindableProperties = getBindableProperties(
|
||||||
$currentAsset.props,
|
$currentAsset,
|
||||||
$store.selectedComponentId
|
$store.selectedComponentId
|
||||||
)
|
)
|
||||||
$: dispatch("update", value)
|
$: dispatch("update", value)
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
type: "query",
|
type: "query",
|
||||||
}))
|
}))
|
||||||
$: bindableProperties = getBindableProperties(
|
$: bindableProperties = getBindableProperties(
|
||||||
$currentAsset.props,
|
$currentAsset,
|
||||||
$store.selectedComponentId
|
$store.selectedComponentId
|
||||||
)
|
)
|
||||||
$: queryBindableProperties = bindableProperties.map(property => ({
|
$: queryBindableProperties = bindableProperties.map(property => ({
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
export let parameters
|
export let parameters
|
||||||
|
|
||||||
$: dataProviderComponents = getDataProviderComponents(
|
$: dataProviderComponents = getDataProviderComponents(
|
||||||
$currentAsset.props,
|
$currentAsset,
|
||||||
$store.selectedComponentId
|
$store.selectedComponentId
|
||||||
)
|
)
|
||||||
$: {
|
$: {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
ds => ds._id === parameters.datasourceId
|
ds => ds._id === parameters.datasourceId
|
||||||
)
|
)
|
||||||
$: bindableProperties = getBindableProperties(
|
$: bindableProperties = getBindableProperties(
|
||||||
$currentAsset.props,
|
$currentAsset,
|
||||||
$store.selectedComponentId
|
$store.selectedComponentId
|
||||||
).map(property => ({
|
).map(property => ({
|
||||||
...property,
|
...property,
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
<script>
|
<script>
|
||||||
import { DataList, Label } from "@budibase/bbui"
|
import { Label } from "@budibase/bbui"
|
||||||
import { allScreens } from "builderStore"
|
import { getBindableProperties } from "builderStore/dataBinding"
|
||||||
|
import { currentAsset, store } from "builderStore"
|
||||||
|
import DrawerBindableInput from "components/common/DrawerBindableInput.svelte"
|
||||||
|
|
||||||
export let parameters
|
export let parameters
|
||||||
|
|
||||||
|
let bindingDrawer
|
||||||
|
let tempValue = parameters.url
|
||||||
|
|
||||||
|
$: bindings = getBindableProperties($currentAsset, $store.selectedComponentId)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
<Label size="m" color="dark">Screen</Label>
|
<Label size="m" color="dark">Screen</Label>
|
||||||
<DataList secondary bind:value={parameters.url}>
|
<DrawerBindableInput
|
||||||
<option value="" />
|
value={parameters.url}
|
||||||
{#each $allScreens as screen}
|
on:change={value => (parameters.url = value.detail)}
|
||||||
<option value={screen.routing.route}>{screen.props._instanceName}</option>
|
{bindings} />
|
||||||
{/each}
|
|
||||||
</DataList>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
export let parameters
|
export let parameters
|
||||||
|
|
||||||
$: dataProviders = getDataProviderComponents(
|
$: dataProviders = getDataProviderComponents(
|
||||||
$currentAsset.props,
|
$currentAsset,
|
||||||
$store.selectedComponentId
|
$store.selectedComponentId
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
const emptyField = () => ({ name: "", value: "" })
|
const emptyField = () => ({ name: "", value: "" })
|
||||||
|
|
||||||
$: bindableProperties = getBindableProperties(
|
$: bindableProperties = getBindableProperties(
|
||||||
$currentAsset.props,
|
$currentAsset,
|
||||||
$store.selectedComponentId
|
$store.selectedComponentId
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
export let parameters
|
export let parameters
|
||||||
|
|
||||||
$: dataProviderComponents = getDataProviderComponents(
|
$: dataProviderComponents = getDataProviderComponents(
|
||||||
$currentAsset.props,
|
$currentAsset,
|
||||||
$store.selectedComponentId
|
$store.selectedComponentId
|
||||||
)
|
)
|
||||||
$: providerComponent = dataProviderComponents.find(
|
$: providerComponent = dataProviderComponents.find(
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
export let parameters
|
export let parameters
|
||||||
|
|
||||||
$: actionProviders = getActionProviderComponents(
|
$: actionProviders = getActionProviderComponents(
|
||||||
$currentAsset.props,
|
$currentAsset,
|
||||||
$store.selectedComponentId,
|
$store.selectedComponentId,
|
||||||
"ValidateForm"
|
"ValidateForm"
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
let valid
|
let valid
|
||||||
|
|
||||||
$: bindableProperties = getBindableProperties(
|
$: bindableProperties = getBindableProperties(
|
||||||
$currentAsset.props,
|
$currentAsset,
|
||||||
$store.selectedComponentId
|
$store.selectedComponentId
|
||||||
)
|
)
|
||||||
$: safeValue = getSafeValue(value, props.defaultValue, bindableProperties)
|
$: safeValue = getSafeValue(value, props.defaultValue, bindableProperties)
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
<script>
|
|
||||||
import { DataList } from "@budibase/bbui"
|
|
||||||
import { createEventDispatcher } from "svelte"
|
|
||||||
import { store, allScreens, currentAsset } from "builderStore"
|
|
||||||
import { getBindableProperties } from "builderStore/dataBinding"
|
|
||||||
|
|
||||||
export let value = ""
|
|
||||||
|
|
||||||
$: urls = getUrls($allScreens, $currentAsset, $store.selectedComponentId)
|
|
||||||
|
|
||||||
// Update value on blur
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
const handleBlur = () => dispatch("change", value)
|
|
||||||
|
|
||||||
// Get all valid screen URL, as well as detail screens which can be used in
|
|
||||||
// the current data context
|
|
||||||
const getUrls = (screens, asset, componentId) => {
|
|
||||||
// Get all screens which aren't detail screens
|
|
||||||
let urls = screens
|
|
||||||
.filter(screen => !screen.props._component.endsWith("/rowdetail"))
|
|
||||||
.map(screen => ({
|
|
||||||
name: screen.props._instanceName,
|
|
||||||
url: screen.routing.route,
|
|
||||||
sort: screen.props._component,
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Add detail screens enriched with the current data context
|
|
||||||
const bindableProperties = getBindableProperties(asset.props, componentId)
|
|
||||||
screens
|
|
||||||
.filter(screen => screen.props._component.endsWith("/rowdetail"))
|
|
||||||
.forEach(detailScreen => {
|
|
||||||
// Find any _id bindings that match the detail screen's table
|
|
||||||
const binding = bindableProperties.find(p => {
|
|
||||||
return (
|
|
||||||
p.type === "context" &&
|
|
||||||
p.runtimeBinding.endsWith("._id") &&
|
|
||||||
p.tableId === detailScreen.props.table
|
|
||||||
)
|
|
||||||
})
|
|
||||||
if (binding) {
|
|
||||||
urls.push({
|
|
||||||
name: detailScreen.props._instanceName,
|
|
||||||
url: detailScreen.routing.route.replace(
|
|
||||||
":id",
|
|
||||||
`{{ ${binding.runtimeBinding} }}`
|
|
||||||
),
|
|
||||||
sort: detailScreen.props._component,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return urls
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<DataList
|
|
||||||
editable
|
|
||||||
secondary
|
|
||||||
extraThin
|
|
||||||
on:blur={handleBlur}
|
|
||||||
on:change
|
|
||||||
bind:value>
|
|
||||||
<option value="" />
|
|
||||||
{#each urls as url}
|
|
||||||
<option value={url.url}>{url.name}</option>
|
|
||||||
{/each}
|
|
||||||
</DataList>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
div :global(> div) {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -18,7 +18,6 @@
|
||||||
import MultiFieldSelect from "./PropertyControls/MultiFieldSelect.svelte"
|
import MultiFieldSelect from "./PropertyControls/MultiFieldSelect.svelte"
|
||||||
import SchemaSelect from "./PropertyControls/SchemaSelect.svelte"
|
import SchemaSelect from "./PropertyControls/SchemaSelect.svelte"
|
||||||
import EventsEditor from "./PropertyControls/EventsEditor"
|
import EventsEditor from "./PropertyControls/EventsEditor"
|
||||||
import ScreenSelect from "./PropertyControls/ScreenSelect.svelte"
|
|
||||||
import DetailScreenSelect from "./PropertyControls/DetailScreenSelect.svelte"
|
import DetailScreenSelect from "./PropertyControls/DetailScreenSelect.svelte"
|
||||||
import { IconSelect } from "./PropertyControls/IconSelect"
|
import { IconSelect } from "./PropertyControls/IconSelect"
|
||||||
import ColorPicker from "./PropertyControls/ColorPicker.svelte"
|
import ColorPicker from "./PropertyControls/ColorPicker.svelte"
|
||||||
|
@ -62,7 +61,6 @@
|
||||||
text: Input,
|
text: Input,
|
||||||
select: OptionSelect,
|
select: OptionSelect,
|
||||||
datasource: DatasourceSelect,
|
datasource: DatasourceSelect,
|
||||||
screen: ScreenSelect,
|
|
||||||
detailScreen: DetailScreenSelect,
|
detailScreen: DetailScreenSelect,
|
||||||
boolean: Checkbox,
|
boolean: Checkbox,
|
||||||
number: Input,
|
number: Input,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "0.7.7",
|
"version": "0.7.8",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
"module": "dist/budibase-client.js",
|
"module": "dist/budibase-client.js",
|
||||||
|
@ -9,14 +9,14 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/string-templates": "^0.7.7",
|
"@budibase/string-templates": "^0.7.8",
|
||||||
"deep-equal": "^2.0.1",
|
"deep-equal": "^2.0.1",
|
||||||
"regexparam": "^1.3.0",
|
"regexparam": "^1.3.0",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"svelte-spa-router": "^3.0.5"
|
"svelte-spa-router": "^3.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@budibase/standard-components": "^0.7.7",
|
"@budibase/standard-components": "^0.7.8",
|
||||||
"@rollup/plugin-commonjs": "^16.0.0",
|
"@rollup/plugin-commonjs": "^16.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^10.0.0",
|
"@rollup/plugin-node-resolve": "^10.0.0",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, setContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import Router from "svelte-spa-router"
|
import Router from "svelte-spa-router"
|
||||||
import { routeStore } from "../store"
|
import { routeStore } from "../store"
|
||||||
import Screen from "./Screen.svelte"
|
import Screen from "./Screen.svelte"
|
||||||
|
@ -10,12 +10,10 @@
|
||||||
// Only wrap this as an array to take advantage of svelte keying,
|
// Only wrap this as an array to take advantage of svelte keying,
|
||||||
// to ensure the svelte-spa-router is fully remounted when route config
|
// to ensure the svelte-spa-router is fully remounted when route config
|
||||||
// changes
|
// changes
|
||||||
$: configs = [
|
$: config = {
|
||||||
{
|
routes: getRouterConfig($routeStore.routes),
|
||||||
routes: getRouterConfig($routeStore.routes),
|
id: $routeStore.routeSessionId,
|
||||||
id: $routeStore.routeSessionId,
|
}
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const getRouterConfig = routes => {
|
const getRouterConfig = routes => {
|
||||||
let config = {}
|
let config = {}
|
||||||
|
@ -33,11 +31,11 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each configs as config (config.id)}
|
{#key config.id}
|
||||||
<div use:styleable={$component.styles}>
|
<div use:styleable={$component.styles}>
|
||||||
<Router on:routeLoading={onRouteLoading} routes={config.routes} />
|
<Router on:routeLoading={onRouteLoading} routes={config.routes} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/key}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { fade } from "svelte/transition"
|
import { fade } from "svelte/transition"
|
||||||
import { screenStore, routeStore } from "../store"
|
import { screenStore, routeStore } from "../store"
|
||||||
import Component from "./Component.svelte"
|
import Component from "./Component.svelte"
|
||||||
|
import Provider from "./Provider.svelte"
|
||||||
|
|
||||||
// Keep route params up to date
|
// Keep route params up to date
|
||||||
export let params = {}
|
export let params = {}
|
||||||
|
@ -12,16 +13,16 @@
|
||||||
|
|
||||||
// Redirect to home layout if no matching route
|
// Redirect to home layout if no matching route
|
||||||
$: screenDefinition == null && routeStore.actions.navigate("/")
|
$: screenDefinition == null && routeStore.actions.navigate("/")
|
||||||
|
|
||||||
// Make a screen array so we can use keying to properly re-render each screen
|
|
||||||
$: screens = screenDefinition ? [screenDefinition] : []
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each screens as screen (screen._id)}
|
<!-- Ensure to fully remount when screen changes -->
|
||||||
<div in:fade>
|
{#key screenDefinition?._id}
|
||||||
<Component definition={screen} />
|
<Provider key="url" data={params}>
|
||||||
</div>
|
<div in:fade>
|
||||||
{/each}
|
<Component definition={screenDefinition} />
|
||||||
|
</div>
|
||||||
|
</Provider>
|
||||||
|
{/key}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
|
|
|
@ -44,7 +44,7 @@ export const enrichProps = async (props, context) => {
|
||||||
let enrichedProps = await enrichDataBindings(validProps, totalContext)
|
let enrichedProps = await enrichDataBindings(validProps, totalContext)
|
||||||
|
|
||||||
// Enrich button actions if they exist
|
// Enrich button actions if they exist
|
||||||
if (props._component.endsWith("/button") && enrichedProps.onClick) {
|
if (props._component?.endsWith("/button") && enrichedProps.onClick) {
|
||||||
enrichedProps.onClick = enrichButtonActions(
|
enrichedProps.onClick = enrichButtonActions(
|
||||||
enrichedProps.onClick,
|
enrichedProps.onClick,
|
||||||
totalContext
|
totalContext
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.7.7",
|
"version": "0.7.8",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/electron.js",
|
"main": "src/electron.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -50,8 +50,8 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/client": "^0.7.7",
|
"@budibase/client": "^0.7.8",
|
||||||
"@budibase/string-templates": "^0.7.7",
|
"@budibase/string-templates": "^0.7.8",
|
||||||
"@elastic/elasticsearch": "7.10.0",
|
"@elastic/elasticsearch": "7.10.0",
|
||||||
"@koa/router": "8.0.0",
|
"@koa/router": "8.0.0",
|
||||||
"@sendgrid/mail": "7.1.1",
|
"@sendgrid/mail": "7.1.1",
|
||||||
|
|
|
@ -176,9 +176,10 @@
|
||||||
"key": "subheading"
|
"key": "subheading"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "screen",
|
"type": "text",
|
||||||
"label": "Link URL",
|
"label": "Link URL",
|
||||||
"key": "destinationUrl"
|
"key": "destinationUrl",
|
||||||
|
"placeholder": "/screen"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -209,9 +210,10 @@
|
||||||
"key": "linkText"
|
"key": "linkText"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "screen",
|
"type": "text",
|
||||||
"label": "Link Url",
|
"label": "Link URL",
|
||||||
"key": "linkUrl"
|
"key": "linkUrl",
|
||||||
|
"placeholder": "/screen"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "color",
|
"type": "color",
|
||||||
|
@ -378,9 +380,10 @@
|
||||||
"key": "text"
|
"key": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "screen",
|
"type": "text",
|
||||||
"label": "URL",
|
"label": "URL",
|
||||||
"key": "url"
|
"key": "url",
|
||||||
|
"placeholder": "/screen"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
@ -451,9 +454,10 @@
|
||||||
"key": "linkText"
|
"key": "linkText"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "screen",
|
"type": "text",
|
||||||
"label": "Link URL",
|
"label": "Link URL",
|
||||||
"key": "linkUrl"
|
"key": "linkUrl",
|
||||||
|
"placeholder": "/screen"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "color",
|
"type": "color",
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"svelte"
|
"svelte"
|
||||||
],
|
],
|
||||||
"version": "0.7.7",
|
"version": "0.7.8",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"gitHead": "1a80b09fd093f2599a68f7db72ad639dd50922dd",
|
"gitHead": "1a80b09fd093f2599a68f7db72ad639dd50922dd",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
<script>
|
|
||||||
import { Label, Multiselect } from "@budibase/bbui"
|
|
||||||
import { capitalise } from "./helpers"
|
|
||||||
import { getContext } from "svelte"
|
|
||||||
|
|
||||||
const { API } = getContext("sdk")
|
|
||||||
|
|
||||||
export let schema = {}
|
|
||||||
export let linkedRows = []
|
|
||||||
export let showLabel = true
|
|
||||||
export let secondary
|
|
||||||
|
|
||||||
let linkedTable
|
|
||||||
let allRows = []
|
|
||||||
|
|
||||||
$: label = capitalise(schema.name)
|
|
||||||
$: linkedTableId = schema.tableId
|
|
||||||
$: fetchRows(linkedTableId)
|
|
||||||
$: fetchTable(linkedTableId)
|
|
||||||
|
|
||||||
async function fetchTable(id) {
|
|
||||||
if (id != null) {
|
|
||||||
linkedTable = await API.fetchTableDefinition(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchRows(id) {
|
|
||||||
if (id != null) {
|
|
||||||
allRows = await API.fetchTableData(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPrettyName(row) {
|
|
||||||
return row[(linkedTable && linkedTable.primaryDisplay) || "_id"]
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if linkedTable != null}
|
|
||||||
{#if linkedTable.primaryDisplay == null}
|
|
||||||
{#if showLabel}
|
|
||||||
<Label extraSmall grey>{label}</Label>
|
|
||||||
{/if}
|
|
||||||
<Label small black>
|
|
||||||
Please choose a display column for the
|
|
||||||
<b>{linkedTable.name}</b>
|
|
||||||
table.
|
|
||||||
</Label>
|
|
||||||
{:else}
|
|
||||||
<Multiselect
|
|
||||||
{secondary}
|
|
||||||
bind:value={linkedRows}
|
|
||||||
label={showLabel ? label : null}
|
|
||||||
placeholder="Choose some options">
|
|
||||||
{#each allRows as row}
|
|
||||||
<option value={row._id}>{getPrettyName(row)}</option>
|
|
||||||
{/each}
|
|
||||||
</Multiselect>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
|
@ -18,16 +18,16 @@
|
||||||
let table
|
let table
|
||||||
let fieldMap = {}
|
let fieldMap = {}
|
||||||
|
|
||||||
// Checks if the closest data context matches the model for this forms
|
// Returns the closes data context which isn't a built in context
|
||||||
// datasource, and use it as the initial form values if so
|
|
||||||
const getInitialValues = context => {
|
const getInitialValues = context => {
|
||||||
return context && context.tableId === datasource?.tableId ? context : {}
|
if (["user", "url"].includes(context.closestComponentId)) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
return context[`${context.closestComponentId}`] || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the closest data context as the initial form values if it matches
|
// Use the closest data context as the initial form values
|
||||||
const initialValues = getInitialValues(
|
const initialValues = getInitialValues($context)
|
||||||
$context[`${$context.closestComponentId}`]
|
|
||||||
)
|
|
||||||
|
|
||||||
// Form state contains observable data about the form
|
// Form state contains observable data about the form
|
||||||
const formState = writable({ values: initialValues, errors: {}, valid: true })
|
const formState = writable({ values: initialValues, errors: {}, valid: true })
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
$: fetchTable(linkedTableId)
|
$: fetchTable(linkedTableId)
|
||||||
|
|
||||||
const fetchTable = async id => {
|
const fetchTable = async id => {
|
||||||
if (id != null) {
|
if (id) {
|
||||||
const result = await API.fetchTableDefinition(id)
|
const result = await API.fetchTableDefinition(id)
|
||||||
if (!result.error) {
|
if (!result.error) {
|
||||||
tableDefinition = result
|
tableDefinition = result
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchRows = async id => {
|
const fetchRows = async id => {
|
||||||
if (id != null) {
|
if (id) {
|
||||||
const rows = await API.fetchTableData(id)
|
const rows = await API.fetchTableData(id)
|
||||||
options = rows && !rows.error ? rows : []
|
options = rows && !rows.error ? rows : []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "0.7.7",
|
"version": "0.7.8",
|
||||||
"description": "Handlebars wrapper for Budibase templating.",
|
"description": "Handlebars wrapper for Budibase templating.",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"module": "src/index.js",
|
"module": "src/index.js",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/deployment",
|
"name": "@budibase/deployment",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.7.7",
|
"version": "0.7.8",
|
||||||
"description": "Budibase Deployment Server",
|
"description": "Budibase Deployment Server",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
Loading…
Reference in New Issue