Add common SpectrumField component, add spectrum labels, add form label position customisation
This commit is contained in:
parent
365c503224
commit
12f3b7f6fa
|
@ -61,3 +61,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ export const styleable = (node, styles = {}) => {
|
|||
// preview
|
||||
const applyStyles = styleString => {
|
||||
node.style = addBuilderPreviewStyles(styleString, componentId, selectable)
|
||||
node.dataset.componentId = componentId
|
||||
}
|
||||
|
||||
// Applies the "normal" style definition
|
||||
|
|
|
@ -114,57 +114,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"form": {
|
||||
"name": "Form",
|
||||
"icon": "ri-file-edit-line",
|
||||
"styleable": true,
|
||||
"hasChildren": true,
|
||||
"dataProvider": true,
|
||||
"datasourceSetting": "datasource",
|
||||
"settings": [
|
||||
{
|
||||
"type": "datasource",
|
||||
"label": "Data",
|
||||
"key": "datasource"
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"label": "Theme",
|
||||
"key": "theme",
|
||||
"defaultValue": "spectrum--light",
|
||||
"options": [
|
||||
{
|
||||
"label": "Light",
|
||||
"value": "spectrum--light"
|
||||
},
|
||||
{
|
||||
"label": "Dark",
|
||||
"value": "spectrum--dark"
|
||||
},
|
||||
{
|
||||
"label": "Darkest",
|
||||
"value": "spectrum--darkest"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"label": "Size",
|
||||
"key": "size",
|
||||
"defaultValue": "spectrum--medium",
|
||||
"options": [
|
||||
{
|
||||
"label": "Medium",
|
||||
"value": "spectrum--medium"
|
||||
},
|
||||
{
|
||||
"label": "Large",
|
||||
"value": "spectrum--large"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"richtext": {
|
||||
"name": "Rich Text",
|
||||
"description": "A component that allows the user to enter long form text.",
|
||||
|
@ -1121,6 +1070,77 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"form": {
|
||||
"name": "Form",
|
||||
"icon": "ri-file-edit-line",
|
||||
"styleable": true,
|
||||
"hasChildren": true,
|
||||
"dataProvider": true,
|
||||
"datasourceSetting": "datasource",
|
||||
"settings": [
|
||||
{
|
||||
"type": "datasource",
|
||||
"label": "Data",
|
||||
"key": "datasource"
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"label": "Theme",
|
||||
"key": "theme",
|
||||
"defaultValue": "spectrum--light",
|
||||
"options": [
|
||||
{
|
||||
"label": "Light",
|
||||
"value": "spectrum--light"
|
||||
},
|
||||
{
|
||||
"label": "Dark",
|
||||
"value": "spectrum--dark"
|
||||
},
|
||||
{
|
||||
"label": "Darkest",
|
||||
"value": "spectrum--darkest"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"label": "Size",
|
||||
"key": "size",
|
||||
"defaultValue": "spectrum--medium",
|
||||
"options": [
|
||||
{
|
||||
"label": "Medium",
|
||||
"value": "spectrum--medium"
|
||||
},
|
||||
{
|
||||
"label": "Large",
|
||||
"value": "spectrum--large"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"label": "Labels",
|
||||
"key": "labelPosition",
|
||||
"defaultValue": "left",
|
||||
"options": [
|
||||
{
|
||||
"label": "Left",
|
||||
"value": "left"
|
||||
},
|
||||
{
|
||||
"label": "Right",
|
||||
"value": "right"
|
||||
},
|
||||
{
|
||||
"label": "Above",
|
||||
"value": "above"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"stringfield": {
|
||||
"name": "Text Field",
|
||||
"description": "A textfield component that allows the user to input text.",
|
||||
|
@ -1189,7 +1209,8 @@
|
|||
{
|
||||
"type": "text",
|
||||
"label": "Placeholder",
|
||||
"key": "placeholder"
|
||||
"key": "placeholder",
|
||||
"placeholder": "Choose an option"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -37,9 +37,15 @@
|
|||
"@adobe/spectrum-css-workflow-icons": "^1.1.0",
|
||||
"@budibase/bbui": "^1.52.4",
|
||||
"@budibase/svelte-ag-grid": "^0.0.16",
|
||||
"@spectrum-css/actionbutton": "^1.0.0-beta.1",
|
||||
"@spectrum-css/button": "^3.0.0-beta.6",
|
||||
"@spectrum-css/fieldlabel": "^3.0.0-beta.7",
|
||||
"@spectrum-css/icon": "^3.0.0-beta.2",
|
||||
"@spectrum-css/menu": "^3.0.0-beta.5",
|
||||
"@spectrum-css/page": "^3.0.0-beta.0",
|
||||
"@spectrum-css/picker": "^1.0.0-beta.3",
|
||||
"@spectrum-css/popover": "^3.0.0-beta.6",
|
||||
"@spectrum-css/stepper": "^3.0.0-beta.7",
|
||||
"@spectrum-css/textfield": "^3.0.0-beta.6",
|
||||
"@spectrum-css/vars": "^3.0.0-beta.2",
|
||||
"apexcharts": "^3.22.1",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import "@spectrum-css/fieldlabel/dist/index-vars.css"
|
||||
import { setContext, getContext, onMount } from "svelte"
|
||||
import { writable, get } from "svelte/store"
|
||||
import { createValidatorFromConstraints } from "./validation"
|
||||
|
@ -6,6 +7,7 @@
|
|||
export let datasource
|
||||
export let theme
|
||||
export let size
|
||||
export let labelPosition = "left"
|
||||
|
||||
const { styleable, API } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
@ -38,6 +40,10 @@
|
|||
fieldMap[field] = {
|
||||
fieldState: makeFieldState(field),
|
||||
fieldApi: makeFieldApi(field, validate),
|
||||
fieldSchema: schema?.[field] ?? {},
|
||||
fieldId: `${Math.random()
|
||||
.toString(32)
|
||||
.substr(2)}/${field}`,
|
||||
}
|
||||
fieldMap = fieldMap
|
||||
return fieldMap[field]
|
||||
|
@ -45,7 +51,7 @@
|
|||
}
|
||||
|
||||
// Provide both form API and state to children
|
||||
setContext("form", { formApi, formState })
|
||||
setContext("form", { formApi, formState, labelPosition })
|
||||
|
||||
// Creates an API for a specific field
|
||||
const makeFieldApi = (field, validate) => {
|
||||
|
@ -116,16 +122,11 @@
|
|||
dir="ltr"
|
||||
use:styleable={$component.styles}
|
||||
class={`spectrum ${size || 'spectrum--medium'} ${theme || 'spectrum--light'}`}>
|
||||
<form
|
||||
class="spectrum-Form"
|
||||
class:spectrum-Form--labelsAbove={labelPosition === 'above'}>
|
||||
{#if loaded}
|
||||
<slot />
|
||||
{/if}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.spectrum :global(label) {
|
||||
font-size: var(
|
||||
--spectrum-alias-item-text-size-m,
|
||||
var(--spectrum-global-dimension-font-size-100)
|
||||
) !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1 +1,107 @@
|
|||
Select
|
||||
<script>
|
||||
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
|
||||
|
||||
// Picker state
|
||||
let open = false
|
||||
$: options = fieldSchema?.constraints?.inclusion ?? []
|
||||
$: 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>
|
||||
<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 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 $fieldState.error}
|
||||
<div class="error">{$fieldState.error}</div>
|
||||
{/if}
|
||||
</SpectrumField>
|
||||
|
||||
<style>
|
||||
.error {
|
||||
color: var(
|
||||
--spectrum-semantic-negative-color-default,
|
||||
var(--spectrum-global-color-red-500)
|
||||
) !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<script>
|
||||
import Placeholder from "./Placeholder.svelte"
|
||||
import { getContext } from "svelte"
|
||||
|
||||
export let label
|
||||
export let field
|
||||
|
||||
const formContext = getContext("form")
|
||||
const { styleable } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
const { labelPosition, formApi } = formContext || {}
|
||||
const formField = formApi?.registerField(field) ?? {}
|
||||
const { fieldId } = formField
|
||||
|
||||
$: labelPositionClass =
|
||||
labelPosition === "top" ? "" : `spectrum-FieldLabel--${labelPosition}`
|
||||
</script>
|
||||
|
||||
{#if !fieldId}
|
||||
<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>
|
||||
{:else}
|
||||
<div class="spectrum-Form-item" use:styleable={$component.styles}>
|
||||
{#if label}
|
||||
<label
|
||||
for={fieldId}
|
||||
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelPositionClass}`}>
|
||||
{label}
|
||||
</label>
|
||||
{/if}
|
||||
<div class="spectrum-Form-itemField">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
|
@ -1,38 +1,34 @@
|
|||
<script>
|
||||
import "@spectrum-css/textfield/dist/index-vars.css"
|
||||
import { Label } from "@budibase/bbui"
|
||||
import "@spectrum-css/actionbutton/dist/index-vars.css"
|
||||
import "@spectrum-css/stepper/dist/index-vars.css"
|
||||
import { getContext } from "svelte"
|
||||
import Placeholder from "./Placeholder.svelte"
|
||||
import SpectrumField from "./SpectrumField.svelte"
|
||||
|
||||
export let field
|
||||
export let label
|
||||
export let placeholder
|
||||
export let type = "text"
|
||||
|
||||
const { styleable } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
const { formApi } = getContext("form") ?? {}
|
||||
|
||||
// Register this field with its form
|
||||
const formField = formApi?.registerField(field, validate) ?? {}
|
||||
const { formApi } = getContext("form") ?? {}
|
||||
const formField = formApi?.registerField(field) ?? {}
|
||||
const { fieldApi, fieldState } = formField
|
||||
|
||||
$: numeric = type === "number"
|
||||
|
||||
// Update value on blur only
|
||||
const onBlur = event => {
|
||||
fieldApi.setValue(event.target.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !field}
|
||||
<Placeholder>Add the Field setting to start using your component</Placeholder>
|
||||
{:else if !fieldState}
|
||||
<Placeholder>Form components need to be wrapped in a Form</Placeholder>
|
||||
{:else}
|
||||
<div class="container" use:styleable={$component.styles}>
|
||||
{#if label}
|
||||
<Label grey>{label}</Label>
|
||||
{/if}
|
||||
<div class="spectrum-Textfield" class:is-invalid={!$fieldState.valid}>
|
||||
<SpectrumField {label} {field}>
|
||||
<div class:spectrum-Stepper={type === 'number'}>
|
||||
<div
|
||||
class="spectrum-Textfield"
|
||||
class:spectrum-Stepper-textfield={numeric}
|
||||
class:is-invalid={!$fieldState.valid}>
|
||||
{#if !$fieldState.valid}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
||||
|
@ -42,27 +38,49 @@
|
|||
</svg>
|
||||
{/if}
|
||||
<input
|
||||
id={$fieldState.fieldId}
|
||||
value={$fieldState.value || ''}
|
||||
placeholder={placeholder || ''}
|
||||
on:blur={onBlur}
|
||||
{type}
|
||||
class="spectrum-Textfield-input" />
|
||||
class="spectrum-Textfield-input"
|
||||
class:spectrum-Stepper-input={numeric} />
|
||||
</div>
|
||||
{#if numeric}
|
||||
<span class="spectrum-Stepper-buttons">
|
||||
<button
|
||||
class="spectrum-ActionButton spectrum-ActionButton--sizeM spectrum-Stepper-stepUp"
|
||||
tabindex="-1">
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-ChevronUp75 spectrum-Stepper-stepUpIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
<use xlink:href="#spectrum-css-icon-Chevron75" />
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="spectrum-ActionButton spectrum-ActionButton--sizeM spectrum-Stepper-stepDown"
|
||||
tabindex="-1">
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-ChevronDown75 spectrum-Stepper-stepDownIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
<use xlink:href="#spectrum-css-icon-Chevron75" />
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
{/if}
|
||||
{#if $fieldState.error}
|
||||
<div class="error">
|
||||
<Label>{$fieldState.error}</Label>
|
||||
</div>
|
||||
<div class="error">{$fieldState.error}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</SpectrumField>
|
||||
|
||||
<style>
|
||||
.error :global(label) {
|
||||
.error {
|
||||
color: var(
|
||||
--spectrum-semantic-negative-color-default,
|
||||
var(--spectrum-global-color-red-500)
|
||||
) !important;
|
||||
margin-top: var(--spacing-s) !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -132,16 +132,31 @@
|
|||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@spectrum-css/actionbutton@^1.0.0-beta.1":
|
||||
version "1.0.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/actionbutton/-/actionbutton-1.0.0-beta.1.tgz#a6684cac108d4a9daefe0be6df8201d3c369a0d6"
|
||||
integrity sha512-QbrPMTkbkmh+dEBP66TFXmF5z3qSde+BnLR5hnlo2XMvKvnblX2VJStEbQ+hTKuSZXCRFADXyXD5o0NOYDTByQ==
|
||||
|
||||
"@spectrum-css/button@^3.0.0-beta.6":
|
||||
version "3.0.0-beta.6"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/button/-/button-3.0.0-beta.6.tgz#007919d3e7a6692e506dc9addcd46aee6b203b1a"
|
||||
integrity sha512-ZoJxezt5Pc006RR7SMG7PfC0VAdWqaGDpd21N8SEykGuz/KmNulqGW8RiSZQGMVX/jk5ZCAthPrH8cI/qtKbMg==
|
||||
|
||||
"@spectrum-css/fieldlabel@^3.0.0-beta.7":
|
||||
version "3.0.0-beta.7"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/fieldlabel/-/fieldlabel-3.0.0-beta.7.tgz#f37797565e21b3609b8fbc2dafcea8ea41ffa114"
|
||||
integrity sha512-0pseiPghqlOdALsRtidveWyt2YjfSXTZWDlSkcne/J0/QXBJOQH/7Qfy7TmROQZYRB2LqH1VzmE1zbvGwr5Aog==
|
||||
|
||||
"@spectrum-css/icon@^3.0.0-beta.2":
|
||||
version "3.0.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/icon/-/icon-3.0.0-beta.2.tgz#2dd7258ded74501b56e5fc42d0b6f0a3f4936aeb"
|
||||
integrity sha512-BEHJ68YIXSwsNAqTdq/FrS4A+jtbKzqYrsGKXdDf93ql+fHWYXRCh1EVYGHx/1696mY73DhM4snMpKGIFtXGFA==
|
||||
|
||||
"@spectrum-css/menu@^3.0.0-beta.5":
|
||||
version "3.0.0-beta.5"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/menu/-/menu-3.0.0-beta.5.tgz#99d5ea7f6760b7a89d5d732f4e91b98dd3f82d74"
|
||||
integrity sha512-jvPD5GbNdX31rdFBLxCG7KoUVGeeNYLzNXDpiGZsWme/djVTwitljgNe7bhVwCVlXZE7H20Ti/YrdafnE154Rw==
|
||||
|
||||
"@spectrum-css/page@^3.0.0-beta.0":
|
||||
version "3.0.0-beta.0"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/page/-/page-3.0.0-beta.0.tgz#885ea41b44861c5dc3aac904536f9e93c9109b58"
|
||||
|
@ -149,6 +164,21 @@
|
|||
dependencies:
|
||||
"@spectrum-css/vars" "^3.0.0-beta.2"
|
||||
|
||||
"@spectrum-css/picker@^1.0.0-beta.3":
|
||||
version "1.0.0-beta.3"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/picker/-/picker-1.0.0-beta.3.tgz#476593597b5a9e0105397e4e39350869cf6e7965"
|
||||
integrity sha512-jHzFnS5Frd3JSwZ6B8ymH/sVnNqAUBo9p93Zax4VHTUDsPTtTkvxj/Vxo4POmrJEL9v3qUB2Yk13rD2BSfEzLQ==
|
||||
|
||||
"@spectrum-css/popover@^3.0.0-beta.6":
|
||||
version "3.0.0-beta.6"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/popover/-/popover-3.0.0-beta.6.tgz#787611f020e091234e6ba7e946b0dbd0ed1a2fa2"
|
||||
integrity sha512-dUJlwxoNpB6jOR0g/ywH2cPoUz2FVsL6xPfkm6BSsLp9ejhYy0/OFF4w0Q32Fu9qJDbWJ9qaoOlPpt7IjQ+/GQ==
|
||||
|
||||
"@spectrum-css/stepper@^3.0.0-beta.7":
|
||||
version "3.0.0-beta.7"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/stepper/-/stepper-3.0.0-beta.7.tgz#fc78435ce878c5e233af13e43ed2c3e8671a2bbc"
|
||||
integrity sha512-TQL2OBcdEgbHBwehMGgqMuWdKZZQPGcBRV5FlF0TUdOT58lEqFAO43Gajqvyte1P23lNmnX8KuMwkRfQdn0RzA==
|
||||
|
||||
"@spectrum-css/textfield@^3.0.0-beta.6":
|
||||
version "3.0.0-beta.6"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/textfield/-/textfield-3.0.0-beta.6.tgz#30c044ceb403d6ea82d8046fb8f767f7fe455da6"
|
||||
|
|
Loading…
Reference in New Issue