UNPKG

@wix/design-system

Version:

@wix/design-system

485 lines (484 loc) 17.7 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 _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireWildcard(require("react")); var _uniqueId = _interopRequireDefault(require("lodash/uniqueId")); var _DropdownBaseSt = require("./DropdownBase.st.css.js"); var _DropdownLayout = _interopRequireDefault(require("../DropdownLayout")); var _PopoverNext = _interopRequireDefault(require("../PopoverNext")); var _context = require("../WixDesignSystemProvider/context"); var _Drawer = _interopRequireDefault(require("../Drawer")); var _excluded = ["onShow", "onHide"]; var _jsxFileName = "/home/builduser/work/57e038ea7326c1ec/packages/wix-design-system/dist/cjs/DropdownBase/DropdownBase.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; } class DropdownBase extends _react.PureComponent { constructor(_props) { var _ref, _this$props$selectedI, _this; super(_props); _this = this; this.triggerElementRef = void 0; this.uniqueId = (0, _uniqueId.default)('DropdownBase'); 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, activeDescendantId: undefined, 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._setIsOpen = (isOpen, _, newState) => { if (!this._isControllingOpen()) { this.setState(_objectSpread(_objectSpread({}, newState), {}, { open: isOpen, listAutoFocus: false })); } }; this._handleClickOutside = () => { var { onClickOutside } = this.props; onClickOutside && onClickOutside(); }; this._handlePopoverMouseEnter = renderedTrigger => { var { onMouseEnter } = this.props; if (/*#__PURE__*/_react.default.isValidElement(renderedTrigger) && renderedTrigger.props.onMouseEnter) { renderedTrigger.props.onMouseEnter(); } onMouseEnter && onMouseEnter(); }; this._handlePopoverMouseLeave = renderedTrigger => { var { onMouseLeave } = this.props; if (this._shouldCloseOnMouseLeave) { this._shouldCloseOnMouseLeave = false; this.setState({ open: false, activeDescendantId: undefined }); } if (/*#__PURE__*/_react.default.isValidElement(renderedTrigger) && renderedTrigger.props.onMouseLeave) { renderedTrigger.props.onMouseLeave(); } onMouseLeave && onMouseLeave(); }; this._handleSelect = selectedOption => { var newState = {}; this._close(); if (!this._isControllingSelection()) { newState.selectedId = selectedOption.id; } this.setState(newState, () => { var { onSelect } = this.props; onSelect && onSelect(selectedOption); }); }; this._handleOptionMarked = (_option, optionElementId) => { this.setState({ activeDescendantId: optionElementId }); }; this._open = () => { this._setIsOpen(true); }; this._close = () => { this._setIsOpen(false); }; 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 => { var _this$props$options; return (_this$props$options = this.props.options) == null ? void 0 : _this$props$options.find(_ref2 => { var { id } = _ref2; return id === selectedId; }); }; /** * Determine if a certain key should open the DropdownLayout */ this._isOpenKey = key => { return ['Enter', ' ', 'ArrowDown'].includes(key); }; this._isClosingKey = key => { if (this._isDialogMode()) { return ['Escape'].includes(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.state.open) { this.setState({ listAutoFocus: true }); this._open(); e.preventDefault(); } else if (this._isClosingKey(e.key) && this.state.open) { // Fallback: handle closing keys when DropdownLayout doesn't process them // This happens in action list mode where DropdownLayout returns false immediately // for container-level keydown events (listType !== ListType.select check) this._close(); } } // Prevent trigger element events when dropdown is open. if (this.state.open && (e.key === 'Enter' || e.key === ' ') && !this._isEventFromFixedRegion(e)) { 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._setDropdownLayoutRef = ref => { var _this$_dropdownLayout; this._dropdownLayoutRef = ref; this.setState({ activeDescendantId: (_this$_dropdownLayout = this._dropdownLayoutRef) == null ? void 0 : _this$_dropdownLayout.getActiveDescendentElementId() }); }; this._handleOutsideClick = (_, reason) => { if (reason === 'outside-press') { this._setIsOpen(false, reason); return; } }; this._isDialogMode = () => { /** This component has two modes: * 1. Menu mode: when fixedHeader or fixedFooter is not present - https://www.w3.org/WAI/ARIA/apg/patterns/menubar/ * 2. Dialog mode: when fixedHeader or fixedFooter is present - https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/ */ var { fixedHeader, fixedFooter } = this.props; return Boolean(fixedHeader || fixedFooter); }; this._isEventFromFixedRegion = event => { var target = event.target; var region = target.closest('[data-dropdown-region]'); if (!region) { return false; } var regionValue = region.getAttribute('data-dropdown-region'); return regionValue === 'header' || regionValue === 'footer'; }; 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(renderedTrigger) { var { children } = this.props; if (!children) { return null; } return /*#__PURE__*/_react.default.isValidElement(children) ? children : /*#__PURE__*/_react.default.isValidElement(renderedTrigger) ? /*#__PURE__*/_react.default.cloneElement(renderedTrigger, _objectSpread(_objectSpread({}, renderedTrigger.props.onMouseEnter ? { onMouseEnter: () => {} } : {}), renderedTrigger.props.onMouseLeave ? { onMouseLeave: () => {} } : {})) : renderedTrigger; } _renderDropdownLayout() { var { options, maxHeight, overflow, focusOnSelectedOption, infiniteScroll, loadMore, hasMore, focusOnOption, markedOption, scrollToOption, onMouseDown, listType, autoFocus, fixedHeader, fixedFooter } = this.props; var { selectedId } = this.state; return /*#__PURE__*/_react.default.createElement(_DropdownLayout.default, { dataHook: "dropdown-base-dropdownlayout", className: _DropdownBaseSt.classes.list, ref: this._setDropdownLayoutRef, selectedId: selectedId, options: options, maxHeightPixels: maxHeight, onSelect: this._handleSelect, onOptionMarked: this._handleOptionMarked, onClose: this._handleClose, inContainer: true, visible: true, overflow: overflow, focusOnSelectedOption: focusOnSelectedOption, infiniteScroll: infiniteScroll, loadMore: loadMore, hasMore: hasMore, focusOnOption: focusOnOption, markedOption: markedOption, scrollToOption: scrollToOption, onMouseDown: onMouseDown, listType: listType // In dialog mode, FloatingFocusManager handles initial focus // In menu mode, DropdownLayout focuses the first option , autoFocus: this._isDialogMode() ? false : autoFocus || this.state.listAutoFocus, fixedHeader: fixedHeader, fixedFooter: fixedFooter, listboxId: "".concat(this.uniqueId, "-listbox"), __self: this, __source: { fileName: _jsxFileName, lineNumber: 326, columnNumber: 7 } }); } render() { var { dataHook, placement, appendTo, showArrow, zIndex, moveBy, minWidth, maxWidth, width, fixed, flip, dynamicWidth, fluid, animate, className, listType, popoverContentClassName, onMouseEnter, onMouseLeave, selectedId, children } = this.props; var { open, activeDescendantId } = this.state; var _this$props = this.props, { onShow, onHide } = _this$props, popoverProps = (0, _objectWithoutProperties2.default)(_this$props, _excluded); var renderedTrigger = typeof children === 'function' ? children({ toggle: Boolean(open) ? this._close : this._open, open: this._open, close: this._close, isOpen: Boolean(open), ref: this.triggerElementRef, delegateKeyDown: this._delegateKeyDown, selectedOption: this._getSelectedOption(selectedId), listboxId: "".concat(this.uniqueId, "-listbox"), activeDescendantId }) : children; return /*#__PURE__*/_react.default.createElement(_context.WixDesignSystemContext.Consumer, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 403, columnNumber: 7 } }, _ref3 => { var { mobile } = _ref3; return mobile ? /*#__PURE__*/_react.default.createElement("div", { __self: this, __source: { fileName: _jsxFileName, lineNumber: 406, columnNumber: 13 } }, /*#__PURE__*/_react.default.createElement("div", { __self: this, __source: { fileName: _jsxFileName, lineNumber: 407, columnNumber: 15 } }, this._renderChildren(renderedTrigger)), /*#__PURE__*/_react.default.createElement(_Drawer.default, { dataHook: dataHook, open: Boolean(open), onClose: isOpen => this._setIsOpen(isOpen), zIndex: zIndex, __self: this, __source: { fileName: _jsxFileName, lineNumber: 408, columnNumber: 15 } }, /*#__PURE__*/_react.default.createElement("div", { className: _DropdownBaseSt.classes.drawerContent, __self: this, __source: { fileName: _jsxFileName, lineNumber: 414, columnNumber: 17 } }, this._renderDropdownLayout()))) : /*#__PURE__*/_react.default.createElement(_PopoverNext.default, (0, _extends2.default)({ dataListType: listType, onOpenChange: this._handleOutsideClick, animate: animate, dataHook: dataHook, open: open, autoUpdateOptions: { animationFrame: true } }, popoverProps, { // backward compatible for migration stylable 1 to stylable 3 placement: placement, dynamicWidth: dynamicWidth, appendTo: appendTo, showArrow: showArrow, zIndex: zIndex, moveBy: moveBy, onKeyDown: this._handleKeyDown, onMouseEnter: onMouseEnter || !!(/*#__PURE__*/_react.default.isValidElement(renderedTrigger) && renderedTrigger.props.onMouseEnter) ? () => this._handlePopoverMouseEnter(renderedTrigger) : undefined, onMouseLeave: onMouseLeave || !!(/*#__PURE__*/_react.default.isValidElement(renderedTrigger) && renderedTrigger.props.onMouseLeave) ? () => this._handlePopoverMouseLeave(renderedTrigger) : undefined, onClickOutside: this._handleClickOutside, fixed: fixed, flip: flip, fluid: fluid, onHide: onHide, onShow: onShow, focusManagerEnabled: this._isDialogMode(), className: (0, _DropdownBaseSt.st)(_DropdownBaseSt.classes.root, { withWidth: Boolean(minWidth || maxWidth) }, className), minWidth: minWidth, maxWidth: maxWidth, width: width, __self: this, __source: { fileName: _jsxFileName, lineNumber: 420, columnNumber: 13 } }), /*#__PURE__*/_react.default.createElement(_PopoverNext.default.Trigger, { className: _DropdownBaseSt.classes.trigger, __self: this, __source: { fileName: _jsxFileName, lineNumber: 471, columnNumber: 15 } }, /*#__PURE__*/_react.default.createElement("div", { __self: this, __source: { fileName: _jsxFileName, lineNumber: 472, columnNumber: 17 } }, this._renderChildren(renderedTrigger))), /*#__PURE__*/_react.default.createElement(_PopoverNext.default.Content, { className: (0, _DropdownBaseSt.st)(_DropdownBaseSt.classes.content, popoverContentClassName, { withWidth: Boolean(minWidth || maxWidth) }), __self: this, __source: { fileName: _jsxFileName, lineNumber: 475, columnNumber: 15 } }, /*#__PURE__*/_react.default.createElement("div", { style: { minWidth, maxWidth }, __self: this, __source: { fileName: _jsxFileName, lineNumber: 480, columnNumber: 17 } }, this._renderDropdownLayout()))); }); } } DropdownBase.displayName = 'DropdownBase'; DropdownBase.defaultProps = { placement: 'bottom', appendTo: 'parent', showArrow: false, maxHeight: '260px', dynamicWidth: true, minWidth: 192, fluid: false, animate: false, listType: 'select', onShow: () => {}, onHide: () => {}, onMouseEnter: () => {}, onMouseLeave: () => {} }; var _default = exports.default = DropdownBase; //# sourceMappingURL=DropdownBase.js.map