Merge pull request #2382 from Budibase/dynamic-picker-options
Dynamic picker options
This commit is contained in:
commit
272677410a
|
@ -32,9 +32,29 @@
|
||||||
if (!control) {
|
if (!control) {
|
||||||
return false
|
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
|
return true
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -12,6 +12,7 @@ import SectionSelect from "./SectionSelect.svelte"
|
||||||
import NavigationEditor from "./NavigationEditor/NavigationEditor.svelte"
|
import NavigationEditor from "./NavigationEditor/NavigationEditor.svelte"
|
||||||
import FilterEditor from "./FilterEditor/FilterEditor.svelte"
|
import FilterEditor from "./FilterEditor/FilterEditor.svelte"
|
||||||
import URLSelect from "./URLSelect.svelte"
|
import URLSelect from "./URLSelect.svelte"
|
||||||
|
import OptionsEditor from "./OptionsEditor/OptionsEditor.svelte"
|
||||||
import FormFieldSelect from "./FormFieldSelect.svelte"
|
import FormFieldSelect from "./FormFieldSelect.svelte"
|
||||||
import ValidationEditor from "./ValidationEditor/ValidationEditor.svelte"
|
import ValidationEditor from "./ValidationEditor/ValidationEditor.svelte"
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ const componentMap = {
|
||||||
icon: IconSelect,
|
icon: IconSelect,
|
||||||
field: FieldSelect,
|
field: FieldSelect,
|
||||||
multifield: MultiFieldSelect,
|
multifield: MultiFieldSelect,
|
||||||
|
options: OptionsEditor,
|
||||||
schema: SchemaSelect,
|
schema: SchemaSelect,
|
||||||
section: SectionSelect,
|
section: SectionSelect,
|
||||||
navigation: NavigationEditor,
|
navigation: NavigationEditor,
|
||||||
|
|
|
@ -1908,6 +1908,7 @@
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
"key": "optionsType",
|
"key": "optionsType",
|
||||||
|
"defaultValue": "select",
|
||||||
"placeholder": "Pick an options type",
|
"placeholder": "Pick an options type",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
|
@ -1931,6 +1932,62 @@
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"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",
|
"type": "validation/string",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
|
|
|
@ -9,10 +9,57 @@
|
||||||
export let optionsType = "select"
|
export let optionsType = "select"
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue
|
export let defaultValue
|
||||||
|
export let optionsSource = "schema"
|
||||||
|
export let dataProvider
|
||||||
|
export let labelColumn
|
||||||
|
export let valueColumn
|
||||||
|
export let customOptions
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
let fieldSchema
|
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>
|
</script>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
|
@ -33,9 +80,11 @@
|
||||||
id={$fieldState.fieldId}
|
id={$fieldState.fieldId}
|
||||||
disabled={$fieldState.disabled}
|
disabled={$fieldState.disabled}
|
||||||
error={$fieldState.error}
|
error={$fieldState.error}
|
||||||
options={fieldSchema?.constraints?.inclusion ?? []}
|
{options}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
on:change={e => fieldApi.setValue(e.detail)}
|
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"}
|
{:else if optionsType === "radio"}
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
|
|
Loading…
Reference in New Issue