- {#if type !== "boolean"}
+ {#if type !== "boolean" && label}
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js
new file mode 100644
index 0000000000..8435971714
--- /dev/null
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js
@@ -0,0 +1,54 @@
+import { Checkbox, Input, Select } from "@budibase/bbui"
+import DataSourceSelect from "./DataSourceSelect.svelte"
+import DataProviderSelect from "./DataProviderSelect.svelte"
+import EventsEditor from "./EventsEditor"
+import TableSelect from "./TableSelect.svelte"
+import ColorPicker from "./ColorPicker.svelte"
+import { IconSelect } from "./IconSelect"
+import FieldSelect from "./FieldSelect.svelte"
+import MultiFieldSelect from "./MultiFieldSelect.svelte"
+import SchemaSelect from "./SchemaSelect.svelte"
+import SectionSelect from "./SectionSelect.svelte"
+import NavigationEditor from "./NavigationEditor/NavigationEditor.svelte"
+import FilterEditor from "./FilterEditor/FilterEditor.svelte"
+import URLSelect from "./URLSelect.svelte"
+import StringFieldSelect from "./StringFieldSelect.svelte"
+import NumberFieldSelect from "./NumberFieldSelect.svelte"
+import OptionsFieldSelect from "./OptionsFieldSelect.svelte"
+import BooleanFieldSelect from "./BooleanFieldSelect.svelte"
+import LongFormFieldSelect from "./LongFormFieldSelect.svelte"
+import DateTimeFieldSelect from "./DateTimeFieldSelect.svelte"
+import AttachmentFieldSelect from "./AttachmentFieldSelect.svelte"
+import RelationshipFieldSelect from "./RelationshipFieldSelect.svelte"
+
+const componentMap = {
+ text: Input,
+ select: Select,
+ dataSource: DataSourceSelect,
+ dataProvider: DataProviderSelect,
+ boolean: Checkbox,
+ number: Input,
+ event: EventsEditor,
+ table: TableSelect,
+ color: ColorPicker,
+ icon: IconSelect,
+ field: FieldSelect,
+ multifield: MultiFieldSelect,
+ schema: SchemaSelect,
+ section: SectionSelect,
+ navigation: NavigationEditor,
+ filter: FilterEditor,
+ url: URLSelect,
+ "field/string": StringFieldSelect,
+ "field/number": NumberFieldSelect,
+ "field/options": OptionsFieldSelect,
+ "field/boolean": BooleanFieldSelect,
+ "field/longform": LongFormFieldSelect,
+ "field/datetime": DateTimeFieldSelect,
+ "field/attachment": AttachmentFieldSelect,
+ "field/link": RelationshipFieldSelect,
+}
+
+export const getComponentForSettingType = type => {
+ return componentMap[type]
+}
diff --git a/packages/builder/src/helpers/lucene.js b/packages/builder/src/helpers/lucene.js
new file mode 100644
index 0000000000..43fb155e14
--- /dev/null
+++ b/packages/builder/src/helpers/lucene.js
@@ -0,0 +1,80 @@
+export const OperatorOptions = {
+ Equals: {
+ value: "equal",
+ label: "Equals",
+ },
+ NotEquals: {
+ value: "notEqual",
+ label: "Not equals",
+ },
+ Empty: {
+ value: "empty",
+ label: "Is empty",
+ },
+ NotEmpty: {
+ value: "notEmpty",
+ label: "Is not empty",
+ },
+ StartsWith: {
+ value: "string",
+ label: "Starts with",
+ },
+ Like: {
+ value: "fuzzy",
+ label: "Like",
+ },
+ MoreThan: {
+ value: "rangeLow",
+ label: "More than",
+ },
+ LessThan: {
+ value: "rangeHigh",
+ label: "Less than",
+ },
+}
+
+export const getValidOperatorsForType = type => {
+ const Op = OperatorOptions
+ if (type === "string") {
+ return [
+ Op.Equals,
+ Op.NotEquals,
+ Op.StartsWith,
+ Op.Like,
+ Op.Empty,
+ Op.NotEmpty,
+ ]
+ } else if (type === "number") {
+ return [
+ Op.Equals,
+ Op.NotEquals,
+ Op.MoreThan,
+ Op.LessThan,
+ Op.Empty,
+ Op.NotEmpty,
+ ]
+ } else if (type === "options") {
+ return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
+ } else if (type === "boolean") {
+ return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
+ } else if (type === "longform") {
+ return [
+ Op.Equals,
+ Op.NotEquals,
+ Op.StartsWith,
+ Op.Like,
+ Op.Empty,
+ Op.NotEmpty,
+ ]
+ } else if (type === "datetime") {
+ return [
+ Op.Equals,
+ Op.NotEquals,
+ Op.MoreThan,
+ Op.LessThan,
+ Op.Empty,
+ Op.NotEmpty,
+ ]
+ }
+ return []
+}
diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte
index 4440e83845..8c7437e523 100644
--- a/packages/client/src/components/Component.svelte
+++ b/packages/client/src/components/Component.svelte
@@ -8,11 +8,18 @@
import { hashString } from "../utils/hash"
import Manifest from "@budibase/standard-components/manifest.json"
import { Placeholder } from "@budibase/standard-components"
+ import {
+ getActiveConditions,
+ reduceConditionActions,
+ } from "../utils/conditions"
export let instance = {}
- // Props that will be passed to the component instance
- let componentProps
+ // The enriched component settings
+ let enrichedSettings
+
+ // Any prop overrides that need to be applied due to conditional UI
+ let conditionalSettings
// Props are hashed when inside the builder preview and used as a key, so that
// components fully remount whenever any props change
@@ -28,6 +35,9 @@
let lastContextKey
let lastInstanceKey
+ // Visibility flag used by conditional UI
+ let visible = true
+
// Get contexts
const context = getContext("context")
const insideScreenslot = !!getContext("screenslot")
@@ -54,6 +64,8 @@
$builderStore.inBuilder &&
$builderStore.selectedComponentId === instance._id
$: interactive = $builderStore.previewType === "layout" || insideScreenslot
+ $: evaluateConditions(enrichedSettings?._conditions)
+ $: componentSettings = { ...enrichedSettings, ...conditionalSettings }
// Update component context
$: componentStore.set({
@@ -62,14 +74,14 @@
styles: { ...instance._styles, id, empty, interactive },
empty,
selected,
- props: componentProps,
+ props: componentSettings,
name,
})
const getRawProps = instance => {
let validProps = {}
Object.entries(instance)
- .filter(([name]) => !name.startsWith("_"))
+ .filter(([name]) => name === "_conditions" || !name.startsWith("_"))
.forEach(([key, value]) => {
validProps[key] = value
})
@@ -123,34 +135,55 @@
return
}
let propsChanged = false
- if (!componentProps) {
- componentProps = {}
+ if (!enrichedSettings) {
+ enrichedSettings = {}
propsChanged = true
}
Object.keys(enrichedProps).forEach(key => {
- if (!propsAreSame(enrichedProps[key], componentProps[key])) {
+ if (!propsAreSame(enrichedProps[key], enrichedSettings[key])) {
propsChanged = true
- componentProps[key] = enrichedProps[key]
+ enrichedSettings[key] = enrichedProps[key]
}
})
// Update the hash if we're in the builder so we can fully remount this
// component
if (get(builderStore).inBuilder && propsChanged) {
- propsHash = hashString(JSON.stringify(componentProps))
+ propsHash = hashString(JSON.stringify(enrichedSettings))
}
}
+
+ const evaluateConditions = conditions => {
+ if (!conditions?.length) {
+ return
+ }
+
+ // Default visible to false if there is a show condition
+ let nextVisible = !conditions.find(condition => condition.action === "show")
+
+ // Execute conditions and determine settings and visibility changes
+ const activeConditions = getActiveConditions(conditions)
+ const result = reduceConditionActions(activeConditions)
+ if (result.visible != null) {
+ nextVisible = result.visible
+ }
+
+ // Update state from condition results
+ conditionalSettings = result.settingUpdates
+ visible = nextVisible
+ }
-
- {#key propsHash}
- {#if constructor && componentProps}
-
+{#key propsHash}
+ {#if constructor && componentSettings && visible}
+
+
{#if children.length}
{#each children as child (child._id)}
@@ -159,9 +192,9 @@
{/if}
- {/if}
- {/key}
-
+
+ {/if}
+{/key}