PagePathNavSticky.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import {
  2. useEffect,
  3. useMemo,
  4. useRef,
  5. useState,
  6. } from 'react';
  7. import { DevidedPagePath } from '@growi/core/dist/models';
  8. import { pagePathUtils } from '@growi/core/dist/utils';
  9. import Sticky from 'react-stickynode';
  10. import LinkedPagePath from '~/models/linked-page-path';
  11. import {
  12. usePageControlsX, useCurrentProductNavWidth, useSidebarMode,
  13. } from '~/stores/ui';
  14. import { PagePathHierarchicalLink } from '../../../components/Common/PagePathHierarchicalLink';
  15. import type { PagePathNavLayoutProps } from '../../../components/Common/PagePathNav';
  16. import { PagePathNav, PagePathNavLayout, Separator } from '../../../components/Common/PagePathNav';
  17. import { CollapsedParentsDropdown } from './CollapsedParentsDropdown';
  18. import styles from './PagePathNavSticky.module.scss';
  19. const moduleClass = styles['grw-page-path-nav-sticky'];
  20. const { isTrashPage } = pagePathUtils;
  21. export const PagePathNavSticky = (props: PagePathNavLayoutProps): JSX.Element => {
  22. const { pagePath } = props;
  23. const { data: pageControlsX } = usePageControlsX();
  24. const { data: sidebarWidth } = useCurrentProductNavWidth();
  25. const { data: sidebarMode } = useSidebarMode();
  26. const pagePathNavRef = useRef<HTMLDivElement>(null);
  27. const [navMaxWidth, setNavMaxWidth] = useState<number | undefined>();
  28. useEffect(() => {
  29. if (pageControlsX == null || pagePathNavRef.current == null || sidebarWidth == null) {
  30. return;
  31. }
  32. setNavMaxWidth(pageControlsX - pagePathNavRef.current.getBoundingClientRect().x - 10);
  33. }, [pageControlsX, pagePathNavRef, sidebarWidth]);
  34. useEffect(() => {
  35. // wait for the end of the animation of the opening and closing of the sidebar
  36. const timeout = setTimeout(() => {
  37. if (pageControlsX == null || pagePathNavRef.current == null || sidebarMode == null) {
  38. return;
  39. }
  40. setNavMaxWidth(pageControlsX - pagePathNavRef.current.getBoundingClientRect().x - 10);
  41. }, 200);
  42. return () => {
  43. clearTimeout(timeout);
  44. };
  45. }, [pageControlsX, pagePathNavRef, sidebarMode]);
  46. const latterLink = useMemo(() => {
  47. const dPagePath = new DevidedPagePath(pagePath, false, true);
  48. const isInTrash = isTrashPage(pagePath);
  49. const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
  50. const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
  51. // not collapsed
  52. if (dPagePath.isRoot || dPagePath.isFormerRoot) {
  53. const linkedPagePath = new LinkedPagePath(pagePath);
  54. return <PagePathHierarchicalLink linkedPagePath={linkedPagePath} isInTrash={isInTrash} />;
  55. }
  56. // collapsed
  57. return (
  58. <>
  59. <CollapsedParentsDropdown linkedPagePath={linkedPagePathFormer} />
  60. <Separator />
  61. <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.former} isInTrash={isInTrash} />
  62. </>
  63. );
  64. }, [pagePath]);
  65. return (
  66. // Controlling pointer-events
  67. // 1. disable pointer-events with 'pe-none'
  68. <div ref={pagePathNavRef}>
  69. <Sticky className={`${moduleClass} mb-4`} innerClass="pe-none" innerActiveClass="active mt-1">
  70. {({ status }) => {
  71. const isCollapseParents = status === Sticky.STATUS_FIXED;
  72. return (
  73. // Controlling pointer-events
  74. // 2. enable pointer-events with 'pe-auto' only against the children
  75. // which width is minimized by 'd-inline-block'
  76. //
  77. <div className="d-inline-block pe-auto">
  78. { !isCollapseParents && <PagePathNav {...props} /> }
  79. { isCollapseParents && (
  80. <PagePathNavLayout
  81. {...props}
  82. latterLink={latterLink}
  83. latterLinkClassName="fs-3 text-truncate"
  84. maxWidth={isCollapseParents ? navMaxWidth : undefined}
  85. />
  86. ) }
  87. </div>
  88. );
  89. }}
  90. </Sticky>
  91. </div>
  92. );
  93. };
  94. PagePathNavSticky.displayName = 'PagePathNavSticky';