SimplifiedPageTreeItem.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import type { FC } from 'react';
  2. import { useCallback } from 'react';
  3. import path from 'path';
  4. import type { IPageToDeleteWithMeta } from '@growi/core/dist/interfaces';
  5. import { getIdStringForRef } from '@growi/core/dist/interfaces';
  6. import { pathUtils } from '@growi/core/dist/utils';
  7. import { useTranslation } from 'next-i18next';
  8. import { useRouter } from 'next/router';
  9. import { toastSuccess } from '~/client/util/toastr';
  10. import {
  11. CREATING_PAGE_VIRTUAL_ID,
  12. ROOT_PAGE_VIRTUAL_ID, usePageTreeInformationUpdate, usePageRename, usePageCreate,
  13. } from '~/features/page-tree';
  14. import type { IPageForItem } from '~/interfaces/page';
  15. import type { OnDeletedFunction, OnDuplicatedFunction } from '~/interfaces/ui';
  16. import { useCurrentPagePath, useFetchCurrentPage } from '~/states/page';
  17. import { usePageDeleteModalActions } from '~/states/ui/modal/page-delete';
  18. import type { IPageForPageDuplicateModal } from '~/states/ui/modal/page-duplicate';
  19. import { usePageDuplicateModalActions } from '~/states/ui/modal/page-duplicate';
  20. import { mutateAllPageInfo } from '~/stores/page';
  21. import { mutatePageTree, mutatePageList } from '~/stores/page-listing';
  22. import { mutateSearching } from '~/stores/search';
  23. import type { TreeItemProps } from '../../TreeItem';
  24. import { TreeItemLayout } from '../../TreeItem';
  25. import { CountBadgeForPageTreeItem } from './CountBadgeForPageTreeItem';
  26. import { usePageItemControl } from './use-page-item-control';
  27. import styles from './PageTreeItem.module.scss';
  28. const moduleClass = styles['page-tree-item'] ?? '';
  29. export const simplifiedPageTreeItemSize = 40; // in px
  30. export const SimplifiedPageTreeItem: FC<TreeItemProps> = ({
  31. item,
  32. targetPath,
  33. targetPathOrId,
  34. isWipPageShown,
  35. isEnableActions = false,
  36. isReadOnlyUser = false,
  37. onToggle,
  38. }) => {
  39. const { t } = useTranslation();
  40. const router = useRouter();
  41. const itemData = item.getItemData();
  42. const currentPagePath = useCurrentPagePath();
  43. const { fetchCurrentPage } = useFetchCurrentPage();
  44. const { open: openDuplicateModal } = usePageDuplicateModalActions();
  45. const { open: openDeleteModal } = usePageDeleteModalActions();
  46. const { notifyUpdateItems } = usePageTreeInformationUpdate();
  47. const onClickDuplicateMenuItem = useCallback((page: IPageForPageDuplicateModal) => {
  48. const duplicatedHandler: OnDuplicatedFunction = (fromPath) => {
  49. toastSuccess(t('duplicated_pages', { fromPath }));
  50. mutatePageTree();
  51. mutateSearching();
  52. mutatePageList();
  53. // Notify headless-tree update
  54. const parentId = itemData.parent != null ? getIdStringForRef(itemData.parent) : ROOT_PAGE_VIRTUAL_ID;
  55. notifyUpdateItems([parentId]);
  56. };
  57. openDuplicateModal(page, { onDuplicated: duplicatedHandler });
  58. }, [openDuplicateModal, t, notifyUpdateItems, itemData.parent]);
  59. const onClickDeleteMenuItem = useCallback((page: IPageToDeleteWithMeta) => {
  60. const onDeletedHandler: OnDeletedFunction = (pathOrPathsToDelete, isRecursively, isCompletely) => {
  61. if (typeof pathOrPathsToDelete !== 'string') {
  62. return;
  63. }
  64. if (isCompletely) {
  65. toastSuccess(t('deleted_pages_completely', { path: pathOrPathsToDelete }));
  66. }
  67. else {
  68. toastSuccess(t('deleted_pages', { path: pathOrPathsToDelete }));
  69. }
  70. mutatePageTree();
  71. mutateSearching();
  72. mutatePageList();
  73. mutateAllPageInfo();
  74. if (currentPagePath === pathOrPathsToDelete) {
  75. fetchCurrentPage({ force: true });
  76. router.push(isCompletely ? path.dirname(pathOrPathsToDelete) : `/trash${pathOrPathsToDelete}`);
  77. }
  78. // Notify headless-tree update
  79. const parentId = itemData.parent != null ? getIdStringForRef(itemData.parent) : ROOT_PAGE_VIRTUAL_ID;
  80. notifyUpdateItems([parentId]);
  81. };
  82. openDeleteModal([page], { onDeleted: onDeletedHandler });
  83. }, [openDeleteModal, t, currentPagePath, fetchCurrentPage, router, itemData.parent, notifyUpdateItems]);
  84. const { Control } = usePageItemControl();
  85. // Rename feature from usePageRename hook
  86. const { isRenaming, RenameAlternativeComponent } = usePageRename();
  87. // Page create feature
  88. const { CreateAlternativeComponent } = usePageCreate();
  89. // Check if this is the creating placeholder node
  90. const isCreatingPlaceholder = itemData._id === CREATING_PAGE_VIRTUAL_ID;
  91. const itemSelectedHandler = useCallback((page: IPageForItem) => {
  92. if (page.path == null || page._id == null) return;
  93. const link = pathUtils.returnPathForURL(page.path, page._id);
  94. router.push(link);
  95. }, [router]);
  96. const itemSelectedByWheelClickHandler = useCallback((page: IPageForItem) => {
  97. if (page.path == null || page._id == null) return;
  98. const url = pathUtils.returnPathForURL(page.path, page._id);
  99. window.open(url, '_blank');
  100. }, []);
  101. return (
  102. <TreeItemLayout
  103. className={moduleClass}
  104. item={item}
  105. targetPath={targetPath}
  106. targetPathOrId={targetPathOrId ?? undefined}
  107. isWipPageShown={isWipPageShown}
  108. isEnableActions={isEnableActions}
  109. isReadOnlyUser={isReadOnlyUser}
  110. onClick={itemSelectedHandler}
  111. onWheelClick={itemSelectedByWheelClickHandler}
  112. onToggle={onToggle}
  113. onClickDuplicateMenuItem={onClickDuplicateMenuItem}
  114. onClickDeleteMenuItem={onClickDeleteMenuItem}
  115. customEndComponents={[CountBadgeForPageTreeItem]}
  116. customHoveredEndComponents={[Control]}
  117. showAlternativeContent={isRenaming(item) || isCreatingPlaceholder}
  118. customAlternativeComponents={isCreatingPlaceholder ? [CreateAlternativeComponent] : [RenameAlternativeComponent]}
  119. />
  120. );
  121. };