monday-ui-react-core
Version:
Official monday.com UI resources for application development in React.js
265 lines (246 loc) • 6.35 kB
JSX
import React, { useCallback, useMemo, useState } from "react";
import Select, { components } from "react-select";
import AsyncSelect from "react-select/async";
import { WindowedMenuList } from "react-windowed-select";
import PropTypes from "prop-types";
import cx from "classnames";
import MenuComponent from "./components/Menu/Menu";
import DropdownIndicatorComponent from "./components/DropdownIndicator/DropdownIndicator";
import OptionComponent from "./components/Option/Option";
import SingleValueComponent from "./components/SingleValue/SingleValue";
import ClearIndicatorComponent from "./components/ClearIndicator/ClearIndicator";
import { SIZE } from "./DropdownConstants";
import { NOOP } from "../../utils/function-utils";
import styles, { customTheme } from "./Dropdown.styles";
import "./Dropdown.scss";
const Dropdown = ({
className,
placeholder,
disabled,
onMenuOpen,
onMenuClose,
onFocus,
onChange,
searchable,
options,
defaultValue,
value,
noOptionsMessage,
openMenuOnFocus,
openMenuOnClick,
clearable,
OptionRenderer,
ValueRenderer,
rtl,
size,
asyncOptions,
cacheOptions,
defaultOptions,
isVirtualized,
}) => {
const [isOpen, setOpen] = useState(false);
const handleMenuOpen = useCallback(
(data) => {
onMenuOpen(data);
setOpen(true);
},
[onMenuOpen, setOpen]
);
const handleMenuClose = useCallback(
(data) => {
onMenuClose(data);
setOpen(false);
},
[setOpen, onMenuClose]
);
const customStyles = useMemo(() => styles({ size, rtl }), [size, rtl]);
const Menu = useCallback(
(props) => <MenuComponent {...props} isOpen={isOpen} />,
[isOpen]
);
const DropdownIndicator = useCallback(
(props) => <DropdownIndicatorComponent {...props} size={size} />,
[size]
);
const Option = useCallback(
(props) => <OptionComponent {...props} OptionRenderer={OptionRenderer} />,
[OptionRenderer]
);
const Input = useCallback(
(props) => <components.Input {...props} aria-label="Dropdown input" />,
[]
);
const SingleValue = useCallback(
(props) => (
<SingleValueComponent {...props} ValueRenderer={ValueRenderer} />
),
[ValueRenderer]
);
const ClearIndicator = useCallback(
(props) => <ClearIndicatorComponent {...props} size={size} />,
[size]
);
const DropDownComponent = asyncOptions ? AsyncSelect : Select;
const asyncAdditions = {
...(asyncOptions && {
loadOptions: asyncOptions,
cacheOptions: cacheOptions,
...(defaultOptions && { defaultOptions }),
}),
};
const additions = {
...(!asyncOptions && { options }),
};
return (
<DropDownComponent
className={cx("dropdown-wrapper", className)}
components={{
DropdownIndicator,
Menu,
ClearIndicator,
Input,
...(OptionRenderer && { Option }),
...(ValueRenderer && { SingleValue }),
...(isVirtualized && { MenuList: WindowedMenuList }),
}}
size={size}
noOptionsMessage={noOptionsMessage}
placeholder={placeholder}
isDisabled={disabled}
isClearable={clearable}
isSearchable={searchable}
defaultValue={defaultValue}
value={value}
onMenuOpen={handleMenuOpen}
onMenuClose={handleMenuClose}
onFocus={onFocus}
onChange={onChange}
openMenuOnFocus={openMenuOnFocus}
openMenuOnClick={openMenuOnClick}
isRtl={rtl}
styles={customStyles}
theme={customTheme}
{...asyncAdditions}
{...additions}
/>
);
};
Dropdown.size = SIZE;
Dropdown.defaultProps = {
className: "",
placeholder: "",
onMenuOpen: NOOP,
onMenuClose: NOOP,
onKeyDown: NOOP,
onFocus: NOOP,
onChange: NOOP,
searchable: true,
options: [],
noOptionsMessage: NOOP,
clearable: true,
size: SIZE.MEDIUM,
};
Dropdown.propTypes = {
/**
* Custom style
*/
className: PropTypes.string,
/**
* Placeholder to show when no value was selected
*/
placeholder: PropTypes.string,
/**
* If set to true, dropdown will be disabled
*/
disabled: PropTypes.bool,
/**
* Called when menu is opened
*/
onMenuOpen: PropTypes.func,
/**
* Called when menu is closed
*/
onMenuClose: PropTypes.func,
/**
* Called when key is pressed in the dropdown
*/
onKeyDown: PropTypes.func,
/**
* Called when focused
*/
onFocus: PropTypes.func,
/**
* Called when selected value has changed
*/
onChange: PropTypes.func,
/**
* If true, search in options will be enabled
*/
searchable: PropTypes.bool,
/**
* The dropdown options
*/
options: PropTypes.arrayOf(PropTypes.object),
/**
* Text to display when there are no options
*/
noOptionsMessage: PropTypes.func,
/**
* If set to true, the menu will open when focused
*/
openMenuOnFocus: PropTypes.bool,
/**
* If set to true, the menu will open when clicked
*/
openMenuOnClick: PropTypes.bool,
/**
* If set to true, clear button will be added
*/
clearable: PropTypes.bool,
/**
* custom option render function
*/
OptionRenderer: PropTypes.func,
/**
* custom value render function
*/
ValueRenderer: PropTypes.func,
/**
* If set to true, the dropdown will be in Right to Left mode
*/
rtl: PropTypes.bool,
/**
* Set default selected value
*/
defaultValue: PropTypes.object,
/**
* Select menu size from `Dropdown.size` - Dropdown.size.LARGE | Dropdown.size.MEDIUM | Dropdown.size.SMALL
*/
size: PropTypes.string,
/**
* If provided Dropdown will work in async mode. Can be either promise or callback
*/
asyncOptions: PropTypes.oneOfType([
PropTypes.func, // callback
PropTypes.shape({
then: PropTypes.func.isRequired,
catch: PropTypes.func.isRequired,
}), // Promise
]),
/**
* If set to true, fetched async options will be cached
*/
cacheOptions: PropTypes.bool,
/**
* If set, `asyncOptions` will be invoked with its value on mount and the resolved results will be loaded
*/
defaultOptions: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.arrayOf(PropTypes.object),
]),
/**
* If set to true, the menu will use virtualization. Virtualized async works only with
*/
isVirtualized: PropTypes.bool,
};
export default Dropdown;