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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@deboxsoft/svelte-select

v2.0.1

Published

A select/autocomplete component for Svelte apps. With support for grouping, filtering, async and more.

Downloads

7

Readme

svelte-select

A select/autocomplete component for Svelte apps. With support for grouping, filtering, async and more.

Installation

pnpm add @deboxsoft/svelte-select

Rollup and low/no-build setups

List position and floating is powered by floating-ui, see their package-entry-points docs if you encounter build errors.

Props

| Prop | Type | Default | Description | | --------------------- | --------- | --------------- | ---------------------------------------------------------- | | items | any[] | [] | Array of items available to display / filter | | value | any | null | Selected value(s) | | justValue | any | null | READ-ONLY Selected value(s) excluding container object | | itemId | string | value | Override default identifier | | label | string | label | Override default label | | id | string | null | id attr for input field | | filterText | string | '' | Text to filter items by | | placeholder | string | Please select | Placeholder text | | hideEmptyState | boolean | false | When no items hide list | | listOpen | boolean | false | Open/close list | | class | string | '' | container classes | | containerStyles | string | '' | Add inline styles to container | | clearable | boolean | true | Enable clearing of value(s) | | disabled | boolean | false | Disable select | | multiple | boolean | false | Enable multi-select | | searchable | boolean | true | If false search/filtering is disabled | | groupHeaderSelectable | boolean | false | Enable selectable group headers | | focused | boolean | false | Controls input focus | | listAutoWidth | boolean | true | If false will ignore width of select | | showChevron | boolean | false | Show chevron | | inputAttributes | object | {} | Pass in HTML attributes to Select's input | | placeholderAlwaysShow | boolean | false | When multiple placeholder text will always show | | loading | boolean | false | Shows loading-icon. loadOptions will override this | | listOffset | number | 5 | px space between select and list | | debounceWait | number | 300 | milliseconds debounce wait | | floatingConfig | object | {} | Floating UI Config |

Named slots

<Select>
  <div slot="prepend" />
  <div slot="selection" let:selection />
  <div slot="clear-icon" />  
  <div slot="multi-clear-icon" />  
  <div slot="loading-icon" />  
  <div slot="chevron-icon" /> 
  <div slot="list" let:filteredItems />  
  <div slot="item" let:item let:index />  
  <!-- Remember you can also use `svelte:fragment` to avoid a container DOM element. -->
  <svelte:fragment slot="empty" />  
</Select>

Events

| Event Name | Callback | Description | | ---------- | ----------------- | ------------------------------------------------------------------------------ | | select | { detail } | fires when item is selected | | change | { detail } | fires when value changes | | focus | { detail } | fires when select > input on:focus | | blur | { detail } | fires when select > input on:blur | | clear | { detail } | fires when clear all is invoked or item is removed (by user) from multi select | | loaded | { options } | fires when loadOptions resolves | | error | { type, details } | fires when error is caught |

Items

items can be simple arrays or collections.

<script>
  import Select from 'svelte-select';

  let simple = ['one', 'two', 'three'];

  let collection = [
    { value: 1, label: 'one' },
    { value: 2, label: 'two' },
    { value: 3, label: 'three' },
  ];
</script>

<Select items={simple} />

<Select items={collection} />

They can also be grouped and include non-selectable items.

<script>
  import Select from 'svelte-select';

  const items = [
    {value: 'chocolate', label: 'Chocolate', group: 'Sweet'},
    {value: 'pizza', label: 'Pizza', group: 'Savory'},
    {value: 'cake', label: 'Cake', group: 'Sweet', selectable: false},
    {value: 'chips', label: 'Chips', group: 'Savory'},
    {value: 'ice-cream', label: 'Ice Cream', group: 'Sweet'}
  ];

  const groupBy = (item) => item.group;
</script>

<Select {items} {groupBy} />

You can also use custom collections.

<script>
  import Select from 'svelte-select';

  const itemId = 'id';
  const label = 'title';

  const items = [
    {id: 0, title: 'Foo'},
    {id: 1, title: 'Bar'},
  ];
</script>

<Select {itemId} {label} {items} />

Async Items

To load items asynchronously then loadOptions is the simplest solution. Supply a function that returns a Promise that resolves with a list of items. loadOptions has debounce baked in and fires each time filterText is updated.

<script>
  import Select from 'svelte-select';

  import { someApiCall } from './services';

  async function examplePromise(filterText) {
    // Put your async code here...
    // For example call an API using filterText as your search params
    // When your API responds resolve your Promise
    let res = await someApiCall(filterText);
    return res;
  }
</script>

<Select loadOptions={examplePromise} />

Exposed methods

These internal functions are exposed to override if needed. See the adv demo or look through the test file (test/src/index.js) for examples.

export let itemFilter = (label, filterText, option) => label.toLowerCase().includes(filterText.toLowerCase());
export let groupBy = undefined;
export let groupFilter = groups => groups;
export let createGroupHeaderItem = groupValue => {
  return {
    value: groupValue,
    label: groupValue
  };
};
export let createItem = filterText => {
  return {
    value: filterText,
    label: filterText
  };
};
export let getOptionLabel = (option, filterText) => {
  return option.isCreator ? `Create \"${filterText}\"` : option.label;
};
export let getSelectionLabel = option => {
  if (option) return option.label;
};
export let getGroupHeaderLabel = option => {
  return option.label;
};
export function handleClear() {
  value = undefined;
  listOpen = false;
  dispatch("clear", value);
  handleFocus();
}
export let loadOptions = undefined; // if used must return a Promise that updates 'items'
/* Return an object with { cancelled: true } to keep the loading state as active. */
export const getFilteredItems = () => {
  return filteredItems;
};

A11y (Accessibility)

Override these methods to change the aria-context and aria-selection text.

export let ariaValues = (values) => {
  return `Option ${values}, selected.`;
}

export let ariaListOpen = (label, count) => {
  return `You are currently focused on option ${label}. There are ${count} results available.`;
}

export let ariaFocused = () => {
  return `Select is focused, type to refine list, press down to open the menu.`;
}

Styling

You can style a component by overriding the available CSS variables.

<script>
  import Select from 'svelte-select';

  const items = ['One', 'Two', 'Three'];
</script>

<style>
  .themed {
    --border: 3px solid blue;
    --borderRadius: 10px;
    --placeholderColor: blue;
  }
</style>

<div class="themed">
  <h2>Theming</h2>
  <Select {items}></Select>
</div>

You can also use the inputStyles prop to write in any override styles needed for the input.

<script>
  import Select from 'svelte-select';

  const items = ['One', 'Two', 'Three'];
</script>

<Select {items} inputStyles="box-sizing: border-box;"></Select>

Events

| Event Name | Callback | Description | |------------|-------------------|--------------------------------------------------------------------------------| | select | { detail } | fires when value changes | | clear | { detail } | fires when clear all is invoked or item is removed (by user) from multi select | | loaded | { items } | fires when loadOptions resolves | | error | { type, details } | fires when error is caught |

<script>
  import Select from 'svelte-select';

  let items = [...];
  function handleSelect(event) {
    // event.detail will contain the selected value
    ...
  }
  function handleClear(event) {
    // event.detail will be null unless isMulti is true and user has removed a single item
    ...
  }
</script>

<Select {items} on:select={handleSelect} on:clear={handleClear}></Select>

Development

yarn global add serve@8
yarn
yarn dev
yarn test:browser

In your favourite browser go to http://localhost:3000 and open devtools and see the console for the test output. When developing its handy to see the component on the page; comment out the select.$destroy(); on the last test in /test/src/index.js or use the test.only() to target just one test.

For example:

test.only('when getSelectionLabel contains HTML then render the HTML', async (t) => {
  const select = new Select({
    target,
    props: {
      value: items[0],
      getSelectionLabel: (option) => `<p>${option.label}</p>`,
    }
  });

  t.ok(document.querySelector('.selection').innerHTML === '<p>Chocolate</p>');

  //select.$destroy();
});