A highly scalable react dropdown list
react-select-me
A highly scalable react dropdown list.
Advantages
? Lightweight
Only 4.28kb gzipped.
? Highly scalable and extendable
You can literally customize any piece of the component using listRenderer
, optionRenderer
, selectedBlockRenderer
, iconRenderer
and others.
? Immutable? Virtualized? No problem!
We have various HOC's that may help you to integrate with your existing application.
? CSS modules
All of our classes are extendable with CSS modules. Take a look at list of them.
?️♂️ Debuggable
Yes, yes! You can inspect dropdown list with help of DevTools. You know what I'm talking about, right?
Still not sure? We have a lot of other cool features. Take a look at our examples.
- Installation
- Usage
- Examples
- HOC
- Properties:
- options: Array
- value: Any
- multiple: Bool
- searchable: Bool
- virtualized: Bool
- onChange: Function
- onSearch: Function
- onAddNewItem: Bool
- selectedValueRenderer: Function
- selectedBlockRenderer: Function
- optionRenderer: Function
- listRenderer: Function
- iconRenderer: Function
- noItemsFound: Bool | String | Function
- addNewItem: Bool | String | Function
- isOpened: Bool
- beforeOpen: Function
- beforeClose: Function
- onOpen: Function
- onClose: Function
- searchClearOnClose: Bool
- listMaxHeight: Number
- listHeight: Number
- optionHeight: Number | Function
- listPosition: String
- getWrapper: Function
- boundaryMargin: Number
- forbidPhantomSelection: Bool
- s: Object
Installation
npm i react-select-me --save
Usage
import Select from 'react-select-me';
// IMPORTANT If you want to provide default styles you have to import them
import 'react-select-me/lib/ReactSelectMe.css';
const options = [{ value: 1, label: 'Label 1' }, { value: 2, label: 'Label 2' }];
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { value: null };
this.onChange = this.onChange.bind(this);
}
onChange(value) {
this.setState({ value });
}
render() {
return <Select options={options} value={this.state.value} onChange={this.onChange} />;
}
}
Examples
Live
http://maslianok.github.io/react-select-me/
Local
-
Clone the repo
git clone git@github.com:maslianok/react-select-me.git
-
Go to the directory
cd react-select-me
-
Install dependencies
npm i
-
Run demo app
npm start
-
Open
localhost:3000
in your browser
HOC
We've extracted some key features into separate HOCs to keep the main library as small as possible
makeVirtualized
Uses virtualized list to render dropdown options. It allows you to render huge lists without affecting the page performance.
import Select from 'react-select-me';
import makeVirtualized from 'react-select-me/lib/hoc/makeVirtualized';
const VirtualizedSelect = makeVirtualized(Select);
// now you can use the VirtualizedSelect component as usual
// ...
<VirtualizedSelect options={options} value={this.state.value} onChange={this.onChange} />;
// ...
makeImmutable
Integrates with immutable-js which allows you to pass immutable structures as component's props.
import Select from 'react-select-me';
import makeImmutable from 'react-select-me/lib/hoc/makeImmutable';
const ImmutableSelect = makeImmutable(Select);
// now you can pass immutable data as options
// ...
<ImmutableSelect options={options} value={this.state.value} onChange={this.onChange} />;
// ...
Properties:
options: Array
Description: list of dropdown options
Default: undefined
Examples:
- List of primitives:
[1, 2]
- List of objects:
[{value: 1, label: 'Label 1'}, {value: 2, label: 'Label 2'}]
value: Any
Description: selected value / values
Default: undefined
Examples:
- Primitive:
1
- Object:
{value: 1, label: 'Label 1'}
- Array of primitives for multiselect:
[1, 2]
- Array of objects for multiselect:
[{value: 1, label: 'Label 1'}, {value: 2, label: 'Label 2'}]
multiple: Bool
Description: multi-value dropdown
Default: false
searchable: Bool
Description: ability to search / filter options. onSearch
function will be called
Default: false
virtualized: Bool
Description: partly render list options using react-virtualized. Huge time to render boost on large datasets. You have to set optionHeight
property if your option height differs from default.
Default: false
immutable: Bool
Description: parse data as immutable lists. When this property set to true
you have to provide options
and value
as immutable objects.
Default: false
onChange: Function
Description: onChange callback. Return false
to leave dropdown opened.
Default: undefined
Arguments:
value: Array|Object|String|Number
: selected option (or array of options for multi select)
Example:
onChange(value) {
// handle new value
}
onSearch: Function
Description: onSearch callback. Calls on every search input change. You have to process search string inside this function and filter your options based on your needs.
Default: undefined
Arguments:
search: String
: search string
Example:
const options = [
{ value: 1, label: 'Label 1' },
{ value: 2, label: 'Label 2' },
];
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { options };
this.onSearch = this.onSearch.bind(this);
}
onSearch(searchString) {
this.setState({
options: options.filter(o => o.label.indexOf(searchString) > -1)
});
}
render() {
return (
<Select
searchable
options={this.state.options}
onSearch={this.onSearch}
...
/>
);
}
}
onAddNewItem: Bool
Description: callback to handle click on the 'Add new item' option
Default: undefined
Arguments:
search: String
: search string
selectedValueRenderer: Function
Description: function to render selected value
Default: undefined
Arguments:
option: Object|String|Number
: option to renderonRemove: Function
: default function to remove value
Example:
selectedValueRenderer(option, onRemove) {
return <div style={{color: 'red'}}>{option.label}</div>;
}
selectedBlockRenderer: Function
Description: function to render block with selected options
Default: undefined
Arguments:
selectedOptions: Array
: currently selected optionsonRemove: Function
: default function to remove valueselectedValueRenderer: Function
: default function to render selected valuesearchInputRenderer: Function
: default function to render search block
Example:
selectedBlockRenderer(selectedOptions, onRemove) {
return <div>{selectedOptions.map(option => option.label).join(', ')}</div>;
}
optionRenderer: Function
Description: function to render custom options
Default: undefined
Arguments:
option: Object|String|Number
: option to renderselectedOptions: Array
: currently selected options
Example:
optionRenderer(option, selectedOptions) {
return <div style={{color: 'red'}}>{option.label}</div>;
}
listRenderer: Function
Description: function to render the list
Arguments:
options: Array
: list of optionsselectedOptions: Array
: currently selected optionsoptionRenderer: Function
: default option rendereronChange: Function
: default onChange callbackonToggleList: Function
: toggle list visibility
Example:
- Simple
listRenderer(options, selectedOptions, optionRenderer) {
return <ul>{options.map(option => optionRenderer(option, selectedOptions))}</ul>;
}
- Advanced
listRenderer(options, selectedOptions, optionRenderer, onChange, onToggle) {
return (
<div className={s.listWrapper}>
<div className={s.options}>
{options.map(option => (
<div className={s.option} onClick={onChange(option)} key={option.value}>
<div style={{backgroundColor: option.color}} className={s.circle}></div>
<div>{option.label}</div>
</div>
))}
</div>
<div className={s.actions>
<button className={s.btn} onClick={onToggle}>Save</button>
</div>
</div>
);
}
iconRenderer: Function
Description: function to render custom icon.
Default: undefined
Arguments:
isOpened: Bool
: whether the list opened
Example:
iconRenderer(isOpened) {
return <i className={isOpened ? 'icon-open' : 'icon-close'} />;
}
noItemsFound: Bool | String | Function
Description: Bool: whether to display 'No items found' option or not. String: 'No items found' label. Function: 'No items found' renderer
Default: true
Example:
noItemsFound() {
return <div className="my-awesome-class">No items found</div>;
}
addNewItem: Bool | String | Function
Description: Bool: whether to display 'Add new item' option or not. String: 'Add new item' label. Function: 'Add new item' renderer. You must handle onClick event via onAddNewItem
callback or your own callback in case of custom renderer.
Default: false
Example:
addNewItem(search) {
return <div className="my-awesome-class" onClick={this.addNewItemToDropdownOptions}>{`Add '${search}'`}</div>;
}
isOpened: Bool
Description: setting this property makes open / close functionality uncontrollable. It always opened when isOpened === true
and always closed when isOpened === false
. Setting this property to undefined
returns component to the usual behaviour.
Default: undefined
beforeOpen: Function
Description: before open handler. Return false
to leave dropdown closed.
Default: undefined
Arguments:
event: Object
: event
beforeClose: Function
Description: before close event. Return false
to leave dropdown opened.
Default: undefined
onOpen: Function
Description: handler for when the menu opens
Default: undefined
onClose: Function
Description: handler for when the menu closes
Default: undefined
searchClearOnClose: Bool
Description: whether to clear the input on close or not
Default: true
listMaxHeight: Number
Description: Dropdown list max height in pixels.
Default: 400
listHeight: Number
Description: when you set this property the list will always have the constant height despite options length and available space. You have to set this property only when you are creating something like horizontally scrolling lists or some other weird lists :) Otherwise, you probably need to listMaxHeight
.
optionHeight: Number | Function
Description: option height. This property has to be set for virtualized lists, because react-virtualized has to know total options height to correctly display scroll. It also used to calculate direction to open the list (in case of direction="auto"
).
Default: 40
listPosition: String
Description: Dropdown list position.
Default: auto
Available values:
top
: expand to topbottom
: expand to bottomauto
: auto detection based onwrapper
element
getWrapper: Function
Description: Function to get wrapper element. Commonly you have to set this parameter if any of component's parents has overflow: hidden
property. This parameter affects to listMaxHeight
and listPosition
properties.
boundaryMargin: Number
Description: the minimal distance between screen / wrapper
boundaries and dropdown list.
Default: 6
forbidPhantomSelection: Bool
Description: doesn't select a value
option if it doesn't exist in options
array
Default: false
s: Object
Description: component classNames.
List of supported classes:
{
// wrapper
dd__wrapper,
// applied to multi select
dd__multi,
// applied to single select
dd__single,
// applied when dropdown opened
dd__opened,
// applied when dropdown has error property
dd__error,
// disabled
dd_disabled: classType,
// selected block class
dd__selectControl,
// selected values wrapper class
dd__selected,
// placeholder class
dd__placeholder,
// selected option class
dd__selectedItem,
// icon to remove selected value class
dd__crossIcon,
// list class
dd__list,
// virtualized list class
dd__listVirtualized,
// applied when select opens to bottom
dd__openTobottom,
// applied when select opens to top
dd__openTotop,
// dropdown option
dd__option,
// virtualized option class
dd__optionVirtualized,
// selected dropdown option
dd__selectedOption,
}
Examples:
- If you are using css modules you can import default styles directly to the component:
import Select from 'react-select-me';
import s from 'react-select-me/src/ReactSelectMe.css';
...
<Select s={s} {...otherProps} />
- If you want to customize any element with help of your own classes
const classNames = {
// usual class names
dd__wrapper: 'my-super-class',
// or even with css modules
dd__selectedOption: s.mySuperSelectedOption,
};
<Select s={classNames} {...otherProps} />;