mirror of
https://github.com/LukeHagar/skeleton.git
synced 2025-12-09 12:47:44 +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">
|
||||
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';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
type SlideTransition = typeof slide;
|
||||
type TransitionIn = Transition;
|
||||
type TransitionOut = Transition;
|
||||
type Value = unknown;
|
||||
type Meta = unknown;
|
||||
</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 { flip } from 'svelte/animate';
|
||||
// import {slide} from 'svelte/transition';
|
||||
|
||||
// Types
|
||||
import type { AutocompleteOption } from './types.js';
|
||||
type Option = AutocompleteOption<Value, Meta>;
|
||||
|
||||
// Event Dispatcher
|
||||
type AutocompleteEvent = {
|
||||
selection: AutocompleteOption;
|
||||
selection: Option;
|
||||
};
|
||||
const dispatch = createEventDispatcher<AutocompleteEvent>();
|
||||
|
||||
// Props
|
||||
/**
|
||||
* 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.
|
||||
* @type {AutocompleteOption[]}
|
||||
*/
|
||||
export let options: AutocompleteOption[] = [];
|
||||
export let options: Option[] = [];
|
||||
/** Limit the total number of suggestions. */
|
||||
export let limit: number | undefined = undefined;
|
||||
/**
|
||||
* Provide allowlist values.
|
||||
* @type {unknown[]}
|
||||
* @type {Value[]}
|
||||
*/
|
||||
export let allowlist: unknown[] = [];
|
||||
export let allowlist: Value[] = [];
|
||||
/**
|
||||
* 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. */
|
||||
export let emptyState = 'No Results Found.';
|
||||
// Props (region)
|
||||
@@ -90,7 +97,7 @@
|
||||
// Local
|
||||
$: listedOptions = options;
|
||||
|
||||
function filterByAllowDeny(allowlist: unknown[], denylist: unknown[]) {
|
||||
function filterByAllowDeny(allowlist: Value[], denylist: Value[]) {
|
||||
let _options = [...options];
|
||||
// Allowed Options
|
||||
if (allowlist.length) {
|
||||
@@ -110,7 +117,7 @@
|
||||
listedOptions = _options;
|
||||
}
|
||||
|
||||
function filterOptions(): AutocompleteOption[] {
|
||||
function filterOptions(): Option[] {
|
||||
// Create a local copy of options
|
||||
let _options = [...listedOptions];
|
||||
// Filter options
|
||||
@@ -125,15 +132,15 @@
|
||||
return _options;
|
||||
}
|
||||
|
||||
function onSelection(option: AutocompleteOption) {
|
||||
/** @event {AutocompleteOption} selection - Fire on option select. */
|
||||
function onSelection(option: Option) {
|
||||
/** @event {Option} selection - Fire on option select. */
|
||||
dispatch('selection', option);
|
||||
}
|
||||
|
||||
// State
|
||||
$: filterByAllowDeny(allowlist, denylist);
|
||||
$: optionsFiltered = input ? filterOptions() : listedOptions;
|
||||
$: sliceLimit = limit !== undefined ? limit : optionsFiltered.length;
|
||||
$: sliceLimit = limit ?? optionsFiltered.length;
|
||||
// Reactive
|
||||
$: classesBase = `${$$props.class ?? ''}`;
|
||||
$: classesNav = `${regionNav}`;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Autocomplete Types
|
||||
|
||||
export interface AutocompleteOption {
|
||||
/** provide a unique display label per option. Supports HTML. */
|
||||
export interface AutocompleteOption<Value = unknown, Meta = unknown> {
|
||||
/** Provide a unique display label per option. Supports HTML. */
|
||||
label: string;
|
||||
/** Provide a unique option value. */
|
||||
value: unknown;
|
||||
value: Value;
|
||||
/** Provide a comma separated list of keywords. */
|
||||
keywords?: any;
|
||||
keywords?: string;
|
||||
/** Pass arbitrary data per option. */
|
||||
meta?: any;
|
||||
meta?: Meta;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
|
||||
let inputDemo = '';
|
||||
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: 'Chocolate', value: 'chocolate', keywords: 'dark, white', meta: { healthy: false } },
|
||||
{ label: 'Strawberry', value: 'strawberry', keywords: 'fruit', meta: { healthy: true } },
|
||||
@@ -47,37 +48,37 @@
|
||||
{ label: 'Pineapple', value: 'pineapple', keywords: 'fruit', meta: { healthy: true } },
|
||||
{ label: 'Peach', value: 'peach', keywords: 'fruit', meta: { healthy: true } }
|
||||
];
|
||||
const flavorAllowlist: string[] = ['neapolitan', 'pineapple', 'peach'];
|
||||
let flavorDenylist: string[] = ['vanilla', 'chocolate'];
|
||||
const flavorAllowlist = ['neapolitan', 'pineapple', 'peach'];
|
||||
let flavorDenylist = ['vanilla', 'chocolate'];
|
||||
|
||||
// Input Chip
|
||||
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);
|
||||
inputDemo = event.detail.label;
|
||||
}
|
||||
|
||||
function onAllowedlistSelect(event: CustomEvent<AutocompleteOption>): void {
|
||||
function onAllowedlistSelect(event: CustomEvent<FlavorOption>): void {
|
||||
console.log(event.detail);
|
||||
inputAllowlist = event.detail.label;
|
||||
}
|
||||
|
||||
function onDeniedlistSelect(event: CustomEvent<AutocompleteOption>): void {
|
||||
function onDeniedlistSelect(event: CustomEvent<FlavorOption>): void {
|
||||
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);
|
||||
if (inputChipList.includes(event.detail.value as string) === false) {
|
||||
inputChipList = [...inputChipList, event.detail.value as string];
|
||||
if (inputChipList.includes(event.detail.value) === false) {
|
||||
inputChipList = [...inputChipList, event.detail.value];
|
||||
inputChip = '';
|
||||
}
|
||||
}
|
||||
|
||||
function onPopupDemoSelect(event: CustomEvent<AutocompleteOption>): void {
|
||||
function onPopupDemoSelect(event: CustomEvent<FlavorOption>): void {
|
||||
inputPopupDemo = event.detail.label;
|
||||
}
|
||||
</script>
|
||||
@@ -101,7 +102,7 @@
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
const flavorOptions: AutocompleteOption[] = [
|
||||
const flavorOptions: AutocompleteOption<string>[] = [
|
||||
{ label: 'Vanilla', value: 'vanilla', keywords: 'plain, basic', meta: { healthy: false } },
|
||||
{ label: 'Chocolate', value: 'chocolate', keywords: 'dark, white', meta: { healthy: false } },
|
||||
{ label: 'Strawberry', value: 'strawberry', keywords: 'fruit', meta: { healthy: true } },
|
||||
@@ -115,7 +116,7 @@ const flavorOptions: AutocompleteOption[] = [
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
function onFlavorSelection(event: CustomEvent<AutocompleteOption>): void {
|
||||
function onFlavorSelection(event: CustomEvent<AutocompleteOption<string>>): void {
|
||||
inputDemo = event.detail.label;
|
||||
}
|
||||
`}
|
||||
@@ -160,7 +161,7 @@ function onFlavorSelection(event: CustomEvent<AutocompleteOption>): void {
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
const flavorOptions: AutocompleteOption[] = [
|
||||
const flavorOptions: AutocompleteOption<string>[] = [
|
||||
{ ..., keywords: 'mix, strawberry, chocolate, vanilla' },
|
||||
{ ..., meta: { healthy: false } },
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user