Add initial work on evaluation of conditional UI conditions in client library

This commit is contained in:
Andrew Kingston 2021-07-21 14:03:49 +01:00
parent 4bdfb49742
commit 5e5332cd4f
4 changed files with 111 additions and 24 deletions

View File

@ -530,6 +530,11 @@ export const getFrontendStore = () => {
selected._styles = { normal: {}, hover: {}, active: {} } selected._styles = { normal: {}, hover: {}, active: {} }
await store.actions.preview.saveSelected() await store.actions.preview.saveSelected()
}, },
updateConditions: async conditions => {
const selected = get(selectedComponent)
selected._conditions = conditions
await store.actions.preview.saveSelected()
},
updateProp: async (name, value) => { updateProp: async (name, value) => {
let component = get(selectedComponent) let component = get(selectedComponent)
if (!name || !component) { if (!name || !component) {

View File

@ -1,6 +1,6 @@
<script> <script>
import { DetailSummary, ActionButton, Drawer, Button } from "@budibase/bbui" import { DetailSummary, ActionButton, Drawer, Button } from "@budibase/bbui"
// import { store } from "builderStore" import { store } from "builderStore"
import ConditionalUIDrawer from "./PropertyControls/ConditionalUIDrawer.svelte" import ConditionalUIDrawer from "./PropertyControls/ConditionalUIDrawer.svelte"
export let componentInstance export let componentInstance
@ -9,12 +9,12 @@
let drawer let drawer
const openDrawer = () => { const openDrawer = () => {
tempValue = componentInstance?._conditions tempValue = JSON.parse(JSON.stringify(componentInstance?._conditions ?? []))
drawer.show() drawer.show()
} }
const save = () => { const save = () => {
// store.actions.components.updateCustomStyle(tempValue) store.actions.components.updateConditions(tempValue)
drawer.hide() drawer.hide()
} }
</script> </script>

View File

@ -8,11 +8,18 @@
import { hashString } from "../utils/hash" import { hashString } from "../utils/hash"
import Manifest from "@budibase/standard-components/manifest.json" import Manifest from "@budibase/standard-components/manifest.json"
import { Placeholder } from "@budibase/standard-components" import { Placeholder } from "@budibase/standard-components"
import {
getActiveConditions,
reduceConditionActions,
} from "../utils/conditions"
export let instance = {} export let instance = {}
// Props that will be passed to the component instance // The enriched component settings
let componentProps 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 // Props are hashed when inside the builder preview and used as a key, so that
// components fully remount whenever any props change // components fully remount whenever any props change
@ -28,6 +35,9 @@
let lastContextKey let lastContextKey
let lastInstanceKey let lastInstanceKey
// Visibility flag used by conditional UI
let visible = true
// Get contexts // Get contexts
const context = getContext("context") const context = getContext("context")
const insideScreenslot = !!getContext("screenslot") const insideScreenslot = !!getContext("screenslot")
@ -54,6 +64,8 @@
$builderStore.inBuilder && $builderStore.inBuilder &&
$builderStore.selectedComponentId === instance._id $builderStore.selectedComponentId === instance._id
$: interactive = $builderStore.previewType === "layout" || insideScreenslot $: interactive = $builderStore.previewType === "layout" || insideScreenslot
$: evaluateConditions(enrichedSettings?._conditions)
$: componentSettings = { ...enrichedSettings, ...conditionalSettings }
// Update component context // Update component context
$: componentStore.set({ $: componentStore.set({
@ -62,14 +74,14 @@
styles: { ...instance._styles, id, empty, interactive }, styles: { ...instance._styles, id, empty, interactive },
empty, empty,
selected, selected,
props: componentProps, props: componentSettings,
name, name,
}) })
const getRawProps = instance => { const getRawProps = instance => {
let validProps = {} let validProps = {}
Object.entries(instance) Object.entries(instance)
.filter(([name]) => !name.startsWith("_")) .filter(([name]) => name === "_conditions" || !name.startsWith("_"))
.forEach(([key, value]) => { .forEach(([key, value]) => {
validProps[key] = value validProps[key] = value
}) })
@ -123,34 +135,63 @@
return return
} }
let propsChanged = false let propsChanged = false
if (!componentProps) { if (!enrichedSettings) {
componentProps = {} enrichedSettings = {}
propsChanged = true propsChanged = true
} }
Object.keys(enrichedProps).forEach(key => { Object.keys(enrichedProps).forEach(key => {
if (!propsAreSame(enrichedProps[key], componentProps[key])) { if (!propsAreSame(enrichedProps[key], enrichedSettings[key])) {
propsChanged = true 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 // Update the hash if we're in the builder so we can fully remount this
// component // component
if (get(builderStore).inBuilder && propsChanged) { if (get(builderStore).inBuilder && propsChanged) {
propsHash = hashString(JSON.stringify(componentProps)) propsHash = hashString(JSON.stringify(enrichedSettings))
} }
} }
const evaluateConditions = conditions => {
console.log("evaluating")
console.log(conditions)
if (!conditions?.length) {
return
}
// Default visible to false if there is a show condition
let nextVisible = true
for (let condition of conditions) {
if (condition.action === "show") {
nextVisible = false
}
}
const activeConditions = getActiveConditions(conditions)
console.log(activeConditions)
const result = reduceConditionActions(activeConditions)
conditionalSettings = result.settingUpdates
if (result.visible != null) {
nextVisible = result.visible
}
visible = nextVisible
}
</script> </script>
<div {#key propsHash}
{#if constructor && componentSettings && visible}
<div
class={`component ${id}`} class={`component ${id}`}
data-type={interactive ? "component" : ""} data-type={interactive ? "component" : ""}
data-id={id} data-id={id}
data-name={name} data-name={name}
> class:hidden={!visible}
{#key propsHash} >
{#if constructor && componentProps} <svelte:component this={constructor} {...componentSettings}>
<svelte:component this={constructor} {...componentProps}>
{#if children.length} {#if children.length}
{#each children as child (child._id)} {#each children as child (child._id)}
<svelte:self instance={child} /> <svelte:self instance={child} />
@ -159,9 +200,9 @@
<Placeholder /> <Placeholder />
{/if} {/if}
</svelte:component> </svelte:component>
</div>
{/if} {/if}
{/key} {/key}
</div>
<style> <style>
.component { .component {

View File

@ -0,0 +1,41 @@
import {
buildLuceneQuery,
luceneQuery,
} from "../../../standard-components/src/lucene"
export const getActiveConditions = conditions => {
if (!conditions?.length) {
return []
}
const luceneCompatibleConditions = conditions.map(condition => {
return {
...condition,
type: "string",
field: "newValue",
value: condition.referenceValue,
}
})
const query = buildLuceneQuery(luceneCompatibleConditions)
console.log(luceneQuery)
return luceneQuery(luceneCompatibleConditions, query)
}
export const reduceConditionActions = conditions => {
let settingUpdates = {}
let visible = null
conditions?.forEach(condition => {
if (condition.action === "show") {
visible = true
} else if (condition.action === "hide") {
visible = false
} else if (condition.setting) {
settingUpdates[condition.setting] = condition.settingValue
}
})
return { settingUpdates, visible }
}