Merge pull request #2372 from mslourens/autocomplete_component
Autocomplete component
This commit is contained in:
commit
7c2b5b6f04
|
@ -11,6 +11,7 @@
|
||||||
export let getOptionLabel = option => option
|
export let getOptionLabel = option => option
|
||||||
export let getOptionValue = option => option
|
export let getOptionValue = option => option
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
|
export let autocomplete = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
$: selectedLookupMap = getSelectedLookupMap(value)
|
$: selectedLookupMap = getSelectedLookupMap(value)
|
||||||
|
@ -77,6 +78,7 @@
|
||||||
{fieldText}
|
{fieldText}
|
||||||
{options}
|
{options}
|
||||||
isPlaceholder={!value?.length}
|
isPlaceholder={!value?.length}
|
||||||
|
{autocomplete}
|
||||||
{isOptionSelected}
|
{isOptionSelected}
|
||||||
{getOptionLabel}
|
{getOptionLabel}
|
||||||
{getOptionValue}
|
{getOptionValue}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import { fly } from "svelte/transition"
|
import { fly } from "svelte/transition"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import clickOutside from "../../Actions/click_outside"
|
import clickOutside from "../../Actions/click_outside"
|
||||||
|
import Search from "./Search.svelte"
|
||||||
|
|
||||||
export let id = null
|
export let id = null
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
@ -23,16 +24,24 @@
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
export let quiet = false
|
export let quiet = false
|
||||||
export let autoWidth = false
|
export let autoWidth = false
|
||||||
|
export let autocomplete = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
let searchTerm = null
|
||||||
|
|
||||||
$: sortedOptions = getSortedOptions(options, getOptionLabel)
|
$: sortedOptions = getSortedOptions(options, getOptionLabel)
|
||||||
|
$: filteredOptions = getFilteredOptions(
|
||||||
|
sortedOptions,
|
||||||
|
searchTerm,
|
||||||
|
getOptionLabel
|
||||||
|
)
|
||||||
|
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
dispatch("click")
|
dispatch("click")
|
||||||
if (readonly) {
|
if (readonly) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
searchTerm = null
|
||||||
open = true
|
open = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +55,16 @@
|
||||||
return labelA > labelB ? 1 : -1
|
return labelA > labelB ? 1 : -1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getFilteredOptions = (options, term, getLabel) => {
|
||||||
|
if (autocomplete && term) {
|
||||||
|
const lowerCaseTerm = term.toLowerCase()
|
||||||
|
return options.filter(option =>
|
||||||
|
getLabel(option)?.toLowerCase().includes(lowerCaseTerm)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
@ -96,6 +115,14 @@
|
||||||
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
|
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
|
||||||
class:auto-width={autoWidth}
|
class:auto-width={autoWidth}
|
||||||
>
|
>
|
||||||
|
{#if autocomplete}
|
||||||
|
<Search
|
||||||
|
value={searchTerm}
|
||||||
|
on:change={event => (searchTerm = event.detail)}
|
||||||
|
{disabled}
|
||||||
|
placeholder="Search"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
<ul class="spectrum-Menu" role="listbox">
|
<ul class="spectrum-Menu" role="listbox">
|
||||||
{#if placeholderOption}
|
{#if placeholderOption}
|
||||||
<li
|
<li
|
||||||
|
@ -116,8 +143,8 @@
|
||||||
</svg>
|
</svg>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
{#if sortedOptions.length}
|
{#if filteredOptions.length}
|
||||||
{#each sortedOptions as option, idx}
|
{#each filteredOptions as option, idx}
|
||||||
<li
|
<li
|
||||||
class="spectrum-Menu-item"
|
class="spectrum-Menu-item"
|
||||||
class:is-selected={isOptionSelected(getOptionValue(option, idx))}
|
class:is-selected={isOptionSelected(getOptionValue(option, idx))}
|
||||||
|
@ -188,4 +215,23 @@
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
.spectrum-Popover :global(.spectrum-Search) {
|
||||||
|
margin-top: -1px;
|
||||||
|
margin-left: -1px;
|
||||||
|
width: calc(100% + 2px);
|
||||||
|
}
|
||||||
|
.spectrum-Popover :global(.spectrum-Search input) {
|
||||||
|
height: auto;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
padding-top: var(--spectrum-global-dimension-size-100);
|
||||||
|
padding-bottom: var(--spectrum-global-dimension-size-100);
|
||||||
|
}
|
||||||
|
.spectrum-Popover :global(.spectrum-Search .spectrum-ClearButton) {
|
||||||
|
right: 1px;
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
|
.spectrum-Popover :global(.spectrum-Search .spectrum-Textfield-icon) {
|
||||||
|
top: 9px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -95,4 +95,7 @@
|
||||||
.is-disabled {
|
.is-disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
.spectrum-Search-clearButton {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
export let quiet = false
|
export let quiet = false
|
||||||
export let autoWidth = false
|
export let autoWidth = false
|
||||||
|
export let autocomplete = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let open = false
|
let open = false
|
||||||
|
@ -70,6 +71,7 @@
|
||||||
{getOptionValue}
|
{getOptionValue}
|
||||||
{getOptionIcon}
|
{getOptionIcon}
|
||||||
{fieldIcon}
|
{fieldIcon}
|
||||||
|
{autocomplete}
|
||||||
isPlaceholder={value == null || value === ""}
|
isPlaceholder={value == null || value === ""}
|
||||||
placeholderOption={placeholder}
|
placeholderOption={placeholder}
|
||||||
isOptionSelected={option => option === value}
|
isOptionSelected={option => option === value}
|
||||||
|
|
|
@ -1926,6 +1926,16 @@
|
||||||
"label": "Default value",
|
"label": "Default value",
|
||||||
"key": "defaultValue"
|
"key": "defaultValue"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Autocomplete",
|
||||||
|
"key": "autocomplete",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "optionsType",
|
||||||
|
"value": "select"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
|
@ -2192,6 +2202,12 @@
|
||||||
"label": "Placeholder",
|
"label": "Placeholder",
|
||||||
"key": "placeholder"
|
"key": "placeholder"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Autocomplete",
|
||||||
|
"key": "autocomplete",
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
export let labelColumn
|
export let labelColumn
|
||||||
export let valueColumn
|
export let valueColumn
|
||||||
export let customOptions
|
export let customOptions
|
||||||
|
export let autocomplete = false
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
@ -85,6 +86,7 @@
|
||||||
on:change={e => fieldApi.setValue(e.detail)}
|
on:change={e => fieldApi.setValue(e.detail)}
|
||||||
getOptionLabel={flatOptions ? x => x : x => x.label}
|
getOptionLabel={flatOptions ? x => x : x => x.label}
|
||||||
getOptionValue={flatOptions ? x => x : x => x.value}
|
getOptionValue={flatOptions ? x => x : x => x.value}
|
||||||
|
{autocomplete}
|
||||||
/>
|
/>
|
||||||
{:else if optionsType === "radio"}
|
{:else if optionsType === "radio"}
|
||||||
<CoreRadioGroup
|
<CoreRadioGroup
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let validation
|
export let validation
|
||||||
|
export let autocomplete = false
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
$: fetchTable(linkedTableId)
|
$: fetchTable(linkedTableId)
|
||||||
$: singleValue = flatten($fieldState?.value)?.[0]
|
$: singleValue = flatten($fieldState?.value)?.[0]
|
||||||
$: multiValue = flatten($fieldState?.value) ?? []
|
$: multiValue = flatten($fieldState?.value) ?? []
|
||||||
|
$: component = multiselect ? CoreMultiselect : CoreSelect
|
||||||
|
|
||||||
const fetchTable = async id => {
|
const fetchTable = async id => {
|
||||||
if (id) {
|
if (id) {
|
||||||
|
@ -74,8 +76,9 @@
|
||||||
>
|
>
|
||||||
{#if fieldState}
|
{#if fieldState}
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={multiselect ? CoreMultiselect : CoreSelect}
|
this={component}
|
||||||
{options}
|
{options}
|
||||||
|
{autocomplete}
|
||||||
value={multiselect ? multiValue : singleValue}
|
value={multiselect ? multiValue : singleValue}
|
||||||
on:change={multiselect ? multiHandler : singleHandler}
|
on:change={multiselect ? multiHandler : singleHandler}
|
||||||
id={$fieldState.fieldId}
|
id={$fieldState.fieldId}
|
||||||
|
|
Loading…
Reference in New Issue