Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/Autocomplete Generics #2021

Merged
merged 11 commits into from
Sep 18, 2023
Merged
6 changes: 4 additions & 2 deletions .vscode/settings.json
HugeLetters marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{
"cSpell.language": "en"
}
"cSpell.language": "en",
"prettier.singleQuote": true,
"prettier.semi": true
}
5 changes: 5 additions & 0 deletions packages/skeleton/.changeset/gold-jars-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@skeletonlabs/skeleton': patch
---

bugfix:Added support for type generics on values provided to Autocomplete component
HugeLetters marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
<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;
</script>

<script lang="ts" generics="TransitionIn extends Transition = SlideTransition, TransitionOut extends Transition = SlideTransition">
<script
lang="ts"
generics="Value = unknown, TransitionIn extends Transition = SlideTransition, TransitionOut extends Transition = SlideTransition"
>
import { createEventDispatcher } from 'svelte';
// import { flip } from 'svelte/animate';
// import {slide} from 'svelte/transition';
Expand All @@ -19,33 +23,33 @@

// Event Dispatcher
type AutocompleteEvent = {
selection: AutocompleteOption;
selection: AutocompleteOption<Value>;
};
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[]}
* @type {AutocompleteOption<Value>[]}
*/
export let options: AutocompleteOption[] = [];
export let options: AutocompleteOption<Value>[] = [];
/** 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)
Expand Down Expand Up @@ -90,7 +94,7 @@
// Local
$: listedOptions = options;

function filterByAllowDeny(allowlist: unknown[], denylist: unknown[]) {
function filterByAllowDeny(allowlist: Value[], denylist: Value[]) {
let _options = [...options];
// Allowed Options
if (allowlist.length) {
Expand All @@ -110,7 +114,7 @@
listedOptions = _options;
}

function filterOptions(): AutocompleteOption[] {
function filterOptions(): AutocompleteOption<Value>[] {
// Create a local copy of options
let _options = [...listedOptions];
// Filter options
Expand All @@ -125,15 +129,15 @@
return _options;
}

function onSelection(option: AutocompleteOption) {
/** @event {AutocompleteOption} selection - Fire on option select. */
function onSelection(option: AutocompleteOption<Value>) {
/** @event {AutocompleteOption<Value>} 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}`;
Expand Down
6 changes: 3 additions & 3 deletions packages/skeleton/src/lib/components/Autocomplete/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Autocomplete Types

export interface AutocompleteOption {
/** provide a unique display label per option. Supports HTML. */
export interface AutocompleteOption<Value = 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;
/** Pass arbitrary data per option. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,45 +39,46 @@

let inputDemo = '';
let inputAllowlist = '';
const flavorOptions: AutocompleteOption[] = [
type FlavorOption = AutocompleteOption<string>;
const flavorOptions = [
{ 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 } },
{ label: 'Neapolitan', value: 'neapolitan', keywords: 'mix, strawberry, chocolate, vanilla', meta: { healthy: false } },
{ 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'];
] satisfies FlavorOption[];
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];
}

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];
inputChip = '';
}
}

function onPopupDemoSelect(event: CustomEvent<AutocompleteOption>): void {
function onPopupDemoSelect(event: CustomEvent<FlavorOption>): void {
inputPopupDemo = event.detail.label;
}
</script>
Expand All @@ -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 } },
Expand All @@ -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;
}
`}
Expand Down Expand Up @@ -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 } },
];
Expand Down