wix-style-react
Version:
910 lines (784 loc) • 32.8 kB
JavaScript
import _typeof from "@babel/runtime/helpers/typeof";
import _extends from "@babel/runtime/helpers/extends";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
import _inherits from "@babel/runtime/helpers/inherits";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
import PropTypes from 'prop-types';
import React 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';
import deprecationLog from '../utils/deprecationLog';
import { filterObject } from '../utils/filterObject';
import ReactDOM from 'react-dom';
import { listItemSectionBuilder } from '../ListItemSection';
import { listItemSelectBuilder } from '../ListItemSelect';
import { isString } from '../utils/StringUtils';
var MOUSE_EVENTS_SUPPORTED = ['mouseup', 'touchend'];
var modulu = function modulu(n, m) {
var remain = n % m;
return remain >= 0 ? remain : remain + m;
};
var getUnit = function getUnit(value) {
return isString(value) ? value : "".concat(value, "px");
};
var NOT_HOVERED_INDEX = -1;
export var DIVIDER_OPTION_VALUE = '-';
var deprecatedPropsLogs = function deprecatedPropsLogs(props) {
var deprecatedProps = [{
propName: 'onClickOutside',
deprecationMsg: '<DropdownLayout/> - onClickOutside prop is deprecated and will be removed soon, please use dropdown base instead.'
}, {
propName: 'itemHeight',
deprecationMsg: '<DropdownLayout/> - itemHeight prop is deprecated and will be removed in the next major release. In order to set a different height than 36px, please use a builder.'
}, {
propName: 'withArrow',
deprecationMsg: '<DropdownLayout/>- withArrow prop is deprecated and will be removed in the next major release, please use DropdownBase (with the prop "showArrow") or Popover component instead.'
}, {
propName: 'dropDirectionUp',
deprecationMsg: '<DropdownLayout/>- dropDirectionUp prop is deprecated and will be removed in the next major release, please use DropdownBase (with the prop "showArrow") or Popover component instead.'
}];
deprecatedProps.forEach(function (_ref) {
var propName = _ref.propName,
deprecationMsg = _ref.deprecationMsg;
if (props.hasOwnProperty(propName)) {
deprecationLog(deprecationMsg);
}
});
};
var DropdownLayout = /*#__PURE__*/function (_React$PureComponent) {
_inherits(DropdownLayout, _React$PureComponent);
var _super = _createSuper(DropdownLayout);
function DropdownLayout(props) {
var _this;
_classCallCheck(this, DropdownLayout);
_this = _super.call(this, props);
_defineProperty(_assertThisInitialized(_this), "_onMouseEventsHandler", function (e) {
if (!_this._checkIfEventOnElements(e, [ReactDOM.findDOMNode(_assertThisInitialized(_this))])) {
_this._onClickOutside(e);
}
});
_defineProperty(_assertThisInitialized(_this), "_onClickOutside", function (event) {
var _this$props = _this.props,
visible = _this$props.visible,
onClickOutside = _this$props.onClickOutside;
if (visible && onClickOutside) {
onClickOutside(event);
}
});
_defineProperty(_assertThisInitialized(_this), "_onSelect", function (index, e) {
var _this$props2 = _this.props,
options = _this$props2.options,
onSelect = _this$props2.onSelect;
var chosenOption = options[index];
if (chosenOption) {
var sameOptionWasPicked = chosenOption.id === _this.state.selectedId;
if (onSelect) {
e.stopPropagation();
onSelect(chosenOption, sameOptionWasPicked);
}
}
if (!_this._isControlled()) {
_this.setState({
selectedId: chosenOption && chosenOption.id
});
}
return !!onSelect && chosenOption;
});
_defineProperty(_assertThisInitialized(_this), "_onMouseEnter", function (index) {
if (_this._isSelectableOption(_this.props.options[index])) {
_this._markOption(index);
}
});
_defineProperty(_assertThisInitialized(_this), "_onMouseLeave", function () {
return _this._markOption(NOT_HOVERED_INDEX);
});
_defineProperty(_assertThisInitialized(_this), "_focusOnOption", function () {
var _this$props3 = _this.props,
focusOnOption = _this$props3.focusOnOption,
options = _this$props3.options;
var markedIndex = options.findIndex(function (option) {
return option.id === focusOnOption;
});
if (markedIndex !== -1) {
_this._markOptionAtIndex(markedIndex);
} else {
// Remove focus
_this._markOption(markedIndex);
}
});
_defineProperty(_assertThisInitialized(_this), "_markOptionAtIndex", function (markedIndex) {
var infiniteScroll = _this.props.infiniteScroll;
_this._markOption(markedIndex);
var menuElement = _this.options;
var hoveredElement = infiniteScroll ? _this.options.childNodes[0].childNodes[markedIndex] : _this.options.childNodes[markedIndex];
scrollIntoView(menuElement, hoveredElement);
});
_defineProperty(_assertThisInitialized(_this), "_onKeyDown", function (event) {
if (!_this.props.visible || _this.props.isComposing) {
return false;
}
switch (event.key) {
case 'ArrowDown':
{
_this._markNextStep(1);
event.preventDefault();
break;
}
case 'ArrowUp':
{
_this._markNextStep(-1);
event.preventDefault();
break;
}
case ' ':
case 'Spacebar':
case 'Enter':
{
if (!_this._onSelect(_this.state.hovered, event)) {
return false;
}
break;
}
case '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;
}
}
break;
}
case 'Escape':
{
_this._onClose();
break;
}
default:
{
return false;
}
}
event.stopPropagation();
return true;
});
_defineProperty(_assertThisInitialized(_this), "_onClose", function () {
_this._markOption(NOT_HOVERED_INDEX);
if (_this.props.onClose) {
_this.props.onClose();
}
});
_defineProperty(_assertThisInitialized(_this), "_wrapWithInfiniteScroll", function (scrollableElement) {
return /*#__PURE__*/React.createElement(InfiniteScroll, {
useWindow: true,
dataHook: DATA_HOOKS.INFINITE_SCROLL_CONTAINER,
scrollElement: _this.options,
loadMore: _this.props.loadMore,
hasMore: _this.props.hasMore,
data: _this.props.options,
loader: /*#__PURE__*/React.createElement("div", {
className: classes.loader
}, /*#__PURE__*/React.createElement(Loader, {
dataHook: DROPDOWN_LAYOUT_LOADER,
size: "small"
}))
}, scrollableElement);
});
_defineProperty(_assertThisInitialized(_this), "_getDataAttributes", function () {
var _filterObject;
var _this$props4 = _this.props,
visible = _this$props4.visible,
dropDirectionUp = _this$props4.dropDirectionUp;
var selectedId = _this.state.selectedId;
return filterObject((_filterObject = {
'data-hook': DATA_HOOKS.CONTENT_CONTAINER
}, _defineProperty(_filterObject, DATA_SHOWN, visible), _defineProperty(_filterObject, DATA_SELECTED_OPTION_ID, selectedId === 0 ? "".concat(selectedId) : selectedId), _defineProperty(_filterObject, DATA_DIRECTION, dropDirectionUp ? DROPDOWN_LAYOUT_DIRECTIONS.UP : DROPDOWN_LAYOUT_DIRECTIONS.DOWN), _filterObject), function (key, value) {
return !!value;
});
});
_defineProperty(_assertThisInitialized(_this), "_getItemDataAttr", function (_ref2) {
var _filterObject2;
var hovered = _ref2.hovered,
selected = _ref2.selected,
disabled = _ref2.disabled;
var _this$props5 = _this.props,
itemHeight = _this$props5.itemHeight,
selectedHighlight = _this$props5.selectedHighlight;
return filterObject((_filterObject2 = {}, _defineProperty(_filterObject2, DATA_OPTION.DISABLED, disabled), _defineProperty(_filterObject2, DATA_OPTION.SELECTED, selected && selectedHighlight), _defineProperty(_filterObject2, DATA_OPTION.HOVERED, hovered), _defineProperty(_filterObject2, DATA_OPTION.SIZE, itemHeight), _filterObject2), function (key, value) {
return !!value;
});
});
_this.containerRef = /*#__PURE__*/React.createRef();
_this.state = {
hovered: NOT_HOVERED_INDEX,
selectedId: props.selectedId
};
deprecatedPropsLogs(props);
return _this;
}
_createClass(DropdownLayout, [{
key: "componentDidMount",
value: function componentDidMount() {
var _this2 = this;
var focusOnSelectedOption = this.props.focusOnSelectedOption;
if (focusOnSelectedOption) {
this._focusOnSelectedOption();
} else if (this.props.hasOwnProperty('focusOnOption')) {
this._focusOnOption();
}
this._markOptionByProperty(this.props); // Deprecated
MOUSE_EVENTS_SUPPORTED.forEach(function (eventName) {
document.addEventListener(eventName, _this2._onMouseEventsHandler, true);
});
this._boundEvents = MOUSE_EVENTS_SUPPORTED;
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
var _this3 = this;
if (this._boundEvents && typeof document !== 'undefined') {
this._boundEvents.forEach(function (eventName) {
document.removeEventListener(eventName, _this3._onMouseEventsHandler, true);
});
}
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
var focusOnOption = this.props.focusOnOption;
if (prevProps.focusOnOption !== focusOnOption) {
this._focusOnOption();
}
}
}, {
key: "UNSAFE_componentWillReceiveProps",
value: function UNSAFE_componentWillReceiveProps(nextProps) {
var _this4 = this;
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 && (!nextProps.options[this.state.hovered] || this.props.options[this.state.hovered].id !== nextProps.options[this.state.hovered].id)) {
this._markOption(this._findIndex(nextProps.options, function (item) {
return item.id === _this4.props.options[_this4.state.hovered].id;
}));
}
this._markOptionByProperty(nextProps);
} // Deprecated
}, {
key: "_checkIfEventOnElements",
value: function _checkIfEventOnElements(e, elem) {
var current = e.target;
while (current.parentNode) {
if (elem.indexOf(current) > -1) {
return true;
}
current = current.parentNode;
}
return current !== document;
} // Deprecated
}, {
key: "_renderTopArrow",
value: // Deprecated
function _renderTopArrow() {
var _this$props6 = this.props,
withArrow = _this$props6.withArrow,
visible = _this$props6.visible;
return withArrow && visible ? /*#__PURE__*/React.createElement("div", {
"data-hook": DATA_HOOKS.TOP_ARROW,
className: classes.arrow
}) : null;
}
}, {
key: "_convertOptionToListItemSectionBuilder",
value: function _convertOptionToListItemSectionBuilder(_ref3) {
var option = _ref3.option,
idx = _ref3.idx;
var value = option.value,
id = option.id,
isTitle = option.title;
if (value === DIVIDER_OPTION_VALUE) {
deprecationLog('to render a divider, please use `listItemSectionBuilder`');
return listItemSectionBuilder({
dataHook: OPTION_DATA_HOOKS.DIVIDER,
id: id || idx,
type: 'divider'
});
}
if (isTitle) {
deprecationLog('to render a title, please use `listItemSectionBuilder`');
return listItemSectionBuilder({
dataHook: OPTION_DATA_HOOKS.TITLE,
id: id,
type: 'subheader',
title: value
});
}
}
}, {
key: "_isControlled",
value: function _isControlled() {
return typeof this.props.selectedId !== 'undefined' && typeof this.props.onSelect !== 'undefined';
}
}, {
key: "_focusOnSelectedOption",
value: function _focusOnSelectedOption() {
if (this.selectedOption) {
this.options.scrollTop = Math.max(this.selectedOption.offsetTop - this.selectedOption.offsetHeight, 0);
}
}
}, {
key: "_setSelectedOptionNode",
value: function _setSelectedOptionNode(optionNode, option) {
if (option.id === this.state.selectedId) {
this.selectedOption = optionNode;
}
}
}, {
key: "_markOption",
value: function _markOption(index, options) {
var onOptionMarked = this.props.onOptionMarked;
options = options || this.props.options;
this.setState({
hovered: index
});
onOptionMarked && onOptionMarked(options[index] || null);
}
}, {
key: "_getMarkedIndex",
value: function _getMarkedIndex() {
var _this5 = this;
var options = this.props.options;
var useHoverIndex = this.state.hovered > NOT_HOVERED_INDEX;
var useSelectedIdIndex = typeof this.state.selectedId !== 'undefined';
var markedIndex;
if (useHoverIndex) {
markedIndex = this.state.hovered;
} else if (useSelectedIdIndex) {
markedIndex = options.findIndex(function (option) {
return option.id === _this5.state.selectedId;
});
} else {
markedIndex = NOT_HOVERED_INDEX;
}
return markedIndex;
}
}, {
key: "_markNextStep",
value: function _markNextStep(step) {
var options = this.props.options;
if (!options.some(this._isSelectableOption)) {
return;
}
var markedIndex = this._getMarkedIndex();
do {
markedIndex = Math.abs(modulu(Math.max(markedIndex + step, -1), options.length));
} while (!this._isSelectableOption(options[markedIndex]));
this._markOptionAtIndex(markedIndex);
}
}, {
key: "_renderNode",
value: function _renderNode(node) {
return node ? /*#__PURE__*/React.createElement("div", null, node) : null;
}
}, {
key: "_convertCustomOptionToBuilder",
value: function _convertCustomOptionToBuilder(_ref4) {
var option = _ref4.option;
var _value = option.value,
id = option.id,
disabled = option.disabled,
overrideOptionStyle = option.overrideOptionStyle,
overrideStyle = option.overrideStyle;
if (overrideStyle) {
deprecationLog('this prop is deprecated. Please use overrideOptionStyle to override all option styles');
return {
id: id,
disabled: disabled,
overrideStyle: overrideStyle,
value: function value(props) {
return /*#__PURE__*/React.createElement("div", {
"data-hook": DATA_HOOKS.OPTION
}, _value);
}
};
}
if (overrideOptionStyle) {
return {
id: id,
disabled: disabled,
overrideOptionStyle: overrideOptionStyle,
value: function value(props) {
return /*#__PURE__*/React.createElement("div", {
"data-hook": DATA_HOOKS.OPTION
}, _value);
}
};
}
}
}, {
key: "_convertOptionToListItemSelectBuilder",
value: function _convertOptionToListItemSelectBuilder(_ref5) {
var option = _ref5.option;
var value = option.value,
id = option.id,
disabled = option.disabled;
var selectedId = this.state.selectedId;
var _this$props7 = this.props,
itemHeight = _this$props7.itemHeight,
selectedHighlight = _this$props7.selectedHighlight;
return listItemSelectBuilder({
id: id,
title: /*#__PURE__*/React.createElement("div", {
"data-hook": DATA_HOOKS.OPTION
}, value),
disabled: disabled,
selected: id === selectedId && selectedHighlight,
className: st(classes.selectableOption, {
itemHeight: itemHeight
})
});
}
}, {
key: "_isBuilderOption",
value: function _isBuilderOption(_ref6) {
var option = _ref6.option;
var value = option.value;
return typeof value === 'function';
}
}, {
key: "_isCustomOption",
value: function _isCustomOption(_ref7) {
var option = _ref7.option;
var overrideOptionStyle = option.overrideOptionStyle,
overrideStyle = option.overrideStyle;
return overrideOptionStyle || overrideStyle;
}
}, {
key: "_isItemSection",
value: function _isItemSection(_ref8) {
var option = _ref8.option;
var value = option.value,
isTitle = option.title;
return value === DIVIDER_OPTION_VALUE || isTitle;
}
}, {
key: "_convertOptionToBuilder",
value: function _convertOptionToBuilder(option, idx) {
if (this._isBuilderOption({
option: option
})) {
return option;
} else if (this._isItemSection({
option: option
})) {
return this._convertOptionToListItemSectionBuilder({
option: option,
idx: idx
});
} else if (this._isCustomOption({
option: option
})) {
return this._convertCustomOptionToBuilder({
option: option
});
} else {
return this._convertOptionToListItemSelectBuilder({
option: option
});
}
}
}, {
key: "_renderOption",
value: function _renderOption(_ref9) {
var option = _ref9.option,
idx = _ref9.idx;
var builderOption = this._convertOptionToBuilder(option, idx);
var content = this._renderOptionContent({
option: builderOption,
idx: idx,
hasLink: !!option.linkTo
});
return option.linkTo ? /*#__PURE__*/React.createElement("a", {
className: classes.linkItem,
key: idx,
"data-hook": DATA_HOOKS.LINK_ITEM,
href: option.linkTo,
role: "option",
"aria-selected": option.id === this.state.selectedId
}, content) : content;
} // For testing purposes only
}, {
key: "_renderOptionContent",
value: function _renderOptionContent(_ref10) {
var _this6 = this;
var option = _ref10.option,
idx = _ref10.idx,
hasLink = _ref10.hasLink;
var _this$props8 = this.props,
itemHeight = _this$props8.itemHeight,
selectedHighlight = _this$props8.selectedHighlight;
var _this$state = this.state,
selectedId = _this$state.selectedId,
hovered = _this$state.hovered;
var id = option.id,
disabled = option.disabled,
overrideStyle = option.overrideStyle,
overrideOptionStyle = option.overrideOptionStyle;
var optionState = {
selected: id === selectedId,
hovered: idx === hovered,
disabled: disabled
};
return /*#__PURE__*/React.createElement("div", _extends({}, this._getItemDataAttr(_objectSpread({}, optionState)), {
role: hasLink ? undefined : 'option',
"aria-selected": hasLink ? undefined : optionState.selected,
className: overrideOptionStyle ? null : st(classes.option, _objectSpread(_objectSpread({}, optionState), {}, {
selected: optionState.selected && selectedHighlight,
itemHeight: itemHeight,
overrideStyle: overrideStyle
})),
ref: function ref(node) {
return _this6._setSelectedOptionNode(node, option);
},
onClick: !disabled ? function (e) {
return _this6._onSelect(idx, e);
} : null,
key: idx,
onMouseEnter: function onMouseEnter() {
return _this6._onMouseEnter(idx);
},
onMouseLeave: this._onMouseLeave,
"data-hook": "dropdown-item-".concat(id)
}), option.value(optionState));
}
}, {
key: "_markOptionByProperty",
value: function _markOptionByProperty(props) {
if (this.state.hovered === NOT_HOVERED_INDEX && props.markedOption) {
var selectableOptions = props.options.filter(this._isSelectableOption);
if (selectableOptions.length) {
var idToMark = props.markedOption === true ? selectableOptions[0].id : props.markedOption;
this._markOption(this._findIndex(props.options, function (item) {
return item.id === idToMark;
}), props.options);
}
}
}
}, {
key: "_findIndex",
value: function _findIndex(arr, predicate) {
return (Array.isArray(arr) ? arr : []).findIndex(predicate);
}
}, {
key: "_isSelectableOption",
value: function _isSelectableOption(option) {
return option && option.value !== DIVIDER_OPTION_VALUE && !option.disabled && !option.title;
}
}, {
key: "render",
value: function render() {
var _this7 = this;
var _this$props9 = this.props,
className = _this$props9.className,
options = _this$props9.options,
visible = _this$props9.visible,
dropDirectionUp = _this$props9.dropDirectionUp,
tabIndex = _this$props9.tabIndex,
onMouseEnter = _this$props9.onMouseEnter,
onMouseLeave = _this$props9.onMouseLeave,
onMouseDown = _this$props9.onMouseDown,
fixedHeader = _this$props9.fixedHeader,
withArrow = _this$props9.withArrow,
fixedFooter = _this$props9.fixedFooter,
inContainer = _this$props9.inContainer,
overflow = _this$props9.overflow,
maxHeightPixels = _this$props9.maxHeightPixels,
minWidthPixels = _this$props9.minWidthPixels,
infiniteScroll = _this$props9.infiniteScroll,
dataHook = _this$props9.dataHook;
var renderedOptions = options.map(function (option, idx) {
return _this7._renderOption({
option: option,
idx: idx
});
});
return /*#__PURE__*/React.createElement("div", {
"data-hook": dataHook,
className: st(classes.root, {
visible: visible,
withArrow: withArrow,
direction: dropDirectionUp ? DROPDOWN_LAYOUT_DIRECTIONS.UP : DROPDOWN_LAYOUT_DIRECTIONS.DOWN,
containerStyles: !inContainer
}, className),
tabIndex: tabIndex,
onKeyDown: this._onKeyDown,
onMouseEnter: onMouseEnter,
onMouseLeave: onMouseLeave,
onMouseDown: onMouseDown,
ref: this.containerRef
}, /*#__PURE__*/React.createElement("div", _extends({}, this._getDataAttributes(), {
className: classes.contentContainer,
style: {
overflow: overflow,
maxHeight: getUnit(maxHeightPixels),
minWidth: getUnit(minWidthPixels)
}
}), this._renderNode(fixedHeader), /*#__PURE__*/React.createElement("div", {
className: classes.options,
style: {
maxHeight: getUnit(parseInt(maxHeightPixels, 10) - 35),
overflow: overflow
},
ref: function ref(_options) {
return _this7.options = _options;
},
"data-hook": DATA_HOOKS.DROPDOWN_LAYOUT_OPTIONS,
role: "listbox"
}, infiniteScroll ? this._wrapWithInfiniteScroll(renderedOptions) : renderedOptions), this._renderNode(fixedFooter)), this._renderTopArrow());
}
}]);
return DropdownLayout;
}(React.PureComponent);
var optionPropTypes = PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
value: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.func]).isRequired,
disabled: PropTypes.bool,
/** @deprecated*/
overrideStyle: PropTypes.bool,
/** @deprecated*/
title: PropTypes.bool,
overrideOptionStyle: PropTypes.bool,
/* the string displayed within the input when the option is selected */
label: PropTypes.string
});
export function optionValidator(props, propName, componentName) {
var option = props[propName]; // Notice: We don't use Proptypes.oneOf() to check for either option OR divider, because then the failure message would be less informative.
if (_typeof(option) === 'object' && option.value === DIVIDER_OPTION_VALUE) {
return;
}
var optionError = PropTypes.checkPropTypes({
option: optionPropTypes
}, {
option: option
}, 'option', componentName);
if (optionError) {
return optionError;
}
if (option.id && option.id.toString().trim().length === 0) {
return new Error('Warning: Failed option type: The option `option.id` should be non-empty after trimming in `DropdownLayout`.');
}
if (option.value && option.value.toString().trim().length === 0) {
return new Error('Warning: Failed option type: The option `option.value` should be non-empty after trimming in `DropdownLayout`.');
}
if (option.label && option.label.toString().trim().length === 0) {
return new Error('Warning: Failed option type: The option `option.label` should be non-empty after trimming in `DropdownLayout`.');
}
}
DropdownLayout.propTypes = {
/** A single CSS class name to be appended to the root element. */
className: PropTypes.string,
/** @deprecated */
dropDirectionUp: PropTypes.bool,
/** Scroll view to the selected option on opening the dropdown */
focusOnSelectedOption: PropTypes.bool,
/** Callback function called whenever the user press the `Escape` keyboard.*/
onClose: PropTypes.func,
/** Callback function called whenever the user selects a different option in the list */
onSelect: PropTypes.func,
/** Callback function called whenever an option becomes focused (hovered/active). Receives the relevant option object from the original props.options array. */
onOptionMarked: PropTypes.func,
/** Set overflow of container */
overflow: PropTypes.string,
/** Should show or hide the component */
visible: PropTypes.bool,
/** Array of objects:
* - id `<string / number>` *required*: the id of the option, should be unique.
* - value `<function / string / node>` *required*: can be a string, react element or a builder function.
* - disabled `<bool>` *default value- false*: whether this option is disabled or not
* - linkTo `<string>`: when provided the option will be an anchor to the given value
* - title `<bool>` *default value- false* **deprecated**: please use `listItemSectionBuilder` for rendering a title.
* - overrideStyle `<bool>` *default value- false* **deprecated**: please use `overrideOptionStyle` for override option styles.
* - overrideOptionStyle `<bool>` *default value- false* - when set to `true`, the option will be responsible to its own styles. No styles will be applied from the DropdownLayout itself.
* - label `<string>`: the string displayed within an input when the option is selected. This is used when using `<DropdownLayout/>` with an `<Input/>`.
*/
options: PropTypes.arrayOf(optionValidator),
/** The id of the selected option in the list */
selectedId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Specifies the tab order of the component. */
tabIndex: PropTypes.number,
/** @deprecated Do not use this prop. */
onClickOutside: PropTypes.func,
/** A fixed header to the list */
fixedHeader: PropTypes.node,
/** A fixed footer to the list */
fixedFooter: PropTypes.node,
/** Set the max height of the dropdownLayout in pixels */
maxHeightPixels: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Set the min width of the dropdownLayout in pixels */
minWidthPixels: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** @deprecated Do not use this prop. */
withArrow: PropTypes.bool,
/** Closes DropdownLayout on option selection */
closeOnSelect: PropTypes.bool,
/** Callback function called whenever the user entered with the mouse to the dropdown layout.*/
onMouseEnter: PropTypes.func,
/** Callback function called whenever the user exited with the mouse from the dropdown layout.*/
onMouseLeave: PropTypes.func,
/** @deprecated Do not use this prop. */
itemHeight: PropTypes.oneOf(['small', 'big']),
/** Whether the selected option will be highlighted when dropdown reopened. */
selectedHighlight: PropTypes.bool,
/** Whether the `<DropdownLayout/>` is in a container component. If `true`, some styles such as shadows, positioning and padding will be added the the component contentContainer. */
inContainer: PropTypes.bool,
/** Set this prop for lazy loading of the dropdown layout items.*/
infiniteScroll: PropTypes.bool,
/** A callback called when more items are requested to be rendered. */
loadMore: PropTypes.func,
/** Whether there are more items to be loaded. */
hasMore: PropTypes.bool,
/** Sets the default hover behavior when:
* 1. `false` means no default
* 2. `true` means to hover the first selectable option
* 3. Any number/string represents the id of option to hover
*/
markedOption: PropTypes.oneOfType([PropTypes.bool, PropTypes.string, PropTypes.number]),
/** Marks (not selects) and scrolls view to the option on opening the dropdown by option id */
focusOnOption: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};
DropdownLayout.defaultProps = {
options: [],
tabIndex: 0,
maxHeightPixels: 260,
closeOnSelect: true,
itemHeight: 'small',
selectedHighlight: true,
inContainer: false,
infiniteScroll: false,
loadMore: null,
hasMore: false,
markedOption: false,
overflow: 'auto'
};
DropdownLayout.displayName = 'DropdownLayout';
DropdownLayout.NONE_SELECTED_ID = NOT_HOVERED_INDEX;
export default DropdownLayout;