x.name.includes(table.name))}
@@ -90,7 +90,6 @@
.content {
letter-spacing: 0px;
- color: #2c2c2c;
}
.item {
cursor: pointer;
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterDrawer.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterDrawer.svelte
index 22363ff8f6..e30fd6d491 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterDrawer.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterDrawer.svelte
@@ -11,14 +11,14 @@
Select,
} from "@budibase/bbui"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
- import BindingPanel from "components/common/bindings/BindingPanel.svelte"
+ import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte"
import { generate } from "shortid"
import { getValidOperatorsForType, OperatorOptions } from "constants/lucene"
export let schemaFields
export let filters = []
export let bindings = []
- export let panel = BindingPanel
+ export let panel = ClientBindingPanel
export let allowBindings = true
const BannedTypes = ["link", "attachment", "formula"]
diff --git a/packages/cli/package.json b/packages/cli/package.json
index f99ece2077..4c65256f32 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
- "version": "0.9.185-alpha.14",
+ "version": "0.9.185-alpha.19",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js",
"bin": {
diff --git a/packages/client/manifest.json b/packages/client/manifest.json
index 375aea3a02..d16b117215 100644
--- a/packages/client/manifest.json
+++ b/packages/client/manifest.json
@@ -247,7 +247,6 @@
"description": "A basic html button that is ready for styling",
"icon": "Button",
"editable": true,
- "illegalChildren": ["section"],
"showSettingsBar": true,
"settings": [
{
@@ -2647,6 +2646,49 @@
}
]
},
+ "dynamicfilter": {
+ "name": "Dynamic Filter",
+ "icon": "FilterEdit",
+ "showSettingsBar": true,
+ "settings": [
+ {
+ "type": "dataProvider",
+ "label": "Provider",
+ "key": "dataProvider"
+ },
+ {
+ "type": "multifield",
+ "label": "Allowed filter fields",
+ "key": "allowedFields",
+ "placeholder": "All fields"
+ },
+ {
+ "type": "select",
+ "label": "Button size",
+ "showInBar": true,
+ "key": "size",
+ "options": [
+ {
+ "label": "Small",
+ "value": "S"
+ },
+ {
+ "label": "Medium",
+ "value": "M"
+ },
+ {
+ "label": "Large",
+ "value": "L"
+ },
+ {
+ "label": "Extra large",
+ "value": "XL"
+ }
+ ],
+ "defaultValue": "M"
+ }
+ ]
+ },
"tableblock": {
"block": true,
"name": "Table block",
diff --git a/packages/client/package.json b/packages/client/package.json
index 607328c591..74091fe94f 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/client",
- "version": "0.9.185-alpha.14",
+ "version": "0.9.185-alpha.19",
"license": "MPL-2.0",
"module": "dist/budibase-client.js",
"main": "dist/budibase-client.js",
@@ -19,9 +19,9 @@
"dev:builder": "rollup -cw"
},
"dependencies": {
- "@budibase/bbui": "^0.9.185-alpha.14",
+ "@budibase/bbui": "^0.9.185-alpha.19",
"@budibase/standard-components": "^0.9.139",
- "@budibase/string-templates": "^0.9.185-alpha.14",
+ "@budibase/string-templates": "^0.9.185-alpha.19",
"regexparam": "^1.3.0",
"shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5"
diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte
index 119a26800f..98dec9667b 100644
--- a/packages/client/src/components/ClientApp.svelte
+++ b/packages/client/src/components/ClientApp.svelte
@@ -121,6 +121,9 @@
-->
+
+
+
diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte
index a4ee290028..d8cdbd3f53 100644
--- a/packages/client/src/components/Component.svelte
+++ b/packages/client/src/components/Component.svelte
@@ -72,13 +72,18 @@
$: inSelectedPath = $builderStore.selectedComponentPath?.includes(id)
$: inDragPath = inSelectedPath && $builderStore.editMode
+ // Derive definition properties which can all be optional, so need to be
+ // coerced to booleans
+ $: editable = !!definition?.editable
+ $: hasChildren = !!definition?.hasChildren
+ $: showEmptyState = definition?.showEmptyState !== false
+
// Interactive components can be selected, dragged and highlighted inside
// the builder preview
$: interactive =
$builderStore.inBuilder &&
($builderStore.previewType === "layout" || insideScreenslot) &&
!isBlock
- $: editable = definition?.editable
$: editing = editable && selected && $builderStore.editMode
$: draggable = !inDragPath && interactive && !isLayout && !isScreen
$: droppable = interactive && !isLayout && !isScreen
@@ -86,8 +91,8 @@
// Empty components are those which accept children but do not have any.
// Empty states can be shown for these components, but can be disabled
// in the component manifest.
- $: empty = interactive && !children.length && definition?.hasChildren
- $: emptyState = empty && definition?.showEmptyState !== false
+ $: empty = interactive && !children.length && hasChildren
+ $: emptyState = empty && showEmptyState
// Raw settings are all settings excluding internal props and children
$: rawSettings = getRawSettings(instance)
@@ -103,6 +108,9 @@
// Build up the final settings object to be passed to the component
$: cacheSettings(enrichedSettings, nestedSettings, conditionalSettings)
+ // Render key is used to determine when components need to fully remount
+ $: renderKey = getRenderKey(id, editing)
+
// Update component context
$: componentStore.set({
id,
@@ -268,35 +276,43 @@
})
}
}
+
+ // Generates a key used to determine when components need to fully remount.
+ // Currently only toggling editing requires remounting.
+ const getRenderKey = (id, editing) => {
+ return hashString(`${id}-${editing}`)
+ }
-{#if constructor && cachedSettings && (visible || inSelectedPath)}
-
-
-
-
- {#if children.length}
- {#each children as child (child._id)}
-
- {/each}
- {:else if emptyState}
-
- {:else if isBlock}
-
- {/if}
-
-
-{/if}
+{#key renderKey}
+ {#if constructor && cachedSettings && (visible || inSelectedPath)}
+
+
+
+
+ {#if children.length}
+ {#each children as child (child._id)}
+
+ {/each}
+ {:else if emptyState}
+
+ {:else if isBlock}
+
+ {/if}
+
+
+ {/if}
+{/key}
diff --git a/packages/client/src/components/app/DataProvider.svelte b/packages/client/src/components/app/DataProvider.svelte
index 08f2fb06cd..e9d306cc3b 100644
--- a/packages/client/src/components/app/DataProvider.svelte
+++ b/packages/client/src/components/app/DataProvider.svelte
@@ -275,11 +275,10 @@
allRows = res.rows
}
- const addQueryExtension = (key, operator, field, value) => {
- if (!key || !operator || !field) {
+ const addQueryExtension = (key, extension) => {
+ if (!key || !extension) {
return
}
- const extension = { operator, field, value }
queryExtensions = { ...queryExtensions, [key]: extension }
}
@@ -295,11 +294,13 @@
const extendQuery = (defaultQuery, extensions) => {
const extensionValues = Object.values(extensions || {})
let extendedQuery = { ...defaultQuery }
- extensionValues.forEach(({ operator, field, value }) => {
- extendedQuery[operator] = {
- ...extendedQuery[operator],
- [field]: value,
- }
+ extensionValues.forEach(extension => {
+ Object.entries(extension || {}).forEach(([operator, fields]) => {
+ extendedQuery[operator] = {
+ ...extendedQuery[operator],
+ ...fields,
+ }
+ })
})
if (JSON.stringify(query) !== JSON.stringify(extendedQuery)) {
diff --git a/packages/client/src/components/app/DateRangePicker.svelte b/packages/client/src/components/app/DateRangePicker.svelte
index 651a19abc4..0246b68198 100644
--- a/packages/client/src/components/app/DateRangePicker.svelte
+++ b/packages/client/src/components/app/DateRangePicker.svelte
@@ -13,15 +13,6 @@
const component = getContext("component")
const { styleable, ActionTypes, getAction } = getContext("sdk")
-
- $: addExtension = getAction(
- dataProvider?.id,
- ActionTypes.AddDataProviderQueryExtension
- )
- $: removeExtension = getAction(
- dataProvider?.id,
- ActionTypes.RemoveDataProviderQueryExtension
- )
const options = [
"Last 1 day",
"Last 7 days",
@@ -32,10 +23,23 @@
]
let value = options.includes(defaultValue) ? defaultValue : "Last 30 days"
- $: queryExtension = getQueryExtension(value)
- $: addExtension?.($component.id, "range", field, queryExtension)
+ $: dataProviderId = dataProvider?.id
+ $: addExtension = getAction(
+ dataProviderId,
+ ActionTypes.AddDataProviderQueryExtension
+ )
+ $: removeExtension = getAction(
+ dataProviderId,
+ ActionTypes.RemoveDataProviderQueryExtension
+ )
+ $: queryExtension = getQueryExtension(field, value)
+ $: addExtension?.($component.id, queryExtension)
+
+ const getQueryExtension = (field, value) => {
+ if (!field || !value) {
+ return null
+ }
- const getQueryExtension = value => {
let low = dayjs.utc().subtract(1, "year")
let high = dayjs.utc().add(1, "day")
@@ -51,7 +55,14 @@
low = dayjs.utc().subtract(6, "months")
}
- return { low: low.format(), high: high.format() }
+ return {
+ range: {
+ [field]: {
+ low: low.format(),
+ high: high.format(),
+ },
+ },
+ }
}
onDestroy(() => {
diff --git a/packages/client/src/components/app/Heading.svelte b/packages/client/src/components/app/Heading.svelte
index 0450d2d11d..b27862f164 100644
--- a/packages/client/src/components/app/Heading.svelte
+++ b/packages/client/src/components/app/Heading.svelte
@@ -47,7 +47,7 @@
// Convert contenteditable HTML to text and save
const updateText = e => {
- const sanitized = e.target.innerHTML.replace(/
/gi, "\n")
+ const sanitized = e.target.innerHTML.replace(/
/gi, "\n").trim()
builderStore.actions.updateProp("text", sanitized)
}
diff --git a/packages/client/src/components/app/Link.svelte b/packages/client/src/components/app/Link.svelte
index 9587b34f11..851b2f0b66 100644
--- a/packages/client/src/components/app/Link.svelte
+++ b/packages/client/src/components/app/Link.svelte
@@ -47,7 +47,7 @@
}
const updateText = e => {
- builderStore.actions.updateProp("text", e.target.textContent)
+ builderStore.actions.updateProp("text", e.target.textContent.trim())
}
diff --git a/packages/client/src/components/app/Text.svelte b/packages/client/src/components/app/Text.svelte
index 14681ebbaf..679434edeb 100644
--- a/packages/client/src/components/app/Text.svelte
+++ b/packages/client/src/components/app/Text.svelte
@@ -46,7 +46,7 @@
// Convert contenteditable HTML to text and save
const updateText = e => {
- const sanitized = e.target.innerHTML.replace(/
/gi, "\n")
+ const sanitized = e.target.innerHTML.replace(/
/gi, "\n").trim()
builderStore.actions.updateProp("text", sanitized)
}
diff --git a/packages/client/src/components/app/dynamic-filter/DynamicFilter.svelte b/packages/client/src/components/app/dynamic-filter/DynamicFilter.svelte
new file mode 100644
index 0000000000..20909d011c
--- /dev/null
+++ b/packages/client/src/components/app/dynamic-filter/DynamicFilter.svelte
@@ -0,0 +1,85 @@
+
+
+