Add more functionality to core SpectrumField component to simplify other form components

This commit is contained in:
Andrew Kingston 2021-01-28 08:47:44 +00:00
parent 734e341ebb
commit 32ad2b6a08
5 changed files with 102 additions and 92 deletions

View File

@ -22,7 +22,7 @@
$: options = getOptions(schema, type) $: options = getOptions(schema, type)
const getOptions = (schema, fieldType) => { const getOptions = (schema, fieldType) => {
let entries = Object.entries(schema) let entries = Object.entries(schema ?? {})
if (fieldType) { if (fieldType) {
entries = entries.filter(entry => entry[1].type === fieldType) entries = entries.filter(entry => entry[1].type === fieldType)
} }

View File

@ -8,7 +8,7 @@
export let theme export let theme
export let size export let size
const { styleable, API } = getContext("sdk") const { styleable, API, setBindableValue } = getContext("sdk")
const component = getContext("component") const component = getContext("component")
let loaded = false let loaded = false
@ -24,7 +24,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, componentId) => {
if (!field) { if (!field) {
return return
} }
@ -38,11 +38,8 @@
fieldMap[field] = { fieldMap[field] = {
fieldState: makeFieldState(field), fieldState: makeFieldState(field),
fieldApi: makeFieldApi(field, validate), fieldApi: makeFieldApi(field, componentId, validate),
fieldSchema: schema?.[field] ?? {}, fieldSchema: schema?.[field] ?? {},
fieldId: `${Math.random()
.toString(32)
.substr(2)}/${field}`,
} }
fieldMap = fieldMap fieldMap = fieldMap
return fieldMap[field] return fieldMap[field]
@ -53,9 +50,11 @@
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, componentId, validate) => {
return { return {
setValue: value => { setValue: value => {
console.log("setting " + componentId + " to " + value)
setBindableValue(value, componentId)
const { fieldState } = fieldMap[field] const { fieldState } = fieldMap[field]
fieldState.update(state => { fieldState.update(state => {
state.value = value state.value = value
@ -72,6 +71,9 @@
const makeFieldState = field => { const makeFieldState = field => {
return writable({ return writable({
field, field,
fieldId: `${Math.random()
.toString(32)
.substr(2)}/${field}`,
value: null, value: null,
error: null, error: null,
valid: true, valid: true,

View File

@ -2,17 +2,15 @@
import "@spectrum-css/picker/dist/index-vars.css" import "@spectrum-css/picker/dist/index-vars.css"
import "@spectrum-css/popover/dist/index-vars.css" import "@spectrum-css/popover/dist/index-vars.css"
import "@spectrum-css/menu/dist/index-vars.css" import "@spectrum-css/menu/dist/index-vars.css"
import { getContext } from "svelte"
import SpectrumField from "./SpectrumField.svelte" import SpectrumField from "./SpectrumField.svelte"
export let field export let field
export let label export let label
export let placeholder export let placeholder
// Register this field with its form let fieldState
const { formApi } = getContext("form") ?? {} let fieldApi
const formField = formApi?.registerField(field) ?? {} let fieldSchema
const { fieldApi, fieldState, fieldSchema } = formField
// Picker state // Picker state
let open = false let open = false
@ -20,14 +18,14 @@
$: placeholderText = placeholder || "Choose an option" $: placeholderText = placeholder || "Choose an option"
$: isNull = $fieldState?.value == null || $fieldState?.value === "" $: isNull = $fieldState?.value == null || $fieldState?.value === ""
// Update value on blur only
const selectOption = value => { const selectOption = value => {
fieldApi.setValue(value) fieldApi.setValue(value)
open = false open = false
} }
</script> </script>
<SpectrumField {field} {label}> <SpectrumField {field} {label} bind:fieldState bind:fieldApi bind:fieldSchema>
{#if fieldState}
<button <button
id={$fieldState.fieldId} id={$fieldState.fieldId}
class="spectrum-Picker" class="spectrum-Picker"
@ -92,4 +90,5 @@
{/each} {/each}
</ul> </ul>
</div> </div>
{/if}
</SpectrumField> </SpectrumField>

View File

@ -5,21 +5,32 @@
export let label export let label
export let field export let field
export let fieldState
export let fieldApi
export let fieldSchema
// Get contexts
const formContext = getContext("form") const formContext = getContext("form")
const fieldGroupContext = getContext("fieldGroup") const fieldGroupContext = getContext("fieldGroup")
const { styleable } = getContext("sdk") const { styleable } = getContext("sdk")
const component = getContext("component") const component = getContext("component")
// Register field with form
const { formApi } = formContext || {} const { formApi } = formContext || {}
const labelPosition = fieldGroupContext?.labelPosition || "above" const labelPosition = fieldGroupContext?.labelPosition || "above"
const formField = formApi?.registerField(field) ?? {} const formField = formApi?.registerField(field, $component.id)
const { fieldId, fieldState } = formField
// Expose field properties to parent component
fieldState = formField?.fieldState
fieldApi = formField?.fieldApi
fieldSchema = formField?.fieldSchema
// Extract label position from field group context
$: labelPositionClass = $: labelPositionClass =
labelPosition === "above" ? "" : `spectrum-FieldLabel--${labelPosition}` labelPosition === "above" ? "" : `spectrum-FieldLabel--${labelPosition}`
</script> </script>
{#if !fieldId} {#if !fieldState}
<Placeholder>Add the Field setting to start using your component</Placeholder> <Placeholder>Add the Field setting to start using your component</Placeholder>
{:else if !formContext} {:else if !formContext}
<Placeholder>Form components need to be wrapped in a Form</Placeholder> <Placeholder>Form components need to be wrapped in a Form</Placeholder>
@ -28,7 +39,7 @@
<div class="spectrum-Form-item" use:styleable={$component.styles}> <div class="spectrum-Form-item" use:styleable={$component.styles}>
{#if label} {#if label}
<label <label
for={fieldId} for={$fieldState.fieldId}
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelPositionClass}`}> class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelPositionClass}`}>
{label} {label}
</label> </label>

View File

@ -1,6 +1,5 @@
<script> <script>
import "@spectrum-css/textfield/dist/index-vars.css" import "@spectrum-css/textfield/dist/index-vars.css"
import { getContext } from "svelte"
import SpectrumField from "./SpectrumField.svelte" import SpectrumField from "./SpectrumField.svelte"
export let field export let field
@ -8,18 +7,16 @@
export let placeholder export let placeholder
export let type = "text" export let type = "text"
// Register this field with its form let fieldState
const { formApi } = getContext("form") ?? {} let fieldApi
const formField = formApi?.registerField(field) ?? {}
const { fieldApi, fieldState } = formField
// Update value on blur only
const onBlur = event => { const onBlur = event => {
fieldApi.setValue(event.target.value) fieldApi.setValue(event.target.value)
} }
</script> </script>
<SpectrumField {label} {field}> <SpectrumField {label} {field} bind:fieldState bind:fieldApi>
{#if fieldState}
<div class="spectrum-Textfield" class:is-invalid={!$fieldState.valid}> <div class="spectrum-Textfield" class:is-invalid={!$fieldState.valid}>
{#if !$fieldState.valid} {#if !$fieldState.valid}
<svg <svg
@ -37,4 +34,5 @@
{type} {type}
class="spectrum-Textfield-input" /> class="spectrum-Textfield-input" />
</div> </div>
{/if}
</SpectrumField> </SpectrumField>