UNPKG

wix-style-react

Version:
366 lines • 18.1 kB
import React, { Component, cloneElement } from 'react'; import PropTypes from 'prop-types'; import { st, classes } from './Sidebar.st.css'; import { SidebarItem } from './SidebarItem'; import { SidebarPersistentHeader } from './SidebarPersistentHeader'; import { SidebarPersistentFooter } from './SidebarPersistentFooter'; import { SidebarBackButton } from './SidebarBackButton'; import { SidebarContext } from './SidebarAPI'; import { dataHooks, sidebarSkins } from './constants'; import { SidebarContentWrapper } from './SidebarContentWrapper'; import { WixStyleReactContext } from '../WixStyleReactProvider/context'; const CollapsibeInnerMenuOpenChildren = props => { const { children, waitForOtherMenuToClose, skin, isAlreadyOpen } = props; return (React.createElement("div", { "data-hook": "open-inner-menu-children", className: isAlreadyOpen ? '' : st(waitForOtherMenuToClose ? classes.innerMenuWrapperInToPlaceAfterClosingOther : classes.innerMenuWrapperInToPlace) }, React.createElement(SidebarContentWrapper, { containerClasses: st(classes.innerMenu), containerDataHook: dataHooks.drivenInChildren, skin: skin }, children))); }; const CollapsibeInnerMenuCloseChildren = props => { const { children } = props; return (React.createElement("div", { "data-hook": "closed-inner-menu-children", className: st(classes.innerMenuWrapperOutOfPlace) }, React.createElement("div", { className: st(classes.innerMenu), "data-hook": dataHooks.drivenOutChildren }, children))); }; const CollapsibeInnerMenuCloseParent = props => { const { isAlreadyClosed, children } = props; const closeParentClass = st(isAlreadyClosed ? classes.closedInnerMenu : classes.closingInnerMenu); return (React.createElement("div", { "data-hook": "closed-inner-menu", className: closeParentClass }, children)); }; const CollapsibeInnerMenuOpenParent = props => { const { isAlreadyOpen, children, waitForOtherMenuToClose } = props; const openParentClass = waitForOtherMenuToClose ? st(classes.openingInnerMenuAfterClosingOther) : st(isAlreadyOpen ? classes.openInnerMenu : classes.openingInnerMenu); return (React.createElement("div", { "data-hook": "open-inner-menu", className: openParentClass }, children)); }; /** A sidebar navigation component */ class Sidebar extends Component { constructor(props) { super(props); this._getInnerMenuCollapsibleState = options => { const { menuToClose, menuToOpen, selected } = options; let expandedInnerMenu = undefined; const openMenuChildren = this.itemKeyToChildren[menuToOpen] && (this.itemKeyToChildren[menuToOpen].children || []); const closeMenuChildren = this.itemKeyToChildren[menuToClose] && (this.itemKeyToChildren[menuToClose].children || []); const collapsibleOnScreenChildren = this.firstLevelItems.reduce((accumalator, child) => { const shouldExpand = menuToOpen && child.props.itemKey === menuToOpen && openMenuChildren.length > 0; const shouldCollapse = this.state.expandedInnerMenu && menuToClose && closeMenuChildren.length > 0 && child.props.itemKey === menuToClose && this.state.expandedInnerMenu === menuToClose; const waitForOtherMenuToClose = this.state.expandedInnerMenu && menuToClose && this.state.expandedInnerMenu === menuToClose && menuToClose !== menuToOpen; if (shouldExpand) { expandedInnerMenu = menuToOpen; return [ ...accumalator, React.createElement(CollapsibeInnerMenuOpenParent, { key: `open-parent-${menuToOpen}`, isAlreadyOpen: this._isAlreadyOpen(menuToOpen), waitForOtherMenuToClose: waitForOtherMenuToClose }, child), React.createElement(CollapsibeInnerMenuOpenChildren, { key: `open-children-${menuToOpen}`, waitForOtherMenuToClose: waitForOtherMenuToClose, skin: this.props.skin, isAlreadyOpen: this._isAlreadyOpen(menuToOpen) }, openMenuChildren), ]; } if (shouldCollapse) { return [ ...accumalator, React.createElement(CollapsibeInnerMenuCloseParent, { key: `closed-parent-${menuToClose}`, isAlreadyClosed: this._isAlreadyClosed(selected, menuToClose) }, child), React.createElement(CollapsibeInnerMenuCloseChildren, { key: `closed-children-${menuToClose}` }, closeMenuChildren), ]; } return [...accumalator, child]; }, []); return { collapsibleOnScreenChildren, expandedInnerMenu, }; }; this._shouldCollapseInnerMenu = itemKey => this.state.expandedInnerMenu === itemKey && ((itemKey === this.state.selectedKey && this.state.expandedInnerMenu) || itemKey !== this.state.selectedKey); this._shouldExpandInnerMenu = (parentKey, itemKey) => (parentKey === this.state.lastSelectedKey && !this.state.expandedInnerMenu) || this.itemKeyToParentKey[itemKey] === parentKey || parentKey !== this.state.lastSelectedKey; this._navigateTo = itemKey => { if (this._isChild(itemKey)) { this._selectItem(itemKey); this.sidebarContext = this._getSidebarContext(); return; } if (this._isParent(itemKey)) { this._openInnerMenu(itemKey); this.sidebarContext = this._getSidebarContext(); return; } if (itemKey) { this._closeInnerMenu(itemKey); this.sidebarContext = this._getSidebarContext(); return; } }; this._getSidebarContext = () => { return { itemClicked: this._navigateTo, backClicked: () => { this._closeInnerMenu(); this.sidebarContext = this._getSidebarContext(); }, getSelectedKey: () => this.state.selectedKey, getSkin: () => this.props.skin, getIsMenuExpanded: () => this.state.expandedInnerMenu, }; }; this.sidebarContext = this._getSidebarContext(); this._getInnerChildSelectedState = itemKey => { const collapsibleInnerMenuState = this._getInnerMenuCollapsibleState({ menuToClose: this.state.lastSelectedKey, menuToOpen: this.itemKeyToParentKey[itemKey], selected: itemKey, }); if (this.itemKeyToParentKey[itemKey] !== this.state.lastSelectedKey) { return { drivenInChildren: this.itemKeyToChildren[this.itemKeyToParentKey[itemKey]].children, selectedKey: itemKey, lastSelectedKey: this.itemKeyToParentKey[itemKey], ...collapsibleInnerMenuState, }; } else { return { ...collapsibleInnerMenuState, selectedKey: itemKey, }; } }; this._getInnerMenuOpenState = itemKey => { const { children, selectedKey } = this.itemKeyToChildren[itemKey]; const selected = this.itemKeyToParentKey[this.state.lastSelectedKey] === itemKey ? this.state.lastSelectedKey : selectedKey; const parentKey = this.itemKeyToParentKey[selected]; const parentItemKeyToOpen = this._getItemToOpenKey(parentKey, itemKey); const parentItemKeyToClose = this._getItemToCloseKey(this.state.lastSelectedKey); return { ...this._getInnerMenuCollapsibleState({ menuToClose: parentItemKeyToClose, menuToOpen: parentItemKeyToOpen, selected, }), drivenInChildren: children, drivenOutChildren: [], selectedKey: selected, lastSelectedKey: itemKey, }; }; this._getItemToCloseKey = itemKey => { if (this._shouldCollapseInnerMenu(itemKey)) { return this.itemKeyToParentKey[itemKey] || itemKey; } if (this._shouldCollapseInnerMenu(this.state.lastSelectedKey)) { return (this.itemKeyToParentKey[this.state.lastSelectedKey] || this.state.lastSelectedKey); } return undefined; }; this._getItemToOpenKey = (parentKey, itemKey) => this._shouldExpandInnerMenu(parentKey, itemKey) ? parentKey : undefined; this._getInnerMenuCloseState = (itemKey, updateCollapsibleOnlyOnChange) => { const selectedKey = this.state.lastSelectedKey || itemKey; const parentItemKeyToClose = this._getItemToCloseKey(selectedKey); return { ...this._getInnerMenuCollapsibleState({ menuToClose: parentItemKeyToClose, selected: selectedKey, }), selectedKey: itemKey || this.state.lastSelectedKey, lastSelectedKey: selectedKey, drivenInChildren: [], drivenOutChildren: this.state.drivenInChildren, }; }; this._getItemWithKey = (item, itemKey) => item.props.itemKey ? item : cloneElement(item, { ...item.props, itemKey }); this._getChildrenWithKeys = child => (child.props.innerMenu && child.props.innerMenu.map((innerChild, index) => this._getItemWithKey(innerChild, child.props.itemKey + index))) || []; this._isParent = itemKey => this.itemKeyToChildren[itemKey]; this._isChild = itemKey => this.itemKeyToParentKey[itemKey]; this._isAlreadyOpen = menuToOpen => this.state.lastSelectedKey === menuToOpen && this.itemKeyToParentKey[this.props.selectedKey] === menuToOpen && this.state.expandedInnerMenu === menuToOpen; this._isAlreadyClosed = (selected, menuToClose) => this.itemKeyToParentKey[selected] !== menuToClose; this._selectItem = itemKey => this.setState(this._getInnerChildSelectedState(itemKey)); this._openInnerMenu = itemKey => this.setState(this._getInnerMenuOpenState(itemKey)); this._closeInnerMenu = itemKey => this.setState(this._getInnerMenuCloseState(itemKey)); this.itemKeyToChildren = {}; this.itemKeyToParentKey = {}; this.firstLevelItems = []; this.state = { persistentTopChildren: [], drivenOutChildren: [], onScreenChildren: [], collapsibleOnScreenChildren: [], drivenInChildren: [], persistentBottomChildren: [], selectedKey: '', lastSelectedKey: '', expandedInnerMenu: undefined, }; } UNSAFE_componentWillMount() { this._setInnerMenus(this.props); } UNSAFE_componentWillReceiveProps(props) { this._setInnerMenus(props); } _setInnerMenus(props) { const persistentTopChildren = []; const persistentBottomChildren = []; const onScreenChildren = []; const findEnabledChild = item => item.props.innerMenu && item.props.innerMenu.find(c => c.type === SidebarItem && !c.props.disable); const handleChild = child => { if (child.type === SidebarItem) { const enabledChild = findEnabledChild(child); const innerChildrenWithKeys = this._getChildrenWithKeys(child); this.itemKeyToChildren[child.props.itemKey] = { selectedKey: enabledChild ? enabledChild.props.itemKey : child.props.itemKey, children: innerChildrenWithKeys, }; if (child.props.innerMenu) { innerChildrenWithKeys.forEach(innerChild => { if (innerChild.type !== SidebarBackButton) { this.itemKeyToParentKey[innerChild.props.itemKey] = child.props.itemKey; } }); } onScreenChildren.push(child); } else if (child.type === SidebarPersistentHeader) { persistentTopChildren.push(child); } else if (child.type === SidebarPersistentFooter) { persistentBottomChildren.push(child); } else { onScreenChildren.push(child); } }; if (props.children) { if (props.children.length) { props.children.forEach(child => { if (child) { if (child.length > 0) { child.forEach(handleChild); } else { handleChild(child); } } }); } else { handleChild(props.children); } } this.firstLevelItems = onScreenChildren.slice(); const newState = { persistentTopChildren, persistentBottomChildren, onScreenChildren, selectedKey: props.selectedKey, }; const selectedItemParentKey = this.itemKeyToParentKey[props.selectedKey]; if (selectedItemParentKey) { this.setState({ ...newState, drivenInChildren: this.itemKeyToChildren[selectedItemParentKey].children, lastSelectedKey: selectedItemParentKey, ...this._getInnerMenuCollapsibleState({ menuToClose: this.itemKeyToParentKey[this.props.selectedKey] || this.props.selctedKey, menuToOpen: selectedItemParentKey, selected: props.selectedKey, }), }); } else { const updateCollapsibleOnlyOnChange = true; this.setState({ ...newState, drivenInChildren: [], ...this._getInnerMenuCloseState(props.selectedKey, updateCollapsibleOnlyOnChange), }); } } render() { const { isHidden, skin } = this.props; const css = { ...classes, ...this.props.classNames }; const sliderClasses = st(css.slider, { skin, }, this.state.drivenInChildren.length !== 0 && css.sliderOutToLeft, this.state.drivenInChildren.length === 0 && this.state.drivenOutChildren.length !== 0 && css.sliderInFromLeft); const collapsibleSliderClasses = st(css.slider, { skin, }); const sliderOutToRightClasses = st(css.slider, { skin, }, !this.props.isHidden && css.sliderOutToRight); const sliderInFromRightClasses = st(css.slider, { skin, }, !this.props.isHidden && css.sliderInFromRight); const rootClasses = st(css.sideBar || classes.root, { hidden: isHidden, skin, }); return (React.createElement(WixStyleReactContext.Consumer, null, ({ sidebarExperimentCollapsible }) => { return (React.createElement(SidebarContext.Provider, { value: this.sidebarContext }, React.createElement("div", { className: rootClasses, "data-hook": this.props.dataHook, "data-skin": skin }, this.state.persistentTopChildren, React.createElement("div", { className: st(css.content) }, !sidebarExperimentCollapsible && this.state.drivenInChildren.length === 0 && this.state.drivenOutChildren.length !== 0 && (React.createElement("div", { className: sliderOutToRightClasses, "data-hook": dataHooks.drivenOutChildren }, this.state.drivenOutChildren)), React.createElement(SidebarContentWrapper, { containerClasses: sidebarExperimentCollapsible ? collapsibleSliderClasses : sliderClasses, skin: skin }, sidebarExperimentCollapsible ? this.state.collapsibleOnScreenChildren : this.state.onScreenChildren), !sidebarExperimentCollapsible && this.state.drivenInChildren.length !== 0 && (React.createElement(SidebarContentWrapper, { key: "collapsiblle", containerClasses: sliderInFromRightClasses, containerDataHook: dataHooks.drivenInChildren, skin: skin }, this.state.drivenInChildren))), this.state.persistentBottomChildren))); })); } } Sidebar.displayName = 'Sidebar'; Sidebar.Item = SidebarItem; Sidebar.PersistentHeader = SidebarPersistentHeader; Sidebar.PersistentFooter = SidebarPersistentFooter; Sidebar.BackButton = SidebarBackButton; Sidebar.propTypes = { /** classNames overrides */ classNames: PropTypes.string, /** The dataHook of the Sidebar */ dataHook: PropTypes.string, /** Sidebar menu children */ children: PropTypes.node, /** Sidebar indicator for animating out or in */ isHidden: PropTypes.bool, /** Sets the skin of the Sidebar */ skin: PropTypes.oneOf(['dark', 'light']), }; Sidebar.defaultProps = { skin: sidebarSkins.dark, isHidden: false, }; export default Sidebar; //# sourceMappingURL=Sidebar.js.map