|
@@ -10,7 +10,7 @@ import {
|
|
|
import { useTranslation } from 'next-i18next';
|
|
import { useTranslation } from 'next-i18next';
|
|
|
import Link from 'next/link';
|
|
import Link from 'next/link';
|
|
|
import { useRouter } from 'next/router';
|
|
import { useRouter } from 'next/router';
|
|
|
-import { useDrag, useDrop } from 'react-dnd';
|
|
|
|
|
|
|
+import { ConnectDragSource, useDrag, useDrop } from 'react-dnd';
|
|
|
import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
|
|
import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
|
|
|
|
|
|
|
|
import { bookmark, unbookmark, resumeRenameOperation } from '~/client/services/page-operation';
|
|
import { bookmark, unbookmark, resumeRenameOperation } from '~/client/services/page-operation';
|
|
@@ -50,6 +50,7 @@ interface ItemProps {
|
|
|
onRenamed?(fromPath: string | undefined, toPath: string): void
|
|
onRenamed?(fromPath: string | undefined, toPath: string): void
|
|
|
onClickDuplicateMenuItem?(pageToDuplicate: IPageForPageDuplicateModal): void
|
|
onClickDuplicateMenuItem?(pageToDuplicate: IPageForPageDuplicateModal): void
|
|
|
onClickDeleteMenuItem?(pageToDelete: IPageToDeleteWithMeta): void
|
|
onClickDeleteMenuItem?(pageToDelete: IPageToDeleteWithMeta): void
|
|
|
|
|
+ itemRef?
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Utility to mark target
|
|
// Utility to mark target
|
|
@@ -75,10 +76,13 @@ const markTarget = (children: ItemNode[], targetPathOrId?: Nullable<string>): vo
|
|
|
* @param newParentPagePath
|
|
* @param newParentPagePath
|
|
|
* @returns
|
|
* @returns
|
|
|
*/
|
|
*/
|
|
|
|
|
+
|
|
|
|
|
+//
|
|
|
const getNewPathAfterMoved = (droppedPagePath: string, newParentPagePath: string): string => {
|
|
const getNewPathAfterMoved = (droppedPagePath: string, newParentPagePath: string): string => {
|
|
|
const pageTitle = nodePath.basename(droppedPagePath);
|
|
const pageTitle = nodePath.basename(droppedPagePath);
|
|
|
return nodePath.join(newParentPagePath, pageTitle);
|
|
return nodePath.join(newParentPagePath, pageTitle);
|
|
|
};
|
|
};
|
|
|
|
|
+//
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Return whether the fromPage could be moved under the newParentPage
|
|
* Return whether the fromPage could be moved under the newParentPage
|
|
@@ -87,6 +91,8 @@ const getNewPathAfterMoved = (droppedPagePath: string, newParentPagePath: string
|
|
|
* @param printLog
|
|
* @param printLog
|
|
|
* @returns
|
|
* @returns
|
|
|
*/
|
|
*/
|
|
|
|
|
+
|
|
|
|
|
+//
|
|
|
const isDroppable = (fromPage?: Partial<IPageHasId>, newParentPage?: Partial<IPageHasId>, printLog = false): boolean => {
|
|
const isDroppable = (fromPage?: Partial<IPageHasId>, newParentPage?: Partial<IPageHasId>, printLog = false): boolean => {
|
|
|
if (fromPage == null || newParentPage == null || fromPage.path == null || newParentPage.path == null) {
|
|
if (fromPage == null || newParentPage == null || fromPage.path == null || newParentPage.path == null) {
|
|
|
if (printLog) {
|
|
if (printLog) {
|
|
@@ -98,6 +104,7 @@ const isDroppable = (fromPage?: Partial<IPageHasId>, newParentPage?: Partial<IPa
|
|
|
const newPathAfterMoved = getNewPathAfterMoved(fromPage.path, newParentPage.path);
|
|
const newPathAfterMoved = getNewPathAfterMoved(fromPage.path, newParentPage.path);
|
|
|
return pagePathUtils.canMoveByPath(fromPage.path, newPathAfterMoved) && !pagePathUtils.isUsersTopPage(newParentPage.path);
|
|
return pagePathUtils.canMoveByPath(fromPage.path, newPathAfterMoved) && !pagePathUtils.isUsersTopPage(newParentPage.path);
|
|
|
};
|
|
};
|
|
|
|
|
+//
|
|
|
|
|
|
|
|
// Component wrapper to make a child element not draggable
|
|
// Component wrapper to make a child element not draggable
|
|
|
// https://github.com/react-dnd/react-dnd/issues/335
|
|
// https://github.com/react-dnd/react-dnd/issues/335
|
|
@@ -140,6 +147,7 @@ const SimpleItem: FC<ItemProps> = (props: ItemProps) => {
|
|
|
const isChildrenLoaded = currentChildren?.length > 0;
|
|
const isChildrenLoaded = currentChildren?.length > 0;
|
|
|
const hasDescendants = descendantCount > 0 || isChildrenLoaded;
|
|
const hasDescendants = descendantCount > 0 || isChildrenLoaded;
|
|
|
|
|
|
|
|
|
|
+ //
|
|
|
// to re-show hidden item when useDrag end() callback
|
|
// to re-show hidden item when useDrag end() callback
|
|
|
const displayDroppedItemByPageId = useCallback((pageId) => {
|
|
const displayDroppedItemByPageId = useCallback((pageId) => {
|
|
|
const target = document.getElementById(`pagetree-item-${pageId}`);
|
|
const target = document.getElementById(`pagetree-item-${pageId}`);
|
|
@@ -147,34 +155,38 @@ const SimpleItem: FC<ItemProps> = (props: ItemProps) => {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // // wait 500ms to avoid removing before d-none is set by useDrag end() callback
|
|
|
|
|
|
|
+ // // wait 500ms to avoid removing before d-none is set by useDrag end() callback
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
target.classList.remove('d-none');
|
|
target.classList.remove('d-none');
|
|
|
}, 500);
|
|
}, 500);
|
|
|
}, []);
|
|
}, []);
|
|
|
-
|
|
|
|
|
- const [, drag] = useDrag({
|
|
|
|
|
- type: 'PAGE_TREE',
|
|
|
|
|
- item: { page },
|
|
|
|
|
- canDrag: () => {
|
|
|
|
|
- if (page.path == null) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- return !pagePathUtils.isUsersProtectedPages(page.path);
|
|
|
|
|
- },
|
|
|
|
|
- end: (item, monitor) => {
|
|
|
|
|
- // in order to set d-none to dropped Item
|
|
|
|
|
- const dropResult = monitor.getDropResult();
|
|
|
|
|
- if (dropResult != null) {
|
|
|
|
|
- setShouldHide(true);
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- collect: monitor => ({
|
|
|
|
|
- isDragging: monitor.isDragging(),
|
|
|
|
|
- canDrag: monitor.canDrag(),
|
|
|
|
|
- }),
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
|
|
+ //
|
|
|
|
|
+
|
|
|
|
|
+ // ここから切り分け開始
|
|
|
|
|
+ // const [, drag] = useDrag({
|
|
|
|
|
+ // type: 'PAGE_TREE',
|
|
|
|
|
+ // item: { page },
|
|
|
|
|
+ // canDrag: () => {
|
|
|
|
|
+ // if (page.path == null) {
|
|
|
|
|
+ // return false;
|
|
|
|
|
+ // }
|
|
|
|
|
+ // return !pagePathUtils.isUsersProtectedPages(page.path);
|
|
|
|
|
+ // },
|
|
|
|
|
+ // end: (item, monitor) => {
|
|
|
|
|
+ // // in order to set d-none to dropped Item
|
|
|
|
|
+ // const dropResult = monitor.getDropResult();
|
|
|
|
|
+ // if (dropResult != null) {
|
|
|
|
|
+ // setShouldHide(true);
|
|
|
|
|
+ // }
|
|
|
|
|
+ // },
|
|
|
|
|
+ // collect: monitor => ({
|
|
|
|
|
+ // isDragging: monitor.isDragging(),
|
|
|
|
|
+ // canDrag: monitor.canDrag(),
|
|
|
|
|
+ // }),
|
|
|
|
|
+ // });
|
|
|
|
|
+ //
|
|
|
|
|
+
|
|
|
|
|
+ //
|
|
|
const pageItemDropHandler = async(item: ItemNode) => {
|
|
const pageItemDropHandler = async(item: ItemNode) => {
|
|
|
const { page: droppedPage } = item;
|
|
const { page: droppedPage } = item;
|
|
|
|
|
|
|
@@ -219,31 +231,34 @@ const SimpleItem: FC<ItemProps> = (props: ItemProps) => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
-
|
|
|
|
|
- const [{ isOver }, drop] = useDrop<ItemNode, Promise<void>, { isOver: boolean }>(
|
|
|
|
|
- () => ({
|
|
|
|
|
- accept: 'PAGE_TREE',
|
|
|
|
|
- drop: pageItemDropHandler,
|
|
|
|
|
- hover: (item, monitor) => {
|
|
|
|
|
- // when a drag item is overlapped more than 1 sec, the drop target item will be opened.
|
|
|
|
|
- if (monitor.isOver()) {
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- if (monitor.isOver()) {
|
|
|
|
|
- setIsOpen(true);
|
|
|
|
|
- }
|
|
|
|
|
- }, 600);
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- canDrop: (item) => {
|
|
|
|
|
- const { page: droppedPage } = item;
|
|
|
|
|
- return isDroppable(droppedPage, page);
|
|
|
|
|
- },
|
|
|
|
|
- collect: monitor => ({
|
|
|
|
|
- isOver: monitor.isOver(),
|
|
|
|
|
- }),
|
|
|
|
|
- }),
|
|
|
|
|
- [page],
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ //
|
|
|
|
|
+
|
|
|
|
|
+ //
|
|
|
|
|
+ // const [{ isOver }, drop] = useDrop<ItemNode, Promise<void>, { isOver: boolean }>(
|
|
|
|
|
+ // () => ({
|
|
|
|
|
+ // accept: 'PAGE_TREE',
|
|
|
|
|
+ // drop: pageItemDropHandler,
|
|
|
|
|
+ // hover: (item, monitor) => {
|
|
|
|
|
+ // // when a drag item is overlapped more than 1 sec, the drop target item will be opened.
|
|
|
|
|
+ // if (monitor.isOver()) {
|
|
|
|
|
+ // setTimeout(() => {
|
|
|
|
|
+ // if (monitor.isOver()) {
|
|
|
|
|
+ // setIsOpen(true);
|
|
|
|
|
+ // }
|
|
|
|
|
+ // }, 600);
|
|
|
|
|
+ // }
|
|
|
|
|
+ // },
|
|
|
|
|
+ // canDrop: (item) => {
|
|
|
|
|
+ // const { page: droppedPage } = item;
|
|
|
|
|
+ // return isDroppable(droppedPage, page);
|
|
|
|
|
+ // },
|
|
|
|
|
+ // collect: monitor => ({
|
|
|
|
|
+ // isOver: monitor.isOver(),
|
|
|
|
|
+ // }),
|
|
|
|
|
+ // }),
|
|
|
|
|
+ // [page],
|
|
|
|
|
+ // );
|
|
|
|
|
+ //
|
|
|
|
|
|
|
|
|
|
|
|
|
const hasChildren = useCallback((): boolean => {
|
|
const hasChildren = useCallback((): boolean => {
|
|
@@ -434,6 +449,26 @@ const SimpleItem: FC<ItemProps> = (props: ItemProps) => {
|
|
|
const shouldShowAttentionIcon = page.processData != null ? shouldRecoverPagePaths(page.processData) : false;
|
|
const shouldShowAttentionIcon = page.processData != null ? shouldRecoverPagePaths(page.processData) : false;
|
|
|
const pageName = nodePath.basename(page.path ?? '') || '/';
|
|
const pageName = nodePath.basename(page.path ?? '') || '/';
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ const itemRef = props.itemRef;
|
|
|
|
|
+
|
|
|
|
|
+ console.log(itemRef);
|
|
|
|
|
+
|
|
|
|
|
+ // const [isLoading, setIsLoading] = useState(true);
|
|
|
|
|
+
|
|
|
|
|
+ // useEffect(() => {
|
|
|
|
|
+ // // someProp が非同期で設定されるまで待機
|
|
|
|
|
+ // if (itemRef !== undefined) {
|
|
|
|
|
+ // setIsLoading(false);
|
|
|
|
|
+ // }
|
|
|
|
|
+ // }, [itemRef]);
|
|
|
|
|
+
|
|
|
|
|
+ // if (isLoading) {
|
|
|
|
|
+ // return <div>Loading...</div>;
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ // 上のやり方だと、意識的にitemRefを設定しなかった場合におかしくなる
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<div
|
|
<div
|
|
|
id={`pagetree-item-${page._id}`}
|
|
id={`pagetree-item-${page._id}`}
|
|
@@ -441,8 +476,7 @@ const SimpleItem: FC<ItemProps> = (props: ItemProps) => {
|
|
|
className={`grw-pagetree-item-container ${shouldHide ? 'd-none' : ''}`}
|
|
className={`grw-pagetree-item-container ${shouldHide ? 'd-none' : ''}`}
|
|
|
>
|
|
>
|
|
|
<li
|
|
<li
|
|
|
- // ref={(c) => { drag(c); drop(c) }}
|
|
|
|
|
- { props.hoge ?? '' }
|
|
|
|
|
|
|
+ ref={itemRef}
|
|
|
className={`list-group-item list-group-item-action border-0 py-0 pr-3 d-flex align-items-center
|
|
className={`list-group-item list-group-item-action border-0 py-0 pr-3 d-flex align-items-center
|
|
|
${page.isTarget ? 'grw-pagetree-current-page-item' : ''}`}
|
|
${page.isTarget ? 'grw-pagetree-current-page-item' : ''}`}
|
|
|
id={page.isTarget ? 'grw-pagetree-current-page-item' : `grw-pagetree-list-${page._id}`}
|
|
id={page.isTarget ? 'grw-pagetree-current-page-item' : `grw-pagetree-list-${page._id}`}
|
|
@@ -559,6 +593,7 @@ const SimpleItem: FC<ItemProps> = (props: ItemProps) => {
|
|
|
onRenamed={onRenamed}
|
|
onRenamed={onRenamed}
|
|
|
onClickDuplicateMenuItem={onClickDuplicateMenuItem}
|
|
onClickDuplicateMenuItem={onClickDuplicateMenuItem}
|
|
|
onClickDeleteMenuItem={onClickDeleteMenuItem}
|
|
onClickDeleteMenuItem={onClickDeleteMenuItem}
|
|
|
|
|
+ itemRef={itemRef}
|
|
|
/>
|
|
/>
|
|
|
{isCreating && (currentChildren.length - 1 === index) && (
|
|
{isCreating && (currentChildren.length - 1 === index) && (
|
|
|
<div className="text-muted text-center">
|
|
<div className="text-muted text-center">
|