Parcourir la source

create useNewPageInput hooks

WNomunomu il y a 2 ans
Parent
commit
90f9c88560

+ 137 - 2
apps/app/src/components/Sidebar/PageTree/PageTreeItem.tsx

@@ -11,16 +11,18 @@ import { useDrag, useDrop } from 'react-dnd';
 import { DropdownToggle } from 'reactstrap';
 import { DropdownToggle } from 'reactstrap';
 
 
 import { bookmark, unbookmark, resumeRenameOperation } from '~/client/services/page-operation';
 import { bookmark, unbookmark, resumeRenameOperation } from '~/client/services/page-operation';
-import { apiv3Put } from '~/client/util/apiv3-client';
+import { apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
 import { ValidationTarget } from '~/client/util/input-validator';
 import { ValidationTarget } from '~/client/util/input-validator';
 import { toastWarning, toastError, toastSuccess } from '~/client/util/toastr';
 import { toastWarning, toastError, toastSuccess } from '~/client/util/toastr';
 import { NotAvailableForGuest } from '~/components/NotAvailableForGuest';
 import { NotAvailableForGuest } from '~/components/NotAvailableForGuest';
+import { NotAvailableForReadOnlyUser } from '~/components/NotAvailableForReadOnlyUser';
 import {
 import {
   IPageHasId, IPageInfoAll, IPageToDeleteWithMeta, IPageForItem,
   IPageHasId, IPageInfoAll, IPageToDeleteWithMeta, IPageForItem,
 } from '~/interfaces/page';
 } from '~/interfaces/page';
 import { useSWRMUTxCurrentUserBookmarks } from '~/stores/bookmark';
 import { useSWRMUTxCurrentUserBookmarks } from '~/stores/bookmark';
 import { useSWRMUTxPageInfo } from '~/stores/page';
 import { useSWRMUTxPageInfo } from '~/stores/page';
 import { mutatePageTree, useSWRxPageChildren } from '~/stores/page-listing';
 import { mutatePageTree, useSWRxPageChildren } from '~/stores/page-listing';
+import { usePageTreeDescCountMap } from '~/stores/ui';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 import ClosableTextInput from '../../Common/ClosableTextInput';
 import ClosableTextInput from '../../Common/ClosableTextInput';
@@ -183,6 +185,136 @@ const Ellipsis: FC<EllipsisProps> = (props) => {
   );
   );
 };
 };
 
 
+export const useNewPageInput = () => {
+
+  const [isNewPageInputShown, setNewPageInputShown] = useState(false);
+
+  const NewPageCreateButton = (props) => {
+    const {
+      page, isOpen: _isOpen = false, children, stateHandlers,
+    } = props;
+
+    const { setIsOpen } = stateHandlers;
+
+    const currentChildren = children;
+
+    // descendantCount
+    const { getDescCount } = usePageTreeDescCountMap();
+    const descendantCount = getDescCount(page._id) || page.descendantCount || 0;
+
+    const isChildrenLoaded = currentChildren?.length > 0;
+    const hasDescendants = descendantCount > 0 || isChildrenLoaded;
+
+    const onClickPlusButton = useCallback(() => {
+      setNewPageInputShown(true);
+      // openNewPageInput();
+
+      if (hasDescendants) {
+        setIsOpen(true);
+      }
+    }, [hasDescendants]);
+
+    return (
+      <>
+        {!pagePathUtils.isUsersTopPage(page.path ?? '') && (
+          <NotAvailableForGuest>
+            <NotAvailableForReadOnlyUser>
+              <button
+                id='page-create-button-in-page-tree'
+                type="button"
+                className="border-0 rounded btn btn-page-item-control p-0 grw-visible-on-hover"
+                onClick={onClickPlusButton}
+              >
+                <i className="icon-plus d-block p-0" />
+              </button>
+            </NotAvailableForReadOnlyUser>
+          </NotAvailableForGuest>
+        )}
+      </>
+    );
+  };
+
+  const NewPageInput = (props) => {
+    const { t } = useTranslation();
+
+    const {
+      page, isOpen: _isOpen = false, isEnableActions, children, stateHandlers,
+    } = props;
+
+    const { isOpen, setIsOpen, setCreating } = stateHandlers;
+
+    const currentChildren = children;
+
+    const { data, mutate: mutateChildren } = useSWRxPageChildren(isOpen ? page._id : null);
+
+    const { getDescCount } = usePageTreeDescCountMap();
+    const descendantCount = getDescCount(page._id) || page.descendantCount || 0;
+
+    const isChildrenLoaded = currentChildren?.length > 0;
+    const hasDescendants = descendantCount > 0 || isChildrenLoaded;
+
+    const onPressEnterForCreateHandler = async(inputText: string) => {
+      setNewPageInputShown(false);
+      // closeNewPageInput();
+      const parentPath = pathUtils.addTrailingSlash(page.path as string);
+      const newPagePath = nodePath.resolve(parentPath, inputText);
+      const isCreatable = pagePathUtils.isCreatablePage(newPagePath);
+
+      if (!isCreatable) {
+        toastWarning(t('you_can_not_create_page_with_this_name'));
+        return;
+      }
+
+      try {
+        setCreating(true);
+
+        await apiv3Post('/pages/', {
+          path: newPagePath,
+          body: undefined,
+          grant: page.grant,
+          grantUserGroupId: page.grantedGroup,
+        });
+
+        mutateChildren();
+
+        if (!hasDescendants) {
+          setIsOpen(true);
+        }
+
+        toastSuccess(t('successfully_saved_the_page'));
+      }
+      catch (err) {
+        toastError(err);
+      }
+      finally {
+        setCreating(false);
+      }
+    };
+
+    return (
+      <>
+        {isEnableActions && isNewPageInputShown && (
+          <div className="flex-fill">
+            <NotDraggableForClosableTextInput>
+              <ClosableTextInput
+                placeholder={t('Input page name')}
+                onClickOutside={() => { setNewPageInputShown(false) }}
+                onPressEnter={onPressEnterForCreateHandler}
+                validationTarget={ValidationTarget.PAGE}
+              />
+            </NotDraggableForClosableTextInput>
+          </div>
+        )}
+      </>
+    );
+  };
+
+  return {
+    NewPageInput,
+    NewPageCreateButton,
+  };
+};
+
 export const PageTreeItem: FC<PageTreeItemProps> = (props) => {
 export const PageTreeItem: FC<PageTreeItemProps> = (props) => {
   const getNewPathAfterMoved = (droppedPagePath: string, newParentPagePath: string): string => {
   const getNewPathAfterMoved = (droppedPagePath: string, newParentPagePath: string): string => {
     const pageTitle = nodePath.basename(droppedPagePath);
     const pageTitle = nodePath.basename(droppedPagePath);
@@ -312,6 +444,8 @@ export const PageTreeItem: FC<PageTreeItemProps> = (props) => {
 
 
   const mainClassName = `${isOver ? 'grw-pagetree-is-over' : ''} ${shouldHide ? 'd-none' : ''}`;
   const mainClassName = `${isOver ? 'grw-pagetree-is-over' : ''} ${shouldHide ? 'd-none' : ''}`;
 
 
+  const { NewPageInput, NewPageCreateButton } = useNewPageInput();
+
   return (
   return (
     <SimpleItem
     <SimpleItem
       key={props.key}
       key={props.key}
@@ -326,7 +460,8 @@ export const PageTreeItem: FC<PageTreeItemProps> = (props) => {
       itemRef={itemRef}
       itemRef={itemRef}
       itemClass={PageTreeItem}
       itemClass={PageTreeItem}
       mainClassName={mainClassName}
       mainClassName={mainClassName}
-      customComponent={Ellipsis}
+      customComponent={[Ellipsis, NewPageCreateButton]}
+      customComponentUnderItem={[NewPageInput]}
     />
     />
   );
   );
 };
 };

+ 5 - 175
apps/app/src/components/Sidebar/PageTree/PageTreeItemForModal.tsx

@@ -16,7 +16,6 @@ import { NotAvailableForReadOnlyUser } from '~/components/NotAvailableForReadOnl
 import { IPageHasId } from '~/interfaces/page';
 import { IPageHasId } from '~/interfaces/page';
 import { mutatePageTree, useSWRxPageChildren } from '~/stores/page-listing';
 import { mutatePageTree, useSWRxPageChildren } from '~/stores/page-listing';
 import { usePageTreeDescCountMap } from '~/stores/ui';
 import { usePageTreeDescCountMap } from '~/stores/ui';
-import { useStaticSWR } from '~/stores/use-static-swr';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 
 
@@ -24,186 +23,17 @@ import ClosableTextInput from '../../Common/ClosableTextInput';
 
 
 import { ItemNode } from './ItemNode';
 import { ItemNode } from './ItemNode';
 // import { SimpleItem, SimpleItemProps } from './SimpleItem';
 // import { SimpleItem, SimpleItemProps } from './SimpleItem';
+import { useNewPageInput } from './PageTreeItem';
 import SimpleItem, { SimpleItemProps, SimpleItemTool, NotDraggableForClosableTextInput } from './SimpleItem';
 import SimpleItem, { SimpleItemProps, SimpleItemTool, NotDraggableForClosableTextInput } from './SimpleItem';
 
 
 const logger = loggerFactory('growi:cli:Item');
 const logger = loggerFactory('growi:cli:Item');
 
 
-const useNewPageInput = (status?) => {
-  const initialStatus = { isNewPageInputShown: false };
-  const swrResponse = useStaticSWR('childPageCreateTextarea', status, { fallbackData: initialStatus });
-
-  return {
-    ...swrResponse,
-    open: () => swrResponse.mutate({ isNewPageInputShown: true }),
-    close: () => swrResponse.mutate({ isNewPageInputShown: false }),
-  };
-};
-
-const ChildPageCreateButton = (props) => {
-  const {
-    page, isOpen: _isOpen = false, isEnableActions, itemNode, children,
-  } = props;
-
-  // const { page, children } = itemNode;
-
-  const currentChildren = children;
-
-  // const [currentChildren, setCurrentChildren] = useState(children);
-  // const [isNewPageInputShown, setNewPageInputShown] = useState(false);
-  const [isOpen, setIsOpen] = useState(_isOpen);
-
-  // const { data, mutate: mutateChildren } = useSWRxPageChildren(isOpen ? page._id : null);
-
-  const {
-    open: openNewPageInput,
-  } = useNewPageInput();
-
-  // descendantCount
-  const { getDescCount } = usePageTreeDescCountMap();
-  const descendantCount = getDescCount(page._id) || page.descendantCount || 0;
-
-  const isChildrenLoaded = currentChildren?.length > 0;
-  const hasDescendants = descendantCount > 0 || isChildrenLoaded;
-
-  // type NotDraggableProps = {
-  //   children: ReactNode,
-  // };
-
-  const onClickPlusButton = useCallback(() => {
-    // setNewPageInputShown(true);
-    openNewPageInput();
-
-    if (hasDescendants) {
-      setIsOpen(true);
-    }
-  }, [hasDescendants]);
-
-
-  // console.log(!pagePathUtils.isUsersTopPage(page.path ?? ''));
-
-  return (
-    <>
-      <SimpleItemTool page={page} isEnableActions={false} isReadOnlyUser={false}/>
-
-      {!pagePathUtils.isUsersTopPage(page.path ?? '') && (
-        <NotAvailableForGuest>
-          <NotAvailableForReadOnlyUser>
-            <button
-              id='page-create-button-in-page-tree'
-              type="button"
-              className="border-0 rounded btn btn-page-item-control p-0 grw-visible-on-hover"
-              onClick={onClickPlusButton}
-            >
-              <i className="icon-plus d-block p-0" />
-            </button>
-          </NotAvailableForReadOnlyUser>
-        </NotAvailableForGuest>
-      )}
-    </>
-  );
-};
-
-const NewPageInput = (props) => {
-  const { t } = useTranslation();
-
-  const {
-    page, isOpen: _isOpen = false, isEnableActions, itemNode, children,
-  } = props;
-
-  // const { page, children } = itemNode;
-
-  const currentChildren = children;
-
-  // const [currentChildren, setCurrentChildren] = useState(children);
-  // const [isNewPageInputShown, setNewPageInputShown] = useState(false);
-  const [isCreating, setCreating] = useState(false);
-  const [isOpen, setIsOpen] = useState(_isOpen);
-
-  const { data, mutate: mutateChildren } = useSWRxPageChildren(isOpen ? page._id : null);
-
-  const {
-    data: newPageInputData,
-    close: closeNewPageInput,
-  } = useNewPageInput();
-
-  const isNewPageInputShown = newPageInputData?.isNewPageInputShown ?? false;
-
-  // descendantCount
-  const { getDescCount } = usePageTreeDescCountMap();
-  const descendantCount = getDescCount(page._id) || page.descendantCount || 0;
-
-  const isChildrenLoaded = currentChildren?.length > 0;
-  const hasDescendants = descendantCount > 0 || isChildrenLoaded;
-
-  const onPressEnterForCreateHandler = async(inputText: string) => {
-    // setNewPageInputShown(false);
-    closeNewPageInput();
-    const parentPath = pathUtils.addTrailingSlash(page.path as string);
-    const newPagePath = nodePath.resolve(parentPath, inputText);
-    const isCreatable = pagePathUtils.isCreatablePage(newPagePath);
-
-    if (!isCreatable) {
-      toastWarning(t('you_can_not_create_page_with_this_name'));
-      return;
-    }
-
-    try {
-      setCreating(true);
-
-      await apiv3Post('/pages/', {
-        path: newPagePath,
-        body: undefined,
-        grant: page.grant,
-        grantUserGroupId: page.grantedGroup,
-      });
-
-      mutateChildren();
-
-      if (!hasDescendants) {
-        setIsOpen(true);
-      }
-
-      toastSuccess(t('successfully_saved_the_page'));
-    }
-    catch (err) {
-      toastError(err);
-    }
-    finally {
-      setCreating(false);
-    }
-  };
-
-  console.log(isNewPageInputShown);
-
-  return (
-    <>
-      {isEnableActions && isNewPageInputShown && (
-        <div className="flex-fill">
-          <NotDraggableForClosableTextInput>
-            <ClosableTextInput
-              placeholder={t('Input page name')}
-              onClickOutside={() => { closeNewPageInput() }}
-              onPressEnter={onPressEnterForCreateHandler}
-              validationTarget={ValidationTarget.PAGE}
-            />
-          </NotDraggableForClosableTextInput>
-        </div>
-      )}
-    </>
-  );
-};
-
 type Optional = 'itemRef' | 'itemClass' | 'mainClassName';
 type Optional = 'itemRef' | 'itemClass' | 'mainClassName';
 type PageTreeItemProps = Omit<SimpleItemProps, Optional> & {key};
 type PageTreeItemProps = Omit<SimpleItemProps, Optional> & {key};
 
 
 export const PageTreeItemForModal = (props) => {
 export const PageTreeItemForModal = (props) => {
-  const {
-    itemNode, isOpen: _isOpen = false, onRenamed,
-  } = props;
-  // const { page } = itemNode;
-  // const [isOpen, setIsOpen] = useState(_isOpen);
-  // const [shouldHide, setShouldHide] = useState(false);
-  // const { mutate: mutateChildren } = useSWRxPageChildren(isOpen ? page._id : null);
+
+  const { NewPageInput, NewPageCreateButton } = useNewPageInput();
 
 
   return (
   return (
     <SimpleItem
     <SimpleItem
@@ -216,9 +46,9 @@ export const PageTreeItemForModal = (props) => {
       onRenamed={props.onRenamed}
       onRenamed={props.onRenamed}
       onClickDuplicateMenuItem={props.onClickDuplicateMenuItem}
       onClickDuplicateMenuItem={props.onClickDuplicateMenuItem}
       onClickDeleteMenuItem={props.onClickDeleteMenuItem}
       onClickDeleteMenuItem={props.onClickDeleteMenuItem}
-      customComponentUnderItem={NewPageInput}
+      customComponentUnderItem={[NewPageInput]}
       itemClass={PageTreeItemForModal}
       itemClass={PageTreeItemForModal}
-      customComponent={ChildPageCreateButton}
+      customComponent={[SimpleItemTool, NewPageCreateButton]}
     />
     />
   );
   );
 };
 };

+ 19 - 74
apps/app/src/components/Sidebar/PageTree/SimpleItem.tsx

@@ -41,8 +41,8 @@ export type SimpleItemProps = {
   itemRef?
   itemRef?
   itemClass?: React.FunctionComponent<SimpleItemProps>
   itemClass?: React.FunctionComponent<SimpleItemProps>
   mainClassName?: string
   mainClassName?: string
-  customComponent?: React.FunctionComponent<SimpleItemToolProps>
-  customComponentUnderItem?
+  customComponent?: Array<React.FunctionComponent<SimpleItemToolProps>>
+  customComponentUnderItem?: Array<React.FunctionComponent<SimpleItemToolProps>>
 };
 };
 
 
 // Utility to mark target
 // Utility to mark target
@@ -151,11 +151,17 @@ const SimpleItem: FC<SimpleItemProps> = (props) => {
 
 
   const [currentChildren, setCurrentChildren] = useState(children);
   const [currentChildren, setCurrentChildren] = useState(children);
   const [isOpen, setIsOpen] = useState(_isOpen);
   const [isOpen, setIsOpen] = useState(_isOpen);
-  const [isNewPageInputShown, setNewPageInputShown] = useState(false);
   const [isCreating, setCreating] = useState(false);
   const [isCreating, setCreating] = useState(false);
 
 
   const { data, mutate: mutateChildren } = useSWRxPageChildren(isOpen ? page._id : null);
   const { data, mutate: mutateChildren } = useSWRxPageChildren(isOpen ? page._id : null);
 
 
+  const stateHandlers = {
+    isOpen,
+    setIsOpen,
+    isCreating,
+    setCreating,
+  };
+
   // descendantCount
   // descendantCount
   const { getDescCount } = usePageTreeDescCountMap();
   const { getDescCount } = usePageTreeDescCountMap();
   const descendantCount = getDescCount(page._id) || page.descendantCount || 0;
   const descendantCount = getDescCount(page._id) || page.descendantCount || 0;
@@ -173,51 +179,6 @@ const SimpleItem: FC<SimpleItemProps> = (props) => {
     setIsOpen(!isOpen);
     setIsOpen(!isOpen);
   }, [isOpen]);
   }, [isOpen]);
 
 
-  const onClickPlusButton = useCallback(() => {
-    setNewPageInputShown(true);
-
-    if (hasDescendants) {
-      setIsOpen(true);
-    }
-  }, [hasDescendants]);
-
-  const onPressEnterForCreateHandler = async(inputText: string) => {
-    setNewPageInputShown(false);
-    const parentPath = pathUtils.addTrailingSlash(page.path as string);
-    const newPagePath = nodePath.resolve(parentPath, inputText);
-    const isCreatable = pagePathUtils.isCreatablePage(newPagePath);
-
-    if (!isCreatable) {
-      toastWarning(t('you_can_not_create_page_with_this_name'));
-      return;
-    }
-
-    try {
-      setCreating(true);
-
-      await apiv3Post('/pages/', {
-        path: newPagePath,
-        body: undefined,
-        grant: page.grant,
-        grantUserGroupId: page.grantedGroup,
-      });
-
-      mutateChildren();
-
-      if (!hasDescendants) {
-        setIsOpen(true);
-      }
-
-      toastSuccess(t('successfully_saved_the_page'));
-    }
-    catch (err) {
-      toastError(err);
-    }
-    finally {
-      setCreating(false);
-    }
-  };
-
   // didMount
   // didMount
   useEffect(() => {
   useEffect(() => {
     if (hasChildren()) setIsOpen(true);
     if (hasChildren()) setIsOpen(true);
@@ -254,13 +215,12 @@ const SimpleItem: FC<SimpleItemProps> = (props) => {
     onRenamed,
     onRenamed,
     onClickDuplicateMenuItem,
     onClickDuplicateMenuItem,
     onClickDeleteMenuItem,
     onClickDeleteMenuItem,
+    stateHandlers,
   };
   };
 
 
   const CustomComponent = props.customComponent;
   const CustomComponent = props.customComponent;
 
 
-  const SimpleItemContent = CustomComponent ?? SimpleItemTool;
-
-  console.dir(SimpleItemContent);
+  const SimpleItemContent = CustomComponent ?? [SimpleItemTool];
 
 
   const SimpleItemContentProps = {
   const SimpleItemContentProps = {
     itemNode,
     itemNode,
@@ -271,10 +231,12 @@ const SimpleItem: FC<SimpleItemProps> = (props) => {
     isEnableActions,
     isEnableActions,
     isReadOnlyUser,
     isReadOnlyUser,
     children,
     children,
+    stateHandlers,
   };
   };
 
 
   const CustomComponentUnderItem = props.customComponentUnderItem;
   const CustomComponentUnderItem = props.customComponentUnderItem;
 
 
+
   return (
   return (
     <div
     <div
       id={`pagetree-item-${page._id}`}
       id={`pagetree-item-${page._id}`}
@@ -300,31 +262,14 @@ const SimpleItem: FC<SimpleItemProps> = (props) => {
             </button>
             </button>
           )}
           )}
         </div>
         </div>
-
-        <SimpleItemContent {...SimpleItemContentProps}/>
-
-        {/* {!pagePathUtils.isUsersTopPage(page.path ?? '') && (
-          <NotAvailableForGuest>
-            <NotAvailableForReadOnlyUser>
-              <button
-                id='page-create-button-in-page-tree'
-                type="button"
-                className="border-0 rounded btn btn-page-item-control p-0 grw-visible-on-hover"
-                onClick={onClickPlusButton}
-              >
-                <i className="icon-plus d-block p-0" />
-              </button>
-            </NotAvailableForReadOnlyUser>
-          </NotAvailableForGuest>
-        )} */}
+        {SimpleItemContent.map((ItemContent, index) => (
+          <ItemContent key={index} {...SimpleItemContentProps}/>
+        ))}
       </li>
       </li>
 
 
-      <CustomComponentUnderItem
-        page={page}
-        isOpen
-        isEnableActions={isEnableActions}
-        itemNode={itemNode}
-      />
+      {CustomComponentUnderItem?.map((UnderItemContent, index) => (
+        <UnderItemContent key={index} {...SimpleItemContentProps}/>
+      ))}
 
 
       {
       {
         isOpen && hasChildren() && currentChildren.map((node, index) => (
         isOpen && hasChildren() && currentChildren.map((node, index) => (