💃 REPL: Show me everything 🕺
npm install svelte-select@beta
See migration guide if upgrading from v4 to v5.
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 |
optionIdentifier | string |
value |
Override default identifier |
labelIdentifier | string |
label |
Override default identifier |
id | string |
null |
Add an id to the filter input field |
filterText | string |
'' |
Text to filter items by |
placeholder | string |
Please select |
Placeholder text |
noOptionsMessage | string |
No options |
Message displayed when no items |
hideEmptyState | boolean |
false |
When no items hide list and noOptionsMessage |
listOpen | boolean |
false |
Open/close list |
class | string |
'' |
container classes |
containerStyles | string |
'' |
Add inline styles to container |
isClearable | boolean |
true |
Enable clearing of value(s) |
isCreatable | boolean |
false |
Can create new item(s) to be added to value |
isDisabled | boolean |
false |
Disable select |
isMulti | boolean |
false |
Enable multi-select |
isSearchable | boolean |
true |
If false search/filtering is disabled |
isGroupHeaderSelectable | boolean |
false |
Enable selectable group headers |
listPlacement | string |
auto |
Display list 'auto' , 'top' or 'bottom' |
hasError | boolean |
false |
Show error styles around select input |
listAutoWidth | boolean |
true |
If false will ignore width of select |
showChevron | boolean |
false |
Show chevron at all times |
inputAttributes | object |
{} |
Pass in HTML attributes to Select's input |
iconProps | object |
{} |
Icon props |
filteredItems | array |
[] |
List of items after filtering (read only) |
placeholderAlwaysShow | boolean |
false |
When isMulti placeholder text will always show |
isWaiting | boolean |
false |
Show LoadingIcon. loadOptions will override this |
listOffset | number |
5 |
px space between select and list |
debounceWait | number |
300 |
milliseconds debounce wait |
suggestions | string[] |
null |
Show search suggestions before user input |
appendListTarget | Element |
document.body |
Change where List gets appended |
Import | Type | Description |
---|---|---|
Item | component |
Item component |
Selection | component |
Selection component |
Multi | component |
Multi select support |
ChevronIcon | component |
Chevron Icon |
ClearIcon | component |
Clear Icon |
LoadingIcon | component |
Spinning Loading Icon |
Import | Type | Description |
---|---|---|
VirtualList | component |
Virtual list support (uses svelte-tiny-virtual-list ) |
Icon | component |
Icon component |
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 |
itemCreated | { options } | fires when isCreatable true and item is created |
error | { type, details } | fires when error is caught |
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 optionIdentifier = 'id';
const labelIdentifier = 'title';
const items = [
{id: 0, title: 'Foo'},
{id: 1, title: 'Bar'},
];
</script>
<Select {optionIdentifier} {labelIdentifier} {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} />
By default List gets appended to the document.body
. For most use-cases this is fine. If you want more control then supply a appendListTarget
Element
<script>
import Select from 'svelte-select';
let target = null;
</script>
<form bind:this={target}>
<Select appendListTarget={target} />
<!-- List will now get appended to the DOM inside this form Element> -->
</form>
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;
};
export function debounce(fn, wait = 1) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, ...args), wait);
};
}
export const sanitiseLabel = (text) => text && `${text}`.replace(/\</gi, '<');
Override core functionality at your own risk! See (get-items.js, filter.js, compute-placement.js)
// core replaceable methods...
<Select
filter={...}
getItems={...}
computePlacement={...}
/>
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.`;
}
You can style a component by overriding the available CSS custom properties.
<script>
import Select from 'svelte-select';
</script>
<Select --border-radius= "10px" --placeholder-color="blue" />
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>
If you'd like to supply your own styles use: import Select from 'svelte-select/no-styles/Select.svelte'
. Then somewhere in your code or build pipeline add your own. There is a tailwind stylesheet via import 'svelte-select/tailwind.css'
. It uses @extend
so PostCSS is required.
npm i
npm run dev-tests
npm test:browser
Open http://localhost:3000 and see devtools console output. When developing it's useful to see the component on the page; comment out the select.$destroy();
on test your debugging in /test/src/index.js and use 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();
});