UNPKG

@6thquake/react-material

Version:

React components that implement Google's Material Design.

318 lines (289 loc) 7.31 kB
import React, { Component } from 'react'; import { withStyles } from '../../styles'; import PropTypes from 'prop-types'; import classNames from 'classnames'; const styles = theme => ({ root: { zIndex: theme.zIndex.appBar, width: '100%', height: '60px', transition: 'background .3s cubic-bezier(0,0,.2,1) 0ms', background: '#333', position: 'relative', // background:'rgba(55,61,65,0)', '&:hover': { background: '#373d41' } }, subContainer: { position: 'absolute', color: '#fff', left: 0, right: 0, top: '60px', background: '#272b2e', transition: 'transform .3s', transform: 'scaleY(0)', transformOrigin: '0% 0%' }, lightSubContainer: { color: '#333', background: '#fff', boxShadow: theme.shadows[4], borderTop: `1px solid ${theme.palette.primary.main}`, top: '61px' }, topContainer: { height: '100%' }, topBar: { margin: 0, padding: 0, listStyle: 'none', height: '100%', display: 'inline-block', position: 'relative' }, line: { transition: 'all 200ms cubic-bezier(0.4, 0, 0.2, 1)', position: 'absolute', width: '0', background: theme.palette.primary.main, left: 0, bottom: 0, height: 3 }, left: { float: 'left', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-around' }, right: { float: 'right', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-around' }, lightRoot: { background: '#fff', '&:hover': { background: '#fff' }, boxShadow: theme.shadows[4] } }); class NavBar extends Component { constructor(...args) { super(...args); this.state = { line: { left: 0, width: 0 }, activeKey: null, subNavOpen: false, initMenu: { index: '', activeKey: '', line: { width: '', left: '' } }, selectedKeys: [] }; this.getNav = e => { const ele = e.target; if (ele.getAttribute('top-nav-title')) { this.setState({ activeKey: ele.getAttribute('event-key'), line: { width: ele.offsetWidth, left: ele.offsetLeft }, subNavOpen: true, hover: ele.getAttribute('event-key') }); } }; this.getInitMenu = (props = this.props) => { const { selectedKeys, children } = props; for (let i = 0, l = children.length; i < l; i++) { const key = children[i].key || `menu-${i}`; const index = selectedKeys.indexOf(key); if (index !== -1) { this.state.initMenu.index = i; this.state.initMenu.activeKey = key; const initMenu = this.state.topBar.childNodes[i]; this.state.initMenu.line.width = initMenu.offsetWidth; // + this.left.offsetWidth; this.state.initMenu.line.left = initMenu.offsetLeft; // todo this.init(); break; } } }; this.init = () => { this.setState({ activeKey: this.state.initMenu.activeKey, line: { width: this.state.initMenu.line.width, left: this.state.initMenu.line.left }, subNavOpen: false }); }; this.hiddenSubContainer = () => { this.setState({ subNavOpen: false }); }; } static getDerivedStateFromProps(nextProps, prevState) { const initMenu = { index: 0, line: {} }; const { selectedKeys, children } = nextProps; if (JSON.stringify(selectedKeys) === JSON.stringify(prevState.selectedKeys)) { return null; } if (prevState.topBar) { for (let i = 0, l = children.length; i < l; i++) { const key = children[i].key || `menu-${i}`; const index = selectedKeys.indexOf(key); if (index !== -1) { initMenu.index = i; initMenu.activeKey = key; const ele = prevState.topBar.childNodes[i]; initMenu.line.width = ele.offsetWidth; // + this.left.offsetWidth; initMenu.line.left = ele.offsetLeft; // todo return { activeKey: initMenu.activeKey, line: { width: initMenu.line.width, left: initMenu.line.left }, initMenu, selectedKeys }; } } } return null; } getChildContext() { return { onClick: this.props.onClick, theme: this.props.theme, hiddenSubContainer: this.hiddenSubContainer, selectedKeys: this.props.selectedKeys }; } renderNavItem(child, i) { const { selectedKeys } = this.props; const childKey = child.key || `menu-${i}`; const props = { index: i, eventKey: childKey, hover: this.state.hover === childKey, active: this.state.activeKey === childKey, selected: selectedKeys.indexOf(childKey) !== -1, selectedKeys, topNav: this }; return React.cloneElement(child, props); } componentDidMount() { this.getInitMenu(); } getSubContainerStyles() { return { transform: `scaleY(${this.state.subNavOpen ? 1 : 0})` }; } render() { const { children, classes, rightTools, leftTools, theme } = this.props; const className = classNames({ [classes.lightRoot]: theme === 'light' }, classes.root); const subContainerClassNames = classNames({ [classes.lightSubContainer]: theme === 'light' }, classes.subContainer); return React.createElement("div", { className: className, onMouseLeave: e => this.init(e) }, React.createElement("div", { className: classes.topContainer }, React.createElement("div", { className: classes.left, ref: e => this.left = e }, leftTools), React.createElement("ul", { className: classes.topBar, onMouseOver: e => this.getNav(e), ref: e => this.state.topBar = e }, React.Children.map(children, (c, i) => this.renderNavItem(c, i)), React.createElement("span", { ref: e => this.line = e, className: classes.line, style: this.state.line })), React.createElement("div", { className: classes.right }, rightTools)), React.createElement("div", { ref: e => this.subContainer = e, className: subContainerClassNames, style: this.getSubContainerStyles() })); } } process.env.NODE_ENV !== "production" ? NavBar.propTypes = { /** * 左侧工具区 */ leftTools: PropTypes.node, /** * 点击 NavBarItem 调用此函数 */ onClick: ({ key, keyPath }, NavBarItem, event) => null, /** * 右侧工具区 */ rightTools: PropTypes.node, /** * 选中的菜单项 key 数组 */ selectedKeys: PropTypes.array, /** * 主题颜色 */ theme: PropTypes.oneOf(['dark', 'light']) } : void 0; NavBar.defaultProps = { selectedKeys: [], theme: 'dark' }; NavBar.childContextTypes = { onClick: PropTypes.func, theme: PropTypes.string, hiddenSubContainer: PropTypes.func, selectedKeys: PropTypes.array }; export default withStyles(styles)(NavBar);