diff --git a/lerna.json b/lerna.json
index 1d3fc4f1c8..b53f4142a5 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "0.7.1",
+ "version": "0.7.4",
"npmClient": "yarn",
"packages": [
"packages/*"
diff --git a/packages/builder/package.json b/packages/builder/package.json
index a43a251c91..9de3db2b7b 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
- "version": "0.7.1",
+ "version": "0.7.4",
"license": "AGPL-3.0",
"private": true,
"scripts": {
@@ -64,26 +64,25 @@
},
"dependencies": {
"@budibase/bbui": "^1.56.0",
- "@budibase/client": "^0.7.1",
- "@budibase/colorpicker": "^1.0.1",
- "@budibase/string-templates": "^0.7.1",
+ "@budibase/client": "^0.7.4",
+ "@budibase/colorpicker": "1.0.1",
+ "@budibase/string-templates": "^0.7.4",
"@budibase/svelte-ag-grid": "^0.0.16",
"@sentry/browser": "5.19.1",
- "@svelteschool/svelte-forms": "^0.7.0",
- "britecharts": "^2.16.0",
+ "@svelteschool/svelte-forms": "0.7.0",
"codemirror": "^5.59.0",
"d3-selection": "^1.4.1",
"deepmerge": "^4.2.2",
- "downloadjs": "^1.4.7",
+ "downloadjs": "1.4.7",
"fast-sort": "^2.2.0",
- "lodash": "^4.17.13",
+ "lodash": "4.17.13",
"posthog-js": "1.4.5",
- "remixicon": "^2.5.0",
- "shortid": "^2.2.15",
+ "remixicon": "2.5.0",
+ "shortid": "2.2.15",
"svelte-loading-spinners": "^0.1.1",
- "svelte-portal": "^0.1.0",
- "uuid": "^8.3.1",
- "yup": "^0.29.2"
+ "svelte-portal": "0.1.0",
+ "uuid": "8.3.1",
+ "yup": "0.29.2"
},
"devDependencies": {
"@babel/core": "^7.5.5",
diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js
index 6eb6e6a0d8..c57099fe1c 100644
--- a/packages/builder/src/builderStore/dataBinding.js
+++ b/packages/builder/src/builderStore/dataBinding.js
@@ -69,8 +69,9 @@ export const getDatasourceForProvider = component => {
}
// Extract datasource from component instance
+ const validSettingTypes = ["datasource", "table", "schema"]
const datasourceSetting = def.settings.find(setting => {
- return setting.type === "datasource" || setting.type === "table"
+ return validSettingTypes.includes(setting.type)
})
if (!datasourceSetting) {
return null
@@ -80,15 +81,14 @@ export const getDatasourceForProvider = component => {
// example an actual datasource object, or a table ID string.
// Convert the datasource setting into a proper datasource object so that
// we can use it properly
- if (datasourceSetting.type === "datasource") {
- return component[datasourceSetting?.key]
- } else if (datasourceSetting.type === "table") {
+ if (datasourceSetting.type === "table") {
return {
tableId: component[datasourceSetting?.key],
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
const dataProviders = getDataProviderComponents(rootComponent, componentId)
let contextBindings = []
+
+ // Create bindings for each data provider
dataProviders.forEach(component => {
+ const isForm = component._component.endsWith("/form")
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
}
- // 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()
// Create bindable properties for each schema field
@@ -132,11 +148,13 @@ export const getContextBindings = (rootComponent, componentId) => {
runtimeBinding: `${makePropSafe(component._id)}.${makePropSafe(
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,
providerId: component._id,
- tableId: datasource.tableId,
- field: key,
+ // tableId: table._id,
+ // field: key,
})
})
})
@@ -164,10 +182,12 @@ export const getContextBindings = (rootComponent, componentId) => {
type: "context",
runtimeBinding: `user.${runtimeBoundKey}`,
readableBinding: `Current User.${key}`,
+ // Field schema and provider are required to construct relationship
+ // datasource options, based on bindable properties
fieldSchema,
providerId: "user",
- tableId: TableNames.USERS,
- field: key,
+ // tableId: TableNames.USERS,
+ // field: key,
})
})
@@ -177,7 +197,7 @@ export const getContextBindings = (rootComponent, componentId) => {
/**
* Gets a schema for a datasource object.
*/
-export const getSchemaForDatasource = datasource => {
+export const getSchemaForDatasource = (datasource, isForm = false) => {
let schema, table
if (datasource) {
const { type } = datasource
@@ -191,6 +211,12 @@ export const getSchemaForDatasource = datasource => {
if (table) {
if (type === "view") {
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 {
schema = cloneDeep(table.schema)
}
@@ -199,6 +225,32 @@ export const getSchemaForDatasource = datasource => {
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.
*/
diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index 340e2829aa..27427c6ef0 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -416,7 +416,14 @@ export const getFrontendStore = () => {
if (cut) {
state.componentToPaste = null
} else {
- componentToPaste._id = uuid()
+ const randomizeIds = component => {
+ if (!component) {
+ return
+ }
+ component._id = uuid()
+ component._children?.forEach(randomizeIds)
+ }
+ randomizeIds(componentToPaste)
}
if (mode === "inside") {
diff --git a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js
index 5c32144190..2ee537ac0c 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js
@@ -35,7 +35,7 @@ const createScreen = table => {
const form = makeMainForm()
.instanceName("Form")
.customProps({
- theme: "spectrum--light",
+ theme: "spectrum--lightest",
size: "spectrum--medium",
datasource: {
label: table.name,
diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
index f15f188681..783bcf40aa 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
@@ -59,6 +59,7 @@ function generateTitleContainer(table, title, formId) {
onClick: [
{
parameters: {
+ providerId: formId,
rowId: `{{ ${makePropSafe(formId)}._id }}`,
revId: `{{ ${makePropSafe(formId)}._rev }}`,
tableId: table._id,
@@ -90,7 +91,7 @@ const createScreen = table => {
const form = makeMainForm()
.instanceName("Form")
.customProps({
- theme: "spectrum--light",
+ theme: "spectrum--lightest",
size: "spectrum--medium",
datasource: {
label: table.name,
diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js
index d2757ec054..8c6bfe646b 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js
@@ -169,7 +169,11 @@ export function makeTableFormComponents(tableId) {
export function makeQueryFormComponents(queryId) {
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)
}
diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
index 439687d0c2..ed89d4316d 100644
--- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
+++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
@@ -64,7 +64,11 @@
{:else if value.customType === 'password'}
{:else if value.customType === 'email'}
-
+
{:else if value.customType === 'table'}
{:else if value.customType === 'row'}
@@ -75,7 +79,7 @@
{:else if value.type === 'string' || value.type === 'number'}
diff --git a/packages/builder/src/components/backend/DataTable/popovers/ExportPopover.svelte b/packages/builder/src/components/backend/DataTable/popovers/ExportPopover.svelte
index 3e7642982a..af561668ec 100644
--- a/packages/builder/src/components/backend/DataTable/popovers/ExportPopover.svelte
+++ b/packages/builder/src/components/backend/DataTable/popovers/ExportPopover.svelte
@@ -20,13 +20,12 @@
let exportFormat = FORMATS[0].key
async function exportView() {
- const response = await api.post(
- `/api/views/export?format=${exportFormat}`,
- view
+ download(
+ `/api/views/export?view=${encodeURIComponent(
+ view.name
+ )}&format=${exportFormat}`
)
- const downloadInfo = await response.json()
onClosed()
- window.location = downloadInfo.url
}
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DatasourceSelect.svelte
similarity index 93%
rename from packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte
rename to packages/builder/src/components/design/PropertiesPanel/PropertyControls/DatasourceSelect.svelte
index a467a954a2..75702b7cdb 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DatasourceSelect.svelte
@@ -19,6 +19,7 @@
let drawer
export let value = {}
+ export let otherSources
$: tables = $backendUiStore.tables.map(m => ({
label: m.name,
@@ -88,7 +89,7 @@
class="dropdownbutton"
bind:this={anchorRight}
on:click={dropdownRight.show}>
- {value?.label ? value.label : 'Choose option'}
+ {value?.label ?? 'Choose option'}
{#if value?.type === 'query'}
@@ -175,6 +176,22 @@
{/each}
+
+ {#if otherSources?.length}
+
+
+ Other
+
+
+ {#each otherSources as source}
+ - handleSelected(source)}>
+ {source.label}
+
+ {/each}
+
+ {/if}
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte
index e61fadd57b..287f76d9e3 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte
@@ -15,8 +15,9 @@
)
$: {
// Automatically set rev and table ID based on row ID
- if (parameters.rowId) {
- parameters.revId = parameters.rowId.replace("_id", "_rev")
+ if (parameters.providerId) {
+ parameters.rowId = `{{ ${parameters.providerId}._id }}`
+ parameters.revId = `{{ ${parameters.providerId}._rev }}`
const providerComponent = dataProviderComponents.find(
provider => provider._id === parameters.providerId
)
@@ -37,12 +38,10 @@
{:else}
-