mirror of
https://github.com/LukeHagar/skeleton.git
synced 2025-12-09 20:57:45 +00:00
feat/Autocomplete Generics (#2021)
Co-authored-by: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com>
This commit is contained in:
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"cSpell.language": "en"
|
"cSpell.language": "en"
|
||||||
}
|
}
|
||||||
|
|||||||
5
packages/skeleton/.changeset/gold-jars-pretend.md
Normal file
5
packages/skeleton/.changeset/gold-jars-pretend.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@skeletonlabs/skeleton': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: Added support for generic types on values provided to Autocomplete component
|
||||||
@@ -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}`;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 } },
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user