PageTitle.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { FC, useState, useCallback } from 'react';
  2. import nodePath from 'path';
  3. import type { IPagePopulatedToShowRevision } from '@growi/core';
  4. import { pathUtils } from '@growi/core/dist/utils';
  5. import { useTranslation } from 'next-i18next';
  6. import { apiv3Put } from '~/client/util/apiv3-client';
  7. import { ValidationTarget } from '~/client/util/input-validator';
  8. import { toastSuccess, toastError } from '~/client/util/toastr';
  9. import { useSWRMUTxCurrentPage } from '~/stores/page';
  10. import { mutatePageTree, mutatePageList } from '~/stores/page-listing';
  11. import { mutateSearching } from '~/stores/search';
  12. import ClosableTextInput from '../Common/ClosableTextInput';
  13. type Props = {
  14. currentPagePath?: string,
  15. currentPage?: IPagePopulatedToShowRevision;
  16. }
  17. export const PageTitle: FC<Props> = (props) => {
  18. const { currentPagePath, currentPage } = props;
  19. const [isRenameInputShown, setRenameInputShown] = useState(false);
  20. const { t } = useTranslation();
  21. const { trigger: mutateCurrentPage } = useSWRMUTxCurrentPage();
  22. const onRenamed = useCallback((fromPath: string | undefined, toPath: string) => {
  23. mutatePageTree();
  24. mutateSearching();
  25. mutatePageList();
  26. if (currentPagePath === fromPath || currentPagePath === toPath) {
  27. mutateCurrentPage();
  28. }
  29. }, [currentPagePath, mutateCurrentPage]);
  30. if (currentPage == null) {
  31. return <></>;
  32. }
  33. const page = currentPage;
  34. const pageName = nodePath.basename(currentPagePath ?? '') || '/';
  35. const onClickPageNameHandler = () => {
  36. setRenameInputShown(true);
  37. };
  38. const onPressEnterForRenameHandler = async(inputText: string) => {
  39. const parentPath = pathUtils.addTrailingSlash(nodePath.dirname(page.path ?? ''));
  40. const newPagePath = nodePath.resolve(parentPath, inputText);
  41. if (newPagePath === page.path) {
  42. setRenameInputShown(false);
  43. return;
  44. }
  45. try {
  46. setRenameInputShown(false);
  47. await apiv3Put('/pages/rename', {
  48. pageId: page._id,
  49. revisionId: page.revision._id,
  50. newPagePath,
  51. });
  52. if (onRenamed != null) {
  53. onRenamed(page.path, newPagePath);
  54. }
  55. toastSuccess(t('renamed_pages', { path: page.path }));
  56. }
  57. catch (err) {
  58. setRenameInputShown(true);
  59. toastError(err);
  60. }
  61. };
  62. return (
  63. <>
  64. {isRenameInputShown ? (
  65. <div className="flex-fill">
  66. <ClosableTextInput
  67. value={pageName}
  68. placeholder={t('Input page name')}
  69. onClickOutside={() => { setRenameInputShown(false) }}
  70. onPressEnter={onPressEnterForRenameHandler}
  71. validationTarget={ValidationTarget.PAGE}
  72. />
  73. </div>
  74. ) : (
  75. <div onClick={onClickPageNameHandler}>{pageName}</div>
  76. )}
  77. </>
  78. );
  79. };