Kaynağa Gözat

Merge pull request #8446 from weseek/feat/139973-implement-function-to-select-parent-page-from-modal

feat: implement function to select parent page from PageSelectModal
Yuki Takei 2 yıl önce
ebeveyn
işleme
54d7ebc4c1

+ 11 - 7
apps/app/src/components/ItemsTree/ItemsTree.tsx

@@ -11,12 +11,13 @@ import { useRouter } from 'next/router';
 import { debounce } from 'throttle-debounce';
 import { debounce } from 'throttle-debounce';
 
 
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { toastError, toastSuccess } from '~/client/util/toastr';
-import { AncestorsChildrenResult, RootPageResult, TargetAndAncestors } from '~/interfaces/page-listing-results';
-import { OnDuplicatedFunction, OnDeletedFunction } from '~/interfaces/ui';
-import { SocketEventName, UpdateDescCountData, UpdateDescCountRawData } from '~/interfaces/websocket';
-import {
-  IPageForPageDuplicateModal, usePageDuplicateModal, usePageDeleteModal,
-} from '~/stores/modal';
+import type { IPageForItem } from '~/interfaces/page';
+import type { AncestorsChildrenResult, RootPageResult, TargetAndAncestors } from '~/interfaces/page-listing-results';
+import type { OnDuplicatedFunction, OnDeletedFunction } from '~/interfaces/ui';
+import type { UpdateDescCountData, UpdateDescCountRawData } from '~/interfaces/websocket';
+import { SocketEventName } from '~/interfaces/websocket';
+import type { IPageForPageDuplicateModal } from '~/stores/modal';
+import { usePageDuplicateModal, usePageDeleteModal } from '~/stores/modal';
 import { mutateAllPageInfo, useCurrentPagePath, useSWRMUTxCurrentPage } from '~/stores/page';
 import { mutateAllPageInfo, useCurrentPagePath, useSWRMUTxCurrentPage } from '~/stores/page';
 import {
 import {
   useSWRxPageAncestorsChildren, useSWRxRootPage, mutatePageTree, mutatePageList,
   useSWRxPageAncestorsChildren, useSWRxRootPage, mutatePageTree, mutatePageList,
@@ -31,6 +32,7 @@ import ItemsTreeContentSkeleton from './ItemsTreeContentSkeleton';
 
 
 import styles from './ItemsTree.module.scss';
 import styles from './ItemsTree.module.scss';
 
 
+
 const logger = loggerFactory('growi:cli:ItemsTree');
 const logger = loggerFactory('growi:cli:ItemsTree');
 
 
 /*
 /*
@@ -93,6 +95,7 @@ type ItemsTreeProps = {
   targetPathOrId?: Nullable<string>
   targetPathOrId?: Nullable<string>
   targetAndAncestorsData?: TargetAndAncestors
   targetAndAncestorsData?: TargetAndAncestors
   CustomTreeItem: React.FunctionComponent<TreeItemProps>
   CustomTreeItem: React.FunctionComponent<TreeItemProps>
+  onClickTreeItem?: (page: IPageForItem) => void;
 }
 }
 
 
 /*
 /*
@@ -100,7 +103,7 @@ type ItemsTreeProps = {
  */
  */
 export const ItemsTree = (props: ItemsTreeProps): JSX.Element => {
 export const ItemsTree = (props: ItemsTreeProps): JSX.Element => {
   const {
   const {
-    targetPath, targetPathOrId, targetAndAncestorsData, isEnableActions, isReadOnlyUser, CustomTreeItem,
+    targetPath, targetPathOrId, targetAndAncestorsData, isEnableActions, isReadOnlyUser, CustomTreeItem, onClickTreeItem,
   } = props;
   } = props;
 
 
   const { t } = useTranslation();
   const { t } = useTranslation();
@@ -282,6 +285,7 @@ export const ItemsTree = (props: ItemsTreeProps): JSX.Element => {
           onRenamed={onRenamed}
           onRenamed={onRenamed}
           onClickDuplicateMenuItem={onClickDuplicateMenuItem}
           onClickDuplicateMenuItem={onClickDuplicateMenuItem}
           onClickDeleteMenuItem={onClickDeleteMenuItem}
           onClickDeleteMenuItem={onClickDeleteMenuItem}
+          onClick={onClickTreeItem}
         />
         />
       </ul>
       </ul>
     );
     );

+ 7 - 5
apps/app/src/components/PageHeader/page-header-utils.ts → apps/app/src/components/PageEditor/page-path-rename-utils.ts

@@ -11,21 +11,23 @@ import { mutatePageTree, mutatePageList } from '~/stores/page-listing';
 type PagePathRenameHandler = (newPagePath: string, onRenameFinish?: () => void, onRenameFailure?: () => void) => Promise<void>
 type PagePathRenameHandler = (newPagePath: string, onRenameFinish?: () => void, onRenameFailure?: () => void) => Promise<void>
 
 
 export const usePagePathRenameHandler = (
 export const usePagePathRenameHandler = (
-    currentPage: IPagePopulatedToShowRevision,
+    currentPage?: IPagePopulatedToShowRevision | null,
 ): PagePathRenameHandler => {
 ): PagePathRenameHandler => {
 
 
   const { trigger: mutateCurrentPage } = useSWRMUTxCurrentPage();
   const { trigger: mutateCurrentPage } = useSWRMUTxCurrentPage();
   const { t } = useTranslation();
   const { t } = useTranslation();
 
 
-  const currentPagePath = currentPage.path;
-
   const pagePathRenameHandler = useCallback(async(newPagePath, onRenameFinish, onRenameFailure) => {
   const pagePathRenameHandler = useCallback(async(newPagePath, onRenameFinish, onRenameFailure) => {
 
 
+    if (currentPage == null) {
+      return;
+    }
+
     const onRenamed = (fromPath: string | undefined, toPath: string) => {
     const onRenamed = (fromPath: string | undefined, toPath: string) => {
       mutatePageTree();
       mutatePageTree();
       mutatePageList();
       mutatePageList();
 
 
-      if (currentPagePath === fromPath || currentPagePath === toPath) {
+      if (currentPage.path === fromPath || currentPage.path === toPath) {
         mutateCurrentPage();
         mutateCurrentPage();
       }
       }
     };
     };
@@ -51,7 +53,7 @@ export const usePagePathRenameHandler = (
       onRenameFailure?.();
       onRenameFailure?.();
       toastError(err);
       toastError(err);
     }
     }
-  }, [currentPage._id, currentPage.path, currentPage.revision._id, currentPagePath, mutateCurrentPage, t]);
+  }, [currentPage, mutateCurrentPage, t]);
 
 
   return pagePathRenameHandler;
   return pagePathRenameHandler;
 };
 };

+ 1 - 2
apps/app/src/components/PageHeader/PagePathHeader.tsx

@@ -12,10 +12,9 @@ import { usePageSelectModal } from '~/stores/modal';
 
 
 import ClosableTextInput from '../Common/ClosableTextInput';
 import ClosableTextInput from '../Common/ClosableTextInput';
 import { PagePathHierarchicalLink } from '../Common/PagePathHierarchicalLink';
 import { PagePathHierarchicalLink } from '../Common/PagePathHierarchicalLink';
+import { usePagePathRenameHandler } from '../PageEditor/page-path-rename-utils';
 import { PageSelectModal } from '../PageSelectModal/PageSelectModal';
 import { PageSelectModal } from '../PageSelectModal/PageSelectModal';
 
 
-import { usePagePathRenameHandler } from './page-header-utils';
-
 import styles from './PagePathHeader.module.scss';
 import styles from './PagePathHeader.module.scss';
 
 
 const moduleClass = styles['page-path-header'];
 const moduleClass = styles['page-path-header'];

+ 1 - 2
apps/app/src/components/PageHeader/PageTitleHeader.tsx

@@ -12,8 +12,7 @@ import { ValidationTarget } from '~/client/util/input-validator';
 
 
 import ClosableTextInput from '../Common/ClosableTextInput';
 import ClosableTextInput from '../Common/ClosableTextInput';
 import { CopyDropdown } from '../Common/CopyDropdown';
 import { CopyDropdown } from '../Common/CopyDropdown';
-
-import { usePagePathRenameHandler } from './page-header-utils';
+import { usePagePathRenameHandler } from '../PageEditor/page-path-rename-utils';
 
 
 import styles from './PageTitleHeader.module.scss';
 import styles from './PageTitleHeader.module.scss';
 
 

+ 44 - 6
apps/app/src/components/PageSelectModal/PageSelectModal.tsx

@@ -1,15 +1,20 @@
-import React, { FC } from 'react';
+import type { FC } from 'react';
+import { useState, useCallback } from 'react';
+
+import nodePath from 'path';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import {
 import {
   Modal, ModalHeader, ModalBody, ModalFooter, Button,
   Modal, ModalHeader, ModalBody, ModalFooter, Button,
 } from 'reactstrap';
 } from 'reactstrap';
 
 
+import type { IPageForItem } from '~/interfaces/page';
 import { useTargetAndAncestors, useIsGuestUser, useIsReadOnlyUser } from '~/stores/context';
 import { useTargetAndAncestors, useIsGuestUser, useIsReadOnlyUser } from '~/stores/context';
 import { usePageSelectModal } from '~/stores/modal';
 import { usePageSelectModal } from '~/stores/modal';
-import { useCurrentPagePath, useCurrentPageId } from '~/stores/page';
+import { useCurrentPagePath, useCurrentPageId, useSWRxCurrentPage } from '~/stores/page';
 
 
 import { ItemsTree } from '../ItemsTree';
 import { ItemsTree } from '../ItemsTree';
+import { usePagePathRenameHandler } from '../PageEditor/page-path-rename-utils';
 
 
 import { TreeItemForModal } from './TreeItemForModal';
 import { TreeItemForModal } from './TreeItemForModal';
 
 
@@ -22,6 +27,8 @@ export const PageSelectModal: FC = () => {
 
 
   const isOpened = PageSelectModalData?.isOpened ?? false;
   const isOpened = PageSelectModalData?.isOpened ?? false;
 
 
+  const [clickedParentPagePath, setClickedParentPagePath] = useState<string | null>(null);
+
   const { t } = useTranslation();
   const { t } = useTranslation();
 
 
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isGuestUser } = useIsGuestUser();
@@ -29,19 +36,48 @@ export const PageSelectModal: FC = () => {
   const { data: currentPath } = useCurrentPagePath();
   const { data: currentPath } = useCurrentPagePath();
   const { data: targetId } = useCurrentPageId();
   const { data: targetId } = useCurrentPageId();
   const { data: targetAndAncestorsData } = useTargetAndAncestors();
   const { data: targetAndAncestorsData } = useTargetAndAncestors();
+  const { data: currentPage } = useSWRxCurrentPage();
+
+  const pagePathRenameHandler = usePagePathRenameHandler(currentPage);
+
+  const onClickTreeItem = useCallback((page: IPageForItem) => {
+    const parentPagePath = page.path;
+
+    if (parentPagePath == null) {
+      return;
+    }
+
+    setClickedParentPagePath(parentPagePath);
+  }, []);
+
+  const onClickCancel = useCallback(() => {
+    setClickedParentPagePath(null);
+    closeModal();
+  }, [closeModal]);
+
+  const onClickDone = useCallback(() => {
+    if (clickedParentPagePath != null) {
+      const currentPageTitle = nodePath.basename(currentPage?.path ?? '') || '/';
+      const newPagePath = nodePath.resolve(clickedParentPagePath, currentPageTitle);
+
+      pagePathRenameHandler(newPagePath);
+    }
+
+    closeModal();
+  }, [clickedParentPagePath, closeModal, currentPage?.path, pagePathRenameHandler]);
 
 
   const targetPathOrId = targetId || currentPath;
   const targetPathOrId = targetId || currentPath;
 
 
+  const path = currentPath || '/';
+
   if (isGuestUser == null) {
   if (isGuestUser == null) {
     return null;
     return null;
   }
   }
 
 
-  const path = currentPath || '/';
-
   return (
   return (
     <Modal
     <Modal
       isOpen={isOpened}
       isOpen={isOpened}
-      toggle={() => closeModal()}
+      toggle={closeModal}
       centered
       centered
       size="sm"
       size="sm"
     >
     >
@@ -54,10 +90,12 @@ export const PageSelectModal: FC = () => {
           targetPath={path}
           targetPath={path}
           targetPathOrId={targetPathOrId}
           targetPathOrId={targetPathOrId}
           targetAndAncestorsData={targetAndAncestorsData}
           targetAndAncestorsData={targetAndAncestorsData}
+          onClickTreeItem={onClickTreeItem}
         />
         />
       </ModalBody>
       </ModalBody>
       <ModalFooter>
       <ModalFooter>
-        <Button color="primary" onClick={closeModal}>{t('Done')}</Button>{' '}
+        <Button color="secondary" onClick={onClickCancel}>{t('Cancel')}</Button>
+        <Button color="primary" onClick={onClickDone}>{t('Done')}</Button>
       </ModalFooter>
       </ModalFooter>
     </Modal>
     </Modal>
   );
   );

+ 5 - 2
apps/app/src/components/PageSelectModal/TreeItemForModal.tsx

@@ -1,16 +1,18 @@
-import React, { type FC } from 'react';
+import type { FC } from 'react';
 
 
 import {
 import {
   SimpleItem, useNewPageInput, type TreeItemProps,
   SimpleItem, useNewPageInput, type TreeItemProps,
 } from '../TreeItem';
 } from '../TreeItem';
 
 
+
 type PageTreeItemProps = TreeItemProps & {
 type PageTreeItemProps = TreeItemProps & {
   key?: React.Key | null,
   key?: React.Key | null,
 };
 };
 
 
 export const TreeItemForModal: FC<PageTreeItemProps> = (props) => {
 export const TreeItemForModal: FC<PageTreeItemProps> = (props) => {
 
 
-  const { isOpen } = props;
+  const { isOpen, onClick } = props;
+
   const { Input: NewPageInput, CreateButton: NewPageCreateButton } = useNewPageInput();
   const { Input: NewPageInput, CreateButton: NewPageCreateButton } = useNewPageInput();
 
 
   return (
   return (
@@ -27,6 +29,7 @@ export const TreeItemForModal: FC<PageTreeItemProps> = (props) => {
       customNextComponents={[NewPageInput]}
       customNextComponents={[NewPageInput]}
       itemClass={TreeItemForModal}
       itemClass={TreeItemForModal}
       customEndComponents={[NewPageCreateButton]}
       customEndComponents={[NewPageCreateButton]}
+      onClick={onClick}
     />
     />
   );
   );
 };
 };