pstanoev / simple-svelte-autocomplete

Simple Autocomplete / typeahead component for Svelte
http://simple-svelte-autocomplete.surge.sh/
MIT License
464 stars 78 forks source link

Typescript support & typings, VS Code intellisense #205

Open rallets opened 1 year ago

rallets commented 1 year ago

Hi, I have seen in another issue that you are not interested in releasing a strongly typed version of this component. So - for who is interested - I have a repo showing one possible solution, using module augmentation and SvelteComponentTyped that supports attributes, events and slots. In this way you get VS code autocompletion and intellisense.

The typings are not comprehensive of all the functionalities, but it could be a good starting point for someone who want to extend it. It could be also a good example for similar issues with Svelte components.

The interesting part is in this file: simple-svelte-autocomplete.d.ts

Here you can find the related Svelte language-tools issue

rallets commented 1 year ago

note that from Svelte 4 SvelteComponentTyped should be renamed in SvelteComponent. No other changes are required. see: v4-migration-guide

Astronautilus14 commented 7 months ago

Here is an updated type with all supported attributes mentioned in the readme

declare module 'simple-svelte-autocomplete' {
    import type { SvelteComponent } from 'svelte';

    export interface AutoCompleteProps<T>
        extends svelte.JSX.HTMLAttributes<HTMLElementTagNameMap['div']> {
        items: T[];
        searchFunction?: () => Promise<T[]>;
        delay?: number;
        localFiltering?: boolean;
        localSorting?: boolean;
        cleanUserText?: boolean;
        multiple?: boolean;
        orderableSection?: boolean;
        selectedItem?: T;
        highlightedItem?: T;
        labelFieldName?: string;
        keywordsFieldName?: string;
        value: T;
        valueFieldName?: string;
        labelFunction?: (a: T) => string;
        keywordsFunction?: (a: T) => string;
        valueFunction?: (a: T) => string;
        keywordsCleanFunction?: (string) => string;
        lowercaseKeywords?: boolean;
        textCleanFunction?: (string) => string;
        selectFirstIfEmpty?: boolean;
        minCharactersToSearch?: number;
        maxItemsToShowInList?: number;
        ignoreAccents?: boolean;
        matchAllKeywords?: boolean;
        sortByMatchedKeywords?: boolean;
        itemSortFunction?: (a: T, b: T) => number;
        itemFilterFunction?: (a: T, string) => boolean;
        disabled?: boolean;
        readonly?: boolean;
        lock?: boolean;
        create?: boolean;
        closeOnBlur?: boolean;
        flag?: boolean;

        placeholder?: string;
        noResultsText?: string;
        moreItemsText?: string;
        createText?: string;
        hideArrow?: boolean;
        showClear?: boolean;
        showLoadingIndicator?: boolean;

        className?: string;
        inputClassName?: string;
        noInputClassName?: boolean;
        inputId?: string;
        dropdownClassName?: string;
        name?: string;
        html5autocomplete?: boolean;
        autocompleteOffValue?: string;
        selectName?: string;
        required?: boolean;
        tabIndex?: number;
    }

    export interface AutoCompleteEvents {
        beforeSearch: (oldSelectedItem: T, newSelectedItem: T) => any;
        onChange: (newSelectedItem: T) => any;
        onFocus: () => any;
        onBlur: () => any;
        onCreate: (text: string) => any;
    }

    export default class AutoComplete extends SvelteComponent<
        AutoCompleteProps<T>,
        AutoCompleteEvents,
        {
            item: { item: T };
            'no-results': null;
        }
    > {}
}
rallets commented 6 months ago

Hi @Astronautilus14 thanks for the improvements! I had the time to check your typings and found that this component doesn't emits events, but it just uses callbacks. So your definition is not correct. To make easier to sync all the attributes, I sorted them by name and split in categories. I did also add some more slots and fixed some parameters here and there. I hope this helps.

/* NB: you cannot have a import or export on top of this file, otherwise it will not work. */
declare module 'simple-svelte-autocomplete' {
    import { SvelteComponent } from 'svelte';

    export interface AutoCompleteAttributes<T>
        extends svelte.JSX.HTMLAttributes<HTMLElementTagNameMap['div']> {
        autocompleteOffValue?: string;
        className?: string;
        cleanUserText?: boolean;
        closeOnBlur?: boolean;
        create?: boolean;
        createText?: string;
        delay?: number;
        disabled?: boolean;
        dropdownClassName?: string;
        flag?: boolean;
        hideArrow?: boolean;
        highlightedItem?: T;
        html5autocomplete?: boolean;
        ignoreAccents?: boolean;
        inputClassName?: string;
        inputId?: string;
        items: T[];
        keywordsFieldName?: string;
        labelFieldName?: string;
        localFiltering?: boolean;
        localSorting?: boolean;
        lock?: boolean;
        lowercaseKeywords?: boolean;
        matchAllKeywords?: boolean;
        maxItemsToShowInList?: number;
        minCharactersToSearch?: number;
        moreItemsText?: string;
        multiple?: boolean;
        name?: string;
        noInputClassName?: boolean;
        noInputStyles?: boolean;
        noResultsText?: string;
        orderableSection?: boolean;
        placeholder?: string;
        readonly?: boolean;
        required?: boolean;
        selectFirstIfEmpty?: boolean;
        selectName?: string;
        selectedItem?: T;
        showClear?: boolean;
        showLoadingIndicator?: boolean;
        sortByMatchedKeywords?: boolean;
        tabIndex?: number;
        value?: T;
        valueFieldName?: string;
    }

    export interface AutoCompleteFunctions<T> {
        itemFilterFunction?: (item: T, keywords: string) => boolean;
        itemSortFunction?: (item1: T, item2: T, keywords: string) => number;
        keywordsCleanFunction?: (keywords: string) => string;
        keywordsFunction?: (item: T) => string;
        labelFunction?: (item: T) => string;
        searchFunction?: (keyword: string, maxItemsToShowInList: number) => Promise<T[]> | boolean;
        textCleanFunction?: (string) => string;
        valueFunction?: (a: T) => string;
    }

    export interface AutoCompleteCallbacks<T> {
        beforeChange?: (oldSelectedItem: T, newSelectedItem: T) => boolean;
        onChange?: (newSelectedItem: T) => void,
        onFocus?: () => void;
        onBlur?: () => void;
        onCreate?: (text: string) => void;
    }

    export interface AutoCompleteSlots<T> {
        'item': { item: T, label: string },
        'no-results': null,
        'loading': { loadingText: string },
        'tag': null,
        'dropdown-header': { nbItems: number, maxItemsToShowInList: number },
        'dropdown-footer': { nbItems: number, maxItemsToShowInList: number }
    }

    export interface AutoCompleteProps<T> extends AutoCompleteAttributes<T>, AutoCompleteCallbacks<T>, AutoCompleteFunctions<T> { }

    export default class AutoComplete extends SvelteComponent<AutoCompleteProps<T>, undefined, AutoCompleteSlots<T>> { }
}