PagePathHeader.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import type { FC } from 'react';
  2. import { useMemo, useState, useEffect } from 'react';
  3. import nodePath from 'path';
  4. import type { IPagePopulatedToShowRevision } from '@growi/core';
  5. import { pathUtils } from '@growi/core/dist/utils';
  6. import { usePageSelectModal } from '~/stores/modal';
  7. import { EditorMode, useEditorMode } from '~/stores/ui';
  8. import { PagePathNav } from '../Common/PagePathNav';
  9. import { PageSelectModal } from '../PageSelectModal/PageSelectModal';
  10. import type { editedPagePathHandler } from './PageHeader';
  11. import { TextInputForPageTitleAndPath } from './TextInputForPageTitleAndPath';
  12. import { usePagePathRenameHandler } from './page-header-utils';
  13. export type Props = {
  14. currentPage: IPagePopulatedToShowRevision
  15. editedPagePathHandler: editedPagePathHandler
  16. }
  17. export const PagePathHeader: FC<Props> = (props) => {
  18. const { currentPage, editedPagePathHandler } = props;
  19. const currentPagePath = currentPage.path;
  20. const [isRenameInputShown, setRenameInputShown] = useState(false);
  21. const [isButtonsShown, setButtonShown] = useState(false);
  22. const { data: editorMode } = useEditorMode();
  23. const { data: PageSelectModalData, open: openPageSelectModal } = usePageSelectModal();
  24. const { editedPagePath, setEditedPagePath } = editedPagePathHandler;
  25. const pageTitle = nodePath.basename(currentPagePath ?? '') || '/';
  26. const parentPagePath = pathUtils.addHeadingSlash(nodePath.dirname(currentPage.path ?? ''));
  27. const onRenameFinish = () => {
  28. setRenameInputShown(false);
  29. };
  30. const onRenameFailure = () => {
  31. setRenameInputShown(true);
  32. };
  33. const pagePathRenameHandler = usePagePathRenameHandler(currentPage, onRenameFinish, onRenameFailure);
  34. const stateHandler = { isRenameInputShown, setRenameInputShown };
  35. const isOpened = PageSelectModalData?.isOpened ?? false;
  36. const isViewMode = editorMode === EditorMode.View;
  37. const isEditorMode = !isViewMode;
  38. const PagePath = useMemo(() => (
  39. <>
  40. {currentPagePath != null && (
  41. <PagePathNav
  42. pagePath={parentPagePath}
  43. isSingleLineMode={isEditorMode}
  44. />
  45. )}
  46. </>
  47. ), [currentPagePath, isEditorMode, parentPagePath]);
  48. const handleInputChange = (inputText: string) => {
  49. setEditedPagePath(inputText);
  50. };
  51. const handleEditButtonClick = () => {
  52. if (isRenameInputShown) {
  53. pagePathRenameHandler(editedPagePath);
  54. }
  55. else {
  56. setRenameInputShown(true);
  57. }
  58. };
  59. const buttonStyle = isButtonsShown ? '' : 'd-none';
  60. const clickOutSideHandler = (e) => {
  61. const container = document.getElementById('page-path-header');
  62. if (container && !container.contains(e.target)) {
  63. setRenameInputShown(false);
  64. }
  65. };
  66. useEffect(() => {
  67. document.addEventListener('click', clickOutSideHandler);
  68. return () => {
  69. document.removeEventListener('click', clickOutSideHandler);
  70. };
  71. }, []);
  72. return (
  73. <div
  74. id="page-path-header"
  75. onMouseLeave={() => setButtonShown(false)}
  76. >
  77. <div className="row">
  78. <div
  79. className="col-4"
  80. onMouseEnter={() => setButtonShown(true)}
  81. >
  82. <TextInputForPageTitleAndPath
  83. currentPage={currentPage}
  84. stateHandler={stateHandler}
  85. editedPagePathHandler={editedPagePathHandler}
  86. inputValue={editedPagePath}
  87. CustomComponent={PagePath}
  88. handleInputChange={handleInputChange}
  89. />
  90. </div>
  91. <div className={`${buttonStyle} col-4 row`}>
  92. <div className="col-4">
  93. <button type="button" onClick={handleEditButtonClick}>
  94. {isRenameInputShown ? <span className="material-symbols-outlined">check_circle</span> : <span className="material-symbols-outlined">edit</span>}
  95. </button>
  96. </div>
  97. <div className="col-4">
  98. <button type="button" onClick={openPageSelectModal}>
  99. <span className="material-symbols-outlined">account_tree</span>
  100. </button>
  101. </div>
  102. </div>
  103. {isOpened
  104. && (
  105. <PageSelectModal />
  106. )}
  107. </div>
  108. </div>
  109. );
  110. };