forked from tsai/budibase
5 changed files with 277 additions and 80 deletions
@ -0,0 +1,38 @@ |
|||||
|
<script> |
||||
|
import { DetailSummary, ActionButton, Drawer, Button } from "@budibase/bbui" |
||||
|
// import { store } from "builderStore" |
||||
|
import ConditionalUIDrawer from "./PropertyControls/ConditionalUIDrawer.svelte" |
||||
|
|
||||
|
export let componentInstance |
||||
|
|
||||
|
let tempValue |
||||
|
let drawer |
||||
|
|
||||
|
const openDrawer = () => { |
||||
|
tempValue = componentInstance?._conditions |
||||
|
drawer.show() |
||||
|
} |
||||
|
|
||||
|
const save = () => { |
||||
|
// store.actions.components.updateCustomStyle(tempValue) |
||||
|
drawer.hide() |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<DetailSummary |
||||
|
name={`Conditions${componentInstance?._conditions ? " *" : ""}`} |
||||
|
collapsible={false} |
||||
|
> |
||||
|
<div> |
||||
|
<ActionButton on:click={openDrawer}>Configure conditions</ActionButton> |
||||
|
</div> |
||||
|
</DetailSummary> |
||||
|
{#key componentInstance?._id} |
||||
|
<Drawer bind:this={drawer} title="Conditions"> |
||||
|
<svelte:fragment slot="description"> |
||||
|
Show, hide and update components in response to conditions being met. |
||||
|
</svelte:fragment> |
||||
|
<Button cta slot="buttons" on:click={save}>Save</Button> |
||||
|
<ConditionalUIDrawer slot="body" bind:conditions={tempValue} /> |
||||
|
</Drawer> |
||||
|
{/key} |
||||
@ -0,0 +1,156 @@ |
|||||
|
<script> |
||||
|
import { Button, Icon, DrawerContent, Layout, Select } from "@budibase/bbui" |
||||
|
import { flip } from "svelte/animate" |
||||
|
import { dndzone } from "svelte-dnd-action" |
||||
|
import { generate } from "shortid" |
||||
|
import DrawerBindableInput from "../../../common/bindings/DrawerBindableInput.svelte" |
||||
|
import ColorPicker from "./ColorPicker.svelte" |
||||
|
import { OperatorOptions, getValidOperatorsForType } from "helpers/lucene" |
||||
|
import { getBindableProperties } from "../../../../builderStore/dataBinding" |
||||
|
import { currentAsset, store } from "../../../../builderStore" |
||||
|
|
||||
|
export let conditions = [] |
||||
|
|
||||
|
const flipDurationMs = 150 |
||||
|
const actionOptions = [ |
||||
|
{ |
||||
|
label: "Show component", |
||||
|
value: "show", |
||||
|
}, |
||||
|
{ |
||||
|
label: "Hide component", |
||||
|
value: "hide", |
||||
|
}, |
||||
|
{ |
||||
|
label: "Update component setting", |
||||
|
value: "update", |
||||
|
}, |
||||
|
] |
||||
|
|
||||
|
$: operatorOptions = getValidOperatorsForType("string") |
||||
|
$: bindableProperties = getBindableProperties( |
||||
|
$currentAsset, |
||||
|
$store.selectedComponentId |
||||
|
) |
||||
|
$: conditions.forEach(link => { |
||||
|
if (!link.id) { |
||||
|
link.id = generate() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const addCondition = () => { |
||||
|
conditions = [ |
||||
|
...conditions, |
||||
|
{ |
||||
|
action: "show", |
||||
|
operator: OperatorOptions.Equals.value, |
||||
|
}, |
||||
|
] |
||||
|
} |
||||
|
|
||||
|
const removeLink = id => { |
||||
|
conditions = conditions.filter(link => link.id !== id) |
||||
|
} |
||||
|
|
||||
|
const updateLinks = e => { |
||||
|
conditions = e.detail.items |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<DrawerContent> |
||||
|
<div class="container"> |
||||
|
<Layout noPadding> |
||||
|
{#if conditions?.length} |
||||
|
<div |
||||
|
class="conditions" |
||||
|
use:dndzone={{ |
||||
|
items: conditions, |
||||
|
flipDurationMs, |
||||
|
dropTargetStyle: { outline: "none" }, |
||||
|
}} |
||||
|
on:finalize={updateLinks} |
||||
|
on:consider={updateLinks} |
||||
|
> |
||||
|
{#each conditions as condition (condition.id)} |
||||
|
<div class="condition" animate:flip={{ duration: flipDurationMs }}> |
||||
|
<Select |
||||
|
placeholder={null} |
||||
|
options={actionOptions} |
||||
|
bind:value={condition.action} |
||||
|
/> |
||||
|
{#if condition.action === "update"} |
||||
|
<Select options={["Color"]} bind:value={condition.setting} /> |
||||
|
<div>TO</div> |
||||
|
<ColorPicker |
||||
|
on:change={e => (condition.settingValue = e.detail)} |
||||
|
value={condition.settingValue} |
||||
|
/> |
||||
|
{/if} |
||||
|
<div>IF</div> |
||||
|
<DrawerBindableInput |
||||
|
bindings={bindableProperties} |
||||
|
placeholder="Value" |
||||
|
value={condition.newValue} |
||||
|
on:change={e => (condition.newValue = e.detail)} |
||||
|
/> |
||||
|
<Select |
||||
|
placeholder={null} |
||||
|
options={operatorOptions} |
||||
|
bind:value={condition.operator} |
||||
|
/> |
||||
|
<DrawerBindableInput |
||||
|
bindings={bindableProperties} |
||||
|
placeholder="Value" |
||||
|
value={condition.referenceValue} |
||||
|
on:change={e => (condition.referenceValue = e.detail)} |
||||
|
/> |
||||
|
<Icon |
||||
|
name="Close" |
||||
|
hoverable |
||||
|
size="S" |
||||
|
on:click={() => removeLink(condition.id)} |
||||
|
/> |
||||
|
</div> |
||||
|
{/each} |
||||
|
</div> |
||||
|
{/if} |
||||
|
<div class="button-container"> |
||||
|
<Button secondary icon="Add" on:click={addCondition}> |
||||
|
Add condition |
||||
|
</Button> |
||||
|
</div> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
</DrawerContent> |
||||
|
|
||||
|
<style> |
||||
|
.container { |
||||
|
width: 100%; |
||||
|
max-width: 1200px; |
||||
|
} |
||||
|
.conditions { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: flex-start; |
||||
|
align-items: stretch; |
||||
|
gap: var(--spacing-s); |
||||
|
} |
||||
|
.condition { |
||||
|
gap: var(--spacing-l); |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
border-radius: var(--border-radius-s); |
||||
|
transition: background-color ease-in-out 130ms; |
||||
|
} |
||||
|
.condition:hover { |
||||
|
background-color: var(--spectrum-global-color-gray-100); |
||||
|
} |
||||
|
.condition > :global(.spectrum-Form-item) { |
||||
|
flex: 1 1 auto; |
||||
|
width: 0; |
||||
|
} |
||||
|
.button-container { |
||||
|
} |
||||
|
</style> |
||||
@ -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 [] |
||||
|
} |
||||
Loading…
Reference in new issue