UNPKG

chowa

Version:

UI component library based on React

320 lines (319 loc) 14 kB
/** * @license chowa v1.1.3 * * Copyright (c) Chowa Techonlogies Co.,Ltd.(http://www.chowa.cn). * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const React = require("react"); const PropTypes = require("prop-types"); const resize_observer_polyfill_1 = require("resize-observer-polyfill"); const classnames_1 = require("classnames"); const utils_1 = require("../utils"); const tabs_panel_1 = require("./tabs-panel"); const icon_1 = require("../icon"); class Tabs extends React.PureComponent { constructor(props) { super(props); this.state = { selfActiveIndex: undefined, showNavScrollBtn: false, navPageCount: 1, navPageNumber: 1, navWrapperStyle: {}, activeLineStyle: {}, panelWrapperStyle: {} }; [ 'onTabClickHandler', 'onCloseHandler', 'onTabAppendHandler', 'preTabPage', 'nextTabPage' ].forEach((fn) => { this[fn] = this[fn].bind(this); }); } preTabPage() { this.setState({ navPageNumber: this.state.navPageNumber - 1 }, () => { this.updateNavPagination(); }); } nextTabPage() { this.setState({ navPageNumber: this.state.navPageNumber + 1 }, () => { this.updateNavPagination(); }); } updateNavPagination() { const { tabPosition } = this.props; const { navPageNumber, navPageCount } = this.state; const { width: clientWidth, height: clientHeight } = utils_1.doms.rect(this.navScrollEle); const { width, height } = utils_1.doms.rect(this.navWrapperEle); let navWrapperStyle = {}; if (tabPosition === 'top' || tabPosition === 'bottom') { navWrapperStyle = { transform: navPageNumber === navPageCount ? `translate(-${width - clientWidth}px, 0)` : `translate(-${(navPageNumber - 1) * clientWidth}px, 0)` }; } else { navWrapperStyle = { transform: navPageNumber === navPageCount ? `translate(0, -${height - clientHeight}px)` : `translate(0, -${(navPageNumber - 1) * clientHeight}px)` }; } this.setState({ navWrapperStyle }); } componentDidUpdate(preProps) { if (preProps.activeIndex !== this.props.activeIndex && this.state.selfActiveIndex !== this.props.activeIndex) { this.setState({ selfActiveIndex: this.props.activeIndex }, () => { this.updateActiveTabs(); this.updateNavScrollParams(); }); } if (React.Children.count(preProps.children) !== React.Children.count(this.props.children)) { this.updateActiveTabs(); this.updateNavScrollParams(); } } componentDidMount() { const { defaultActiveIndex, activeIndex, children } = this.props; let firstIndex; React.Children.forEach(children, (child, key) => { if (utils_1.isReactElement(child) && child.type === tabs_panel_1.default && !utils_1.isExist(firstIndex)) { firstIndex = child.props.index || key; } }); this.setState({ selfActiveIndex: activeIndex || defaultActiveIndex || firstIndex }); this.resizeObserver = new resize_observer_polyfill_1.default(() => { this.updateNavScrollParams(); this.updateActiveTabs(); }); this.resizeObserver.observe(this.navScrollEle); } componentWillUnmount() { this.resizeObserver.unobserve(this.navScrollEle); this.resizeObserver.disconnect(); } updateNavScrollParams() { const { tabPosition } = this.props; const { width: parentWidth, height: parentHeight } = utils_1.doms.rect(this.containerEle); const { top: clientTop, left: clientLeft, width: clientWidth, height: clientHeight } = utils_1.doms.rect(this.navScrollEle); const { width, height } = utils_1.doms.rect(this.navWrapperEle); const activeEle = this.navWrapperEle.querySelector('.' + utils_1.preClass('tabs-tab-active')); const { left, top } = utils_1.doms.rect(activeEle); const showNavScrollBtn = (tabPosition === 'top' || tabPosition === 'bottom') ? width > parentWidth : height > parentHeight; const navPageCount = (tabPosition === 'top' || tabPosition === 'bottom') ? Math.ceil(width / clientWidth) : Math.ceil(height / clientHeight); let navPageNumber = 0; if (tabPosition === 'top' || tabPosition === 'bottom') { const horizontalDiff = left - clientLeft; navPageNumber = Math.ceil(horizontalDiff / clientWidth); } else { const verticalDiff = top - clientTop; navPageNumber = Math.ceil(verticalDiff / clientHeight); } this.setState({ showNavScrollBtn, navPageCount, navPageNumber }, () => { this.updateNavPagination(); this.updateActiveTabs(); }); } onTabClickHandler(index) { this.setState({ selfActiveIndex: index }, () => { if (this.props.onChange) { this.props.onChange(index); } this.updateActiveTabs(); }); } onCloseHandler(index, e) { const { onBeforeClose, onClose } = this.props; utils_1.stopReactPropagation(e); if (onBeforeClose && !onBeforeClose(index)) { return; } if (onClose) { onClose(index); } } onTabAppendHandler() { if (this.props.onAppend) { this.props.onAppend(); } } updateActiveTabs() { const { tabPosition, mode } = this.props; const { selfActiveIndex } = this.state; let activeLineStyle = {}; if (mode === 'line') { const activeEle = this.navWrapperEle.querySelector('.' + utils_1.preClass('tabs-tab-active')); const { width, left, height, top } = utils_1.doms.rect(activeEle); const { left: parsentOffetLeft, top: parsentOffetTop } = utils_1.doms.rect(this.navWrapperEle); if (tabPosition === 'top' || tabPosition === 'bottom') { activeLineStyle = { width, transform: `translate(${left - parsentOffetLeft}px, 0)` }; } else { activeLineStyle = { height, transform: `translate(0, ${top - parsentOffetTop}px)` }; } } const { width: panelOffsetWidth } = utils_1.doms.rect(this.panelWrapperEle); const arrIndex = this.tabIndexs.indexOf(selfActiveIndex); this.setState({ activeLineStyle, panelWrapperStyle: { transform: `translate(-${panelOffsetWidth * arrIndex}px, 0)` } }); } compileTabs() { const { children, mode, tabPosition, closable, appendable } = this.props; const { selfActiveIndex, showNavScrollBtn, navPageCount, navPageNumber, navWrapperStyle, activeLineStyle, panelWrapperStyle } = this.state; const tabNodes = []; const panelNodes = []; const renderNodes = []; const tabIndexs = []; React.Children.forEach(children, (child, key) => { if (!utils_1.isReactElement(child) || child.type !== tabs_panel_1.default) { throw new Error('Tabs elements only support Tabs.Panel'); } const { index: originIndex, tab, disabled } = child.props; const index = utils_1.isExist(originIndex) ? originIndex : key; const tabClass = classnames_1.default({ [utils_1.preClass('tabs-tab')]: true, [utils_1.preClass('tabs-tab-active')]: selfActiveIndex === index, [utils_1.preClass('tabs-tab-disabled')]: disabled }); const tablCloseClass = classnames_1.default({ [utils_1.preClass('tabs-tab-close')]: true, [utils_1.preClass('tabs-tab-close-hide')]: selfActiveIndex === index }); const panelClass = classnames_1.default({ [utils_1.preClass('tabs-panel-active')]: selfActiveIndex === index, [utils_1.preClass('tabs-panel-inactive')]: selfActiveIndex !== index, [child.props.className]: utils_1.isExist(child.props.className) }); tabNodes.push(React.createElement("li", { key: index, className: tabClass, onClick: disabled || selfActiveIndex === index ? null : this.onTabClickHandler.bind(this, index) }, tab, closable && React.createElement("button", { className: tablCloseClass, onClick: this.onCloseHandler.bind(this, index) }, React.createElement(icon_1.default, { type: 'close' })))); panelNodes.push(React.cloneElement(child, { key: index, className: panelClass })); tabIndexs.push(index); }); this.tabIndexs = tabIndexs; if (appendable) { tabNodes.push(React.createElement("li", { key: 'append-tab-btn', className: utils_1.preClass('tabs-tab-append'), onClick: this.onTabAppendHandler }, React.createElement(icon_1.default, { type: 'plus' }))); } const preTabClass = classnames_1.default({ [utils_1.preClass('tabs-tab-pre')]: true, [utils_1.preClass('tabs-tab-disabled')]: navPageNumber === 1 }); const nextTabClass = classnames_1.default({ [utils_1.preClass('tabs-tab-next')]: true, [utils_1.preClass('tabs-tab-disabled')]: navPageNumber === navPageCount }); const tabsNav = (React.createElement("div", { className: utils_1.preClass('tabs-tab-container'), key: 'tabs-tab', ref: (ele) => { this.containerEle = ele; } }, showNavScrollBtn && React.createElement("span", { onClick: navPageNumber !== 1 ? this.preTabPage : null, className: preTabClass }, (tabPosition === 'top' || tabPosition === 'bottom') && React.createElement(icon_1.default, { type: 'arrow-left' }), (tabPosition === 'left' || tabPosition === 'right') && React.createElement(icon_1.default, { type: 'arrow-top' })), React.createElement("div", { className: utils_1.preClass('tabs-tab-scroll'), ref: (ele) => { this.navScrollEle = ele; } }, React.createElement("div", { className: utils_1.preClass('tabs-tab-wrapper'), style: navWrapperStyle, ref: (ele) => { this.navWrapperEle = ele; } }, React.createElement("ul", { className: utils_1.preClass('tabs-nav') }, tabNodes), mode === 'line' && React.createElement("i", { className: utils_1.preClass('tabs-tab-active-line'), style: activeLineStyle }))), showNavScrollBtn && React.createElement("span", { onClick: navPageNumber !== navPageCount ? this.nextTabPage : null, className: nextTabClass }, (tabPosition === 'top' || tabPosition === 'bottom') && React.createElement(icon_1.default, { type: 'arrow-right' }), (tabPosition === 'left' || tabPosition === 'right') && React.createElement(icon_1.default, { type: 'arrow-down' })))); const tabsPanel = (React.createElement("div", { className: utils_1.preClass('tabs-panel-container'), key: 'tabs-panel' }, React.createElement("div", { className: utils_1.preClass('tabs-panel-wrapper'), style: panelWrapperStyle, ref: (ele) => { this.panelWrapperEle = ele; } }, panelNodes))); renderNodes.push(tabsNav); if (tabPosition === 'top' || tabPosition === 'left') { renderNodes.push(tabsPanel); } else { renderNodes.unshift(tabsPanel); } return renderNodes; } render() { const { className, style, tabPosition, mode, tabJustified } = this.props; const componentClass = classnames_1.default({ [utils_1.preClass('tabs')]: true, [utils_1.preClass(`tabs-tab-${tabPosition}`)]: true, [utils_1.preClass(`tabs-${mode}`)]: mode, [utils_1.preClass('tabs-tab-justified')]: tabJustified, [className]: utils_1.isExist(className) }); return (React.createElement("section", { style: style, className: componentClass }, this.compileTabs())); } } Tabs.propTypes = { className: PropTypes.string, style: PropTypes.object, tabPosition: PropTypes.oneOf(['top', 'left', 'right', 'bottom']), tabJustified: PropTypes.bool, defaultActiveIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), mode: PropTypes.oneOf(['line', 'card']), closable: PropTypes.bool, appendable: PropTypes.bool, onChange: PropTypes.func, onBeforeClose: PropTypes.func, onClose: PropTypes.func, onAppend: PropTypes.func }; Tabs.defaultProps = { closable: false, tabJustified: false, appendable: false, tabPosition: 'top', mode: 'line' }; Tabs.Panel = tabs_panel_1.default; exports.default = Tabs;