npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@get-set/gs-autocomplete

v1.0.2

Published

Get-Set Autocomplete / Combobox

Downloads

153

Readme

@get-set/gs-autocomplete

A beautiful, accessible autocomplete / combobox that enhances a plain text <input>. One codebase, four ways to use it:

  1. window.GSAutocomplete(selectorOrElement, params) — vanilla factory
  2. $(selector).GSAutocomplete(params) — jQuery plugin
  3. element.GSAutocomplete(params)HTMLElement.prototype method
  4. <GSAutocomplete … /> — React component with a typed imperative ref

The native targets and the React component share the same actions, constants, helpers, types and CSS, so every option behaves identically across all modes.


Features

  • Any source — a static { label, value, group? }[] array, or an async fn(query) => Promise<Item[]> for remote search.
  • FilteringstartsWith, contains, fuzzy (subsequence) or a fully custom predicate. Case-sensitive opt-in.
  • Multi-select — removable chips, maxItems, dedupe, Backspace-to-remove.
  • Free text — commit arbitrary typed values not in the source.
  • Grouping — bucket items under headings via their group field.
  • Highlighting — bold the matched substring in each row.
  • Custom rendering — return an HTML string per row (itemRender), icons and secondary descriptions per item.
  • Async UX — loading state + spinner, debounce, stale-response guarding, noResultsText.
  • Beautiful motion — 8 dropdown entrance animations, micro-interactions, honoring prefers-reduced-motion.
  • Full keyboard nav — Arrow Up/Down, Enter, Esc, Tab, Home, End, Backspace.
  • ARIA comboboxrole="combobox"/listbox/option, aria-expanded, aria-activedescendant, aria-selected.
  • Theminglight / dark / auto + accent color, three sizes, RTL.
  • React-only gsx — scoped per-instance styles injected on mount, removed on unmount.
  • Registrywindow.GSAutocompleteConfigue.instance(reference) to reach any live instance.

Compatibility

| Target | How | | ----------------- | ------------------------------------------------------------- | | Vanilla JS | window.GSAutocomplete(selectorOrElement, params) | | jQuery | $(sel).GSAutocomplete(params) (auto-registered if jQuery present) | | Prototype | el.GSAutocomplete(params) | | React 16.8–19 | import GSAutocomplete from '@get-set/gs-autocomplete' |

React / ReactDOM are optional peer dependencies — the vanilla bundle has no React dependency.


Project layout

GSAutocomplete.ts            native factory + window.GSAutocompleteConfigue registry
actions/                     init, open/close, query, search, select, keynav, render, animate, refresh, destroy
helpers/                     filter, layout, render, uihelpers
constants/                   defaultParams, sizes, animations
types/                       params, ref, global.d.ts
components/GSAutocomplete.tsx React component (forwardRef + useImperativeHandle)
components/styles/           GSAutocomplete.css/.scss + GSAutocompleteCSS.ts (string export)
styles/                      GSAutocomplete.css/.scss (publishable)
example.html                 native showcase
tests/                       vitest suite (logic + native + react)

Install

npm i @get-set/gs-autocomplete

Build & test

npm install
npm run build      # build:js (webpack -> dist-js/bundle.js) + build:react (tsc -> dist/)
npm test           # vitest run
  • npm run build:js bundles the native target to dist-js/bundle.js and appends a banner registering the jQuery ($.fn.GSAutocomplete) and prototype (HTMLElement.prototype.GSAutocomplete) adapters.
  • npm run build:react emits the React component + typings to dist/.

Usage — Native JS

Include the bundle and the stylesheet, then call the factory with a CSS selector string or a DOM element.

<link rel="stylesheet" href="node_modules/@get-set/gs-autocomplete/styles/GSAutocomplete.css" />
<script src="node_modules/@get-set/gs-autocomplete/dist-js/bundle.js"></script>

<input id="fruit" placeholder="Search fruit…" />

<script>
  const fruits = [
    { label: 'Apple', value: 'apple' },
    { label: 'Apricot', value: 'apricot' },
    { label: 'Banana', value: 'banana' },
    { label: 'Cherry', value: 'cherry', group: 'Stone fruit' }
  ];

  // (a) selector string
  const ac = window.GSAutocomplete('#fruit', {
    reference: 'fruit',
    source: fruits,
    filter: 'contains',
    highlightMatch: true,
    onChange: (value) => console.log('value:', value)
  });

  // (b) …or pass the element directly — this is exactly what the jQuery and
  //     prototype adapters do, so the factory MUST accept an element.
  const el = document.querySelector('#fruit');
  // window.GSAutocomplete(el, { source: fruits });

  ac.setValue('banana');
  console.log(ac.getValue()); // "banana"
</script>

Instance methods (registry)

const ac = window.GSAutocompleteConfigue.instance('fruit');
ac.openDropdown();
ac.setValue(['apple', 'banana']); // multiple mode
ac.clear();
ac.destroy();

Usage — jQuery

The bundle auto-registers $.fn.GSAutocomplete when window.jQuery is present. Each matched element is enhanced (the adapter passes the element to the factory).

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="node_modules/@get-set/gs-autocomplete/dist-js/bundle.js"></script>

<input class="ac" />

<script>
  $('.ac').GSAutocomplete({
    source: ['Apple', 'Banana', 'Cherry'].map((l) => ({ label: l, value: l })),
    placeholder: 'Type to search…'
  });

  // Reach the instance through the registry:
  const ac = window.GSAutocompleteConfigue.instance('<reference>');
</script>

Usage — Prototype

Every HTMLElement gains a .GSAutocomplete(params) method (also passes the element to the factory):

<input id="city" />
<script>
  document.getElementById('city').GSAutocomplete({
    source: async (q) =>
      (await fetch(`/api/cities?q=${encodeURIComponent(q)}`)).json(),
    minChars: 2,
    debounce: 250,
    noResultsText: 'No cities found'
  });
</script>

Usage — React

import { useRef } from 'react';
import GSAutocomplete, {
  GSAutocompleteHandle
} from '@get-set/gs-autocomplete';

const fruits = [
  { label: 'Apple', value: 'apple' },
  { label: 'Apricot', value: 'apricot' },
  { label: 'Banana', value: 'banana' }
];

export default function Demo() {
  const ref = useRef<GSAutocompleteHandle>(null);

  return (
    <>
      <GSAutocomplete
        ref={ref}
        source={fruits}
        filter="fuzzy"
        multiple
        grouping
        size="md"
        theme="auto"
        accentColor="#7c3aed"
        placeholder="Pick fruit…"
        onSelect={(item) => console.log('selected', item)}
        onChange={(value) => console.log('value', value)}
        gsx={{ '.gs-ac-option': { fontWeight: '500' } }}
      />

      <button onClick={() => ref.current?.open()}>Open</button>
      <button onClick={() => ref.current?.setValue(['apple', 'banana'])}>Set</button>
      <button onClick={() => ref.current?.clear()}>Clear</button>
    </>
  );
}

Controlled value:

const [value, setValue] = useState<string>('apple');
<GSAutocomplete source={fruits} value={value} onChange={(v) => setValue(v as string)} />

gsx — scoped styles (React-only)

gsx accepts a nested CSS object. It is compiled and injected into <head> scoped to this instance via [data-key='…'], and removed automatically on unmount:

<GSAutocomplete
  source={fruits}
  gsx={{
    '.gs-ac-control': { borderRadius: '999px' },
    '.gs-ac-option.gs-ac-active': { background: 'rgba(124,58,237,.14)' }
  }}
/>

Options / props

Every option works in all four modes (native param key === React prop name).

Data

| Name | Type | Default | Description | | --------------- | ------------------------------------------------------------------- | ------------ | ----------- | | source | Item[] | (query) => Item[] \| Promise<Item[]> | undefined | Array source (filtered locally) or async query function (the authority for remote search). | | filter | 'startsWith' | 'contains' | 'fuzzy' | (item, query) => boolean | 'contains' | Built-in strategy or custom predicate (array sources). | | caseSensitive | boolean | false | Case-sensitive matching for the built-in filters. | | minChars | number | 1 | Minimum characters before a search runs. | | debounce | number | 200 | Debounce delay (ms) before searching. 0 = immediate. | | maxResults | number | false | 50 | Cap rendered results. false = uncapped. | | highlightMatch| boolean | true | Bold the matched substring in each row. | | grouping | boolean | false | Group items by their group field under headings. | | openOnFocus | boolean | false | Show suggestions immediately on focus. |

Selection behaviour

| Name | Type | Default | Description | | --------------- | ------------------- | --------------------------- | ----------- | | multiple | boolean | false | Multi-select with removable chips. | | allowFreeText | boolean | false | Commit arbitrary typed text (Enter) not in the source. | | closeOnSelect | boolean | true single / false multi | Close the dropdown after a selection. | | clearOnSelect | boolean | false single / true multi | Clear the query text after selecting. | | maxItems | number | false | false | Max chips allowed (multiple mode). |

Presentation

| Name | Type | Default | Description | | --------------- | -------------------------- | -------------- | ----------- | | placeholder | string | undefined | Input placeholder text. | | clearable | boolean | true | Show the clear (×) button when there is a value. | | disabled | boolean | false | Disable the whole control. | | size | 'sm' | 'md' | 'lg' | 'md' | Control size preset. | | noResultsText | string | 'No results' | Text shown when a search yields nothing. | | loaderHtml | string | built-in | Custom spinner content (HTML string). | | itemRender | (item, query, index) => string | undefined | Custom HTML for each option row. |

Animation

| Name | Type | Default | Description | | ------------------- | ----------------------------- | -------------------------------- | ----------- | | animation | GSAutocompleteAnimation | false | 'fade-up' | Dropdown entrance animation. false disables motion. | | animationDuration | number | 180 | Duration in ms. | | animationEasing | string | cubic-bezier(0.16,1,0.3,1) | CSS easing. | | reducedMotion | 'auto' | boolean | 'auto' | Honor prefers-reduced-motion. |

Theming

| Name | Type | Default | Description | | ------------- | ----------------------------------- | ------- | ----------- | | theme | 'light' | 'dark' | 'auto' | 'auto'| Visual theme (auto follows prefers-color-scheme). | | accentColor | string | #2563eb | Accent for active rows / focus ring (--gsa-accent). | | rtl | boolean | false | Right-to-left layout. | | customClass | GSAutocompleteCustomClass | undefined | Per-part class overrides. | | className | string | undefined | Extra class on the wrapper root. |

Identity / value

| Name | Type | Default | Description | | -------------- | ----------------------------------------------- | ------- | ----------- | | reference | string | auto GUID | Registry key. | | value | string | number | Array<string\|number> | undefined | Native: initial value. React: controlled value. | | defaultValue | same | undefined | React only: uncontrolled initial value. | | gsx | NestedCSS | undefined | React only: scoped per-instance styles. |

Callbacks

| Name | Type | Description | | ---------- | ----------------------------- | ----------- | | onSearch | (query: string) => void | Fired (debounced) when a search runs. | | onSelect | (item: Item) => void | Fired when an item is selected / free text committed. | | onChange | (value) => void | Fired when the committed value changes. | | onOpen | () => void | Fired when the dropdown opens. | | onClose | () => void | Fired when the dropdown closes. |

Item shape

interface GSAutocompleteItem {
  label: string;            // shown in the dropdown (and input in single mode)
  value?: string | number;  // committed value (defaults to label)
  group?: string;           // bucket heading when grouping
  disabled?: boolean;       // non-selectable
  description?: string;     // secondary line under the label
  icon?: string;            // leading icon (HTML string)
  [key: string]: unknown;   // passthrough data for callbacks / itemRender
}

Variant catalogs

Filter catalog

| Value | Behaviour | | ------------- | --------- | | startsWith | Label starts with the query. | | contains | Label contains the query (default). | | fuzzy | Query characters appear in order (subsequence) in the label. | | (fn) | Custom (item, query) => boolean predicate. |

Animation catalog

| Value | Motion | | ------------- | ------ | | fade | Opacity fade-in. | | fade-up | Fade + rise from below (default). | | fade-down | Fade + drop from above. | | scale | Scale up from 96%. | | slide-down | Slide down + clip. | | slide-up | Slide up + clip. | | flip | Perspective flip on the X axis. | | expand | Expand from the top edge. | | none | No motion (also the reduced-motion fallback). | | false | Disables motion entirely. |

Size catalog

| Value | Control height / typography | | ----- | --------------------------- | | sm | Compact. | | md | Default. | | lg | Large. |

Theme catalog

| Value | Appearance | | ------- | ---------- | | light | Light surface. | | dark | Dark surface. | | auto | Follows prefers-color-scheme. |


Async / remote search

window.GSAutocomplete('#city', {
  source: async (query) => {
    const res = await fetch(`/api/cities?q=${encodeURIComponent(query)}`);
    return res.json(); // [{ label, value }, …]
  },
  minChars: 2,
  debounce: 250
});

While a function source is in flight the dropdown shows the loading row / spinner. Stale responses are ignored via a monotonic request token, so only the latest query's results are rendered.


Imperative API

React GSAutocompleteHandle (via ref)

| Method | Signature | Description | | ------------- | ----------------------------------------------------- | ----------- | | open | () => void | Open the dropdown (runs a search with the current query). | | close | () => void | Close the dropdown. | | clear | () => void | Clear the selection / input value. | | setValue | (value: string \| number \| Array<…>) => void | Set the committed value programmatically. | | getValue | () => string \| number \| Array<…> | Get the committed value (string single / array multiple). | | getSelected | () => Item \| Item[] \| null | Get the full selected item object(s). | | focus | () => void | Focus the input. | | refresh | () => void | Re-run the current search / re-render. | | destroy | () => void | Unregister the instance from the registry. |

Native instance

window.GSAutocomplete(...) returns a ref with:

| Method | Description | | --------------- | ----------- | | openDropdown()| Open the dropdown (runs a search with the current input). | | closeDropdown()| Close the dropdown. | | isOpen() | Whether the dropdown is open. | | clear() | Clear the selection / input value. | | setValue(v) | Set the committed value. | | getValue() | Get the committed value. | | getSelected() | Get the full selected item object(s). | | focus() | Focus the input. | | refresh() | Rebuild the control from the current params. | | setSource(s) | Replace the data source at runtime. | | destroy() | Tear down and restore the original input. |

Registry — window.GSAutocompleteConfigue

window.GSAutocompleteConfigue.references; // Array<{ key, ref }>
window.GSAutocompleteConfigue.instance('myReference'); // -> ref | undefined

Accessibility

  • Input: role="combobox", aria-autocomplete="list", aria-haspopup="listbox", aria-expanded, aria-controls, aria-activedescendant.
  • List: role="listbox"; options: role="option" + aria-selected + aria-disabled.
  • Full keyboard control: ↓/↑ move, Home/End jump, Enter select, Esc close, Tab select active + close, Backspace removes the last chip (multiple mode, empty input).
  • All animations respect prefers-reduced-motion.

License

ISC © Get-Set