mirror of https://github.com/Budibase/budibase.git
8 changed files with 223 additions and 66 deletions
@ -0,0 +1,5 @@ |
|||
<script> |
|||
import FormFieldSelect from "./FormFieldSelect.svelte" |
|||
</script> |
|||
|
|||
<FormFieldSelect {...$$props} type="link" /> |
|||
@ -0,0 +1,103 @@ |
|||
<script> |
|||
import { fly } from "svelte/transition" |
|||
|
|||
export let fieldState |
|||
export let fieldText = "" |
|||
export let isPlaceholder = false |
|||
export let placeholderOption = null |
|||
export let options = [] |
|||
export let isOptionSelected = () => false |
|||
export let onSelectOption = () => {} |
|||
export let getOptionLabel = option => option |
|||
export let getOptionValue = option => option |
|||
export let open = false |
|||
</script> |
|||
|
|||
{#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={isPlaceholder}> |
|||
{fieldText} |
|||
</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> |
|||
{#if open} |
|||
<div class="overlay" on:mousedown|self={() => (open = false)} /> |
|||
<div |
|||
transition:fly={{ y: -20, duration: 200 }} |
|||
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"> |
|||
<ul class="spectrum-Menu" role="listbox"> |
|||
{#if placeholderOption} |
|||
<li |
|||
class="spectrum-Menu-item" |
|||
class:is-selected={isPlaceholder} |
|||
role="option" |
|||
aria-selected="true" |
|||
tabindex="0" |
|||
on:click={() => onSelectOption(null)}> |
|||
<span class="spectrum-Menu-itemLabel">{placeholderOption}</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> |
|||
{/if} |
|||
{#each options as option} |
|||
<li |
|||
class="spectrum-Menu-item" |
|||
class:is-selected={isOptionSelected(getOptionValue(option))} |
|||
role="option" |
|||
aria-selected="true" |
|||
tabindex="0" |
|||
on:click={() => onSelectOption(getOptionValue(option))}> |
|||
<span |
|||
class="spectrum-Menu-itemLabel">{getOptionLabel(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} |
|||
{/if} |
|||
|
|||
<style> |
|||
.overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100vw; |
|||
height: 100vh; |
|||
z-index: 999; |
|||
} |
|||
.spectrum-Popover { |
|||
max-height: 240px; |
|||
width: var(--spectrum-global-dimension-size-2400); |
|||
z-index: 999; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,76 @@ |
|||
<script> |
|||
import { getContext } from "svelte" |
|||
import SpectrumField from "./SpectrumField.svelte" |
|||
import Picker from "./Picker.svelte" |
|||
|
|||
const { API } = getContext("sdk") |
|||
|
|||
export let field |
|||
export let label |
|||
|
|||
let fieldState |
|||
let fieldApi |
|||
let fieldSchema |
|||
|
|||
// Picker state |
|||
let options = [] |
|||
let tableDefinition |
|||
|
|||
$: fieldText = `${$fieldState?.value?.length ?? 0} selected rows` |
|||
$: valueLookupMap = getValueLookupMap($fieldState?.value) |
|||
$: isOptionSelected = option => valueLookupMap[option] === true |
|||
$: linkedTableId = fieldSchema?.tableId |
|||
$: fetchRows(linkedTableId) |
|||
$: fetchTable(linkedTableId) |
|||
|
|||
const fetchTable = async id => { |
|||
if (id != null) { |
|||
tableDefinition = await API.fetchTableDefinition(id) |
|||
} |
|||
} |
|||
|
|||
const fetchRows = async id => { |
|||
if (id != null) { |
|||
options = await API.fetchTableData(id) |
|||
} |
|||
} |
|||
|
|||
const getDisplayName = row => { |
|||
return row[tableDefinition?.primaryDisplay || "_id"] |
|||
} |
|||
|
|||
const getValueLookupMap = value => { |
|||
let map = {} |
|||
if (value?.length) { |
|||
value.forEach(option => { |
|||
map[option] = true |
|||
}) |
|||
} |
|||
return map |
|||
} |
|||
|
|||
const toggleOption = option => { |
|||
if ($fieldState.value.includes(option)) { |
|||
fieldApi.setValue($fieldState.value.filter(x => x !== option)) |
|||
} else { |
|||
fieldApi.setValue([...$fieldState.value, option]) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<SpectrumField |
|||
{label} |
|||
{field} |
|||
bind:fieldState |
|||
bind:fieldApi |
|||
bind:fieldSchema |
|||
defaultValue={[]}> |
|||
<Picker |
|||
{fieldState} |
|||
{fieldText} |
|||
{options} |
|||
{isOptionSelected} |
|||
onSelectOption={toggleOption} |
|||
getOptionLabel={getDisplayName} |
|||
getOptionValue={option => option._id} /> |
|||
</SpectrumField> |
|||
Loading…
Reference in new issue