PageTitleHeader.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import type { FC } from 'react';
  2. import { useState, useCallback } from 'react';
  3. import nodePath from 'path';
  4. import { pathUtils } from '@growi/core/dist/utils';
  5. import { useTranslation } from 'next-i18next';
  6. import { ValidationTarget } from '~/client/util/input-validator';
  7. import ClosableTextInput from '../Common/ClosableTextInput';
  8. import type { Props } from './PagePathHeader';
  9. import { usePagePathRenameHandler } from './page-header-utils';
  10. export const PageTitleHeader: FC<Props> = (props) => {
  11. const { currentPage } = props;
  12. const currentPagePath = currentPage.path;
  13. const pageTitle = nodePath.basename(currentPagePath) || '/';
  14. const [isRenameInputShown, setRenameInputShown] = useState(false);
  15. const [editedPagePath, setEditedPagePath] = useState(currentPagePath);
  16. const pagePathRenameHandler = usePagePathRenameHandler(currentPage);
  17. const { t } = useTranslation();
  18. const onRenameFinish = useCallback(() => {
  19. setRenameInputShown(false);
  20. }, []);
  21. const onRenameFailure = useCallback(() => {
  22. setRenameInputShown(true);
  23. }, []);
  24. const onInputChange = useCallback((inputText: string) => {
  25. const parentPagePath = pathUtils.addTrailingSlash(nodePath.dirname(currentPage.path));
  26. const newPagePath = nodePath.resolve(parentPagePath, inputText);
  27. setEditedPagePath(newPagePath);
  28. }, [currentPage?.path, setEditedPagePath]);
  29. const onPressEnter = useCallback(() => {
  30. pagePathRenameHandler(editedPagePath, onRenameFinish, onRenameFailure);
  31. }, [editedPagePath, onRenameFailure, onRenameFinish, pagePathRenameHandler]);
  32. const onPressEscape = useCallback(() => {
  33. setEditedPagePath(currentPagePath);
  34. setRenameInputShown(false);
  35. }, [currentPagePath]);
  36. const onClickButton = useCallback(() => {
  37. pagePathRenameHandler(editedPagePath, onRenameFinish, onRenameFailure);
  38. }, [editedPagePath, onRenameFailure, onRenameFinish, pagePathRenameHandler]);
  39. const onClickPageTitle = useCallback(() => {
  40. setEditedPagePath(currentPagePath);
  41. setRenameInputShown(true);
  42. }, [currentPagePath]);
  43. const PageTitle = <div onClick={onClickPageTitle}>{pageTitle}</div>;
  44. const buttonStyle = isRenameInputShown ? '' : 'd-none';
  45. return (
  46. <div className="row">
  47. <div className="col-4">
  48. {isRenameInputShown ? (
  49. <div className="flex-fill">
  50. <ClosableTextInput
  51. value={pageTitle}
  52. placeholder={t('Input page name')}
  53. onPressEnter={onPressEnter}
  54. onPressEscape={onPressEscape}
  55. validationTarget={ValidationTarget.PAGE}
  56. handleInputChange={onInputChange}
  57. />
  58. </div>
  59. ) : (
  60. <>{ PageTitle }</>
  61. )}
  62. </div>
  63. <div className={`col-4 ${buttonStyle}`}>
  64. <button type="button" onClick={onClickButton}>
  65. <span className="material-symbols-outlined">check_circle</span>
  66. </button>
  67. </div>
  68. </div>
  69. );
  70. };