@strapi/content-type-builder 5.53.0 → 5.55.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/dist/admin/components/AIChat/hooks/useAIFetch.js +6 -2
- package/dist/admin/components/AIChat/hooks/useAIFetch.js.map +1 -1
- package/dist/admin/components/AIChat/hooks/useAIFetch.mjs +7 -3
- package/dist/admin/components/AIChat/hooks/useAIFetch.mjs.map +1 -1
- package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.js +20 -0
- package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.js.map +1 -0
- package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.mjs +18 -0
- package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.js +83 -59
- package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.js.map +1 -1
- package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.mjs +86 -62
- package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.mjs.map +1 -1
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.js +112 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.mjs +110 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.js +159 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.mjs +157 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.js +311 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.mjs +309 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.js +379 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.mjs +376 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.js +172 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.mjs +166 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.js +62 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.mjs +60 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.js +145 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.mjs +143 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.js +176 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.mjs +169 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.js +277 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.mjs +273 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.js +62 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.mjs +58 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.mjs.map +1 -0
- package/dist/admin/components/DataManager/DataManagerContext.js.map +1 -1
- package/dist/admin/components/DataManager/DataManagerContext.mjs.map +1 -1
- package/dist/admin/components/DataManager/DataManagerProvider.js +70 -8
- package/dist/admin/components/DataManager/DataManagerProvider.js.map +1 -1
- package/dist/admin/components/DataManager/DataManagerProvider.mjs +70 -8
- package/dist/admin/components/DataManager/DataManagerProvider.mjs.map +1 -1
- package/dist/admin/components/DataManager/reducer.js +411 -33
- package/dist/admin/components/DataManager/reducer.js.map +1 -1
- package/dist/admin/components/DataManager/reducer.mjs +411 -33
- package/dist/admin/components/DataManager/reducer.mjs.map +1 -1
- package/dist/admin/components/DataManager/utils/cleanData.js +11 -2
- package/dist/admin/components/DataManager/utils/cleanData.js.map +1 -1
- package/dist/admin/components/DataManager/utils/cleanData.mjs +11 -2
- package/dist/admin/components/DataManager/utils/cleanData.mjs.map +1 -1
- package/dist/admin/components/DataManager/utils/contentStructure.js +150 -0
- package/dist/admin/components/DataManager/utils/contentStructure.js.map +1 -0
- package/dist/admin/components/DataManager/utils/contentStructure.mjs +133 -0
- package/dist/admin/components/DataManager/utils/contentStructure.mjs.map +1 -0
- package/dist/admin/components/FolderSelect.js +200 -0
- package/dist/admin/components/FolderSelect.js.map +1 -0
- package/dist/admin/components/FolderSelect.mjs +198 -0
- package/dist/admin/components/FolderSelect.mjs.map +1 -0
- package/dist/admin/components/FormModal/FormModal.js +23 -2
- package/dist/admin/components/FormModal/FormModal.js.map +1 -1
- package/dist/admin/components/FormModal/FormModal.mjs +23 -2
- package/dist/admin/components/FormModal/FormModal.mjs.map +1 -1
- package/dist/admin/components/FormModal/contentType/contentTypeForm.js +15 -18
- package/dist/admin/components/FormModal/contentType/contentTypeForm.js.map +1 -1
- package/dist/admin/components/FormModal/contentType/contentTypeForm.mjs +15 -18
- package/dist/admin/components/FormModal/contentType/contentTypeForm.mjs.map +1 -1
- package/dist/admin/components/FormModal/reducer.js +6 -0
- package/dist/admin/components/FormModal/reducer.js.map +1 -1
- package/dist/admin/components/FormModal/reducer.mjs +6 -0
- package/dist/admin/components/FormModal/reducer.mjs.map +1 -1
- package/dist/admin/pages/App/index.js +2 -5
- package/dist/admin/pages/App/index.js.map +1 -1
- package/dist/admin/pages/App/index.mjs +2 -5
- package/dist/admin/pages/App/index.mjs.map +1 -1
- package/dist/admin/src/components/AIChat/hooks/usePrefetchAIToken.d.ts +1 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.d.ts +16 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.d.ts +12 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.d.ts +14 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.d.ts +38 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/{useContentTypeBuilderMenu.d.ts → hooks/useContentTypeBuilderMenu.d.ts} +17 -7
- package/dist/admin/src/components/ContentTypeBuilderNav/hooks/useFolderActions.d.ts +42 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/hooks/useSortableTree.d.ts +53 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/lib/buildFolderTree.d.ts +42 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/lib/dropProjection.d.ts +58 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/lib/flatModel.d.ts +18 -0
- package/dist/admin/src/components/DataManager/DataManagerContext.d.ts +41 -0
- package/dist/admin/src/components/DataManager/reducer.d.ts +105 -1
- package/dist/admin/src/components/DataManager/utils/cleanData.d.ts +5 -1
- package/dist/admin/src/components/DataManager/utils/contentStructure.d.ts +68 -0
- package/dist/admin/src/components/FolderSelect.d.ts +18 -0
- package/dist/admin/src/components/FormModal/contentType/contentTypeForm.d.ts +0 -21
- package/dist/admin/src/components/FormModal/forms/forms.d.ts +0 -10
- package/dist/admin/src/components/FormModal/reducer.d.ts +7 -0
- package/dist/admin/src/utils/folderTelemetry.d.ts +5 -0
- package/dist/admin/translations/en.json.js +23 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +23 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/folderTelemetry.js +19 -0
- package/dist/admin/utils/folderTelemetry.js.map +1 -0
- package/dist/admin/utils/folderTelemetry.mjs +16 -0
- package/dist/admin/utils/folderTelemetry.mjs.map +1 -0
- package/dist/server/controllers/schema.js +1 -1
- package/dist/server/controllers/schema.js.map +1 -1
- package/dist/server/controllers/schema.mjs +1 -1
- package/dist/server/controllers/schema.mjs.map +1 -1
- package/dist/server/controllers/validation/common.js +3 -0
- package/dist/server/controllers/validation/common.js.map +1 -1
- package/dist/server/controllers/validation/common.mjs +3 -1
- package/dist/server/controllers/validation/common.mjs.map +1 -1
- package/dist/server/controllers/validation/content-structure.js +177 -0
- package/dist/server/controllers/validation/content-structure.js.map +1 -0
- package/dist/server/controllers/validation/content-structure.mjs +173 -0
- package/dist/server/controllers/validation/content-structure.mjs.map +1 -0
- package/dist/server/controllers/validation/schema.js +3 -1
- package/dist/server/controllers/validation/schema.js.map +1 -1
- package/dist/server/controllers/validation/schema.mjs +3 -1
- package/dist/server/controllers/validation/schema.mjs.map +1 -1
- package/dist/server/register.js +1 -1
- package/dist/server/register.js.map +1 -1
- package/dist/server/register.mjs +1 -1
- package/dist/server/register.mjs.map +1 -1
- package/dist/server/routes/content-api.js +4 -4
- package/dist/server/routes/content-api.js.map +1 -1
- package/dist/server/routes/content-api.mjs +4 -4
- package/dist/server/routes/content-api.mjs.map +1 -1
- package/dist/server/services/api-handler.js +37 -4
- package/dist/server/services/api-handler.js.map +1 -1
- package/dist/server/services/api-handler.mjs +36 -5
- package/dist/server/services/api-handler.mjs.map +1 -1
- package/dist/server/services/content-structure.js +174 -0
- package/dist/server/services/content-structure.js.map +1 -0
- package/dist/server/services/content-structure.mjs +172 -0
- package/dist/server/services/content-structure.mjs.map +1 -0
- package/dist/server/services/content-types.js +196 -44
- package/dist/server/services/content-types.js.map +1 -1
- package/dist/server/services/content-types.mjs +195 -45
- package/dist/server/services/content-types.mjs.map +1 -1
- package/dist/server/services/index.js +3 -1
- package/dist/server/services/index.js.map +1 -1
- package/dist/server/services/index.mjs +3 -1
- package/dist/server/services/index.mjs.map +1 -1
- package/dist/server/services/schema-builder/index.js +4 -2
- package/dist/server/services/schema-builder/index.js.map +1 -1
- package/dist/server/services/schema-builder/index.mjs +4 -2
- package/dist/server/services/schema-builder/index.mjs.map +1 -1
- package/dist/server/services/schema-builder/schema-handler.js +19 -17
- package/dist/server/services/schema-builder/schema-handler.js.map +1 -1
- package/dist/server/services/schema-builder/schema-handler.mjs +19 -17
- package/dist/server/services/schema-builder/schema-handler.mjs.map +1 -1
- package/dist/server/services/schema-mutation.js +46 -0
- package/dist/server/services/schema-mutation.js.map +1 -0
- package/dist/server/services/schema-mutation.mjs +43 -0
- package/dist/server/services/schema-mutation.mjs.map +1 -0
- package/dist/server/services/schema.js +134 -79
- package/dist/server/services/schema.js.map +1 -1
- package/dist/server/services/schema.mjs +135 -80
- package/dist/server/services/schema.mjs.map +1 -1
- package/dist/server/src/controllers/schema.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/common.d.ts +1 -0
- package/dist/server/src/controllers/validation/common.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/content-structure.d.ts +56 -0
- package/dist/server/src/controllers/validation/content-structure.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/schema.d.ts +35 -0
- package/dist/server/src/controllers/validation/schema.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +3 -0
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/routes/content-api.d.ts.map +1 -1
- package/dist/server/src/services/api-handler.d.ts +13 -1
- package/dist/server/src/services/api-handler.d.ts.map +1 -1
- package/dist/server/src/services/content-structure.d.ts +37 -0
- package/dist/server/src/services/content-structure.d.ts.map +1 -0
- package/dist/server/src/services/content-types.d.ts +4 -0
- package/dist/server/src/services/content-types.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +4 -0
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/schema-builder/index.d.ts +3 -1
- package/dist/server/src/services/schema-builder/index.d.ts.map +1 -1
- package/dist/server/src/services/schema-builder/schema-handler.d.ts.map +1 -1
- package/dist/server/src/services/schema-mutation.d.ts +26 -0
- package/dist/server/src/services/schema-mutation.d.ts.map +1 -0
- package/dist/server/src/services/schema.d.ts +1 -0
- package/dist/server/src/services/schema.d.ts.map +1 -1
- package/dist/server/src/utils/index.d.ts +2 -0
- package/dist/server/src/utils/index.d.ts.map +1 -1
- package/dist/server/utils/index.js.map +1 -1
- package/dist/server/utils/index.mjs.map +1 -1
- package/package.json +8 -8
- package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.js +0 -157
- package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.js.map +0 -1
- package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.mjs +0 -151
- package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.mjs.map +0 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { useState, useRef, useCallback } from 'react';
|
|
2
|
+
import { pointerWithin, closestCenter, useSensors, useSensor, PointerSensor } from '@dnd-kit/core';
|
|
3
|
+
import { resolveDropTarget } from '../lib/dropProjection.mjs';
|
|
4
|
+
import { removeSubtree } from '../lib/flatModel.mjs';
|
|
5
|
+
|
|
6
|
+
// How long the pointer must dwell over a closed folder's nest zone before it
|
|
7
|
+
// springs open, letting you drop deeper.
|
|
8
|
+
const SPRING_OPEN_MS = 700;
|
|
9
|
+
const useHoverFolderOpen = (onExpandFolder)=>{
|
|
10
|
+
const springRef = useRef({
|
|
11
|
+
folderId: null
|
|
12
|
+
});
|
|
13
|
+
const cancelFolderOpen = ()=>{
|
|
14
|
+
clearTimeout(springRef.current.timer);
|
|
15
|
+
springRef.current = {
|
|
16
|
+
folderId: null
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
// Arm (once per folder) a timer that springs the closed folder open after a
|
|
20
|
+
// dwell, so the user can drop into a deeper level.
|
|
21
|
+
const scheduleFolderOpen = (folderId)=>{
|
|
22
|
+
if (springRef.current.folderId === folderId) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
cancelFolderOpen();
|
|
26
|
+
springRef.current.folderId = folderId;
|
|
27
|
+
springRef.current.timer = setTimeout(()=>{
|
|
28
|
+
onExpandFolder(folderId);
|
|
29
|
+
springRef.current.folderId = null;
|
|
30
|
+
}, SPRING_OPEN_MS);
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
scheduleFolderOpen,
|
|
34
|
+
cancelFolderOpen
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The drag-and-drop engine for a flattened folder tree: it owns the drag state
|
|
39
|
+
* machine, pointer-vs-row geometry, spring-open timers and drop projection, and
|
|
40
|
+
* returns the props a `DndContext` + `SortableContext` need plus the resolved
|
|
41
|
+
* drop indicator. It carries no domain logic — nesting rules, the move itself,
|
|
42
|
+
* and expansion are supplied by the caller through {@link UseSortableTreeOptions}.
|
|
43
|
+
*/ const useSortableTree = ({ onExpandFolder, collapsed, disabled, onDrop, canDrop = ()=>true, items })=>{
|
|
44
|
+
const [projected, setProjected] = useState(null);
|
|
45
|
+
const [activeId, setActiveId] = useState(null);
|
|
46
|
+
const pointerRef = useRef(null);
|
|
47
|
+
const { scheduleFolderOpen, cancelFolderOpen } = useHoverFolderOpen(onExpandFolder);
|
|
48
|
+
const collisionDetection = useCallback((args)=>{
|
|
49
|
+
// Captured here (onDragMove has no pointer position) to decide whether the
|
|
50
|
+
// pointer sits over a folder's nest zone (its middle third).
|
|
51
|
+
pointerRef.current = args.pointerCoordinates;
|
|
52
|
+
// Resolve `over` from the pointer, NOT the drag overlay's centre. dnd-kit
|
|
53
|
+
// keeps the grab point under the cursor, so the overlay centre sits up to
|
|
54
|
+
// half a row from the pointer depending on where the row was picked up; with
|
|
55
|
+
// closest-centre, `over` and the pointer-based nest-zone check then disagree
|
|
56
|
+
// and nesting into a folder misfires. Fall back to closest-centre only when
|
|
57
|
+
// the pointer is outside every row (the list's padding / edges). A static
|
|
58
|
+
// list keeps both results stable, so no hysteresis is needed.
|
|
59
|
+
const withinPointer = pointerWithin(args);
|
|
60
|
+
return withinPointer.length > 0 ? withinPointer : closestCenter(args);
|
|
61
|
+
}, []);
|
|
62
|
+
// While dragging a folder, its descendents are hidden so it cannot nest in itself.
|
|
63
|
+
const renderedItems = activeId ? removeSubtree(items, activeId) : items;
|
|
64
|
+
const activeItem = items.find((item)=>item.id === activeId) ?? null;
|
|
65
|
+
const sensors = useSensors(useSensor(PointerSensor, {
|
|
66
|
+
activationConstraint: {
|
|
67
|
+
distance: 8
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
70
|
+
const onDragStart = (event)=>{
|
|
71
|
+
if (disabled) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
cancelFolderOpen();
|
|
75
|
+
setActiveId(String(event.active.id));
|
|
76
|
+
setProjected(null);
|
|
77
|
+
};
|
|
78
|
+
const onDragCancel = ()=>{
|
|
79
|
+
resetDrag();
|
|
80
|
+
};
|
|
81
|
+
const onDragMove = (event)=>{
|
|
82
|
+
if (!activeId || !event.over) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const target = resolveDropTarget(renderedItems, {
|
|
86
|
+
pointer: pointerRef.current ?? undefined,
|
|
87
|
+
overId: String(event.over.id),
|
|
88
|
+
overRect: event.over.rect,
|
|
89
|
+
offsetX: event.delta.x,
|
|
90
|
+
activeId
|
|
91
|
+
});
|
|
92
|
+
if (target && activeItem && !canDrop(activeItem.node, target)) {
|
|
93
|
+
setProjected(null);
|
|
94
|
+
cancelFolderOpen();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
setProjected(target);
|
|
98
|
+
if (target?.kind === 'nest' && collapsed.has(target.folderId)) {
|
|
99
|
+
scheduleFolderOpen(target.folderId);
|
|
100
|
+
} else {
|
|
101
|
+
cancelFolderOpen();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const resetDrag = ()=>{
|
|
105
|
+
cancelFolderOpen();
|
|
106
|
+
setActiveId(null);
|
|
107
|
+
setProjected(null);
|
|
108
|
+
};
|
|
109
|
+
const onDragEnd = ()=>{
|
|
110
|
+
if (disabled) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const item = activeItem;
|
|
114
|
+
const target = projected;
|
|
115
|
+
resetDrag();
|
|
116
|
+
if (!item || !target) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
onDrop(item.node, target);
|
|
120
|
+
};
|
|
121
|
+
const dropLine = projected?.kind === 'insert' && projected.line ? {
|
|
122
|
+
...projected.line,
|
|
123
|
+
depth: projected.depth
|
|
124
|
+
} : null;
|
|
125
|
+
const dropTargetId = projected?.kind === 'nest' ? `folder:${projected.folderId}` : null;
|
|
126
|
+
const sortingStrategy = ()=>null;
|
|
127
|
+
return {
|
|
128
|
+
collisionDetection,
|
|
129
|
+
sortingStrategy,
|
|
130
|
+
renderedItems,
|
|
131
|
+
dropTargetId,
|
|
132
|
+
activeItem,
|
|
133
|
+
dropLine,
|
|
134
|
+
sensors,
|
|
135
|
+
onDragCancel,
|
|
136
|
+
onDragStart,
|
|
137
|
+
onDragMove,
|
|
138
|
+
onDragEnd
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export { useSortableTree };
|
|
143
|
+
//# sourceMappingURL=useSortableTree.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSortableTree.mjs","sources":["../../../../../admin/src/components/ContentTypeBuilderNav/hooks/useSortableTree.ts"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\n\nimport { closestCenter, PointerSensor, pointerWithin, useSensor, useSensors } from '@dnd-kit/core';\n\nimport { resolveDropTarget } from '../lib/dropProjection';\nimport { removeSubtree } from '../lib/flatModel';\n\nimport type { FolderTreeNode } from '../lib/buildFolderTree';\nimport type { DropTarget } from '../lib/dropProjection';\nimport type { FlatItem } from '../lib/flatModel';\nimport type {\n CollisionDetection,\n DragMoveEvent,\n DragStartEvent,\n SensorDescriptor,\n} from '@dnd-kit/core';\nimport type { SortingStrategy } from '@dnd-kit/sortable';\n\n// How long the pointer must dwell over a closed folder's nest zone before it\n// springs open, letting you drop deeper.\nconst SPRING_OPEN_MS = 700;\n\n/** The insertion line to paint, plus the depth its left edge indents to. */\nexport type DropLine = { anchorId: string; edge: 'top' | 'bottom'; depth: number };\n\ninterface UseSortableTreeOptions {\n /** Commit a drop. Called only for a real move, with the node and its resolved target. */\n onDrop: (activeNode: FolderTreeNode, target: DropTarget) => void;\n /**\n * Function which determines whether the dragged node may land on the resolved target.\n */\n canDrop?: (activeNode: FolderTreeNode, target: DropTarget) => boolean;\n /** Spring-open a closed folder held under the pointer, so the user can drop deeper. */\n onExpandFolder: (folderId: string) => void;\n /** Ids of collapsed folders — used to tell a closed folder (springs open) from an empty one. */\n collapsed: Set<string>;\n /** When true the contentStructure tree is read-only. */\n disabled: boolean;\n /** The full flattened, visible tree (descendants of collapsed folders omitted). */\n items: FlatItem[];\n}\n\nexport interface UseSortableTreeResult {\n collisionDetection: CollisionDetection;\n sensors: SensorDescriptor<object>[];\n sortingStrategy: SortingStrategy;\n /** The items to render (the dragged folder's descendants are hidden mid-drag). */\n renderedItems: FlatItem[];\n /** The row being dragged, or null. */\n activeItem: FlatItem | null;\n /** Folder row (by dnd id) to highlight as the nest target — no line is drawn for it. */\n dropTargetId: string | null;\n /** The insertion line to paint, or null when nesting into a highlighted folder. */\n dropLine: DropLine | null;\n onDragStart: (event: DragStartEvent) => void;\n onDragMove: (event: DragMoveEvent) => void;\n onDragCancel: () => void;\n onDragEnd: () => void;\n}\n\ntype SpringRef = { folderId: string | null; timer?: ReturnType<typeof setTimeout> };\n\nconst useHoverFolderOpen = (onExpandFolder: (folderId: string) => void) => {\n const springRef = useRef<SpringRef>({ folderId: null });\n\n const cancelFolderOpen = () => {\n clearTimeout(springRef.current.timer);\n springRef.current = { folderId: null };\n };\n\n // Arm (once per folder) a timer that springs the closed folder open after a\n // dwell, so the user can drop into a deeper level.\n const scheduleFolderOpen = (folderId: string) => {\n if (springRef.current.folderId === folderId) {\n return;\n }\n\n cancelFolderOpen();\n springRef.current.folderId = folderId;\n\n springRef.current.timer = setTimeout(() => {\n onExpandFolder(folderId);\n springRef.current.folderId = null;\n }, SPRING_OPEN_MS);\n };\n\n return { scheduleFolderOpen, cancelFolderOpen };\n};\n\n/**\n * The drag-and-drop engine for a flattened folder tree: it owns the drag state\n * machine, pointer-vs-row geometry, spring-open timers and drop projection, and\n * returns the props a `DndContext` + `SortableContext` need plus the resolved\n * drop indicator. It carries no domain logic — nesting rules, the move itself,\n * and expansion are supplied by the caller through {@link UseSortableTreeOptions}.\n */\nexport const useSortableTree = ({\n onExpandFolder,\n collapsed,\n disabled,\n onDrop,\n canDrop = () => true,\n items,\n}: UseSortableTreeOptions): UseSortableTreeResult => {\n const [projected, setProjected] = useState<DropTarget | null>(null);\n const [activeId, setActiveId] = useState<string | null>(null);\n\n const pointerRef = useRef<{ x: number; y: number } | null>(null);\n const { scheduleFolderOpen, cancelFolderOpen } = useHoverFolderOpen(onExpandFolder);\n\n const collisionDetection = useCallback<CollisionDetection>((args) => {\n // Captured here (onDragMove has no pointer position) to decide whether the\n // pointer sits over a folder's nest zone (its middle third).\n pointerRef.current = args.pointerCoordinates;\n\n // Resolve `over` from the pointer, NOT the drag overlay's centre. dnd-kit\n // keeps the grab point under the cursor, so the overlay centre sits up to\n // half a row from the pointer depending on where the row was picked up; with\n // closest-centre, `over` and the pointer-based nest-zone check then disagree\n // and nesting into a folder misfires. Fall back to closest-centre only when\n // the pointer is outside every row (the list's padding / edges). A static\n // list keeps both results stable, so no hysteresis is needed.\n const withinPointer = pointerWithin(args);\n return withinPointer.length > 0 ? withinPointer : closestCenter(args);\n }, []);\n\n // While dragging a folder, its descendents are hidden so it cannot nest in itself.\n const renderedItems = activeId ? removeSubtree(items, activeId) : items;\n const activeItem = items.find((item) => item.id === activeId) ?? null;\n\n const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 8 } }));\n\n const onDragStart = (event: DragStartEvent) => {\n if (disabled) {\n return;\n }\n\n cancelFolderOpen();\n setActiveId(String(event.active.id));\n setProjected(null);\n };\n\n const onDragCancel = () => {\n resetDrag();\n };\n\n const onDragMove = (event: DragMoveEvent) => {\n if (!activeId || !event.over) {\n return;\n }\n\n const target = resolveDropTarget(renderedItems, {\n pointer: pointerRef.current ?? undefined,\n overId: String(event.over.id),\n overRect: event.over.rect,\n offsetX: event.delta.x,\n activeId,\n });\n\n if (target && activeItem && !canDrop(activeItem.node, target)) {\n setProjected(null);\n cancelFolderOpen();\n return;\n }\n\n setProjected(target);\n\n if (target?.kind === 'nest' && collapsed.has(target.folderId)) {\n scheduleFolderOpen(target.folderId);\n } else {\n cancelFolderOpen();\n }\n };\n\n const resetDrag = () => {\n cancelFolderOpen();\n setActiveId(null);\n setProjected(null);\n };\n\n const onDragEnd = () => {\n if (disabled) {\n return;\n }\n\n const item = activeItem;\n const target = projected;\n\n resetDrag();\n\n if (!item || !target) {\n return;\n }\n\n onDrop(item.node, target);\n };\n\n const dropLine =\n projected?.kind === 'insert' && projected.line\n ? { ...projected.line, depth: projected.depth }\n : null;\n\n const dropTargetId = projected?.kind === 'nest' ? `folder:${projected.folderId}` : null;\n\n const sortingStrategy: SortingStrategy = () => null;\n\n return {\n collisionDetection,\n sortingStrategy,\n renderedItems,\n dropTargetId,\n activeItem,\n dropLine,\n sensors,\n onDragCancel,\n onDragStart,\n onDragMove,\n onDragEnd,\n };\n};\n"],"names":["SPRING_OPEN_MS","useHoverFolderOpen","onExpandFolder","springRef","useRef","folderId","cancelFolderOpen","clearTimeout","current","timer","scheduleFolderOpen","setTimeout","useSortableTree","collapsed","disabled","onDrop","canDrop","items","projected","setProjected","useState","activeId","setActiveId","pointerRef","collisionDetection","useCallback","args","pointerCoordinates","withinPointer","pointerWithin","length","closestCenter","renderedItems","removeSubtree","activeItem","find","item","id","sensors","useSensors","useSensor","PointerSensor","activationConstraint","distance","onDragStart","event","String","active","onDragCancel","resetDrag","onDragMove","over","target","resolveDropTarget","pointer","undefined","overId","overRect","rect","offsetX","delta","x","node","kind","has","onDragEnd","dropLine","line","depth","dropTargetId","sortingStrategy"],"mappings":"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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var contentStructure = require('../../DataManager/utils/contentStructure.js');
|
|
4
|
+
|
|
5
|
+
function buildSectionTree(section, links, compareTitle) {
|
|
6
|
+
const groups = section.groups;
|
|
7
|
+
const groupById = new Map(groups.map((group)=>[
|
|
8
|
+
group.id,
|
|
9
|
+
group
|
|
10
|
+
]));
|
|
11
|
+
const linkByUid = new Map(links.map((link)=>[
|
|
12
|
+
link.uid,
|
|
13
|
+
link
|
|
14
|
+
]));
|
|
15
|
+
const childGroupIds = new Set();
|
|
16
|
+
for (const group of groups){
|
|
17
|
+
for (const child of group.children){
|
|
18
|
+
if (child.type === 'group') {
|
|
19
|
+
childGroupIds.add(child.id);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const grouped = new Set();
|
|
24
|
+
const buildFolder = (group, depth)=>{
|
|
25
|
+
const children = [];
|
|
26
|
+
for (const child of group.children){
|
|
27
|
+
if (child.type === 'group') {
|
|
28
|
+
const subGroup = groupById.get(child.id);
|
|
29
|
+
if (!subGroup) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
children.push(buildFolder(subGroup, depth + 1));
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const link = linkByUid.get(child.uid);
|
|
36
|
+
if (!link) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
grouped.add(child.uid);
|
|
40
|
+
children.push({
|
|
41
|
+
type: 'contentType',
|
|
42
|
+
status: link.status,
|
|
43
|
+
parentId: group.id,
|
|
44
|
+
title: link.title,
|
|
45
|
+
depth: depth + 1,
|
|
46
|
+
uid: link.uid,
|
|
47
|
+
to: link.to
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
type: 'folder',
|
|
52
|
+
parentId: group.parent,
|
|
53
|
+
status: group.status,
|
|
54
|
+
name: group.name,
|
|
55
|
+
id: group.id,
|
|
56
|
+
children,
|
|
57
|
+
depth
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
const rootFolders = groups.filter((group)=>!childGroupIds.has(group.id)).map((group)=>buildFolder(group, 0));
|
|
61
|
+
const ungrouped = links.filter((link)=>!grouped.has(link.uid)).sort((a, b)=>compareTitle(a.title, b.title)).map((link)=>({
|
|
62
|
+
type: 'contentType',
|
|
63
|
+
status: link.status,
|
|
64
|
+
title: link.title,
|
|
65
|
+
parentId: null,
|
|
66
|
+
uid: link.uid,
|
|
67
|
+
to: link.to,
|
|
68
|
+
depth: 0
|
|
69
|
+
}));
|
|
70
|
+
return [
|
|
71
|
+
...ungrouped,
|
|
72
|
+
...rootFolders
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
function filterTree(nodes, matches) {
|
|
76
|
+
const result = [];
|
|
77
|
+
for (const node of nodes){
|
|
78
|
+
if (node.type === 'contentType') {
|
|
79
|
+
if (matches(node.title) || matches(node.uid)) {
|
|
80
|
+
result.push(node);
|
|
81
|
+
}
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (matches(node.name)) {
|
|
85
|
+
result.push(node);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const filteredChildren = filterTree(node.children, matches);
|
|
89
|
+
if (filteredChildren.length > 0) {
|
|
90
|
+
result.push({
|
|
91
|
+
...node,
|
|
92
|
+
children: filteredChildren
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
function subtreeFolderHeight(folder) {
|
|
99
|
+
let deepestChild = 0;
|
|
100
|
+
for (const child of folder.children){
|
|
101
|
+
if (child.type === 'folder') {
|
|
102
|
+
deepestChild = Math.max(deepestChild, subtreeFolderHeight(child));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return 1 + deepestChild;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Whether moving `folder` under a target (a folder, or `null` for the section
|
|
109
|
+
* root) would keep the resulting subtree within {@link MAX_FOLDER_DEPTH}.
|
|
110
|
+
*/ function canNestFolderAt(folder, target) {
|
|
111
|
+
const targetDomainDepth = target ? target.depth + 1 : 0;
|
|
112
|
+
const deepestResult = targetDomainDepth + subtreeFolderHeight(folder);
|
|
113
|
+
return deepestResult <= contentStructure.MAX_FOLDER_DEPTH;
|
|
114
|
+
}
|
|
115
|
+
function countSubtree(section, folderId, countableUids) {
|
|
116
|
+
const groupById = new Map(section.groups.map((group)=>[
|
|
117
|
+
group.id,
|
|
118
|
+
group
|
|
119
|
+
]));
|
|
120
|
+
let contentTypes = 0;
|
|
121
|
+
let subfolders = 0;
|
|
122
|
+
const walk = (group)=>{
|
|
123
|
+
for (const child of group.children){
|
|
124
|
+
if (child.type === 'contentType') {
|
|
125
|
+
if (countableUids.has(child.uid)) {
|
|
126
|
+
contentTypes += 1;
|
|
127
|
+
}
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const childGroup = groupById.get(child.id);
|
|
131
|
+
if (!childGroup) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
subfolders += 1;
|
|
135
|
+
walk(childGroup);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const root = groupById.get(folderId);
|
|
139
|
+
if (root) {
|
|
140
|
+
walk(root);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
contentTypes,
|
|
144
|
+
subfolders
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function collectSubtreeContentTypeUids(section, folderId) {
|
|
148
|
+
const groupById = new Map(section.groups.map((group)=>[
|
|
149
|
+
group.id,
|
|
150
|
+
group
|
|
151
|
+
]));
|
|
152
|
+
const uids = [];
|
|
153
|
+
const walk = (id)=>{
|
|
154
|
+
const group = groupById.get(id);
|
|
155
|
+
if (!group) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
for (const child of group.children){
|
|
159
|
+
if (child.type === 'contentType') {
|
|
160
|
+
uids.push(child.uid);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
walk(child.id);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
walk(folderId);
|
|
167
|
+
return uids;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
exports.buildSectionTree = buildSectionTree;
|
|
171
|
+
exports.canNestFolderAt = canNestFolderAt;
|
|
172
|
+
exports.collectSubtreeContentTypeUids = collectSubtreeContentTypeUids;
|
|
173
|
+
exports.countSubtree = countSubtree;
|
|
174
|
+
exports.filterTree = filterTree;
|
|
175
|
+
exports.subtreeFolderHeight = subtreeFolderHeight;
|
|
176
|
+
//# sourceMappingURL=buildFolderTree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildFolderTree.js","sources":["../../../../../admin/src/components/ContentTypeBuilderNav/lib/buildFolderTree.ts"],"sourcesContent":["import { MAX_FOLDER_DEPTH } from '../../DataManager/utils/contentStructure';\n\nimport type { Status } from '../../../types';\nimport type {\n ContentStructureSection,\n GroupStatus,\n} from '../../DataManager/utils/contentStructure';\nimport type { UID } from '@strapi/types';\n\nexport type ContentTypeLink = {\n uid: UID.ContentType;\n to: string;\n title: string;\n status: Status;\n};\n\nexport type FolderNode = {\n type: 'folder';\n id: string;\n name: string;\n status: GroupStatus;\n depth: number;\n parentId: string | null;\n children: FolderTreeNode[];\n};\n\nexport type ContentTypeNode = {\n type: 'contentType';\n uid: UID.ContentType;\n to: string;\n title: string;\n status: Status;\n depth: number;\n parentId: string | null;\n};\n\nexport type FolderTreeNode = FolderNode | ContentTypeNode;\n\nexport function buildSectionTree(\n section: ContentStructureSection,\n links: ContentTypeLink[],\n compareTitle: (a: string, b: string) => number\n): FolderTreeNode[] {\n const groups = section.groups;\n\n const groupById = new Map(groups.map((group) => [group.id, group]));\n const linkByUid = new Map(links.map((link) => [link.uid, link]));\n\n const childGroupIds = new Set<string>();\n\n for (const group of groups) {\n for (const child of group.children) {\n if (child.type === 'group') {\n childGroupIds.add(child.id);\n }\n }\n }\n\n const grouped = new Set<UID.ContentType>();\n\n const buildFolder = (\n group: ContentStructureSection['groups'][number],\n depth: number\n ): FolderNode => {\n const children: FolderTreeNode[] = [];\n\n for (const child of group.children) {\n if (child.type === 'group') {\n const subGroup = groupById.get(child.id);\n\n if (!subGroup) {\n continue;\n }\n\n children.push(buildFolder(subGroup, depth + 1));\n continue;\n }\n\n const link = linkByUid.get(child.uid);\n\n if (!link) {\n continue;\n }\n\n grouped.add(child.uid);\n\n children.push({\n type: 'contentType',\n status: link.status,\n parentId: group.id,\n title: link.title,\n depth: depth + 1,\n uid: link.uid,\n to: link.to,\n });\n }\n\n return {\n type: 'folder',\n parentId: group.parent,\n status: group.status,\n name: group.name,\n id: group.id,\n children,\n depth,\n };\n };\n\n const rootFolders = groups\n .filter((group) => !childGroupIds.has(group.id))\n .map((group) => buildFolder(group, 0));\n\n const ungrouped = links\n .filter((link) => !grouped.has(link.uid))\n .sort((a, b) => compareTitle(a.title, b.title))\n .map(\n (link): ContentTypeNode => ({\n type: 'contentType',\n status: link.status,\n title: link.title,\n parentId: null,\n uid: link.uid,\n to: link.to,\n depth: 0,\n })\n );\n\n return [...ungrouped, ...rootFolders];\n}\n\nexport function filterTree(\n nodes: FolderTreeNode[],\n matches: (text: string) => boolean\n): FolderTreeNode[] {\n const result: FolderTreeNode[] = [];\n\n for (const node of nodes) {\n if (node.type === 'contentType') {\n if (matches(node.title) || matches(node.uid)) {\n result.push(node);\n }\n\n continue;\n }\n\n if (matches(node.name)) {\n result.push(node);\n continue;\n }\n\n const filteredChildren = filterTree(node.children, matches);\n\n if (filteredChildren.length > 0) {\n result.push({ ...node, children: filteredChildren });\n }\n }\n\n return result;\n}\n\nexport function subtreeFolderHeight(folder: FolderNode): number {\n let deepestChild = 0;\n\n for (const child of folder.children) {\n if (child.type === 'folder') {\n deepestChild = Math.max(deepestChild, subtreeFolderHeight(child));\n }\n }\n\n return 1 + deepestChild;\n}\n\n/**\n * Whether moving `folder` under a target (a folder, or `null` for the section\n * root) would keep the resulting subtree within {@link MAX_FOLDER_DEPTH}.\n */\nexport function canNestFolderAt(folder: FolderNode, target: FolderNode | null): boolean {\n const targetDomainDepth = target ? target.depth + 1 : 0;\n const deepestResult = targetDomainDepth + subtreeFolderHeight(folder);\n\n return deepestResult <= MAX_FOLDER_DEPTH;\n}\n\nexport function countSubtree(\n section: ContentStructureSection,\n folderId: string,\n countableUids: ReadonlySet<UID.ContentType>\n): { contentTypes: number; subfolders: number } {\n const groupById = new Map(section.groups.map((group) => [group.id, group]));\n\n let contentTypes = 0;\n let subfolders = 0;\n\n const walk = (group: ContentStructureSection['groups'][number]) => {\n for (const child of group.children) {\n if (child.type === 'contentType') {\n if (countableUids.has(child.uid)) {\n contentTypes += 1;\n }\n\n continue;\n }\n\n const childGroup = groupById.get(child.id);\n\n if (!childGroup) {\n continue;\n }\n\n subfolders += 1;\n walk(childGroup);\n }\n };\n\n const root = groupById.get(folderId);\n\n if (root) {\n walk(root);\n }\n\n return { contentTypes, subfolders };\n}\n\nexport function collectSubtreeContentTypeUids(\n section: ContentStructureSection,\n folderId: string\n): UID.ContentType[] {\n const groupById = new Map(section.groups.map((group) => [group.id, group]));\n const uids: UID.ContentType[] = [];\n\n const walk = (id: string) => {\n const group = groupById.get(id);\n\n if (!group) {\n return;\n }\n\n for (const child of group.children) {\n if (child.type === 'contentType') {\n uids.push(child.uid);\n continue;\n }\n\n walk(child.id);\n }\n };\n\n walk(folderId);\n\n return uids;\n}\n\nexport function indexFolders(nodes: FolderTreeNode[]): Map<string, FolderNode> {\n const index = new Map<string, FolderNode>();\n\n const walk = (node: FolderTreeNode) => {\n if (node.type !== 'folder') {\n return;\n }\n\n index.set(node.id, node);\n node.children.forEach(walk);\n };\n\n nodes.forEach(walk);\n\n return index;\n}\n"],"names":["buildSectionTree","section","links","compareTitle","groups","groupById","Map","map","group","id","linkByUid","link","uid","childGroupIds","Set","child","children","type","add","grouped","buildFolder","depth","subGroup","get","push","status","parentId","title","to","parent","name","rootFolders","filter","has","ungrouped","sort","a","b","filterTree","nodes","matches","result","node","filteredChildren","length","subtreeFolderHeight","folder","deepestChild","Math","max","canNestFolderAt","target","targetDomainDepth","deepestResult","MAX_FOLDER_DEPTH","countSubtree","folderId","countableUids","contentTypes","subfolders","walk","childGroup","root","collectSubtreeContentTypeUids","uids"],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { MAX_FOLDER_DEPTH } from '../../DataManager/utils/contentStructure.mjs';
|
|
2
|
+
|
|
3
|
+
function buildSectionTree(section, links, compareTitle) {
|
|
4
|
+
const groups = section.groups;
|
|
5
|
+
const groupById = new Map(groups.map((group)=>[
|
|
6
|
+
group.id,
|
|
7
|
+
group
|
|
8
|
+
]));
|
|
9
|
+
const linkByUid = new Map(links.map((link)=>[
|
|
10
|
+
link.uid,
|
|
11
|
+
link
|
|
12
|
+
]));
|
|
13
|
+
const childGroupIds = new Set();
|
|
14
|
+
for (const group of groups){
|
|
15
|
+
for (const child of group.children){
|
|
16
|
+
if (child.type === 'group') {
|
|
17
|
+
childGroupIds.add(child.id);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const grouped = new Set();
|
|
22
|
+
const buildFolder = (group, depth)=>{
|
|
23
|
+
const children = [];
|
|
24
|
+
for (const child of group.children){
|
|
25
|
+
if (child.type === 'group') {
|
|
26
|
+
const subGroup = groupById.get(child.id);
|
|
27
|
+
if (!subGroup) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
children.push(buildFolder(subGroup, depth + 1));
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const link = linkByUid.get(child.uid);
|
|
34
|
+
if (!link) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
grouped.add(child.uid);
|
|
38
|
+
children.push({
|
|
39
|
+
type: 'contentType',
|
|
40
|
+
status: link.status,
|
|
41
|
+
parentId: group.id,
|
|
42
|
+
title: link.title,
|
|
43
|
+
depth: depth + 1,
|
|
44
|
+
uid: link.uid,
|
|
45
|
+
to: link.to
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
type: 'folder',
|
|
50
|
+
parentId: group.parent,
|
|
51
|
+
status: group.status,
|
|
52
|
+
name: group.name,
|
|
53
|
+
id: group.id,
|
|
54
|
+
children,
|
|
55
|
+
depth
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const rootFolders = groups.filter((group)=>!childGroupIds.has(group.id)).map((group)=>buildFolder(group, 0));
|
|
59
|
+
const ungrouped = links.filter((link)=>!grouped.has(link.uid)).sort((a, b)=>compareTitle(a.title, b.title)).map((link)=>({
|
|
60
|
+
type: 'contentType',
|
|
61
|
+
status: link.status,
|
|
62
|
+
title: link.title,
|
|
63
|
+
parentId: null,
|
|
64
|
+
uid: link.uid,
|
|
65
|
+
to: link.to,
|
|
66
|
+
depth: 0
|
|
67
|
+
}));
|
|
68
|
+
return [
|
|
69
|
+
...ungrouped,
|
|
70
|
+
...rootFolders
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
function filterTree(nodes, matches) {
|
|
74
|
+
const result = [];
|
|
75
|
+
for (const node of nodes){
|
|
76
|
+
if (node.type === 'contentType') {
|
|
77
|
+
if (matches(node.title) || matches(node.uid)) {
|
|
78
|
+
result.push(node);
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (matches(node.name)) {
|
|
83
|
+
result.push(node);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const filteredChildren = filterTree(node.children, matches);
|
|
87
|
+
if (filteredChildren.length > 0) {
|
|
88
|
+
result.push({
|
|
89
|
+
...node,
|
|
90
|
+
children: filteredChildren
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
function subtreeFolderHeight(folder) {
|
|
97
|
+
let deepestChild = 0;
|
|
98
|
+
for (const child of folder.children){
|
|
99
|
+
if (child.type === 'folder') {
|
|
100
|
+
deepestChild = Math.max(deepestChild, subtreeFolderHeight(child));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return 1 + deepestChild;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Whether moving `folder` under a target (a folder, or `null` for the section
|
|
107
|
+
* root) would keep the resulting subtree within {@link MAX_FOLDER_DEPTH}.
|
|
108
|
+
*/ function canNestFolderAt(folder, target) {
|
|
109
|
+
const targetDomainDepth = target ? target.depth + 1 : 0;
|
|
110
|
+
const deepestResult = targetDomainDepth + subtreeFolderHeight(folder);
|
|
111
|
+
return deepestResult <= MAX_FOLDER_DEPTH;
|
|
112
|
+
}
|
|
113
|
+
function countSubtree(section, folderId, countableUids) {
|
|
114
|
+
const groupById = new Map(section.groups.map((group)=>[
|
|
115
|
+
group.id,
|
|
116
|
+
group
|
|
117
|
+
]));
|
|
118
|
+
let contentTypes = 0;
|
|
119
|
+
let subfolders = 0;
|
|
120
|
+
const walk = (group)=>{
|
|
121
|
+
for (const child of group.children){
|
|
122
|
+
if (child.type === 'contentType') {
|
|
123
|
+
if (countableUids.has(child.uid)) {
|
|
124
|
+
contentTypes += 1;
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const childGroup = groupById.get(child.id);
|
|
129
|
+
if (!childGroup) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
subfolders += 1;
|
|
133
|
+
walk(childGroup);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
const root = groupById.get(folderId);
|
|
137
|
+
if (root) {
|
|
138
|
+
walk(root);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
contentTypes,
|
|
142
|
+
subfolders
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function collectSubtreeContentTypeUids(section, folderId) {
|
|
146
|
+
const groupById = new Map(section.groups.map((group)=>[
|
|
147
|
+
group.id,
|
|
148
|
+
group
|
|
149
|
+
]));
|
|
150
|
+
const uids = [];
|
|
151
|
+
const walk = (id)=>{
|
|
152
|
+
const group = groupById.get(id);
|
|
153
|
+
if (!group) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
for (const child of group.children){
|
|
157
|
+
if (child.type === 'contentType') {
|
|
158
|
+
uids.push(child.uid);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
walk(child.id);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
walk(folderId);
|
|
165
|
+
return uids;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export { buildSectionTree, canNestFolderAt, collectSubtreeContentTypeUids, countSubtree, filterTree, subtreeFolderHeight };
|
|
169
|
+
//# sourceMappingURL=buildFolderTree.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildFolderTree.mjs","sources":["../../../../../admin/src/components/ContentTypeBuilderNav/lib/buildFolderTree.ts"],"sourcesContent":["import { MAX_FOLDER_DEPTH } from '../../DataManager/utils/contentStructure';\n\nimport type { Status } from '../../../types';\nimport type {\n ContentStructureSection,\n GroupStatus,\n} from '../../DataManager/utils/contentStructure';\nimport type { UID } from '@strapi/types';\n\nexport type ContentTypeLink = {\n uid: UID.ContentType;\n to: string;\n title: string;\n status: Status;\n};\n\nexport type FolderNode = {\n type: 'folder';\n id: string;\n name: string;\n status: GroupStatus;\n depth: number;\n parentId: string | null;\n children: FolderTreeNode[];\n};\n\nexport type ContentTypeNode = {\n type: 'contentType';\n uid: UID.ContentType;\n to: string;\n title: string;\n status: Status;\n depth: number;\n parentId: string | null;\n};\n\nexport type FolderTreeNode = FolderNode | ContentTypeNode;\n\nexport function buildSectionTree(\n section: ContentStructureSection,\n links: ContentTypeLink[],\n compareTitle: (a: string, b: string) => number\n): FolderTreeNode[] {\n const groups = section.groups;\n\n const groupById = new Map(groups.map((group) => [group.id, group]));\n const linkByUid = new Map(links.map((link) => [link.uid, link]));\n\n const childGroupIds = new Set<string>();\n\n for (const group of groups) {\n for (const child of group.children) {\n if (child.type === 'group') {\n childGroupIds.add(child.id);\n }\n }\n }\n\n const grouped = new Set<UID.ContentType>();\n\n const buildFolder = (\n group: ContentStructureSection['groups'][number],\n depth: number\n ): FolderNode => {\n const children: FolderTreeNode[] = [];\n\n for (const child of group.children) {\n if (child.type === 'group') {\n const subGroup = groupById.get(child.id);\n\n if (!subGroup) {\n continue;\n }\n\n children.push(buildFolder(subGroup, depth + 1));\n continue;\n }\n\n const link = linkByUid.get(child.uid);\n\n if (!link) {\n continue;\n }\n\n grouped.add(child.uid);\n\n children.push({\n type: 'contentType',\n status: link.status,\n parentId: group.id,\n title: link.title,\n depth: depth + 1,\n uid: link.uid,\n to: link.to,\n });\n }\n\n return {\n type: 'folder',\n parentId: group.parent,\n status: group.status,\n name: group.name,\n id: group.id,\n children,\n depth,\n };\n };\n\n const rootFolders = groups\n .filter((group) => !childGroupIds.has(group.id))\n .map((group) => buildFolder(group, 0));\n\n const ungrouped = links\n .filter((link) => !grouped.has(link.uid))\n .sort((a, b) => compareTitle(a.title, b.title))\n .map(\n (link): ContentTypeNode => ({\n type: 'contentType',\n status: link.status,\n title: link.title,\n parentId: null,\n uid: link.uid,\n to: link.to,\n depth: 0,\n })\n );\n\n return [...ungrouped, ...rootFolders];\n}\n\nexport function filterTree(\n nodes: FolderTreeNode[],\n matches: (text: string) => boolean\n): FolderTreeNode[] {\n const result: FolderTreeNode[] = [];\n\n for (const node of nodes) {\n if (node.type === 'contentType') {\n if (matches(node.title) || matches(node.uid)) {\n result.push(node);\n }\n\n continue;\n }\n\n if (matches(node.name)) {\n result.push(node);\n continue;\n }\n\n const filteredChildren = filterTree(node.children, matches);\n\n if (filteredChildren.length > 0) {\n result.push({ ...node, children: filteredChildren });\n }\n }\n\n return result;\n}\n\nexport function subtreeFolderHeight(folder: FolderNode): number {\n let deepestChild = 0;\n\n for (const child of folder.children) {\n if (child.type === 'folder') {\n deepestChild = Math.max(deepestChild, subtreeFolderHeight(child));\n }\n }\n\n return 1 + deepestChild;\n}\n\n/**\n * Whether moving `folder` under a target (a folder, or `null` for the section\n * root) would keep the resulting subtree within {@link MAX_FOLDER_DEPTH}.\n */\nexport function canNestFolderAt(folder: FolderNode, target: FolderNode | null): boolean {\n const targetDomainDepth = target ? target.depth + 1 : 0;\n const deepestResult = targetDomainDepth + subtreeFolderHeight(folder);\n\n return deepestResult <= MAX_FOLDER_DEPTH;\n}\n\nexport function countSubtree(\n section: ContentStructureSection,\n folderId: string,\n countableUids: ReadonlySet<UID.ContentType>\n): { contentTypes: number; subfolders: number } {\n const groupById = new Map(section.groups.map((group) => [group.id, group]));\n\n let contentTypes = 0;\n let subfolders = 0;\n\n const walk = (group: ContentStructureSection['groups'][number]) => {\n for (const child of group.children) {\n if (child.type === 'contentType') {\n if (countableUids.has(child.uid)) {\n contentTypes += 1;\n }\n\n continue;\n }\n\n const childGroup = groupById.get(child.id);\n\n if (!childGroup) {\n continue;\n }\n\n subfolders += 1;\n walk(childGroup);\n }\n };\n\n const root = groupById.get(folderId);\n\n if (root) {\n walk(root);\n }\n\n return { contentTypes, subfolders };\n}\n\nexport function collectSubtreeContentTypeUids(\n section: ContentStructureSection,\n folderId: string\n): UID.ContentType[] {\n const groupById = new Map(section.groups.map((group) => [group.id, group]));\n const uids: UID.ContentType[] = [];\n\n const walk = (id: string) => {\n const group = groupById.get(id);\n\n if (!group) {\n return;\n }\n\n for (const child of group.children) {\n if (child.type === 'contentType') {\n uids.push(child.uid);\n continue;\n }\n\n walk(child.id);\n }\n };\n\n walk(folderId);\n\n return uids;\n}\n\nexport function indexFolders(nodes: FolderTreeNode[]): Map<string, FolderNode> {\n const index = new Map<string, FolderNode>();\n\n const walk = (node: FolderTreeNode) => {\n if (node.type !== 'folder') {\n return;\n }\n\n index.set(node.id, node);\n node.children.forEach(walk);\n };\n\n nodes.forEach(walk);\n\n return index;\n}\n"],"names":["buildSectionTree","section","links","compareTitle","groups","groupById","Map","map","group","id","linkByUid","link","uid","childGroupIds","Set","child","children","type","add","grouped","buildFolder","depth","subGroup","get","push","status","parentId","title","to","parent","name","rootFolders","filter","has","ungrouped","sort","a","b","filterTree","nodes","matches","result","node","filteredChildren","length","subtreeFolderHeight","folder","deepestChild","Math","max","canNestFolderAt","target","targetDomainDepth","deepestResult","MAX_FOLDER_DEPTH","countSubtree","folderId","countableUids","contentTypes","subfolders","walk","childGroup","root","collectSubtreeContentTypeUids","uids"],"mappings":"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"}
|