feat/Autocomplete Generics (#2021)

Co-authored-by: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com>
This commit is contained in:
Evgenii Perminov
2023-09-18 19:03:02 +02:00
committed by GitHub
parent 53dc1d761c
commit b92d622e3e
5 changed files with 50 additions and 37 deletions

View File

@@ -1,3 +1,3 @@
{ {
"cSpell.language": "en" "cSpell.language": "en"
} }

View File

@@ -0,0 +1,5 @@
---
'@skeletonlabs/skeleton': minor
---
feat: Added support for generic types on values provided to Autocomplete component

View File

@@ -1,51 +1,58 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
import { type Transition, type TransitionParams, prefersReducedMotionStore } from '../../index.js'; import { prefersReducedMotionStore, type Transition, type TransitionParams } from '../../index.js';
import { dynamicTransition } from '../../internal/transitions.js'; import { dynamicTransition } from '../../internal/transitions.js';
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
type SlideTransition = typeof slide; type SlideTransition = typeof slide;
type TransitionIn = Transition; type TransitionIn = Transition;
type TransitionOut = Transition; type TransitionOut = Transition;
type Value = unknown;
type Meta = unknown;
</script> </script>
<script lang="ts" generics="TransitionIn extends Transition = SlideTransition, TransitionOut extends Transition = SlideTransition"> <script
lang="ts"
generics="Value = unknown, Meta = unknown,
TransitionIn extends Transition = SlideTransition, TransitionOut extends Transition = SlideTransition"
>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
// import { flip } from 'svelte/animate'; // import { flip } from 'svelte/animate';
// import {slide} from 'svelte/transition'; // import {slide} from 'svelte/transition';
// Types // Types
import type { AutocompleteOption } from './types.js'; import type { AutocompleteOption } from './types.js';
type Option = AutocompleteOption<Value, Meta>;
// Event Dispatcher // Event Dispatcher
type AutocompleteEvent = { type AutocompleteEvent = {
selection: AutocompleteOption; selection: Option;
}; };
const dispatch = createEventDispatcher<AutocompleteEvent>(); const dispatch = createEventDispatcher<AutocompleteEvent>();
// Props // Props
/** /**
* Bind the input value. * Bind the input value.
* @type {unknown} * @type {Value | undefined}
*/ */
export let input: unknown = undefined; export let input: Value | undefined = undefined;
/** /**
* Define values for the list. * Define values for the list.
* @type {AutocompleteOption[]} * @type {AutocompleteOption[]}
*/ */
export let options: AutocompleteOption[] = []; export let options: Option[] = [];
/** Limit the total number of suggestions. */ /** Limit the total number of suggestions. */
export let limit: number | undefined = undefined; export let limit: number | undefined = undefined;
/** /**
* Provide allowlist values. * Provide allowlist values.
* @type {unknown[]} * @type {Value[]}
*/ */
export let allowlist: unknown[] = []; export let allowlist: Value[] = [];
/** /**
* Provide denylist values. * Provide denylist values.
* @type {unknown[]} * @type {Value[]}
*/ */
export let denylist: unknown[] = []; export let denylist: Value[] = [];
/** Provide a HTML markup to display when no match is found. */ /** Provide a HTML markup to display when no match is found. */
export let emptyState = 'No Results Found.'; export let emptyState = 'No Results Found.';
// Props (region) // Props (region)
@@ -90,7 +97,7 @@
// Local // Local
$: listedOptions = options; $: listedOptions = options;
function filterByAllowDeny(allowlist: unknown[], denylist: unknown[]) { function filterByAllowDeny(allowlist: Value[], denylist: Value[]) {
let _options = [...options]; let _options = [...options];
// Allowed Options // Allowed Options
if (allowlist.length) { if (allowlist.length) {
@@ -110,7 +117,7 @@
listedOptions = _options; listedOptions = _options;
} }
function filterOptions(): AutocompleteOption[] { function filterOptions(): Option[] {
// Create a local copy of options // Create a local copy of options
let _options = [...listedOptions]; let _options = [...listedOptions];
// Filter options // Filter options
@@ -125,15 +132,15 @@
return _options; return _options;
} }
function onSelection(option: AutocompleteOption) { function onSelection(option: Option) {
/** @event {AutocompleteOption} selection - Fire on option select. */ /** @event {Option} selection - Fire on option select. */
dispatch('selection', option); dispatch('selection', option);
} }
// State // State
$: filterByAllowDeny(allowlist, denylist); $: filterByAllowDeny(allowlist, denylist);
$: optionsFiltered = input ? filterOptions() : listedOptions; $: optionsFiltered = input ? filterOptions() : listedOptions;
$: sliceLimit = limit !== undefined ? limit : optionsFiltered.length; $: sliceLimit = limit ?? optionsFiltered.length;
// Reactive // Reactive
$: classesBase = `${$$props.class ?? ''}`; $: classesBase = `${$$props.class ?? ''}`;
$: classesNav = `${regionNav}`; $: classesNav = `${regionNav}`;

View File

@@ -1,12 +1,12 @@
// Autocomplete Types // Autocomplete Types
export interface AutocompleteOption { export interface AutocompleteOption<Value = unknown, Meta = unknown> {
/** provide a unique display label per option. Supports HTML. */ /** Provide a unique display label per option. Supports HTML. */
label: string; label: string;
/** Provide a unique option value. */ /** Provide a unique option value. */
value: unknown; value: Value;
/** Provide a comma separated list of keywords. */ /** Provide a comma separated list of keywords. */
keywords?: any; keywords?: string;
/** Pass arbitrary data per option. */ /** Pass arbitrary data per option. */
meta?: any; meta?: Meta;
} }

View File

@@ -39,7 +39,8 @@
let inputDemo = ''; let inputDemo = '';
let inputAllowlist = ''; let inputAllowlist = '';
const flavorOptions: AutocompleteOption[] = [ type FlavorOption = AutocompleteOption<string, { healthy: boolean }>;
const flavorOptions: FlavorOption[] = [
{ label: 'Vanilla', value: 'vanilla', keywords: 'plain, basic', meta: { healthy: false } }, { label: 'Vanilla', value: 'vanilla', keywords: 'plain, basic', meta: { healthy: false } },
{ label: 'Chocolate', value: 'chocolate', keywords: 'dark, white', meta: { healthy: false } }, { label: 'Chocolate', value: 'chocolate', keywords: 'dark, white', meta: { healthy: false } },
{ label: 'Strawberry', value: 'strawberry', keywords: 'fruit', meta: { healthy: true } }, { label: 'Strawberry', value: 'strawberry', keywords: 'fruit', meta: { healthy: true } },
@@ -47,37 +48,37 @@
{ label: 'Pineapple', value: 'pineapple', keywords: 'fruit', meta: { healthy: true } }, { label: 'Pineapple', value: 'pineapple', keywords: 'fruit', meta: { healthy: true } },
{ label: 'Peach', value: 'peach', keywords: 'fruit', meta: { healthy: true } } { label: 'Peach', value: 'peach', keywords: 'fruit', meta: { healthy: true } }
]; ];
const flavorAllowlist: string[] = ['neapolitan', 'pineapple', 'peach']; const flavorAllowlist = ['neapolitan', 'pineapple', 'peach'];
let flavorDenylist: string[] = ['vanilla', 'chocolate']; let flavorDenylist = ['vanilla', 'chocolate'];
// Input Chip // Input Chip
let inputChip = ''; let inputChip = '';
let inputChipList: string[] = ['vanilla', 'chocolate']; let inputChipList = ['vanilla', 'chocolate'];
function onDemoSelection(event: CustomEvent<AutocompleteOption>): void { function onDemoSelection(event: CustomEvent<FlavorOption>): void {
console.log(event.detail); console.log(event.detail);
inputDemo = event.detail.label; inputDemo = event.detail.label;
} }
function onAllowedlistSelect(event: CustomEvent<AutocompleteOption>): void { function onAllowedlistSelect(event: CustomEvent<FlavorOption>): void {
console.log(event.detail); console.log(event.detail);
inputAllowlist = event.detail.label; inputAllowlist = event.detail.label;
} }
function onDeniedlistSelect(event: CustomEvent<AutocompleteOption>): void { function onDeniedlistSelect(event: CustomEvent<FlavorOption>): void {
console.log(event.detail); console.log(event.detail);
flavorDenylist = [event.detail.value as string]; flavorDenylist = [event.detail.value];
} }
function onInputChipSelect(event: CustomEvent<AutocompleteOption>): void { function onInputChipSelect(event: CustomEvent<FlavorOption>): void {
console.log('onInputChipSelect', event.detail); console.log('onInputChipSelect', event.detail);
if (inputChipList.includes(event.detail.value as string) === false) { if (inputChipList.includes(event.detail.value) === false) {
inputChipList = [...inputChipList, event.detail.value as string]; inputChipList = [...inputChipList, event.detail.value];
inputChip = ''; inputChip = '';
} }
} }
function onPopupDemoSelect(event: CustomEvent<AutocompleteOption>): void { function onPopupDemoSelect(event: CustomEvent<FlavorOption>): void {
inputPopupDemo = event.detail.label; inputPopupDemo = event.detail.label;
} }
</script> </script>
@@ -101,7 +102,7 @@
<CodeBlock <CodeBlock
language="ts" language="ts"
code={` code={`
const flavorOptions: AutocompleteOption[] = [ const flavorOptions: AutocompleteOption<string>[] = [
{ label: 'Vanilla', value: 'vanilla', keywords: 'plain, basic', meta: { healthy: false } }, { label: 'Vanilla', value: 'vanilla', keywords: 'plain, basic', meta: { healthy: false } },
{ label: 'Chocolate', value: 'chocolate', keywords: 'dark, white', meta: { healthy: false } }, { label: 'Chocolate', value: 'chocolate', keywords: 'dark, white', meta: { healthy: false } },
{ label: 'Strawberry', value: 'strawberry', keywords: 'fruit', meta: { healthy: true } }, { label: 'Strawberry', value: 'strawberry', keywords: 'fruit', meta: { healthy: true } },
@@ -115,7 +116,7 @@ const flavorOptions: AutocompleteOption[] = [
<CodeBlock <CodeBlock
language="ts" language="ts"
code={` code={`
function onFlavorSelection(event: CustomEvent<AutocompleteOption>): void { function onFlavorSelection(event: CustomEvent<AutocompleteOption<string>>): void {
inputDemo = event.detail.label; inputDemo = event.detail.label;
} }
`} `}
@@ -160,7 +161,7 @@ function onFlavorSelection(event: CustomEvent<AutocompleteOption>): void {
<CodeBlock <CodeBlock
language="ts" language="ts"
code={` code={`
const flavorOptions: AutocompleteOption[] = [ const flavorOptions: AutocompleteOption<string>[] = [
{ ..., keywords: 'mix, strawberry, chocolate, vanilla' }, { ..., keywords: 'mix, strawberry, chocolate, vanilla' },
{ ..., meta: { healthy: false } }, { ..., meta: { healthy: false } },
]; ];