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