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