@propellerads/select
v6.0.2
Published
One select for one value, several values as chips, a list that loads as the user types, and values the user creates. It is [react-select](https://react-select.com) 5 with the library's look: react-select renders unstyled and every part takes a class from
Keywords
Readme
Select
One select for one value, several values as chips, a list that loads as the user types, and values the user creates. It is react-select 5 with the library's look: react-select renders unstyled and every part takes a class from one stylesheet, where every colour, gap, corner and duration comes from @propellerads/tokens. The field matches Input and Button; the menu and its items follow shadcn's Select.
It replaces @propellerads/tags-input: a multi select does everything it did.
Installation
bun add @propellerads/selectThe built entry imports its own stylesheet. The tokens are the app's:
import '@propellerads/tokens/common.css';
import '@propellerads/tokens/propellerads.css'; // or monetag / propush / zeydooUse
import Select from '@propellerads/select';
<Select id="brand" options={brands} value={brand} onChange={setBrand} />
<Select id="brand" options={brands} value={brand} onChange={setBrand} errors={['Choose a brand']} />
<Select id="countries" isMulti options={countries} value={picked} onChange={setPicked} />
<Select id="cities" isMulti isAsync loadOptions={searchCities} minSearchLength={2} />
<Select id="folder" isCreatable options={folders} onCreateOption={createFolder} />Every react-select prop is accepted and passed on — isMulti,
filterOption, formatOptionLabel, getOptionLabel, menuPortalTarget,
closeMenuOnSelect, classNamePrefix, name, and the rest, with the async
and creatable ones. A product's styles is written inline by react-select, so
it wins over the library's classes, as before. Its components replace the
library's, part by part. Its classNames are added to the library's.
What the library adds
| Prop | Default | What it does |
|---------------------------|-------------|---------------------------------------------------------------------|
| errors | [] | The ! mark among the indicators and the red contour. The mark's click does not open the menu |
| showErrors | true | false keeps the contour and hides the mark |
| tone | 'danger' | 'warning' for a value that is allowed but worth a second look |
| icon | — | An icon at the start of the field |
| isCreatable | false | Adds a Create "…" row. A promise from onCreateOption shows the spinner and locks the field |
| createOptionPlaceholder | 'Create ' | What the create row starts with |
| isAsync | false | Loads the options with loadOptions |
| minSearchLength | 0 | An async search waits for this many characters and says so |
| searchableKeys | ['label'] | Pasting a list into a multi select picks every option whose key equals one of its parts |
An option flagged isException shows its chip in the danger colours.
The defaults of v5 stay: a single select is not searchable, the placeholder is
"Select", the menu opens where there is room, and isDisabled on an option is
ignored unless isOptionDisabled is passed. A multi select is searchable, and
reads isDisabled on its options, as react-select does.
Moving from TagsInput
// before
<TagsInput elementId="geo" isMulti options={countries} value={value} onChange={setValue} />
// after
<Select id="geo" isMulti options={countries} value={value} onChange={setValue} />TagsInput's own props still work on Select and are deprecated, so a first move can be the import alone:
| TagsInput | Select |
|-----------------------|-----------------------------------------------------------|
| elementId | id |
| isErrorLabelVisible | showErrors |
| customStyles | styles — all of it, not only control |
| promiseOptions | loadOptions with minSearchLength={2} — the alias keeps the two characters |
| actionColor | --select-chip-bg on an ancestor |
| minHeightInput | ignored: the field grows with its chips |
Paste-to-select, isException chips, the ${id}-clear cross, async and
creatable work as they did.
Parts and overrides
The parts take select__* classes: select__control, select__menu,
select__option, select__multi-value and the rest, with state modifiers such
as select__control--focused and select__option--selected. The custom
properties are the override API:
.filters .select {
--select-menu-max-height: 240px;
--select-chip-bg: var(--color-quiet-bg);
--select-chip-text: var(--color-dark-inversion);
}--select-menu-z-index (2500 by default) lifts the menu above a modal that
stands higher.
Tokens
| What | Token |
|-------------------|----------------------------------------------------------------|
| Field | 36px, --radius-8, contour --color-rich, hover --color-quiet |
| Focus ring | --color-rich, as the contour; with errors, the tone's --color-danger or --color-warning. 2px, offset 1px — the button's |
| Menu | --color-light-inversion, contour --color-rich, --shadow-middle, --radius-8 |
| Item under pointer| --color-action at --opacity-tint; the check --color-action |
| Chips | --color-action-bg / --color-action-text; exceptions --color-danger-bg / --color-danger-text |
| Group heading | --font-description, --color-quiet-text |
| Motion | the menu appears from 95% in --duration-200 |
