|
@@ -10,6 +10,8 @@ import nodePath from 'path';
|
|
|
|
|
|
|
|
import { pathUtils, pagePathUtils } from '@growi/core';
|
|
import { pathUtils, pagePathUtils } from '@growi/core';
|
|
|
|
|
|
|
|
|
|
+import loggerFactory from '~/utils/logger';
|
|
|
|
|
+
|
|
|
import { toastWarning, toastError, toastSuccess } from '~/client/util/apiNotification';
|
|
import { toastWarning, toastError, toastSuccess } from '~/client/util/apiNotification';
|
|
|
|
|
|
|
|
import { useSWRxPageChildren } from '~/stores/page-listing';
|
|
import { useSWRxPageChildren } from '~/stores/page-listing';
|
|
@@ -21,6 +23,11 @@ import { bookmark, unbookmark } from '~/client/services/page-operation';
|
|
|
import ClosableTextInput, { AlertInfo, AlertType } from '../../Common/ClosableTextInput';
|
|
import ClosableTextInput, { AlertInfo, AlertType } from '../../Common/ClosableTextInput';
|
|
|
import { PageItemControl } from '../../Common/Dropdown/PageItemControl';
|
|
import { PageItemControl } from '../../Common/Dropdown/PageItemControl';
|
|
|
import { ItemNode } from './ItemNode';
|
|
import { ItemNode } from './ItemNode';
|
|
|
|
|
+import { IPageHasId } from '~/interfaces/page';
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+const logger = loggerFactory('growi:cli:Item');
|
|
|
|
|
+
|
|
|
|
|
|
|
|
interface ItemProps {
|
|
interface ItemProps {
|
|
|
isEnableActions: boolean
|
|
isEnableActions: boolean
|
|
@@ -55,6 +62,37 @@ const bookmarkMenuItemClickHandler = async(_pageId: string, _newValue: boolean):
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * Return new page path after the droppedPagePath is moved under the newParentPagePath
|
|
|
|
|
+ * @param droppedPagePath
|
|
|
|
|
+ * @param newParentPagePath
|
|
|
|
|
+ * @returns
|
|
|
|
|
+ */
|
|
|
|
|
+const getNewPathAfterMoved = (droppedPagePath: string, newParentPagePath: string): string => {
|
|
|
|
|
+ const pageTitle = nodePath.basename(droppedPagePath);
|
|
|
|
|
+ return nodePath.join(newParentPagePath, pageTitle);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Return whether the fromPage could be moved under the newParentPage
|
|
|
|
|
+ * @param fromPage
|
|
|
|
|
+ * @param newParentPage
|
|
|
|
|
+ * @param printLog
|
|
|
|
|
+ * @returns
|
|
|
|
|
+ */
|
|
|
|
|
+const canMoveUnderNewParent = (fromPage?: Partial<IPageHasId>, newParentPage?: Partial<IPageHasId>, printLog = false): boolean => {
|
|
|
|
|
+ if (fromPage == null || newParentPage == null || fromPage.path == null || newParentPage.path == null) {
|
|
|
|
|
+ if (printLog) {
|
|
|
|
|
+ logger.warn('Any of page, page.path or droppedPage.path is null');
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const newPathAfterMoved = getNewPathAfterMoved(fromPage.path, newParentPage.path);
|
|
|
|
|
+ return pagePathUtils.canMoveByPath(fromPage.path, newPathAfterMoved);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
type ItemCountProps = {
|
|
type ItemCountProps = {
|
|
|
descendantCount: number
|
|
descendantCount: number
|
|
|
}
|
|
}
|
|
@@ -119,16 +157,18 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
|
|
|
}),
|
|
}),
|
|
|
}));
|
|
}));
|
|
|
|
|
|
|
|
- const pageItemDropHandler = async(item, monitor) => {
|
|
|
|
|
- if (page == null || page.path == null) {
|
|
|
|
|
|
|
+ const pageItemDropHandler = async(item: ItemNode) => {
|
|
|
|
|
+ const { page: droppedPage } = item;
|
|
|
|
|
+
|
|
|
|
|
+ if (!canMoveUnderNewParent(droppedPage, page, true)) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const { page: droppedPage } = item;
|
|
|
|
|
|
|
+ if (droppedPage.path == null || page.path == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const pageTitle = nodePath.basename(droppedPage.path);
|
|
|
|
|
- const newParentPath = page.path;
|
|
|
|
|
- const newPagePath = nodePath.join(newParentPath, pageTitle);
|
|
|
|
|
|
|
+ const newPagePath = getNewPathAfterMoved(droppedPage.path, page.path);
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
await apiv3Put('/pages/rename', {
|
|
await apiv3Put('/pages/rename', {
|
|
@@ -157,7 +197,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const [{ isOver }, drop] = useDrop(() => ({
|
|
|
|
|
|
|
+ const [{ isOver }, drop] = useDrop<ItemNode, Promise<void>, { isOver: boolean }>(() => ({
|
|
|
accept: 'PAGE_TREE',
|
|
accept: 'PAGE_TREE',
|
|
|
drop: pageItemDropHandler,
|
|
drop: pageItemDropHandler,
|
|
|
hover: (item, monitor) => {
|
|
hover: (item, monitor) => {
|
|
@@ -170,6 +210,10 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
|
|
|
}, 1000);
|
|
}, 1000);
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
+ canDrop: (item) => {
|
|
|
|
|
+ const { page: droppedPage } = item;
|
|
|
|
|
+ return canMoveUnderNewParent(droppedPage, page);
|
|
|
|
|
+ },
|
|
|
collect: monitor => ({
|
|
collect: monitor => ({
|
|
|
isOver: monitor.isOver(),
|
|
isOver: monitor.isOver(),
|
|
|
}),
|
|
}),
|