Merge branch 'form-builder' of github.com:Budibase/budibase into form-builder
This commit is contained in:
commit
c7ed77381e
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "0.7.1",
|
"version": "0.7.4",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "0.7.1",
|
"version": "0.7.4",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -64,26 +64,25 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.56.0",
|
"@budibase/bbui": "^1.56.0",
|
||||||
"@budibase/client": "^0.7.1",
|
"@budibase/client": "^0.7.4",
|
||||||
"@budibase/colorpicker": "^1.0.1",
|
"@budibase/colorpicker": "1.0.1",
|
||||||
"@budibase/string-templates": "^0.7.1",
|
"@budibase/string-templates": "^0.7.4",
|
||||||
"@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",
|
||||||
"britecharts": "^2.16.0",
|
|
||||||
"codemirror": "^5.59.0",
|
"codemirror": "^5.59.0",
|
||||||
"d3-selection": "^1.4.1",
|
"d3-selection": "^1.4.1",
|
||||||
"deepmerge": "^4.2.2",
|
"deepmerge": "^4.2.2",
|
||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "1.4.7",
|
||||||
"fast-sort": "^2.2.0",
|
"fast-sort": "^2.2.0",
|
||||||
"lodash": "^4.17.13",
|
"lodash": "4.17.13",
|
||||||
"posthog-js": "1.4.5",
|
"posthog-js": "1.4.5",
|
||||||
"remixicon": "^2.5.0",
|
"remixicon": "2.5.0",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "2.2.15",
|
||||||
"svelte-loading-spinners": "^0.1.1",
|
"svelte-loading-spinners": "^0.1.1",
|
||||||
"svelte-portal": "^0.1.0",
|
"svelte-portal": "0.1.0",
|
||||||
"uuid": "^8.3.1",
|
"uuid": "8.3.1",
|
||||||
"yup": "^0.29.2"
|
"yup": "0.29.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.5.5",
|
"@babel/core": "^7.5.5",
|
||||||
|
|
|
@ -69,8 +69,9 @@ export const getDatasourceForProvider = component => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract datasource from component instance
|
// Extract datasource from component instance
|
||||||
|
const validSettingTypes = ["datasource", "table", "schema"]
|
||||||
const datasourceSetting = def.settings.find(setting => {
|
const datasourceSetting = def.settings.find(setting => {
|
||||||
return setting.type === "datasource" || setting.type === "table"
|
return validSettingTypes.includes(setting.type)
|
||||||
})
|
})
|
||||||
if (!datasourceSetting) {
|
if (!datasourceSetting) {
|
||||||
return null
|
return null
|
||||||
|
@ -80,15 +81,14 @@ export const getDatasourceForProvider = component => {
|
||||||
// example an actual datasource object, or a table ID string.
|
// example an actual datasource object, or a table ID string.
|
||||||
// Convert the datasource setting into a proper datasource object so that
|
// Convert the datasource setting into a proper datasource object so that
|
||||||
// we can use it properly
|
// we can use it properly
|
||||||
if (datasourceSetting.type === "datasource") {
|
if (datasourceSetting.type === "table") {
|
||||||
return component[datasourceSetting?.key]
|
|
||||||
} else if (datasourceSetting.type === "table") {
|
|
||||||
return {
|
return {
|
||||||
tableId: component[datasourceSetting?.key],
|
tableId: component[datasourceSetting?.key],
|
||||||
type: "table",
|
type: "table",
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return component[datasourceSetting?.key]
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -99,21 +99,37 @@ export const getContextBindings = (rootComponent, componentId) => {
|
||||||
// Extract any components which provide data contexts
|
// Extract any components which provide data contexts
|
||||||
const dataProviders = getDataProviderComponents(rootComponent, componentId)
|
const dataProviders = getDataProviderComponents(rootComponent, componentId)
|
||||||
let contextBindings = []
|
let contextBindings = []
|
||||||
|
|
||||||
|
// Create bindings for each data provider
|
||||||
dataProviders.forEach(component => {
|
dataProviders.forEach(component => {
|
||||||
|
const isForm = component._component.endsWith("/form")
|
||||||
const datasource = getDatasourceForProvider(component)
|
const datasource = getDatasourceForProvider(component)
|
||||||
if (!datasource) {
|
let tableName, schema
|
||||||
|
|
||||||
|
// Forms are an edge case which do not need table schemas
|
||||||
|
if (isForm) {
|
||||||
|
schema = buildFormSchema(component)
|
||||||
|
tableName = "Schema"
|
||||||
|
} else {
|
||||||
|
if (!datasource) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get schema and table for the datasource
|
||||||
|
const info = getSchemaForDatasource(datasource, isForm)
|
||||||
|
schema = info.schema
|
||||||
|
tableName = info.table?.name
|
||||||
|
|
||||||
|
// Add _id and _rev fields for certain types
|
||||||
|
if (datasource.type === "table" || datasource.type === "link") {
|
||||||
|
schema["_id"] = { type: "string" }
|
||||||
|
schema["_rev"] = { type: "string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!schema || !tableName) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get schema and add _id and _rev fields for certain types
|
|
||||||
let { schema, table } = getSchemaForDatasource(datasource)
|
|
||||||
if (!schema || !table) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (datasource.type === "table" || datasource.type === "link") {
|
|
||||||
schema["_id"] = { type: "string" }
|
|
||||||
schema["_rev"] = { type: "string " }
|
|
||||||
}
|
|
||||||
const keys = Object.keys(schema).sort()
|
const keys = Object.keys(schema).sort()
|
||||||
|
|
||||||
// Create bindable properties for each schema field
|
// Create bindable properties for each schema field
|
||||||
|
@ -132,11 +148,13 @@ export const getContextBindings = (rootComponent, componentId) => {
|
||||||
runtimeBinding: `${makePropSafe(component._id)}.${makePropSafe(
|
runtimeBinding: `${makePropSafe(component._id)}.${makePropSafe(
|
||||||
runtimeBoundKey
|
runtimeBoundKey
|
||||||
)}`,
|
)}`,
|
||||||
readableBinding: `${component._instanceName}.${table.name}.${key}`,
|
readableBinding: `${component._instanceName}.${tableName}.${key}`,
|
||||||
|
// Field schema and provider are required to construct relationship
|
||||||
|
// datasource options, based on bindable properties
|
||||||
fieldSchema,
|
fieldSchema,
|
||||||
providerId: component._id,
|
providerId: component._id,
|
||||||
tableId: datasource.tableId,
|
// tableId: table._id,
|
||||||
field: key,
|
// field: key,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -164,10 +182,12 @@ export const getContextBindings = (rootComponent, componentId) => {
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: `user.${runtimeBoundKey}`,
|
runtimeBinding: `user.${runtimeBoundKey}`,
|
||||||
readableBinding: `Current User.${key}`,
|
readableBinding: `Current User.${key}`,
|
||||||
|
// Field schema and provider are required to construct relationship
|
||||||
|
// datasource options, based on bindable properties
|
||||||
fieldSchema,
|
fieldSchema,
|
||||||
providerId: "user",
|
providerId: "user",
|
||||||
tableId: TableNames.USERS,
|
// tableId: TableNames.USERS,
|
||||||
field: key,
|
// field: key,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -177,7 +197,7 @@ export const getContextBindings = (rootComponent, componentId) => {
|
||||||
/**
|
/**
|
||||||
* Gets a schema for a datasource object.
|
* Gets a schema for a datasource object.
|
||||||
*/
|
*/
|
||||||
export const getSchemaForDatasource = datasource => {
|
export const getSchemaForDatasource = (datasource, isForm = false) => {
|
||||||
let schema, table
|
let schema, table
|
||||||
if (datasource) {
|
if (datasource) {
|
||||||
const { type } = datasource
|
const { type } = datasource
|
||||||
|
@ -191,6 +211,12 @@ export const getSchemaForDatasource = datasource => {
|
||||||
if (table) {
|
if (table) {
|
||||||
if (type === "view") {
|
if (type === "view") {
|
||||||
schema = cloneDeep(table.views?.[datasource.name]?.schema)
|
schema = cloneDeep(table.views?.[datasource.name]?.schema)
|
||||||
|
} else if (type === "query" && isForm) {
|
||||||
|
schema = {}
|
||||||
|
const params = table.parameters || []
|
||||||
|
params.forEach(param => {
|
||||||
|
schema[param.name] = { ...param, type: "string" }
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
schema = cloneDeep(table.schema)
|
schema = cloneDeep(table.schema)
|
||||||
}
|
}
|
||||||
|
@ -199,6 +225,32 @@ export const getSchemaForDatasource = datasource => {
|
||||||
return { schema, table }
|
return { schema, table }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a form schema given a form component.
|
||||||
|
* A form schema is a schema of all the fields nested anywhere within a form.
|
||||||
|
*/
|
||||||
|
const buildFormSchema = component => {
|
||||||
|
let schema = {}
|
||||||
|
if (!component) {
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
const def = store.actions.components.getDefinition(component._component)
|
||||||
|
const fieldSetting = def?.settings?.find(
|
||||||
|
setting => setting.key === "field" && setting.type.startsWith("field/")
|
||||||
|
)
|
||||||
|
if (fieldSetting && component.field) {
|
||||||
|
const type = fieldSetting.type.split("field/")[1]
|
||||||
|
if (type) {
|
||||||
|
schema[component.field] = { name: component.field, type }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
component._children?.forEach(child => {
|
||||||
|
const childSchema = buildFormSchema(child)
|
||||||
|
schema = { ...schema, ...childSchema }
|
||||||
|
})
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* utility function for the readableToRuntimeBinding and runtimeToReadableBinding.
|
* utility function for the readableToRuntimeBinding and runtimeToReadableBinding.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -416,7 +416,14 @@ export const getFrontendStore = () => {
|
||||||
if (cut) {
|
if (cut) {
|
||||||
state.componentToPaste = null
|
state.componentToPaste = null
|
||||||
} else {
|
} else {
|
||||||
componentToPaste._id = uuid()
|
const randomizeIds = component => {
|
||||||
|
if (!component) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
component._id = uuid()
|
||||||
|
component._children?.forEach(randomizeIds)
|
||||||
|
}
|
||||||
|
randomizeIds(componentToPaste)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode === "inside") {
|
if (mode === "inside") {
|
||||||
|
|
|
@ -35,7 +35,7 @@ const createScreen = table => {
|
||||||
const form = makeMainForm()
|
const form = makeMainForm()
|
||||||
.instanceName("Form")
|
.instanceName("Form")
|
||||||
.customProps({
|
.customProps({
|
||||||
theme: "spectrum--light",
|
theme: "spectrum--lightest",
|
||||||
size: "spectrum--medium",
|
size: "spectrum--medium",
|
||||||
datasource: {
|
datasource: {
|
||||||
label: table.name,
|
label: table.name,
|
||||||
|
|
|
@ -59,6 +59,7 @@ function generateTitleContainer(table, title, formId) {
|
||||||
onClick: [
|
onClick: [
|
||||||
{
|
{
|
||||||
parameters: {
|
parameters: {
|
||||||
|
providerId: formId,
|
||||||
rowId: `{{ ${makePropSafe(formId)}._id }}`,
|
rowId: `{{ ${makePropSafe(formId)}._id }}`,
|
||||||
revId: `{{ ${makePropSafe(formId)}._rev }}`,
|
revId: `{{ ${makePropSafe(formId)}._rev }}`,
|
||||||
tableId: table._id,
|
tableId: table._id,
|
||||||
|
@ -90,7 +91,7 @@ const createScreen = table => {
|
||||||
const form = makeMainForm()
|
const form = makeMainForm()
|
||||||
.instanceName("Form")
|
.instanceName("Form")
|
||||||
.customProps({
|
.customProps({
|
||||||
theme: "spectrum--light",
|
theme: "spectrum--lightest",
|
||||||
size: "spectrum--medium",
|
size: "spectrum--medium",
|
||||||
datasource: {
|
datasource: {
|
||||||
label: table.name,
|
label: table.name,
|
||||||
|
|
|
@ -169,7 +169,11 @@ export function makeTableFormComponents(tableId) {
|
||||||
|
|
||||||
export function makeQueryFormComponents(queryId) {
|
export function makeQueryFormComponents(queryId) {
|
||||||
const queries = get(backendUiStore).queries
|
const queries = get(backendUiStore).queries
|
||||||
const schema = queries.find(query => query._id === queryId)?.schema ?? []
|
const params = queries.find(query => query._id === queryId)?.parameters ?? []
|
||||||
|
let schema = {}
|
||||||
|
params.forEach(param => {
|
||||||
|
schema[param.name] = { ...param, type: "string" }
|
||||||
|
})
|
||||||
return makeSchemaFormComponents(schema)
|
return makeSchemaFormComponents(schema)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,11 @@
|
||||||
{:else if value.customType === 'password'}
|
{:else if value.customType === 'password'}
|
||||||
<Input type="password" extraThin bind:value={block.inputs[key]} />
|
<Input type="password" extraThin bind:value={block.inputs[key]} />
|
||||||
{:else if value.customType === 'email'}
|
{:else if value.customType === 'email'}
|
||||||
<Input type="email" extraThin bind:value={block.inputs[key]} />
|
<BindableInput
|
||||||
|
type={'email'}
|
||||||
|
extraThin
|
||||||
|
bind:value={block.inputs[key]}
|
||||||
|
{bindings} />
|
||||||
{:else if value.customType === 'table'}
|
{:else if value.customType === 'table'}
|
||||||
<TableSelector bind:value={block.inputs[key]} />
|
<TableSelector bind:value={block.inputs[key]} />
|
||||||
{:else if value.customType === 'row'}
|
{:else if value.customType === 'row'}
|
||||||
|
@ -75,7 +79,7 @@
|
||||||
<SchemaSetup bind:value={block.inputs[key]} />
|
<SchemaSetup bind:value={block.inputs[key]} />
|
||||||
{:else if value.type === 'string' || value.type === 'number'}
|
{:else if value.type === 'string' || value.type === 'number'}
|
||||||
<BindableInput
|
<BindableInput
|
||||||
type="string"
|
type={value.customType}
|
||||||
extraThin
|
extraThin
|
||||||
bind:value={block.inputs[key]}
|
bind:value={block.inputs[key]}
|
||||||
{bindings} />
|
{bindings} />
|
||||||
|
|
|
@ -20,13 +20,12 @@
|
||||||
let exportFormat = FORMATS[0].key
|
let exportFormat = FORMATS[0].key
|
||||||
|
|
||||||
async function exportView() {
|
async function exportView() {
|
||||||
const response = await api.post(
|
download(
|
||||||
`/api/views/export?format=${exportFormat}`,
|
`/api/views/export?view=${encodeURIComponent(
|
||||||
view
|
view.name
|
||||||
|
)}&format=${exportFormat}`
|
||||||
)
|
)
|
||||||
const downloadInfo = await response.json()
|
|
||||||
onClosed()
|
onClosed()
|
||||||
window.location = downloadInfo.url
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
let drawer
|
let drawer
|
||||||
|
|
||||||
export let value = {}
|
export let value = {}
|
||||||
|
export let otherSources
|
||||||
|
|
||||||
$: tables = $backendUiStore.tables.map(m => ({
|
$: tables = $backendUiStore.tables.map(m => ({
|
||||||
label: m.name,
|
label: m.name,
|
||||||
|
@ -88,7 +89,7 @@
|
||||||
class="dropdownbutton"
|
class="dropdownbutton"
|
||||||
bind:this={anchorRight}
|
bind:this={anchorRight}
|
||||||
on:click={dropdownRight.show}>
|
on:click={dropdownRight.show}>
|
||||||
<span>{value?.label ? value.label : 'Choose option'}</span>
|
<span>{value?.label ?? 'Choose option'}</span>
|
||||||
<Icon name="arrowdown" />
|
<Icon name="arrowdown" />
|
||||||
</div>
|
</div>
|
||||||
{#if value?.type === 'query'}
|
{#if value?.type === 'query'}
|
||||||
|
@ -175,6 +176,22 @@
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
{#if otherSources?.length}
|
||||||
|
<hr />
|
||||||
|
<div class="title">
|
||||||
|
<Heading extraSmall>Other</Heading>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
{#each otherSources as source}
|
||||||
|
<li
|
||||||
|
class:selected={value === source}
|
||||||
|
on:click={() => handleSelected(source)}>
|
||||||
|
{source.label}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
|
@ -15,8 +15,9 @@
|
||||||
)
|
)
|
||||||
$: {
|
$: {
|
||||||
// Automatically set rev and table ID based on row ID
|
// Automatically set rev and table ID based on row ID
|
||||||
if (parameters.rowId) {
|
if (parameters.providerId) {
|
||||||
parameters.revId = parameters.rowId.replace("_id", "_rev")
|
parameters.rowId = `{{ ${parameters.providerId}._id }}`
|
||||||
|
parameters.revId = `{{ ${parameters.providerId}._rev }}`
|
||||||
const providerComponent = dataProviderComponents.find(
|
const providerComponent = dataProviderComponents.find(
|
||||||
provider => provider._id === parameters.providerId
|
provider => provider._id === parameters.providerId
|
||||||
)
|
)
|
||||||
|
@ -37,12 +38,10 @@
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<Label size="m" color="dark">Datasource</Label>
|
<Label size="m" color="dark">Datasource</Label>
|
||||||
<Select secondary bind:value={parameters.rowId}>
|
<Select secondary bind:value={parameters.providerId}>
|
||||||
<option value="" />
|
<option value="" />
|
||||||
{#each dataProviderComponents as provider}
|
{#each dataProviderComponents as provider}
|
||||||
<option value={`{{ ${provider._id}._id }}`}>
|
<option value={provider._id}>{provider._instanceName}</option>
|
||||||
{provider._instanceName}
|
|
||||||
</option>
|
|
||||||
{/each}
|
{/each}
|
||||||
</Select>
|
</Select>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<script>
|
||||||
|
import { Select, Label } from "@budibase/bbui"
|
||||||
|
import { currentAsset, store } from "builderStore"
|
||||||
|
import { getDataProviderComponents } from "builderStore/dataBinding"
|
||||||
|
|
||||||
|
export let parameters
|
||||||
|
|
||||||
|
$: dataProviders = getDataProviderComponents(
|
||||||
|
$currentAsset.props,
|
||||||
|
$store.selectedComponentId
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="root">
|
||||||
|
<Label size="m" color="dark">Form</Label>
|
||||||
|
<Select secondary bind:value={parameters.componentId}>
|
||||||
|
<option value="" />
|
||||||
|
{#if dataProviders}
|
||||||
|
{#each dataProviders as component}
|
||||||
|
<option value={component._id}>{component._instanceName}</option>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.root {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(> div) {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: var(--spacing-l);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -4,6 +4,7 @@ import DeleteRow from "./DeleteRow.svelte"
|
||||||
import ExecuteQuery from "./ExecuteQuery.svelte"
|
import ExecuteQuery from "./ExecuteQuery.svelte"
|
||||||
import TriggerAutomation from "./TriggerAutomation.svelte"
|
import TriggerAutomation from "./TriggerAutomation.svelte"
|
||||||
import ValidateForm from "./ValidateForm.svelte"
|
import ValidateForm from "./ValidateForm.svelte"
|
||||||
|
import RefreshDatasource from "./RefreshDatasource.svelte"
|
||||||
|
|
||||||
// defines what actions are available, when adding a new one
|
// defines what actions are available, when adding a new one
|
||||||
// the component is the setup panel for the action
|
// the component is the setup panel for the action
|
||||||
|
@ -35,4 +36,8 @@ export default [
|
||||||
name: "Validate Form",
|
name: "Validate Form",
|
||||||
component: ValidateForm,
|
component: ValidateForm,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Refresh Datasource",
|
||||||
|
component: RefreshDatasource,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import OptionSelect from "./OptionSelect.svelte"
|
import { DataList } from "@budibase/bbui"
|
||||||
import {
|
import {
|
||||||
getDatasourceForProvider,
|
getDatasourceForProvider,
|
||||||
getSchemaForDatasource,
|
getSchemaForDatasource,
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
component => component._component === "@budibase/standard-components/form"
|
component => component._component === "@budibase/standard-components/form"
|
||||||
)
|
)
|
||||||
$: datasource = getDatasourceForProvider(form)
|
$: datasource = getDatasourceForProvider(form)
|
||||||
$: schema = getSchemaForDatasource(datasource).schema
|
$: schema = getSchemaForDatasource(datasource, true).schema
|
||||||
$: options = getOptions(schema, type)
|
$: options = getOptions(schema, type)
|
||||||
|
|
||||||
const getOptions = (schema, fieldType) => {
|
const getOptions = (schema, fieldType) => {
|
||||||
|
@ -28,6 +28,32 @@
|
||||||
}
|
}
|
||||||
return entries.map(entry => entry[0])
|
return entries.map(entry => entry[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleBlur = () => onChange(value)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<OptionSelect {value} {onChange} {options} />
|
<div>
|
||||||
|
<DataList
|
||||||
|
editable
|
||||||
|
secondary
|
||||||
|
extraThin
|
||||||
|
on:blur={handleBlur}
|
||||||
|
on:change
|
||||||
|
bind:value>
|
||||||
|
<option value="" />
|
||||||
|
{#each options as option}
|
||||||
|
<option value={option}>{option}</option>
|
||||||
|
{/each}
|
||||||
|
</DataList>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
div :global(> div) {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<script>
|
||||||
|
import FieldSelect from "./FieldSelect.svelte"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FieldSelect {...$$props} multiselect />
|
|
@ -1,5 +0,0 @@
|
||||||
<script>
|
|
||||||
import TableViewFieldSelect from "./TableViewFieldSelect.svelte"
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<TableViewFieldSelect {...$$props} multiselect />
|
|
|
@ -138,7 +138,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding-left: var(--spacing-xs);
|
padding-left: 7px;
|
||||||
border-left: 1px solid var(--grey-4);
|
border-left: 1px solid var(--grey-4);
|
||||||
background-color: var(--grey-2);
|
background-color: var(--grey-2);
|
||||||
border-top-right-radius: var(--border-radius-m);
|
border-top-right-radius: var(--border-radius-m);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<DetailSummary name={`${name}${changed ? ' *' : ''}`} on:open show={open} thin>
|
<DetailSummary name={`${name}${changed ? ' *' : ''}`} on:open show={open} thin>
|
||||||
{#if open}
|
{#if open}
|
||||||
<div>
|
<div>
|
||||||
{#each properties as prop}
|
{#each properties as prop (`${componentInstance._id}-${prop.key}`)}
|
||||||
<PropertyControl
|
<PropertyControl
|
||||||
bindable={false}
|
bindable={false}
|
||||||
label={`${prop.label}${hasPropChanged(style, prop) ? ' *' : ''}`}
|
label={`${prop.label}${hasPropChanged(style, prop) ? ' *' : ''}`}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script>
|
||||||
|
import DatasourceSelect from "./DatasourceSelect.svelte"
|
||||||
|
|
||||||
|
const otherSources = [{ name: "Custom", label: "Custom" }]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DatasourceSelect on:change {...$$props} {otherSources} />
|
|
@ -11,11 +11,12 @@
|
||||||
import LayoutSelect from "./PropertyControls/LayoutSelect.svelte"
|
import LayoutSelect from "./PropertyControls/LayoutSelect.svelte"
|
||||||
import RoleSelect from "./PropertyControls/RoleSelect.svelte"
|
import RoleSelect from "./PropertyControls/RoleSelect.svelte"
|
||||||
import OptionSelect from "./PropertyControls/OptionSelect.svelte"
|
import OptionSelect from "./PropertyControls/OptionSelect.svelte"
|
||||||
import MultiTableViewFieldSelect from "./PropertyControls/MultiTableViewFieldSelect.svelte"
|
|
||||||
import Checkbox from "./PropertyControls/Checkbox.svelte"
|
import Checkbox from "./PropertyControls/Checkbox.svelte"
|
||||||
import TableSelect from "./PropertyControls/TableSelect.svelte"
|
import TableSelect from "./PropertyControls/TableSelect.svelte"
|
||||||
import TableViewSelect from "./PropertyControls/TableViewSelect.svelte"
|
import DatasourceSelect from "./PropertyControls/DatasourceSelect.svelte"
|
||||||
import TableViewFieldSelect from "./PropertyControls/TableViewFieldSelect.svelte"
|
import FieldSelect from "./PropertyControls/FieldSelect.svelte"
|
||||||
|
import MultiFieldSelect from "./PropertyControls/MultiFieldSelect.svelte"
|
||||||
|
import SchemaSelect from "./PropertyControls/SchemaSelect.svelte"
|
||||||
import EventsEditor from "./PropertyControls/EventsEditor"
|
import EventsEditor from "./PropertyControls/EventsEditor"
|
||||||
import ScreenSelect from "./PropertyControls/ScreenSelect.svelte"
|
import ScreenSelect from "./PropertyControls/ScreenSelect.svelte"
|
||||||
import DetailScreenSelect from "./PropertyControls/DetailScreenSelect.svelte"
|
import DetailScreenSelect from "./PropertyControls/DetailScreenSelect.svelte"
|
||||||
|
@ -60,7 +61,7 @@
|
||||||
const controlMap = {
|
const controlMap = {
|
||||||
text: Input,
|
text: Input,
|
||||||
select: OptionSelect,
|
select: OptionSelect,
|
||||||
datasource: TableViewSelect,
|
datasource: DatasourceSelect,
|
||||||
screen: ScreenSelect,
|
screen: ScreenSelect,
|
||||||
detailScreen: DetailScreenSelect,
|
detailScreen: DetailScreenSelect,
|
||||||
boolean: Checkbox,
|
boolean: Checkbox,
|
||||||
|
@ -69,8 +70,9 @@
|
||||||
table: TableSelect,
|
table: TableSelect,
|
||||||
color: ColorPicker,
|
color: ColorPicker,
|
||||||
icon: IconSelect,
|
icon: IconSelect,
|
||||||
field: TableViewFieldSelect,
|
field: FieldSelect,
|
||||||
multifield: MultiTableViewFieldSelect,
|
multifield: MultiFieldSelect,
|
||||||
|
schema: SchemaSelect,
|
||||||
"field/string": StringFieldSelect,
|
"field/string": StringFieldSelect,
|
||||||
"field/number": NumberFieldSelect,
|
"field/number": NumberFieldSelect,
|
||||||
"field/options": OptionsFieldSelect,
|
"field/options": OptionsFieldSelect,
|
||||||
|
@ -78,7 +80,7 @@
|
||||||
"field/longform": LongFormFieldSelect,
|
"field/longform": LongFormFieldSelect,
|
||||||
"field/datetime": DateTimeFieldSelect,
|
"field/datetime": DateTimeFieldSelect,
|
||||||
"field/attachment": AttachmentFieldSelect,
|
"field/attachment": AttachmentFieldSelect,
|
||||||
"field/relationship": RelationshipFieldSelect,
|
"field/link": RelationshipFieldSelect,
|
||||||
}
|
}
|
||||||
|
|
||||||
const getControl = type => {
|
const getControl = type => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { Popover } from "@budibase/bbui"
|
import { Popover } from "@budibase/bbui"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
import FeedbackIframe from "./FeedbackIframe.svelte"
|
import FeedbackIframe from "./FeedbackIframe.svelte"
|
||||||
import analytics from "analytics"
|
import analytics from "analytics"
|
||||||
|
@ -10,9 +11,15 @@
|
||||||
let iconContainer
|
let iconContainer
|
||||||
let popover
|
let popover
|
||||||
|
|
||||||
setInterval(() => {
|
onMount(() => {
|
||||||
$store.highlightFeedbackIcon = analytics.highlightFeedbackIcon()
|
const interval = setInterval(() => {
|
||||||
}, FIVE_MINUTES)
|
store.update(state => {
|
||||||
|
state.highlightFeedbackIcon = analytics.highlightFeedbackIcon()
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
}, FIVE_MINUTES)
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container" bind:this={iconContainer} on:click={popover.show}>
|
<div class="container" bind:this={iconContainer} on:click={popover.show}>
|
||||||
|
|
|
@ -132,7 +132,8 @@
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<Input placeholder="✎ Edit Query Name" bind:value={query.name} />
|
<div class="label">Enter query name:</div>
|
||||||
|
<Input outline border bind:value={query.name} />
|
||||||
</div>
|
</div>
|
||||||
{#if config}
|
{#if config}
|
||||||
<div class="props">
|
<div class="props">
|
||||||
|
@ -216,7 +217,9 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.input {
|
.input {
|
||||||
width: 300px;
|
width: 500px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.select {
|
.select {
|
||||||
|
@ -288,4 +291,12 @@
|
||||||
margin-top: -28px;
|
margin-top: -28px;
|
||||||
z-index: -2;
|
z-index: -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
color: var(--grey-8);
|
||||||
|
font-size: var(--font-size-s);
|
||||||
|
margin-right: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,9 +14,12 @@
|
||||||
async function exportApp() {
|
async function exportApp() {
|
||||||
appExportLoading = true
|
appExportLoading = true
|
||||||
try {
|
try {
|
||||||
download(`/api/backups/export?appId=${_id}`)
|
download(
|
||||||
|
`/api/backups/export?appId=${_id}&appname=${encodeURIComponent(name)}`
|
||||||
|
)
|
||||||
notifier.success("App Export Complete.")
|
notifier.success("App Export Complete.")
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
notifier.danger("App Export Failed.")
|
notifier.danger("App Export Failed.")
|
||||||
} finally {
|
} finally {
|
||||||
appExportLoading = false
|
appExportLoading = false
|
||||||
|
@ -29,13 +32,13 @@
|
||||||
<Spacer medium />
|
<Spacer medium />
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<TextButton text medium blue href="/_builder/{_id}">
|
<TextButton text medium blue href="/_builder/{_id}">
|
||||||
Open
|
Open {name} →
|
||||||
{name}
|
|
||||||
→
|
|
||||||
</TextButton>
|
</TextButton>
|
||||||
{#if appExportLoading}
|
{#if appExportLoading}
|
||||||
<Spinner size="10" />
|
<Spinner size="10" />
|
||||||
{:else}<i class="ri-folder-download-line" on:click={exportApp} />{/if}
|
{:else}
|
||||||
|
<i class="ri-folder-download-line" on:click={exportApp} />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -854,7 +854,7 @@
|
||||||
svelte-portal "^1.0.0"
|
svelte-portal "^1.0.0"
|
||||||
turndown "^7.0.0"
|
turndown "^7.0.0"
|
||||||
|
|
||||||
"@budibase/colorpicker@^1.0.1":
|
"@budibase/colorpicker@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/colorpicker/-/colorpicker-1.0.1.tgz#940c180e7ebba0cb0756c4c8ef13f5dfab58e810"
|
resolved "https://registry.yarnpkg.com/@budibase/colorpicker/-/colorpicker-1.0.1.tgz#940c180e7ebba0cb0756c4c8ef13f5dfab58e810"
|
||||||
integrity sha512-+DTHYhU0sTi5RfCyd7AAvMsLFwyF/wgs0owf7KyQU+ZILRW+YsWa7OQMz+hKQfgVAmvzwrNz8ATiBlG3Ac6Asg==
|
integrity sha512-+DTHYhU0sTi5RfCyd7AAvMsLFwyF/wgs0owf7KyQU+ZILRW+YsWa7OQMz+hKQfgVAmvzwrNz8ATiBlG3Ac6Asg==
|
||||||
|
@ -1257,7 +1257,7 @@
|
||||||
node-fetch "^2.6.0"
|
node-fetch "^2.6.0"
|
||||||
utf-8-validate "^5.0.2"
|
utf-8-validate "^5.0.2"
|
||||||
|
|
||||||
"@svelteschool/svelte-forms@^0.7.0":
|
"@svelteschool/svelte-forms@0.7.0":
|
||||||
version "0.7.0"
|
version "0.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/@svelteschool/svelte-forms/-/svelte-forms-0.7.0.tgz#4ecba15e9a9ab2b04fad3d892931a561118a4cea"
|
resolved "https://registry.yarnpkg.com/@svelteschool/svelte-forms/-/svelte-forms-0.7.0.tgz#4ecba15e9a9ab2b04fad3d892931a561118a4cea"
|
||||||
integrity sha512-TSt8ROqK6wq+Hav7EhZL1I0GtsZhg28aJuuDSviBzG/NG9pC0eprf8roWjl59DKHOVWIUTPTeY+T+lipb9gf8w==
|
integrity sha512-TSt8ROqK6wq+Hav7EhZL1I0GtsZhg28aJuuDSviBzG/NG9pC0eprf8roWjl59DKHOVWIUTPTeY+T+lipb9gf8w==
|
||||||
|
@ -1775,11 +1775,6 @@ balanced-match@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||||
|
|
||||||
base-64@^0.1.0:
|
|
||||||
version "0.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
|
|
||||||
integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs=
|
|
||||||
|
|
||||||
base@^0.11.1:
|
base@^0.11.1:
|
||||||
version "0.11.2"
|
version "0.11.2"
|
||||||
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
||||||
|
@ -1870,15 +1865,6 @@ braces@^3.0.1, braces@~3.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
fill-range "^7.0.1"
|
fill-range "^7.0.1"
|
||||||
|
|
||||||
britecharts@^2.16.0:
|
|
||||||
version "2.17.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/britecharts/-/britecharts-2.17.2.tgz#78e7743e7c1dcaccd78ab7dacc479d37d509cdf2"
|
|
||||||
integrity sha512-+wMG/ci+UHPRIySppTs8wQZmmlYFQHn2bCvbNiWUOYd1qAoiEQyKA/dVtgdTyR09qM+h8b9YsFofaWHJRT1mQg==
|
|
||||||
dependencies:
|
|
||||||
base-64 "^0.1.0"
|
|
||||||
d3 "^5.16.0"
|
|
||||||
lodash.assign "^4.2.0"
|
|
||||||
|
|
||||||
brorand@^1.0.1:
|
brorand@^1.0.1:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||||
|
@ -2282,16 +2268,16 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
delayed-stream "~1.0.0"
|
delayed-stream "~1.0.0"
|
||||||
|
|
||||||
commander@2, commander@^2.20.0:
|
|
||||||
version "2.20.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
|
||||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
|
||||||
|
|
||||||
commander@2.17.x:
|
commander@2.17.x:
|
||||||
version "2.17.1"
|
version "2.17.1"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||||
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||||
|
|
||||||
|
commander@^2.20.0:
|
||||||
|
version "2.20.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
|
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||||
|
|
||||||
commander@^5.0.0, commander@^5.1.0:
|
commander@^5.0.0, commander@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||||
|
@ -2536,254 +2522,11 @@ cypress@^5.1.0:
|
||||||
url "^0.11.0"
|
url "^0.11.0"
|
||||||
yauzl "^2.10.0"
|
yauzl "^2.10.0"
|
||||||
|
|
||||||
d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0:
|
d3-selection@^1.4.1:
|
||||||
version "1.2.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"
|
|
||||||
integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==
|
|
||||||
|
|
||||||
d3-axis@1:
|
|
||||||
version "1.0.12"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.12.tgz#cdf20ba210cfbb43795af33756886fb3638daac9"
|
|
||||||
integrity sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==
|
|
||||||
|
|
||||||
d3-brush@1:
|
|
||||||
version "1.1.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.1.6.tgz#b0a22c7372cabec128bdddf9bddc058592f89e9b"
|
|
||||||
integrity sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==
|
|
||||||
dependencies:
|
|
||||||
d3-dispatch "1"
|
|
||||||
d3-drag "1"
|
|
||||||
d3-interpolate "1"
|
|
||||||
d3-selection "1"
|
|
||||||
d3-transition "1"
|
|
||||||
|
|
||||||
d3-chord@1:
|
|
||||||
version "1.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.6.tgz#309157e3f2db2c752f0280fedd35f2067ccbb15f"
|
|
||||||
integrity sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==
|
|
||||||
dependencies:
|
|
||||||
d3-array "1"
|
|
||||||
d3-path "1"
|
|
||||||
|
|
||||||
d3-collection@1:
|
|
||||||
version "1.0.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e"
|
|
||||||
integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==
|
|
||||||
|
|
||||||
d3-color@1:
|
|
||||||
version "1.4.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a"
|
|
||||||
integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==
|
|
||||||
|
|
||||||
d3-contour@1:
|
|
||||||
version "1.3.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-1.3.2.tgz#652aacd500d2264cb3423cee10db69f6f59bead3"
|
|
||||||
integrity sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==
|
|
||||||
dependencies:
|
|
||||||
d3-array "^1.1.1"
|
|
||||||
|
|
||||||
d3-dispatch@1:
|
|
||||||
version "1.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58"
|
|
||||||
integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==
|
|
||||||
|
|
||||||
d3-drag@1:
|
|
||||||
version "1.2.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.5.tgz#2537f451acd39d31406677b7dc77c82f7d988f70"
|
|
||||||
integrity sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==
|
|
||||||
dependencies:
|
|
||||||
d3-dispatch "1"
|
|
||||||
d3-selection "1"
|
|
||||||
|
|
||||||
d3-dsv@1:
|
|
||||||
version "1.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.2.0.tgz#9d5f75c3a5f8abd611f74d3f5847b0d4338b885c"
|
|
||||||
integrity sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==
|
|
||||||
dependencies:
|
|
||||||
commander "2"
|
|
||||||
iconv-lite "0.4"
|
|
||||||
rw "1"
|
|
||||||
|
|
||||||
d3-ease@1:
|
|
||||||
version "1.0.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.7.tgz#9a834890ef8b8ae8c558b2fe55bd57f5993b85e2"
|
|
||||||
integrity sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==
|
|
||||||
|
|
||||||
d3-fetch@1:
|
|
||||||
version "1.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-1.2.0.tgz#15ce2ecfc41b092b1db50abd2c552c2316cf7fc7"
|
|
||||||
integrity sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==
|
|
||||||
dependencies:
|
|
||||||
d3-dsv "1"
|
|
||||||
|
|
||||||
d3-force@1:
|
|
||||||
version "1.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.2.1.tgz#fd29a5d1ff181c9e7f0669e4bd72bdb0e914ec0b"
|
|
||||||
integrity sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==
|
|
||||||
dependencies:
|
|
||||||
d3-collection "1"
|
|
||||||
d3-dispatch "1"
|
|
||||||
d3-quadtree "1"
|
|
||||||
d3-timer "1"
|
|
||||||
|
|
||||||
d3-format@1:
|
|
||||||
version "1.4.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4"
|
|
||||||
integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==
|
|
||||||
|
|
||||||
d3-geo@1:
|
|
||||||
version "1.12.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.12.1.tgz#7fc2ab7414b72e59fbcbd603e80d9adc029b035f"
|
|
||||||
integrity sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==
|
|
||||||
dependencies:
|
|
||||||
d3-array "1"
|
|
||||||
|
|
||||||
d3-hierarchy@1:
|
|
||||||
version "1.1.9"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83"
|
|
||||||
integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==
|
|
||||||
|
|
||||||
d3-interpolate@1:
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987"
|
|
||||||
integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==
|
|
||||||
dependencies:
|
|
||||||
d3-color "1"
|
|
||||||
|
|
||||||
d3-path@1:
|
|
||||||
version "1.0.9"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf"
|
|
||||||
integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==
|
|
||||||
|
|
||||||
d3-polygon@1:
|
|
||||||
version "1.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.6.tgz#0bf8cb8180a6dc107f518ddf7975e12abbfbd38e"
|
|
||||||
integrity sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==
|
|
||||||
|
|
||||||
d3-quadtree@1:
|
|
||||||
version "1.0.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.7.tgz#ca8b84df7bb53763fe3c2f24bd435137f4e53135"
|
|
||||||
integrity sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==
|
|
||||||
|
|
||||||
d3-random@1:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.2.tgz#2833be7c124360bf9e2d3fd4f33847cfe6cab291"
|
|
||||||
integrity sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==
|
|
||||||
|
|
||||||
d3-scale-chromatic@1:
|
|
||||||
version "1.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz#54e333fc78212f439b14641fb55801dd81135a98"
|
|
||||||
integrity sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==
|
|
||||||
dependencies:
|
|
||||||
d3-color "1"
|
|
||||||
d3-interpolate "1"
|
|
||||||
|
|
||||||
d3-scale@2:
|
|
||||||
version "2.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f"
|
|
||||||
integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==
|
|
||||||
dependencies:
|
|
||||||
d3-array "^1.2.0"
|
|
||||||
d3-collection "1"
|
|
||||||
d3-format "1"
|
|
||||||
d3-interpolate "1"
|
|
||||||
d3-time "1"
|
|
||||||
d3-time-format "2"
|
|
||||||
|
|
||||||
d3-selection@1, d3-selection@^1.1.0, d3-selection@^1.4.1:
|
|
||||||
version "1.4.2"
|
version "1.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.2.tgz#dcaa49522c0dbf32d6c1858afc26b6094555bc5c"
|
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.2.tgz#dcaa49522c0dbf32d6c1858afc26b6094555bc5c"
|
||||||
integrity sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==
|
integrity sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==
|
||||||
|
|
||||||
d3-shape@1:
|
|
||||||
version "1.3.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7"
|
|
||||||
integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==
|
|
||||||
dependencies:
|
|
||||||
d3-path "1"
|
|
||||||
|
|
||||||
d3-time-format@2:
|
|
||||||
version "2.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.3.0.tgz#107bdc028667788a8924ba040faf1fbccd5a7850"
|
|
||||||
integrity sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==
|
|
||||||
dependencies:
|
|
||||||
d3-time "1"
|
|
||||||
|
|
||||||
d3-time@1:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1"
|
|
||||||
integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==
|
|
||||||
|
|
||||||
d3-timer@1:
|
|
||||||
version "1.0.10"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5"
|
|
||||||
integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==
|
|
||||||
|
|
||||||
d3-transition@1:
|
|
||||||
version "1.3.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.3.2.tgz#a98ef2151be8d8600543434c1ca80140ae23b398"
|
|
||||||
integrity sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==
|
|
||||||
dependencies:
|
|
||||||
d3-color "1"
|
|
||||||
d3-dispatch "1"
|
|
||||||
d3-ease "1"
|
|
||||||
d3-interpolate "1"
|
|
||||||
d3-selection "^1.1.0"
|
|
||||||
d3-timer "1"
|
|
||||||
|
|
||||||
d3-voronoi@1:
|
|
||||||
version "1.1.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297"
|
|
||||||
integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==
|
|
||||||
|
|
||||||
d3-zoom@1:
|
|
||||||
version "1.8.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.8.3.tgz#b6a3dbe738c7763121cd05b8a7795ffe17f4fc0a"
|
|
||||||
integrity sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==
|
|
||||||
dependencies:
|
|
||||||
d3-dispatch "1"
|
|
||||||
d3-drag "1"
|
|
||||||
d3-interpolate "1"
|
|
||||||
d3-selection "1"
|
|
||||||
d3-transition "1"
|
|
||||||
|
|
||||||
d3@^5.16.0:
|
|
||||||
version "5.16.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/d3/-/d3-5.16.0.tgz#9c5e8d3b56403c79d4ed42fbd62f6113f199c877"
|
|
||||||
integrity sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==
|
|
||||||
dependencies:
|
|
||||||
d3-array "1"
|
|
||||||
d3-axis "1"
|
|
||||||
d3-brush "1"
|
|
||||||
d3-chord "1"
|
|
||||||
d3-collection "1"
|
|
||||||
d3-color "1"
|
|
||||||
d3-contour "1"
|
|
||||||
d3-dispatch "1"
|
|
||||||
d3-drag "1"
|
|
||||||
d3-dsv "1"
|
|
||||||
d3-ease "1"
|
|
||||||
d3-fetch "1"
|
|
||||||
d3-force "1"
|
|
||||||
d3-format "1"
|
|
||||||
d3-geo "1"
|
|
||||||
d3-hierarchy "1"
|
|
||||||
d3-interpolate "1"
|
|
||||||
d3-path "1"
|
|
||||||
d3-polygon "1"
|
|
||||||
d3-quadtree "1"
|
|
||||||
d3-random "1"
|
|
||||||
d3-scale "2"
|
|
||||||
d3-scale-chromatic "1"
|
|
||||||
d3-selection "1"
|
|
||||||
d3-shape "1"
|
|
||||||
d3-time "1"
|
|
||||||
d3-time-format "2"
|
|
||||||
d3-timer "1"
|
|
||||||
d3-transition "1"
|
|
||||||
d3-voronoi "1"
|
|
||||||
d3-zoom "1"
|
|
||||||
|
|
||||||
dashdash@^1.12.0:
|
dashdash@^1.12.0:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
||||||
|
@ -2995,7 +2738,7 @@ dotenv@^8.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
||||||
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
|
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
|
||||||
|
|
||||||
downloadjs@^1.4.7:
|
downloadjs@1.4.7:
|
||||||
version "1.4.7"
|
version "1.4.7"
|
||||||
resolved "https://registry.yarnpkg.com/downloadjs/-/downloadjs-1.4.7.tgz#f69f96f940e0d0553dac291139865a3cd0101e3c"
|
resolved "https://registry.yarnpkg.com/downloadjs/-/downloadjs-1.4.7.tgz#f69f96f940e0d0553dac291139865a3cd0101e3c"
|
||||||
integrity sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=
|
integrity sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=
|
||||||
|
@ -3842,7 +3585,7 @@ human-signals@^1.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
|
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
|
||||||
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
|
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
|
||||||
|
|
||||||
iconv-lite@0.4, iconv-lite@0.4.24:
|
iconv-lite@0.4.24:
|
||||||
version "0.4.24"
|
version "0.4.24"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||||
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
||||||
|
@ -5047,11 +4790,6 @@ lodash-es@^4.17.11:
|
||||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
|
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
|
||||||
integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
|
integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
|
||||||
|
|
||||||
lodash.assign@^4.2.0:
|
|
||||||
version "4.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
|
|
||||||
integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=
|
|
||||||
|
|
||||||
lodash.once@^4.1.1:
|
lodash.once@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||||
|
@ -5062,7 +4800,12 @@ lodash.sortby@^4.7.0:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||||
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
||||||
|
|
||||||
lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19:
|
lodash@4.17.13:
|
||||||
|
version "4.17.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.13.tgz#0bdc3a6adc873d2f4e0c4bac285df91b64fc7b93"
|
||||||
|
integrity sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA==
|
||||||
|
|
||||||
|
lodash@^4.17.15, lodash@^4.17.19:
|
||||||
version "4.17.20"
|
version "4.17.20"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
|
||||||
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
|
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
|
||||||
|
@ -6135,7 +5878,7 @@ relateurl@0.2.x:
|
||||||
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
|
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
|
||||||
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
|
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
|
||||||
|
|
||||||
remixicon@^2.5.0:
|
remixicon@2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/remixicon/-/remixicon-2.5.0.tgz#b5e245894a1550aa23793f95daceadbf96ad1a41"
|
resolved "https://registry.yarnpkg.com/remixicon/-/remixicon-2.5.0.tgz#b5e245894a1550aa23793f95daceadbf96ad1a41"
|
||||||
integrity sha512-q54ra2QutYDZpuSnFjmeagmEiN9IMo56/zz5dDNitzKD23oFRw77cWo4TsrAdmdkPiEn8mxlrTqxnkujDbEGww==
|
integrity sha512-q54ra2QutYDZpuSnFjmeagmEiN9IMo56/zz5dDNitzKD23oFRw77cWo4TsrAdmdkPiEn8mxlrTqxnkujDbEGww==
|
||||||
|
@ -6436,11 +6179,6 @@ run-parallel@^1.1.9:
|
||||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef"
|
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef"
|
||||||
integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==
|
integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==
|
||||||
|
|
||||||
rw@1:
|
|
||||||
version "1.3.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
|
|
||||||
integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=
|
|
||||||
|
|
||||||
rxjs@^6.3.3, rxjs@^6.5.5:
|
rxjs@^6.3.3, rxjs@^6.5.5:
|
||||||
version "6.6.3"
|
version "6.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"
|
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"
|
||||||
|
@ -6583,10 +6321,10 @@ shellwords@^0.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||||
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
|
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
|
||||||
|
|
||||||
shortid@^2.2.15:
|
shortid@2.2.15:
|
||||||
version "2.2.16"
|
version "2.2.15"
|
||||||
resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.16.tgz#b742b8f0cb96406fd391c76bfc18a67a57fe5608"
|
resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.15.tgz#2b902eaa93a69b11120373cd42a1f1fe4437c122"
|
||||||
integrity sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==
|
integrity sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw==
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid "^2.1.0"
|
nanoid "^2.1.0"
|
||||||
|
|
||||||
|
@ -6996,7 +6734,7 @@ svelte-loading-spinners@^0.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/svelte-loading-spinners/-/svelte-loading-spinners-0.1.1.tgz#a35a811b7db0389ec2a5de6904c718c58c36e1f9"
|
resolved "https://registry.yarnpkg.com/svelte-loading-spinners/-/svelte-loading-spinners-0.1.1.tgz#a35a811b7db0389ec2a5de6904c718c58c36e1f9"
|
||||||
integrity sha512-or4zs10VOdczOJo3u25IINXQOkZbLNAxMrXK0PRbzVoJtPQq/QZPNxI32383bpe+soYcEKmESbmW+JlW3MbUKQ==
|
integrity sha512-or4zs10VOdczOJo3u25IINXQOkZbLNAxMrXK0PRbzVoJtPQq/QZPNxI32383bpe+soYcEKmESbmW+JlW3MbUKQ==
|
||||||
|
|
||||||
svelte-portal@^0.1.0:
|
svelte-portal@0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/svelte-portal/-/svelte-portal-0.1.0.tgz#cc2821cc84b05ed5814e0218dcdfcbebc53c1742"
|
resolved "https://registry.yarnpkg.com/svelte-portal/-/svelte-portal-0.1.0.tgz#cc2821cc84b05ed5814e0218dcdfcbebc53c1742"
|
||||||
integrity sha512-kef+ksXVKun224mRxat+DdO4C+cGHla+fEcZfnBAvoZocwiaceOfhf5azHYOPXSSB1igWVFTEOF3CDENPnuWxg==
|
integrity sha512-kef+ksXVKun224mRxat+DdO4C+cGHla+fEcZfnBAvoZocwiaceOfhf5azHYOPXSSB1igWVFTEOF3CDENPnuWxg==
|
||||||
|
@ -7317,16 +7055,16 @@ util.promisify@^1.0.0:
|
||||||
has-symbols "^1.0.1"
|
has-symbols "^1.0.1"
|
||||||
object.getownpropertydescriptors "^2.1.0"
|
object.getownpropertydescriptors "^2.1.0"
|
||||||
|
|
||||||
|
uuid@8.3.1:
|
||||||
|
version "8.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31"
|
||||||
|
integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==
|
||||||
|
|
||||||
uuid@^3.3.2:
|
uuid@^3.3.2:
|
||||||
version "3.4.0"
|
version "3.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||||
|
|
||||||
uuid@^8.3.1:
|
|
||||||
version "8.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31"
|
|
||||||
integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==
|
|
||||||
|
|
||||||
validate-npm-package-license@^3.0.1:
|
validate-npm-package-license@^3.0.1:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
||||||
|
@ -7586,10 +7324,10 @@ yauzl@^2.10.0:
|
||||||
buffer-crc32 "~0.2.3"
|
buffer-crc32 "~0.2.3"
|
||||||
fd-slicer "~1.1.0"
|
fd-slicer "~1.1.0"
|
||||||
|
|
||||||
yup@^0.29.2:
|
yup@0.29.2:
|
||||||
version "0.29.3"
|
version "0.29.2"
|
||||||
resolved "https://registry.yarnpkg.com/yup/-/yup-0.29.3.tgz#69a30fd3f1c19f5d9e31b1cf1c2b851ce8045fea"
|
resolved "https://registry.yarnpkg.com/yup/-/yup-0.29.2.tgz#5302abd9024cca335b987793f8df868e410b7b67"
|
||||||
integrity sha512-RNUGiZ/sQ37CkhzKFoedkeMfJM0vNQyaz+wRZJzxdKE7VfDeVKH8bb4rr7XhRLbHJz5hSjoDNwMEIaKhuMZ8gQ==
|
integrity sha512-FbAAeopli+TnpZ8Lzv2M72wltLw58iWBT7wW8FuAPFPb3CelXmSKCXQbV1o4keywpIK1BZ0ULTLv2s3w1CfOwA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.10.5"
|
"@babel/runtime" "^7.10.5"
|
||||||
fn-name "~3.0.0"
|
fn-name "~3.0.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "0.7.1",
|
"version": "0.7.4",
|
||||||
"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.1",
|
"@budibase/string-templates": "^0.7.4",
|
||||||
"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.1",
|
"@budibase/standard-components": "^0.7.4",
|
||||||
"@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",
|
||||||
|
@ -30,5 +30,5 @@
|
||||||
"svelte": "^3.30.0",
|
"svelte": "^3.30.0",
|
||||||
"svelte-jester": "^1.0.6"
|
"svelte-jester": "^1.0.6"
|
||||||
},
|
},
|
||||||
"gitHead": "62ebf3cedcd7e9b2494b4f8cbcfb90927609b491"
|
"gitHead": "1a80b09fd093f2599a68f7db72ad639dd50922dd"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { notificationStore } from "../store/notification"
|
import { notificationStore, datasourceStore } from "../store"
|
||||||
import API from "./api"
|
import API from "./api"
|
||||||
import { fetchTableDefinition } from "./tables"
|
import { fetchTableDefinition } from "./tables"
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@ import { fetchTableDefinition } from "./tables"
|
||||||
* Fetches data about a certain row in a table.
|
* Fetches data about a certain row in a table.
|
||||||
*/
|
*/
|
||||||
export const fetchRow = async ({ tableId, rowId }) => {
|
export const fetchRow = async ({ tableId, rowId }) => {
|
||||||
|
if (!tableId || !rowId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const row = await API.get({
|
const row = await API.get({
|
||||||
url: `/api/${tableId}/rows/${rowId}`,
|
url: `/api/${tableId}/rows/${rowId}`,
|
||||||
})
|
})
|
||||||
|
@ -16,6 +19,9 @@ export const fetchRow = async ({ tableId, rowId }) => {
|
||||||
* Creates a row in a table.
|
* Creates a row in a table.
|
||||||
*/
|
*/
|
||||||
export const saveRow = async row => {
|
export const saveRow = async row => {
|
||||||
|
if (!row?.tableId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const res = await API.post({
|
const res = await API.post({
|
||||||
url: `/api/${row.tableId}/rows`,
|
url: `/api/${row.tableId}/rows`,
|
||||||
body: row,
|
body: row,
|
||||||
|
@ -23,6 +29,10 @@ export const saveRow = async row => {
|
||||||
res.error
|
res.error
|
||||||
? notificationStore.danger("An error has occurred")
|
? notificationStore.danger("An error has occurred")
|
||||||
: notificationStore.success("Row saved")
|
: notificationStore.success("Row saved")
|
||||||
|
|
||||||
|
// Refresh related datasources
|
||||||
|
datasourceStore.actions.invalidateDatasource(row.tableId)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +40,9 @@ export const saveRow = async row => {
|
||||||
* Updates a row in a table.
|
* Updates a row in a table.
|
||||||
*/
|
*/
|
||||||
export const updateRow = async row => {
|
export const updateRow = async row => {
|
||||||
|
if (!row?.tableId || !row?._id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const res = await API.patch({
|
const res = await API.patch({
|
||||||
url: `/api/${row.tableId}/rows/${row._id}`,
|
url: `/api/${row.tableId}/rows/${row._id}`,
|
||||||
body: row,
|
body: row,
|
||||||
|
@ -37,6 +50,10 @@ export const updateRow = async row => {
|
||||||
res.error
|
res.error
|
||||||
? notificationStore.danger("An error has occurred")
|
? notificationStore.danger("An error has occurred")
|
||||||
: notificationStore.success("Row updated")
|
: notificationStore.success("Row updated")
|
||||||
|
|
||||||
|
// Refresh related datasources
|
||||||
|
datasourceStore.actions.invalidateDatasource(row.tableId)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,12 +61,19 @@ export const updateRow = async row => {
|
||||||
* Deletes a row from a table.
|
* Deletes a row from a table.
|
||||||
*/
|
*/
|
||||||
export const deleteRow = async ({ tableId, rowId, revId }) => {
|
export const deleteRow = async ({ tableId, rowId, revId }) => {
|
||||||
|
if (!tableId || !rowId || !revId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const res = await API.del({
|
const res = await API.del({
|
||||||
url: `/api/${tableId}/rows/${rowId}/${revId}`,
|
url: `/api/${tableId}/rows/${rowId}/${revId}`,
|
||||||
})
|
})
|
||||||
res.error
|
res.error
|
||||||
? notificationStore.danger("An error has occurred")
|
? notificationStore.danger("An error has occurred")
|
||||||
: notificationStore.success("Row deleted")
|
: notificationStore.success("Row deleted")
|
||||||
|
|
||||||
|
// Refresh related datasources
|
||||||
|
datasourceStore.actions.invalidateDatasource(tableId)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +81,9 @@ export const deleteRow = async ({ tableId, rowId, revId }) => {
|
||||||
* Deletes many rows from a table.
|
* Deletes many rows from a table.
|
||||||
*/
|
*/
|
||||||
export const deleteRows = async ({ tableId, rows }) => {
|
export const deleteRows = async ({ tableId, rows }) => {
|
||||||
|
if (!tableId || !rows) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const res = await API.post({
|
const res = await API.post({
|
||||||
url: `/api/${tableId}/rows`,
|
url: `/api/${tableId}/rows`,
|
||||||
body: {
|
body: {
|
||||||
|
@ -67,6 +94,10 @@ export const deleteRows = async ({ tableId, rows }) => {
|
||||||
res.error
|
res.error
|
||||||
? notificationStore.danger("An error has occurred")
|
? notificationStore.danger("An error has occurred")
|
||||||
: notificationStore.success(`${rows.length} row(s) deleted`)
|
: notificationStore.success(`${rows.length} row(s) deleted`)
|
||||||
|
|
||||||
|
// Refresh related datasources
|
||||||
|
datasourceStore.actions.invalidateDatasource(tableId)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import { setContext, onMount } from "svelte"
|
import { setContext, onMount } from "svelte"
|
||||||
import Component from "./Component.svelte"
|
import Component from "./Component.svelte"
|
||||||
import NotificationDisplay from "./NotificationDisplay.svelte"
|
import NotificationDisplay from "./NotificationDisplay.svelte"
|
||||||
|
import Provider from "./Provider.svelte"
|
||||||
import SDK from "../sdk"
|
import SDK from "../sdk"
|
||||||
import {
|
import {
|
||||||
createContextStore,
|
createContextStore,
|
||||||
|
@ -27,6 +28,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if loaded && $screenStore.activeLayout}
|
{#if loaded && $screenStore.activeLayout}
|
||||||
<Component definition={$screenStore.activeLayout.props} />
|
<Provider key="user" data={$authStore}>
|
||||||
<NotificationDisplay />
|
<Component definition={$screenStore.activeLayout.props} />
|
||||||
|
<NotificationDisplay />
|
||||||
|
</Provider>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import * as ComponentLibrary from "@budibase/standard-components"
|
import * as ComponentLibrary from "@budibase/standard-components"
|
||||||
import Router from "./Router.svelte"
|
import Router from "./Router.svelte"
|
||||||
import { enrichProps, propsAreSame } from "../utils/componentProps"
|
import { enrichProps, propsAreSame } from "../utils/componentProps"
|
||||||
import { authStore, builderStore } from "../store"
|
import { builderStore } from "../store"
|
||||||
import { hashString } from "../utils/hash"
|
import { hashString } from "../utils/hash"
|
||||||
|
|
||||||
export let definition = {}
|
export let definition = {}
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
$: constructor = getComponentConstructor(definition._component)
|
$: constructor = getComponentConstructor(definition._component)
|
||||||
$: children = definition._children || []
|
$: children = definition._children || []
|
||||||
$: id = definition._id
|
$: id = definition._id
|
||||||
$: updateComponentProps(definition, $context, $authStore)
|
$: updateComponentProps(definition, $context)
|
||||||
$: styles = definition._styles
|
$: styles = definition._styles
|
||||||
|
|
||||||
// Update component context
|
// Update component context
|
||||||
|
@ -53,13 +53,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enriches any string component props using handlebars
|
// Enriches any string component props using handlebars
|
||||||
const updateComponentProps = async (definition, context, user) => {
|
const updateComponentProps = async (definition, context) => {
|
||||||
// Record the timestamp so we can reference it after enrichment
|
// Record the timestamp so we can reference it after enrichment
|
||||||
latestUpdateTime = Date.now()
|
latestUpdateTime = Date.now()
|
||||||
const enrichmentTime = latestUpdateTime
|
const enrichmentTime = latestUpdateTime
|
||||||
|
|
||||||
// Enrich props with context
|
// Enrich props with context
|
||||||
const enrichedProps = await enrichProps(definition, context, user)
|
const enrichedProps = await enrichProps(definition, context)
|
||||||
|
|
||||||
// Abandon this update if a newer update has started
|
// Abandon this update if a newer update has started
|
||||||
if (enrichmentTime !== latestUpdateTime) {
|
if (enrichmentTime !== latestUpdateTime) {
|
||||||
|
|
|
@ -1,29 +1,54 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, setContext } from "svelte"
|
import { getContext, setContext, onMount } from "svelte"
|
||||||
import { createContextStore } from "../store"
|
import { datasourceStore, createContextStore } from "../store"
|
||||||
|
import { ActionTypes } from "../constants"
|
||||||
|
import { generate } from "shortid"
|
||||||
|
|
||||||
export let data
|
export let data
|
||||||
export let actions
|
export let actions
|
||||||
|
export let key
|
||||||
|
|
||||||
// Clone and create new data context for this component tree
|
// Clone and create new data context for this component tree
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
const newContext = createContextStore($context)
|
const newContext = createContextStore($context)
|
||||||
setContext("context", newContext)
|
setContext("context", newContext)
|
||||||
|
$: providerKey = key || $component.id
|
||||||
|
|
||||||
|
// Instance ID is unique to each instance of a provider
|
||||||
|
let instanceId
|
||||||
|
|
||||||
// Add data context
|
// Add data context
|
||||||
$: {
|
$: data !== undefined && newContext.actions.provideData(providerKey, data)
|
||||||
if (data !== undefined) {
|
|
||||||
newContext.actions.provideData($component.id, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add actions context
|
// Add actions context
|
||||||
$: {
|
$: {
|
||||||
actions?.forEach(({ type, callback }) => {
|
if (instanceId) {
|
||||||
newContext.actions.provideAction($component.id, type, callback)
|
actions?.forEach(({ type, callback, metadata }) => {
|
||||||
})
|
newContext.actions.provideAction(providerKey, type, callback)
|
||||||
|
|
||||||
|
// Register any "refresh datasource" actions with a singleton store
|
||||||
|
// so we can easily refresh data at all levels for any datasource
|
||||||
|
if (type === ActionTypes.RefreshDatasource) {
|
||||||
|
const { datasource } = metadata || {}
|
||||||
|
datasourceStore.actions.registerDatasource(
|
||||||
|
datasource,
|
||||||
|
instanceId,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
// Generate a permanent unique ID for this component and use it to register
|
||||||
|
// any datasource actions
|
||||||
|
instanceId = generate()
|
||||||
|
|
||||||
|
// Unregister all datasource instances when unmounting this provider
|
||||||
|
return () => datasourceStore.actions.unregisterInstance(instanceId)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
@ -4,4 +4,5 @@ export const TableNames = {
|
||||||
|
|
||||||
export const ActionTypes = {
|
export const ActionTypes = {
|
||||||
ValidateForm: "ValidateForm",
|
ValidateForm: "ValidateForm",
|
||||||
|
RefreshDatasource: "RefreshDatasource",
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,26 +4,28 @@ export const createContextStore = existingContext => {
|
||||||
const store = writable({ ...existingContext })
|
const store = writable({ ...existingContext })
|
||||||
|
|
||||||
// Adds a data context layer to the tree
|
// Adds a data context layer to the tree
|
||||||
const provideData = (componentId, data) => {
|
const provideData = (providerId, data) => {
|
||||||
|
if (!providerId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
if (componentId) {
|
state[providerId] = data
|
||||||
state[componentId] = data
|
|
||||||
|
|
||||||
// Keep track of the closest component ID so we can later hydrate a "data" prop.
|
// Keep track of the closest component ID so we can later hydrate a "data" prop.
|
||||||
// This is only required for legacy bindings that used "data" rather than a
|
// This is only required for legacy bindings that used "data" rather than a
|
||||||
// component ID.
|
// component ID.
|
||||||
state.closestComponentId = componentId
|
state.closestComponentId = providerId
|
||||||
}
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds an action context layer to the tree
|
// Adds an action context layer to the tree
|
||||||
const provideAction = (componentId, actionType, callback) => {
|
const provideAction = (providerId, actionType, callback) => {
|
||||||
|
if (!providerId || !actionType) {
|
||||||
|
return
|
||||||
|
}
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
if (actionType && componentId) {
|
state[`${providerId}_${actionType}`] = callback
|
||||||
state[`${componentId}_${actionType}`] = callback
|
|
||||||
}
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { writable, get } from "svelte/store"
|
||||||
|
|
||||||
|
export const createDatasourceStore = () => {
|
||||||
|
const store = writable([])
|
||||||
|
|
||||||
|
// Registers a new datasource instance
|
||||||
|
const registerDatasource = (datasource, instanceId, refresh) => {
|
||||||
|
if (!datasource || !instanceId || !refresh) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a list of all relevant datasource IDs which would require that
|
||||||
|
// this datasource is refreshed
|
||||||
|
let datasourceIds = []
|
||||||
|
|
||||||
|
// Extract table ID
|
||||||
|
if (datasource.type === "table") {
|
||||||
|
if (datasource.tableId) {
|
||||||
|
datasourceIds.push(datasource.tableId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract both table IDs from both sides of the relationship
|
||||||
|
else if (datasource.type === "link") {
|
||||||
|
if (datasource.rowTableId) {
|
||||||
|
datasourceIds.push(datasource.rowTableId)
|
||||||
|
}
|
||||||
|
if (datasource.tableId) {
|
||||||
|
datasourceIds.push(datasource.tableId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the datasource ID (not the query ID) for queries
|
||||||
|
else if (datasource.type === "query") {
|
||||||
|
if (datasource.datasourceId) {
|
||||||
|
datasourceIds.push(datasource.datasourceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store configs for each relevant datasource ID
|
||||||
|
if (datasourceIds.length) {
|
||||||
|
store.update(state => {
|
||||||
|
datasourceIds.forEach(id => {
|
||||||
|
state.push({
|
||||||
|
datasourceId: id,
|
||||||
|
instanceId,
|
||||||
|
refresh,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes all registered datasource instances belonging to a particular
|
||||||
|
// instance ID
|
||||||
|
const unregisterInstance = instanceId => {
|
||||||
|
store.update(state => {
|
||||||
|
return state.filter(instance => instance.instanceId !== instanceId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalidates a specific datasource ID by refreshing all instances
|
||||||
|
// which depend on data from that datasource
|
||||||
|
const invalidateDatasource = datasourceId => {
|
||||||
|
const relatedInstances = get(store).filter(instance => {
|
||||||
|
return instance.datasourceId === datasourceId
|
||||||
|
})
|
||||||
|
relatedInstances?.forEach(instance => {
|
||||||
|
instance.refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe: store.subscribe,
|
||||||
|
actions: { registerDatasource, unregisterInstance, invalidateDatasource },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const datasourceStore = createDatasourceStore()
|
|
@ -3,6 +3,7 @@ export { notificationStore } from "./notification"
|
||||||
export { routeStore } from "./routes"
|
export { routeStore } from "./routes"
|
||||||
export { screenStore } from "./screens"
|
export { screenStore } from "./screens"
|
||||||
export { builderStore } from "./builder"
|
export { builderStore } from "./builder"
|
||||||
|
export { datasourceStore } from "./datasource"
|
||||||
|
|
||||||
// Context stores are layered and duplicated, so it is not a singleton
|
// Context stores are layered and duplicated, so it is not a singleton
|
||||||
export { createContextStore } from "./context"
|
export { createContextStore } from "./context"
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { enrichDataBinding, enrichDataBindings } from "./enrichDataBinding"
|
|
||||||
import { routeStore, builderStore } from "../store"
|
import { routeStore, builderStore } from "../store"
|
||||||
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
|
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
|
||||||
import { ActionTypes } from "../constants"
|
import { ActionTypes } from "../constants"
|
||||||
|
@ -10,35 +9,26 @@ const saveRowHandler = async (action, context) => {
|
||||||
let draft = context[providerId]
|
let draft = context[providerId]
|
||||||
if (fields) {
|
if (fields) {
|
||||||
for (let [key, entry] of Object.entries(fields)) {
|
for (let [key, entry] of Object.entries(fields)) {
|
||||||
draft[key] = await enrichDataBinding(entry.value, context)
|
draft[key] = entry.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await saveRow(draft)
|
await saveRow(draft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteRowHandler = async (action, context) => {
|
const deleteRowHandler = async action => {
|
||||||
const { tableId, revId, rowId } = action.parameters
|
const { tableId, revId, rowId } = action.parameters
|
||||||
if (tableId && revId && rowId) {
|
if (tableId && revId && rowId) {
|
||||||
const [enrichTable, enrichRow, enrichRev] = await Promise.all([
|
await deleteRow({ tableId, rowId, revId })
|
||||||
enrichDataBinding(tableId, context),
|
|
||||||
enrichDataBinding(rowId, context),
|
|
||||||
enrichDataBinding(revId, context),
|
|
||||||
])
|
|
||||||
await deleteRow({
|
|
||||||
tableId: enrichTable,
|
|
||||||
rowId: enrichRow,
|
|
||||||
revId: enrichRev,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const triggerAutomationHandler = async (action, context) => {
|
const triggerAutomationHandler = async action => {
|
||||||
const { fields } = action.parameters()
|
const { fields } = action.parameters
|
||||||
if (fields) {
|
if (fields) {
|
||||||
const params = {}
|
const params = {}
|
||||||
for (let field in fields) {
|
for (let field in fields) {
|
||||||
params[field] = await enrichDataBinding(fields[field].value, context)
|
params[field] = fields[field].value
|
||||||
}
|
}
|
||||||
await triggerAutomation(action.parameters.automationId, params)
|
await triggerAutomation(action.parameters.automationId, params)
|
||||||
}
|
}
|
||||||
|
@ -60,14 +50,29 @@ const queryExecutionHandler = async action => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const validateFormHandler = async (action, context) => {
|
const executeActionHandler = async (context, componentId, actionType) => {
|
||||||
const { componentId } = action.parameters
|
const fn = context[`${componentId}_${actionType}`]
|
||||||
const fn = context[`${componentId}_${ActionTypes.ValidateForm}`]
|
|
||||||
if (fn) {
|
if (fn) {
|
||||||
return await fn()
|
return await fn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validateFormHandler = async (action, context) => {
|
||||||
|
return await executeActionHandler(
|
||||||
|
context,
|
||||||
|
action.parameters.componentId,
|
||||||
|
ActionTypes.ValidateForm
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshDatasourceHandler = async (action, context) => {
|
||||||
|
return await executeActionHandler(
|
||||||
|
context,
|
||||||
|
action.parameters.componentId,
|
||||||
|
ActionTypes.RefreshDatasource
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const handlerMap = {
|
const handlerMap = {
|
||||||
["Save Row"]: saveRowHandler,
|
["Save Row"]: saveRowHandler,
|
||||||
["Delete Row"]: deleteRowHandler,
|
["Delete Row"]: deleteRowHandler,
|
||||||
|
@ -75,6 +80,7 @@ const handlerMap = {
|
||||||
["Execute Query"]: queryExecutionHandler,
|
["Execute Query"]: queryExecutionHandler,
|
||||||
["Trigger Automation"]: triggerAutomationHandler,
|
["Trigger Automation"]: triggerAutomationHandler,
|
||||||
["Validate Form"]: validateFormHandler,
|
["Validate Form"]: validateFormHandler,
|
||||||
|
["Refresh Datasource"]: refreshDatasourceHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const propsAreSame = (a, b) => {
|
||||||
* Enriches component props.
|
* Enriches component props.
|
||||||
* Data bindings are enriched, and button actions are enriched.
|
* Data bindings are enriched, and button actions are enriched.
|
||||||
*/
|
*/
|
||||||
export const enrichProps = async (props, context, user) => {
|
export const enrichProps = async (props, context) => {
|
||||||
// Exclude all private props that start with an underscore
|
// Exclude all private props that start with an underscore
|
||||||
let validProps = {}
|
let validProps = {}
|
||||||
Object.entries(props)
|
Object.entries(props)
|
||||||
|
@ -34,7 +34,6 @@ export const enrichProps = async (props, context, user) => {
|
||||||
// Duplicate the closest context as "data" which the builder requires
|
// Duplicate the closest context as "data" which the builder requires
|
||||||
const totalContext = {
|
const totalContext = {
|
||||||
...context,
|
...context,
|
||||||
user,
|
|
||||||
|
|
||||||
// This is only required for legacy bindings that used "data" rather than a
|
// This is only required for legacy bindings that used "data" rather than a
|
||||||
// component ID.
|
// component ID.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.7.1",
|
"version": "0.7.4",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/electron.js",
|
"main": "src/electron.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -50,58 +50,58 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/client": "^0.7.1",
|
"@budibase/client": "^0.7.4",
|
||||||
"@budibase/string-templates": "^0.7.1",
|
"@budibase/string-templates": "^0.7.4",
|
||||||
"@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",
|
||||||
"@sentry/node": "^5.19.2",
|
"@sentry/node": "5.19.2",
|
||||||
"airtable": "^0.10.1",
|
"airtable": "0.10.1",
|
||||||
"arangojs": "^7.2.0",
|
"arangojs": "7.2.0",
|
||||||
"aws-sdk": "^2.767.0",
|
"aws-sdk": "^2.767.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"chmodr": "^1.2.0",
|
"chmodr": "1.2.0",
|
||||||
"csvtojson": "^2.0.10",
|
"csvtojson": "2.0.10",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "8.2.0",
|
||||||
"download": "^8.0.0",
|
"download": "8.0.0",
|
||||||
"electron-is-dev": "^1.2.0",
|
"electron-is-dev": "1.2.0",
|
||||||
"electron-unhandled": "^3.0.2",
|
"electron-unhandled": "3.0.2",
|
||||||
"electron-updater": "^4.3.1",
|
"electron-updater": "4.3.1",
|
||||||
"electron-util": "^0.14.2",
|
"electron-util": "0.14.2",
|
||||||
"fix-path": "^3.0.0",
|
"fix-path": "3.0.0",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "8.1.0",
|
||||||
"jimp": "^0.16.1",
|
"jimp": "0.16.1",
|
||||||
"joi": "^17.2.1",
|
"joi": "17.2.1",
|
||||||
"jsonschema": "^1.4.0",
|
"jsonschema": "1.4.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "8.5.1",
|
||||||
"koa": "^2.7.0",
|
"koa": "2.7.0",
|
||||||
"koa-body": "^4.2.0",
|
"koa-body": "4.2.0",
|
||||||
"koa-compress": "^4.0.1",
|
"koa-compress": "4.0.1",
|
||||||
"koa-pino-logger": "^3.0.0",
|
"koa-pino-logger": "3.0.0",
|
||||||
"koa-send": "^5.0.0",
|
"koa-send": "5.0.0",
|
||||||
"koa-session": "^5.12.0",
|
"koa-session": "5.12.0",
|
||||||
"koa-static": "^5.0.0",
|
"koa-static": "5.0.0",
|
||||||
"lodash": "^4.17.13",
|
"lodash": "4.17.13",
|
||||||
"mongodb": "^3.6.3",
|
"mongodb": "3.6.3",
|
||||||
"mssql": "^6.2.3",
|
"mssql": "6.2.3",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "2.18.1",
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "2.6.0",
|
||||||
"open": "^7.3.0",
|
"open": "7.3.0",
|
||||||
"pg": "^8.5.1",
|
"pg": "8.5.1",
|
||||||
"pino-pretty": "^4.0.0",
|
"pino-pretty": "4.0.0",
|
||||||
"pouchdb": "^7.2.1",
|
"pouchdb": "7.2.1",
|
||||||
"pouchdb-all-dbs": "^1.0.2",
|
"pouchdb-all-dbs": "1.0.2",
|
||||||
"pouchdb-replication-stream": "^1.2.9",
|
"pouchdb-replication-stream": "1.2.9",
|
||||||
"sanitize-s3-objectkey": "^0.0.1",
|
"sanitize-s3-objectkey": "0.0.1",
|
||||||
"server-destroy": "^1.0.1",
|
"server-destroy": "1.0.1",
|
||||||
"svelte": "^3.30.0",
|
"svelte": "3.30.0",
|
||||||
"tar-fs": "^2.1.0",
|
"tar-fs": "2.1.0",
|
||||||
"to-json-schema": "^0.2.5",
|
"to-json-schema": "0.2.5",
|
||||||
"uuid": "^3.3.2",
|
"uuid": "3.3.2",
|
||||||
"validate.js": "^0.13.1",
|
"validate.js": "0.13.1",
|
||||||
"worker-farm": "^1.7.0",
|
"worker-farm": "1.7.0",
|
||||||
"yargs": "^13.2.4",
|
"yargs": "13.2.4",
|
||||||
"zlib": "^1.0.5"
|
"zlib": "1.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/test-sequencer": "^24.8.0",
|
"@jest/test-sequencer": "^24.8.0",
|
||||||
|
@ -120,5 +120,5 @@
|
||||||
"./scripts/jestSetup.js"
|
"./scripts/jestSetup.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gitHead": "62ebf3cedcd7e9b2494b4f8cbcfb90927609b491"
|
"gitHead": "1a80b09fd093f2599a68f7db72ad639dd50922dd"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,12 @@ const fs = require("fs-extra")
|
||||||
exports.exportAppDump = async function(ctx) {
|
exports.exportAppDump = async function(ctx) {
|
||||||
const { appId } = ctx.query
|
const { appId } = ctx.query
|
||||||
|
|
||||||
|
const appname = decodeURI(ctx.query.appname)
|
||||||
|
|
||||||
const backupsDir = path.join(os.homedir(), ".budibase", "backups")
|
const backupsDir = path.join(os.homedir(), ".budibase", "backups")
|
||||||
fs.ensureDirSync(backupsDir)
|
fs.ensureDirSync(backupsDir)
|
||||||
|
|
||||||
const backupIdentifier = `${appId} Backup: ${new Date()}.txt`
|
const backupIdentifier = `${appname}Backup${new Date().getTime()}.txt`
|
||||||
|
|
||||||
await performDump({
|
await performDump({
|
||||||
dir: backupsDir,
|
dir: backupsDir,
|
||||||
|
@ -23,19 +25,4 @@ exports.exportAppDump = async function(ctx) {
|
||||||
|
|
||||||
ctx.attachment(backupIdentifier)
|
ctx.attachment(backupIdentifier)
|
||||||
ctx.body = fs.createReadStream(backupFile)
|
ctx.body = fs.createReadStream(backupFile)
|
||||||
// ctx.body = {
|
|
||||||
// url: `/api/backups/download/${backupIdentifier}`,
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// exports.downloadAppDump = async function(ctx) {
|
|
||||||
// const fileName = ctx.params.fileName
|
|
||||||
|
|
||||||
// const backupsDir = path.join(os.homedir(), ".budibase", "backups")
|
|
||||||
// fs.ensureDirSync(backupsDir)
|
|
||||||
|
|
||||||
// const backupFile = path.join(backupsDir, fileName)
|
|
||||||
|
|
||||||
// ctx.attachment(fileName)
|
|
||||||
// ctx.body = fs.createReadStream(backupFile)
|
|
||||||
// }
|
|
||||||
|
|
|
@ -83,23 +83,42 @@ const controller = {
|
||||||
ctx.message = `View ${ctx.params.viewName} saved successfully.`
|
ctx.message = `View ${ctx.params.viewName} saved successfully.`
|
||||||
},
|
},
|
||||||
exportView: async ctx => {
|
exportView: async ctx => {
|
||||||
const view = ctx.query.view
|
const db = new CouchDB(ctx.user.appId)
|
||||||
|
const designDoc = await db.get("_design/database")
|
||||||
|
|
||||||
|
const viewName = decodeURI(ctx.query.view)
|
||||||
|
|
||||||
|
const view = designDoc.views[viewName]
|
||||||
const format = ctx.query.format
|
const format = ctx.query.format
|
||||||
|
|
||||||
// Fetch view rows
|
if (view) {
|
||||||
ctx.params.viewName = view.name
|
ctx.params.viewName = viewName
|
||||||
ctx.query.group = view.groupBy
|
// Fetch view rows
|
||||||
if (view.field) {
|
ctx.query = {
|
||||||
ctx.query.stats = true
|
group: view.meta.groupBy,
|
||||||
ctx.query.field = view.field
|
calculation: view.meta.calculation,
|
||||||
|
stats: !!view.meta.field,
|
||||||
|
field: view.meta.field,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// table all_ view
|
||||||
|
ctx.params.viewName = viewName
|
||||||
}
|
}
|
||||||
|
|
||||||
await fetchView(ctx)
|
await fetchView(ctx)
|
||||||
|
|
||||||
|
let schema = view && view.meta && view.meta.schema
|
||||||
|
if (!schema) {
|
||||||
|
const tableId = ctx.params.tableId || view.meta.tableId
|
||||||
|
const table = await db.get(tableId)
|
||||||
|
schema = table.schema
|
||||||
|
}
|
||||||
|
|
||||||
// Export part
|
// Export part
|
||||||
let headers = Object.keys(view.schema)
|
let headers = Object.keys(schema)
|
||||||
const exporter = exporters[format]
|
const exporter = exporters[format]
|
||||||
const exportedFile = exporter(headers, ctx.body)
|
const exportedFile = exporter(headers, ctx.body)
|
||||||
const filename = `${view.name}.${format}`
|
const filename = `${viewName}.${format}`
|
||||||
fs.writeFileSync(join(os.tmpdir(), filename), exportedFile)
|
fs.writeFileSync(join(os.tmpdir(), filename), exportedFile)
|
||||||
|
|
||||||
ctx.attachment(filename)
|
ctx.attachment(filename)
|
||||||
|
|
|
@ -6,10 +6,5 @@ const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router.get("/api/backups/export", authorized(BUILDER), controller.exportAppDump)
|
router.get("/api/backups/export", authorized(BUILDER), controller.exportAppDump)
|
||||||
// .get(
|
|
||||||
// "/api/backups/download/:fileName",
|
|
||||||
// authorized(BUILDER),
|
|
||||||
// controller.downloadAppDump
|
|
||||||
// )
|
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -12,6 +12,7 @@ const usage = require("../../middleware/usageQuota")
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
|
.get("/api/views/export", authorized(BUILDER), viewController.exportView)
|
||||||
.get(
|
.get(
|
||||||
"/api/views/:viewName",
|
"/api/views/:viewName",
|
||||||
authorized(PermissionTypes.VIEW, PermissionLevels.READ),
|
authorized(PermissionTypes.VIEW, PermissionLevels.READ),
|
||||||
|
@ -25,6 +26,5 @@ router
|
||||||
viewController.destroy
|
viewController.destroy
|
||||||
)
|
)
|
||||||
.post("/api/views", authorized(BUILDER), usage, viewController.save)
|
.post("/api/views", authorized(BUILDER), usage, viewController.save)
|
||||||
.post("/api/views/export", authorized(BUILDER), viewController.exportView)
|
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -106,6 +106,7 @@
|
||||||
"styleable": true,
|
"styleable": true,
|
||||||
"hasChildren": true,
|
"hasChildren": true,
|
||||||
"dataProvider": true,
|
"dataProvider": true,
|
||||||
|
"actions": ["RefreshDatasource"],
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "datasource",
|
"type": "datasource",
|
||||||
|
@ -1045,12 +1046,11 @@
|
||||||
"styleable": true,
|
"styleable": true,
|
||||||
"hasChildren": true,
|
"hasChildren": true,
|
||||||
"dataProvider": true,
|
"dataProvider": true,
|
||||||
"datasourceSetting": "datasource",
|
|
||||||
"actions": ["ValidateForm"],
|
"actions": ["ValidateForm"],
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "datasource",
|
"type": "schema",
|
||||||
"label": "Data",
|
"label": "Schema",
|
||||||
"key": "datasource"
|
"key": "datasource"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1059,6 +1059,10 @@
|
||||||
"key": "theme",
|
"key": "theme",
|
||||||
"defaultValue": "spectrum--light",
|
"defaultValue": "spectrum--light",
|
||||||
"options": [
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Lightest",
|
||||||
|
"value": "spectrum--lightest"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Light",
|
"label": "Light",
|
||||||
"value": "spectrum--light"
|
"value": "spectrum--light"
|
||||||
|
@ -1282,7 +1286,7 @@
|
||||||
"styleable": true,
|
"styleable": true,
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "field/relationship",
|
"type": "field/link",
|
||||||
"label": "Field",
|
"label": "Field",
|
||||||
"key": "field"
|
"key": "field"
|
||||||
},
|
},
|
||||||
|
|
|
@ -35,9 +35,9 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"svelte"
|
"svelte"
|
||||||
],
|
],
|
||||||
"version": "0.7.1",
|
"version": "0.7.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"gitHead": "62ebf3cedcd7e9b2494b4f8cbcfb90927609b491",
|
"gitHead": "1a80b09fd093f2599a68f7db72ad639dd50922dd",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adobe/spectrum-css-workflow-icons": "^1.1.0",
|
"@adobe/spectrum-css-workflow-icons": "^1.1.0",
|
||||||
"@budibase/bbui": "^1.55.1",
|
"@budibase/bbui": "^1.55.1",
|
||||||
|
|
|
@ -2,15 +2,23 @@
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { isEmpty } from "lodash/fp"
|
import { isEmpty } from "lodash/fp"
|
||||||
|
|
||||||
const { API, styleable, Provider, builderStore } = getContext("sdk")
|
|
||||||
const component = getContext("component")
|
|
||||||
|
|
||||||
export let datasource = []
|
export let datasource = []
|
||||||
|
|
||||||
|
const { API, styleable, Provider, builderStore, ActionTypes } = getContext(
|
||||||
|
"sdk"
|
||||||
|
)
|
||||||
|
const component = getContext("component")
|
||||||
let rows = []
|
let rows = []
|
||||||
let loaded = false
|
let loaded = false
|
||||||
|
|
||||||
$: fetchData(datasource)
|
$: fetchData(datasource)
|
||||||
|
$: actions = [
|
||||||
|
{
|
||||||
|
type: ActionTypes.RefreshDatasource,
|
||||||
|
callback: () => fetchData(datasource),
|
||||||
|
metadata: { datasource },
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
async function fetchData(datasource) {
|
async function fetchData(datasource) {
|
||||||
if (!isEmpty(datasource)) {
|
if (!isEmpty(datasource)) {
|
||||||
|
@ -20,23 +28,25 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if rows.length > 0}
|
<Provider {actions}>
|
||||||
<div use:styleable={$component.styles}>
|
{#if rows.length > 0}
|
||||||
{#if $component.children === 0 && $builderStore.inBuilder}
|
<div use:styleable={$component.styles}>
|
||||||
<p>Add some components too</p>
|
{#if $component.children === 0 && $builderStore.inBuilder}
|
||||||
{:else}
|
<p>Add some components too</p>
|
||||||
{#each rows as row}
|
{:else}
|
||||||
<Provider data={row}>
|
{#each rows as row}
|
||||||
<slot />
|
<Provider data={row}>
|
||||||
</Provider>
|
<slot />
|
||||||
{/each}
|
</Provider>
|
||||||
{/if}
|
{/each}
|
||||||
</div>
|
{/if}
|
||||||
{:else if loaded && $builderStore.inBuilder}
|
</div>
|
||||||
<div use:styleable={$component.styles}>
|
{:else if loaded && $builderStore.inBuilder}
|
||||||
<p>Feed me some data</p>
|
<div use:styleable={$component.styles}>
|
||||||
</div>
|
<p>Feed me some data</p>
|
||||||
{/if}
|
</div>
|
||||||
|
{/if}
|
||||||
|
</Provider>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
p {
|
p {
|
||||||
|
|
|
@ -63,13 +63,18 @@
|
||||||
.nav__menu {
|
.nav__menu {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
gap: 16px;
|
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.nav__menu > a {
|
|
||||||
|
.nav__menu > * {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.nav__menu > a) {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import SpectrumField from "./SpectrumField.svelte"
|
import Field from "./Field.svelte"
|
||||||
import Dropzone from "../attachments/Dropzone.svelte"
|
import Dropzone from "../attachments/Dropzone.svelte"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
@ -21,8 +21,14 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SpectrumField {label} {field} bind:fieldState bind:fieldApi defaultValue={[]}>
|
<Field
|
||||||
|
{label}
|
||||||
|
{field}
|
||||||
|
type="attachment"
|
||||||
|
bind:fieldState
|
||||||
|
bind:fieldApi
|
||||||
|
defaultValue={[]}>
|
||||||
{#if mounted}
|
{#if mounted}
|
||||||
<Dropzone bind:files={value} />
|
<Dropzone bind:files={value} />
|
||||||
{/if}
|
{/if}
|
||||||
</SpectrumField>
|
</Field>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte"
|
|
||||||
import "@spectrum-css/checkbox/dist/index-vars.css"
|
import "@spectrum-css/checkbox/dist/index-vars.css"
|
||||||
import SpectrumField from "./SpectrumField.svelte"
|
import Field from "./Field.svelte"
|
||||||
|
|
||||||
export let field
|
export let field
|
||||||
export let label
|
export let label
|
||||||
|
@ -15,9 +14,10 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SpectrumField
|
<Field
|
||||||
{label}
|
{label}
|
||||||
{field}
|
{field}
|
||||||
|
type="boolean"
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
defaultValue={false}>
|
defaultValue={false}>
|
||||||
|
@ -48,4 +48,4 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</SpectrumField>
|
</Field>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import Flatpickr from "svelte-flatpickr"
|
import Flatpickr from "svelte-flatpickr"
|
||||||
import SpectrumField from "./SpectrumField.svelte"
|
import Field from "./Field.svelte"
|
||||||
import "flatpickr/dist/flatpickr.css"
|
import "flatpickr/dist/flatpickr.css"
|
||||||
import "@spectrum-css/inputgroup/dist/index-vars.css"
|
import "@spectrum-css/inputgroup/dist/index-vars.css"
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SpectrumField {label} {field} bind:fieldState bind:fieldApi>
|
<Field {label} {field} type="datetime" bind:fieldState bind:fieldApi>
|
||||||
{#if fieldState}
|
{#if fieldState}
|
||||||
<Flatpickr
|
<Flatpickr
|
||||||
bind:flatpickr
|
bind:flatpickr
|
||||||
|
@ -114,7 +114,7 @@
|
||||||
<div class="overlay" on:mousedown|self={flatpickr?.close} />
|
<div class="overlay" on:mousedown|self={flatpickr?.close} />
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</SpectrumField>
|
</Field>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.spectrum-Textfield-input {
|
.spectrum-Textfield-input {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
export let fieldApi
|
export let fieldApi
|
||||||
export let fieldSchema
|
export let fieldSchema
|
||||||
export let defaultValue
|
export let defaultValue
|
||||||
|
export let type
|
||||||
|
|
||||||
// Get contexts
|
// Get contexts
|
||||||
const formContext = getContext("form")
|
const formContext = getContext("form")
|
||||||
|
@ -45,6 +46,10 @@
|
||||||
<Placeholder>
|
<Placeholder>
|
||||||
Add the Field setting to start using your component
|
Add the Field setting to start using your component
|
||||||
</Placeholder>
|
</Placeholder>
|
||||||
|
{:else if fieldSchema?.type && fieldSchema?.type !== type}
|
||||||
|
<Placeholder>
|
||||||
|
This Field setting is the wrong data type for this component
|
||||||
|
</Placeholder>
|
||||||
{:else}
|
{:else}
|
||||||
<slot />
|
<slot />
|
||||||
{#if $fieldState.error}
|
{#if $fieldState.error}
|
|
@ -133,7 +133,15 @@
|
||||||
} else {
|
} else {
|
||||||
table = await API.fetchTableDefinition(datasource?.tableId)
|
table = await API.fetchTableDefinition(datasource?.tableId)
|
||||||
if (table) {
|
if (table) {
|
||||||
schema = table.schema || {}
|
if (datasource?.type === "query") {
|
||||||
|
schema = {}
|
||||||
|
const params = table.parameters || []
|
||||||
|
params.forEach(param => {
|
||||||
|
schema[param.name] = { ...param, type: "string" }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
schema = table.schema || {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loaded = true
|
loaded = true
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { RichText } from "@budibase/bbui"
|
import { RichText } from "@budibase/bbui"
|
||||||
import SpectrumField from "./SpectrumField.svelte"
|
import Field from "./Field.svelte"
|
||||||
|
|
||||||
export let field
|
export let field
|
||||||
export let label
|
export let label
|
||||||
|
@ -38,13 +38,19 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SpectrumField {label} {field} bind:fieldState bind:fieldApi defaultValue="">
|
<Field
|
||||||
|
{label}
|
||||||
|
{field}
|
||||||
|
type="longform"
|
||||||
|
bind:fieldState
|
||||||
|
bind:fieldApi
|
||||||
|
defaultValue="">
|
||||||
{#if mounted}
|
{#if mounted}
|
||||||
<div>
|
<div>
|
||||||
<RichText bind:value {options} />
|
<RichText bind:value {options} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</SpectrumField>
|
</Field>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import SpectrumField from "./SpectrumField.svelte"
|
import Field from "./Field.svelte"
|
||||||
import Picker from "./Picker.svelte"
|
import Picker from "./Picker.svelte"
|
||||||
|
|
||||||
export let field
|
export let field
|
||||||
|
@ -23,7 +23,13 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SpectrumField {field} {label} bind:fieldState bind:fieldApi bind:fieldSchema>
|
<Field
|
||||||
|
{field}
|
||||||
|
{label}
|
||||||
|
type="options"
|
||||||
|
bind:fieldState
|
||||||
|
bind:fieldApi
|
||||||
|
bind:fieldSchema>
|
||||||
{#if fieldState}
|
{#if fieldState}
|
||||||
<Picker
|
<Picker
|
||||||
bind:open
|
bind:open
|
||||||
|
@ -35,4 +41,4 @@
|
||||||
isOptionSelected={option => option === $fieldState.value}
|
isOptionSelected={option => option === $fieldState.value}
|
||||||
onSelectOption={selectOption} />
|
onSelectOption={selectOption} />
|
||||||
{/if}
|
{/if}
|
||||||
</SpectrumField>
|
</Field>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import SpectrumField from "./SpectrumField.svelte"
|
import Field from "./Field.svelte"
|
||||||
import Picker from "./Picker.svelte"
|
import Picker from "./Picker.svelte"
|
||||||
|
|
||||||
const { API } = getContext("sdk")
|
const { API } = getContext("sdk")
|
||||||
|
@ -62,9 +62,10 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SpectrumField
|
<Field
|
||||||
{label}
|
{label}
|
||||||
{field}
|
{field}
|
||||||
|
type="link"
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
bind:fieldSchema
|
bind:fieldSchema
|
||||||
|
@ -77,4 +78,4 @@
|
||||||
onSelectOption={toggleOption}
|
onSelectOption={toggleOption}
|
||||||
getOptionLabel={getDisplayName}
|
getOptionLabel={getDisplayName}
|
||||||
getOptionValue={option => option._id} />
|
getOptionValue={option => option._id} />
|
||||||
</SpectrumField>
|
</Field>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte"
|
|
||||||
import "@spectrum-css/textfield/dist/index-vars.css"
|
import "@spectrum-css/textfield/dist/index-vars.css"
|
||||||
import SpectrumField from "./SpectrumField.svelte"
|
import Field from "./Field.svelte"
|
||||||
|
|
||||||
export let field
|
export let field
|
||||||
export let label
|
export let label
|
||||||
|
@ -31,7 +30,12 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SpectrumField {label} {field} bind:fieldState bind:fieldApi>
|
<Field
|
||||||
|
{label}
|
||||||
|
{field}
|
||||||
|
type={type === 'number' ? 'number' : 'string'}
|
||||||
|
bind:fieldState
|
||||||
|
bind:fieldApi>
|
||||||
{#if fieldState}
|
{#if fieldState}
|
||||||
<div class="spectrum-Textfield" class:is-invalid={!$fieldState.valid}>
|
<div class="spectrum-Textfield" class:is-invalid={!$fieldState.valid}>
|
||||||
{#if !$fieldState.valid}
|
{#if !$fieldState.valid}
|
||||||
|
@ -53,4 +57,4 @@
|
||||||
class="spectrum-Textfield-input" />
|
class="spectrum-Textfield-input" />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</SpectrumField>
|
</Field>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import flatpickr from "flatpickr"
|
import flatpickr from "flatpickr"
|
||||||
import { isEmpty } from "lodash/fp"
|
|
||||||
|
|
||||||
export const createValidatorFromConstraints = (constraints, field, table) => {
|
export const createValidatorFromConstraints = (constraints, field, table) => {
|
||||||
let checks = []
|
let checks = []
|
||||||
|
@ -14,33 +13,33 @@ export const createValidatorFromConstraints = (constraints, field, table) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// String length constraint
|
// String length constraint
|
||||||
if (constraints.length?.maximum) {
|
if (exists(constraints.length?.maximum)) {
|
||||||
const length = constraints.length.maximum
|
const length = constraints.length.maximum
|
||||||
checks.push(lengthConstraint(length))
|
checks.push(lengthConstraint(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Min / max number constraint
|
// Min / max number constraint
|
||||||
if (!isEmpty(constraints.numericality?.greaterThanOrEqualTo)) {
|
if (exists(constraints.numericality?.greaterThanOrEqualTo)) {
|
||||||
const min = constraints.numericality.greaterThanOrEqualTo
|
const min = constraints.numericality.greaterThanOrEqualTo
|
||||||
checks.push(numericalConstraint(x => x >= min, `Minimum value is ${min}`))
|
checks.push(numericalConstraint(x => x >= min, `Minimum value is ${min}`))
|
||||||
}
|
}
|
||||||
if (!isEmpty(constraints.numericality?.lessThanOrEqualTo)) {
|
if (exists(constraints.numericality?.lessThanOrEqualTo)) {
|
||||||
const max = constraints.numericality.lessThanOrEqualTo
|
const max = constraints.numericality.lessThanOrEqualTo
|
||||||
checks.push(numericalConstraint(x => x <= max, `Maximum value is ${max}`))
|
checks.push(numericalConstraint(x => x <= max, `Maximum value is ${max}`))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inclusion constraint
|
// Inclusion constraint
|
||||||
if (!isEmpty(constraints.inclusion)) {
|
if (exists(constraints.inclusion)) {
|
||||||
const options = constraints.inclusion
|
const options = constraints.inclusion
|
||||||
checks.push(inclusionConstraint(options))
|
checks.push(inclusionConstraint(options))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date constraint
|
// Date constraint
|
||||||
if (!isEmpty(constraints.datetime?.earliest)) {
|
if (exists(constraints.datetime?.earliest)) {
|
||||||
const limit = constraints.datetime.earliest
|
const limit = constraints.datetime.earliest
|
||||||
checks.push(dateConstraint(limit, true))
|
checks.push(dateConstraint(limit, true))
|
||||||
}
|
}
|
||||||
if (!isEmpty(constraints.datetime?.latest)) {
|
if (exists(constraints.datetime?.latest)) {
|
||||||
const limit = constraints.datetime.latest
|
const limit = constraints.datetime.latest
|
||||||
checks.push(dateConstraint(limit, false))
|
checks.push(dateConstraint(limit, false))
|
||||||
}
|
}
|
||||||
|
@ -58,6 +57,8 @@ export const createValidatorFromConstraints = (constraints, field, table) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const exists = value => value != null && value !== ""
|
||||||
|
|
||||||
const presenceConstraint = value => {
|
const presenceConstraint = value => {
|
||||||
let invalid
|
let invalid
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import "@budibase/bbui/dist/bbui.css"
|
||||||
import "@spectrum-css/vars/dist/spectrum-global.css"
|
import "@spectrum-css/vars/dist/spectrum-global.css"
|
||||||
import "@spectrum-css/vars/dist/spectrum-medium.css"
|
import "@spectrum-css/vars/dist/spectrum-medium.css"
|
||||||
import "@spectrum-css/vars/dist/spectrum-large.css"
|
import "@spectrum-css/vars/dist/spectrum-large.css"
|
||||||
|
import "@spectrum-css/vars/dist/spectrum-lightest.css"
|
||||||
import "@spectrum-css/vars/dist/spectrum-light.css"
|
import "@spectrum-css/vars/dist/spectrum-light.css"
|
||||||
import "@spectrum-css/vars/dist/spectrum-dark.css"
|
import "@spectrum-css/vars/dist/spectrum-dark.css"
|
||||||
import "@spectrum-css/vars/dist/spectrum-darkest.css"
|
import "@spectrum-css/vars/dist/spectrum-darkest.css"
|
||||||
|
|
|
@ -1097,8 +1097,17 @@
|
||||||
"format"
|
"format"
|
||||||
],
|
],
|
||||||
"numArgs": 2,
|
"numArgs": 2,
|
||||||
"example": "{{date now \"DD-MM-YYYY\"}}",
|
"example": "{{date now \"DD-MM-YYYY\"}} -> 21-01-2021",
|
||||||
"description": "<p>Format a date using moment.js date formatting.</p>\n"
|
"description": "<p>Format a date using moment.js date formatting.</p>\n"
|
||||||
|
},
|
||||||
|
"duration": {
|
||||||
|
"args": [
|
||||||
|
"time",
|
||||||
|
"durationType"
|
||||||
|
],
|
||||||
|
"numArgs": 2,
|
||||||
|
"example": "{{duration timeLeft \"seconds\"}} -> a few seconds",
|
||||||
|
"description": "<p>Produce a humanized duration left/until given an amount of time and the type of time measurement.</p>\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "0.7.1",
|
"version": "0.7.4",
|
||||||
"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",
|
||||||
|
@ -32,5 +32,6 @@
|
||||||
"rollup-plugin-node-resolve": "^5.2.0",
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"typescript": "^4.1.3"
|
"typescript": "^4.1.3"
|
||||||
}
|
},
|
||||||
|
"gitHead": "1a80b09fd093f2599a68f7db72ad639dd50922dd"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,18 +5,32 @@ const fs = require("fs")
|
||||||
const doctrine = require("doctrine")
|
const doctrine = require("doctrine")
|
||||||
const marked = require("marked")
|
const marked = require("marked")
|
||||||
|
|
||||||
const DIRECTORY = fs.existsSync("node_modules") ? "." : ".."
|
|
||||||
|
|
||||||
const FILENAME = `${DIRECTORY}/manifest.json`
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* full list of supported helpers can be found here:
|
* full list of supported helpers can be found here:
|
||||||
* https://github.com/helpers/handlebars-helpers
|
* https://github.com/budibase/handlebars-helpers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const DIRECTORY = fs.existsSync("node_modules") ? "." : ".."
|
||||||
const COLLECTIONS = ["math", "array", "number", "url", "string", "comparison"]
|
const COLLECTIONS = ["math", "array", "number", "url", "string", "comparison"]
|
||||||
|
const FILENAME = `${DIRECTORY}/manifest.json`
|
||||||
const outputJSON = {}
|
const outputJSON = {}
|
||||||
|
const ADDED_HELPERS = {
|
||||||
|
date: {
|
||||||
|
date: {
|
||||||
|
args: ["datetime", "format"],
|
||||||
|
numArgs: 2,
|
||||||
|
example: '{{date now "DD-MM-YYYY"}} -> 21-01-2021',
|
||||||
|
description: "Format a date using moment.js date formatting.",
|
||||||
|
},
|
||||||
|
duration: {
|
||||||
|
args: ["time", "durationType"],
|
||||||
|
numArgs: 2,
|
||||||
|
example: '{{duration timeLeft "seconds"}} -> a few seconds',
|
||||||
|
description:
|
||||||
|
"Produce a humanized duration left/until given an amount of time and the type of time measurement.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
function fixSpecialCases(name, obj) {
|
function fixSpecialCases(name, obj) {
|
||||||
const args = obj.args
|
const args = obj.args
|
||||||
|
@ -134,15 +148,15 @@ function run() {
|
||||||
}
|
}
|
||||||
outputJSON[collection] = collectionInfo
|
outputJSON[collection] = collectionInfo
|
||||||
}
|
}
|
||||||
// add the date helper
|
// add extra helpers
|
||||||
outputJSON["date"] = {
|
for (let [collectionName, collection] of Object.entries(ADDED_HELPERS)) {
|
||||||
date: {
|
let input = collection
|
||||||
args: ["datetime", "format"],
|
if (outputJSON[collectionName]) {
|
||||||
numArgs: 2,
|
input = Object.assign(outputJSON[collectionName], collection)
|
||||||
example: '{{date now "DD-MM-YYYY"}}',
|
}
|
||||||
description: "Format a date using moment.js date formatting.",
|
outputJSON[collectionName] = input
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert all markdown to HTML
|
// convert all markdown to HTML
|
||||||
for (let collection of Object.values(outputJSON)) {
|
for (let collection of Object.values(outputJSON)) {
|
||||||
for (let helper of Object.values(collection)) {
|
for (let helper of Object.values(collection)) {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
const dayjs = require("dayjs")
|
const dayjs = require("dayjs")
|
||||||
|
dayjs.extend(require("dayjs/plugin/duration"))
|
||||||
|
dayjs.extend(require("dayjs/plugin/advancedFormat"))
|
||||||
|
dayjs.extend(require("dayjs/plugin/relativeTime"))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file was largely taken from the helper-date package - we did this for two reasons:
|
* This file was largely taken from the helper-date package - we did this for two reasons:
|
||||||
|
@ -50,7 +53,7 @@ function getContext(thisArg, locals, options) {
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function dateHelper(str, pattern, options) {
|
function initialConfig(str, pattern, options) {
|
||||||
if (isOptions(pattern)) {
|
if (isOptions(pattern)) {
|
||||||
options = pattern
|
options = pattern
|
||||||
pattern = null
|
pattern = null
|
||||||
|
@ -61,18 +64,42 @@ module.exports = function dateHelper(str, pattern, options) {
|
||||||
pattern = null
|
pattern = null
|
||||||
str = null
|
str = null
|
||||||
}
|
}
|
||||||
|
return { str, pattern, options }
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLocale(str, pattern, options) {
|
||||||
|
// if options is null then it'll get updated here
|
||||||
|
const config = initialConfig(str, pattern, options)
|
||||||
|
const defaults = { lang: "en", date: new Date(config.str) }
|
||||||
|
const opts = getContext(this, defaults, config.options)
|
||||||
|
|
||||||
|
// set the language to use
|
||||||
|
dayjs.locale(opts.lang || opts.language)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.date = (str, pattern, options) => {
|
||||||
|
const config = initialConfig(str, pattern, options)
|
||||||
|
|
||||||
// if no args are passed, return a formatted date
|
// if no args are passed, return a formatted date
|
||||||
if (str == null && pattern == null) {
|
if (config.str == null && config.pattern == null) {
|
||||||
dayjs.locale("en")
|
dayjs.locale("en")
|
||||||
return dayjs().format("MMMM DD, YYYY")
|
return dayjs().format("MMMM DD, YYYY")
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaults = { lang: "en", date: new Date(str) }
|
setLocale(config.str, config.pattern, config.options)
|
||||||
const opts = getContext(this, defaults, options)
|
|
||||||
|
|
||||||
// set the language to use
|
return dayjs(new Date(config.str)).format(config.pattern)
|
||||||
dayjs.locale(opts.lang || opts.language)
|
}
|
||||||
|
|
||||||
return dayjs(new Date(str)).format(pattern)
|
module.exports.duration = (str, pattern, format) => {
|
||||||
|
const config = initialConfig(str, pattern)
|
||||||
|
|
||||||
|
setLocale(config.str, config.pattern)
|
||||||
|
|
||||||
|
const duration = dayjs.duration(config.str, config.pattern)
|
||||||
|
if (!isOptions(format)) {
|
||||||
|
return duration.format(format)
|
||||||
|
} else {
|
||||||
|
return duration.humanize()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const helpers = require("@budibase/handlebars-helpers")
|
const helpers = require("@budibase/handlebars-helpers")
|
||||||
const dateHelper = require("./date")
|
const { date, duration } = require("./date")
|
||||||
const { HelperFunctionBuiltin } = require("./constants")
|
const { HelperFunctionBuiltin } = require("./constants")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,10 +18,15 @@ const EXTERNAL_FUNCTION_COLLECTIONS = [
|
||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
const DATE_NAME = "date"
|
const ADDED_HELPERS = {
|
||||||
|
date: date,
|
||||||
|
duration: duration,
|
||||||
|
}
|
||||||
|
|
||||||
exports.registerAll = handlebars => {
|
exports.registerAll = handlebars => {
|
||||||
handlebars.registerHelper(DATE_NAME, dateHelper)
|
for (let [name, helper] of Object.entries(ADDED_HELPERS)) {
|
||||||
|
handlebars.registerHelper(name, helper)
|
||||||
|
}
|
||||||
let externalNames = []
|
let externalNames = []
|
||||||
for (let collection of EXTERNAL_FUNCTION_COLLECTIONS) {
|
for (let collection of EXTERNAL_FUNCTION_COLLECTIONS) {
|
||||||
// collect information about helper
|
// collect information about helper
|
||||||
|
@ -43,12 +48,13 @@ exports.registerAll = handlebars => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// add date external functionality
|
// add date external functionality
|
||||||
externalNames.push(DATE_NAME)
|
exports.externalHelperNames = externalNames.concat(Object.keys(ADDED_HELPERS))
|
||||||
exports.externalHelperNames = externalNames
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.unregisterAll = handlebars => {
|
exports.unregisterAll = handlebars => {
|
||||||
handlebars.unregisterHelper(DATE_NAME)
|
for (let name of Object.keys(ADDED_HELPERS)) {
|
||||||
|
handlebars.unregisterHelper(name)
|
||||||
|
}
|
||||||
for (let name of module.exports.externalHelperNames) {
|
for (let name of module.exports.externalHelperNames) {
|
||||||
handlebars.unregisterHelper(name)
|
handlebars.unregisterHelper(name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,6 +318,17 @@ describe("Cover a few complex use cases", () => {
|
||||||
expect(validity).toBe(true)
|
expect(validity).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("test a very complex duration output", async () => {
|
||||||
|
const currentTime = new Date(1612432082000).toISOString(),
|
||||||
|
eventTime = new Date(1612432071000).toISOString()
|
||||||
|
const input = `{{duration ( subtract (date currentTime "X")(date eventTime "X")) "seconds"}}`
|
||||||
|
const output = await processString(input, {
|
||||||
|
currentTime,
|
||||||
|
eventTime,
|
||||||
|
})
|
||||||
|
expect(output).toBe("a few seconds")
|
||||||
|
})
|
||||||
|
|
||||||
it("should confirm a bunch of invalid strings", () => {
|
it("should confirm a bunch of invalid strings", () => {
|
||||||
const invalids = ["{{ awd )", "{{ awdd () ", "{{ awdwad ", "{{ awddawd }"]
|
const invalids = ["{{ awd )", "{{ awdd () ", "{{ awdwad ", "{{ awddawd }"]
|
||||||
for (let invalid of invalids) {
|
for (let invalid of invalids) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/deployment",
|
"name": "@budibase/deployment",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.7.1",
|
"version": "0.7.4",
|
||||||
"description": "Budibase Deployment Server",
|
"description": "Budibase Deployment Server",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -34,5 +34,5 @@
|
||||||
"pouchdb-all-dbs": "^1.0.2",
|
"pouchdb-all-dbs": "^1.0.2",
|
||||||
"server-destroy": "^1.0.1"
|
"server-destroy": "^1.0.1"
|
||||||
},
|
},
|
||||||
"gitHead": "62ebf3cedcd7e9b2494b4f8cbcfb90927609b491"
|
"gitHead": "1a80b09fd093f2599a68f7db72ad639dd50922dd"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue