@wix/design-system
Version:
@wix/design-system
1,039 lines (1,033 loc) • 36.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.default = exports.DropdownLayout = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _react = _interopRequireWildcard(require("react"));
var _Loader = _interopRequireDefault(require("../Loader/Loader"));
var _InfiniteScroll = _interopRequireDefault(require("../utils/InfiniteScroll"));
var _scrollIntoView = _interopRequireDefault(require("../utils/scrollIntoView"));
var _DataAttr = require("./DataAttr");
var _DropdownLayoutSt = require("./DropdownLayout.st.css.js");
var _filterObject = require("../utils/filterObject");
var _ListItemSection = require("../ListItemSection");
var _ListItemSelect = require("../ListItemSelect");
var _ListItemAction = require("../ListItemAction");
var _StringUtils = require("../utils/StringUtils");
var _withOverlayScrollbar = require("./withOverlayScrollbar");
var _DropdownLayout2 = require("./DropdownLayout.constants");
var _forkRef = require("../utils/forkRef");
var _excluded = ["id", "value", "disabled", "disabledDescription", "optionTitle", "title"];
var _jsxFileName = "/home/builduser/work/57e038ea7326c1ec/packages/wix-design-system/dist/cjs/DropdownLayout/DropdownLayout.tsx";
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var modulus = (n, m) => (n % m + m) % m;
var getUnit = value => (0, _StringUtils.isString)(value) ? value : "".concat(value, "px");
class DropdownLayout extends _react.PureComponent {
constructor(props) {
super(props);
this.setScrollElement = node => {
this.setState({
scrollElement: node
});
};
this.containerRef = /*#__PURE__*/_react.default.createRef();
this.optionsRef = /*#__PURE__*/_react.default.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 => {
var {
visible,
onClickOutside
} = this.props;
if (visible && onClickOutside) {
onClickOutside(event);
}
};
this._onSelect = (index, e) => {
var {
onSelect,
listType
} = this.props;
var options = this.props.options || [];
if (listType !== _DropdownLayout2.ListType.select) {
e.stopPropagation();
this._onClose();
return;
}
var chosenOption = options[index];
if (chosenOption) {
var 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 => {
var _this$savedOnClicks$f;
var onClick = (_this$savedOnClicks$f = this.savedOnClicks.find(_ref => {
var {
id
} = _ref;
return id === e.id;
})) == null ? void 0 : _this$savedOnClicks$f.onClick;
onClick && onClick(e);
};
this._saveOnClicks = () => {
var options = this.props.options || [];
this.savedOnClicks = options.map(_ref2 => {
var {
id,
// only builder options have onClick
// @ts-expect-error
onClick
} = _ref2;
return {
id,
onClick
};
});
};
this._onMouseEnter = index => {
var {
options = []
} = this.props;
if (this._isSelectableOption(options[index])) {
var _this$props$onOptions, _this$props;
var markedOption = options[index];
(_this$props$onOptions = (_this$props = this.props).onOptionsNavigate) == null || _this$props$onOptions.call(_this$props, markedOption);
this._markOption(index);
}
};
this._onMouseLeave = () => {
this._markOption(_DropdownLayout2.NOT_HOVERED_INDEX);
};
this._onContainerMouseLeave = e => {
var _this$props$onOptions2, _this$props2;
(_this$props$onOptions2 = (_this$props2 = this.props).onOptionsNavigate) == null || _this$props$onOptions2.call(_this$props2, null);
this._markOption(_DropdownLayout2.NOT_HOVERED_INDEX);
if (this.props.onMouseLeave) {
this.props.onMouseLeave(e);
}
};
this._focusOnOption = () => {
var {
focusOnOption
} = this.props;
var options = this.props.options || [];
var 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 => {
var _this$optionsRef$curr, _this$optionsRef$curr2;
var {
infiniteScroll
} = this.props;
var menuElement = this.optionsRef.current;
var hoveredElement = infiniteScroll ? (_this$optionsRef$curr = this.optionsRef.current) == null ? void 0 : _this$optionsRef$curr.childNodes[0].childNodes[index] : (_this$optionsRef$curr2 = this.optionsRef.current) == null ? void 0 : _this$optionsRef$curr2.childNodes[index];
(0, _scrollIntoView.default)(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 !== _DropdownLayout2.ListType.select) {
return false;
}
switch (event.key) {
case _DropdownLayout2.KEY.arrowDown:
{
this._markNextStep(1);
event.preventDefault();
break;
}
case _DropdownLayout2.KEY.arrowUp:
{
this._markNextStep(-1);
event.preventDefault();
break;
}
case _DropdownLayout2.KEY.space:
case _DropdownLayout2.KEY.enter:
{
if (!this._onSelect(this.state.hovered, event)) {
return false;
}
break;
}
case _DropdownLayout2.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 _DropdownLayout2.KEY.escape:
{
this._onClose();
break;
}
default:
{
return false;
}
}
event.stopPropagation();
return true;
};
this._focus = (focusedItemId, e) => {
e && e.preventDefault();
var element = this.children[focusedItemId];
if (!element) {
return;
}
var native = element.focus;
var focusableHOC = element.wrappedComponentRef;
var callback = native ? element.focus : focusableHOC ? focusableHOC.innerComponentRef.focus : () => ({});
this.setState({
focusedItemId
}, () => callback == null ? void 0 : callback());
};
this._handleActionListNavigation = (event, id) => {
var length = this.focusableItemsIdsList.length;
var focusedItemId = this.state.focusedItemId;
var {
key
} = event;
var currentMenuItemIndex = this.focusableItemsIdsList.indexOf(id);
var firstMenuItem = this.focusableItemsIdsList[0];
var lastMenuItem = this.focusableItemsIdsList[length - 1];
if (key === _DropdownLayout2.KEY.arrowLeft || key === _DropdownLayout2.KEY.arrowUp) {
focusedItemId = id === 0 ? lastMenuItem : this.focusableItemsIdsList[currentMenuItemIndex - 1];
}
if (key === _DropdownLayout2.KEY.arrowRight || key === _DropdownLayout2.KEY.arrowDown) {
focusedItemId = currentMenuItemIndex === length - 1 ? firstMenuItem : this.focusableItemsIdsList[currentMenuItemIndex + 1];
}
if (key === _DropdownLayout2.KEY.home) {
focusedItemId = firstMenuItem;
}
if (key === _DropdownLayout2.KEY.end) {
focusedItemId = lastMenuItem;
}
if (focusedItemId !== this.state.focusedItemId) {
this._focus(focusedItemId, event);
this._scrollIntoViewByIndex(focusedItemId);
}
};
this._onActionListKeyDown = (event, id) => {
if (this.props.listType !== _DropdownLayout2.ListType.action) {
return;
}
var {
key
} = event;
if (key === _DropdownLayout2.KEY.space || key === _DropdownLayout2.KEY.enter) {
event.preventDefault();
this._onActionClick({
id: this.state.focusedItemId
});
this._onClose();
} else if (key === _DropdownLayout2.KEY.escape || key === _DropdownLayout2.KEY.tab) {
this._onClose();
} else {
this._handleActionListNavigation(event, id);
}
event.stopPropagation();
};
this._onClose = () => {
var _this$props$onOptions3, _this$props3;
(_this$props$onOptions3 = (_this$props3 = this.props).onOptionsNavigate) == null || _this$props$onOptions3.call(_this$props3, null);
this._markOption(_DropdownLayout2.NOT_HOVERED_INDEX);
if (this.props.onClose) {
this.props.onClose();
}
};
this._wrapWithInfiniteScroll = scrollableElement => {
if (!this.optionsRef.current) {
this.loadedWithUndefinedOptions = true;
}
return /*#__PURE__*/_react.default.createElement(_InfiniteScroll.default, {
useWindow: true,
dataHook: _DataAttr.DATA_HOOKS.INFINITE_SCROLL_CONTAINER,
scrollElement: this.state.scrollElement,
loadMore: this.props.loadMore,
hasMore: this.props.hasMore,
data: this.props.options,
loader: /*#__PURE__*/_react.default.createElement("div", {
className: _DropdownLayoutSt.classes.loader,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 752,
columnNumber: 11
}
}, /*#__PURE__*/_react.default.createElement(_Loader.default, {
dataHook: _DataAttr.DROPDOWN_LAYOUT_LOADER,
size: "small",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 753,
columnNumber: 13
}
})),
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 744,
columnNumber: 7
}
}, scrollableElement);
};
/** for testing purposes only */
this._getDataAttributes = () => {
var {
visible,
dropDirectionUp
} = this.props;
var {
selectedId
} = this.state;
return (0, _filterObject.filterObject)({
'data-hook': _DataAttr.DATA_HOOKS.CONTENT_CONTAINER,
[_DataAttr.DATA_SHOWN]: visible,
[_DataAttr.DATA_SELECTED_OPTION_ID]: selectedId === 0 ? "".concat(selectedId) : selectedId,
[_DataAttr.DATA_DIRECTION]: dropDirectionUp ? _DataAttr.DROPDOWN_LAYOUT_DIRECTIONS.UP : _DataAttr.DROPDOWN_LAYOUT_DIRECTIONS.DOWN
}, (_, value) => !!value);
};
// For testing purposes only
this._getItemDataAttr = _ref3 => {
var {
hovered,
selected,
disabled
} = _ref3;
var {
itemHeight,
selectedHighlight
} = this.props;
return (0, _filterObject.filterObject)({
[_DataAttr.DATA_OPTION.DISABLED]: disabled,
[_DataAttr.DATA_OPTION.SELECTED]: selected && selectedHighlight,
[_DataAttr.DATA_OPTION.HOVERED]: hovered,
/* deprecated */
[_DataAttr.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
var hasTitle = option.title;
return !this._isDivider(option) && !option.disabled && !hasTitle;
};
this.state = {
hovered: _DropdownLayout2.NOT_HOVERED_INDEX,
selectedId: props.selectedId,
scrollElement: null,
focusedItemId: null
};
}
componentDidMount() {
if (this.loadedWithUndefinedOptions && this.optionsRef.current) {
this.forceUpdate();
}
var {
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 */
_DropdownLayout2.MOUSE_EVENTS_SUPPORTED.forEach(eventName => {
document.addEventListener(eventName, this._onMouseEventsHandler, true);
});
this._boundEvents = _DropdownLayout2.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) {
var {
focusOnOption
} = this.props;
if (prevProps.focusOnOption !== focusOnOption) {
this._focusOnOption();
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
var options = this.props.options || [];
var nextOptions = nextProps.options || [];
if (this.props.visible !== nextProps.visible) {
this._markOption(_DropdownLayout2.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 !== _DropdownLayout2.NOT_HOVERED_INDEX && (!nextOptions[this.state.hovered] || options[this.state.hovered].id !== nextOptions[this.state.hovered].id)) {
var _nextProps$options;
this._markOption(this._findIndex((_nextProps$options = nextProps.options) !== null && _nextProps$options !== void 0 ? _nextProps$options : [], item => item.id === options[this.state.hovered].id));
}
this._markOptionByProperty(nextProps);
}
_getOptionElementId(optionIndex) {
return "dropdown-item-".concat(optionIndex);
}
_focusFirstOption() {
this._focus(this.focusableItemsIdsList[0]);
}
/* @deprecated */
_checkIfEventOnElements(e, elem) {
var current = e.target;
while (current.parentNode) {
if (elem.indexOf(current) > -1) {
return true;
}
current = current.parentNode;
}
return current !== document;
}
/* @deprecated */
_renderTopArrow() {
var {
withArrow,
visible
} = this.props;
return withArrow && visible ? /*#__PURE__*/_react.default.createElement("div", {
"data-hook": _DataAttr.DATA_HOOKS.TOP_ARROW,
className: _DropdownLayoutSt.classes.arrow,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 285,
columnNumber: 7
}
}) : null;
}
_convertOptionToListItemSectionBuilder(_ref4) {
var {
option,
idx
} = _ref4;
if (this._isDivider(option)) {
return (0, _ListItemSection.listItemSectionBuilder)({
dataHook: _DataAttr.OPTION_DATA_HOOKS.DIVIDER,
id: option.id || idx,
type: 'divider'
});
} else if (option.title) {
return (0, _ListItemSection.listItemSectionBuilder)({
dataHook: _DataAttr.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(_ref5) {
var {
option,
idx
} = _ref5;
var {
id,
value,
disabled,
disabledDescription,
optionTitle,
title
} = option,
rest = (0, _objectWithoutProperties2.default)(option, _excluded);
var {
size
} = this.props;
return (0, _ListItemAction.listItemActionBuilder)(_objectSpread({
id: id !== undefined ? id : idx,
ref: _ref6 => this.children[id] = _ref6,
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) {
var {
onOptionMarked
} = this.props;
options = options || this.props.options || [];
this.setState({
hovered: index
});
if (onOptionMarked) {
var markedOption = options[index] || null;
var markedOptionElementId = index === _DropdownLayout2.NOT_HOVERED_INDEX ? undefined : this._getOptionElementId(index);
// the type declaration was incorrect, casting to avoid breaking changes after TS conversion
onOptionMarked(markedOption, markedOptionElementId);
}
}
_getMarkedIndex() {
var options = this.props.options || [];
var useHoverIndex = this.state.hovered > _DropdownLayout2.NOT_HOVERED_INDEX;
var useSelectedIdIndex = typeof this.state.selectedId !== 'undefined';
var markedIndex;
if (useHoverIndex) {
markedIndex = this.state.hovered;
} else if (useSelectedIdIndex) {
markedIndex = options.findIndex(option => option.id === this.state.selectedId);
} else {
markedIndex = _DropdownLayout2.NOT_HOVERED_INDEX;
}
return markedIndex;
}
getActiveDescendentElementId() {
var options = this.props.options || [];
var markedIndex = this._getMarkedIndex();
if (markedIndex === _DropdownLayout2.NOT_HOVERED_INDEX) {
markedIndex = options.findIndex(this._isSelectableOption);
}
return markedIndex === _DropdownLayout2.NOT_HOVERED_INDEX ? undefined : this._getOptionElementId(markedIndex);
}
_markNextStep(step) {
var {
onOptionsNavigate,
options = []
} = this.props;
if (!options.some(this._isSelectableOption)) {
return;
}
var markedIndex = this._getMarkedIndex();
do {
markedIndex = modulus(Math.max(markedIndex + step, -1), options.length);
} while (!this._isSelectableOption(options[markedIndex]));
var markedOption = options[markedIndex];
onOptionsNavigate == null || onOptionsNavigate(markedOption);
this._markOptionAtIndex(markedIndex);
}
_scrollToOption() {
var _this$optionsRef$curr3;
var {
scrollToOption
} = this.props;
var options = this.props.options || [];
var optionIndex = options.findIndex(option => option.id === scrollToOption);
var optionNode = (_this$optionsRef$curr3 = this.optionsRef.current) == null ? void 0 : _this$optionsRef$curr3.childNodes[optionIndex];
if (!optionNode || !this.optionsRef.current) {
return;
}
this.optionsRef.current.scrollTop = Math.max(optionNode.offsetTop - optionNode.offsetHeight, 0);
}
_renderNode(node) {
return node ? /*#__PURE__*/_react.default.createElement("div", {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 735,
columnNumber: 19
}
}, node) : null;
}
_convertCustomOptionToBuilder(_ref7) {
var {
option
} = _ref7;
var {
value: _value,
id,
disabled,
disabledDescription,
overrideOptionStyle,
overrideStyle
} = option;
if (overrideStyle) {
return {
id,
disabled,
disabledDescription,
overrideStyle,
value: () => /*#__PURE__*/_react.default.createElement("div", {
className: _DropdownLayoutSt.classes.dropdownOption,
"data-hook": _DataAttr.DATA_HOOKS.OPTION,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 802,
columnNumber: 11
}
}, _value)
};
}
if (overrideOptionStyle) {
return {
id,
disabled,
disabledDescription,
overrideOptionStyle,
value: () => /*#__PURE__*/_react.default.createElement("div", {
className: _DropdownLayoutSt.classes.dropdownOption,
"data-hook": _DataAttr.DATA_HOOKS.OPTION,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 816,
columnNumber: 11
}
}, _value)
};
} else {
// This should never happen but let's make TS happy
return undefined;
}
}
_convertOptionToListItemSelectBuilder(_ref8) {
var {
option
} = _ref8;
var {
value,
id,
disabled,
disabledDescription
} = option;
var {
selectedId
} = this.state;
var {
itemHeight,
selectedHighlight,
size
} = this.props;
return (0, _ListItemSelect.listItemSelectBuilder)({
id,
title: /*#__PURE__*/_react.default.createElement("div", {
"data-hook": _DataAttr.DATA_HOOKS.OPTION,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 838,
columnNumber: 9
}
}, value),
disabled,
disabledDescription,
size,
selected: id === selectedId && selectedHighlight,
className: (0, _DropdownLayoutSt.st)(_DropdownLayoutSt.classes.selectableOption, {
itemHeight
})
});
}
_isBuilderOption(option) {
var {
value
} = option;
return typeof value === 'function';
}
_isCustomOption(option) {
var {
overrideOptionStyle,
overrideStyle
} = option;
return overrideOptionStyle || overrideStyle;
}
_isActionOption(option) {
return 'value' in option && option.value === _DropdownLayout2.ListType.action;
}
_isItemSection(option) {
// only DropdownLayoutValueOption type has title prop, which is deprecated
// therefore expecting error instead of dealing with it
// @ts-expect-error
var {
title: isTitle
} = option;
return this._isDivider(option) || isTitle;
}
_isDivider(opt) {
return opt.value === _DropdownLayout2.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(_ref9) {
var {
option,
idx
} = _ref9;
var isValueOption = opt => 'id' in opt && 'value' in opt && typeof opt.value !== 'function';
var builderOption = this._convertOptionToBuilder(option, idx);
var hasLink = isValueOption(option) && !!option.linkTo;
var content = this._renderOptionContent({
option: builderOption,
idx,
hasLink
});
var isActionItem = this.props.listType === _DropdownLayout2.ListType.action;
return hasLink ? /*#__PURE__*/_react.default.createElement("a", {
className: _DropdownLayoutSt.classes.linkItem,
key: idx,
"data-hook": _DataAttr.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,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 920,
columnNumber: 7
}
}, content) : content;
}
_renderOptionContent(_ref0) {
var {
option,
idx,
hasLink
} = _ref0;
if (!option) {
return undefined;
}
var {
itemHeight,
selectedHighlight,
listType
} = this.props;
var {
selectedId,
hovered
} = this.state;
var {
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;
var optionState = {
selected: id === selectedId,
hovered: idx === hovered,
disabled,
size: this.props.size
};
var isDisabledWithTooltip = disabled && !!disabledDescription;
if (!disabled || isDisabledWithTooltip) {
this.focusableItemsIdsList = [...this.focusableItemsIdsList, id];
}
var isActionItem = listType === _DropdownLayout2.ListType.action;
return /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({
"aria-selected": hasLink || isActionItem || disabled ? undefined : optionState.selected,
"aria-hidden": disabled
}, this._getItemDataAttr(_objectSpread({}, optionState)), {
role: hasLink || isActionItem ? undefined : 'option',
className: overrideOptionStyle ? null : (0, _DropdownLayoutSt.st)(_DropdownLayoutSt.classes.option, _objectSpread(_objectSpread({}, 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-".concat(id),
onKeyDown: e => this._onActionListKeyDown(e, id),
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 1007,
columnNumber: 7
}
}), option == null ? void 0 : option.value(optionState));
}
_markOptionByProperty(props) {
var options = props.options || [];
if (this.state.hovered === _DropdownLayout2.NOT_HOVERED_INDEX && props.markedOption) {
var selectableOptions = options.filter(this._isSelectableOption);
if (selectableOptions.length) {
var idToMark = props.markedOption === true ? selectableOptions[0].id : props.markedOption;
var 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();
var options = this.props.options || [];
return options.map((option, idx) => this._renderOption({
option,
idx
}));
}
render() {
var {
className,
visible,
dropDirectionUp,
tabIndex,
onMouseEnter,
onMouseDown,
fixedHeader,
withArrow,
fixedFooter,
inContainer,
overflow,
maxHeightPixels = 260,
minWidthPixels,
infiniteScroll,
dataHook,
listType,
overlayScrollbarProps,
listboxId
} = this.props;
var renderedOptions = this._renderOptions();
var OverlayScrollbarHostElement = overlayScrollbarProps == null ? void 0 : overlayScrollbarProps.OverlayScrollbarHostElement;
var OverlayScrollbarContentElement = overlayScrollbarProps == null ? void 0 : overlayScrollbarProps.OverlayScrollbarContentElement;
var optionsContainerProps = {
style: {
maxHeight: getUnit(parseInt(String(maxHeightPixels), 10) - 35),
overflow
},
'data-hook': _DataAttr.DATA_HOOKS.DROPDOWN_LAYOUT_OPTIONS,
role: listType === _DropdownLayout2.ListType.select ? 'listbox' : 'menu',
id: listboxId
};
var options = infiniteScroll ? this._wrapWithInfiniteScroll(renderedOptions) : renderedOptions;
var optionsContainer = this.props.scrollbar === _DropdownLayout2.Scrollbar.OVERLAY && OverlayScrollbarHostElement && OverlayScrollbarContentElement ? /*#__PURE__*/_react.default.createElement(OverlayScrollbarHostElement, (0, _extends2.default)({
className: _DropdownLayoutSt.classes.options
}, optionsContainerProps, {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 1139,
columnNumber: 9
}
}), /*#__PURE__*/_react.default.createElement(OverlayScrollbarContentElement, {
className: _DropdownLayoutSt.classes.optionsContent,
ref: (0, _forkRef.forkRef)(this.optionsRef, this.setScrollElement),
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 1143,
columnNumber: 11
}
}, options)) : /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({
className: (0, _DropdownLayoutSt.st)(_DropdownLayoutSt.classes.options, _DropdownLayoutSt.classes.optionsContent),
ref: (0, _forkRef.forkRef)(this.optionsRef, this.setScrollElement)
}, optionsContainerProps, {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 1154,
columnNumber: 9
}
}), options);
return /*#__PURE__*/_react.default.createElement("div", {
"data-list-type": listType,
"data-hook": dataHook,
className: (0, _DropdownLayoutSt.st)(_DropdownLayoutSt.classes.root, {
visible,
withArrow,
direction: dropDirectionUp ? _DataAttr.DROPDOWN_LAYOUT_DIRECTIONS.UP : _DataAttr.DROPDOWN_LAYOUT_DIRECTIONS.DOWN,
containerStyles: !inContainer
}, className),
tabIndex: tabIndex,
onKeyDown: this._onSelectListKeyDown,
onMouseEnter: onMouseEnter,
onMouseLeave: this._onContainerMouseLeave,
onMouseDown: onMouseDown,
ref: this.containerRef,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 1164,
columnNumber: 7
}
}, /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({}, this._getDataAttributes(), {
className: _DropdownLayoutSt.classes.contentContainer,
style: {
overflow,
maxHeight: maxHeightPixels ? getUnit(maxHeightPixels) : undefined,
minWidth: minWidthPixels ? getUnit(minWidthPixels) : undefined
},
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 1186,
columnNumber: 9
}
}), this._renderNode(fixedHeader), optionsContainer, this._renderNode(fixedFooter)), this._renderTopArrow());
}
}
exports.DropdownLayout = DropdownLayout;
DropdownLayout.propTypes = {
className: _propTypes.default.any,
dropDirectionUp: _propTypes.default.any,
focusOnSelectedOption: _propTypes.default.any,
onClose: _propTypes.default.any,
onSelect: _propTypes.default.any,
onOptionMarked: _propTypes.default.any,
onOptionsNavigate: _propTypes.default.any,
overflow: _propTypes.default.any,
visible: _propTypes.default.any,
options: _propTypes.default.any,
selectedId: _propTypes.default.any,
tabIndex: _propTypes.default.any,
onClickOutside: _propTypes.default.any,
fixedHeader: _propTypes.default.any,
fixedFooter: _propTypes.default.any,
maxHeightPixels: _propTypes.default.any,
minWidthPixels: _propTypes.default.any,
withArrow: _propTypes.default.any,
closeOnSelect: _propTypes.default.any,
onMouseEnter: _propTypes.default.any,
onMouseLeave: _propTypes.default.any,
itemHeight: _propTypes.default.any,
selectedHighlight: _propTypes.default.any,
inContainer: _propTypes.default.any,
infiniteScroll: _propTypes.default.any,
loadMore: _propTypes.default.any,
hasMore: _propTypes.default.any,
markedOption: _propTypes.default.any,
focusOnOption: _propTypes.default.any,
scrollToOption: _propTypes.default.any,
listType: _propTypes.default.any,
autoFocus: _propTypes.default.any,
scrollbar: _propTypes.default.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: _DropdownLayout2.ListType.select,
scrollbar: 'fixed'
};
DropdownLayout.displayName = 'DropdownLayout';
DropdownLayout.NONE_SELECTED_ID = _DropdownLayout2.NOT_HOVERED_INDEX;
var _default = exports.default = (0, _withOverlayScrollbar.withOverlayScrollbar)(DropdownLayout);
//# sourceMappingURL=DropdownLayout.js.map