Add more functionality to core SpectrumField component to simplify other form components
This commit is contained in:
parent
b8fb2ba43c
commit
8c90edf462
|
@ -22,7 +22,7 @@
|
|||
$: options = getOptions(schema, type)
|
||||
|
||||
const getOptions = (schema, fieldType) => {
|
||||
let entries = Object.entries(schema)
|
||||
let entries = Object.entries(schema ?? {})
|
||||
if (fieldType) {
|
||||
entries = entries.filter(entry => entry[1].type === fieldType)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
export let theme
|
||||
export let size
|
||||
|
||||
const { styleable, API } = getContext("sdk")
|
||||
const { styleable, API, setBindableValue } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
||||
let loaded = false
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
// Form API contains functions to control the form
|
||||
const formApi = {
|
||||
registerField: field => {
|
||||
registerField: (field, componentId) => {
|
||||
if (!field) {
|
||||
return
|
||||
}
|
||||
|
@ -38,11 +38,8 @@
|
|||
|
||||
fieldMap[field] = {
|
||||
fieldState: makeFieldState(field),
|
||||
fieldApi: makeFieldApi(field, validate),
|
||||
fieldApi: makeFieldApi(field, componentId, validate),
|
||||
fieldSchema: schema?.[field] ?? {},
|
||||
fieldId: `${Math.random()
|
||||
.toString(32)
|
||||
.substr(2)}/${field}`,
|
||||
}
|
||||
fieldMap = fieldMap
|
||||
return fieldMap[field]
|
||||
|
@ -53,9 +50,11 @@
|
|||
setContext("form", { formApi, formState })
|
||||
|
||||
// Creates an API for a specific field
|
||||
const makeFieldApi = (field, validate) => {
|
||||
const makeFieldApi = (field, componentId, validate) => {
|
||||
return {
|
||||
setValue: value => {
|
||||
console.log("setting " + componentId + " to " + value)
|
||||
setBindableValue(value, componentId)
|
||||
const { fieldState } = fieldMap[field]
|
||||
fieldState.update(state => {
|
||||
state.value = value
|
||||
|
@ -72,6 +71,9 @@
|
|||
const makeFieldState = field => {
|
||||
return writable({
|
||||
field,
|
||||
fieldId: `${Math.random()
|
||||
.toString(32)
|
||||
.substr(2)}/${field}`,
|
||||
value: null,
|
||||
error: null,
|
||||
valid: true,
|
||||
|
|
|
@ -2,17 +2,15 @@
|
|||
import "@spectrum-css/picker/dist/index-vars.css"
|
||||
import "@spectrum-css/popover/dist/index-vars.css"
|
||||
import "@spectrum-css/menu/dist/index-vars.css"
|
||||
import { getContext } from "svelte"
|
||||
import SpectrumField from "./SpectrumField.svelte"
|
||||
|
||||
export let field
|
||||
export let label
|
||||
export let placeholder
|
||||
|
||||
// Register this field with its form
|
||||
const { formApi } = getContext("form") ?? {}
|
||||
const formField = formApi?.registerField(field) ?? {}
|
||||
const { fieldApi, fieldState, fieldSchema } = formField
|
||||
let fieldState
|
||||
let fieldApi
|
||||
let fieldSchema
|
||||
|
||||
// Picker state
|
||||
let open = false
|
||||
|
@ -20,68 +18,52 @@
|
|||
$: placeholderText = placeholder || "Choose an option"
|
||||
$: isNull = $fieldState?.value == null || $fieldState?.value === ""
|
||||
|
||||
// Update value on blur only
|
||||
const selectOption = value => {
|
||||
fieldApi.setValue(value)
|
||||
open = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<SpectrumField {field} {label}>
|
||||
<button
|
||||
id={$fieldState.fieldId}
|
||||
class="spectrum-Picker"
|
||||
class:is-invalid={!$fieldState.valid}
|
||||
class:is-open={open}
|
||||
aria-haspopup="listbox"
|
||||
on:click={() => (open = true)}>
|
||||
<span class="spectrum-Picker-label" class:is-placeholder={isNull}>
|
||||
{isNull ? placeholderText : $fieldState.value}
|
||||
</span>
|
||||
{#if !$fieldState.valid}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Picker-validationIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label="Folder">
|
||||
<use xlink:href="#spectrum-icon-18-Alert" />
|
||||
</svg>
|
||||
{/if}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
||||
</svg>
|
||||
</button>
|
||||
<div
|
||||
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover"
|
||||
class:is-open={open}>
|
||||
<ul class="spectrum-Menu" role="listbox">
|
||||
<li
|
||||
class="spectrum-Menu-item"
|
||||
class:is-selected={isNull}
|
||||
role="option"
|
||||
aria-selected="true"
|
||||
tabindex="0"
|
||||
on:click={() => selectOption(null)}>
|
||||
<span class="spectrum-Menu-itemLabel">{placeholderText}</span>
|
||||
<SpectrumField {field} {label} bind:fieldState bind:fieldApi bind:fieldSchema>
|
||||
{#if fieldState}
|
||||
<button
|
||||
id={$fieldState.fieldId}
|
||||
class="spectrum-Picker"
|
||||
class:is-invalid={!$fieldState.valid}
|
||||
class:is-open={open}
|
||||
aria-haspopup="listbox"
|
||||
on:click={() => (open = true)}>
|
||||
<span class="spectrum-Picker-label" class:is-placeholder={isNull}>
|
||||
{isNull ? placeholderText : $fieldState.value}
|
||||
</span>
|
||||
{#if !$fieldState.valid}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Picker-validationIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
||||
aria-hidden="true"
|
||||
aria-label="Folder">
|
||||
<use xlink:href="#spectrum-icon-18-Alert" />
|
||||
</svg>
|
||||
</li>
|
||||
{#each options as option}
|
||||
{/if}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
||||
</svg>
|
||||
</button>
|
||||
<div
|
||||
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover"
|
||||
class:is-open={open}>
|
||||
<ul class="spectrum-Menu" role="listbox">
|
||||
<li
|
||||
class="spectrum-Menu-item"
|
||||
class:is-selected={option === $fieldState.value}
|
||||
class:is-selected={isNull}
|
||||
role="option"
|
||||
aria-selected="true"
|
||||
tabindex="0"
|
||||
on:click={() => selectOption(option)}>
|
||||
<span class="spectrum-Menu-itemLabel">{option}</span>
|
||||
on:click={() => selectOption(null)}>
|
||||
<span class="spectrum-Menu-itemLabel">{placeholderText}</span>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
||||
focusable="false"
|
||||
|
@ -89,7 +71,24 @@
|
|||
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
||||
</svg>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{#each options as option}
|
||||
<li
|
||||
class="spectrum-Menu-item"
|
||||
class:is-selected={option === $fieldState.value}
|
||||
role="option"
|
||||
aria-selected="true"
|
||||
tabindex="0"
|
||||
on:click={() => selectOption(option)}>
|
||||
<span class="spectrum-Menu-itemLabel">{option}</span>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
||||
</svg>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
</SpectrumField>
|
||||
|
|
|
@ -5,21 +5,32 @@
|
|||
|
||||
export let label
|
||||
export let field
|
||||
export let fieldState
|
||||
export let fieldApi
|
||||
export let fieldSchema
|
||||
|
||||
// Get contexts
|
||||
const formContext = getContext("form")
|
||||
const fieldGroupContext = getContext("fieldGroup")
|
||||
const { styleable } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
||||
// Register field with form
|
||||
const { formApi } = formContext || {}
|
||||
const labelPosition = fieldGroupContext?.labelPosition || "above"
|
||||
const formField = formApi?.registerField(field) ?? {}
|
||||
const { fieldId, fieldState } = formField
|
||||
const formField = formApi?.registerField(field, $component.id)
|
||||
|
||||
// Expose field properties to parent component
|
||||
fieldState = formField?.fieldState
|
||||
fieldApi = formField?.fieldApi
|
||||
fieldSchema = formField?.fieldSchema
|
||||
|
||||
// Extract label position from field group context
|
||||
$: labelPositionClass =
|
||||
labelPosition === "above" ? "" : `spectrum-FieldLabel--${labelPosition}`
|
||||
</script>
|
||||
|
||||
{#if !fieldId}
|
||||
{#if !fieldState}
|
||||
<Placeholder>Add the Field setting to start using your component</Placeholder>
|
||||
{:else if !formContext}
|
||||
<Placeholder>Form components need to be wrapped in a Form</Placeholder>
|
||||
|
@ -28,7 +39,7 @@
|
|||
<div class="spectrum-Form-item" use:styleable={$component.styles}>
|
||||
{#if label}
|
||||
<label
|
||||
for={fieldId}
|
||||
for={$fieldState.fieldId}
|
||||
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelPositionClass}`}>
|
||||
{label}
|
||||
</label>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script>
|
||||
import "@spectrum-css/textfield/dist/index-vars.css"
|
||||
import { getContext } from "svelte"
|
||||
import SpectrumField from "./SpectrumField.svelte"
|
||||
|
||||
export let field
|
||||
|
@ -8,33 +7,32 @@
|
|||
export let placeholder
|
||||
export let type = "text"
|
||||
|
||||
// Register this field with its form
|
||||
const { formApi } = getContext("form") ?? {}
|
||||
const formField = formApi?.registerField(field) ?? {}
|
||||
const { fieldApi, fieldState } = formField
|
||||
let fieldState
|
||||
let fieldApi
|
||||
|
||||
// Update value on blur only
|
||||
const onBlur = event => {
|
||||
fieldApi.setValue(event.target.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<SpectrumField {label} {field}>
|
||||
<div class="spectrum-Textfield" class:is-invalid={!$fieldState.valid}>
|
||||
{#if !$fieldState.valid}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
<use xlink:href="#spectrum-icon-18-Alert" />
|
||||
</svg>
|
||||
{/if}
|
||||
<input
|
||||
id={$fieldState.fieldId}
|
||||
value={$fieldState.value || ''}
|
||||
placeholder={placeholder || ''}
|
||||
on:blur={onBlur}
|
||||
{type}
|
||||
class="spectrum-Textfield-input" />
|
||||
</div>
|
||||
<SpectrumField {label} {field} bind:fieldState bind:fieldApi>
|
||||
{#if fieldState}
|
||||
<div class="spectrum-Textfield" class:is-invalid={!$fieldState.valid}>
|
||||
{#if !$fieldState.valid}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
<use xlink:href="#spectrum-icon-18-Alert" />
|
||||
</svg>
|
||||
{/if}
|
||||
<input
|
||||
id={$fieldState.fieldId}
|
||||
value={$fieldState.value || ''}
|
||||
placeholder={placeholder || ''}
|
||||
on:blur={onBlur}
|
||||
{type}
|
||||
class="spectrum-Textfield-input" />
|
||||
</div>
|
||||
{/if}
|
||||
</SpectrumField>
|
||||
|
|
Loading…
Reference in New Issue