Merge pull request #2382 from Budibase/dynamic-picker-options

Dynamic picker options
This commit is contained in:
PClmnt 2021-08-17 13:08:18 +01:00 committed by GitHub
commit 272677410a
6 changed files with 241 additions and 3 deletions

View File

@ -32,9 +32,29 @@
if (!control) {
return false
}
if (setting.dependsOn && isEmpty(componentInstance[setting.dependsOn])) {
return false
// Parse dependant settings
if (setting.dependsOn) {
let dependantSetting = setting.dependsOn
let dependantValue = null
if (typeof setting.dependsOn === "object") {
dependantSetting = setting.dependsOn.setting
dependantValue = setting.dependsOn.value
}
if (!dependantSetting) {
return false
}
// If no specific value is depended upon, check if a value exists at all
// for the dependent setting
if (dependantValue == null) {
return !isEmpty(componentInstance[dependantSetting])
}
// Otherwise check the value matches
return componentInstance[dependantSetting] === dependantValue
}
return true
}
</script>

View File

@ -0,0 +1,82 @@
<script>
import {
Icon,
Button,
Input,
DrawerContent,
Layout,
Body,
} from "@budibase/bbui"
import { generate } from "shortid"
export let options = []
const removeOption = id => {
options = options.filter(option => option.id !== id)
}
const addOption = () => {
options = [
...options,
{
id: generate(),
label: null,
value: null,
},
]
}
</script>
<DrawerContent>
<div class="container">
<Layout noPadding gap="S">
{#if !options.length}
<Body size="S">Add an option to get started.</Body>
{/if}
{#if options?.length}
<div class="options">
{#each options as option (option.id)}
<Input
placeholder="Label"
bind:value={option.label}
label="Label"
labelPosition="left"
/>
<Input
placeholder="Value"
bind:value={option.value}
label="Value"
labelPosition="left"
/>
<Icon
name="Close"
hoverable
size="S"
on:click={() => removeOption(option.id)}
/>
{/each}
</div>
{/if}
<div>
<Button icon="AddCircle" size="M" on:click={addOption} secondary>
Add Option
</Button>
</div>
</Layout>
</div>
</DrawerContent>
<style>
.container {
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.options {
display: grid;
column-gap: var(--spacing-l);
row-gap: var(--spacing-s);
align-items: center;
grid-template-columns: 1fr 1fr auto;
}
</style>

View File

@ -0,0 +1,28 @@
<script>
import { ActionButton, Button, Drawer } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import OptionsDrawer from "./OptionsDrawer.svelte"
const dispatch = createEventDispatcher()
export let value
let drawer
let tempValue = value || []
const saveFilter = async () => {
// Filter out incomplete options
tempValue = tempValue.filter(option => option.value && option.label)
dispatch("change", tempValue)
drawer.hide()
}
</script>
<ActionButton on:click={drawer.show}>Define Options</ActionButton>
<Drawer bind:this={drawer} title="Options">
<svelte:fragment slot="description">
Define the options for this picker.
</svelte:fragment>
<Button cta slot="buttons" on:click={saveFilter}>Save</Button>
<OptionsDrawer bind:options={tempValue} slot="body" />
</Drawer>

View File

@ -12,6 +12,7 @@ import SectionSelect from "./SectionSelect.svelte"
import NavigationEditor from "./NavigationEditor/NavigationEditor.svelte"
import FilterEditor from "./FilterEditor/FilterEditor.svelte"
import URLSelect from "./URLSelect.svelte"
import OptionsEditor from "./OptionsEditor/OptionsEditor.svelte"
import FormFieldSelect from "./FormFieldSelect.svelte"
import ValidationEditor from "./ValidationEditor/ValidationEditor.svelte"
@ -28,6 +29,7 @@ const componentMap = {
icon: IconSelect,
field: FieldSelect,
multifield: MultiFieldSelect,
options: OptionsEditor,
schema: SchemaSelect,
section: SectionSelect,
navigation: NavigationEditor,

View File

@ -1908,6 +1908,7 @@
"type": "select",
"label": "Type",
"key": "optionsType",
"defaultValue": "select",
"placeholder": "Pick an options type",
"options": [
{
@ -1931,6 +1932,62 @@
"key": "disabled",
"defaultValue": false
},
{
"type": "select",
"label": "Options source",
"key": "optionsSource",
"defaultValue": "schema",
"placeholder": "Pick an options source",
"options": [
{
"label": "Schema",
"value": "schema"
},
{
"label": "Data provider",
"value": "provider"
},
{
"label": "Custom",
"value": "custom"
}
]
},
{
"type": "dataProvider",
"label": "Options Provider",
"key": "dataProvider",
"dependsOn": {
"setting": "optionsSource",
"value": "provider"
}
},
{
"type": "field",
"label": "Label Column",
"key": "labelColumn",
"dependsOn": {
"setting": "optionsSource",
"value": "provider"
}
},
{
"type": "field",
"label": "Value Column",
"key": "valueColumn",
"dependsOn": {
"setting": "optionsSource",
"value": "provider"
}
},
{
"type": "options",
"key": "customOptions",
"dependsOn": {
"setting": "optionsSource",
"value": "custom"
}
},
{
"type": "validation/string",
"label": "Validation",

View File

@ -9,10 +9,57 @@
export let optionsType = "select"
export let validation
export let defaultValue
export let optionsSource = "schema"
export let dataProvider
export let labelColumn
export let valueColumn
export let customOptions
let fieldState
let fieldApi
let fieldSchema
$: flatOptions = optionsSource == null || optionsSource === "schema"
$: options = getOptions(
optionsSource,
fieldSchema,
dataProvider,
labelColumn,
valueColumn
)
const getOptions = (
optionsSource,
fieldSchema,
dataProvider,
labelColumn,
valueColumn
) => {
// Take options from schema
if (optionsSource == null || optionsSource === "schema") {
return fieldSchema?.constraints?.inclusion ?? []
}
// Extract options from data provider
if (optionsSource === "provider" && valueColumn) {
let optionsSet = {}
dataProvider?.rows?.forEach(row => {
const value = row?.[valueColumn]
if (value) {
const label = row[labelColumn] || value
optionsSet[value] = { value, label }
}
})
return Object.values(optionsSet)
}
// Extract custom options
if (optionsSource === "custom" && customOptions) {
return customOptions
}
return []
}
</script>
<Field
@ -33,9 +80,11 @@
id={$fieldState.fieldId}
disabled={$fieldState.disabled}
error={$fieldState.error}
options={fieldSchema?.constraints?.inclusion ?? []}
{options}
{placeholder}
on:change={e => fieldApi.setValue(e.detail)}
getOptionLabel={flatOptions ? x => x : x => x.label}
getOptionValue={flatOptions ? x => x : x => x.value}
/>
{:else if optionsType === "radio"}
<RadioGroup