PageSelectModal.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import type { FC } from 'react';
  2. import {
  3. Suspense, useState, useCallback,
  4. } from 'react';
  5. import nodePath from 'path';
  6. import { pathUtils } from '@growi/core/dist/utils';
  7. import { useTranslation } from 'next-i18next';
  8. import {
  9. Modal, ModalHeader, ModalBody, ModalFooter, Button,
  10. } from 'reactstrap';
  11. import SimpleBar from 'simplebar-react';
  12. import type { IPageForItem } from '~/interfaces/page';
  13. import { useTargetAndAncestors, useIsGuestUser, useIsReadOnlyUser } from '~/stores-universal/context';
  14. import { usePageSelectModal } from '~/stores/modal';
  15. import { useSWRxCurrentPage } from '~/stores/page';
  16. import { ItemsTree } from '../ItemsTree';
  17. import ItemsTreeContentSkeleton from '../ItemsTree/ItemsTreeContentSkeleton';
  18. import { TreeItemForModal } from './TreeItemForModal';
  19. const PageSelectModalSubstance: FC = () => {
  20. const {
  21. data: PageSelectModalData,
  22. close: closeModal,
  23. } = usePageSelectModal();
  24. const [clickedParentPage, setClickedParentPage] = useState<IPageForItem | null>(null);
  25. const [isIncludeSubPage, setIsIncludeSubPage] = useState(true);
  26. const { t } = useTranslation();
  27. const { data: isGuestUser } = useIsGuestUser();
  28. const { data: isReadOnlyUser } = useIsReadOnlyUser();
  29. const { data: targetAndAncestorsData } = useTargetAndAncestors();
  30. const { data: currentPage } = useSWRxCurrentPage();
  31. const { data: pageSelectModalData } = usePageSelectModal();
  32. const isHierarchicalSelectionMode = pageSelectModalData?.opts?.isHierarchicalSelectionMode ?? false;
  33. const onClickTreeItem = useCallback((page: IPageForItem) => {
  34. const parentPagePath = page.path;
  35. if (parentPagePath == null) {
  36. return;
  37. }
  38. setClickedParentPage(page);
  39. }, []);
  40. const onClickCancel = useCallback(() => {
  41. setClickedParentPage(null);
  42. closeModal();
  43. }, [closeModal]);
  44. const onClickDone = useCallback(() => {
  45. if (clickedParentPage != null) {
  46. PageSelectModalData?.opts?.onSelected?.(clickedParentPage, isIncludeSubPage);
  47. }
  48. closeModal();
  49. }, [PageSelectModalData?.opts, clickedParentPage, closeModal, isIncludeSubPage]);
  50. const parentPagePath = pathUtils.addTrailingSlash(nodePath.dirname(currentPage?.path ?? ''));
  51. const targetPathOrId = clickedParentPage?.path || parentPagePath;
  52. const targetPath = clickedParentPage?.path || parentPagePath;
  53. if (isGuestUser == null) {
  54. return <></>;
  55. }
  56. return (
  57. <>
  58. <ModalHeader toggle={closeModal}>{t('page_select_modal.select_page_location')}</ModalHeader>
  59. <ModalBody className="p-0">
  60. <Suspense fallback={<ItemsTreeContentSkeleton />}>
  61. <SimpleBar style={{ maxHeight: 'calc(85vh - 133px)' }}> {/* 133px = 63px(ModalHeader) + 70px(ModalFooter) */}
  62. <div className="p-3">
  63. <ItemsTree
  64. CustomTreeItem={TreeItemForModal}
  65. isEnableActions={!isGuestUser}
  66. isReadOnlyUser={!!isReadOnlyUser}
  67. targetPath={targetPath}
  68. targetPathOrId={targetPathOrId}
  69. targetAndAncestorsData={targetAndAncestorsData}
  70. onClickTreeItem={onClickTreeItem}
  71. />
  72. </div>
  73. </SimpleBar>
  74. </Suspense>
  75. </ModalBody>
  76. <ModalFooter className="border-top d-flex flex-column">
  77. { isHierarchicalSelectionMode && (
  78. <div className="form-check form-check-info align-self-start ms-4">
  79. <input
  80. type="checkbox"
  81. id="includeSubPages"
  82. className="form-check-input"
  83. name="fileUpload"
  84. checked={isIncludeSubPage}
  85. onChange={() => setIsIncludeSubPage(!isIncludeSubPage)}
  86. />
  87. <label
  88. className="form-label form-check-label"
  89. htmlFor="includeSubPages"
  90. >
  91. {t('Include Subordinated Page')}
  92. </label>
  93. </div>
  94. )}
  95. <div className="d-flex gap-2 align-self-end">
  96. <Button color="secondary" onClick={onClickCancel}>{t('Cancel')}</Button>
  97. <Button color="primary" onClick={onClickDone}>{t('Done')}</Button>
  98. </div>
  99. </ModalFooter>
  100. </>
  101. );
  102. };
  103. export const PageSelectModal = (): JSX.Element => {
  104. const { data: pageSelectModalData, close: closePageSelectModal } = usePageSelectModal();
  105. const isOpen = pageSelectModalData?.isOpened ?? false;
  106. if (!isOpen) {
  107. return <></>;
  108. }
  109. return (
  110. <Modal isOpen={isOpen} toggle={closePageSelectModal} centered>
  111. <PageSelectModalSubstance />
  112. </Modal>
  113. );
  114. };