payload-nested-docs-page-tree 1.2.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +136 -47
- package/dist/components/PageTreeContext.d.ts +13 -1
- package/dist/components/PageTreeContext.js +11 -1
- package/dist/components/PageTreeContext.js.map +1 -1
- package/dist/components/PageTreeListView.client.d.ts +3 -4
- package/dist/components/PageTreeListView.client.js +645 -381
- package/dist/components/PageTreeListView.client.js.map +1 -1
- package/dist/components/PageTreeListView.module.css +90 -67
- package/dist/components/PageTreeListView.server.js +18 -28
- package/dist/components/PageTreeListView.server.js.map +1 -1
- package/dist/components/PageTreeTitleCell.js +80 -18
- package/dist/components/PageTreeTitleCell.js.map +1 -1
- package/dist/endpoints/createMovePageEndpoint.d.ts +2 -0
- package/dist/endpoints/createMovePageEndpoint.js +166 -123
- package/dist/endpoints/createMovePageEndpoint.js.map +1 -1
- package/dist/endpoints/createReorderPageEndpoint.d.ts +8 -0
- package/dist/endpoints/createReorderPageEndpoint.js +703 -0
- package/dist/endpoints/createReorderPageEndpoint.js.map +1 -0
- package/dist/endpoints/shared.d.ts +40 -0
- package/dist/endpoints/shared.js +93 -0
- package/dist/endpoints/shared.js.map +1 -0
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/rsc.js.map +1 -1
- package/dist/index.js +105 -26
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +9 -2
- package/dist/types.js +0 -2
- package/dist/types.js.map +1 -1
- package/dist/utilities/badgeConfig.js.map +1 -1
- package/dist/utilities/diagnostics.d.ts +42 -0
- package/dist/utilities/diagnostics.js +83 -0
- package/dist/utilities/diagnostics.js.map +1 -0
- package/dist/utilities/dropTargets.d.ts +2 -2
- package/dist/utilities/dropTargets.js +9 -7
- package/dist/utilities/dropTargets.js.map +1 -1
- package/dist/utilities/pageTree.js +4 -6
- package/dist/utilities/pageTree.js.map +1 -1
- package/dist/utilities/pageTreeCollision.d.ts +6 -0
- package/dist/utilities/pageTreeCollision.js +39 -4
- package/dist/utilities/pageTreeCollision.js.map +1 -1
- package/dist/utilities/pageTreeConfig.js.map +1 -1
- package/dist/utilities/status.d.ts +1 -1
- package/dist/utilities/status.js +7 -3
- package/dist/utilities/status.js.map +1 -1
- package/package.json +13 -12
|
@@ -1,105 +1,141 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { DndContext, DragOverlay, PointerSensor, useDroppable, useSensor, useSensors } from '@dnd-kit/core';
|
|
4
|
-
import {
|
|
4
|
+
import { arrayMove, SortableContext, useSortable } from '@dnd-kit/sortable';
|
|
5
|
+
import { DefaultListView, ListQueryProvider, Pill, SelectAll, SelectRow, SortHeader, toast, useConfig, useLocale, usePreferences, useTranslation } from '@payloadcms/ui';
|
|
5
6
|
import { useRouter, useSearchParams } from 'next/navigation';
|
|
6
7
|
import React from 'react';
|
|
7
|
-
import {
|
|
8
|
-
import { buildInsertDropTargets, getDropTargetParentDoc, getOrderPlacementFromDropTarget } from '../utilities/dropTargets.js';
|
|
8
|
+
import { buildInsertDropTargets, getDropTargetParentDoc } from '../utilities/dropTargets.js';
|
|
9
9
|
import { CANCEL_DRAG_MESSAGE, getDropValidation } from '../utilities/moveValidation.js';
|
|
10
10
|
import { buildDocSlugPath, buildPageTreeDocs, buildProspectiveDocSlugPath, getDocDisplayLabel, getVisibleTreeDocs } from '../utilities/pageTree.js';
|
|
11
11
|
import { pageTreeCollisionDetectionStrategy } from '../utilities/pageTreeCollision.js';
|
|
12
12
|
import { getPageTreeBadgeColor, getPageTreeBadgeLabel, getPageTreeDisplayStatus } from '../utilities/status.js';
|
|
13
|
-
import { PageTreeProvider } from './PageTreeContext.js';
|
|
13
|
+
import { PageTreeProvider, PageTreeRowDndProvider } from './PageTreeContext.js';
|
|
14
14
|
import styles from './PageTreeListView.module.css';
|
|
15
15
|
import { PageTreeTitleCell } from './PageTreeTitleCell.js';
|
|
16
|
-
const MANUAL_ORDER_COLUMN_ACCESSOR = '_dragHandle';
|
|
17
16
|
const SILENT_MOVE_MESSAGES = new Set([
|
|
18
17
|
CANCEL_DRAG_MESSAGE
|
|
19
18
|
]);
|
|
19
|
+
const ALREADY_ROOT_MESSAGE = 'This page is already in root level.';
|
|
20
|
+
function getParentMovePreferenceKey(collectionSlug) {
|
|
21
|
+
return `payload-nested-docs-page-tree:${collectionSlug}:parentMoveHandle`;
|
|
22
|
+
}
|
|
20
23
|
function getRowDropID(rowID) {
|
|
21
24
|
return `page-drop:${rowID}`;
|
|
22
25
|
}
|
|
23
|
-
function
|
|
24
|
-
return
|
|
26
|
+
function getSortableRowID(rowID) {
|
|
27
|
+
return `page-sort:${rowID}`;
|
|
25
28
|
}
|
|
26
|
-
function
|
|
27
|
-
return typeof
|
|
29
|
+
function getDiagnosticString(value) {
|
|
30
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
28
31
|
}
|
|
29
|
-
function
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
isValid: true,
|
|
34
|
-
parentID: null
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
if (targetDoc.__pageTreeID === activeDoc.__pageTreeID) {
|
|
38
|
-
return {
|
|
39
|
-
isValid: false,
|
|
40
|
-
message: CANCEL_DRAG_MESSAGE,
|
|
41
|
-
parentID: targetDoc.__pageTreeID
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
if (targetDoc.__pageTreeAncestorIDs.includes(activeDoc.__pageTreeID)) {
|
|
45
|
-
return {
|
|
46
|
-
isValid: false,
|
|
47
|
-
message: 'A document cannot be moved under one of its descendants.',
|
|
48
|
-
parentID: targetDoc.__pageTreeID
|
|
49
|
-
};
|
|
32
|
+
function getPageTreeDragData(value) {
|
|
33
|
+
if (!value || typeof value !== 'object') {
|
|
34
|
+
return {};
|
|
50
35
|
}
|
|
36
|
+
const data = value;
|
|
37
|
+
const dragType = data.dragType === 'move' || data.dragType === 'order' ? data.dragType : undefined;
|
|
38
|
+
const parentID = data.parentID === null || typeof data.parentID === 'string' ? data.parentID : undefined;
|
|
39
|
+
const rowID = typeof data.rowID === 'string' ? data.rowID : undefined;
|
|
51
40
|
return {
|
|
52
|
-
|
|
53
|
-
parentID
|
|
41
|
+
dragType,
|
|
42
|
+
parentID,
|
|
43
|
+
rowID
|
|
54
44
|
};
|
|
55
45
|
}
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return getManualOrderDropValidation({
|
|
60
|
-
activeDoc,
|
|
61
|
-
targetDoc
|
|
62
|
-
});
|
|
46
|
+
function getSortableTransform(transform) {
|
|
47
|
+
if (!transform) {
|
|
48
|
+
return undefined;
|
|
63
49
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
});
|
|
50
|
+
const scaleX = transform.scaleX ?? 1;
|
|
51
|
+
const scaleY = transform.scaleY ?? 1;
|
|
52
|
+
return `translate3d(${transform.x}px, ${transform.y}px, 0) scaleX(${scaleX}) scaleY(${scaleY})`;
|
|
68
53
|
}
|
|
69
|
-
function
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
54
|
+
function getVisibleSubtreeIndexRange(docs, rootIndex) {
|
|
55
|
+
const rootDoc = docs[rootIndex];
|
|
56
|
+
if (!rootDoc) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
let end = rootIndex;
|
|
60
|
+
for(let index = rootIndex + 1; index < docs.length; index += 1){
|
|
61
|
+
if (!docs[index]?.__pageTreeAncestorIDs.includes(rootDoc.__pageTreeID)) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
end = index;
|
|
80
65
|
}
|
|
81
66
|
return {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
previousSiblingID: docs.slice(0, activeIndex).filter(siblingIsNotMoving).at(-1)?.__pageTreeID ?? null
|
|
67
|
+
end,
|
|
68
|
+
start: rootIndex
|
|
85
69
|
};
|
|
86
70
|
}
|
|
87
|
-
function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const targetDocID = placement.previousSiblingID ?? placement.nextSiblingID;
|
|
93
|
-
if (!targetDocID) {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
const targetDoc = docsByID.get(targetDocID);
|
|
97
|
-
if (!targetDoc) {
|
|
71
|
+
function getRangeRect(args) {
|
|
72
|
+
const { activeNodeRect, activeRange, range, rects } = args;
|
|
73
|
+
const firstRect = rects[range.start] ?? (range.start === activeRange.start ? activeNodeRect : undefined);
|
|
74
|
+
const lastRect = rects[range.end] ?? (range.end === activeRange.end ? activeNodeRect : undefined);
|
|
75
|
+
if (!firstRect || !lastRect) {
|
|
98
76
|
return null;
|
|
99
77
|
}
|
|
100
78
|
return {
|
|
101
|
-
|
|
102
|
-
|
|
79
|
+
bottom: lastRect.top + lastRect.height,
|
|
80
|
+
height: lastRect.top + lastRect.height - firstRect.top,
|
|
81
|
+
top: firstRect.top
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function isIndexInRange(index, range) {
|
|
85
|
+
return index >= range.start && index <= range.end;
|
|
86
|
+
}
|
|
87
|
+
function buildPageTreeSortingStrategy(docs) {
|
|
88
|
+
return ({ activeIndex, activeNodeRect, index, overIndex, rects })=>{
|
|
89
|
+
const activeRange = getVisibleSubtreeIndexRange(docs, activeIndex);
|
|
90
|
+
const overRange = getVisibleSubtreeIndexRange(docs, overIndex);
|
|
91
|
+
if (!activeRange || !overRange || activeRange.start === overRange.start) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const activeRect = getRangeRect({
|
|
95
|
+
activeNodeRect,
|
|
96
|
+
activeRange,
|
|
97
|
+
range: activeRange,
|
|
98
|
+
rects
|
|
99
|
+
});
|
|
100
|
+
const overRect = getRangeRect({
|
|
101
|
+
activeNodeRect,
|
|
102
|
+
activeRange,
|
|
103
|
+
range: overRange,
|
|
104
|
+
rects
|
|
105
|
+
});
|
|
106
|
+
if (!activeRect || !overRect) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
if (isIndexInRange(index, activeRange)) {
|
|
110
|
+
return {
|
|
111
|
+
scaleX: 1,
|
|
112
|
+
scaleY: 1,
|
|
113
|
+
x: 0,
|
|
114
|
+
y: activeRange.start < overRange.start ? overRect.bottom - activeRect.bottom : overRect.top - activeRect.top
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (activeRange.start < overRange.start && index > activeRange.end && index <= overRange.end) {
|
|
118
|
+
return {
|
|
119
|
+
scaleX: 1,
|
|
120
|
+
scaleY: 1,
|
|
121
|
+
x: 0,
|
|
122
|
+
y: -activeRect.height
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (activeRange.start > overRange.start && index >= overRange.start && index < activeRange.start) {
|
|
126
|
+
return {
|
|
127
|
+
scaleX: 1,
|
|
128
|
+
scaleY: 1,
|
|
129
|
+
x: 0,
|
|
130
|
+
y: activeRect.height
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
scaleX: 1,
|
|
135
|
+
scaleY: 1,
|
|
136
|
+
x: 0,
|
|
137
|
+
y: 0
|
|
138
|
+
};
|
|
103
139
|
};
|
|
104
140
|
}
|
|
105
141
|
function buildPaginatedData(docs, limit, requestedPage) {
|
|
@@ -135,8 +171,12 @@ function normalizePositiveInt(value, fallback) {
|
|
|
135
171
|
const parsedValue = Number.parseInt(value, 10);
|
|
136
172
|
return Number.isNaN(parsedValue) || parsedValue <= 0 ? fallback : parsedValue;
|
|
137
173
|
}
|
|
138
|
-
function
|
|
139
|
-
|
|
174
|
+
function isOrderableSortActive(args) {
|
|
175
|
+
const { orderableFieldName, sort } = args;
|
|
176
|
+
return Boolean(orderableFieldName && (sort === orderableFieldName || sort === `-${orderableFieldName}`));
|
|
177
|
+
}
|
|
178
|
+
function ensureUseAsTitleColumn(columnState, useAsTitle) {
|
|
179
|
+
return columnState.map((column)=>column.accessor === useAsTitle ? {
|
|
140
180
|
...column,
|
|
141
181
|
active: true
|
|
142
182
|
} : column);
|
|
@@ -161,6 +201,191 @@ function getSelectableRowData(doc) {
|
|
|
161
201
|
function shouldSilenceMoveMessage(message) {
|
|
162
202
|
return typeof message === 'string' && SILENT_MOVE_MESSAGES.has(message);
|
|
163
203
|
}
|
|
204
|
+
function getVisibleSubtreeRowIDs(docs, rowID) {
|
|
205
|
+
const rootIndex = docs.findIndex((doc)=>doc.__pageTreeID === rowID);
|
|
206
|
+
if (rootIndex < 0) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
const rowIDs = [
|
|
210
|
+
rowID
|
|
211
|
+
];
|
|
212
|
+
for (const doc of docs.slice(rootIndex + 1)){
|
|
213
|
+
if (!doc.__pageTreeAncestorIDs.includes(rowID)) {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
rowIDs.push(doc.__pageTreeID);
|
|
217
|
+
}
|
|
218
|
+
return rowIDs;
|
|
219
|
+
}
|
|
220
|
+
function buildOptimisticOrderRowIDs(args) {
|
|
221
|
+
const { activeRowID, docs, targetRowID } = args;
|
|
222
|
+
if (activeRowID === targetRowID) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
const activeDoc = docs.find((doc)=>doc.__pageTreeID === activeRowID);
|
|
226
|
+
const targetDoc = docs.find((doc)=>doc.__pageTreeID === targetRowID);
|
|
227
|
+
if (!activeDoc || !targetDoc || activeDoc.__pageTreeParentID !== targetDoc.__pageTreeParentID) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const activeBlockIDs = getVisibleSubtreeRowIDs(docs, activeRowID);
|
|
231
|
+
const targetBlockIDs = getVisibleSubtreeRowIDs(docs, targetRowID);
|
|
232
|
+
if (activeBlockIDs.length === 0 || targetBlockIDs.length === 0 || targetBlockIDs.includes(activeRowID)) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const currentRowIDs = docs.map((doc)=>doc.__pageTreeID);
|
|
236
|
+
if (activeBlockIDs.length === 1 && targetBlockIDs.length === 1) {
|
|
237
|
+
const activeIndex = currentRowIDs.indexOf(activeRowID);
|
|
238
|
+
const targetIndex = currentRowIDs.indexOf(targetRowID);
|
|
239
|
+
return activeIndex < 0 || targetIndex < 0 ? null : arrayMove(currentRowIDs, activeIndex, targetIndex);
|
|
240
|
+
}
|
|
241
|
+
const remainingRowIDs = currentRowIDs.filter((rowID)=>!activeBlockIDs.includes(rowID));
|
|
242
|
+
const activeIndex = currentRowIDs.indexOf(activeRowID);
|
|
243
|
+
const targetIndex = currentRowIDs.indexOf(targetRowID);
|
|
244
|
+
const targetAnchorID = activeIndex < targetIndex ? targetBlockIDs[targetBlockIDs.length - 1] : targetBlockIDs[0];
|
|
245
|
+
if (!targetAnchorID) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
const targetAnchorIndex = remainingRowIDs.indexOf(targetAnchorID);
|
|
249
|
+
if (targetAnchorIndex < 0) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const insertIndex = activeIndex < targetIndex ? targetAnchorIndex + 1 : targetAnchorIndex;
|
|
253
|
+
return [
|
|
254
|
+
...remainingRowIDs.slice(0, insertIndex),
|
|
255
|
+
...activeBlockIDs,
|
|
256
|
+
...remainingRowIDs.slice(insertIndex)
|
|
257
|
+
];
|
|
258
|
+
}
|
|
259
|
+
function buildReorderRequestBody(args) {
|
|
260
|
+
const { activeRowID, docs, orderableFieldName, sort, targetRowID } = args;
|
|
261
|
+
if (!orderableFieldName || sort !== orderableFieldName && sort !== `-${orderableFieldName}`) {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
if (activeRowID === targetRowID) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const activeDoc = docs.find((doc)=>doc.__pageTreeID === activeRowID);
|
|
268
|
+
const targetDoc = docs.find((doc)=>doc.__pageTreeID === targetRowID);
|
|
269
|
+
if (!activeDoc || !targetDoc || activeDoc.__pageTreeParentID !== targetDoc.__pageTreeParentID) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
const siblingDocs = docs.filter((doc)=>doc.__pageTreeParentID === activeDoc.__pageTreeParentID);
|
|
273
|
+
const moveFromIndex = siblingDocs.findIndex((doc)=>doc.__pageTreeID === activeRowID);
|
|
274
|
+
const moveToIndex = siblingDocs.findIndex((doc)=>doc.__pageTreeID === targetRowID);
|
|
275
|
+
if (moveFromIndex < 0 || moveToIndex < 0 || moveFromIndex === moveToIndex) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
const newBeforeRow = moveToIndex > moveFromIndex ? siblingDocs[moveToIndex] : siblingDocs[moveToIndex - 1];
|
|
279
|
+
const newAfterRow = moveToIndex > moveFromIndex ? siblingDocs[moveToIndex + 1] : siblingDocs[moveToIndex];
|
|
280
|
+
const target = newBeforeRow ?? newAfterRow;
|
|
281
|
+
if (!target) {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
const getFrontendOrderEntry = (doc, index)=>{
|
|
285
|
+
const orderKey = doc[orderableFieldName];
|
|
286
|
+
return {
|
|
287
|
+
slug: getDiagnosticString(doc.slug),
|
|
288
|
+
index,
|
|
289
|
+
orderKey: typeof orderKey === 'string' && orderKey.length > 0 ? orderKey : null
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
const siblingDocsAfter = [
|
|
293
|
+
...siblingDocs
|
|
294
|
+
];
|
|
295
|
+
const [movedSiblingDoc] = siblingDocsAfter.splice(moveFromIndex, 1);
|
|
296
|
+
if (!movedSiblingDoc) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
siblingDocsAfter.splice(moveToIndex, 0, movedSiblingDoc);
|
|
300
|
+
const targetKey = target[orderableFieldName];
|
|
301
|
+
const newKeyWillBe = newBeforeRow && sort === orderableFieldName || !newBeforeRow && sort === `-${orderableFieldName}` ? 'greater' : 'less';
|
|
302
|
+
return {
|
|
303
|
+
docsToMove: [
|
|
304
|
+
activeRowID
|
|
305
|
+
],
|
|
306
|
+
frontendOrder: {
|
|
307
|
+
activeSlug: getDiagnosticString(activeDoc.slug),
|
|
308
|
+
after: siblingDocsAfter.map(getFrontendOrderEntry),
|
|
309
|
+
before: siblingDocs.map(getFrontendOrderEntry),
|
|
310
|
+
moveFromIndex,
|
|
311
|
+
moveToIndex,
|
|
312
|
+
newAfterRowSlug: newAfterRow ? getDiagnosticString(newAfterRow.slug) : null,
|
|
313
|
+
newBeforeRowSlug: newBeforeRow ? getDiagnosticString(newBeforeRow.slug) : null,
|
|
314
|
+
sort: sort ?? null,
|
|
315
|
+
targetSlug: getDiagnosticString(targetDoc.slug)
|
|
316
|
+
},
|
|
317
|
+
newKeyWillBe,
|
|
318
|
+
orderableFieldName,
|
|
319
|
+
target: {
|
|
320
|
+
id: target.__pageTreeID,
|
|
321
|
+
key: typeof targetKey === 'string' && targetKey.length > 0 ? targetKey : null
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function orderDocsByRowIDs(docs, orderRowIDs) {
|
|
326
|
+
if (!orderRowIDs) {
|
|
327
|
+
return docs;
|
|
328
|
+
}
|
|
329
|
+
const docsByID = new Map(docs.map((doc)=>[
|
|
330
|
+
doc.__pageTreeID,
|
|
331
|
+
doc
|
|
332
|
+
]));
|
|
333
|
+
const orderedDocs = orderRowIDs.flatMap((rowID)=>{
|
|
334
|
+
const doc = docsByID.get(rowID);
|
|
335
|
+
return doc ? [
|
|
336
|
+
doc
|
|
337
|
+
] : [];
|
|
338
|
+
});
|
|
339
|
+
return orderedDocs.length === docs.length ? orderedDocs : docs;
|
|
340
|
+
}
|
|
341
|
+
function getDropTargetValidation(args) {
|
|
342
|
+
const { activeDoc, docsByID, dropTarget } = args;
|
|
343
|
+
if (dropTarget.dropType === 'row') {
|
|
344
|
+
return getDropValidation({
|
|
345
|
+
activeDoc,
|
|
346
|
+
targetDoc: docsByID.get(dropTarget.rowID) ?? undefined
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
if (dropTarget.parentID === null) {
|
|
350
|
+
if (activeDoc.__pageTreeParentID === null) {
|
|
351
|
+
return {
|
|
352
|
+
isValid: false,
|
|
353
|
+
message: ALREADY_ROOT_MESSAGE,
|
|
354
|
+
parentID: null
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
isValid: true,
|
|
359
|
+
parentID: null
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
const targetDoc = docsByID.get(dropTarget.parentID);
|
|
363
|
+
if (!targetDoc) {
|
|
364
|
+
return {
|
|
365
|
+
isValid: false,
|
|
366
|
+
message: 'Could not resolve drop target.',
|
|
367
|
+
parentID: dropTarget.parentID
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
if (targetDoc.__pageTreeID === activeDoc.__pageTreeID) {
|
|
371
|
+
return {
|
|
372
|
+
isValid: false,
|
|
373
|
+
message: CANCEL_DRAG_MESSAGE,
|
|
374
|
+
parentID: targetDoc.__pageTreeID
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
if (targetDoc.__pageTreeAncestorIDs.includes(activeDoc.__pageTreeID)) {
|
|
378
|
+
return {
|
|
379
|
+
isValid: false,
|
|
380
|
+
message: 'A document cannot be moved under one of its descendants.',
|
|
381
|
+
parentID: targetDoc.__pageTreeID
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
isValid: true,
|
|
386
|
+
parentID: targetDoc.__pageTreeID
|
|
387
|
+
};
|
|
388
|
+
}
|
|
164
389
|
function getStatusClassName(status) {
|
|
165
390
|
if (status === 'changed' || status === 'draft' || status === 'published') {
|
|
166
391
|
return status;
|
|
@@ -192,25 +417,17 @@ function renderStatusBadge(args) {
|
|
|
192
417
|
})
|
|
193
418
|
}, doc.__pageTreeID ?? index);
|
|
194
419
|
}
|
|
195
|
-
function
|
|
196
|
-
return {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
function insertManualOrderColumn(columns, orderColumn) {
|
|
207
|
-
const selectColumnIndex = columns.findIndex((column)=>column.accessor === '_select');
|
|
208
|
-
const insertIndex = selectColumnIndex >= 0 ? selectColumnIndex + 1 : 0;
|
|
209
|
-
return [
|
|
210
|
-
...columns.slice(0, insertIndex),
|
|
211
|
-
orderColumn,
|
|
212
|
-
...columns.slice(insertIndex)
|
|
213
|
-
];
|
|
420
|
+
function ParentMoveToggle({ collectionSlug, enabled, onToggle }) {
|
|
421
|
+
return /*#__PURE__*/ _jsx(Pill, {
|
|
422
|
+
"aria-checked": enabled,
|
|
423
|
+
"aria-label": `${enabled ? 'Hide' : 'Show'} edit hierarchy handles`,
|
|
424
|
+
className: "pages-hierarchy-parent-move-toggle",
|
|
425
|
+
id: `pages-hierarchy-parent-move-toggle-${collectionSlug}`,
|
|
426
|
+
onClick: onToggle,
|
|
427
|
+
pillStyle: enabled ? 'dark' : 'light',
|
|
428
|
+
size: "small",
|
|
429
|
+
children: "Edit Hierarchy"
|
|
430
|
+
});
|
|
214
431
|
}
|
|
215
432
|
function buildTableColumns(args) {
|
|
216
433
|
const { badgeConfig, columnState, docs, enableRowSelections, homeIndicatorEnabled, orderableFieldName, parentFieldSlug, t, useAsTitle } = args;
|
|
@@ -261,21 +478,35 @@ function buildTableColumns(args) {
|
|
|
261
478
|
}, doc.__pageTreeID ?? index))
|
|
262
479
|
});
|
|
263
480
|
}
|
|
264
|
-
if (orderableFieldName
|
|
265
|
-
|
|
481
|
+
if (orderableFieldName) {
|
|
482
|
+
const orderableColumn = {
|
|
483
|
+
accessor: '_pageTreeOrderSort',
|
|
484
|
+
active: true,
|
|
485
|
+
field: {
|
|
486
|
+
hidden: true
|
|
487
|
+
},
|
|
488
|
+
Heading: /*#__PURE__*/ _jsx(SortHeader, {}),
|
|
489
|
+
renderedCells: docs.map((doc, index)=>/*#__PURE__*/ _jsx("span", {
|
|
490
|
+
"aria-hidden": "true",
|
|
491
|
+
className: "pages-hierarchy-order-sort-spacer"
|
|
492
|
+
}, doc.__pageTreeID ?? index))
|
|
493
|
+
};
|
|
494
|
+
const selectColumnIndex = columnsToUse.findIndex((column)=>column.accessor === '_select');
|
|
495
|
+
columnsToUse.splice(selectColumnIndex >= 0 ? selectColumnIndex + 1 : 0, 0, orderableColumn);
|
|
266
496
|
}
|
|
267
497
|
return columnsToUse;
|
|
268
498
|
}
|
|
269
|
-
function HierarchyInsertRow({ activeColumnsCount, activeDragRowID, dropTarget, dropValidation, isMovePending }) {
|
|
499
|
+
function HierarchyInsertRow({ activeColumnsCount, activeDragRowID, activeDropTargetID, dropTarget, dropValidation, isMovePending }) {
|
|
270
500
|
const { isOver, setNodeRef } = useDroppable({
|
|
271
501
|
id: dropTarget.dropID,
|
|
272
502
|
data: dropTarget,
|
|
273
503
|
disabled: isMovePending
|
|
274
504
|
});
|
|
275
505
|
const hasActiveDrag = Boolean(activeDragRowID);
|
|
506
|
+
const showInsertLine = isOver && activeDropTargetID === dropTarget.dropID;
|
|
276
507
|
return /*#__PURE__*/ _jsx("tr", {
|
|
277
508
|
className: "pages-hierarchy-insert-row",
|
|
278
|
-
"data-drag-over":
|
|
509
|
+
"data-drag-over": showInsertLine ? 'true' : 'false',
|
|
279
510
|
"data-drop-valid": hasActiveDrag ? dropValidation?.isValid ? 'true' : 'false' : undefined,
|
|
280
511
|
"data-page-tree-insert": "true",
|
|
281
512
|
children: /*#__PURE__*/ _jsx("td", {
|
|
@@ -288,7 +519,7 @@ function HierarchyInsertRow({ activeColumnsCount, activeDragRowID, dropTarget, d
|
|
|
288
519
|
})
|
|
289
520
|
});
|
|
290
521
|
}
|
|
291
|
-
function HierarchyTableRow({ activeColumns, activeDragRowID, doc, dropValidation, insertAfterDropID, insertBeforeDropID, isMovePending, rowIndex, titleCellAccessor }) {
|
|
522
|
+
function HierarchyTableRow({ activeColumns, activeDragRowID, activeDragType, canReorderRows, doc, dropValidation, insertAfterDropID, insertBeforeDropID, isMovePending, rowIndex, titleCellAccessor }) {
|
|
292
523
|
const { isOver, setNodeRef } = useDroppable({
|
|
293
524
|
id: getRowDropID(doc.__pageTreeID),
|
|
294
525
|
data: {
|
|
@@ -299,53 +530,110 @@ function HierarchyTableRow({ activeColumns, activeDragRowID, doc, dropValidation
|
|
|
299
530
|
},
|
|
300
531
|
disabled: isMovePending
|
|
301
532
|
});
|
|
302
|
-
const
|
|
533
|
+
const sortable = useSortable({
|
|
534
|
+
id: getSortableRowID(doc.__pageTreeID),
|
|
535
|
+
data: {
|
|
536
|
+
dragType: 'order',
|
|
537
|
+
parentID: doc.__pageTreeParentID,
|
|
538
|
+
rowID: doc.__pageTreeID
|
|
539
|
+
},
|
|
540
|
+
disabled: !canReorderRows || isMovePending || activeDragType === 'move'
|
|
541
|
+
});
|
|
542
|
+
const hasActiveMoveDrag = activeDragType === 'move' && Boolean(activeDragRowID);
|
|
303
543
|
const isActiveDragRow = activeDragRowID === doc.__pageTreeID;
|
|
544
|
+
const setRowRef = React.useCallback((element)=>{
|
|
545
|
+
setNodeRef(element);
|
|
546
|
+
sortable.setNodeRef(element);
|
|
547
|
+
}, [
|
|
548
|
+
setNodeRef,
|
|
549
|
+
sortable
|
|
550
|
+
]);
|
|
551
|
+
const rowDndValue = React.useMemo(()=>({
|
|
552
|
+
isOrderDragging: sortable.isDragging,
|
|
553
|
+
orderHandleAttributes: sortable.attributes,
|
|
554
|
+
orderHandleListeners: sortable.listeners,
|
|
555
|
+
orderHandleRef: sortable.setActivatorNodeRef
|
|
556
|
+
}), [
|
|
557
|
+
sortable.attributes,
|
|
558
|
+
sortable.isDragging,
|
|
559
|
+
sortable.listeners,
|
|
560
|
+
sortable.setActivatorNodeRef
|
|
561
|
+
]);
|
|
562
|
+
const sortableStyle = React.useMemo(()=>({
|
|
563
|
+
position: sortable.isDragging ? 'relative' : undefined,
|
|
564
|
+
transform: getSortableTransform(sortable.transform),
|
|
565
|
+
transition: activeDragType === 'order' ? sortable.transition : undefined,
|
|
566
|
+
zIndex: sortable.isDragging ? 1 : undefined
|
|
567
|
+
}), [
|
|
568
|
+
activeDragType,
|
|
569
|
+
sortable.isDragging,
|
|
570
|
+
sortable.transform,
|
|
571
|
+
sortable.transition
|
|
572
|
+
]);
|
|
304
573
|
return /*#__PURE__*/ _jsx("tr", {
|
|
305
574
|
className: `row-${rowIndex + 1}`,
|
|
306
|
-
"data-drag-over": isOver ? 'true' : 'false',
|
|
307
|
-
"data-drop-valid":
|
|
575
|
+
"data-drag-over": activeDragType === 'move' && isOver ? 'true' : 'false',
|
|
576
|
+
"data-drop-valid": hasActiveMoveDrag ? dropValidation?.isValid ? 'true' : 'false' : undefined,
|
|
308
577
|
"data-id": doc.id,
|
|
309
|
-
"data-is-drag-source": isActiveDragRow ? 'true' : 'false',
|
|
578
|
+
"data-is-drag-source": activeDragType === 'move' && isActiveDragRow ? 'true' : 'false',
|
|
579
|
+
"data-is-order-drag-source": sortable.isDragging ? 'true' : 'false',
|
|
310
580
|
"data-page-tree-row": "true",
|
|
311
|
-
ref:
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
"
|
|
318
|
-
|
|
319
|
-
|
|
581
|
+
ref: setRowRef,
|
|
582
|
+
style: sortableStyle,
|
|
583
|
+
children: /*#__PURE__*/ _jsx(PageTreeRowDndProvider, {
|
|
584
|
+
value: rowDndValue,
|
|
585
|
+
children: activeColumns.map((column, columnIndex)=>{
|
|
586
|
+
const { accessor } = column;
|
|
587
|
+
return /*#__PURE__*/ _jsx("td", {
|
|
588
|
+
className: `cell-${accessor.replace(/\./g, '__')}`,
|
|
589
|
+
"data-page-tree-title-cell": accessor === titleCellAccessor ? 'true' : undefined,
|
|
590
|
+
children: column.renderedCells?.[rowIndex] ?? null
|
|
591
|
+
}, columnIndex);
|
|
592
|
+
})
|
|
320
593
|
})
|
|
321
594
|
});
|
|
322
595
|
}
|
|
323
|
-
function HierarchyTable({ activeDragRowID, allDocsByID,
|
|
596
|
+
function HierarchyTable({ activeDragRowID, activeDragType, activeDropTarget, allDocsByID, canReorderRows, columns, data, isMovePending, titleCellAccessor }) {
|
|
324
597
|
const activeColumns = React.useMemo(()=>columns.filter((column)=>column?.active), [
|
|
325
598
|
columns
|
|
326
599
|
]);
|
|
327
|
-
const
|
|
328
|
-
|
|
600
|
+
const sortableItems = React.useMemo(()=>data.map((doc)=>getSortableRowID(doc.__pageTreeID)), [
|
|
601
|
+
data
|
|
602
|
+
]);
|
|
603
|
+
const sortingStrategy = React.useMemo(()=>buildPageTreeSortingStrategy(data), [
|
|
604
|
+
data
|
|
329
605
|
]);
|
|
330
606
|
const insertDropTargets = React.useMemo(()=>buildInsertDropTargets(data), [
|
|
331
607
|
data
|
|
332
608
|
]);
|
|
333
|
-
const
|
|
609
|
+
const insertDropTargetsByReferenceRowID = React.useMemo(()=>new Map(insertDropTargets.map((dropTarget)=>[
|
|
610
|
+
dropTarget.referenceRowID,
|
|
611
|
+
dropTarget
|
|
612
|
+
])), [
|
|
613
|
+
insertDropTargets
|
|
614
|
+
]);
|
|
615
|
+
const activeDropTargetID = activeDropTarget?.dropType === 'insert' ? activeDropTarget.dropID : null;
|
|
616
|
+
const activeDoc = activeDragType === 'move' && activeDragRowID ? allDocsByID.get(activeDragRowID) ?? null : null;
|
|
334
617
|
const rowDropValidationByID = React.useMemo(()=>{
|
|
335
618
|
if (!activeDoc) {
|
|
336
619
|
return new Map();
|
|
337
620
|
}
|
|
338
621
|
return new Map(data.map((doc)=>[
|
|
339
622
|
doc.__pageTreeID,
|
|
340
|
-
|
|
623
|
+
getDropTargetValidation({
|
|
341
624
|
activeDoc,
|
|
342
|
-
|
|
343
|
-
|
|
625
|
+
docsByID: allDocsByID,
|
|
626
|
+
dropTarget: {
|
|
627
|
+
dropType: 'row',
|
|
628
|
+
insertAfterDropID: null,
|
|
629
|
+
insertBeforeDropID: null,
|
|
630
|
+
rowID: doc.__pageTreeID
|
|
631
|
+
}
|
|
344
632
|
})
|
|
345
633
|
]));
|
|
346
634
|
}, [
|
|
347
635
|
activeDoc,
|
|
348
|
-
|
|
636
|
+
allDocsByID,
|
|
349
637
|
data
|
|
350
638
|
]);
|
|
351
639
|
const insertDropValidationByID = React.useMemo(()=>{
|
|
@@ -354,15 +642,14 @@ function HierarchyTable({ activeDragRowID, allDocsByID, allowSameParentDrops, co
|
|
|
354
642
|
}
|
|
355
643
|
return new Map(insertDropTargets.map((dropTarget)=>[
|
|
356
644
|
dropTarget.dropID,
|
|
357
|
-
|
|
645
|
+
getDropTargetValidation({
|
|
358
646
|
activeDoc,
|
|
359
|
-
|
|
360
|
-
|
|
647
|
+
docsByID: allDocsByID,
|
|
648
|
+
dropTarget
|
|
361
649
|
})
|
|
362
650
|
]));
|
|
363
651
|
}, [
|
|
364
652
|
activeDoc,
|
|
365
|
-
allowSameParentDrops,
|
|
366
653
|
allDocsByID,
|
|
367
654
|
insertDropTargets
|
|
368
655
|
]);
|
|
@@ -376,7 +663,6 @@ function HierarchyTable({ activeDragRowID, allDocsByID, allowSameParentDrops, co
|
|
|
376
663
|
'table-wrap pages-hierarchy-table',
|
|
377
664
|
activeDoc ? 'pages-hierarchy-table--dragging' : ''
|
|
378
665
|
].filter(Boolean).join(' '),
|
|
379
|
-
"data-page-tree-orderable": hasManualOrderColumn ? 'true' : undefined,
|
|
380
666
|
children: /*#__PURE__*/ _jsx("div", {
|
|
381
667
|
className: "table table--appearance-default",
|
|
382
668
|
children: /*#__PURE__*/ _jsxs("table", {
|
|
@@ -386,45 +672,50 @@ function HierarchyTable({ activeDragRowID, allDocsByID, allowSameParentDrops, co
|
|
|
386
672
|
/*#__PURE__*/ _jsx("thead", {
|
|
387
673
|
children: /*#__PURE__*/ _jsx("tr", {
|
|
388
674
|
children: activeColumns.map((column, index)=>/*#__PURE__*/ _jsx("th", {
|
|
389
|
-
"data-page-tree-manual-order-cell": column.accessor === MANUAL_ORDER_COLUMN_ACCESSOR ? 'true' : undefined,
|
|
390
675
|
"data-page-tree-title-cell": column.accessor === titleCellAccessor ? 'true' : undefined,
|
|
391
676
|
id: `heading-${column.accessor.replace(/\./g, '__')}`,
|
|
392
677
|
children: column.Heading
|
|
393
678
|
}, index))
|
|
394
679
|
})
|
|
395
680
|
}),
|
|
396
|
-
/*#__PURE__*/
|
|
397
|
-
children:
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
681
|
+
/*#__PURE__*/ _jsx("tbody", {
|
|
682
|
+
children: /*#__PURE__*/ _jsx(SortableContext, {
|
|
683
|
+
items: sortableItems,
|
|
684
|
+
strategy: sortingStrategy,
|
|
685
|
+
children: data.map((doc, rowIndex)=>{
|
|
686
|
+
const rootInsertBeforeDropTarget = doc.__pageTreeParentID === null ? insertDropTargetsByReferenceRowID.get(doc.__pageTreeID) : undefined;
|
|
687
|
+
const rootInsertTargetIndex = rootInsertBeforeDropTarget !== undefined ? insertDropTargets.indexOf(rootInsertBeforeDropTarget) : -1;
|
|
688
|
+
const rootInsertAfterDropTarget = rootInsertTargetIndex >= 0 ? insertDropTargets[rootInsertTargetIndex + 1] : undefined;
|
|
689
|
+
const isTrailingRootInsert = rootInsertAfterDropTarget === insertDropTargets[insertDropTargets.length - 1];
|
|
690
|
+
const nextVisibleDoc = data[rowIndex + 1];
|
|
691
|
+
const hasVisibleDescendantAfter = nextVisibleDoc?.__pageTreeAncestorIDs.includes(doc.__pageTreeID) ?? false;
|
|
692
|
+
return /*#__PURE__*/ _jsxs(React.Fragment, {
|
|
406
693
|
children: [
|
|
694
|
+
rootInsertBeforeDropTarget ? /*#__PURE__*/ _jsx(HierarchyInsertRow, {
|
|
695
|
+
activeColumnsCount: activeColumns.length,
|
|
696
|
+
activeDragRowID: activeDoc ? activeDragRowID : null,
|
|
697
|
+
activeDropTargetID: activeDropTargetID,
|
|
698
|
+
dropTarget: rootInsertBeforeDropTarget,
|
|
699
|
+
dropValidation: insertDropValidationByID.get(rootInsertBeforeDropTarget.dropID),
|
|
700
|
+
isMovePending: isMovePending
|
|
701
|
+
}, rootInsertBeforeDropTarget.dropID) : null,
|
|
407
702
|
/*#__PURE__*/ _jsx(HierarchyTableRow, {
|
|
408
703
|
activeColumns: activeColumns,
|
|
409
704
|
activeDragRowID: activeDragRowID,
|
|
705
|
+
activeDragType: activeDragType,
|
|
706
|
+
canReorderRows: canReorderRows,
|
|
410
707
|
doc: doc,
|
|
411
708
|
dropValidation: rowDropValidationByID.get(doc.__pageTreeID),
|
|
412
|
-
insertAfterDropID:
|
|
413
|
-
insertBeforeDropID:
|
|
709
|
+
insertAfterDropID: hasVisibleDescendantAfter || isTrailingRootInsert ? null : rootInsertAfterDropTarget?.dropID ?? null,
|
|
710
|
+
insertBeforeDropID: rootInsertBeforeDropTarget?.dropID ?? null,
|
|
414
711
|
isMovePending: isMovePending,
|
|
415
712
|
rowIndex: rowIndex,
|
|
416
713
|
titleCellAccessor: titleCellAccessor
|
|
417
|
-
})
|
|
418
|
-
insertDropTargets[rowIndex + 1] ? /*#__PURE__*/ _jsx(HierarchyInsertRow, {
|
|
419
|
-
activeColumnsCount: activeColumns.length,
|
|
420
|
-
activeDragRowID: activeDragRowID,
|
|
421
|
-
dropTarget: insertDropTargets[rowIndex + 1],
|
|
422
|
-
dropValidation: insertDropValidationByID.get(insertDropTargets[rowIndex + 1].dropID),
|
|
423
|
-
isMovePending: isMovePending
|
|
424
|
-
}, insertDropTargets[rowIndex + 1].dropID) : null
|
|
714
|
+
})
|
|
425
715
|
]
|
|
426
|
-
}, doc.__pageTreeID)
|
|
427
|
-
|
|
716
|
+
}, doc.__pageTreeID);
|
|
717
|
+
})
|
|
718
|
+
})
|
|
428
719
|
})
|
|
429
720
|
]
|
|
430
721
|
})
|
|
@@ -438,14 +729,15 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
438
729
|
const locale = useLocale();
|
|
439
730
|
const { i18n } = useTranslation();
|
|
440
731
|
const [activeDragRowID, setActiveDragRowID] = React.useState(null);
|
|
732
|
+
const [activeDragType, setActiveDragType] = React.useState(null);
|
|
441
733
|
const [activeDropTarget, setActiveDropTarget] = React.useState(null);
|
|
442
734
|
const [collapsedIDs, setCollapsedIDs] = React.useState(()=>new Set());
|
|
443
|
-
const [
|
|
735
|
+
const [optimisticOrderRowIDs, setOptimisticOrderRowIDs] = React.useState(null);
|
|
736
|
+
const [parentMoveEnabled, setParentMoveEnabled] = React.useState(false);
|
|
444
737
|
const [pendingMoveRowID, setPendingMoveRowID] = React.useState(null);
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
sourceDocs
|
|
738
|
+
const { getPreference, setPreference } = usePreferences();
|
|
739
|
+
const parentMovePreferenceKey = React.useMemo(()=>getParentMovePreferenceKey(props.collectionSlug), [
|
|
740
|
+
props.collectionSlug
|
|
449
741
|
]);
|
|
450
742
|
const toggleRow = React.useCallback((rowID)=>{
|
|
451
743
|
setCollapsedIDs((currentState)=>{
|
|
@@ -458,6 +750,16 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
458
750
|
return nextState;
|
|
459
751
|
});
|
|
460
752
|
}, []);
|
|
753
|
+
const handleParentMoveToggle = React.useCallback(()=>{
|
|
754
|
+
setParentMoveEnabled((currentValue)=>{
|
|
755
|
+
const nextValue = !currentValue;
|
|
756
|
+
void setPreference(parentMovePreferenceKey, nextValue);
|
|
757
|
+
return nextValue;
|
|
758
|
+
});
|
|
759
|
+
}, [
|
|
760
|
+
parentMovePreferenceKey,
|
|
761
|
+
setPreference
|
|
762
|
+
]);
|
|
461
763
|
const currentSort = React.useMemo(()=>{
|
|
462
764
|
const searchParamSort = searchParams.getAll('sort');
|
|
463
765
|
if (searchParamSort.length > 0) {
|
|
@@ -468,7 +770,10 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
468
770
|
query.sort,
|
|
469
771
|
searchParams
|
|
470
772
|
]);
|
|
471
|
-
const
|
|
773
|
+
const canReorderRows = canMoveDocs && isOrderableSortActive({
|
|
774
|
+
orderableFieldName,
|
|
775
|
+
sort: currentSort
|
|
776
|
+
});
|
|
472
777
|
const currentLimit = React.useMemo(()=>normalizePositiveInt(searchParams.get('limit'), typeof query.limit === 'number' && query.limit > 0 ? query.limit : 10), [
|
|
473
778
|
query.limit,
|
|
474
779
|
searchParams
|
|
@@ -477,13 +782,13 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
477
782
|
query.page,
|
|
478
783
|
searchParams
|
|
479
784
|
]);
|
|
480
|
-
const liveAllDocs = React.useMemo(()=>buildPageTreeDocs(
|
|
785
|
+
const liveAllDocs = React.useMemo(()=>buildPageTreeDocs(sourceDocs, {
|
|
481
786
|
parentFieldSlug,
|
|
482
787
|
sort: currentSort
|
|
483
788
|
}), [
|
|
484
789
|
currentSort,
|
|
485
|
-
|
|
486
|
-
|
|
790
|
+
parentFieldSlug,
|
|
791
|
+
sourceDocs
|
|
487
792
|
]);
|
|
488
793
|
const collapseResetKey = React.useMemo(()=>JSON.stringify([
|
|
489
794
|
props.collectionSlug,
|
|
@@ -498,19 +803,37 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
498
803
|
activeDragRowID,
|
|
499
804
|
canMoveDocs,
|
|
500
805
|
collapsedIDs,
|
|
806
|
+
parentMoveEnabled,
|
|
501
807
|
pendingMoveRowID,
|
|
502
808
|
toggleRow
|
|
503
809
|
}), [
|
|
504
810
|
activeDragRowID,
|
|
505
811
|
canMoveDocs,
|
|
506
812
|
collapsedIDs,
|
|
813
|
+
parentMoveEnabled,
|
|
507
814
|
pendingMoveRowID,
|
|
508
815
|
toggleRow
|
|
509
816
|
]);
|
|
817
|
+
React.useEffect(()=>{
|
|
818
|
+
let isMounted = true;
|
|
819
|
+
void getPreference(parentMovePreferenceKey).then((storedValue)=>{
|
|
820
|
+
if (isMounted) {
|
|
821
|
+
setParentMoveEnabled(storedValue === true);
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
return ()=>{
|
|
825
|
+
isMounted = false;
|
|
826
|
+
};
|
|
827
|
+
}, [
|
|
828
|
+
getPreference,
|
|
829
|
+
parentMovePreferenceKey
|
|
830
|
+
]);
|
|
510
831
|
React.useEffect(()=>{
|
|
511
832
|
setActiveDragRowID(null);
|
|
833
|
+
setActiveDragType(null);
|
|
512
834
|
setActiveDropTarget(null);
|
|
513
835
|
setCollapsedIDs(new Set());
|
|
836
|
+
setOptimisticOrderRowIDs(null);
|
|
514
837
|
}, [
|
|
515
838
|
collapseResetKey
|
|
516
839
|
]);
|
|
@@ -524,17 +847,36 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
524
847
|
visibleDocs
|
|
525
848
|
]);
|
|
526
849
|
const paginatedDocs = paginatedData.docs;
|
|
850
|
+
const paginatedServerOrderKey = React.useMemo(()=>paginatedDocs.map((doc)=>doc.__pageTreeID).join('\0'), [
|
|
851
|
+
paginatedDocs
|
|
852
|
+
]);
|
|
853
|
+
const displayedPaginatedDocs = React.useMemo(()=>orderDocsByRowIDs(paginatedDocs, optimisticOrderRowIDs), [
|
|
854
|
+
optimisticOrderRowIDs,
|
|
855
|
+
paginatedDocs
|
|
856
|
+
]);
|
|
857
|
+
const displayedPaginatedData = React.useMemo(()=>({
|
|
858
|
+
...paginatedData,
|
|
859
|
+
docs: displayedPaginatedDocs
|
|
860
|
+
}), [
|
|
861
|
+
displayedPaginatedDocs,
|
|
862
|
+
paginatedData
|
|
863
|
+
]);
|
|
864
|
+
React.useEffect(()=>{
|
|
865
|
+
setOptimisticOrderRowIDs(null);
|
|
866
|
+
}, [
|
|
867
|
+
paginatedServerOrderKey
|
|
868
|
+
]);
|
|
527
869
|
const allDocsByID = React.useMemo(()=>new Map(liveAllDocs.map((doc)=>[
|
|
528
870
|
doc.__pageTreeID,
|
|
529
871
|
doc
|
|
530
872
|
])), [
|
|
531
873
|
liveAllDocs
|
|
532
874
|
]);
|
|
533
|
-
const paginatedDocsByID = React.useMemo(()=>new Map(
|
|
875
|
+
const paginatedDocsByID = React.useMemo(()=>new Map(displayedPaginatedDocs.map((doc)=>[
|
|
534
876
|
doc.__pageTreeID,
|
|
535
877
|
doc
|
|
536
878
|
])), [
|
|
537
|
-
|
|
879
|
+
displayedPaginatedDocs
|
|
538
880
|
]);
|
|
539
881
|
const renderedCellIndexByDocID = React.useMemo(()=>new Map(allDocs.map((doc, index)=>[
|
|
540
882
|
doc.__pageTreeID,
|
|
@@ -542,19 +884,19 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
542
884
|
])), [
|
|
543
885
|
allDocs
|
|
544
886
|
]);
|
|
545
|
-
const normalizedColumnState = React.useMemo(()=>
|
|
887
|
+
const normalizedColumnState = React.useMemo(()=>ensureUseAsTitleColumn(columnState, useAsTitle), [
|
|
546
888
|
columnState,
|
|
547
889
|
useAsTitle
|
|
548
890
|
]);
|
|
549
|
-
const paginatedColumnState = React.useMemo(()=>sliceColumnState(normalizedColumnState,
|
|
891
|
+
const paginatedColumnState = React.useMemo(()=>sliceColumnState(normalizedColumnState, displayedPaginatedDocs, renderedCellIndexByDocID), [
|
|
892
|
+
displayedPaginatedDocs,
|
|
550
893
|
normalizedColumnState,
|
|
551
|
-
paginatedDocs,
|
|
552
894
|
renderedCellIndexByDocID
|
|
553
895
|
]);
|
|
554
896
|
const tableColumns = React.useMemo(()=>buildTableColumns({
|
|
555
897
|
badgeConfig,
|
|
556
898
|
columnState: paginatedColumnState,
|
|
557
|
-
docs:
|
|
899
|
+
docs: displayedPaginatedDocs,
|
|
558
900
|
enableRowSelections: props.enableRowSelections,
|
|
559
901
|
homeIndicatorEnabled,
|
|
560
902
|
orderableFieldName,
|
|
@@ -563,7 +905,7 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
563
905
|
useAsTitle
|
|
564
906
|
}), [
|
|
565
907
|
paginatedColumnState,
|
|
566
|
-
|
|
908
|
+
displayedPaginatedDocs,
|
|
567
909
|
badgeConfig,
|
|
568
910
|
homeIndicatorEnabled,
|
|
569
911
|
orderableFieldName,
|
|
@@ -574,7 +916,7 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
574
916
|
]);
|
|
575
917
|
const activeDragDoc = activeDragRowID ? paginatedDocsByID.get(activeDragRowID) ?? null : null;
|
|
576
918
|
const activeDragPreviewPath = React.useMemo(()=>{
|
|
577
|
-
if (!activeDragDoc) {
|
|
919
|
+
if (!activeDragDoc || activeDragType !== 'move') {
|
|
578
920
|
return null;
|
|
579
921
|
}
|
|
580
922
|
if (!activeDropTarget) {
|
|
@@ -587,10 +929,10 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
587
929
|
docsByID: allDocsByID,
|
|
588
930
|
dropTarget: activeDropTarget
|
|
589
931
|
});
|
|
590
|
-
const dropValidation =
|
|
932
|
+
const dropValidation = getDropTargetValidation({
|
|
591
933
|
activeDoc: activeDragDoc,
|
|
592
|
-
|
|
593
|
-
|
|
934
|
+
docsByID: allDocsByID,
|
|
935
|
+
dropTarget: activeDropTarget
|
|
594
936
|
});
|
|
595
937
|
if (!dropValidation.isValid) {
|
|
596
938
|
return buildDocSlugPath({
|
|
@@ -605,218 +947,46 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
605
947
|
});
|
|
606
948
|
}, [
|
|
607
949
|
activeDragDoc,
|
|
950
|
+
activeDragType,
|
|
608
951
|
activeDropTarget,
|
|
609
|
-
allDocsByID
|
|
610
|
-
manualOrderIsActive
|
|
952
|
+
allDocsByID
|
|
611
953
|
]);
|
|
612
954
|
const isMovePending = pendingMoveRowID !== null;
|
|
613
955
|
const sensors = useSensors(useSensor(PointerSensor, {
|
|
614
956
|
activationConstraint: {
|
|
615
|
-
distance:
|
|
957
|
+
distance: 2
|
|
616
958
|
}
|
|
617
959
|
}));
|
|
618
|
-
const moveDocument = React.useCallback(async (args)=>{
|
|
619
|
-
const { parentID, rowID } = args;
|
|
620
|
-
const params = new URLSearchParams();
|
|
621
|
-
if (locale?.code) {
|
|
622
|
-
params.set('locale', locale.code);
|
|
623
|
-
}
|
|
624
|
-
const response = await fetch(`${config.routes.api}/${props.collectionSlug}/${encodeURIComponent(rowID)}/move${params.size > 0 ? `?${params.toString()}` : ''}`, {
|
|
625
|
-
body: JSON.stringify({
|
|
626
|
-
parentID
|
|
627
|
-
}),
|
|
628
|
-
credentials: 'include',
|
|
629
|
-
headers: {
|
|
630
|
-
'Accept-Language': i18n.language,
|
|
631
|
-
'Content-Type': 'application/json',
|
|
632
|
-
[pageTreeMoveRequestHeader]: pageTreeMoveRequestHeaderValue
|
|
633
|
-
},
|
|
634
|
-
method: 'POST'
|
|
635
|
-
});
|
|
636
|
-
const result = await response.json().catch(()=>null);
|
|
637
|
-
if (!response.ok) {
|
|
638
|
-
throw new Error(result?.message ?? 'Could not move document.');
|
|
639
|
-
}
|
|
640
|
-
return result;
|
|
641
|
-
}, [
|
|
642
|
-
config.routes.api,
|
|
643
|
-
i18n.language,
|
|
644
|
-
locale?.code,
|
|
645
|
-
props.collectionSlug
|
|
646
|
-
]);
|
|
647
|
-
const reorderDocument = React.useCallback(async (args)=>{
|
|
648
|
-
const { movedDocID, newKeyWillBe, targetDoc } = args;
|
|
649
|
-
const targetID = getPayloadDocID(targetDoc);
|
|
650
|
-
if (!orderableFieldName || targetID === undefined) {
|
|
651
|
-
return undefined;
|
|
652
|
-
}
|
|
653
|
-
const params = new URLSearchParams();
|
|
654
|
-
if (locale?.code) {
|
|
655
|
-
params.set('locale', locale.code);
|
|
656
|
-
}
|
|
657
|
-
const response = await fetch(`${config.routes.api}/reorder${params.size > 0 ? `?${params.toString()}` : ''}`, {
|
|
658
|
-
body: JSON.stringify({
|
|
659
|
-
collectionSlug: props.collectionSlug,
|
|
660
|
-
docsToMove: [
|
|
661
|
-
movedDocID
|
|
662
|
-
],
|
|
663
|
-
newKeyWillBe,
|
|
664
|
-
orderableFieldName,
|
|
665
|
-
target: {
|
|
666
|
-
id: targetID,
|
|
667
|
-
key: targetDoc[orderableFieldName]
|
|
668
|
-
}
|
|
669
|
-
}),
|
|
670
|
-
credentials: 'include',
|
|
671
|
-
headers: {
|
|
672
|
-
'Accept-Language': i18n.language,
|
|
673
|
-
'Content-Type': 'application/json',
|
|
674
|
-
[pageTreeMoveRequestHeader]: pageTreeMoveRequestHeaderValue
|
|
675
|
-
},
|
|
676
|
-
method: 'POST'
|
|
677
|
-
});
|
|
678
|
-
const result = await response.json().catch(()=>null);
|
|
679
|
-
if (response.status === 403) {
|
|
680
|
-
throw new Error('You do not have permission to reorder these rows');
|
|
681
|
-
}
|
|
682
|
-
if (!response.ok) {
|
|
683
|
-
throw new Error('Failed to reorder. This can happen if you reorder several rows too quickly. Please try again.');
|
|
684
|
-
}
|
|
685
|
-
if (result?.message === 'initial migration') {
|
|
686
|
-
throw new Error('You have enabled "orderable" on a collection with existing documents and this is the first time you have sorted documents. We have run an automatic migration to add an initial order to the documents. Please refresh the page and try again.');
|
|
687
|
-
}
|
|
688
|
-
return result?.orderValues?.[0];
|
|
689
|
-
}, [
|
|
690
|
-
config.routes.api,
|
|
691
|
-
i18n.language,
|
|
692
|
-
locale?.code,
|
|
693
|
-
orderableFieldName,
|
|
694
|
-
props.collectionSlug
|
|
695
|
-
]);
|
|
696
|
-
const handleManualOrderMoveEnd = React.useCallback(async (args)=>{
|
|
697
|
-
const { activeDoc, dropTarget, rowID } = args;
|
|
698
|
-
if (!orderableFieldName) {
|
|
699
|
-
return;
|
|
700
|
-
}
|
|
701
|
-
const targetDoc = getDropTargetParentDoc({
|
|
702
|
-
docsByID: allDocsByID,
|
|
703
|
-
dropTarget
|
|
704
|
-
});
|
|
705
|
-
const dropValidation = getManualOrderDropValidation({
|
|
706
|
-
activeDoc,
|
|
707
|
-
targetDoc: targetDoc ?? undefined
|
|
708
|
-
});
|
|
709
|
-
if (!dropValidation.isValid) {
|
|
710
|
-
if (!shouldSilenceMoveMessage(dropValidation.message)) {
|
|
711
|
-
toast.error(dropValidation.message ?? 'Could not move document.');
|
|
712
|
-
}
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
const placement = getOrderPlacementFromDropTarget({
|
|
716
|
-
activeDoc,
|
|
717
|
-
docs: paginatedDocs,
|
|
718
|
-
docsByID: allDocsByID,
|
|
719
|
-
dropTarget
|
|
720
|
-
});
|
|
721
|
-
if (!placement) {
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
const currentPlacement = getCurrentOrderPlacement({
|
|
725
|
-
activeDoc,
|
|
726
|
-
docs: paginatedDocs
|
|
727
|
-
});
|
|
728
|
-
const parentChanged = activeDoc.__pageTreeParentID !== placement.parentID;
|
|
729
|
-
const orderChanged = orderPlacementHasChanged(currentPlacement, placement);
|
|
730
|
-
const reorderTarget = orderChanged ? getReorderTargetFromPlacement({
|
|
731
|
-
currentSort,
|
|
732
|
-
docsByID: allDocsByID,
|
|
733
|
-
orderableFieldName,
|
|
734
|
-
placement
|
|
735
|
-
}) : null;
|
|
736
|
-
if (!parentChanged && !reorderTarget) {
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
const movedDocID = getPayloadDocID(activeDoc);
|
|
740
|
-
if (movedDocID === undefined) {
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
const parentDoc = placement.parentID ? allDocsByID.get(placement.parentID) : undefined;
|
|
744
|
-
const nextParentValue = placement.parentID === null ? null : parentDoc ? getPayloadDocID(parentDoc) ?? placement.parentID : placement.parentID;
|
|
745
|
-
let parentMoveSucceeded = false;
|
|
746
|
-
setPendingMoveRowID(rowID);
|
|
747
|
-
try {
|
|
748
|
-
if (parentChanged) {
|
|
749
|
-
await moveDocument({
|
|
750
|
-
parentID: placement.parentID,
|
|
751
|
-
rowID
|
|
752
|
-
});
|
|
753
|
-
parentMoveSucceeded = true;
|
|
754
|
-
}
|
|
755
|
-
const nextOrderValue = reorderTarget ? await reorderDocument({
|
|
756
|
-
movedDocID,
|
|
757
|
-
newKeyWillBe: reorderTarget.newKeyWillBe,
|
|
758
|
-
targetDoc: reorderTarget.targetDoc
|
|
759
|
-
}) : undefined;
|
|
760
|
-
if (parentChanged || typeof nextOrderValue === 'string') {
|
|
761
|
-
setLocalSourceDocs((currentDocs)=>currentDocs.map((doc)=>String(doc.id ?? '') === String(movedDocID) ? {
|
|
762
|
-
...doc,
|
|
763
|
-
...parentChanged ? {
|
|
764
|
-
[parentFieldSlug]: nextParentValue
|
|
765
|
-
} : {},
|
|
766
|
-
...typeof nextOrderValue === 'string' ? {
|
|
767
|
-
[orderableFieldName]: nextOrderValue
|
|
768
|
-
} : {}
|
|
769
|
-
} : doc));
|
|
770
|
-
}
|
|
771
|
-
if (parentChanged) {
|
|
772
|
-
toast.success(`Moved "${getDocDisplayLabel(activeDoc)}".`);
|
|
773
|
-
}
|
|
774
|
-
React.startTransition(()=>{
|
|
775
|
-
router.refresh();
|
|
776
|
-
});
|
|
777
|
-
} catch (error) {
|
|
778
|
-
const message = error instanceof Error && error.message ? error.message : 'Could not move document.';
|
|
779
|
-
toast.error(parentMoveSucceeded ? `Moved "${getDocDisplayLabel(activeDoc)}", but could not update its manual order. ${message}` : message);
|
|
780
|
-
if (parentMoveSucceeded) {
|
|
781
|
-
React.startTransition(()=>{
|
|
782
|
-
router.refresh();
|
|
783
|
-
});
|
|
784
|
-
}
|
|
785
|
-
} finally{
|
|
786
|
-
setPendingMoveRowID(null);
|
|
787
|
-
}
|
|
788
|
-
}, [
|
|
789
|
-
allDocsByID,
|
|
790
|
-
currentSort,
|
|
791
|
-
moveDocument,
|
|
792
|
-
orderableFieldName,
|
|
793
|
-
paginatedDocs,
|
|
794
|
-
parentFieldSlug,
|
|
795
|
-
reorderDocument,
|
|
796
|
-
router
|
|
797
|
-
]);
|
|
798
960
|
const handleDragCancel = React.useCallback(()=>{
|
|
799
961
|
setActiveDragRowID(null);
|
|
962
|
+
setActiveDragType(null);
|
|
800
963
|
setActiveDropTarget(null);
|
|
801
964
|
}, []);
|
|
802
965
|
const handleDragStart = React.useCallback((event)=>{
|
|
803
|
-
const dragData = getPageTreeDragData(event.active.data.current);
|
|
804
966
|
if (!canMoveDocs || isMovePending) {
|
|
805
967
|
return;
|
|
806
968
|
}
|
|
807
|
-
const
|
|
808
|
-
|
|
969
|
+
const dragData = getPageTreeDragData(event.active.data.current);
|
|
970
|
+
const { dragType, rowID } = dragData;
|
|
971
|
+
if (typeof rowID === 'string' && dragType && paginatedDocsByID.has(rowID) && (dragType !== 'order' || canReorderRows)) {
|
|
809
972
|
setActiveDragRowID(rowID);
|
|
973
|
+
setActiveDragType(dragType);
|
|
810
974
|
setActiveDropTarget(null);
|
|
811
975
|
}
|
|
812
976
|
}, [
|
|
813
977
|
canMoveDocs,
|
|
978
|
+
canReorderRows,
|
|
814
979
|
isMovePending,
|
|
815
980
|
paginatedDocsByID
|
|
816
981
|
]);
|
|
817
982
|
const handleDragOver = React.useCallback((event)=>{
|
|
983
|
+
const dragData = getPageTreeDragData(event.active.data.current);
|
|
818
984
|
const overData = event.over?.data.current;
|
|
819
|
-
if (
|
|
985
|
+
if (dragData.dragType === 'order') {
|
|
986
|
+
setActiveDropTarget(null);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
if (dragData.dragType !== 'move' || !overData) {
|
|
820
990
|
setActiveDropTarget(null);
|
|
821
991
|
return;
|
|
822
992
|
}
|
|
@@ -832,62 +1002,132 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
832
1002
|
}, []);
|
|
833
1003
|
const handleDragEnd = React.useCallback(async (event)=>{
|
|
834
1004
|
const dragData = getPageTreeDragData(event.active.data.current);
|
|
835
|
-
const rowID = dragData
|
|
1005
|
+
const { dragType, rowID } = dragData;
|
|
836
1006
|
const activeDoc = typeof rowID === 'string' ? paginatedDocsByID.get(rowID) ?? null : null;
|
|
837
1007
|
const overData = event.over?.data.current;
|
|
1008
|
+
const overDragData = getPageTreeDragData(event.over?.data.current);
|
|
838
1009
|
setActiveDragRowID(null);
|
|
1010
|
+
setActiveDragType(null);
|
|
839
1011
|
setActiveDropTarget(null);
|
|
840
|
-
if (
|
|
1012
|
+
if (dragType === 'order') {
|
|
1013
|
+
if (typeof rowID === 'string' && typeof overDragData.rowID === 'string') {
|
|
1014
|
+
const nextOrderRowIDs = buildOptimisticOrderRowIDs({
|
|
1015
|
+
activeRowID: rowID,
|
|
1016
|
+
docs: displayedPaginatedDocs,
|
|
1017
|
+
targetRowID: overDragData.rowID
|
|
1018
|
+
});
|
|
1019
|
+
const reorderRequestBody = buildReorderRequestBody({
|
|
1020
|
+
activeRowID: rowID,
|
|
1021
|
+
docs: displayedPaginatedDocs,
|
|
1022
|
+
orderableFieldName,
|
|
1023
|
+
sort: currentSort,
|
|
1024
|
+
targetRowID: overDragData.rowID
|
|
1025
|
+
});
|
|
1026
|
+
if (!nextOrderRowIDs || !reorderRequestBody) {
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
const apiRoute = config.routes.api;
|
|
1030
|
+
const params = new URLSearchParams();
|
|
1031
|
+
if (locale?.code) {
|
|
1032
|
+
params.set('locale', locale.code);
|
|
1033
|
+
}
|
|
1034
|
+
setOptimisticOrderRowIDs(nextOrderRowIDs);
|
|
1035
|
+
setPendingMoveRowID(rowID);
|
|
1036
|
+
try {
|
|
1037
|
+
const response = await fetch(`${apiRoute}/${props.collectionSlug}/${encodeURIComponent(rowID)}/reorder${params.size > 0 ? `?${params.toString()}` : ''}`, {
|
|
1038
|
+
body: JSON.stringify(reorderRequestBody),
|
|
1039
|
+
credentials: 'include',
|
|
1040
|
+
headers: {
|
|
1041
|
+
'Accept-Language': i18n.language,
|
|
1042
|
+
'Content-Type': 'application/json'
|
|
1043
|
+
},
|
|
1044
|
+
method: 'POST'
|
|
1045
|
+
});
|
|
1046
|
+
const result = await response.json().catch(()=>null);
|
|
1047
|
+
if (!response.ok) {
|
|
1048
|
+
setOptimisticOrderRowIDs(null);
|
|
1049
|
+
toast.error(result?.message ?? 'Could not reorder document.');
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
if (result?.message === 'initial migration') {
|
|
1053
|
+
setOptimisticOrderRowIDs(null);
|
|
1054
|
+
}
|
|
1055
|
+
React.startTransition(()=>{
|
|
1056
|
+
router.refresh();
|
|
1057
|
+
});
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
const message = error instanceof Error && error.message ? error.message : 'Could not reorder document.';
|
|
1060
|
+
setOptimisticOrderRowIDs(null);
|
|
1061
|
+
toast.error(message);
|
|
1062
|
+
} finally{
|
|
1063
|
+
setPendingMoveRowID(null);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
841
1066
|
return;
|
|
842
1067
|
}
|
|
843
|
-
if (
|
|
844
|
-
await handleManualOrderMoveEnd({
|
|
845
|
-
activeDoc,
|
|
846
|
-
dropTarget: overData,
|
|
847
|
-
rowID
|
|
848
|
-
});
|
|
1068
|
+
if (dragType !== 'move' || typeof rowID !== 'string' || !activeDoc || !overData) {
|
|
849
1069
|
return;
|
|
850
1070
|
}
|
|
851
|
-
const
|
|
1071
|
+
const dropValidation = getDropTargetValidation({
|
|
1072
|
+
activeDoc,
|
|
852
1073
|
docsByID: allDocsByID,
|
|
853
1074
|
dropTarget: overData
|
|
854
1075
|
});
|
|
855
|
-
const dropValidation = getDropValidation({
|
|
856
|
-
activeDoc,
|
|
857
|
-
targetDoc: targetDoc ?? undefined
|
|
858
|
-
});
|
|
859
1076
|
if (!dropValidation.isValid) {
|
|
860
1077
|
if (!shouldSilenceMoveMessage(dropValidation.message)) {
|
|
861
1078
|
toast.error(dropValidation.message ?? 'Could not move document.');
|
|
862
1079
|
}
|
|
863
1080
|
return;
|
|
864
1081
|
}
|
|
1082
|
+
if (overData.dropType === 'insert' && activeDoc.__pageTreeParentID === dropValidation.parentID) {
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
const apiRoute = config.routes.api;
|
|
1086
|
+
const params = new URLSearchParams();
|
|
1087
|
+
if (locale?.code) {
|
|
1088
|
+
params.set('locale', locale.code);
|
|
1089
|
+
}
|
|
865
1090
|
setPendingMoveRowID(rowID);
|
|
866
1091
|
try {
|
|
867
|
-
await
|
|
868
|
-
|
|
869
|
-
|
|
1092
|
+
const response = await fetch(`${apiRoute}/${props.collectionSlug}/${encodeURIComponent(rowID)}/move${params.size > 0 ? `?${params.toString()}` : ''}`, {
|
|
1093
|
+
body: JSON.stringify({
|
|
1094
|
+
parentID: dropValidation.parentID
|
|
1095
|
+
}),
|
|
1096
|
+
credentials: 'include',
|
|
1097
|
+
headers: {
|
|
1098
|
+
'Accept-Language': i18n.language,
|
|
1099
|
+
'Content-Type': 'application/json'
|
|
1100
|
+
},
|
|
1101
|
+
method: 'POST'
|
|
870
1102
|
});
|
|
1103
|
+
const result = await response.json().catch(()=>null);
|
|
1104
|
+
if (!response.ok) {
|
|
1105
|
+
if (shouldSilenceMoveMessage(result?.message)) {
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
toast.error(result?.message ?? 'Could not move document.');
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
871
1111
|
toast.success(`Moved "${getDocDisplayLabel(activeDoc)}".`);
|
|
872
1112
|
React.startTransition(()=>{
|
|
873
1113
|
router.refresh();
|
|
874
1114
|
});
|
|
875
1115
|
} catch (error) {
|
|
876
1116
|
const message = error instanceof Error && error.message ? error.message : 'Could not move document.';
|
|
877
|
-
if (shouldSilenceMoveMessage(message)) {
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
1117
|
toast.error(message);
|
|
881
1118
|
} finally{
|
|
882
1119
|
setPendingMoveRowID(null);
|
|
883
1120
|
}
|
|
884
1121
|
}, [
|
|
885
1122
|
allDocsByID,
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1123
|
+
config.routes.api,
|
|
1124
|
+
currentSort,
|
|
1125
|
+
displayedPaginatedDocs,
|
|
1126
|
+
i18n.language,
|
|
1127
|
+
locale?.code,
|
|
889
1128
|
orderableFieldName,
|
|
890
1129
|
paginatedDocsByID,
|
|
1130
|
+
props.collectionSlug,
|
|
891
1131
|
router
|
|
892
1132
|
]);
|
|
893
1133
|
const handleDragEndSync = React.useCallback((event)=>{
|
|
@@ -905,10 +1145,12 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
905
1145
|
children: [
|
|
906
1146
|
/*#__PURE__*/ _jsx(HierarchyTable, {
|
|
907
1147
|
activeDragRowID: activeDragRowID,
|
|
1148
|
+
activeDragType: activeDragType,
|
|
1149
|
+
activeDropTarget: activeDropTarget,
|
|
908
1150
|
allDocsByID: allDocsByID,
|
|
909
|
-
|
|
1151
|
+
canReorderRows: canReorderRows,
|
|
910
1152
|
columns: tableColumns,
|
|
911
|
-
data:
|
|
1153
|
+
data: displayedPaginatedDocs,
|
|
912
1154
|
isMovePending: isMovePending,
|
|
913
1155
|
titleCellAccessor: useAsTitle
|
|
914
1156
|
}),
|
|
@@ -926,35 +1168,57 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
926
1168
|
}), [
|
|
927
1169
|
activeDragPreviewPath,
|
|
928
1170
|
activeDragRowID,
|
|
1171
|
+
activeDragType,
|
|
1172
|
+
activeDropTarget,
|
|
1173
|
+
allDocsByID,
|
|
1174
|
+
canReorderRows,
|
|
1175
|
+
displayedPaginatedDocs,
|
|
929
1176
|
handleDragCancel,
|
|
930
1177
|
handleDragEndSync,
|
|
931
1178
|
handleDragOver,
|
|
932
1179
|
handleDragStart,
|
|
933
1180
|
isMovePending,
|
|
934
|
-
allDocsByID,
|
|
935
|
-
manualOrderIsActive,
|
|
936
|
-
paginatedDocs,
|
|
937
1181
|
sensors,
|
|
938
1182
|
tableColumns,
|
|
939
1183
|
useAsTitle
|
|
940
1184
|
]);
|
|
1185
|
+
const beforeActions = React.useMemo(()=>{
|
|
1186
|
+
const actions = props.beforeActions ? [
|
|
1187
|
+
...props.beforeActions
|
|
1188
|
+
] : [];
|
|
1189
|
+
if (canMoveDocs) {
|
|
1190
|
+
actions.unshift(/*#__PURE__*/ _jsx(ParentMoveToggle, {
|
|
1191
|
+
collectionSlug: props.collectionSlug,
|
|
1192
|
+
enabled: parentMoveEnabled,
|
|
1193
|
+
onToggle: handleParentMoveToggle
|
|
1194
|
+
}, "parent-move-toggle"));
|
|
1195
|
+
}
|
|
1196
|
+
return actions.length > 0 ? actions : undefined;
|
|
1197
|
+
}, [
|
|
1198
|
+
canMoveDocs,
|
|
1199
|
+
handleParentMoveToggle,
|
|
1200
|
+
parentMoveEnabled,
|
|
1201
|
+
props.beforeActions,
|
|
1202
|
+
props.collectionSlug
|
|
1203
|
+
]);
|
|
941
1204
|
return /*#__PURE__*/ _jsx("div", {
|
|
942
1205
|
className: styles.root,
|
|
943
1206
|
children: /*#__PURE__*/ _jsx(PageTreeProvider, {
|
|
944
1207
|
value: hierarchyValue,
|
|
945
1208
|
children: /*#__PURE__*/ _jsx(ListQueryProvider, {
|
|
946
1209
|
collectionSlug: props.collectionSlug,
|
|
947
|
-
data:
|
|
1210
|
+
data: displayedPaginatedData,
|
|
948
1211
|
modifySearchParams: true,
|
|
949
1212
|
orderableFieldName: orderableFieldName,
|
|
950
1213
|
query: {
|
|
951
1214
|
...query,
|
|
952
1215
|
limit: currentLimit,
|
|
953
|
-
page:
|
|
1216
|
+
page: displayedPaginatedData.page,
|
|
954
1217
|
sort: currentSort
|
|
955
1218
|
},
|
|
956
1219
|
children: /*#__PURE__*/ _jsx(DefaultListView, {
|
|
957
1220
|
...props,
|
|
1221
|
+
beforeActions: beforeActions,
|
|
958
1222
|
columnState: paginatedColumnState,
|
|
959
1223
|
Table: tableNode
|
|
960
1224
|
})
|