@wix/design-system
Version:
@wix/design-system
735 lines • 31.2 kB
JavaScript
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import Loader from '../Loader/Loader';
import InfiniteScroll from '../utils/InfiniteScroll';
import scrollIntoView from '../utils/scrollIntoView';
import { DATA_HOOKS, DATA_OPTION, DATA_SHOWN, DATA_DIRECTION, DROPDOWN_LAYOUT_DIRECTIONS, OPTION_DATA_HOOKS, DROPDOWN_LAYOUT_LOADER, DATA_SELECTED_OPTION_ID, } from './DataAttr';
import { st, classes } from './DropdownLayout.st.css.js';
import { filterObject } from '../utils/filterObject';
import { listItemSectionBuilder } from '../ListItemSection';
import { listItemSelectBuilder } from '../ListItemSelect';
import { listItemActionBuilder } from '../ListItemAction';
import { isString } from '../utils/StringUtils';
import { withOverlayScrollbar, } from './withOverlayScrollbar';
import { DIVIDER_OPTION_VALUE, KEY, ListType, MOUSE_EVENTS_SUPPORTED, NOT_HOVERED_INDEX, Scrollbar, } from './DropdownLayout.constants';
import { forkRef } from '../utils/forkRef';
const modulus = (n, m) => ((n % m) + m) % m;
const getUnit = (value) => isString(value) ? value : `${value}px`;
export class DropdownLayout extends PureComponent {
constructor(props) {
super(props);
this.setScrollElement = (node) => {
this.setState({ scrollElement: node });
};
this.containerRef = React.createRef();
this.optionsRef = React.createRef();
this.loadedWithUndefinedOptions = false;
this._boundEvents = [];
this.focusableItemsIdsList = [];
this.savedOnClicks = [];
this.children = {};
this.selectedOption = null;
/* @deprecated */
this._onMouseEventsHandler = (e) => {
if (!this._checkIfEventOnElements(e, [this.containerRef.current])) {
this._onClickOutside(e);
}
};
this._onClickOutside = (event) => {
const { visible, onClickOutside } = this.props;
if (visible && onClickOutside) {
onClickOutside(event);
}
};
this._onSelect = (index, e) => {
const { onSelect, listType } = this.props;
const options = this.props.options || [];
if (listType !== ListType.select) {
e.stopPropagation();
this._onClose();
return;
}
const chosenOption = options[index];
if (chosenOption) {
const sameOptionWasPicked = chosenOption.id === this.state.selectedId;
if (onSelect) {
e.stopPropagation();
onSelect(
// casting to avoid breaking changes after TS conversion
chosenOption, sameOptionWasPicked);
}
}
if (!this._isControlled()) {
this.setState({ selectedId: chosenOption && chosenOption.id });
}
return !!onSelect && chosenOption;
};
this._onActionClick = (e) => {
const onClick = this.savedOnClicks.find(({ id }) => id === e.id)?.onClick;
onClick && onClick(e);
};
this._saveOnClicks = () => {
const options = this.props.options || [];
this.savedOnClicks = options.map(({ id,
// only builder options have onClick
// @ts-expect-error
onClick, }) => ({
id,
onClick,
}));
};
this._onMouseEnter = (index) => {
const { options = [] } = this.props;
if (this._isSelectableOption(options[index])) {
const markedOption = options[index];
this.props.onOptionsNavigate?.(markedOption);
this._markOption(index);
}
};
this._onMouseLeave = () => {
this._markOption(NOT_HOVERED_INDEX);
};
this._onContainerMouseLeave = (e) => {
this.props.onOptionsNavigate?.(null);
this._markOption(NOT_HOVERED_INDEX);
if (this.props.onMouseLeave) {
this.props.onMouseLeave(e);
}
};
this._focusOnOption = () => {
const { focusOnOption } = this.props;
const options = this.props.options || [];
const markedIndex = options.findIndex(option => option.id === focusOnOption);
if (markedIndex !== -1) {
this._markOptionAtIndex(markedIndex);
}
else {
// Remove focus
this._markOption(markedIndex);
}
};
this._markOptionAtIndex = (markedIndex) => {
this._markOption(markedIndex);
this._scrollIntoViewByIndex(markedIndex);
};
this._scrollIntoViewByIndex = (index) => {
const { infiniteScroll } = this.props;
const menuElement = this.optionsRef.current;
const hoveredElement = infiniteScroll
? this.optionsRef.current?.childNodes[0].childNodes[index]
: this.optionsRef.current?.childNodes[index];
scrollIntoView(menuElement, hoveredElement);
};
/**
* Handle keydown events for the DropdownLayout, mostly for accessibility
*
* @param event - The keydown event triggered by React
* @returns Whether the event was handled by the component
*/
this._onSelectListKeyDown = (event) => {
if (!this.props.visible ||
// such prop does not exist but let's avoid potential breaking change
// @ts-expect-error
this.props.isComposing ||
this.props.listType !== ListType.select) {
return false;
}
switch (event.key) {
case KEY.arrowDown: {
this._markNextStep(1);
event.preventDefault();
break;
}
case KEY.arrowUp: {
this._markNextStep(-1);
event.preventDefault();
break;
}
case KEY.space:
case KEY.enter: {
if (!this._onSelect(this.state.hovered, event)) {
return false;
}
break;
}
case KEY.tab: {
if (this.props.closeOnSelect) {
return this._onSelect(this.state.hovered, event);
}
else {
if (this._onSelect(this.state.hovered, event)) {
event.preventDefault();
return true;
}
else {
return false;
}
}
}
case KEY.escape: {
this._onClose();
break;
}
default: {
return false;
}
}
event.stopPropagation();
return true;
};
this._focus = (focusedItemId, e) => {
e && e.preventDefault();
const element = this.children[focusedItemId];
if (!element) {
return;
}
const native = element.focus;
const focusableHOC = element.wrappedComponentRef;
const callback = native
? element.focus
: focusableHOC
? focusableHOC.innerComponentRef.focus
: () => ({});
this.setState({ focusedItemId }, () => callback?.());
};
this._handleActionListNavigation = (event, id) => {
const length = this.focusableItemsIdsList.length;
let focusedItemId = this.state.focusedItemId;
const { key } = event;
const currentMenuItemIndex = this.focusableItemsIdsList.indexOf(id);
const firstMenuItem = this.focusableItemsIdsList[0];
const lastMenuItem = this.focusableItemsIdsList[length - 1];
if (key === KEY.arrowLeft || key === KEY.arrowUp) {
focusedItemId =
id === 0
? lastMenuItem
: this.focusableItemsIdsList[currentMenuItemIndex - 1];
}
if (key === KEY.arrowRight || key === KEY.arrowDown) {
focusedItemId =
currentMenuItemIndex === length - 1
? firstMenuItem
: this.focusableItemsIdsList[currentMenuItemIndex + 1];
}
if (key === KEY.home) {
focusedItemId = firstMenuItem;
}
if (key === KEY.end) {
focusedItemId = lastMenuItem;
}
if (focusedItemId !== this.state.focusedItemId) {
this._focus(focusedItemId, event);
this._scrollIntoViewByIndex(focusedItemId);
}
};
this._onActionListKeyDown = (event, id) => {
if (this.props.listType !== ListType.action) {
return;
}
const { key } = event;
if (key === KEY.space || key === KEY.enter) {
event.preventDefault();
this._onActionClick({ id: this.state.focusedItemId });
this._onClose();
}
else if (key === KEY.escape || key === KEY.tab) {
this._onClose();
}
else {
this._handleActionListNavigation(event, id);
}
event.stopPropagation();
};
this._onClose = () => {
this.props.onOptionsNavigate?.(null);
this._markOption(NOT_HOVERED_INDEX);
if (this.props.onClose) {
this.props.onClose();
}
};
this._wrapWithInfiniteScroll = (scrollableElement) => {
if (!this.optionsRef.current) {
this.loadedWithUndefinedOptions = true;
}
return (React.createElement(InfiniteScroll, { useWindow: true, dataHook: DATA_HOOKS.INFINITE_SCROLL_CONTAINER, scrollElement: this.state.scrollElement, loadMore: this.props.loadMore, hasMore: this.props.hasMore, data: this.props.options, loader: React.createElement("div", { className: classes.loader },
React.createElement(Loader, { dataHook: DROPDOWN_LAYOUT_LOADER, size: "small" })) }, scrollableElement));
};
/** for testing purposes only */
this._getDataAttributes = () => {
const { visible, dropDirectionUp } = this.props;
const { selectedId } = this.state;
return filterObject({
'data-hook': DATA_HOOKS.CONTENT_CONTAINER,
[DATA_SHOWN]: visible,
[DATA_SELECTED_OPTION_ID]: selectedId === 0 ? `${selectedId}` : selectedId,
[DATA_DIRECTION]: dropDirectionUp
? DROPDOWN_LAYOUT_DIRECTIONS.UP
: DROPDOWN_LAYOUT_DIRECTIONS.DOWN,
}, (_, value) => !!value);
};
// For testing purposes only
this._getItemDataAttr = ({ hovered, selected, disabled, }) => {
const { itemHeight, selectedHighlight } = this.props;
return filterObject({
[DATA_OPTION.DISABLED]: disabled,
[DATA_OPTION.SELECTED]: selected && selectedHighlight,
[DATA_OPTION.HOVERED]: hovered,
/* deprecated */
[DATA_OPTION.SIZE]: itemHeight,
}, (_, value) => !!value);
};
this._isSelectableOption = (option) => {
if (!option) {
return false;
}
if (this._isDivider(option)) {
return false;
}
// only DropdownLayoutValueOption type has title prop, which is deprecated
// therefore expecting error instead of dealing with it
// @ts-expect-error
const hasTitle = option.title;
return !this._isDivider(option) && !option.disabled && !hasTitle;
};
this.state = {
hovered: NOT_HOVERED_INDEX,
selectedId: props.selectedId,
scrollElement: null,
focusedItemId: null,
};
}
componentDidMount() {
if (this.loadedWithUndefinedOptions && this.optionsRef.current) {
this.forceUpdate();
}
const { focusOnSelectedOption, scrollToOption, autoFocus } = this.props;
if (focusOnSelectedOption) {
this._focusOnSelectedOption();
}
else if (this.props.hasOwnProperty('focusOnOption')) {
this._focusOnOption();
}
if (scrollToOption) {
this._scrollToOption();
}
this._markOptionByProperty(this.props);
/* @deprecated */
MOUSE_EVENTS_SUPPORTED.forEach(eventName => {
document.addEventListener(eventName, this._onMouseEventsHandler, true);
});
this._boundEvents = MOUSE_EVENTS_SUPPORTED;
if (autoFocus) {
this._focusFirstOption();
}
}
componentWillUnmount() {
if (this._boundEvents && typeof document !== 'undefined') {
this._boundEvents.forEach(eventName => {
document.removeEventListener(eventName, this._onMouseEventsHandler, true);
});
}
}
componentDidUpdate(prevProps) {
const { focusOnOption } = this.props;
if (prevProps.focusOnOption !== focusOnOption) {
this._focusOnOption();
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
const options = this.props.options || [];
const nextOptions = nextProps.options || [];
if (this.props.visible !== nextProps.visible) {
this._markOption(NOT_HOVERED_INDEX);
}
if (this.props.selectedId !== nextProps.selectedId) {
this.setState({ selectedId: nextProps.selectedId });
}
// make sure the same item is hovered if options changed
if (this.state.hovered !== NOT_HOVERED_INDEX &&
(!nextOptions[this.state.hovered] ||
options[this.state.hovered].id !== nextOptions[this.state.hovered].id)) {
this._markOption(this._findIndex(nextProps.options ?? [], item => item.id === options[this.state.hovered].id));
}
this._markOptionByProperty(nextProps);
}
_getOptionElementId(optionIndex) {
return `dropdown-item-${optionIndex}`;
}
_focusFirstOption() {
this._focus(this.focusableItemsIdsList[0]);
}
/* @deprecated */
_checkIfEventOnElements(e, elem) {
let current = e.target;
while (current.parentNode) {
if (elem.indexOf(current) > -1) {
return true;
}
current = current.parentNode;
}
return current !== document;
}
/* @deprecated */
_renderTopArrow() {
const { withArrow, visible } = this.props;
return withArrow && visible ? (React.createElement("div", { "data-hook": DATA_HOOKS.TOP_ARROW, className: classes.arrow })) : null;
}
_convertOptionToListItemSectionBuilder({ option, idx, }) {
if (this._isDivider(option)) {
return listItemSectionBuilder({
dataHook: OPTION_DATA_HOOKS.DIVIDER,
id: option.id || idx,
type: 'divider',
});
}
else if (option.title) {
return listItemSectionBuilder({
dataHook: OPTION_DATA_HOOKS.TITLE,
id: option.id,
type: 'subheader',
title: option.value,
});
}
else {
// This should never happen but let's make TS happy
return undefined;
}
}
_convertOptionToListItemActionBuilder({ option, idx, }) {
const { id, value, disabled, disabledDescription, optionTitle, title, ...rest } = option;
const { size } = this.props;
return listItemActionBuilder({
id: id !== undefined ? id : idx,
ref: (ref) => (this.children[id] = ref),
tabIndex: id === this.state.focusedItemId && !disabled ? '0' : '-1',
disabled,
disabledDescription,
title: optionTitle,
role: 'menuitem',
size,
...rest,
});
}
_isControlled() {
return (typeof this.props.selectedId !== 'undefined' &&
typeof this.props.onSelect !== 'undefined');
}
_focusOnSelectedOption() {
if (this.selectedOption && this.optionsRef.current) {
this.optionsRef.current.scrollTop = Math.max(this.selectedOption.offsetTop - this.selectedOption.offsetHeight, 0);
}
}
_setSelectedOptionNode(optionNode, option) {
if (option.id === this.state.selectedId) {
this.selectedOption = optionNode;
}
}
_markOption(index, options) {
const { onOptionMarked } = this.props;
options = options || this.props.options || [];
this.setState({ hovered: index });
if (onOptionMarked) {
const markedOption = options[index] || null;
const markedOptionElementId = index === NOT_HOVERED_INDEX
? undefined
: this._getOptionElementId(index);
// the type declaration was incorrect, casting to avoid breaking changes after TS conversion
onOptionMarked(markedOption, markedOptionElementId);
}
}
_getMarkedIndex() {
const options = this.props.options || [];
const useHoverIndex = this.state.hovered > NOT_HOVERED_INDEX;
const useSelectedIdIndex = typeof this.state.selectedId !== 'undefined';
let markedIndex;
if (useHoverIndex) {
markedIndex = this.state.hovered;
}
else if (useSelectedIdIndex) {
markedIndex = options.findIndex(option => option.id === this.state.selectedId);
}
else {
markedIndex = NOT_HOVERED_INDEX;
}
return markedIndex;
}
getActiveDescendentElementId() {
const options = this.props.options || [];
let markedIndex = this._getMarkedIndex();
if (markedIndex === NOT_HOVERED_INDEX) {
markedIndex = options.findIndex(this._isSelectableOption);
}
return markedIndex === NOT_HOVERED_INDEX
? undefined
: this._getOptionElementId(markedIndex);
}
_markNextStep(step) {
const { onOptionsNavigate, options = [] } = this.props;
if (!options.some(this._isSelectableOption)) {
return;
}
let markedIndex = this._getMarkedIndex();
do {
markedIndex = modulus(Math.max(markedIndex + step, -1), options.length);
} while (!this._isSelectableOption(options[markedIndex]));
const markedOption = options[markedIndex];
onOptionsNavigate?.(markedOption);
this._markOptionAtIndex(markedIndex);
}
_scrollToOption() {
const { scrollToOption } = this.props;
const options = this.props.options || [];
const optionIndex = options.findIndex(option => option.id === scrollToOption);
const optionNode = this.optionsRef.current?.childNodes[optionIndex];
if (!optionNode || !this.optionsRef.current) {
return;
}
this.optionsRef.current.scrollTop = Math.max(optionNode.offsetTop - optionNode.offsetHeight, 0);
}
_renderNode(node) {
return node ? React.createElement("div", null, node) : null;
}
_convertCustomOptionToBuilder({ option, }) {
const { value, id, disabled, disabledDescription, overrideOptionStyle, overrideStyle, } = option;
if (overrideStyle) {
return {
id,
disabled,
disabledDescription,
overrideStyle,
value: () => (React.createElement("div", { className: classes.dropdownOption, "data-hook": DATA_HOOKS.OPTION }, value)),
};
}
if (overrideOptionStyle) {
return {
id,
disabled,
disabledDescription,
overrideOptionStyle,
value: () => (React.createElement("div", { className: classes.dropdownOption, "data-hook": DATA_HOOKS.OPTION }, value)),
};
}
else {
// This should never happen but let's make TS happy
return undefined;
}
}
_convertOptionToListItemSelectBuilder({ option, }) {
const { value, id, disabled, disabledDescription } = option;
const { selectedId } = this.state;
const { itemHeight, selectedHighlight, size } = this.props;
return listItemSelectBuilder({
id,
title: (React.createElement("div", { "data-hook": DATA_HOOKS.OPTION }, value)),
disabled,
disabledDescription,
size,
selected: id === selectedId && selectedHighlight,
className: st(classes.selectableOption, { itemHeight }),
});
}
_isBuilderOption(option) {
const { value } = option;
return typeof value === 'function';
}
_isCustomOption(option) {
const { overrideOptionStyle, overrideStyle } = option;
return overrideOptionStyle || overrideStyle;
}
_isActionOption(option) {
return 'value' in option && option.value === ListType.action;
}
_isItemSection(option) {
// only DropdownLayoutValueOption type has title prop, which is deprecated
// therefore expecting error instead of dealing with it
// @ts-expect-error
const { title: isTitle } = option;
return this._isDivider(option) || isTitle;
}
_isDivider(opt) {
return opt.value === DIVIDER_OPTION_VALUE;
}
_convertOptionToBuilder(option, idx) {
if (this._isBuilderOption(option)) {
return option;
}
else if (this._isActionOption(option)) {
return this._convertOptionToListItemActionBuilder({ option, idx });
}
else if (this._isItemSection(option)) {
return this._convertOptionToListItemSectionBuilder({ option, idx });
}
else if (this._isCustomOption(option)) {
return this._convertCustomOptionToBuilder({ option });
}
else {
return this._convertOptionToListItemSelectBuilder({ option });
}
}
_renderOption({ option, idx, }) {
const isValueOption = (opt) => 'id' in opt && 'value' in opt && typeof opt.value !== 'function';
const builderOption = this._convertOptionToBuilder(option, idx);
const hasLink = isValueOption(option) && !!option.linkTo;
const content = this._renderOptionContent({
option: builderOption,
idx,
hasLink,
});
const isActionItem = this.props.listType === ListType.action;
return hasLink ? (React.createElement("a", { className: classes.linkItem, key: idx, "data-hook": DATA_HOOKS.LINK_ITEM, href: option.linkTo, role: isActionItem ? undefined : 'option', "aria-selected": isActionItem || option.disabled
? undefined
: option.id === this.state.selectedId, "aria-hidden": option.disabled }, content)) : (content);
}
_renderOptionContent({ option, idx, hasLink, }) {
if (!option) {
return undefined;
}
const { itemHeight, selectedHighlight, listType } = this.props;
const { selectedId, hovered } = this.state;
const { id, disabled,
// Only ListItemAction and ListItemSelect have disabledDescription prop
// @ts-expect-error
disabledDescription,
// only ConvertedCustomOption type has overrideStyle prop, which is deprecated
// therefore expecting error instead of dealing with it
// @ts-expect-error
overrideStyle, overrideOptionStyle, } = option;
const optionState = {
selected: id === selectedId,
hovered: idx === hovered,
disabled,
size: this.props.size,
};
const isDisabledWithTooltip = disabled && !!disabledDescription;
if (!disabled || isDisabledWithTooltip) {
this.focusableItemsIdsList = [...this.focusableItemsIdsList, id];
}
const isActionItem = listType === ListType.action;
return (React.createElement("div", { "aria-selected": hasLink || isActionItem || disabled ? undefined : optionState.selected, "aria-hidden": disabled, ...this._getItemDataAttr({ ...optionState }), role: hasLink || isActionItem ? undefined : 'option', className: overrideOptionStyle
? null
: st(classes.option, {
...optionState,
selected: optionState.selected && selectedHighlight,
itemHeight,
overrideStyle,
}), id: this._getOptionElementId(idx), ref: node => this._setSelectedOptionNode(node, option), onClick: !disabled ? e => this._onSelect(idx, e) : undefined, key: idx, onMouseEnter: () => this._onMouseEnter(idx), onMouseLeave: this._onMouseLeave, "data-hook": `dropdown-item-${id}`, onKeyDown: e => this._onActionListKeyDown(e, id) }, option?.value(optionState)));
}
_markOptionByProperty(props) {
const options = props.options || [];
if (this.state.hovered === NOT_HOVERED_INDEX && props.markedOption) {
const selectableOptions = options.filter(this._isSelectableOption);
if (selectableOptions.length) {
const idToMark = props.markedOption === true
? selectableOptions[0].id
: props.markedOption;
const foundIndex = this._findIndex(options, (item) => item.id === idToMark);
if (foundIndex !== -1) {
this._markOption(foundIndex, props.options);
}
}
}
}
_findIndex(arr, predicate) {
return (Array.isArray(arr) ? arr : []).findIndex(predicate);
}
_renderOptions() {
this.focusableItemsIdsList = [];
this._saveOnClicks();
const options = this.props.options || [];
return options.map((option, idx) => this._renderOption({ option, idx }));
}
render() {
const { className, visible, dropDirectionUp, tabIndex, onMouseEnter, onMouseDown, fixedHeader, withArrow, fixedFooter, inContainer, overflow, maxHeightPixels = 260, minWidthPixels, infiniteScroll, dataHook, listType, overlayScrollbarProps, listboxId, } = this.props;
const renderedOptions = this._renderOptions();
const OverlayScrollbarHostElement = overlayScrollbarProps?.OverlayScrollbarHostElement;
const OverlayScrollbarContentElement = overlayScrollbarProps?.OverlayScrollbarContentElement;
const optionsContainerProps = {
style: {
maxHeight: getUnit(parseInt(String(maxHeightPixels), 10) - 35),
overflow,
},
'data-hook': DATA_HOOKS.DROPDOWN_LAYOUT_OPTIONS,
role: listType === ListType.select ? 'listbox' : 'menu',
id: listboxId,
};
const options = infiniteScroll
? this._wrapWithInfiniteScroll(renderedOptions)
: renderedOptions;
const optionsContainer = this.props.scrollbar === Scrollbar.OVERLAY &&
OverlayScrollbarHostElement &&
OverlayScrollbarContentElement ? (React.createElement(OverlayScrollbarHostElement, { className: classes.options, ...optionsContainerProps },
React.createElement(OverlayScrollbarContentElement, { className: classes.optionsContent, ref: forkRef(this.optionsRef, this.setScrollElement) }, options))) : (React.createElement("div", { className: st(classes.options, classes.optionsContent), ref: forkRef(this.optionsRef, this.setScrollElement), ...optionsContainerProps }, options));
return (React.createElement("div", { "data-list-type": listType, "data-hook": dataHook, className: st(classes.root, {
visible,
withArrow,
direction: dropDirectionUp
? DROPDOWN_LAYOUT_DIRECTIONS.UP
: DROPDOWN_LAYOUT_DIRECTIONS.DOWN,
containerStyles: !inContainer,
}, className), tabIndex: tabIndex, onKeyDown: this._onSelectListKeyDown, onMouseEnter: onMouseEnter, onMouseLeave: this._onContainerMouseLeave, onMouseDown: onMouseDown, ref: this.containerRef },
React.createElement("div", { ...this._getDataAttributes(), className: classes.contentContainer, style: {
overflow,
maxHeight: maxHeightPixels ? getUnit(maxHeightPixels) : undefined,
minWidth: minWidthPixels ? getUnit(minWidthPixels) : undefined,
} },
this._renderNode(fixedHeader),
optionsContainer,
this._renderNode(fixedFooter)),
this._renderTopArrow()));
}
}
DropdownLayout.propTypes = {
className: PropTypes.any,
dropDirectionUp: PropTypes.any,
focusOnSelectedOption: PropTypes.any,
onClose: PropTypes.any,
onSelect: PropTypes.any,
onOptionMarked: PropTypes.any,
onOptionsNavigate: PropTypes.any,
overflow: PropTypes.any,
visible: PropTypes.any,
options: PropTypes.any,
selectedId: PropTypes.any,
tabIndex: PropTypes.any,
onClickOutside: PropTypes.any,
fixedHeader: PropTypes.any,
fixedFooter: PropTypes.any,
maxHeightPixels: PropTypes.any,
minWidthPixels: PropTypes.any,
withArrow: PropTypes.any,
closeOnSelect: PropTypes.any,
onMouseEnter: PropTypes.any,
onMouseLeave: PropTypes.any,
itemHeight: PropTypes.any,
selectedHighlight: PropTypes.any,
inContainer: PropTypes.any,
infiniteScroll: PropTypes.any,
loadMore: PropTypes.any,
hasMore: PropTypes.any,
markedOption: PropTypes.any,
focusOnOption: PropTypes.any,
scrollToOption: PropTypes.any,
listType: PropTypes.any,
autoFocus: PropTypes.any,
scrollbar: PropTypes.any,
};
DropdownLayout.defaultProps = {
options: [],
tabIndex: 0,
maxHeightPixels: 260,
closeOnSelect: true,
itemHeight: 'small',
selectedHighlight: true,
inContainer: false,
infiniteScroll: false,
loadMore: undefined,
hasMore: false,
markedOption: false,
overflow: 'auto',
listType: ListType.select,
scrollbar: 'fixed',
};
DropdownLayout.displayName = 'DropdownLayout';
DropdownLayout.NONE_SELECTED_ID = NOT_HOVERED_INDEX;
export default withOverlayScrollbar(DropdownLayout);
//# sourceMappingURL=DropdownLayout.js.map