Răsfoiți Sursa

Add component for action menu button

https://youtrack.weseek.co.jp/issue/GW-7905
- Add component BookmarkFolderItemControl for rename and delete folder
- Show rename input on rename action click
- Adjust some variable name
- Remove mutation on press enter
- Add parent types definition to BookmarkFolderItems
- Update return value of bookmark folder list route
Mudana-Grune 3 ani în urmă
părinte
comite
d57eb45e8b

+ 39 - 19
packages/app/src/components/Sidebar/Bookmarks/BookmarkFolderItem.tsx

@@ -3,6 +3,7 @@ import {
 } from 'react';
 
 import { useTranslation } from 'next-i18next';
+import { DropdownToggle } from 'reactstrap';
 
 import { toastError, toastSuccess } from '~/client/util/apiNotification';
 import { apiv3Post } from '~/client/util/apiv3-client';
@@ -12,6 +13,7 @@ import TriangleIcon from '~/components/Icons/TriangleIcon';
 import { BookmarkFolderItems } from '~/server/models/bookmark-folder';
 import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder';
 
+import BookmarkFolderItemControl from './BookmarkFolderItemControl';
 import BookmarkFolderNameInput from './BookmarkFolderNameInput';
 
 
@@ -23,13 +25,15 @@ const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkFolderIt
   const { bookmarkFolder, isOpen: _isOpen = false } = props;
 
   const { t } = useTranslation();
-  const { name, _id: parentId, children } = bookmarkFolder;
+  const {
+    name, _id: folderId, children, parent,
+  } = bookmarkFolder;
   const [currentChildren, setCurrentChildren] = useState<BookmarkFolderItems[]>();
   const [isRenameInputShown, setIsRenameInputShown] = useState<boolean>(false);
-  const [currentParentFolder, setCurrentParentFolder] = useState<string | null>(parentId);
+  const [currentParentFolder, setCurrentParentFolder] = useState<string | null>(folderId);
   const [isOpen, setIsOpen] = useState(_isOpen);
   const { data: childBookmarkFolderData, mutate: mutateChildBookmarkData } = useSWRxBookamrkFolderAndChild(isOpen ? currentParentFolder : null);
-
+  const [isRenameAction, setIsRenameAction] = useState<boolean>(false);
 
   const childCount = useCallback((): number => {
     if (currentChildren != null && currentChildren.length > children.length) {
@@ -55,8 +59,8 @@ const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkFolderIt
 
   const loadChildFolder = useCallback(async() => {
     setIsOpen(!isOpen);
-    setCurrentParentFolder(bookmarkFolder._id);
-  }, [bookmarkFolder, isOpen]);
+    setCurrentParentFolder(folderId);
+  }, [folderId, isOpen]);
 
 
   const onPressEnterHandler = useCallback(async(folderName: string) => {
@@ -65,14 +69,13 @@ const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkFolderIt
       await apiv3Post('/bookmark-folder', { name: folderName, parent: currentParentFolder });
       setIsOpen(true);
       setIsRenameInputShown(false);
-      mutateChildBookmarkData();
       toastSuccess(t('Create New Bookmark Folder Success'));
     }
     catch (err) {
       toastError(err);
     }
 
-  }, [currentParentFolder, mutateChildBookmarkData, t]);
+  }, [currentParentFolder, t]);
 
   const onClickPlusButton = useCallback(async() => {
     if (!isOpen && hasChildren()) {
@@ -94,9 +97,12 @@ const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkFolderIt
     });
   };
 
+  const onClickRenameHandler = useCallback(() => {
+    setIsRenameAction(true);
+  }, []);
 
   return (
-    <div id={`bookmark-folder-item-${bookmarkFolder._id}`} className="grw-foldertree-item-container"
+    <div id={`bookmark-folder-item-${folderId}`} className="grw-foldertree-item-container"
     >
       <li className="list-group-item list-group-item-action border-0 py-0 pr-3 d-flex align-items-center">
         <div className="grw-triangle-container d-flex justify-content-center">
@@ -117,19 +123,33 @@ const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkFolderIt
             <FolderIcon isOpen={isOpen} />
           </div>
         }
-        {
-          <div className='grw-foldertree-title-anchor flex-grow-1 pl-2' onClick={loadChildFolder}>
-            <p className={'text-truncate m-auto '}>{name}</p>
-          </div>
+        { isRenameAction ? (
+          <BookmarkFolderNameInput
+            onClickOutside={() => setIsRenameAction(false)}
+            onPressEnter={onPressEnterHandler} value={name}
+          />
+        ) : (
+          <>
+            <div className='grw-foldertree-title-anchor flex-grow-1 pl-2' onClick={loadChildFolder}>
+              <p className={'text-truncate m-auto '}>{name}</p>
+            </div>
+            {hasChildren() && (
+              <div className="grw-foldertree-count-wrapper">
+                <CountBadge count={ childCount() } />
+              </div>
+            )}
+          </>
+        )
+
         }
-        {hasChildren() && (
-          <div className="grw-foldertree-count-wrapper">
-            <CountBadge count={ childCount() } />
-          </div>
-        )}
         <div className="grw-foldertree-control d-flex">
-
-
+          <BookmarkFolderItemControl
+            onClickRename={onClickRenameHandler}
+          >
+            <DropdownToggle color="transparent" className="border-0 rounded btn-page-item-control p-0 grw-visible-on-hover mr-1">
+              <i className="icon-options fa fa-rotate-90 p-1"></i>
+            </DropdownToggle>
+          </BookmarkFolderItemControl>
           <button
             type="button"
             className="border-0 rounded btn btn-page-item-control p-0 grw-visible-on-hover"

+ 49 - 0
packages/app/src/components/Sidebar/Bookmarks/BookmarkFolderItemControl.tsx

@@ -0,0 +1,49 @@
+import React, { useState } from 'react';
+
+import { useTranslation } from 'next-i18next';
+import {
+  Dropdown, DropdownItem, DropdownMenu, DropdownToggle,
+} from 'reactstrap';
+
+
+type BookmarkFolderItemControlProps = {
+  children?: React.ReactNode
+  onClickRename: () => void
+}
+const BookmarkFolderItemControl = (props: BookmarkFolderItemControlProps): JSX.Element => {
+  const { t } = useTranslation();
+  const { children, onClickRename } = props;
+  const [isOpen, setIsOpen] = useState(false);
+  return (
+    <Dropdown isOpen={isOpen} toggle={() => setIsOpen(!isOpen)}>
+      { children ?? (
+        <DropdownToggle color="transparent" className="border-0 rounded btn-page-item-control d-flex align-items-center justify-content-center">
+          <i className="icon-options"></i>
+        </DropdownToggle>
+      ) }
+      <DropdownMenu
+        positionFixed
+        modifiers={{ preventOverflow: { boundariesElement: undefined } }}
+        right={true}
+      >
+        <DropdownItem
+          onClick={onClickRename}
+        >
+          <i className="icon-fw icon-action-redo grw-page-control-dropdown-icon"></i>
+          {t('Rename')}
+        </DropdownItem>
+
+        <DropdownItem divider/>
+        <DropdownItem
+          className='pt-2 grw-page-control-dropdown-item text-danger'
+          onClick={() => {}}
+        >
+          <i className="icon-fw icon-trash grw-page-control-dropdown-icon"></i>
+          {t('Delete')}
+        </DropdownItem>
+      </DropdownMenu>
+    </Dropdown>
+  );
+};
+
+export default BookmarkFolderItemControl;

+ 1 - 0
packages/app/src/server/models/bookmark-folder.ts

@@ -16,6 +16,7 @@ const logger = loggerFactory('growi:models:bookmark-folder');
 export interface BookmarkFolderItems {
   _id: string
   name: string
+  parent: string
   children: this[]
 }
 export interface BookmarkFolderDocument extends Document {

+ 1 - 0
packages/app/src/server/routes/apiv3/bookmark-folder.ts

@@ -54,6 +54,7 @@ module.exports = (crowi) => {
       const bookmarkFolderItems = bookmarkFolders.map(bookmarkFolder => ({
         _id: bookmarkFolder._id,
         name: bookmarkFolder.name,
+        parent: bookmarkFolder.parent,
         children: bookmarkFolder.children,
       }));
       return res.apiv3({ bookmarkFolderItems });