Repository: metonym/svelte-typeahead Branch: master Commit: 678597726233 Files: 15 Total size: 31.1 KB Directory structure: gitextract_vgom6s0h/ ├── .github/ │ └── workflows/ │ ├── ci.yml │ ├── github-pages.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── rollup.config.ts ├── src/ │ ├── Typeahead.svelte │ ├── Typeahead.svelte.d.ts │ ├── index.d.ts │ └── index.js ├── tests/ │ └── Typeahead.test.svelte └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/ci.yml ================================================ on: pull_request: push: branches: [master] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: oven-sh/setup-bun@v2 - name: Install dependencies run: bun ci - name: Run unit tests run: bun run test ================================================ FILE: .github/workflows/github-pages.yml ================================================ on: push: branches: [master] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: oven-sh/setup-bun@v2 - name: Install dependencies run: bun ci - name: Build app run: bun run rollup -c - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: dist ================================================ FILE: .github/workflows/release.yml ================================================ on: push: tags: - "v*" jobs: build: runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: 22 registry-url: "https://registry.npmjs.org" - name: Prune package.json run: npx culls --preserve=svelte - name: Publish package env: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} run: npm publish --provenance --access public ================================================ FILE: .gitignore ================================================ .DS_Store *.tgz /dist /lib /node_modules ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [5.0.2](https://github.com/metonym/svelte-typeahead/releases/tag/v5.0.2) - 2025-11-24 **Fixes** - close dropdown for keyboard navigation ## [5.0.1](https://github.com/metonym/svelte-typeahead/releases/tag/v5.0.1) - 2025-09-11 **Fixes** - patch `svelte-search` to v2.1.2 ## [5.0.0](https://github.com/metonym/svelte-typeahead/releases/tag/v5.0.0) - 2025-01-20 **Breaking Changes** - set `package.json#type` to `module` with `exports` field - drop bundled ESM/UMD builds (only Svelte source code is distributed) - colocate TypeScript definitions with Svelte source code **Fixes** - patch `svelte-search` to v2.1.1 - run `npm pkg fix` to fix `package.json` metadata ## [4.4.2](https://github.com/metonym/svelte-typeahead/releases/tag/v4.4.2) - 2024-11-20 - fix `$$restProps` type errors by upgrading `svelte-search` ## [4.4.1](https://github.com/metonym/svelte-typeahead/releases/tag/v4.4.1) - 2022-11-12 - replace `aria-owns` with `aria-controls` to resolve `a11y-role-has-required-aria-props` warning ## [4.4.0](https://github.com/metonym/svelte-typeahead/releases/tag/v4.4.0) - 2022-11-08 **Features** - add `showAllResultsOnFocus` prop to display all results when focusing an empty input **Fixes** - silence a11y warnings emitted by Svelte@3.52 - event type for dispatched `clear` event should be `null` - unset `ul` margin ## [4.3.2](https://github.com/metonym/svelte-typeahead/releases/tag/v4.3.2) - 2022-09-01 **Fixes** - allow results to be selected if using `showDropdownOnFocus` ## [4.3.1](https://github.com/metonym/svelte-typeahead/releases/tag/v4.3.1) - 2022-08-29 **Fixes** - skip disabled results when auto-selecting a result ## [4.3.0](https://github.com/metonym/svelte-typeahead/releases/tag/v4.3.0) - 2022-08-29 **Features** - add `showDropdownOnFocus` prop to only show dropdown if the search input is focused ## [4.2.4](https://github.com/metonym/svelte-typeahead/releases/tag/v4.2.4) - 2022-07-05 **Fixes** - set dropdown menu `z-index: 1` when expanded ## [4.2.3](https://github.com/metonym/svelte-typeahead/releases/tag/v4.2.3) - 2022-07-04 **Fixes** - update selected index when hovering over a result ## [4.2.2](https://github.com/metonym/svelte-typeahead/releases/tag/v4.2.2) - 2022-03-20 **Fixes** - pressing "ArrowUp" on last item should not reset highlighted index ## [4.2.1](https://github.com/metonym/svelte-typeahead/releases/tag/v4.2.1) - 2021-10-30 **Fixes** - prevent keyboard selection and navigation of disabled results - add missing annotation for `TItem.disabled` to `results` prop **Documentation** - add `pnpm` installation command - remove "disabling items after selection" example - remove `id` fields from `data` array - simplify dispatched events example ## [4.2.0](https://github.com/metonym/svelte-typeahead/releases/tag/v4.2.0) - 2021-10-26 **Features** - use TypeScript generics so that the item type can be inferred from `data` value ## [4.1.0](https://github.com/metonym/svelte-typeahead/releases/tag/v4.1.0) - 2021-10-03 **Features** - render a "no-results" slot when the search value does not yield results - add `value` to the default slot props ## [4.0.0](https://github.com/metonym/svelte-typeahead/releases/tag/v4.0.0) - 2021-09-05 **Breaking Changes** - use `.svelte.d.ts` extension for component TypeScript definition ## [3.0.0](https://github.com/metonym/svelte-typeahead/releases/tag/v3.0.0) - 2021-03-16 **Breaking Changes** - the `ul` element is rendered even without results to preserve the accessibility label **Fixes** - disable form ARIA attributes in `svelte-search` - pass the correct `aria-activedescendant` item to `svelte-search` ## [2.4.1](https://github.com/metonym/svelte-typeahead/releases/tag/v2.4.1) - 2021-03-14 **Fixes** - pass `id` to `Search` to fix `aria-labelledby` references ## [2.4.0](https://github.com/metonym/svelte-typeahead/releases/tag/v2.4.0) - 2021-03-04 **Features** - add `limit` prop to limit number of results; default value is `Infinity` ## [2.3.0](https://github.com/metonym/svelte-typeahead/releases/tag/v2.3.0) - 2021-02-21 **Features** - add `disable`, `filter` props to disable and filter items from the result set **Fixes** - bind the input element reference correctly to fix focusing behavior - don't pass the Typeahead id to Search ## [2.2.0](https://github.com/metonym/svelte-typeahead/releases/tag/v2.2.0) - 2021-02-20 **Features** - add `inputAfterSelect` prop to allow user to preserve or clear the input field after selecting a result; possible values are `"update" | "clear" | "keep"` (default is `"update"`) - add searched value to dispatched "select" event detail (`e.detail.searched`) ## [2.1.0](https://github.com/metonym/svelte-typeahead/releases/tag/v2.1.0) - 2021-02-20 **Features** - include `original` item and `originalIndex` in dispatched "select" event ## [2.0.0](https://github.com/metonym/svelte-typeahead/releases/tag/v2.0.0) - 2020-12-31 **Breaking Changes** - upgrade `svelte-search` to version 1.0.0 - defer to default `label`, `placeholder` props from `search-svelte` - use `SvelteComponentTyped` interface in TypeScript definitions ## [1.0.0](https://github.com/metonym/svelte-typeahead/releases/tag/v1.0.0) - 2020-11-28 **Features** - export reactive `results` array containing fuzzy results - add `focusAfterSelect` to opt in to focusing input after selecting a result - keydown default behavior is preventing if pressing "ArrowUp", "ArrowDown", or "Escape" **Breaking changes** - `focusAfterSelect` is `false` by default - redesigned default styles - if using TS, Svelte version >=3.30 is required ## [0.2.0](https://github.com/metonym/svelte-typeahead/releases/tag/v0.2.0) - 2020-11-17 - Add TypeScript definitions ## [0.1.1](https://github.com/metonym/svelte-typeahead/releases/tag/v0.1.1) - 2020-08-06 - Remove `filter` named export ## [0.1.0](https://github.com/metonym/svelte-typeahead/releases/tag/v0.1.0) - 2020-04-15 - Initial release ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020-present Eric Liu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # svelte-typeahead [![NPM][npm]][npm-url] > Accessible, fuzzy search typeahead component. This component uses the lightweight [fuzzy](https://github.com/mattyork/fuzzy) library for client-side, fuzzy search and follows [WAI-ARIA guidelines](https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html). Try it in the [Svelte REPL](https://svelte.dev/repl/a1b828d80de24f7e995b2365782c8d04). --- ## Installation ```bash # npm npm i svelte-typeahead # pnpm pnpm i svelte-typeahead # Yarn yarn add svelte-typeahead # Bun bun add svelte-typeahead ``` ## Usage ### Basic Pass an array of objects to the `data` prop. Use the `extract` prop to specify the value to search on. ```svelte ``` ### Custom label `$$restProps` are forwarded to [svelte-search](https://github.com/metonym/svelte-search). Use the `label` prop to specify a custom label. ```svelte ``` ### Hidden label Set `hideLabel` to `true` to visually hide the label. It's recommended that you set the `label` – even if hidden – for accessibility. ```svelte ``` ### Custom-styled results This component uses the [fuzzy](https://github.com/mattyork/fuzzy) library to highlight matching characters with the [mark](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark) element. Use the default slot to render custom results. ```svelte {@html result.string} {index} ``` ### No results Use the "no-results" slot to render a message if the search value does not yield results. ```svelte No results found for "{value}" ``` ### Limit the number of results Use the `limit` prop to specify the maximum number of results to display. The default is `Infinity`. ```svelte ``` ### Disabled items Disable items using the `disable` filter. Disabled items are not selectable or navigable by keyboard. In the following example, items with a `state` value containing "Carolina" are disabled. ```svelte item.state} disable={(item) => /Carolina/.test(item.state)} /> ``` ### Focus after select Set `focusAfterSelect` to `true` to re-focus the search input after selecting a result. ```svelte ``` ### Show dropdown on focus By default, the dropdown will be shown if the `value` has results. Set `showDropdownOnFocus` to `true` to only show the dropdown when the search input is focused. ```svelte ``` ### Show all results on focus By default, no results are shown if an empty input (i.e., `value=""`) is focused. Set `showAllResultsOnFocus` to `true` for all results to be shown when an empty input is focused. ```svelte ``` ### Styling **Note:** this component is minimally styled by design. You can target the component using the `[data-svelte-typeahead]` selector. ```css :global([data-svelte-typeahead]) { margin: 1rem; } ``` ## API ### Props | Name | Type | Default value | Description | | :-------------------- | :------------------------------------------------------------------------- | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------ | | value | `string` | `""` | Input search value. | | data | `TItem[]` | `[]` | Items to search. | | extract | `(TItem) => any` | `(item) => item` | Target the value if `TItem` is an object. | | disable | `(TItem) => boolean` | `(item) => false` | Disabled items are shown in results but are not selectable. | | filter | `(TItem) => boolean` | `(item) => false` | Filtered out items will not be displayed in the results. | | autoselect | `boolean` | `true` | Whether to automatically select the first result. | | inputAfterSelect | `"update" \| "clear" \| "keep"` | `"update"` | Set to `"clear"` to clear the `value` after selecting a result. Set to `"keep"` to keep the search field unchanged after a selection. | | results | `FuzzyResult[]` | `[]` | Raw fuzzy results from the [fuzzy](https://github.com/mattyork/fuzzy) module | | focusAfterSelect | `boolean` | `false` | Set to `true` to re-focus the input after selecting a result. | | showDropdownOnFocus | `boolean` | `false` | Set to `true` to only show results when the input is focused. | | showAllResultsOnFocus | `boolean` | `false` | Set to `true` for all results to be shown when an empty input is focused. | | limit | `number` | `Infinity` | Specify the maximum number of results to display. | | `...$$restProps` | (forwarded to [`svelte-search`](https://github.com/metonym/svelte-search)) | N/A | All other props are forwarded to the input element. | ### Dispatched events - **on:select**: dispatched when selecting a result - **on:clear**: dispatched when clearing the input field ```svelte (e = [...e, { event: "select", detail }])} on:clear={() => (e = [...e, { event: "clear" }])} />
{JSON.stringify(e, null, 2)}
``` ### Forwarded events The following events are forwarded to the [svelte-search](https://github.com/metonym/svelte-search) component. - on:type - on:input - on:change - on:focus - on:clear - on:blur - on:keydown ## Changelog [Changelog](CHANGELOG.md) ## License [MIT](LICENSE) [npm]: https://img.shields.io/npm/v/svelte-typeahead.svg?color=%23ff3e00&style=for-the-badge [npm-url]: https://npmjs.com/package/svelte-typeahead ================================================ FILE: package.json ================================================ { "name": "svelte-typeahead", "version": "5.0.2", "license": "MIT", "description": "Accessible, fuzzy search typeahead component", "author": "Eric Liu (https://github.com/metonym)", "type": "module", "svelte": "./src/index.js", "main": "./src/index.js", "types": "./src/index.d.ts", "exports": { "./*.svelte": { "types": "./src/*.svelte.d.ts", "import": "./src/*.svelte" }, "./*.js": { "types": "./src/*.d.ts", "import": "./src/*.js" }, ".": { "types": "./src/index.d.ts", "import": "./src/index.js", "svelte": "./src/index.js" } }, "scripts": { "dev": "rollup -cw", "test": "svelte-check --workspace tests" }, "dependencies": { "fuzzy": "0.1.3", "svelte-search": "^2.1.2" }, "devDependencies": { "@rollup/plugin-commonjs": "^21.1.0", "@types/bun": "^1.3.3", "culls": "^0.1.2", "svelte": "^3.59.1", "svelte-check": "^4.1.4", "svelte-readme": "^3.6.3", "typescript": "^5.9.3" }, "repository": { "type": "git", "url": "git+https://github.com/metonym/svelte-typeahead.git" }, "homepage": "https://github.com/metonym/svelte-typeahead", "bugs": "https://github.com/metonym/svelte-typeahead/issues", "keywords": [ "svelte", "svelte component", "typeahead", "fuzzy", "highlight", "search", "filter", "WAI-ARIA", "accessibility" ], "files": [ "src" ] } ================================================ FILE: rollup.config.ts ================================================ import commonjs from "@rollup/plugin-commonjs"; import svelteReadme from "svelte-readme"; export default svelteReadme({ plugins: [commonjs()], style: ` .code-fence li + li { margin: 0; } .code-fence { min-height: 16rem; } .code-fence pre { margin-bottom: 0; margin-top: 12px; } `, }); ================================================ FILE: src/Typeahead.svelte ================================================ { if (!hideDropdown && !comboboxRef?.contains(target)) { close(); } }} />
0} aria-expanded={showResults} id="{id}-typeahead" > = 0 && !hideDropdown && results.length > 0 ? `${id}-result-${selectedIndex}` : null} bind:value on:type on:input on:change on:focus on:focus={() => { open(); if (showDropdownOnFocus || showAllResultsOnFocus) { showResults = true; isFocused = true; } }} on:clear on:clear={open} on:blur={(e) => { // Check if focus is moving to an element within the combobox const relatedTarget = e.relatedTarget; if (relatedTarget && comboboxRef?.contains(relatedTarget)) { return; } // Close immediately for keyboard navigation (Tab, Shift+Tab) close(); }} on:keydown on:keydown={(e) => { if (results.length === 0) return; switch (e.key) { case "Enter": select(); break; case "ArrowDown": e.preventDefault(); change(1); break; case "ArrowUp": e.preventDefault(); change(-1); break; case "Escape": e.preventDefault(); value = ""; searchRef?.focus(); close(); break; } }} />
    {#if showResults} {#each results as result, index}
  • { if (result.disabled) return; e.preventDefault(); // Prevent input from losing focus selectedIndex = index; select(); }} on:mouseenter={() => { if (result.disabled) return; selectedIndex = index; }} > {@html result.string}
  • {/each} {/if} {#if $$slots["no-results"] && !hideDropdown && value.length > 0 && results.length === 0}
    {/if}
================================================ FILE: src/Typeahead.svelte.d.ts ================================================ import type { SvelteComponentTyped } from "svelte"; import type { SearchProps } from "svelte-search/src/Search.svelte"; export interface TypeaheadProps extends Omit { /** * @default "typeahead-" + Math.random().toString(36) */ id?: string; /** * @default "" */ value?: string; /** * @default [] */ data?: TItem[]; /** * @default (item) => item */ extract?: (item: TItem) => any; /** * @default (item) => false */ disable?: (item: TItem) => boolean; /** * @default (item) => false */ filter?: (item: TItem) => boolean; /** * Set to `false` to prevent the first result from being selected * @default true */ autoselect?: boolean; /** * Set to `keep` to keep the search field unchanged after select, set to `clear` to auto-clear search field * @default "update" */ inputAfterSelect?: "update" | "clear" | "keep"; /** * @default [] */ results?: { original: TItem; index: number; score: number; string: string; disabled?: boolean; }[]; /** * Set to `true` to re-focus the input after selecting a result * @default false */ focusAfterSelect?: boolean; /** * Set to `true` to only show results when the input is focused * @default false */ showDropdownOnFocus?: boolean; /** * Set to `true` for all results to be shown when an empty input is focused * @default false */ showAllResultsOnFocus?: boolean; /** * Specify the maximum number of results to return * @default Infinity */ limit?: number; } export default class Typeahead< TItem = string | number | Record > extends SvelteComponentTyped< TypeaheadProps, { select: CustomEvent<{ searched: string; selected: TItem; selectedIndex: number; original: TItem; originalIndex: number; }>; type: CustomEvent; clear: CustomEvent; input: WindowEventMap["input"]; change: WindowEventMap["change"]; focus: WindowEventMap["focus"]; blur: WindowEventMap["blur"]; keydown: WindowEventMap["keydown"]; }, { default: { result: { original: TItem; index: number; score: number; string: string; }; index: number; value: string; }; "no-results": { value: string; }; } > {} ================================================ FILE: src/index.d.ts ================================================ export { default } from "./Typeahead.svelte"; ================================================ FILE: src/index.js ================================================ export { default } from "./Typeahead.svelte"; ================================================ FILE: tests/Typeahead.test.svelte ================================================ { console.log("select", e.detail); }} on:clear on:type={(e) => { console.log(e.detail); }} bind:results let:result let:index let:value={searchedValue} > {result.original} {@html result.string} {index} {result.score} {searchedValue} No results {value} ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "noEmit": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "verbatimModuleSyntax": true, "isolatedModules": true, "target": "ESNext", "module": "ESNext", "moduleResolution": "node", "strict": true, "skipLibCheck": true, "paths": { "svelte-typeahead": ["./src"], "svelte-typeahead/*": ["./src/*"] } }, "include": ["src", "tests"] }