UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

389 lines (388 loc) 14.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _get2 = _interopRequireDefault(require("lodash/get")); var _times2 = _interopRequireDefault(require("lodash/times")); var _baseComponent = _interopRequireDefault(require("../_base/baseComponent")); var _react = _interopRequireDefault(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _classnames = _interopRequireDefault(require("classnames")); require("@douyinfe/semi-foundation/lib/cjs/navigation/navigation.css"); var _isNullOrUndefined = _interopRequireDefault(require("@douyinfe/semi-foundation/lib/cjs/utils/isNullOrUndefined")); var _subNavFoundation = _interopRequireDefault(require("@douyinfe/semi-foundation/lib/cjs/navigation/subNavFoundation")); var _constants = require("@douyinfe/semi-foundation/lib/cjs/navigation/constants"); var _semiIcons = require("@douyinfe/semi-icons"); var _Item = _interopRequireDefault(require("./Item")); var _dropdown = _interopRequireDefault(require("../dropdown")); var _navContext = _interopRequireDefault(require("./nav-context")); var _collapsible = _interopRequireDefault(require("../collapsible")); var _cssAnimation = _interopRequireDefault(require("../_cssAnimation")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } class SubNav extends _baseComponent.default { constructor(props) { super(props); this.setItemRef = ref => { if (ref && ref.current) { this.itemRef = ref; } else { this.itemRef = { current: ref }; } }; this.setTitleRef = ref => { if (ref && ref.current) { this.titleRef = ref; } else { this.titleRef = { current: ref }; } }; this.handleClick = e => { this.foundation.handleClick(e && e.nativeEvent, this.titleRef && this.titleRef.current); }; this.handleKeyPress = e => { this.foundation.handleKeyPress(e && e.nativeEvent, this.titleRef && this.titleRef.current); }; this.handleDropdownVisible = visible => this.foundation.handleDropdownVisibleChange(visible); this.state = { isHovered: false }; this.adapter.setCache('firstMounted', true); this.titleRef = /*#__PURE__*/_react.default.createRef(); this.itemRef = /*#__PURE__*/_react.default.createRef(); this.foundation = new _subNavFoundation.default(this.adapter); } _invokeContextFunc(funcName) { if (funcName && this.context && typeof this.context[funcName] === 'function') { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } return this.context[funcName](...args); } return null; } get adapter() { var _this = this; return Object.assign(Object.assign({}, super.adapter), { updateIsHovered: isHovered => this.setState({ isHovered }), getOpenKeys: () => this.context && this.context.openKeys, getOpenKeysIsControlled: () => this.context && this.context.openKeysIsControlled, getCanUpdateOpenKeys: () => this.context && this.context.canUpdateOpenKeys, updateOpen: isOpen => this._invokeContextFunc(isOpen ? 'addOpenKeys' : 'removeOpenKeys', this.props.itemKey), notifyGlobalOpenChange: function () { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } return _this._invokeContextFunc('onOpenChange', ...args); }, notifyGlobalOnSelect: function () { for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } return _this._invokeContextFunc('onSelect', ...args); }, notifyGlobalOnClick: function () { for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } return _this._invokeContextFunc('onClick', ...args); }, getIsSelected: itemKey => Boolean(!(0, _isNullOrUndefined.default)(itemKey) && (0, _get2.default)(this.context, 'selectedKeys', []).includes(String(itemKey))), getIsOpen: () => { const { itemKey } = this.props; return Boolean(this.context && this.context.openKeys && this.context.openKeys.includes(this.props.itemKey)); } }); } renderIcon(icon, pos, withTransition) { let isToggleIcon = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; let key = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; const { prefixCls } = this.context; let iconSize = 'large'; if (pos === _constants.strings.ICON_POS_RIGHT) { iconSize = 'default'; } const className = (0, _classnames.default)(`${prefixCls}-item-icon`, { [`${prefixCls}-item-icon-toggle-${this.context.toggleIconPosition}`]: isToggleIcon, [`${prefixCls}-item-icon-info`]: !isToggleIcon }); const isOpen = this.adapter.getIsOpen(); const iconElem = /*#__PURE__*/_react.default.isValidElement(icon) ? withTransition ? (/*#__PURE__*/_react.default.createElement(_cssAnimation.default, { animationState: isOpen ? "enter" : "leave", startClassName: `${_constants.cssClasses.PREFIX}-icon-rotate-${isOpen ? "180" : "0"}` }, _ref => { let { animationClassName } = _ref; // @ts-ignore return /*#__PURE__*/_react.default.cloneElement(icon, { size: iconSize, className: animationClassName }); }) // @ts-ignore ) : /*#__PURE__*/_react.default.cloneElement(icon, { size: iconSize }) : null; return /*#__PURE__*/_react.default.createElement("i", { key: key, className: className }, iconElem); } renderTitleDiv() { const { text, icon, itemKey, indent, disabled, level, expandIcon } = this.props; const { mode, isInSubNav, isCollapsed, prefixCls, subNavMotion, limitIndent } = this.context; const isOpen = this.adapter.getIsOpen(); const titleCls = (0, _classnames.default)(`${prefixCls}-sub-title`, { [`${prefixCls}-sub-title-selected`]: this.adapter.getIsSelected(itemKey), [`${prefixCls}-sub-title-disabled`]: disabled }); let withTransition = false; let toggleIconType = ''; if (isCollapsed) { if (isInSubNav) { toggleIconType = /*#__PURE__*/_react.default.createElement(_semiIcons.IconChevronRight, null); } else { toggleIconType = null; } } else if (mode === _constants.strings.MODE_HORIZONTAL) { if (isInSubNav) { toggleIconType = /*#__PURE__*/_react.default.createElement(_semiIcons.IconChevronRight, { "aria-hidden": true }); } else { toggleIconType = expandIcon ? expandIcon : /*#__PURE__*/_react.default.createElement(_semiIcons.IconChevronDown, { "aria-hidden": true }); // Horizontal mode does not require animation fix#1198 // withTransition = true; } } else { if (subNavMotion) { withTransition = true; } toggleIconType = expandIcon ? expandIcon : /*#__PURE__*/_react.default.createElement(_semiIcons.IconChevronDown, { "aria-hidden": true }); } let placeholderIcons = null; if (mode === _constants.strings.MODE_VERTICAL && !limitIndent && !isCollapsed) { /* Different icons' amount means different indents.*/ const iconAmount = icon && !indent ? level : level - 1; placeholderIcons = (0, _times2.default)(iconAmount, index => this.renderIcon(null, _constants.strings.ICON_POS_RIGHT, false, false, index)); } const isIconChevronRightShow = !isCollapsed && isInSubNav && mode === _constants.strings.MODE_HORIZONTAL || isCollapsed && isInSubNav; const titleDiv = /*#__PURE__*/_react.default.createElement("div", { role: "menuitem", // to avoid nested horizontal navigation be focused tabIndex: isIconChevronRightShow ? -1 : 0, ref: this.setTitleRef, className: titleCls, onClick: this.handleClick, onKeyPress: this.handleKeyPress, "aria-expanded": isOpen ? 'true' : 'false' }, /*#__PURE__*/_react.default.createElement("div", { className: `${prefixCls}-item-inner` }, placeholderIcons, this.context.toggleIconPosition === _constants.strings.TOGGLE_ICON_LEFT && this.renderIcon(toggleIconType, _constants.strings.ICON_POS_RIGHT, withTransition, true, 'key-toggle-position-left'), icon || indent || isInSubNav && mode !== _constants.strings.MODE_HORIZONTAL ? this.renderIcon(icon, _constants.strings.ICON_POS_LEFT, false, false, 'key-inSubNav-position-left') : null, /*#__PURE__*/_react.default.createElement("span", { className: `${prefixCls}-item-text` }, text), this.context.toggleIconPosition === _constants.strings.TOGGLE_ICON_RIGHT && this.renderIcon(toggleIconType, _constants.strings.ICON_POS_RIGHT, withTransition, true, 'key-toggle-position-right'))); return titleDiv; } renderSubUl() { const { children, maxHeight } = this.props; const { isCollapsed, mode, subNavMotion, prefixCls } = this.context; const isOpen = this.adapter.getIsOpen(); const isHorizontal = mode === _constants.strings.MODE_HORIZONTAL; const subNavCls = (0, _classnames.default)(`${prefixCls}-sub`, { [`${prefixCls}-sub-open`]: isOpen, [`${prefixCls}-sub-popover`]: isCollapsed || isHorizontal }); const ulWithMotion = /*#__PURE__*/_react.default.createElement(_collapsible.default, { motion: subNavMotion, isOpen: isOpen, keepDOM: false, fade: true }, !isCollapsed ? /*#__PURE__*/_react.default.createElement("ul", { className: subNavCls }, children) : null); const finalDom = isHorizontal ? null : subNavMotion ? ulWithMotion : isOpen && !isCollapsed ? (/*#__PURE__*/_react.default.createElement("ul", { className: subNavCls }, children)) : null; return finalDom; } wrapDropdown() { let elem = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; let _elem = elem; const { children, dropdownStyle, disabled, subDropdownProps, dropdownProps: userDropdownProps } = this.props; const { mode, isInSubNav, isCollapsed, subNavCloseDelay, subNavOpenDelay, prefixCls, getPopupContainer } = this.context; const isOpen = this.adapter.getIsOpen(); const openKeysIsControlled = this.adapter.getOpenKeysIsControlled(); const subNavCls = (0, _classnames.default)({ [`${prefixCls}-popover`]: isCollapsed }); const dropdownProps = { trigger: 'hover', style: dropdownStyle }; if (openKeysIsControlled) { dropdownProps.trigger = 'custom'; dropdownProps.visible = isOpen; } if (getPopupContainer) { dropdownProps.getPopupContainer = getPopupContainer; } if (isCollapsed || mode === _constants.strings.MODE_HORIZONTAL) { // Do not show dropdown when disabled _elem = !disabled ? (/*#__PURE__*/_react.default.createElement(_dropdown.default, Object.assign({ className: subNavCls, render: (/*#__PURE__*/_react.default.createElement(_dropdown.default.Menu, null, children)), position: mode === _constants.strings.MODE_HORIZONTAL && !isInSubNav ? 'bottomLeft' : 'rightTop', mouseEnterDelay: subNavOpenDelay, mouseLeaveDelay: subNavCloseDelay, onVisibleChange: this.handleDropdownVisible }, userDropdownProps ? userDropdownProps : subDropdownProps, dropdownProps), _elem)) : _elem; } return _elem; } render() { const { itemKey, style, onMouseEnter, onMouseLeave, disabled, text } = this.props; const { mode, isCollapsed, prefixCls } = this.context; let titleDiv = this.renderTitleDiv(); const subUl = this.renderSubUl(); // When mode=horizontal, it is displayed in Dropdown if (isCollapsed || mode === _constants.strings.MODE_HORIZONTAL) { titleDiv = this.wrapDropdown(titleDiv); } return ( /*#__PURE__*/ // Children is not a recommended usage and may cause some bug-like performance, but some users have already used it, so here we only delete the ts definition instead of deleting the actual code // children 并不是我们推荐的用法,可能会导致一些像 bug的表现,但是有些用户已经用了,所以此处仅作删除 ts 定义而非删除实际代码的操作 // refer https://github.com/DouyinFE/semi-design/issues/2710 // @ts-ignore _react.default.createElement(_Item.default, { style: style, isSubNav: true, itemKey: itemKey, forwardRef: this.setItemRef, isCollapsed: isCollapsed, className: `${prefixCls}-sub-wrap`, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, disabled: disabled, text: text }, /*#__PURE__*/_react.default.createElement(_navContext.default.Provider, { value: Object.assign(Object.assign({}, this.context), { isInSubNav: true }) }, titleDiv, subUl)) ); } } exports.default = SubNav; SubNav.contextType = _navContext.default; SubNav.propTypes = { /** * Unique identification */ itemKey: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]), /** * Copywriting */ text: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.node]), /** * Whether child navigation is expanded */ isOpen: _propTypes.default.bool, /** * Whether it is in the state of being stowed to the sidebar */ isCollapsed: _propTypes.default.bool, /** * Whether to keep the left Icon placeholder */ indent: _propTypes.default.oneOfType([_propTypes.default.bool, _propTypes.default.number]), /** * Nested child elements */ children: _propTypes.default.node, style: _propTypes.default.object, /** * Icon name on the left */ icon: _propTypes.default.node, /** * Maximum height (for animation) */ maxHeight: _propTypes.default.number, onMouseEnter: _propTypes.default.func, onMouseLeave: _propTypes.default.func, // Is it disabled disabled: _propTypes.default.bool, level: _propTypes.default.number }; SubNav.defaultProps = { level: 0, indent: false, isCollapsed: false, isOpen: false, maxHeight: _constants.numbers.DEFAULT_SUBNAV_MAX_HEIGHT, disabled: false };