UNPKG

react-responsive-tabs

Version:
628 lines (529 loc) 25.9 kB
"use strict"; function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _react = _interopRequireWildcard(require("react")); var _withPolyfill = _interopRequireDefault(require("react-resize-detector/build/withPolyfill")); var _classnames = _interopRequireDefault(require("classnames")); var _lodash = _interopRequireDefault(require("lodash.throttle")); var _propTypes = _interopRequireDefault(require("prop-types")); var _ShowMore = _interopRequireDefault(require("./components/ShowMore")); var _Tab = _interopRequireDefault(require("./components/Tab")); var _TabPanel = _interopRequireDefault(require("./components/TabPanel")); var _InkBar = _interopRequireDefault(require("./components/InkBar")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } 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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 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 _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 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; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var tabPrefix = 'tab-'; var panelPrefix = 'panel-'; var Tabs = /*#__PURE__*/function (_Component) { _inherits(Tabs, _Component); var _super = _createSuper(Tabs); function Tabs(props) { var _this; _classCallCheck(this, Tabs); _this = _super.call(this, props); _defineProperty(_assertThisInitialized(_this), "onResize", function () { if (_this.tabsWrapper.current) { var currentIsCollapsed = _this.getIsCollapsed(); _this.setState({ blockWidth: _this.tabsWrapper.current.offsetWidth }, function () { var items = _this.props.items; var selectedTabKey = _this.state.selectedTabKey; var nextIsCollapsed = _this.getIsCollapsed(); if (currentIsCollapsed && !nextIsCollapsed && selectedTabKey === -1 && items && items.length) { var firstTabKey = items[0].key || 0; _this.setState({ selectedTabKey: firstTabKey }); } }); } }); _defineProperty(_assertThisInitialized(_this), "onChangeTab", function (nextTabKey, evt) { var _this$props = _this.props, beforeChange = _this$props.beforeChange, onChange = _this$props.onChange; var selectedTabKey = _this.state.selectedTabKey; if (typeof beforeChange === 'function') { var beforeChangeRes = beforeChange({ selectedTabKey: selectedTabKey, nextTabKey: nextTabKey }); if (beforeChangeRes === false) { evt.preventDefault(); return; } } var isCollapsed = _this.getIsCollapsed(); if (isCollapsed && selectedTabKey === nextTabKey) { // hide on mobile _this.setState({ selectedTabKey: -1 }); } else { // change active tab _this.setState({ selectedTabKey: nextTabKey }); } if (onChange) { onChange(nextTabKey); } }); _defineProperty(_assertThisInitialized(_this), "onFocusTab", function (focusedTabKey) { return function () { return _this.setState({ focusedTabKey: focusedTabKey }); }; }); _defineProperty(_assertThisInitialized(_this), "onBlurTab", function () { return _this.setState({ focusedTabKey: null }); }); _defineProperty(_assertThisInitialized(_this), "onKeyDown", function (event) { var focusedTabKey = _this.state.focusedTabKey; if (event.keyCode === 13 && focusedTabKey !== null) { _this.setState({ selectedTabKey: focusedTabKey }); } }); _defineProperty(_assertThisInitialized(_this), "setTabsDimensions", function () { if (!_this.tabsWrapper.current) { // it shouldn't happen ever. Just a paranoic check return; } var tabDimensions = _this.state.tabDimensions; // initial wrapper width calculation var blockWidth = _this.tabsWrapper.current.offsetWidth; // calculate width and offset for each tab var tabsTotalWidth = 0; var tabDimensionsNext = {}; Object.keys(_this.tabRefs).forEach(function (key) { if (_this.tabRefs[key]) { var tabKey = key.replace(tabPrefix, ''); var width = _this.tabRefs[key].tab.offsetWidth; if (width) { tabDimensionsNext[tabKey] = { width: width, offset: tabsTotalWidth }; } else { tabDimensionsNext[tabKey] = tabDimensions[tabKey]; } tabsTotalWidth += tabDimensionsNext[tabKey].width; } }); _this.setState({ tabDimensions: tabDimensionsNext, tabsTotalWidth: tabsTotalWidth, blockWidth: blockWidth }); }); _defineProperty(_assertThisInitialized(_this), "getTabs", function () { var _this$props2 = _this.props, showMore = _this$props2.showMore, transform = _this$props2.transform, transformWidth = _this$props2.transformWidth, items = _this$props2.items, allowRemove = _this$props2.allowRemove, removeActiveOnly = _this$props2.removeActiveOnly, _onRemove = _this$props2.onRemove; var _this$state = _this.state, blockWidth = _this$state.blockWidth, tabsTotalWidth = _this$state.tabsTotalWidth, tabDimensions = _this$state.tabDimensions, showMoreWidth = _this$state.showMoreWidth; var selectedTabKey = _this.getSelectedTabKey(); var collapsed = blockWidth && transform && blockWidth < transformWidth; var tabIndex = 0; var availableWidth = blockWidth - (tabsTotalWidth > blockWidth ? showMoreWidth : 0); return items.reduce(function (result, item, index) { var _item$key = item.key, key = _item$key === void 0 ? index : _item$key, title = item.title, content = item.content, getContent = item.getContent, disabled = item.disabled, tabClassName = item.tabClassName, panelClassName = item.panelClassName; var selected = selectedTabKey === key; var payload = { tabIndex: tabIndex, collapsed: collapsed, selected: selected, disabled: disabled, key: key }; var tabPayload = _objectSpread(_objectSpread({}, payload), {}, { title: title, onRemove: function onRemove(evt) { if (typeof _onRemove === 'function') { _onRemove(key, evt); } }, allowRemove: allowRemove && (!removeActiveOnly || selected), className: tabClassName }); var panelPayload = _objectSpread(_objectSpread({}, payload), {}, { content: content, getContent: getContent, className: panelClassName }); var tabWidth = tabDimensions[key] ? tabDimensions[key].width : 0; tabIndex += 1; /* eslint-disable no-param-reassign */ if ( // don't need to `Show more` button !showMore || // initial call !blockWidth || // collapsed mode collapsed || // all tabs are fit into the block blockWidth > tabsTotalWidth || // current tab fit into the block availableWidth - tabWidth > 0) { result.tabsVisible.push(tabPayload); } else { result.tabsHidden.push(tabPayload); if (selected) result.isSelectedTabHidden = true; } /* eslint-enable no-param-reassign */ result.panels[key] = panelPayload; // eslint-disable-line no-param-reassign availableWidth -= tabWidth; return result; }, { tabsVisible: [], tabsHidden: [], panels: {}, isSelectedTabHidden: false }); }); _defineProperty(_assertThisInitialized(_this), "getTabProps", function (_ref) { var title = _ref.title, key = _ref.key, selected = _ref.selected, collapsed = _ref.collapsed, tabIndex = _ref.tabIndex, disabled = _ref.disabled, className = _ref.className, onRemove = _ref.onRemove, allowRemove = _ref.allowRemove; return { selected: selected, allowRemove: allowRemove, children: title, key: tabPrefix + key, id: tabPrefix + key, ref: function ref(e) { return _this.tabRefs[tabPrefix + key] = e; }, originalKey: key, onClick: _this.onChangeTab, onFocus: _this.onFocusTab, onBlur: _this.onBlurTab, onRemove: onRemove, panelId: panelPrefix + key, classNames: _this.getClassNamesFor('tab', { selected: selected, collapsed: collapsed, tabIndex: tabIndex, disabled: disabled, className: className }) }; }); _defineProperty(_assertThisInitialized(_this), "getPanelProps", function (_ref2, isHidden) { var key = _ref2.key, content = _ref2.content, getContent = _ref2.getContent, className = _ref2.className; return { getContent: getContent, children: content, key: panelPrefix + key, id: panelPrefix + key, tabId: tabPrefix + key, classNames: _this.getClassNamesFor('panel', { className: className, isHidden: isHidden }), isHidden: isHidden }; }); _defineProperty(_assertThisInitialized(_this), "getShowMoreProps", function (isShown, isSelectedTabHidden, showMoreLabel) { return { onShowMoreChanged: _this.showMoreChanged, isShown: isShown, label: showMoreLabel, hasChildSelected: isSelectedTabHidden }; }); _defineProperty(_assertThisInitialized(_this), "getClassNamesFor", function (type, _ref3) { var selected = _ref3.selected, collapsed = _ref3.collapsed, tabIndex = _ref3.tabIndex, disabled = _ref3.disabled, _ref3$className = _ref3.className, className = _ref3$className === void 0 ? '' : _ref3$className, isHidden = _ref3.isHidden; var _this$props3 = _this.props, tabClass = _this$props3.tabClass, panelClass = _this$props3.panelClass; switch (type) { case 'tab': return (0, _classnames["default"])('RRT__tab', className, tabClass, { 'RRT__tab--first': !tabIndex, 'RRT__tab--selected': selected, 'RRT__tab--disabled': disabled, 'RRT__tab--collapsed': collapsed }); case 'panel': return (0, _classnames["default"])('RRT__panel', className, panelClass, { 'RRT__panel--hidden': isHidden }); default: return ''; } }); _defineProperty(_assertThisInitialized(_this), "getSelectedTabKey", function () { var items = _this.props.items; var selectedTabKey = _this.state.selectedTabKey; if (typeof selectedTabKey === 'undefined') { if (!items[0]) { return undefined; } return items[0].key || 0; } return selectedTabKey; }); _defineProperty(_assertThisInitialized(_this), "getIsCollapsed", function () { var _this$props4 = _this.props, transform = _this$props4.transform, transformWidth = _this$props4.transformWidth; var blockWidth = _this.state.blockWidth; return blockWidth && transform && blockWidth < transformWidth; }); _defineProperty(_assertThisInitialized(_this), "showMoreChanged", function (element) { if (!element) { return; } var showMoreWidth = _this.state.showMoreWidth; var offsetWidth = element.offsetWidth; if (showMoreWidth === offsetWidth) { return; } _this.setState({ showMoreWidth: offsetWidth }); }); _defineProperty(_assertThisInitialized(_this), "getExpandedTabs", function (panels, selectedTabKey, isCollapsed) { var unmountOnExit = _this.props.unmountOnExit; if (isCollapsed) { return undefined; } if (!unmountOnExit) { // render all tabs if unmountOnExit === false (inactive are hidden) return Object.keys(panels).map(function (key) { return /*#__PURE__*/_react["default"].createElement(_TabPanel["default"], _this.getPanelProps(panels[key], "".concat(selectedTabKey) !== "".concat(key))); }); } if (panels[selectedTabKey]) { // render only active tab if unmountOnExit === true return /*#__PURE__*/_react["default"].createElement(_TabPanel["default"], _this.getPanelProps(panels[selectedTabKey])); } return undefined; }); _this.tabRefs = {}; _this.tabsWrapper = /*#__PURE__*/(0, _react.createRef)(); _this.selectedTabKeyProp = props.selectedTabKey; _this.state = { tabDimensions: {}, blockWidth: 0, tabsTotalWidth: 0, showMoreWidth: 40, selectedTabKey: props.selectedTabKey, focusedTabKey: null }; _this.onResizeThrottled = (0, _lodash["default"])(_this.onResize, props.resizeThrottle, { trailing: true }); return _this; } _createClass(Tabs, [{ key: "componentDidMount", value: function componentDidMount() { this.setTabsDimensions(); } }, { key: "shouldComponentUpdate", value: function shouldComponentUpdate(nextProps, nextState) { var _this$state2 = this.state, selectedTabKey = _this$state2.selectedTabKey, tabsTotalWidth = _this$state2.tabsTotalWidth, blockWidth = _this$state2.blockWidth, showMoreWidth = _this$state2.showMoreWidth; var _this$props5 = this.props, items = _this$props5.items, transform = _this$props5.transform, showMore = _this$props5.showMore, showInkBar = _this$props5.showInkBar, allowRemove = _this$props5.allowRemove, removeActiveOnly = _this$props5.removeActiveOnly, uid = _this$props5.uid; return items !== nextProps.items || nextProps.uid !== uid || nextProps.transform !== transform || nextProps.showMore !== showMore || nextProps.showInkBar !== showInkBar || nextProps.allowRemove !== allowRemove || nextProps.removeActiveOnly !== removeActiveOnly || nextState.tabsTotalWidth !== tabsTotalWidth || nextState.blockWidth !== blockWidth || nextState.showMoreWidth !== showMoreWidth || nextProps.selectedTabKey !== this.selectedTabKeyProp || nextState.selectedTabKey !== selectedTabKey; } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { var _this$props6 = this.props, uid = _this$props6.uid, items = _this$props6.items, selectedTabKey = _this$props6.selectedTabKey; if (this.selectedTabKeyProp !== selectedTabKey) { // eslint-disable-next-line react/no-did-update-set-state this.setState({ selectedTabKey: selectedTabKey }); } if (uid !== prevProps.uid || items.length !== prevProps.items.length || items.every(function (item, i) { return item.title !== prevProps.items[i].title; })) { this.setTabsDimensions(); } this.selectedTabKeyProp = selectedTabKey; } }, { key: "render", value: function render() { var _this2 = this; var _this$props7 = this.props, showInkBar = _this$props7.showInkBar, containerClass = _this$props7.containerClass, tabsWrapperClass = _this$props7.tabsWrapperClass, showMore = _this$props7.showMore, transform = _this$props7.transform, showMoreLabel = _this$props7.showMoreLabel, unmountOnExit = _this$props7.unmountOnExit; var tabDimensions = this.state.tabDimensions; var _this$getTabs = this.getTabs(), tabsVisible = _this$getTabs.tabsVisible, tabsHidden = _this$getTabs.tabsHidden, panels = _this$getTabs.panels, isSelectedTabHidden = _this$getTabs.isSelectedTabHidden; var isCollapsed = this.getIsCollapsed(); var selectedTabKey = this.getSelectedTabKey(); var selectedTabDimensions = tabDimensions[selectedTabKey] || {}; var containerClasses = (0, _classnames["default"])('RRT__container', containerClass); var tabsClasses = (0, _classnames["default"])('RRT__tabs', tabsWrapperClass, { RRT__accordion: isCollapsed }); var handleResize = showMore || transform; return /*#__PURE__*/_react["default"].createElement(_withPolyfill["default"], { handleWidth: handleResize, handleHeight: false, targetRef: this.tabsWrapper, onResize: this.onResizeThrottled }, function () { return /*#__PURE__*/_react["default"].createElement("div", { className: containerClasses, ref: _this2.tabsWrapper, onKeyDown: _this2.onKeyDown }, /*#__PURE__*/_react["default"].createElement("div", { className: tabsClasses }, tabsVisible.reduce(function (result, tab) { result.push( /*#__PURE__*/_react["default"].createElement(_Tab["default"], _this2.getTabProps(tab))); if (isCollapsed && (!unmountOnExit || selectedTabKey === tab.key)) { result.push( /*#__PURE__*/_react["default"].createElement(_TabPanel["default"], _this2.getPanelProps(panels[tab.key], selectedTabKey !== tab.key))); } return result; }, []), !isCollapsed && /*#__PURE__*/_react["default"].createElement(_ShowMore["default"], _this2.getShowMoreProps(showMore, isSelectedTabHidden, showMoreLabel), tabsHidden.map(function (tab) { return /*#__PURE__*/_react["default"].createElement(_Tab["default"], _this2.getTabProps(tab)); }))), showInkBar && !isCollapsed && !isSelectedTabHidden && /*#__PURE__*/_react["default"].createElement(_InkBar["default"], { left: selectedTabDimensions.offset || 0, width: selectedTabDimensions.width || 0 }), _this2.getExpandedTabs(panels, selectedTabKey, isCollapsed)); }); } }]); return Tabs; }(_react.Component); exports["default"] = Tabs; Tabs.propTypes = { /* eslint-disable react/no-unused-prop-types */ // list of tabs items: _propTypes["default"].oneOfType([_propTypes["default"].array, _propTypes["default"].object]), /* eslint-enable react/no-unused-prop-types */ // selected tab key selectedTabKey: _propTypes["default"].oneOfType([_propTypes["default"].number, _propTypes["default"].string]), // show 'X' and remove tab allowRemove: _propTypes["default"].bool, // show 'X' closing element only for active tab removeActiveOnly: _propTypes["default"].bool, // move tabs to the special `Show more` tab if they don't fit into a screen showMore: _propTypes["default"].bool, // materialUI-like rail under the selected tab showInkBar: _propTypes["default"].bool, // transform to the accordion on small screens transform: _propTypes["default"].bool, // tabs will be transformed to accodrion for screen sizes below `transformWidth`px transformWidth: _propTypes["default"].number, // beforeChange callback: return false to prevent tab change beforeChange: _propTypes["default"].func, // onChange active tab callback onChange: _propTypes["default"].func, // onRemove callback onRemove: _propTypes["default"].func, // frequency of onResize recalculation fires resizeThrottle: _propTypes["default"].number, // unmounts the tab when it gets inactive (unselected) unmountOnExit: _propTypes["default"].bool, // classnames containerClass: _propTypes["default"].string, tabsWrapperClass: _propTypes["default"].string, tabClass: _propTypes["default"].string, panelClass: _propTypes["default"].string, // optional external id. Force rerender when it changes // eslint-disable-next-line react/forbid-prop-types uid: _propTypes["default"].any, // labels showMoreLabel: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].node]) }; Tabs.defaultProps = { items: [], uid: undefined, selectedTabKey: undefined, showMore: true, showInkBar: false, allowRemove: false, removeActiveOnly: false, transform: true, transformWidth: 800, resizeThrottle: 100, containerClass: undefined, tabsWrapperClass: undefined, tabClass: undefined, panelClass: undefined, showMoreLabel: '...', unmountOnExit: true, beforeChange: undefined, onChange: function onChange() { return null; }, onRemove: function onRemove() { return null; } };