TIP
If you need to customize the dropdown menu's behavior, please refer to the intergalactic/popper documentation.
The Select component serves as a wrapper over intergalactic/dropdown-menu with the additional functionality of item selection.
Basic usage
In the simplest case, you can implement the select by passing an array of options. The options
array consists of objects with the following fields:
value
: the value of the selected option.label
: the value displayed in the trigger when selecting an option.children
: represents nested options displayed in the dropdown list.
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
const options = Array(6)
.fill('')
.map((_, index) => ({
value: index, // value of the selected option
label: `Label ${index}`, // the value displayed in the trigger when the option is selected
children: `Option ${index}`, // option's children displayed in the dropdown
}));
const Demo = () => (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='basic-select'>
Basic select
</Text>
<Select mt={2} mr='auto' options={options} placeholder='Select option' id='basic-select' />
</Flex>
);
export default Demo;
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
const options = Array(6)
.fill('')
.map((_, index) => ({
value: index, // value of the selected option
label: `Label ${index}`, // the value displayed in the trigger when the option is selected
children: `Option ${index}`, // option's children displayed in the dropdown
}));
const Demo = () => (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='basic-select'>
Basic select
</Text>
<Select mt={2} mr='auto' options={options} placeholder='Select option' id='basic-select' />
</Flex>
);
export default Demo;
Controlled and uncontrolled modes
The component can operate in either controlled or uncontrolled mode.
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
const options = Array(6)
.fill('')
.map((_, index) => ({
value: index,
label: `Label ${index}`,
children: `Option ${index}`,
}));
const { value: initialValue } = options[0];
const Demo = () => {
const [value, setValue] = React.useState(initialValue);
return (
<Flex gap={2} flexWrap>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='controlled-mode-select'>
Controlled mode
</Text>
<Select
id='controlled-mode-select'
mt={2}
value={value}
onChange={setValue}
options={options}
placeholder='Select option'
m='auto'
w='100%'
/>
</Flex>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='uncontrolled-mode-select'>
Uncontrolled mode
</Text>
<Select
id='uncontrolled-mode-select'
mt={2}
defaultValue={initialValue}
onChange={setValue}
options={options}
placeholder='Select option'
m='auto'
w='100%'
/>
</Flex>
</Flex>
);
};
export default Demo;
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
const options = Array(6)
.fill('')
.map((_, index) => ({
value: index,
label: `Label ${index}`,
children: `Option ${index}`,
}));
const { value: initialValue } = options[0];
const Demo = () => {
const [value, setValue] = React.useState(initialValue);
return (
<Flex gap={2} flexWrap>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='controlled-mode-select'>
Controlled mode
</Text>
<Select
id='controlled-mode-select'
mt={2}
value={value}
onChange={setValue}
options={options}
placeholder='Select option'
m='auto'
w='100%'
/>
</Flex>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='uncontrolled-mode-select'>
Uncontrolled mode
</Text>
<Select
id='uncontrolled-mode-select'
mt={2}
defaultValue={initialValue}
onChange={setValue}
options={options}
placeholder='Select option'
m='auto'
w='100%'
/>
</Flex>
</Flex>
);
};
export default Demo;
Trigger customization
When you need to customize the trigger, you can pass the desired component to the tag
property of the select. The property will be passed to Select.Trigger
and replace its render.
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import { ButtonTrigger, LinkTrigger } from 'intergalactic/base-trigger';
import { Text } from 'intergalactic/typography';
const options = Array(6)
.fill('')
.map((_, index) => ({
value: index,
label: `Label ${index}`,
children: `Option ${index}`,
}));
const Demo = () => (
<Flex gap={4} flexWrap direction='column'>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='button-trigger-select'>
Button trigger select
</Text>
{/* ButtonTrigger is the default trigger */}
<Select
tag={ButtonTrigger}
options={options}
id='button-trigger-select'
placeholder='Select option'
mt={2}
mr='auto'
w='100%'
/>
</Flex>
<Flex direction='column'>
<Select tag={LinkTrigger} options={options} placeholder='Select option' mt={2} mr='auto' />
</Flex>
</Flex>
);
export default Demo;
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import { ButtonTrigger, LinkTrigger } from 'intergalactic/base-trigger';
import { Text } from 'intergalactic/typography';
const options = Array(6)
.fill('')
.map((_, index) => ({
value: index,
label: `Label ${index}`,
children: `Option ${index}`,
}));
const Demo = () => (
<Flex gap={4} flexWrap direction='column'>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='button-trigger-select'>
Button trigger select
</Text>
{/* ButtonTrigger is the default trigger */}
<Select
tag={ButtonTrigger}
options={options}
id='button-trigger-select'
placeholder='Select option'
mt={2}
mr='auto'
w='100%'
/>
</Flex>
<Flex direction='column'>
<Select tag={LinkTrigger} options={options} placeholder='Select option' mt={2} mr='auto' />
</Flex>
</Flex>
);
export default Demo;
In cases when you require deeper customization, you can "unfold" the component into its constituents. The example below shows how to create a Select component for selecting a list of countries.
import React from 'react';
import Select from 'intergalactic/select';
import { Flex } from 'intergalactic/flex-box';
import Flags, { iso2Name } from 'intergalactic/flags';
import { Text } from 'intergalactic/typography';
const formatName = (name) => name?.replace(/([a-z])([A-Z])/g, '$1 $2');
const Demo = () => {
const [value, setValue] = React.useState(null);
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='language-select'>
Language select
</Text>
<Select onChange={setValue} placeholder='Select country'>
<Select.Trigger mt={2} mr='auto' id='language-select'>
<Select.Trigger.Addon>
<Flags iso2={value} />
</Select.Trigger.Addon>
<Select.Trigger.Text>{formatName(iso2Name[value])}</Select.Trigger.Text>
</Select.Trigger>
<Select.Menu hMax={180}>
{Object.keys(iso2Name).map((value) => (
<Select.Option key={value} value={value}>
<Flags iso2={value as keyof typeof iso2Name} mr={2} />
{formatName(iso2Name[value])}
</Select.Option>
))}
</Select.Menu>
</Select>
</Flex>
);
};
export default Demo;
import React from 'react';
import Select from 'intergalactic/select';
import { Flex } from 'intergalactic/flex-box';
import Flags, { iso2Name } from 'intergalactic/flags';
import { Text } from 'intergalactic/typography';
const formatName = (name) => name?.replace(/([a-z])([A-Z])/g, '$1 $2');
const Demo = () => {
const [value, setValue] = React.useState(null);
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='language-select'>
Language select
</Text>
<Select onChange={setValue} placeholder='Select country'>
<Select.Trigger mt={2} mr='auto' id='language-select'>
<Select.Trigger.Addon>
<Flags iso2={value} />
</Select.Trigger.Addon>
<Select.Trigger.Text>{formatName(iso2Name[value])}</Select.Trigger.Text>
</Select.Trigger>
<Select.Menu hMax={180}>
{Object.keys(iso2Name).map((value) => (
<Select.Option key={value} value={value}>
<Flags iso2={value as keyof typeof iso2Name} mr={2} />
{formatName(iso2Name[value])}
</Select.Option>
))}
</Select.Menu>
</Select>
</Flex>
);
};
export default Demo;
DropdownMenu customization
Similar to intergalactic/dropdown-menu, the dropdown menu can be implemented in two ways:
Select.Menu
Select.Popper
+Select.List
These components serve as wrappers over the corresponding components of the DropdownMenu.
Select.Popper
is a layout for the dropdown window.Select.List
is a component for the option list with the ScrollArea inside.Select.Menu
is a wrapper overSelect.Popper
andSelect.List
, and all props are passed toSelect.List
.
The example below shows how to insert a Notice in the Select dropdown window.
import React from 'react';
import Select from 'intergalactic/select';
import { Flex } from 'intergalactic/flex-box';
import Notice from 'intergalactic/notice';
import { Text } from 'intergalactic/typography';
const options = Array(12)
.fill('')
.map((_, index) => `Option ${index}`);
const noticeStyle = {
border: 'none',
borderRadius: '0 0 6px 6px',
padding: '12px 8px',
};
const Demo = () => (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='customized-dropdown-select'>
Customized dropdown
</Text>
<Select placeholder={'Select something'}>
<Select.Trigger mt={2} mr='auto' id='customized-dropdown-select' />
<Select.Popper>
<Select.List hMax='240px'>
{options.map((option, index) => (
<Select.Option value={option} key={index}>
{option}
</Select.Option>
))}
</Select.List>
<Notice style={noticeStyle}>
<Notice.Content aria-live='polite'>Woooop, it's simple magic!</Notice.Content>
</Notice>
</Select.Popper>
</Select>
</Flex>
);
export default Demo;
import React from 'react';
import Select from 'intergalactic/select';
import { Flex } from 'intergalactic/flex-box';
import Notice from 'intergalactic/notice';
import { Text } from 'intergalactic/typography';
const options = Array(12)
.fill('')
.map((_, index) => `Option ${index}`);
const noticeStyle = {
border: 'none',
borderRadius: '0 0 6px 6px',
padding: '12px 8px',
};
const Demo = () => (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='customized-dropdown-select'>
Customized dropdown
</Text>
<Select placeholder={'Select something'}>
<Select.Trigger mt={2} mr='auto' id='customized-dropdown-select' />
<Select.Popper>
<Select.List hMax='240px'>
{options.map((option, index) => (
<Select.Option value={option} key={index}>
{option}
</Select.Option>
))}
</Select.List>
<Notice style={noticeStyle}>
<Notice.Content aria-live='polite'>Woooop, it's simple magic!</Notice.Content>
</Notice>
</Select.Popper>
</Select>
</Flex>
);
export default Demo;
Options
The component offers several variants of options layout:
Select.Option
: an element of the list (can be selected from the keyboard).Select.OptionCheckbox
: an element of the list for multiple selections (can be selected from the keyboard).Select.OptionTitle
: a title of the list (cannot be selected from the keyboard).Select.OptionHint
: a subtitle of the list or a message with additional information (cannot be selected from the keyboard).
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
const Demo = () => (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='options-select'>
Options
</Text>
<Select>
<Select.Trigger
placeholder="I'll show u some options, buddy"
mr='auto'
mt={2}
id='options-select'
/>
<Select.Menu>
<Select.Option value={1}>I'm option</Select.Option>
<Select.Option value={2}>
<Select.Option.Checkbox />
I'm option-checkbox
</Select.Option>
<Select.Option value={3} disabled>
<Select.Option.Checkbox />
I'm disabled option-checkbox
</Select.Option>
<Select.Option value={3}>
<Select.Option.Checkbox indeterminate />
I'm indeterminate option-checkbox
</Select.Option>
<Select.OptionTitle>I'm title</Select.OptionTitle>
<Select.OptionHint>I'm hint</Select.OptionHint>
</Select.Menu>
</Select>
</Flex>
);
export default Demo;
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
const Demo = () => (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='options-select'>
Options
</Text>
<Select>
<Select.Trigger
placeholder="I'll show u some options, buddy"
mr='auto'
mt={2}
id='options-select'
/>
<Select.Menu>
<Select.Option value={1}>I'm option</Select.Option>
<Select.Option value={2}>
<Select.Option.Checkbox />
I'm option-checkbox
</Select.Option>
<Select.Option value={3} disabled>
<Select.Option.Checkbox />
I'm disabled option-checkbox
</Select.Option>
<Select.Option value={3}>
<Select.Option.Checkbox indeterminate />
I'm indeterminate option-checkbox
</Select.Option>
<Select.OptionTitle>I'm title</Select.OptionTitle>
<Select.OptionHint>I'm hint</Select.OptionHint>
</Select.Menu>
</Select>
</Flex>
);
export default Demo;
Options filtering
The InputSearch
is added to Select for filtering elements in the list. This is a stylized wrapper over the Input component with clear button.
The example below shows one of the ways to implement filtering.
import React from 'react';
import Select, { InputSearch } from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
import { Flex } from 'intergalactic/flex-box';
const data = Array(26)
.fill(0)
.map((_, index) => ({
label: `Option ${String.fromCharCode('a'.charCodeAt(0) + index)}`,
value: `Option ${String.fromCharCode('a'.charCodeAt(0) + index)}`,
}));
const Demo = () => {
const [filter, setFilter] = React.useState('');
const options = React.useMemo(
() => data.filter((option) => option.value.toString().includes(filter)),
[filter],
);
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='options-filtering-select'>
Options filtering
</Text>
<Select placeholder='Select value'>
<Select.Trigger id='options-filtering-select' mr='auto' mt={2} />
<Select.Popper>
{({ highlightedIndex }) => (
<>
<InputSearch
value={filter}
onChange={setFilter}
placeholder='Search'
role='combobox'
aria-autocomplete='list'
aria-controls='search-list'
aria-owns='search-list'
aria-expanded='true'
aria-activedescendant={`option-${highlightedIndex}`}
/>
<Select.List hMax={'224px'} id='search-list'>
{options.map(({ value, label }, index) => (
<Select.Option
value={value}
key={value}
id={`option-${index}`}
aria-selected={index === highlightedIndex}
>
{label}
</Select.Option>
))}
{!options.length && (
<Select.OptionHint key='Nothing'>Nothing found</Select.OptionHint>
)}
</Select.List>
</>
)}
</Select.Popper>
</Select>
</Flex>
);
};
export default Demo;
import React from 'react';
import Select, { InputSearch } from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
import { Flex } from 'intergalactic/flex-box';
const data = Array(26)
.fill(0)
.map((_, index) => ({
label: `Option ${String.fromCharCode('a'.charCodeAt(0) + index)}`,
value: `Option ${String.fromCharCode('a'.charCodeAt(0) + index)}`,
}));
const Demo = () => {
const [filter, setFilter] = React.useState('');
const options = React.useMemo(
() => data.filter((option) => option.value.toString().includes(filter)),
[filter],
);
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='options-filtering-select'>
Options filtering
</Text>
<Select placeholder='Select value'>
<Select.Trigger id='options-filtering-select' mr='auto' mt={2} />
<Select.Popper>
{({ highlightedIndex }) => (
<>
<InputSearch
value={filter}
onChange={setFilter}
placeholder='Search'
role='combobox'
aria-autocomplete='list'
aria-controls='search-list'
aria-owns='search-list'
aria-expanded='true'
aria-activedescendant={`option-${highlightedIndex}`}
/>
<Select.List hMax={'224px'} id='search-list'>
{options.map(({ value, label }, index) => (
<Select.Option
value={value}
key={value}
id={`option-${index}`}
aria-selected={index === highlightedIndex}
>
{label}
</Select.Option>
))}
{!options.length && (
<Select.OptionHint key='Nothing'>Nothing found</Select.OptionHint>
)}
</Select.List>
</>
)}
</Select.Popper>
</Select>
</Flex>
);
};
export default Demo;
Loading state
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import Spin from 'intergalactic/spin';
import { Text } from 'intergalactic/typography';
const Demo = () => (
<Flex gap={2} flexWrap>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='loading-select'>
Normal loading state
</Text>
<Select mt={2} mr='auto' id='loading-select'>
<Select.Trigger loading>Trigger</Select.Trigger>
</Select>
</Flex>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='loading-select-no-chevron'>
Loading state without chevron
</Text>
<div>
<Select mt={2} mr='auto' id='loading-select-no-chevron'>
<Select.Trigger chevron={false} placeholder={<Spin size='xs' mx={4} />} />
</Select>
</div>
</Flex>
</Flex>
);
export default Demo;
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import Spin from 'intergalactic/spin';
import { Text } from 'intergalactic/typography';
const Demo = () => (
<Flex gap={2} flexWrap>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='loading-select'>
Normal loading state
</Text>
<Select mt={2} mr='auto' id='loading-select'>
<Select.Trigger loading>Trigger</Select.Trigger>
</Select>
</Flex>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='loading-select-no-chevron'>
Loading state without chevron
</Text>
<div>
<Select mt={2} mr='auto' id='loading-select-no-chevron'>
<Select.Trigger chevron={false} placeholder={<Spin size='xs' mx={4} />} />
</Select>
</div>
</Flex>
</Flex>
);
export default Demo;
Advanced filtering control
To get more control over the parts of InputSearch
component, you can use children InputSearch.SearchIcon
, InputSearch.Value
and InputSearch.Clear
components.
In the example below clear button handler is disabled.
import React from 'react';
import Select, { InputSearch } from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
import { Flex } from 'intergalactic/flex-box';
const data = Array(26)
.fill(0)
.map((_, index) => ({
label: `Option ${String.fromCharCode('a'.charCodeAt(0) + index)}`,
value: `Option ${String.fromCharCode('a'.charCodeAt(0) + index)}`,
}));
const Demo = () => {
const [filter, setFilter] = React.useState('');
const options = React.useMemo(
() => data.filter((option) => option.value.toString().includes(filter)),
[filter],
);
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='advanced-filtering'>
Advanced filtering control
</Text>
<Select placeholder='Select value'>
<Select.Trigger mt={2} mr='auto' id='advanced-filtering' />
<Select.Popper>
{({ highlightedIndex }) => (
<>
<InputSearch value={filter} onChange={setFilter}>
<InputSearch.SearchIcon />
<InputSearch.Value
placeholder='Search'
role='combobox'
aria-autocomplete='list'
aria-controls='search-list'
aria-owns='search-list'
aria-expanded='true'
aria-activedescendant={`option-${highlightedIndex}`}
/>
<InputSearch.Clear
onClick={() => {
return false;
}}
/>
</InputSearch>
<Select.List hMax={'224px'} id='search-list'>
{options.map(({ value, label }, index) => (
<Select.Option
value={value}
key={value}
id={`option-${index}`}
aria-selected={index === highlightedIndex}
>
{label}
</Select.Option>
))}
{!options.length && (
<Select.OptionHint key='Nothing'>Nothing found</Select.OptionHint>
)}
</Select.List>
</>
)}
</Select.Popper>
</Select>
</Flex>
);
};
export default Demo;
import React from 'react';
import Select, { InputSearch } from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
import { Flex } from 'intergalactic/flex-box';
const data = Array(26)
.fill(0)
.map((_, index) => ({
label: `Option ${String.fromCharCode('a'.charCodeAt(0) + index)}`,
value: `Option ${String.fromCharCode('a'.charCodeAt(0) + index)}`,
}));
const Demo = () => {
const [filter, setFilter] = React.useState('');
const options = React.useMemo(
() => data.filter((option) => option.value.toString().includes(filter)),
[filter],
);
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='advanced-filtering'>
Advanced filtering control
</Text>
<Select placeholder='Select value'>
<Select.Trigger mt={2} mr='auto' id='advanced-filtering' />
<Select.Popper>
{({ highlightedIndex }) => (
<>
<InputSearch value={filter} onChange={setFilter}>
<InputSearch.SearchIcon />
<InputSearch.Value
placeholder='Search'
role='combobox'
aria-autocomplete='list'
aria-controls='search-list'
aria-owns='search-list'
aria-expanded='true'
aria-activedescendant={`option-${highlightedIndex}`}
/>
<InputSearch.Clear
onClick={() => {
return false;
}}
/>
</InputSearch>
<Select.List hMax={'224px'} id='search-list'>
{options.map(({ value, label }, index) => (
<Select.Option
value={value}
key={value}
id={`option-${index}`}
aria-selected={index === highlightedIndex}
>
{label}
</Select.Option>
))}
{!options.length && (
<Select.OptionHint key='Nothing'>Nothing found</Select.OptionHint>
)}
</Select.List>
</>
)}
</Select.Popper>
</Select>
</Flex>
);
};
export default Demo;
Multiselect
The component has the ability to select several options. This functionality can be enabled by using the multiselect
property.
The layout of options inside the component will be changed to Select.OptionCheckbox
, and the value
will become an array.
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
const options = Array(20)
.fill('')
.map((_, index) => ({
value: index,
label: `Label ${index}`,
children: `Option ${index}`,
}));
const Demo = () => (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='multiselect-select'>
Multiselect
</Text>
<Select mt={2} mr='auto' id='multiselect-select' options={options} multiselect />
</Flex>
);
export default Demo;
import React from 'react';
import { Flex } from 'intergalactic/flex-box';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
const options = Array(20)
.fill('')
.map((_, index) => ({
value: index,
label: `Label ${index}`,
children: `Option ${index}`,
}));
const Demo = () => (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='multiselect-select'>
Multiselect
</Text>
<Select mt={2} mr='auto' id='multiselect-select' options={options} multiselect />
</Flex>
);
export default Demo;
Sorting multiselect options
The example below shows one of the ways to sort the selected options.
import React from 'react';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
import { Flex } from 'intergalactic/flex-box';
const options = Array(20)
.fill('')
.map((i, idx) => ({
value: idx,
title: `Awesome option ${idx}`,
}));
const Option = ({ value, title }) => (
<Select.Option value={value} key={value}>
<Select.Option.Checkbox />
{title}
</Select.Option>
);
const Demo = () => {
const [selected, setSelected] = React.useState([]);
const [prevSelected, setPrevSelected] = React.useState([]);
const handleVisibleChange = (value) => {
if (value) return;
setPrevSelected(options.filter((o) => selected.includes(o.value)));
};
const renderOptions = () => {
if (!prevSelected.length) {
return options.map((props) => <Option key={props.value} {...props} />);
}
const [checked, unchecked] = options.reduce(
(acc, o) => {
prevSelected.find((v) => v.value === o.value) ? acc[0].push(o) : acc[1].push(o);
return acc;
},
[[], []],
);
return [
...checked.map((props) => <Option key={props.value} {...props} />),
<Select.Divider />,
...unchecked.map((props) => <Option key={props.value} {...props} />),
];
};
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='sortable-multiselect'>
Sortable multiselect
</Text>
<Select
value={selected}
onChange={(v) => setSelected(v)}
onVisibleChange={handleVisibleChange}
multiselect
placeholder='Select values'
>
<Select.Trigger mt={2} mr='auto' id='sortable-multiselect' />
<Select.Menu hMax='240px'>{renderOptions()}</Select.Menu>
</Select>
</Flex>
);
};
export default Demo;
import React from 'react';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
import { Flex } from 'intergalactic/flex-box';
const options = Array(20)
.fill('')
.map((i, idx) => ({
value: idx,
title: `Awesome option ${idx}`,
}));
const Option = ({ value, title }) => (
<Select.Option value={value} key={value}>
<Select.Option.Checkbox />
{title}
</Select.Option>
);
const Demo = () => {
const [selected, setSelected] = React.useState([]);
const [prevSelected, setPrevSelected] = React.useState([]);
const handleVisibleChange = (value) => {
if (value) return;
setPrevSelected(options.filter((o) => selected.includes(o.value)));
};
const renderOptions = () => {
if (!prevSelected.length) {
return options.map((props) => <Option key={props.value} {...props} />);
}
const [checked, unchecked] = options.reduce(
(acc, o) => {
prevSelected.find((v) => v.value === o.value) ? acc[0].push(o) : acc[1].push(o);
return acc;
},
[[], []],
);
return [
...checked.map((props) => <Option key={props.value} {...props} />),
<Select.Divider />,
...unchecked.map((props) => <Option key={props.value} {...props} />),
];
};
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='sortable-multiselect'>
Sortable multiselect
</Text>
<Select
value={selected}
onChange={(v) => setSelected(v)}
onVisibleChange={handleVisibleChange}
multiselect
placeholder='Select values'
>
<Select.Trigger mt={2} mr='auto' id='sortable-multiselect' />
<Select.Menu hMax='240px'>{renderOptions()}</Select.Menu>
</Select>
</Flex>
);
};
export default Demo;
Render-function
As with many of our components, you can access the logic of the component by passing a render-function to it.
The example below shows how to implement "Select all" and "Deselect all" buttons using this function.
import React from 'react';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
import { Flex } from 'intergalactic/flex-box';
const options = Array(5)
.fill('')
.map((i, idx) => ({
value: `Option ${idx}`,
}));
const Demo = () => (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='render-function-select'>
Select with custom render function
</Text>
<Select placeholder='Select value' multiselect>
{(props, handlers) => {
const {
getTriggerProps, // function encapsulating Select.Trigger logic
getPopperProps, // function encapsulating Select.Popper logic
getListProps, // function encapsulating Select.List logic
getDividerProps, // function encapsulating Select.Divider logic
getItemHintProps, // function encapsulating Select.ItemHint logic
getItemProps, // function encapsulating Select.Item logic,
getItemTitleProps, // function encapsulating Select.ItemTitle logic
getOptionProps, // function encapsulating Select.Option logic
getOptionCheckboxProps, // function encapsulating Select.OptionCheckbox logic
value: currentValue, // the current value of the select
} = props;
const {
visible, // function that controls the internal state of visibility
value, // function that controls the internal state of the selected value
} = handlers;
const handleClick = () => {
const newValue = (currentValue as any).length ? [] : options.map(({ value }) => value);
value(newValue);
return false; // cancel the default handler
};
return (
<React.Fragment>
<Select.Trigger mt={2} mr='auto' id='render-function-select' />
<Select.Menu>
<Select.Option value='%all%' onClick={handleClick}>
<Text color='text-link'>
{(currentValue as any).length ? 'Deselect all' : 'Select all'}
</Text>
</Select.Option>
{options.map((option) => (
<Select.Option value={option.value} key={option.value}>
<Select.Option.Checkbox />
{option.value}
</Select.Option>
))}
</Select.Menu>
</React.Fragment>
);
}}
</Select>
</Flex>
);
export default Demo;
import React from 'react';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
import { Flex } from 'intergalactic/flex-box';
const options = Array(5)
.fill('')
.map((i, idx) => ({
value: `Option ${idx}`,
}));
const Demo = () => (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='render-function-select'>
Select with custom render function
</Text>
<Select placeholder='Select value' multiselect>
{(props, handlers) => {
const {
getTriggerProps, // function encapsulating Select.Trigger logic
getPopperProps, // function encapsulating Select.Popper logic
getListProps, // function encapsulating Select.List logic
getDividerProps, // function encapsulating Select.Divider logic
getItemHintProps, // function encapsulating Select.ItemHint logic
getItemProps, // function encapsulating Select.Item logic,
getItemTitleProps, // function encapsulating Select.ItemTitle logic
getOptionProps, // function encapsulating Select.Option logic
getOptionCheckboxProps, // function encapsulating Select.OptionCheckbox logic
value: currentValue, // the current value of the select
} = props;
const {
visible, // function that controls the internal state of visibility
value, // function that controls the internal state of the selected value
} = handlers;
const handleClick = () => {
const newValue = (currentValue as any).length ? [] : options.map(({ value }) => value);
value(newValue);
return false; // cancel the default handler
};
return (
<React.Fragment>
<Select.Trigger mt={2} mr='auto' id='render-function-select' />
<Select.Menu>
<Select.Option value='%all%' onClick={handleClick}>
<Text color='text-link'>
{(currentValue as any).length ? 'Deselect all' : 'Select all'}
</Text>
</Select.Option>
{options.map((option) => (
<Select.Option value={option.value} key={option.value}>
<Select.Option.Checkbox />
{option.value}
</Select.Option>
))}
</Select.Menu>
</React.Fragment>
);
}}
</Select>
</Flex>
);
export default Demo;