PagePathNavSticky.tsx 4.2 KB

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