PageTreeItemForModal.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import React, {
  2. useCallback, useState, FC, ReactNode,
  3. } from 'react';
  4. import nodePath from 'path';
  5. import { pathUtils, pagePathUtils } from '@growi/core';
  6. import { useTranslation } from 'next-i18next';
  7. import { useDrag, useDrop } from 'react-dnd';
  8. import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
  9. import { ValidationTarget } from '~/client/util/input-validator';
  10. import { toastWarning, toastError, toastSuccess } from '~/client/util/toastr';
  11. import { NotAvailableForGuest } from '~/components/NotAvailableForGuest';
  12. import { NotAvailableForReadOnlyUser } from '~/components/NotAvailableForReadOnlyUser';
  13. import { IPageHasId } from '~/interfaces/page';
  14. import { mutatePageTree, useSWRxPageChildren } from '~/stores/page-listing';
  15. import { usePageTreeDescCountMap } from '~/stores/ui';
  16. import loggerFactory from '~/utils/logger';
  17. import ClosableTextInput from '../../Common/ClosableTextInput';
  18. import { ItemNode } from './ItemNode';
  19. // import { SimpleItem, SimpleItemProps } from './SimpleItem';
  20. import SimpleItem, { SimpleItemProps, SimpleItemTool } from './SimpleItem';
  21. const logger = loggerFactory('growi:cli:Item');
  22. type NotDraggableProps = {
  23. children: ReactNode,
  24. };
  25. const NotDraggableForClosableTextInput = (props: NotDraggableProps): JSX.Element => {
  26. return <div draggable onDragStart={e => e.preventDefault()}>{props.children}</div>;
  27. };
  28. const ChildPageCreateButton = (props) => {
  29. const { t } = useTranslation();
  30. const {
  31. page, isOpen: _isOpen = false, isEnableActions, itemNode, children,
  32. } = props;
  33. // const { page, children } = itemNode;
  34. const [currentChildren, setCurrentChildren] = useState(children);
  35. const [isNewPageInputShown, setNewPageInputShown] = useState(false);
  36. const [isCreating, setCreating] = useState(false);
  37. const [isOpen, setIsOpen] = useState(_isOpen);
  38. const { data, mutate: mutateChildren } = useSWRxPageChildren(isOpen ? page._id : null);
  39. // descendantCount
  40. const { getDescCount } = usePageTreeDescCountMap();
  41. const descendantCount = getDescCount(page._id) || page.descendantCount || 0;
  42. const isChildrenLoaded = currentChildren?.length > 0;
  43. const hasDescendants = descendantCount > 0 || isChildrenLoaded;
  44. const onClickPlusButton = useCallback(() => {
  45. setNewPageInputShown(true);
  46. if (hasDescendants) {
  47. setIsOpen(true);
  48. }
  49. }, [hasDescendants]);
  50. const onPressEnterForCreateHandler = async(inputText: string) => {
  51. setNewPageInputShown(false);
  52. const parentPath = pathUtils.addTrailingSlash(page.path as string);
  53. const newPagePath = nodePath.resolve(parentPath, inputText);
  54. const isCreatable = pagePathUtils.isCreatablePage(newPagePath);
  55. if (!isCreatable) {
  56. toastWarning(t('you_can_not_create_page_with_this_name'));
  57. return;
  58. }
  59. try {
  60. setCreating(true);
  61. await apiv3Post('/pages/', {
  62. path: newPagePath,
  63. body: undefined,
  64. grant: page.grant,
  65. grantUserGroupId: page.grantedGroup,
  66. });
  67. mutateChildren();
  68. if (!hasDescendants) {
  69. setIsOpen(true);
  70. }
  71. toastSuccess(t('successfully_saved_the_page'));
  72. }
  73. catch (err) {
  74. toastError(err);
  75. }
  76. finally {
  77. setCreating(false);
  78. }
  79. };
  80. console.log(!pagePathUtils.isUsersTopPage(page.path ?? ''));
  81. return (
  82. <>
  83. <SimpleItemTool page={page} isEnableActions={false} isReadOnlyUser={false}/>
  84. {!pagePathUtils.isUsersTopPage(page.path ?? '') && (
  85. <NotAvailableForGuest>
  86. <NotAvailableForReadOnlyUser>
  87. <button
  88. id='page-create-button-in-page-tree'
  89. type="button"
  90. className="border-0 rounded btn btn-page-item-control p-0 grw-visible-on-hover"
  91. onClick={onClickPlusButton}
  92. >
  93. <i className="icon-plus d-block p-0" />
  94. </button>
  95. </NotAvailableForReadOnlyUser>
  96. </NotAvailableForGuest>
  97. )}
  98. </>
  99. );
  100. };
  101. type Optional = 'itemRef' | 'itemClass' | 'mainClassName';
  102. type PageTreeItemProps = Omit<SimpleItemProps, Optional> & {key};
  103. export const PageTreeItemForModal = (props) => {
  104. const {
  105. itemNode, isOpen: _isOpen = false, onRenamed,
  106. } = props;
  107. const { page } = itemNode;
  108. const [isOpen, setIsOpen] = useState(_isOpen);
  109. const [shouldHide, setShouldHide] = useState(false);
  110. const { mutate: mutateChildren } = useSWRxPageChildren(isOpen ? page._id : null);
  111. return (
  112. <SimpleItem
  113. key={props.key}
  114. targetPathOrId={props.targetPathOrId}
  115. itemNode={props.itemNode}
  116. isOpen
  117. isEnableActions={props.isEnableActions}
  118. isReadOnlyUser={props.isReadOnlyUser}
  119. onRenamed={props.onRenamed}
  120. onClickDuplicateMenuItem={props.onClickDuplicateMenuItem}
  121. onClickDeleteMenuItem={props.onClickDeleteMenuItem}
  122. // customComponentUnderItem={ChildPageCreateButton}
  123. customComponent={ChildPageCreateButton}
  124. />
  125. );
  126. };