PagePathNavSticky.tsx 4.1 KB

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