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

@polarityio/pi-select

v1.0.4

Published

Feature-rich select/dropdown with single and multi-select, async search, and custom rendering

Downloads

561

Readme

Polarity Integration Component Library

Select Component

A feature-rich select/dropdown component supporting single and multi-select modes, synchronous and asynchronous search, custom option rendering, and intelligent dropdown positioning via Floating UI.

Installation

npm install @polarityio/pi-select

Peer Dependencies

Usage

import '@polarityio/pi-select';

Basic Single Select

import { html } from 'lit';

html`
  <pi-select placeholder="Choose a fruit" .options=${['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry']}></pi-select>
`;

Multi-Select

import { html } from 'lit';

html`
  <pi-select
    multiple
    placeholder="Select tags"
    .options=${['Bug', 'Feature', 'Enhancement', 'Documentation']}
    .selected=${['Bug', 'Feature']}
  ></pi-select>
`;

Async Search

import { html } from 'lit';

html`
  <pi-select
    placeholder="Search users..."
    search-placeholder="Type a name..."
    search-message="Type to search for users"
    loading-message="Searching..."
    .search=${async (term) => {
      const res = await fetch(`/api/users?q=${term}`);
      return res.json();
    }}
    label-field="name"
    search-field="name"
  ></pi-select>
`;

Custom Option Rendering

import { html } from 'lit';

const select = document.querySelector('pi-select');

select.options = [
  { name: 'Alice', role: 'Admin', avatar: '/alice.png' },
  { name: 'Bob', role: 'Editor', avatar: '/bob.png' }
];

select.labelField = 'name';
select.searchField = 'name';

select.renderOption = (option) => html`
  <div style="display: flex; align-items: center; gap: 8px;">
    <img src="${option.avatar}" width="24" height="24" style="border-radius: 50%;" />
    <div>
      <div>${option.name}</div>
      <small style="opacity: 0.7;">${option.role}</small>
    </div>
  </div>
`;

Handling Events

const select = document.querySelector('pi-select');

select.addEventListener('pi-change', (e) => {
  console.log('Selected:', e.detail.selected);
  console.log('Previous:', e.detail.previous);
});

select.addEventListener('pi-search', (e) => {
  console.log('Search term:', e.detail.term);
  console.log('Results:', e.detail.results);
});

select.addEventListener('pi-open', () => console.log('Dropdown opened'));
select.addEventListener('pi-close', () => console.log('Dropdown closed'));

API Reference

Properties

| Property | Type | Default | Description | | -------------------- | ----------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------- | | options | T[] | [] | Array of option items to display in the dropdown | | selected | T \| T[] \| undefined | undefined | Currently selected option(s). Array for multiple mode. | | multiple | boolean | false | Enable multi-select mode | | placeholder | string | '' | Placeholder text when no option is selected | | disabled | boolean | false | Disable the entire select component | | searchEnabled | boolean | true | Enable search/filter functionality | | searchField | string \| undefined | undefined | Object property name to search within (for object options) | | labelField | string \| undefined | undefined | Object property name to display as the option label | | allowClear | boolean | false | Show a clear button to reset the selection | | closeOnSelect | boolean \| undefined | undefined | Close dropdown after selection. Defaults to true in single mode, false in multiple mode. | | searchPlaceholder | string | '' | Placeholder text for the search input | | noMatchesMessage | string | 'No results found' | Message displayed when no options match the search | | searchMessage | string | 'Type to search' | Message shown in async search mode before the first search | | loadingMessage | string | 'Loading...' | Message displayed while loading async search results | | highlightOnHover | boolean | true | Highlight options on mouse hover | | matchTriggerWidth | boolean | true | Make dropdown width match the trigger width | | triggerClass | string | '' | Additional CSS class(es) for the trigger element | | dropdownClass | string | '' | Additional CSS class(es) for the dropdown element | | triggerTabindex | number | 0 | Tab index for keyboard navigation | | searchDebounce | number | 150 | Debounce delay in milliseconds for search filtering | | loading | boolean | false | Indicates async search is in progress | | search | (term: string) => Promise<T[]> | undefined | Async search function. When provided, enables async search mode. | | matcher | (option: T, term: string) => number | undefined | Custom matcher function for filtering. Return -1 for no match, 0+ for match position. | | renderOption | (option: T) => TemplateResult \| string | undefined | Custom render callback for option display in the dropdown | | renderSelectedItem | (option: T) => TemplateResult \| string | undefined | Custom render callback for selected item display in the trigger |

Events

| Event Name | Detail | Description | | ----------- | ---------------------------------------------------------------------- | -------------------------------------------------------- | | pi-change | { selected: T \| T[] \| undefined, previous: T \| T[] \| undefined } | Fired when the selection changes | | pi-open | {} | Fired when the dropdown opens | | pi-close | {} | Fired when the dropdown closes | | pi-search | { term: string, results: T[] } | Fired on search input with the term and filtered results | | pi-focus | {} | Fired when the select gains focus | | pi-blur | {} | Fired when the select loses focus |

Slots

| Slot Name | Description | | ---------------- | --------------------------------------------------------------------- | | before-options | Content displayed before the options list in the dropdown | | after-options | Content displayed after the options list in the dropdown | | loading | Custom loading message (replaces loadingMessage when provided) | | no-matches | Custom no-matches message (replaces noMatchesMessage when provided) |

CSS Custom Properties

| Property | Default | Description | | ---------------------------------------- | ----------------------------------------------- | -------------------------------------------------- | | --pi-select-dropdown-z-index | 1000 | Dropdown z-index | | --pi-select-line-height | 1.75em | Select line height | | --pi-select-trigger-padding | 4px 16px 4px 8px | Trigger padding | | --pi-select-border-color | var(--pi-color-border-element) | Border color | | --pi-select-border-radius | var(--pi-size-radius-base) | Border radius | | --pi-select-background | var(--pi-color-background-container-base) | Background color | | --pi-select-text-color | var(--pi-color-font-primary) | Text color | | --pi-select-disabled-bg | var(--pi-color-background-container-disabled) | Disabled background | | --pi-select-placeholder-color | var(--pi-color-font-disabled) | Placeholder color | | --pi-select-loading-icon-color | var(--pi-color-font-disabled) | Loading icon color | | --pi-select-dropdown-border | 1px solid var(--pi-select-border-color) | Dropdown border | | --pi-select-dropdown-box-shadow | var(--pi-shadow-sm) | Dropdown shadow | | --pi-select-dropdown-margin | 2px 0 0 | Dropdown margin from trigger | | --pi-select-option-padding | 4px 8px | Option padding | | --pi-select-search-input-border-radius | 3px | Search input border radius | | --pi-select-search-field-border | 1px solid var(--pi-select-border-color) | Search field border | | --pi-select-visible-options | 7 | Maximum number of visible options before scrolling | | --pi-select-highlight-bg | var(--pi-color-background-primary) | Highlighted option background | | --pi-select-highlight-color | var(--pi-color-font-inverse) | Highlighted option text color | | --pi-select-selected-bg | var(--pi-color-background-hover) | Selected option background | | --pi-select-disabled-color | var(--pi-color-font-disabled) | Disabled option text color | | --pi-select-tag-bg | var(--pi-color-background-secondary) | Multi-select tag background | | --pi-select-tag-color | var(--pi-color-font-primary) | Multi-select tag text color | | --pi-select-tag-border | 1px solid var(--pi-color-border-container) | Multi-select tag border | | --pi-select-tag-border-radius | 3px | Multi-select tag border radius | | --pi-select-tag-padding | 0 4px | Multi-select tag padding |

CSS Parts

| Part Name | Description | | -------------------- | ----------------------------------------- | | trigger | The select trigger/button area | | selected-item | Selected item text (single mode) | | placeholder | Placeholder text | | clear-btn | Clear button | | loading-icon | Loading spinner icon | | status-icon | Dropdown arrow icon | | tags-area | Area containing tags (multiple mode) | | tag | Individual tag (multiple mode) | | tag-label | Tag label text (multiple mode) | | tag-remove | Tag remove button (multiple mode) | | search-input | Search input field | | icons | Icons container (multiple mode) | | dropdown | Dropdown container | | search | Search input container (single mode) | | options-list | Options list container (role="listbox") | | option | Individual option | | option-highlighted | Highlighted option (keyboard/hover) | | option-selected | Selected option | | option-disabled | Disabled option | | no-matches | No-matches message element | | loading | Loading message element |

Public Methods

| Method | Description | | ---------- | ------------------------------------------------------ | | open() | Opens the dropdown. No-op if already open or disabled. | | close() | Closes the dropdown and returns focus to the trigger. | | toggle() | Toggles the dropdown between open and closed states. |

Keyboard Navigation

| Key | Behavior | | ----------------- | ------------------------------------------------------ | | ArrowDown | Open dropdown or move highlight to the next option | | ArrowUp | Open dropdown or move highlight to the previous option | | Home | Move highlight to the first option | | End | Move highlight to the last option | | Enter / Space | Select highlighted option or open dropdown | | Escape | Close dropdown | | Tab | Close dropdown and move focus | | Backspace | Remove last selected tag (multiple mode, empty search) | | (type-ahead) | Jump to matching option when dropdown is closed |

Theming

Customize the appearance using CSS custom properties. This component uses design tokens from @polarityio/pi-shared for consistent theming across the component library.

pi-select {
  --pi-select-border-radius: 8px;
  --pi-select-visible-options: 10;
  --pi-select-highlight-bg: #3b82f6;
}

pi-select::part(trigger) {
  min-height: 40px;
}

pi-select::part(tag) {
  border-radius: 12px;
}

License

MIT