| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- import React from 'react';
- import PropTypes from 'prop-types';
- import {
- withNavigationUIController,
- LayoutManager,
- NavigationProvider,
- ThemeProvider,
- } from '@atlaskit/navigation-next';
- import { withUnstatedContainers } from './UnstatedUtils';
- import AppContainer from '../services/AppContainer';
- import NavigationContainer from '../services/NavigationContainer';
- import DrawerToggler from './Navbar/DrawerToggler';
- import SidebarNav from './Sidebar/SidebarNav';
- import SidebarContents from './Sidebar/SidebarContents';
- import StickyStretchableScroller from './StickyStretchableScroller';
- const sidebarDefaultWidth = 320;
- class Sidebar extends React.Component {
- static propTypes = {
- appContainer: PropTypes.instanceOf(AppContainer).isRequired,
- navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
- navigationUIController: PropTypes.any.isRequired,
- isDrawerModeOnInit: PropTypes.bool,
- };
- componentWillMount() {
- this.hackUIController();
- }
- componentDidUpdate(prevProps, prevState) {
- this.toggleDrawerMode(this.isDrawerMode);
- }
- /**
- * hack and override UIController.storeState
- *
- * Since UIController is an unstated container, setState() in storeState method should be awaited before writing to cache.
- */
- hackUIController() {
- const { navigationUIController } = this.props;
- // see: @atlaskit/navigation-next/dist/esm/ui-controller/UIController.js
- const orgStoreState = navigationUIController.storeState;
- navigationUIController.storeState = async(state) => {
- await navigationUIController.setState(state);
- orgStoreState(state);
- };
- }
- /**
- * return whether drawer mode or not
- */
- get isDrawerMode() {
- let isDrawerMode = this.props.navigationContainer.state.isDrawerMode;
- if (isDrawerMode == null) {
- isDrawerMode = this.props.isDrawerModeOnInit;
- }
- return isDrawerMode;
- }
- toggleDrawerMode(bool) {
- const { navigationUIController } = this.props;
- const isStateModified = navigationUIController.state.isResizeDisabled !== bool;
- if (!isStateModified) {
- return;
- }
- // Drawer <-- Dock
- if (bool) {
- // cache state
- this.sidebarCollapsedCached = navigationUIController.state.isCollapsed;
- this.sidebarWidthCached = navigationUIController.state.productNavWidth;
- // clear transition temporary
- if (this.sidebarCollapsedCached) {
- this.addCssClassTemporary('grw-sidebar-supress-transitions-to-drawer');
- }
- navigationUIController.disableResize();
- // fix width
- navigationUIController.setState({ productNavWidth: sidebarDefaultWidth });
- }
- // Drawer --> Dock
- else {
- // clear transition temporary
- if (this.sidebarCollapsedCached) {
- this.addCssClassTemporary('grw-sidebar-supress-transitions-to-dock');
- }
- navigationUIController.enableResize();
- // restore width
- if (this.sidebarWidthCached != null) {
- navigationUIController.setState({ productNavWidth: this.sidebarWidthCached });
- }
- }
- }
- get sidebarElem() {
- return document.querySelector('.grw-sidebar');
- }
- addCssClassTemporary(className) {
- // clear
- this.sidebarElem.classList.add(className);
- // restore after 300ms
- setTimeout(() => {
- this.sidebarElem.classList.remove(className);
- }, 300);
- }
- backdropClickedHandler = () => {
- const { navigationContainer } = this.props;
- navigationContainer.toggleDrawer();
- }
- itemSelectedHandler = (contentsId) => {
- const { navigationContainer, navigationUIController } = this.props;
- const { sidebarContentsId } = navigationContainer.state;
- // already selected
- if (sidebarContentsId === contentsId) {
- navigationUIController.toggleCollapse();
- }
- // switch and expand
- else {
- navigationUIController.expand();
- }
- }
- calcViewHeight() {
- const scrollTargetElem = document.querySelector('#grw-sidebar-contents-scroll-target');
- return window.innerHeight - scrollTargetElem.getBoundingClientRect().top;
- }
- renderGlobalNavigation = () => (
- <SidebarNav onItemSelected={this.itemSelectedHandler} />
- );
- renderSidebarContents = () => {
- const scrollTargetSelector = '#grw-sidebar-contents-scroll-target';
- return (
- <>
- <StickyStretchableScroller
- scrollTargetSelector={scrollTargetSelector}
- contentsElemSelector="#grw-sidebar-content-container"
- stickyElemSelector=".grw-sidebar"
- calcViewHeightFunc={this.calcViewHeight}
- />
- <div id="grw-sidebar-contents-scroll-target">
- <div id="grw-sidebar-content-container">
- <SidebarContents
- isSharedUser={this.props.appContainer.isSharedUser}
- />
- </div>
- </div>
- <DrawerToggler iconClass="icon-arrow-left" />
- </>
- );
- };
- render() {
- const { isDrawerOpened } = this.props.navigationContainer.state;
- return (
- <>
- <div className={`grw-sidebar d-print-none ${this.isDrawerMode ? 'grw-sidebar-drawer' : ''} ${isDrawerOpened ? 'open' : ''}`}>
- <ThemeProvider
- theme={theme => ({
- ...theme,
- context: 'product',
- })}
- >
- <LayoutManager
- globalNavigation={this.renderGlobalNavigation}
- productNavigation={() => null}
- containerNavigation={this.renderSidebarContents}
- experimental_hideNavVisuallyOnCollapse
- experimental_flyoutOnHover
- experimental_alternateFlyoutBehaviour
- experimental_fullWidthFlyout
- shouldHideGlobalNavShadow
- showContextualNavigation
- >
- </LayoutManager>
- </ThemeProvider>
- </div>
- { isDrawerOpened && (
- <div className="grw-sidebar-backdrop modal-backdrop show" onClick={this.backdropClickedHandler}></div>
- ) }
- </>
- );
- }
- }
- const SidebarWithNavigationUIController = withNavigationUIController(Sidebar);
- /**
- * Wrapper component for using unstated
- */
- const SidebarWithNavigation = (props) => {
- const { preferDrawerModeByUser: isDrawerModeOnInit } = props.navigationContainer.state;
- const initUICForDrawerMode = isDrawerModeOnInit
- // generate initialUIController for Drawer mode
- ? {
- isCollapsed: false,
- isResizeDisabled: true,
- productNavWidth: sidebarDefaultWidth,
- }
- // set undefined (should be initialized by cache)
- : undefined;
- return (
- <NavigationProvider initialUIController={initUICForDrawerMode}>
- <SidebarWithNavigationUIController {...props} isDrawerModeOnInit={isDrawerModeOnInit} />
- </NavigationProvider>
- );
- };
- SidebarWithNavigation.propTypes = {
- appContainer: PropTypes.instanceOf(AppContainer).isRequired,
- navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
- };
- export default withUnstatedContainers(SidebarWithNavigation, [AppContainer, NavigationContainer]);
|