| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- import {
- FC, useCallback, useEffect, useState,
- } from 'react';
- import { useTranslation } from 'next-i18next';
- import { DropdownToggle } from 'reactstrap';
- import { toastError, toastSuccess } from '~/client/util/apiNotification';
- import { apiv3Delete, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
- import CountBadge from '~/components/Common/CountBadge';
- import FolderIcon from '~/components/Icons/FolderIcon';
- import TriangleIcon from '~/components/Icons/TriangleIcon';
- import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
- import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder';
- import BookmarkFolderItemControl from './BookmarkFolderItemControl';
- import BookmarkFolderNameInput from './BookmarkFolderNameInput';
- import DeleteBookmarkFolderModal from './DeleteBookmarkFolderModal';
- type BookmarkFolderItemProps = {
- bookmarkFolder: BookmarkFolderItems
- isOpen?: boolean
- }
- const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkFolderItemProps) => {
- const { bookmarkFolder, isOpen: _isOpen = false } = props;
- const { t } = useTranslation();
- const {
- name, _id: folderId, children, parent,
- } = bookmarkFolder;
- const [currentChildren, setCurrentChildren] = useState<BookmarkFolderItems[]>();
- const [targetFolder, setTargetFolder] = useState<string | null>(folderId);
- const [isOpen, setIsOpen] = useState(_isOpen);
- const { data: childBookmarkFolderData, mutate: mutateChildBookmarkData } = useSWRxBookamrkFolderAndChild(targetFolder);
- const { mutate: mutateParentBookmarkFolder } = useSWRxBookamrkFolderAndChild(parent);
- const [isRenameAction, setIsRenameAction] = useState<boolean>(false);
- const [isCreateAction, setIsCreateAction] = useState<boolean>(false);
- const [isDeleteFolderModalShown, setIsDeleteFolderModalShown] = useState<boolean>(false);
- const getChildCount = useCallback((): number => {
- if (currentChildren != null && currentChildren.length > children.length) {
- return currentChildren.length;
- }
- return children.length;
- }, [children.length, currentChildren]);
- useEffect(() => {
- if (childBookmarkFolderData != null) {
- mutateChildBookmarkData();
- setCurrentChildren(childBookmarkFolderData);
- }
- }, [childBookmarkFolderData, mutateChildBookmarkData]);
- const hasChildren = useCallback((): boolean => {
- if (currentChildren != null && currentChildren.length > children.length) {
- return currentChildren.length > 0;
- }
- return children.length > 0;
- }, [children.length, currentChildren]);
- const loadChildFolder = useCallback(async() => {
- setIsOpen(!isOpen);
- setTargetFolder(folderId);
- }, [folderId, isOpen]);
- const loadParent = useCallback(async() => {
- if (!isRenameAction) {
- if (parent != null) {
- await mutateParentBookmarkFolder();
- }
- // Reload root folder structure
- setTargetFolder(null);
- }
- else {
- await mutateParentBookmarkFolder();
- }
- }, [isRenameAction, mutateParentBookmarkFolder, parent]);
- // Rename for bookmark folder handler
- const onPressEnterHandlerForRename = useCallback(async(folderName: string) => {
- try {
- await apiv3Put('/bookmark-folder', { bookmarkFolderId: folderId, name: folderName, parent });
- loadParent();
- setIsRenameAction(false);
- toastSuccess(t('toaster.update_successed', { target: t('bookmark_folder.bookmark_folder') }));
- }
- catch (err) {
- toastError(err);
- }
- }, [folderId, loadParent, parent, t]);
- // Create new folder / subfolder handler
- const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => {
- try {
- await apiv3Post('/bookmark-folder', { name: folderName, parent: targetFolder });
- setIsOpen(true);
- setIsCreateAction(false);
- mutateChildBookmarkData();
- toastSuccess(t('toaster.create_succeeded', { target: t('bookmark_folder.bookmark_folder') }));
- }
- catch (err) {
- toastError(err);
- }
- }, [mutateChildBookmarkData, t, targetFolder]);
- // Delete Fodler handler
- const onClickDeleteButtonHandler = useCallback(async() => {
- try {
- await apiv3Delete(`/bookmark-folder/${folderId}`);
- setIsDeleteFolderModalShown(false);
- loadParent();
- toastSuccess(t('toaster.delete_succeeded', { target: t('bookmark_folder.bookmark_folder') }));
- }
- catch (err) {
- toastError(err);
- }
- }, [folderId, loadParent, t]);
- const onClickPlusButton = useCallback(async(e) => {
- e.stopPropagation();
- if (!isOpen && hasChildren()) {
- setIsOpen(true);
- }
- setIsCreateAction(true);
- }, [hasChildren, isOpen]);
- const renderChildFolder = () => {
- return isOpen && currentChildren?.map((childFolder) => {
- return (
- <div key={childFolder._id} className="grw-foldertree-item-children">
- <BookmarkFolderItem
- key={childFolder._id}
- bookmarkFolder={childFolder}
- />
- </div>
- );
- });
- };
- const onClickRenameHandler = useCallback(() => {
- setIsRenameAction(true);
- }, []);
- const onClickDeleteHandler = useCallback(() => {
- setIsDeleteFolderModalShown(true);
- }, []);
- const onDeleteFolderModalClose = useCallback(() => {
- setIsDeleteFolderModalShown(false);
- }, []);
- return (
- <div id={`grw-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" onClick={loadChildFolder}>
- <div className="grw-triangle-container d-flex justify-content-center">
- {hasChildren() && (
- <button
- type="button"
- className={`grw-foldertree-triangle-btn btn ${isOpen ? 'grw-foldertree-open' : ''}`}
- onClick={loadChildFolder}
- >
- <div className="d-flex justify-content-center">
- <TriangleIcon />
- </div>
- </button>
- )}
- </div>
- {
- <div>
- <FolderIcon isOpen={isOpen} />
- </div>
- }
- { isRenameAction ? (
- <BookmarkFolderNameInput
- onClickOutside={() => setIsRenameAction(false)}
- onPressEnter={onPressEnterHandlerForRename}
- value={name}
- />
- ) : (
- <>
- <div className='grw-foldertree-title-anchor pl-2' >
- <p className={'text-truncate m-auto '}>{name}</p>
- </div>
- {hasChildren() && (
- <div className="grw-foldertree-count-wrapper">
- <CountBadge count={ getChildCount() } />
- </div>
- )}
- </>
- )
- }
- <div className="grw-foldertree-control d-flex">
- <BookmarkFolderItemControl
- onClickRename={onClickRenameHandler}
- onClickDelete={onClickDeleteHandler}
- >
- <div onClick={e => e.stopPropagation()}>
- <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>
- </div>
- </BookmarkFolderItemControl>
- <button
- 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>
- </div>
- </li>
- {isCreateAction && (
- <div className="flex-fill">
- <BookmarkFolderNameInput
- onClickOutside={() => setIsCreateAction(false)}
- onPressEnter={onPressEnterHandlerForCreate}
- />
- </div>
- )}
- {
- renderChildFolder()
- }
- <DeleteBookmarkFolderModal
- bookmarkFolder={bookmarkFolder}
- isOpen={isDeleteFolderModalShown}
- onClickDeleteButton={onClickDeleteButtonHandler}
- onModalClose={onDeleteFolderModalClose}/>
- </div>
- );
- };
- export default BookmarkFolderItem;
|