Add form support for fields which can only use bindings rather than explicit onchange handlers
This commit is contained in:
parent
4630699f13
commit
c9bf2700e3
|
@ -1,27 +1,28 @@
|
|||
<script>
|
||||
import SpectrumField from "./SpectrumField.svelte"
|
||||
import Dropzone from "../attachments/Dropzone.svelte"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
export let field
|
||||
export let label
|
||||
|
||||
let previousFiles = []
|
||||
let files = []
|
||||
$: {
|
||||
// Only actually update the value when it changes, so that we don't trigger
|
||||
// validation unnecessarily
|
||||
if (files !== previousFiles) {
|
||||
fieldApi?.setValue(files)
|
||||
previousFiles = files
|
||||
}
|
||||
}
|
||||
|
||||
let fieldState
|
||||
let fieldApi
|
||||
|
||||
// Update form value from bound value after we've mounted
|
||||
let value
|
||||
let mounted = false
|
||||
$: mounted && fieldApi?.setValue(value)
|
||||
|
||||
// Get the fields initial value after initialising
|
||||
onMount(() => {
|
||||
value = $fieldState?.value
|
||||
mounted = true
|
||||
})
|
||||
</script>
|
||||
|
||||
<SpectrumField {label} {field} bind:fieldState bind:fieldApi>
|
||||
{#if fieldState}
|
||||
<Dropzone bind:files />
|
||||
<SpectrumField {label} {field} bind:fieldState bind:fieldApi defaultValue={[]}>
|
||||
{#if mounted}
|
||||
<Dropzone bind:files={value} />
|
||||
{/if}
|
||||
</SpectrumField>
|
||||
|
|
|
@ -13,18 +13,19 @@
|
|||
const onChange = event => {
|
||||
fieldApi.setValue(event.target.checked)
|
||||
}
|
||||
|
||||
// Ensure a valid boolean value is set
|
||||
onMount(() => {
|
||||
fieldApi.setValue($fieldState.value || false)
|
||||
})
|
||||
</script>
|
||||
|
||||
<SpectrumField {label} {field} bind:fieldState bind:fieldApi>
|
||||
<SpectrumField
|
||||
{label}
|
||||
{field}
|
||||
bind:fieldState
|
||||
bind:fieldApi
|
||||
defaultValue={false}>
|
||||
{#if fieldState}
|
||||
<div class="spectrum-FieldGroup spectrum-FieldGroup--horizontal">
|
||||
<label class="spectrum-Checkbox" class:is-invalid={!$fieldState.valid}>
|
||||
<input
|
||||
checked={$fieldState.value}
|
||||
on:change={onChange}
|
||||
type="checkbox"
|
||||
class="spectrum-Checkbox-input"
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
// Form API contains functions to control the form
|
||||
const formApi = {
|
||||
registerField: field => {
|
||||
registerField: (field, defaultValue = null) => {
|
||||
if (!field) {
|
||||
return
|
||||
}
|
||||
|
@ -47,8 +47,8 @@
|
|||
const validate = createValidatorFromConstraints(constraints, field, table)
|
||||
|
||||
fieldMap[field] = {
|
||||
fieldState: makeFieldState(field),
|
||||
fieldApi: makeFieldApi(field, validate),
|
||||
fieldState: makeFieldState(field, defaultValue),
|
||||
fieldApi: makeFieldApi(field, defaultValue, validate),
|
||||
fieldSchema: schema?.[field] ?? {},
|
||||
}
|
||||
fieldMap = fieldMap
|
||||
|
@ -60,13 +60,16 @@
|
|||
setContext("form", { formApi, formState })
|
||||
|
||||
// Creates an API for a specific field
|
||||
const makeFieldApi = (field, validate) => {
|
||||
const makeFieldApi = (field, defaultValue, validate) => {
|
||||
return {
|
||||
setValue: value => {
|
||||
const { fieldState } = fieldMap[field]
|
||||
fieldState.update(state => {
|
||||
state.value = value
|
||||
state.error = validate ? validate(value) : null
|
||||
if (state.value === value) {
|
||||
return state
|
||||
}
|
||||
state.value = value == null ? defaultValue : value
|
||||
state.error = validate ? validate(state.value) : null
|
||||
state.valid = !state.error
|
||||
return state
|
||||
})
|
||||
|
@ -76,13 +79,13 @@
|
|||
}
|
||||
|
||||
// Creates observable state data about a specific field
|
||||
const makeFieldState = field => {
|
||||
const makeFieldState = (field, defaultValue) => {
|
||||
return writable({
|
||||
field,
|
||||
fieldId: `${Math.random()
|
||||
.toString(32)
|
||||
.substr(2)}/${field}`,
|
||||
value: initialValues[field] ?? null,
|
||||
value: initialValues[field] ?? defaultValue,
|
||||
error: null,
|
||||
valid: true,
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { RichText } from "@budibase/bbui"
|
||||
import SpectrumField from "./SpectrumField.svelte"
|
||||
|
||||
|
@ -8,17 +9,17 @@
|
|||
|
||||
let fieldState
|
||||
let fieldApi
|
||||
let previousValue = ""
|
||||
let value = ""
|
||||
|
||||
$: {
|
||||
// Only actually update the value when it changes, so that we don't trigger
|
||||
// validation unnecessarily
|
||||
if (value !== previousValue) {
|
||||
fieldApi?.setValue(value)
|
||||
previousValue = value
|
||||
}
|
||||
}
|
||||
// Update form value from bound value after we've mounted
|
||||
let value
|
||||
let mounted = false
|
||||
$: mounted && fieldApi?.setValue(value)
|
||||
|
||||
// Get the fields initial value after initialising
|
||||
onMount(() => {
|
||||
value = $fieldState?.value
|
||||
mounted = true
|
||||
})
|
||||
|
||||
// Options for rich text component
|
||||
const options = {
|
||||
|
@ -37,8 +38,8 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<SpectrumField {label} {field} bind:fieldState bind:fieldApi>
|
||||
{#if fieldState}
|
||||
<SpectrumField {label} {field} bind:fieldState bind:fieldApi defaultValue="">
|
||||
{#if mounted}
|
||||
<div>
|
||||
<RichText bind:value {options} />
|
||||
</div>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
export let fieldState
|
||||
export let fieldApi
|
||||
export let fieldSchema
|
||||
export let defaultValue
|
||||
|
||||
// Get contexts
|
||||
const formContext = getContext("form")
|
||||
|
@ -16,16 +17,14 @@
|
|||
const component = getContext("component")
|
||||
|
||||
// Register field with form
|
||||
$: formApi = formContext?.formApi
|
||||
$: labelPosition = fieldGroupContext?.labelPosition || "above"
|
||||
$: formField = formApi?.registerField(field)
|
||||
const formApi = formContext?.formApi
|
||||
const labelPosition = fieldGroupContext?.labelPosition || "above"
|
||||
const formField = formApi?.registerField(field, defaultValue)
|
||||
|
||||
// Expose field properties to parent component
|
||||
$: {
|
||||
fieldState = formField?.fieldState
|
||||
fieldApi = formField?.fieldApi
|
||||
fieldSchema = formField?.fieldSchema
|
||||
}
|
||||
|
||||
// Extract label position from field group context
|
||||
$: labelPositionClass =
|
||||
|
|
Loading…
Reference in New Issue