PagePathHeader.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import {
  2. useMemo, useState, useEffect, useCallback,
  3. } from 'react';
  4. import type { FC } from 'react';
  5. import type { IPagePopulatedToShowRevision } from '@growi/core';
  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 { TextInputForPageTitleAndPath } from './TextInputForPageTitleAndPath';
  11. import { usePagePathRenameHandler } from './page-header-utils';
  12. export type Props = {
  13. currentPage: IPagePopulatedToShowRevision
  14. inputValue: string
  15. onInputChange?: (inputText: string) => void
  16. }
  17. export const PagePathHeader: FC<Props> = (props) => {
  18. const { currentPage } = 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 onRenameFinish = () => {
  25. setRenameInputShown(false);
  26. };
  27. const onRenameFailure = () => {
  28. setRenameInputShown(true);
  29. };
  30. const pagePathRenameHandler = usePagePathRenameHandler(currentPage, onRenameFinish, onRenameFailure);
  31. const stateHandler = { isRenameInputShown, setRenameInputShown };
  32. const isOpened = PageSelectModalData?.isOpened ?? false;
  33. const isViewMode = editorMode === EditorMode.View;
  34. const isEditorMode = !isViewMode;
  35. const PagePath = useMemo(() => (
  36. <PagePathNav
  37. pageId={currentPage._id}
  38. pagePath={currentPagePath}
  39. isSingleLineMode={isEditorMode}
  40. />
  41. ), [currentPage._id, currentPagePath, isEditorMode]);
  42. const onClickEditButton = useCallback(() => {
  43. if (isRenameInputShown) {
  44. pagePathRenameHandler(props.inputValue);
  45. }
  46. else {
  47. setRenameInputShown(true);
  48. }
  49. }, [isRenameInputShown, pagePathRenameHandler, props.inputValue]);
  50. const buttonStyle = isButtonsShown ? '' : 'd-none';
  51. const clickOutSideHandler = useCallback((e) => {
  52. const container = document.getElementById('page-path-header');
  53. if (container && !container.contains(e.target)) {
  54. setRenameInputShown(false);
  55. }
  56. }, []);
  57. useEffect(() => {
  58. document.addEventListener('click', clickOutSideHandler);
  59. return () => {
  60. document.removeEventListener('click', clickOutSideHandler);
  61. };
  62. }, []);
  63. return (
  64. <div
  65. id="page-path-header"
  66. onMouseLeave={() => setButtonShown(false)}
  67. >
  68. <div className="row">
  69. <div
  70. className="col-4"
  71. onMouseEnter={() => setButtonShown(true)}
  72. >
  73. <TextInputForPageTitleAndPath
  74. currentPage={currentPage}
  75. stateHandler={stateHandler}
  76. inputValue={props.inputValue}
  77. CustomComponent={PagePath}
  78. onInputChange={props.onInputChange}
  79. />
  80. </div>
  81. <div className={`${buttonStyle} col-4 row`}>
  82. <div className="col-4">
  83. <button type="button" onClick={onClickEditButton}>
  84. {isRenameInputShown ? <span className="material-symbols-outlined">check_circle</span> : <span className="material-symbols-outlined">edit</span>}
  85. </button>
  86. </div>
  87. <div className="col-4">
  88. <button type="button" onClick={openPageSelectModal}>
  89. <span className="material-symbols-outlined">account_tree</span>
  90. </button>
  91. </div>
  92. </div>
  93. {isOpened
  94. && (
  95. <PageSelectModal />
  96. )}
  97. </div>
  98. </div>
  99. );
  100. };