forked from tsai/budibase
9 changed files with 311 additions and 91 deletions
@ -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} |
||||
Loading…
Reference in new issue