Sidebar.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import React, {
  2. memo, useCallback, useEffect, useState,
  3. } from 'react';
  4. import dynamic from 'next/dynamic';
  5. import { scheduleToPut } from '~/client/services/user-ui-settings';
  6. import {
  7. useDrawerMode, useDrawerOpened,
  8. useCollapsedMode, useCollapsedContentsOpened,
  9. useCurrentProductNavWidth,
  10. useSidebarResizeDisabled,
  11. } from '~/stores/ui';
  12. import { ResizableArea } from './ResizableArea/ResizableArea';
  13. import { SidebarNav } from './SidebarNav';
  14. import styles from './Sidebar.module.scss';
  15. const SidebarContents = dynamic(() => import('./SidebarContents').then(mod => mod.SidebarContents), { ssr: false });
  16. const sidebarMinWidth = 240;
  17. const sidebarCollapsedWidth = 0;
  18. const sidebarFixedWidthInDrawerMode = 320;
  19. export const SidebarSubstance = memo((): JSX.Element => {
  20. const { data: isDrawerMode } = useDrawerMode();
  21. const { data: currentProductNavWidth, mutate: mutateProductNavWidth } = useCurrentProductNavWidth();
  22. const { data: isCollapsedMode, mutate: mutateCollapsedMode } = useCollapsedMode();
  23. const { mutate: mutateCollapsedContentsOpened } = useCollapsedContentsOpened();
  24. const { data: isResizeDisabled, mutate: mutateSidebarResizeDisabled } = useSidebarResizeDisabled();
  25. const [resizableAreaWidth, setResizableAreaWidth] = useState(0);
  26. const toggleDrawerMode = useCallback((bool) => {
  27. const isStateModified = isResizeDisabled !== bool;
  28. if (!isStateModified) {
  29. return;
  30. }
  31. // Drawer <-- Dock
  32. if (bool) {
  33. // disable resize
  34. mutateSidebarResizeDisabled(true, false);
  35. }
  36. // Drawer --> Dock
  37. else {
  38. // enable resize
  39. mutateSidebarResizeDisabled(false, false);
  40. }
  41. }, [isResizeDisabled, mutateSidebarResizeDisabled]);
  42. const resizeHandler = useCallback((newWidth: number) => {
  43. setResizableAreaWidth(newWidth);
  44. }, []);
  45. const resizeDoneHandler = useCallback((newWidth: number) => {
  46. mutateProductNavWidth(newWidth, false);
  47. scheduleToPut({ preferCollapsedModeByUser: false, currentProductNavWidth: newWidth });
  48. }, [mutateProductNavWidth]);
  49. const collapsedByResizableAreaHandler = useCallback(() => {
  50. mutateCollapsedMode(true);
  51. mutateCollapsedContentsOpened(false);
  52. mutateProductNavWidth(sidebarMinWidth, false);
  53. scheduleToPut({ preferCollapsedModeByUser: true, currentProductNavWidth: sidebarMinWidth });
  54. }, [mutateCollapsedContentsOpened, mutateCollapsedMode, mutateProductNavWidth]);
  55. useEffect(() => {
  56. toggleDrawerMode(isDrawerMode);
  57. }, [isDrawerMode, toggleDrawerMode]);
  58. // open/close resizable container when drawer mode
  59. useEffect(() => {
  60. if (isDrawerMode) {
  61. setResizableAreaWidth(sidebarFixedWidthInDrawerMode);
  62. }
  63. else if (isCollapsedMode) {
  64. setResizableAreaWidth(sidebarCollapsedWidth);
  65. }
  66. else {
  67. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  68. setResizableAreaWidth(currentProductNavWidth!);
  69. }
  70. }, [currentProductNavWidth, isCollapsedMode, isDrawerMode]);
  71. const disableResizing = isResizeDisabled || isDrawerMode || isCollapsedMode;
  72. return (
  73. <div className="data-layout-container">
  74. <div className="navigation transition-enabled">
  75. <div className="grw-navigation-wrap">
  76. <SidebarNav />
  77. <div className="sidebar-contents-container">
  78. <ResizableArea
  79. width={resizableAreaWidth}
  80. minWidth={sidebarMinWidth}
  81. disabled={disableResizing}
  82. onResize={resizeHandler}
  83. onResizeDone={resizeDoneHandler}
  84. onCollapsed={collapsedByResizableAreaHandler}
  85. >
  86. <SidebarContents />
  87. </ResizableArea>
  88. </div>
  89. </div>
  90. </div>
  91. </div>
  92. );
  93. });
  94. export const Sidebar = (): JSX.Element => {
  95. const { data: isDrawerMode } = useDrawerMode();
  96. const { data: isDrawerOpened } = useDrawerOpened();
  97. // css styles
  98. const grwSidebarClass = `grw-sidebar ${styles['grw-sidebar']}`;
  99. const sidebarModeClass = `${isDrawerMode ? 'grw-sidebar-drawer' : 'grw-sidebar-dock'}`;
  100. const isOpenClass = `${isDrawerOpened ? 'open' : ''}`;
  101. return (
  102. <div className={`${grwSidebarClass} ${sidebarModeClass} ${isOpenClass} d-print-none`} data-testid="grw-sidebar">
  103. <SidebarSubstance />
  104. </div>
  105. );
  106. };