UNPKG

wix-style-react

Version:
496 lines (494 loc) • 18.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _react = _interopRequireDefault(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _DropdownBaseSt = require("./DropdownBase.st.css"); var _Popover = _interopRequireWildcard(require("../Popover")); var _DropdownLayout = _interopRequireDefault(require("../DropdownLayout")); var _excluded = ["onShow", "onHide"]; var _jsxFileName = "/home/builduser/work/a9c1ac8876d5057c/packages/wix-style-react/dist/cjs/DropdownBase/DropdownBase.js"; 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); } class DropdownBase extends _react.default.PureComponent { constructor(_props) { var _ref, _this$props$selectedI, _this; super(_props); _this = this; this._dropdownLayoutRef = null; this._shouldCloseOnMouseLeave = false; this.state = { open: this.props.open, selectedId: (_ref = (_this$props$selectedI = this.props.selectedId) !== null && _this$props$selectedI !== void 0 ? _this$props$selectedI : this.props.initialSelectedId) !== null && _ref !== void 0 ? _ref : -1, listAutoFocus: false }; /** * Return `true` if the `open` prop is being controlled */ this._isControllingOpen = function () { var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.props; return typeof props.open !== 'undefined'; }; /** * Return `true` if the selection behaviour is being controlled */ this._isControllingSelection = function () { var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.props; return typeof props.selectedId !== 'undefined' && typeof props.onSelect !== 'undefined'; }; this._open = () => { if (this.state.open) { this._dropdownLayoutRef && this._dropdownLayoutRef._focusFirstOption(); return; } if (!this._isControllingOpen()) { this.setState({ open: true }); this.props.onShow(); } }; this._close = e => { if (this._isControllingOpen()) { return; } // If called within a `mouseleave` event on the target element, we would like to close the // popover only on the popover's `mouseleave` event if (e && e.type === 'mouseleave') { // We're not using `setState` since we don't want to wait for the next render this._shouldCloseOnMouseLeave = true; } else { this.setState({ open: false, listAutoFocus: false }); } this.props.onHide(); }; this._toggle = () => { !this._isControllingOpen() && this.setState(_ref2 => { var { open } = _ref2; if (open) { this.props.onHide(); } else { this.props.onShow(); } return { open: !open, listAutoFocus: false }; }); }; this._handleClickOutside = () => { var { onClickOutside } = this.props; this._close(); onClickOutside && onClickOutside(); }; this._handlePopoverMouseEnter = () => { var { onMouseEnter } = this.props; onMouseEnter && onMouseEnter(); }; this._handlePopoverMouseLeave = () => { var { onMouseLeave } = this.props; if (this._shouldCloseOnMouseLeave) { this._shouldCloseOnMouseLeave = false; this.setState({ open: false }); } onMouseLeave && onMouseLeave(); }; this._handleSelect = selectedOption => { var newState = {}; if (!this._isControllingOpen()) { newState.open = false; this.props.onHide(); } if (!this._isControllingSelection()) { newState.selectedId = selectedOption.id; } this.setState(newState, () => { var { onSelect } = this.props; onSelect && onSelect(selectedOption); }); }; this._handleClose = () => { if (this.state.open) { this._close(); } if (this.triggerElementRef && this.triggerElementRef.current && this.triggerElementRef.current.focus) { this.triggerElementRef.current.focus(); } }; this._getSelectedOption = selectedId => { return this.props.options.find(_ref3 => { var { id } = _ref3; return id === selectedId; }); }; /** * Determine if a certain key should open the DropdownLayout */ this._isOpenKey = key => { return ['Enter', ' ', 'ArrowDown'].includes(key); }; this._isClosingKey = key => { return ['Tab', 'Escape'].includes(key); }; /** * A common `keydown` event that can be used for the target elements. It will automatically * delegate the event to the underlying <DropdownLayout/>, and will determine when to open the * dropdown depending on the pressed key. */ this._handleKeyDown = e => { if (this._isControllingOpen()) { return; } var isHandledByDropdownLayout = this._delegateKeyDown(e); if (!isHandledByDropdownLayout) { if (this._isOpenKey(e.key)) { this.setState({ listAutoFocus: true }); this._open(); e.preventDefault(); } else if (this._isClosingKey(e.key)) { this._close(); } } // prevent toggle button onClick when pressing enter and dropdown is open else if (this.state.open && e.key === 'Enter') { e.preventDefault(); } }; /* * Delegate the event to the DropdownLayout. It'll handle the navigation, option selection and * closing of the dropdown. */ this._delegateKeyDown = e => { if (!this._dropdownLayoutRef) { return false; } return this._dropdownLayoutRef._onSelectListKeyDown(e); }; this.triggerElementRef = /*#__PURE__*/_react.default.createRef(); } UNSAFE_componentWillReceiveProps(nextProps) { // Keep internal state updated if needed if (this._isControllingOpen(nextProps) && this.props.open !== nextProps.open) { this.setState({ open: nextProps.open }); } if (this._isControllingSelection(nextProps) && this.props.selectedId !== nextProps.selectedId) { this.setState({ selectedId: nextProps.selectedId }); } } _renderChildren() { var { children } = this.props; var { selectedId, open } = this.state; if (!children) { return null; } return /*#__PURE__*/_react.default.isValidElement(children) ? children // Returning the children as is when using in controlled mode : children({ open: this._open, close: this._close, toggle: this._toggle, isOpen: Boolean(open), ref: this.triggerElementRef, delegateKeyDown: this._delegateKeyDown, selectedOption: this._getSelectedOption(selectedId) }); } render() { var { dataHook, placement, appendTo, showArrow, zIndex, moveBy, options, minWidth, maxWidth, fixed, flip, tabIndex, overflow, dynamicWidth, maxHeight, fluid, animate, className, focusOnSelectedOption, infiniteScroll, loadMore, hasMore, focusOnOption, scrollToOption, markedOption, onMouseDown, listType, fixedHeader, fixedFooter, autoFocus } = this.props; var { open, selectedId } = this.state; var _this$props = this.props, { onShow, onHide } = _this$props, popoverProps = (0, _objectWithoutProperties2.default)(_this$props, _excluded); return /*#__PURE__*/_react.default.createElement(_Popover.default, (0, _extends2.default)({ "data-list-type": listType }, popoverProps, { // backward compatible for migration stylable 1 to stylable 3 animate: animate, dataHook: dataHook, shown: open, placement: placement, dynamicWidth: dynamicWidth, appendTo: appendTo, showArrow: showArrow, zIndex: zIndex, moveBy: moveBy, onKeyDown: this._handleKeyDown, onMouseEnter: this._handlePopoverMouseEnter, onMouseLeave: this._handlePopoverMouseLeave, onClickOutside: this._handleClickOutside, fixed: fixed, flip: flip, fluid: fluid, className: (0, _DropdownBaseSt.st)(_DropdownBaseSt.classes.root, { withWidth: Boolean(minWidth || maxWidth) }, className), __self: this, __source: { fileName: _jsxFileName, lineNumber: 445, columnNumber: 7 } }), /*#__PURE__*/_react.default.createElement(_Popover.default.Element, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 472, columnNumber: 9 } }, this._renderChildren()), /*#__PURE__*/_react.default.createElement(_Popover.default.Content, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 474, columnNumber: 9 } }, /*#__PURE__*/_react.default.createElement("div", { style: { minWidth, maxWidth }, __self: this, __source: { fileName: _jsxFileName, lineNumber: 475, columnNumber: 11 } }, /*#__PURE__*/_react.default.createElement(_DropdownLayout.default, { dataHook: "dropdown-base-dropdownlayout", className: _DropdownBaseSt.classes.list, ref: r => this._dropdownLayoutRef = r, selectedId: selectedId, options: options, maxHeightPixels: maxHeight, onSelect: this._handleSelect, onClose: this._handleClose, tabIndex: tabIndex, inContainer: true, visible: true, overflow: overflow, focusOnSelectedOption: focusOnSelectedOption, infiniteScroll: infiniteScroll, loadMore: loadMore, hasMore: hasMore, focusOnOption: focusOnOption, markedOption: markedOption, scrollToOption: scrollToOption, onMouseDown: onMouseDown, listType: listType, autoFocus: autoFocus || this.state.listAutoFocus, fixedHeader: fixedHeader, fixedFooter: fixedFooter, __self: this, __source: { fileName: _jsxFileName, lineNumber: 481, columnNumber: 13 } })))); } } DropdownBase.displayName = 'DropdownBase'; DropdownBase.propTypes = { /** Applies a data-hook HTML attribute that can be used in the tests */ dataHook: _propTypes.default.string, /** Specifies a CSS class name to be appended to the component’s root element */ className: _propTypes.default.string, /** Control whether the <Popover/> should be opened */ open: _propTypes.default.bool, /** Control popover placement */ placement: _propTypes.default.oneOf(_Popover.placements), /** Specifies where popover should be inserted as a last child - whether `parent` or `window` containers */ appendTo: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.node]), /** Specifies whether popover arrow should be shown */ showArrow: _propTypes.default.bool, /** Defines a callback function which is called when user clicks outside of a dropdown */ onClickOutside: _propTypes.default.func, /** Defines a callback function which is called on `onMouseEnter` event on the entire component */ onMouseEnter: _propTypes.default.func, /** Defines a callback function which is called on `onMouseLeave` event on the entire component */ onMouseLeave: _propTypes.default.func, /** Defines a callback function which is called when dropdown is opened */ onShow: _propTypes.default.func, /** Defines a callback function which is called when dropdown is closed */ onHide: _propTypes.default.func, /** Defines a callback function which is called whenever user selects a different option in the list */ onSelect: _propTypes.default.func, /** * Set popover's content width to a minimum width of a trigger element, * but it can expand up to the defined value of `maxWidth` */ dynamicWidth: _propTypes.default.bool, /** Controls the minimum width of dropdown layout */ minWidth: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]), /** Controls the maximum width of dropdown layout */ maxWidth: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]), /** Controls the maximum height of dropdown layout */ maxHeight: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]), /** * Specifies a target component to be rendered. If a regular node is passed, it'll be rendered as-is. * If a function is passed, it's expected to return a React element. * The function accepts an object containing the following properties: * * * `open` - will open the Popover * * `close` - will close the Popover * * `toggle` - will toggle the Popover * * `isOpen` - indicates whether the items list is currently open * * `delegateKeyDown` - the underlying DropdownLayout's keydown handler. It can be called * inside another keyDown event in order to delegate it. * * `selectedOption` - the currently selected option * * Check inserted component documentation for more information on available properties. */ children: _propTypes.default.oneOfType([_propTypes.default.node, _propTypes.default.func]), /** * Specifies an array of options for a dropdown list. Objects must have an id and can include string value or node. * If value is '-', a divider will be rendered instead (dividers do not require and id). */ options: _propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.shape({ id: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]).isRequired, value: _propTypes.default.oneOfType([_propTypes.default.node, _propTypes.default.string, _propTypes.default.func]).isRequired, disabled: _propTypes.default.bool, overrideStyle: _propTypes.default.bool }), // A divider option without an id _propTypes.default.shape({ value: _propTypes.default.oneOf(['-']) })])), /** Sets the initial marking of an option in the list when opened: * - `false` - no initially hovered list item * - `true` - hover first selectable option * - any `number/string` specify the id of an option to be hovered */ markedOption: _propTypes.default.oneOfType([_propTypes.default.bool, _propTypes.default.string, _propTypes.default.number]), /** Define the selected option in the list */ selectedId: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]), /** Handles container overflow behaviour */ overflow: _propTypes.default.string, /** Indicates that element can be focused and where it participates in sequential keyboard navigation */ tabIndex: _propTypes.default.number, /** * Sets the initially selected option in the list. Used when selection * behaviour is being controlled. */ initialSelectedId: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]), /** Specifies the stack order (`z-index`) of a dropdown layout */ zIndex: _propTypes.default.number, /** Moves dropdown content relative to the parent on X or Y axis by a defined amount of pixels */ moveBy: _propTypes.default.shape({ x: _propTypes.default.number, y: _propTypes.default.number }), /** * Specifies whether to flip the <Popover/> placement * when it starts to overlap the target element (<Popover.Element/>) */ flip: _propTypes.default.bool, /** * Specifies whether to enable the fixed behaviour. If enabled, <Popover/> keep its * original placement even when it's being positioned outside the boundary. */ fixed: _propTypes.default.bool, /** Stretches trigger element to fill its parent container width */ fluid: _propTypes.default.bool, /** Adds enter and exit animation */ animate: _propTypes.default.bool, /** Focus to the selected option when dropdown is opened */ focusOnSelectedOption: _propTypes.default.bool, /** Specifies whether lazy loading of the dropdown items is enabled */ infiniteScroll: _propTypes.default.bool, /** Defines a callback function which is called on a request to render more list items */ loadMore: _propTypes.default.func, /** Specifies whether there are more items to load */ hasMore: _propTypes.default.bool, /** Focus to the specified option when dropdown is opened */ focusOnOption: _propTypes.default.number, /** Scrolls to the specified option when dropdown is opened without marking it */ scrollToOption: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]), /** Defines type of behavior applied in list */ listType: _propTypes.default.oneOf(['action', 'select']), /** A fixed header to the list */ fixedHeader: _propTypes.default.node, /** A fixed footer to the list */ fixedFooter: _propTypes.default.node, /** Specifies whether first list item should be focused */ autoFocus: _propTypes.default.bool }; DropdownBase.defaultProps = { placement: 'bottom', appendTo: 'parent', showArrow: false, maxHeight: '260px', fluid: false, animate: false, listType: 'select', onShow: () => {}, onHide: () => {} }; var _default = exports.default = DropdownBase; //# sourceMappingURL=DropdownBase.js.map