payload-nested-docs-page-tree 1.2.4 → 1.4.1
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/README.md +356 -222
- 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 +4 -5
- package/dist/components/PageTreeListView.client.js +668 -128
- package/dist/components/PageTreeListView.client.js.map +1 -1
- package/dist/components/PageTreeListView.module.css +399 -376
- 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 +3 -0
- package/dist/endpoints/createMovePageEndpoint.js +186 -125
- package/dist/endpoints/createMovePageEndpoint.js.map +1 -1
- package/dist/endpoints/createReorderPageEndpoint.d.ts +8 -0
- package/dist/endpoints/createReorderPageEndpoint.js +706 -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/index.js +110 -27
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +47 -2
- package/dist/types.js +22 -3
- package/dist/types.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/moveValidation.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 +54 -32
|
@@ -1,27 +1,142 @@
|
|
|
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 { pageTreeMoveRequestHeader, pageTreeMoveRequestHeaderValue } from '../types.js';
|
|
8
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
|
}
|
|
26
|
+
function getSortableRowID(rowID) {
|
|
27
|
+
return `page-sort:${rowID}`;
|
|
28
|
+
}
|
|
29
|
+
function getDiagnosticString(value) {
|
|
30
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
31
|
+
}
|
|
23
32
|
function getPageTreeDragData(value) {
|
|
24
|
-
|
|
33
|
+
if (!value || typeof value !== 'object') {
|
|
34
|
+
return {};
|
|
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;
|
|
40
|
+
return {
|
|
41
|
+
dragType,
|
|
42
|
+
parentID,
|
|
43
|
+
rowID
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function getSortableTransform(transform) {
|
|
47
|
+
if (!transform) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
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})`;
|
|
53
|
+
}
|
|
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;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
end,
|
|
68
|
+
start: rootIndex
|
|
69
|
+
};
|
|
70
|
+
}
|
|
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) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
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
|
+
};
|
|
139
|
+
};
|
|
25
140
|
}
|
|
26
141
|
function buildPaginatedData(docs, limit, requestedPage) {
|
|
27
142
|
const totalDocs = docs.length;
|
|
@@ -56,8 +171,12 @@ function normalizePositiveInt(value, fallback) {
|
|
|
56
171
|
const parsedValue = Number.parseInt(value, 10);
|
|
57
172
|
return Number.isNaN(parsedValue) || parsedValue <= 0 ? fallback : parsedValue;
|
|
58
173
|
}
|
|
59
|
-
function
|
|
60
|
-
|
|
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 ? {
|
|
61
180
|
...column,
|
|
62
181
|
active: true
|
|
63
182
|
} : column);
|
|
@@ -82,6 +201,191 @@ function getSelectableRowData(doc) {
|
|
|
82
201
|
function shouldSilenceMoveMessage(message) {
|
|
83
202
|
return typeof message === 'string' && SILENT_MOVE_MESSAGES.has(message);
|
|
84
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
|
+
}
|
|
85
389
|
function getStatusClassName(status) {
|
|
86
390
|
if (status === 'changed' || status === 'draft' || status === 'published') {
|
|
87
391
|
return status;
|
|
@@ -113,8 +417,20 @@ function renderStatusBadge(args) {
|
|
|
113
417
|
})
|
|
114
418
|
}, doc.__pageTreeID ?? index);
|
|
115
419
|
}
|
|
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
|
+
});
|
|
431
|
+
}
|
|
116
432
|
function buildTableColumns(args) {
|
|
117
|
-
const { badgeConfig, columnState, docs, enableRowSelections, homeIndicatorEnabled, parentFieldSlug, t, useAsTitle } = args;
|
|
433
|
+
const { badgeConfig, columnState, docs, enableRowSelections, homeIndicatorEnabled, orderableFieldName, parentFieldSlug, t, useAsTitle } = args;
|
|
118
434
|
const columnsToUse = columnState.map((column)=>{
|
|
119
435
|
if (column.accessor === useAsTitle) {
|
|
120
436
|
return {
|
|
@@ -162,18 +478,35 @@ function buildTableColumns(args) {
|
|
|
162
478
|
}, doc.__pageTreeID ?? index))
|
|
163
479
|
});
|
|
164
480
|
}
|
|
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);
|
|
496
|
+
}
|
|
165
497
|
return columnsToUse;
|
|
166
498
|
}
|
|
167
|
-
function HierarchyInsertRow({ activeColumnsCount, activeDragRowID, dropTarget, dropValidation, isMovePending }) {
|
|
499
|
+
function HierarchyInsertRow({ activeColumnsCount, activeDragRowID, activeDropTargetID, dropTarget, dropValidation, isMovePending }) {
|
|
168
500
|
const { isOver, setNodeRef } = useDroppable({
|
|
169
501
|
id: dropTarget.dropID,
|
|
170
502
|
data: dropTarget,
|
|
171
503
|
disabled: isMovePending
|
|
172
504
|
});
|
|
173
505
|
const hasActiveDrag = Boolean(activeDragRowID);
|
|
506
|
+
const showInsertLine = isOver && activeDropTargetID === dropTarget.dropID;
|
|
174
507
|
return /*#__PURE__*/ _jsx("tr", {
|
|
175
508
|
className: "pages-hierarchy-insert-row",
|
|
176
|
-
"data-drag-over":
|
|
509
|
+
"data-drag-over": showInsertLine ? 'true' : 'false',
|
|
177
510
|
"data-drop-valid": hasActiveDrag ? dropValidation?.isValid ? 'true' : 'false' : undefined,
|
|
178
511
|
"data-page-tree-insert": "true",
|
|
179
512
|
children: /*#__PURE__*/ _jsx("td", {
|
|
@@ -186,7 +519,7 @@ function HierarchyInsertRow({ activeColumnsCount, activeDragRowID, dropTarget, d
|
|
|
186
519
|
})
|
|
187
520
|
});
|
|
188
521
|
}
|
|
189
|
-
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 }) {
|
|
190
523
|
const { isOver, setNodeRef } = useDroppable({
|
|
191
524
|
id: getRowDropID(doc.__pageTreeID),
|
|
192
525
|
data: {
|
|
@@ -197,51 +530,110 @@ function HierarchyTableRow({ activeColumns, activeDragRowID, doc, dropValidation
|
|
|
197
530
|
},
|
|
198
531
|
disabled: isMovePending
|
|
199
532
|
});
|
|
200
|
-
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);
|
|
201
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
|
+
]);
|
|
202
573
|
return /*#__PURE__*/ _jsx("tr", {
|
|
203
574
|
className: `row-${rowIndex + 1}`,
|
|
204
|
-
"data-drag-over": isOver ? 'true' : 'false',
|
|
205
|
-
"data-drop-valid":
|
|
575
|
+
"data-drag-over": activeDragType === 'move' && isOver ? 'true' : 'false',
|
|
576
|
+
"data-drop-valid": hasActiveMoveDrag ? dropValidation?.isValid ? 'true' : 'false' : undefined,
|
|
206
577
|
"data-id": doc.id,
|
|
207
|
-
"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',
|
|
208
580
|
"data-page-tree-row": "true",
|
|
209
|
-
ref:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
"
|
|
216
|
-
|
|
217
|
-
|
|
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
|
+
})
|
|
218
593
|
})
|
|
219
594
|
});
|
|
220
595
|
}
|
|
221
|
-
function HierarchyTable({ activeDragRowID, allDocsByID, columns, data, isMovePending, titleCellAccessor }) {
|
|
596
|
+
function HierarchyTable({ activeDragRowID, activeDragType, activeDropTarget, allDocsByID, canReorderRows, columns, data, isMovePending, titleCellAccessor }) {
|
|
222
597
|
const activeColumns = React.useMemo(()=>columns.filter((column)=>column?.active), [
|
|
223
598
|
columns
|
|
224
599
|
]);
|
|
225
|
-
const
|
|
226
|
-
|
|
600
|
+
const sortableItems = React.useMemo(()=>data.map((doc)=>getSortableRowID(doc.__pageTreeID)), [
|
|
601
|
+
data
|
|
602
|
+
]);
|
|
603
|
+
const sortingStrategy = React.useMemo(()=>buildPageTreeSortingStrategy(data), [
|
|
604
|
+
data
|
|
227
605
|
]);
|
|
228
606
|
const insertDropTargets = React.useMemo(()=>buildInsertDropTargets(data), [
|
|
229
607
|
data
|
|
230
608
|
]);
|
|
231
|
-
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;
|
|
232
617
|
const rowDropValidationByID = React.useMemo(()=>{
|
|
233
618
|
if (!activeDoc) {
|
|
234
619
|
return new Map();
|
|
235
620
|
}
|
|
236
621
|
return new Map(data.map((doc)=>[
|
|
237
622
|
doc.__pageTreeID,
|
|
238
|
-
|
|
623
|
+
getDropTargetValidation({
|
|
239
624
|
activeDoc,
|
|
240
|
-
|
|
625
|
+
docsByID: allDocsByID,
|
|
626
|
+
dropTarget: {
|
|
627
|
+
dropType: 'row',
|
|
628
|
+
insertAfterDropID: null,
|
|
629
|
+
insertBeforeDropID: null,
|
|
630
|
+
rowID: doc.__pageTreeID
|
|
631
|
+
}
|
|
241
632
|
})
|
|
242
633
|
]));
|
|
243
634
|
}, [
|
|
244
635
|
activeDoc,
|
|
636
|
+
allDocsByID,
|
|
245
637
|
data
|
|
246
638
|
]);
|
|
247
639
|
const insertDropValidationByID = React.useMemo(()=>{
|
|
@@ -250,9 +642,10 @@ function HierarchyTable({ activeDragRowID, allDocsByID, columns, data, isMovePen
|
|
|
250
642
|
}
|
|
251
643
|
return new Map(insertDropTargets.map((dropTarget)=>[
|
|
252
644
|
dropTarget.dropID,
|
|
253
|
-
|
|
645
|
+
getDropTargetValidation({
|
|
254
646
|
activeDoc,
|
|
255
|
-
|
|
647
|
+
docsByID: allDocsByID,
|
|
648
|
+
dropTarget
|
|
256
649
|
})
|
|
257
650
|
]));
|
|
258
651
|
}, [
|
|
@@ -270,7 +663,6 @@ function HierarchyTable({ activeDragRowID, allDocsByID, columns, data, isMovePen
|
|
|
270
663
|
'table-wrap pages-hierarchy-table',
|
|
271
664
|
activeDoc ? 'pages-hierarchy-table--dragging' : ''
|
|
272
665
|
].filter(Boolean).join(' '),
|
|
273
|
-
"data-page-tree-orderable": hasManualOrderColumn ? 'true' : undefined,
|
|
274
666
|
children: /*#__PURE__*/ _jsx("div", {
|
|
275
667
|
className: "table table--appearance-default",
|
|
276
668
|
children: /*#__PURE__*/ _jsxs("table", {
|
|
@@ -280,66 +672,72 @@ function HierarchyTable({ activeDragRowID, allDocsByID, columns, data, isMovePen
|
|
|
280
672
|
/*#__PURE__*/ _jsx("thead", {
|
|
281
673
|
children: /*#__PURE__*/ _jsx("tr", {
|
|
282
674
|
children: activeColumns.map((column, index)=>/*#__PURE__*/ _jsx("th", {
|
|
283
|
-
"data-page-tree-manual-order-cell": column.accessor === MANUAL_ORDER_COLUMN_ACCESSOR ? 'true' : undefined,
|
|
284
675
|
"data-page-tree-title-cell": column.accessor === titleCellAccessor ? 'true' : undefined,
|
|
285
676
|
id: `heading-${column.accessor.replace(/\./g, '__')}`,
|
|
286
677
|
children: column.Heading
|
|
287
678
|
}, index))
|
|
288
679
|
})
|
|
289
680
|
}),
|
|
290
|
-
/*#__PURE__*/
|
|
291
|
-
children:
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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, {
|
|
300
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,
|
|
301
702
|
/*#__PURE__*/ _jsx(HierarchyTableRow, {
|
|
302
703
|
activeColumns: activeColumns,
|
|
303
704
|
activeDragRowID: activeDragRowID,
|
|
705
|
+
activeDragType: activeDragType,
|
|
706
|
+
canReorderRows: canReorderRows,
|
|
304
707
|
doc: doc,
|
|
305
708
|
dropValidation: rowDropValidationByID.get(doc.__pageTreeID),
|
|
306
|
-
insertAfterDropID:
|
|
307
|
-
insertBeforeDropID:
|
|
709
|
+
insertAfterDropID: hasVisibleDescendantAfter || isTrailingRootInsert ? null : rootInsertAfterDropTarget?.dropID ?? null,
|
|
710
|
+
insertBeforeDropID: rootInsertBeforeDropTarget?.dropID ?? null,
|
|
308
711
|
isMovePending: isMovePending,
|
|
309
712
|
rowIndex: rowIndex,
|
|
310
713
|
titleCellAccessor: titleCellAccessor
|
|
311
|
-
})
|
|
312
|
-
insertDropTargets[rowIndex + 1] ? /*#__PURE__*/ _jsx(HierarchyInsertRow, {
|
|
313
|
-
activeColumnsCount: activeColumns.length,
|
|
314
|
-
activeDragRowID: activeDragRowID,
|
|
315
|
-
dropTarget: insertDropTargets[rowIndex + 1],
|
|
316
|
-
dropValidation: insertDropValidationByID.get(insertDropTargets[rowIndex + 1].dropID),
|
|
317
|
-
isMovePending: isMovePending
|
|
318
|
-
}, insertDropTargets[rowIndex + 1].dropID) : null
|
|
714
|
+
})
|
|
319
715
|
]
|
|
320
|
-
}, doc.__pageTreeID)
|
|
321
|
-
|
|
716
|
+
}, doc.__pageTreeID);
|
|
717
|
+
})
|
|
718
|
+
})
|
|
322
719
|
})
|
|
323
720
|
]
|
|
324
721
|
})
|
|
325
722
|
})
|
|
326
723
|
});
|
|
327
724
|
}
|
|
328
|
-
export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDocs, columnState, homeIndicatorEnabled, orderableFieldName
|
|
725
|
+
export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDocs, columnState, homeIndicatorEnabled, orderableFieldName, parentFieldSlug, query, sourceDocs, useAsTitle, ...props }) {
|
|
329
726
|
const router = useRouter();
|
|
330
727
|
const searchParams = useSearchParams();
|
|
331
728
|
const { config } = useConfig();
|
|
332
729
|
const locale = useLocale();
|
|
333
730
|
const { i18n } = useTranslation();
|
|
334
731
|
const [activeDragRowID, setActiveDragRowID] = React.useState(null);
|
|
732
|
+
const [activeDragType, setActiveDragType] = React.useState(null);
|
|
335
733
|
const [activeDropTarget, setActiveDropTarget] = React.useState(null);
|
|
336
734
|
const [collapsedIDs, setCollapsedIDs] = React.useState(()=>new Set());
|
|
337
|
-
const [
|
|
735
|
+
const [optimisticOrderRowIDs, setOptimisticOrderRowIDs] = React.useState(null);
|
|
736
|
+
const [parentMoveEnabled, setParentMoveEnabled] = React.useState(false);
|
|
338
737
|
const [pendingMoveRowID, setPendingMoveRowID] = React.useState(null);
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
sourceDocs
|
|
738
|
+
const { getPreference, setPreference } = usePreferences();
|
|
739
|
+
const parentMovePreferenceKey = React.useMemo(()=>getParentMovePreferenceKey(props.collectionSlug), [
|
|
740
|
+
props.collectionSlug
|
|
343
741
|
]);
|
|
344
742
|
const toggleRow = React.useCallback((rowID)=>{
|
|
345
743
|
setCollapsedIDs((currentState)=>{
|
|
@@ -352,6 +750,16 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
352
750
|
return nextState;
|
|
353
751
|
});
|
|
354
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
|
+
]);
|
|
355
763
|
const currentSort = React.useMemo(()=>{
|
|
356
764
|
const searchParamSort = searchParams.getAll('sort');
|
|
357
765
|
if (searchParamSort.length > 0) {
|
|
@@ -362,6 +770,10 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
362
770
|
query.sort,
|
|
363
771
|
searchParams
|
|
364
772
|
]);
|
|
773
|
+
const canReorderRows = canMoveDocs && isOrderableSortActive({
|
|
774
|
+
orderableFieldName,
|
|
775
|
+
sort: currentSort
|
|
776
|
+
});
|
|
365
777
|
const currentLimit = React.useMemo(()=>normalizePositiveInt(searchParams.get('limit'), typeof query.limit === 'number' && query.limit > 0 ? query.limit : 10), [
|
|
366
778
|
query.limit,
|
|
367
779
|
searchParams
|
|
@@ -370,13 +782,13 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
370
782
|
query.page,
|
|
371
783
|
searchParams
|
|
372
784
|
]);
|
|
373
|
-
const liveAllDocs = React.useMemo(()=>buildPageTreeDocs(
|
|
785
|
+
const liveAllDocs = React.useMemo(()=>buildPageTreeDocs(sourceDocs, {
|
|
374
786
|
parentFieldSlug,
|
|
375
787
|
sort: currentSort
|
|
376
788
|
}), [
|
|
377
789
|
currentSort,
|
|
378
|
-
|
|
379
|
-
|
|
790
|
+
parentFieldSlug,
|
|
791
|
+
sourceDocs
|
|
380
792
|
]);
|
|
381
793
|
const collapseResetKey = React.useMemo(()=>JSON.stringify([
|
|
382
794
|
props.collectionSlug,
|
|
@@ -391,19 +803,37 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
391
803
|
activeDragRowID,
|
|
392
804
|
canMoveDocs,
|
|
393
805
|
collapsedIDs,
|
|
806
|
+
parentMoveEnabled,
|
|
394
807
|
pendingMoveRowID,
|
|
395
808
|
toggleRow
|
|
396
809
|
}), [
|
|
397
810
|
activeDragRowID,
|
|
398
811
|
canMoveDocs,
|
|
399
812
|
collapsedIDs,
|
|
813
|
+
parentMoveEnabled,
|
|
400
814
|
pendingMoveRowID,
|
|
401
815
|
toggleRow
|
|
402
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
|
+
]);
|
|
403
831
|
React.useEffect(()=>{
|
|
404
832
|
setActiveDragRowID(null);
|
|
833
|
+
setActiveDragType(null);
|
|
405
834
|
setActiveDropTarget(null);
|
|
406
835
|
setCollapsedIDs(new Set());
|
|
836
|
+
setOptimisticOrderRowIDs(null);
|
|
407
837
|
}, [
|
|
408
838
|
collapseResetKey
|
|
409
839
|
]);
|
|
@@ -417,17 +847,36 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
417
847
|
visibleDocs
|
|
418
848
|
]);
|
|
419
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
|
+
]);
|
|
420
869
|
const allDocsByID = React.useMemo(()=>new Map(liveAllDocs.map((doc)=>[
|
|
421
870
|
doc.__pageTreeID,
|
|
422
871
|
doc
|
|
423
872
|
])), [
|
|
424
873
|
liveAllDocs
|
|
425
874
|
]);
|
|
426
|
-
const paginatedDocsByID = React.useMemo(()=>new Map(
|
|
875
|
+
const paginatedDocsByID = React.useMemo(()=>new Map(displayedPaginatedDocs.map((doc)=>[
|
|
427
876
|
doc.__pageTreeID,
|
|
428
877
|
doc
|
|
429
878
|
])), [
|
|
430
|
-
|
|
879
|
+
displayedPaginatedDocs
|
|
431
880
|
]);
|
|
432
881
|
const renderedCellIndexByDocID = React.useMemo(()=>new Map(allDocs.map((doc, index)=>[
|
|
433
882
|
doc.__pageTreeID,
|
|
@@ -435,29 +884,31 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
435
884
|
])), [
|
|
436
885
|
allDocs
|
|
437
886
|
]);
|
|
438
|
-
const normalizedColumnState = React.useMemo(()=>
|
|
887
|
+
const normalizedColumnState = React.useMemo(()=>ensureUseAsTitleColumn(columnState, useAsTitle), [
|
|
439
888
|
columnState,
|
|
440
889
|
useAsTitle
|
|
441
890
|
]);
|
|
442
|
-
const paginatedColumnState = React.useMemo(()=>sliceColumnState(normalizedColumnState,
|
|
891
|
+
const paginatedColumnState = React.useMemo(()=>sliceColumnState(normalizedColumnState, displayedPaginatedDocs, renderedCellIndexByDocID), [
|
|
892
|
+
displayedPaginatedDocs,
|
|
443
893
|
normalizedColumnState,
|
|
444
|
-
paginatedDocs,
|
|
445
894
|
renderedCellIndexByDocID
|
|
446
895
|
]);
|
|
447
896
|
const tableColumns = React.useMemo(()=>buildTableColumns({
|
|
448
897
|
badgeConfig,
|
|
449
898
|
columnState: paginatedColumnState,
|
|
450
|
-
docs:
|
|
899
|
+
docs: displayedPaginatedDocs,
|
|
451
900
|
enableRowSelections: props.enableRowSelections,
|
|
452
901
|
homeIndicatorEnabled,
|
|
902
|
+
orderableFieldName,
|
|
453
903
|
parentFieldSlug,
|
|
454
904
|
t: i18n.t,
|
|
455
905
|
useAsTitle
|
|
456
906
|
}), [
|
|
457
907
|
paginatedColumnState,
|
|
458
|
-
|
|
908
|
+
displayedPaginatedDocs,
|
|
459
909
|
badgeConfig,
|
|
460
910
|
homeIndicatorEnabled,
|
|
911
|
+
orderableFieldName,
|
|
461
912
|
parentFieldSlug,
|
|
462
913
|
props.enableRowSelections,
|
|
463
914
|
i18n.t,
|
|
@@ -465,7 +916,7 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
465
916
|
]);
|
|
466
917
|
const activeDragDoc = activeDragRowID ? paginatedDocsByID.get(activeDragRowID) ?? null : null;
|
|
467
918
|
const activeDragPreviewPath = React.useMemo(()=>{
|
|
468
|
-
if (!activeDragDoc) {
|
|
919
|
+
if (!activeDragDoc || activeDragType !== 'move') {
|
|
469
920
|
return null;
|
|
470
921
|
}
|
|
471
922
|
if (!activeDropTarget) {
|
|
@@ -478,9 +929,10 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
478
929
|
docsByID: allDocsByID,
|
|
479
930
|
dropTarget: activeDropTarget
|
|
480
931
|
});
|
|
481
|
-
const dropValidation =
|
|
932
|
+
const dropValidation = getDropTargetValidation({
|
|
482
933
|
activeDoc: activeDragDoc,
|
|
483
|
-
|
|
934
|
+
docsByID: allDocsByID,
|
|
935
|
+
dropTarget: activeDropTarget
|
|
484
936
|
});
|
|
485
937
|
if (!dropValidation.isValid) {
|
|
486
938
|
return buildDocSlugPath({
|
|
@@ -495,66 +947,46 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
495
947
|
});
|
|
496
948
|
}, [
|
|
497
949
|
activeDragDoc,
|
|
950
|
+
activeDragType,
|
|
498
951
|
activeDropTarget,
|
|
499
952
|
allDocsByID
|
|
500
953
|
]);
|
|
501
954
|
const isMovePending = pendingMoveRowID !== null;
|
|
502
955
|
const sensors = useSensors(useSensor(PointerSensor, {
|
|
503
956
|
activationConstraint: {
|
|
504
|
-
distance:
|
|
957
|
+
distance: 2
|
|
505
958
|
}
|
|
506
959
|
}));
|
|
507
|
-
const moveDocument = React.useCallback(async (args)=>{
|
|
508
|
-
const { parentID, rowID } = args;
|
|
509
|
-
const params = new URLSearchParams();
|
|
510
|
-
if (locale?.code) {
|
|
511
|
-
params.set('locale', locale.code);
|
|
512
|
-
}
|
|
513
|
-
const response = await fetch(`${config.routes.api}/${props.collectionSlug}/${encodeURIComponent(rowID)}/move${params.size > 0 ? `?${params.toString()}` : ''}`, {
|
|
514
|
-
body: JSON.stringify({
|
|
515
|
-
parentID
|
|
516
|
-
}),
|
|
517
|
-
credentials: 'include',
|
|
518
|
-
headers: {
|
|
519
|
-
'Accept-Language': i18n.language,
|
|
520
|
-
'Content-Type': 'application/json',
|
|
521
|
-
[pageTreeMoveRequestHeader]: pageTreeMoveRequestHeaderValue
|
|
522
|
-
},
|
|
523
|
-
method: 'POST'
|
|
524
|
-
});
|
|
525
|
-
const result = await response.json().catch(()=>null);
|
|
526
|
-
if (!response.ok) {
|
|
527
|
-
throw new Error(result?.message ?? 'Could not move document.');
|
|
528
|
-
}
|
|
529
|
-
return result;
|
|
530
|
-
}, [
|
|
531
|
-
config.routes.api,
|
|
532
|
-
i18n.language,
|
|
533
|
-
locale?.code,
|
|
534
|
-
props.collectionSlug
|
|
535
|
-
]);
|
|
536
960
|
const handleDragCancel = React.useCallback(()=>{
|
|
537
961
|
setActiveDragRowID(null);
|
|
962
|
+
setActiveDragType(null);
|
|
538
963
|
setActiveDropTarget(null);
|
|
539
964
|
}, []);
|
|
540
965
|
const handleDragStart = React.useCallback((event)=>{
|
|
541
|
-
const dragData = getPageTreeDragData(event.active.data.current);
|
|
542
966
|
if (!canMoveDocs || isMovePending) {
|
|
543
967
|
return;
|
|
544
968
|
}
|
|
545
|
-
const
|
|
546
|
-
|
|
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)) {
|
|
547
972
|
setActiveDragRowID(rowID);
|
|
973
|
+
setActiveDragType(dragType);
|
|
548
974
|
setActiveDropTarget(null);
|
|
549
975
|
}
|
|
550
976
|
}, [
|
|
551
977
|
canMoveDocs,
|
|
978
|
+
canReorderRows,
|
|
552
979
|
isMovePending,
|
|
553
980
|
paginatedDocsByID
|
|
554
981
|
]);
|
|
555
982
|
const handleDragOver = React.useCallback((event)=>{
|
|
983
|
+
const dragData = getPageTreeDragData(event.active.data.current);
|
|
556
984
|
const overData = event.over?.data.current;
|
|
557
|
-
if (
|
|
985
|
+
if (dragData.dragType === 'order') {
|
|
986
|
+
setActiveDropTarget(null);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
if (dragData.dragType !== 'move' || !overData) {
|
|
558
990
|
setActiveDropTarget(null);
|
|
559
991
|
return;
|
|
560
992
|
}
|
|
@@ -570,51 +1002,132 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
570
1002
|
}, []);
|
|
571
1003
|
const handleDragEnd = React.useCallback(async (event)=>{
|
|
572
1004
|
const dragData = getPageTreeDragData(event.active.data.current);
|
|
573
|
-
const rowID = dragData
|
|
1005
|
+
const { dragType, rowID } = dragData;
|
|
574
1006
|
const activeDoc = typeof rowID === 'string' ? paginatedDocsByID.get(rowID) ?? null : null;
|
|
575
1007
|
const overData = event.over?.data.current;
|
|
1008
|
+
const overDragData = getPageTreeDragData(event.over?.data.current);
|
|
576
1009
|
setActiveDragRowID(null);
|
|
1010
|
+
setActiveDragType(null);
|
|
577
1011
|
setActiveDropTarget(null);
|
|
578
|
-
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
|
+
}
|
|
579
1066
|
return;
|
|
580
1067
|
}
|
|
581
|
-
|
|
1068
|
+
if (dragType !== 'move' || typeof rowID !== 'string' || !activeDoc || !overData) {
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
const dropValidation = getDropTargetValidation({
|
|
1072
|
+
activeDoc,
|
|
582
1073
|
docsByID: allDocsByID,
|
|
583
1074
|
dropTarget: overData
|
|
584
1075
|
});
|
|
585
|
-
const dropValidation = getDropValidation({
|
|
586
|
-
activeDoc,
|
|
587
|
-
targetDoc: targetDoc ?? undefined
|
|
588
|
-
});
|
|
589
1076
|
if (!dropValidation.isValid) {
|
|
590
1077
|
if (!shouldSilenceMoveMessage(dropValidation.message)) {
|
|
591
1078
|
toast.error(dropValidation.message ?? 'Could not move document.');
|
|
592
1079
|
}
|
|
593
1080
|
return;
|
|
594
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
|
+
}
|
|
595
1090
|
setPendingMoveRowID(rowID);
|
|
596
1091
|
try {
|
|
597
|
-
await
|
|
598
|
-
|
|
599
|
-
|
|
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'
|
|
600
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
|
+
}
|
|
601
1111
|
toast.success(`Moved "${getDocDisplayLabel(activeDoc)}".`);
|
|
602
1112
|
React.startTransition(()=>{
|
|
603
1113
|
router.refresh();
|
|
604
1114
|
});
|
|
605
1115
|
} catch (error) {
|
|
606
1116
|
const message = error instanceof Error && error.message ? error.message : 'Could not move document.';
|
|
607
|
-
if (shouldSilenceMoveMessage(message)) {
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
1117
|
toast.error(message);
|
|
611
1118
|
} finally{
|
|
612
1119
|
setPendingMoveRowID(null);
|
|
613
1120
|
}
|
|
614
1121
|
}, [
|
|
615
1122
|
allDocsByID,
|
|
616
|
-
|
|
1123
|
+
config.routes.api,
|
|
1124
|
+
currentSort,
|
|
1125
|
+
displayedPaginatedDocs,
|
|
1126
|
+
i18n.language,
|
|
1127
|
+
locale?.code,
|
|
1128
|
+
orderableFieldName,
|
|
617
1129
|
paginatedDocsByID,
|
|
1130
|
+
props.collectionSlug,
|
|
618
1131
|
router
|
|
619
1132
|
]);
|
|
620
1133
|
const handleDragEndSync = React.useCallback((event)=>{
|
|
@@ -632,9 +1145,12 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
632
1145
|
children: [
|
|
633
1146
|
/*#__PURE__*/ _jsx(HierarchyTable, {
|
|
634
1147
|
activeDragRowID: activeDragRowID,
|
|
1148
|
+
activeDragType: activeDragType,
|
|
1149
|
+
activeDropTarget: activeDropTarget,
|
|
635
1150
|
allDocsByID: allDocsByID,
|
|
1151
|
+
canReorderRows: canReorderRows,
|
|
636
1152
|
columns: tableColumns,
|
|
637
|
-
data:
|
|
1153
|
+
data: displayedPaginatedDocs,
|
|
638
1154
|
isMovePending: isMovePending,
|
|
639
1155
|
titleCellAccessor: useAsTitle
|
|
640
1156
|
}),
|
|
@@ -652,33 +1168,57 @@ export default function PageTreeListViewClient({ allDocs, badgeConfig, canMoveDo
|
|
|
652
1168
|
}), [
|
|
653
1169
|
activeDragPreviewPath,
|
|
654
1170
|
activeDragRowID,
|
|
1171
|
+
activeDragType,
|
|
1172
|
+
activeDropTarget,
|
|
1173
|
+
allDocsByID,
|
|
1174
|
+
canReorderRows,
|
|
1175
|
+
displayedPaginatedDocs,
|
|
655
1176
|
handleDragCancel,
|
|
656
1177
|
handleDragEndSync,
|
|
657
1178
|
handleDragOver,
|
|
658
1179
|
handleDragStart,
|
|
659
1180
|
isMovePending,
|
|
660
|
-
allDocsByID,
|
|
661
|
-
paginatedDocs,
|
|
662
1181
|
sensors,
|
|
663
1182
|
tableColumns,
|
|
664
1183
|
useAsTitle
|
|
665
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
|
+
]);
|
|
666
1204
|
return /*#__PURE__*/ _jsx("div", {
|
|
667
1205
|
className: styles.root,
|
|
668
1206
|
children: /*#__PURE__*/ _jsx(PageTreeProvider, {
|
|
669
1207
|
value: hierarchyValue,
|
|
670
1208
|
children: /*#__PURE__*/ _jsx(ListQueryProvider, {
|
|
671
1209
|
collectionSlug: props.collectionSlug,
|
|
672
|
-
data:
|
|
1210
|
+
data: displayedPaginatedData,
|
|
673
1211
|
modifySearchParams: true,
|
|
1212
|
+
orderableFieldName: orderableFieldName,
|
|
674
1213
|
query: {
|
|
675
1214
|
...query,
|
|
676
1215
|
limit: currentLimit,
|
|
677
|
-
page:
|
|
1216
|
+
page: displayedPaginatedData.page,
|
|
678
1217
|
sort: currentSort
|
|
679
1218
|
},
|
|
680
1219
|
children: /*#__PURE__*/ _jsx(DefaultListView, {
|
|
681
1220
|
...props,
|
|
1221
|
+
beforeActions: beforeActions,
|
|
682
1222
|
columnState: paginatedColumnState,
|
|
683
1223
|
Table: tableNode
|
|
684
1224
|
})
|