@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
|
@@ -6,6 +6,7 @@ var applyPrivateSearchDefault = require('../../utils/applyPrivateSearchDefault.j
|
|
|
6
6
|
var getRelationType = require('../../utils/getRelationType.js');
|
|
7
7
|
var makeUnique = require('../../utils/makeUnique.js');
|
|
8
8
|
var undoRedo = require('./undoRedo.js');
|
|
9
|
+
var contentStructure = require('./utils/contentStructure.js');
|
|
9
10
|
|
|
10
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
12
|
|
|
@@ -17,6 +18,8 @@ const initialState = {
|
|
|
17
18
|
contentTypes: {},
|
|
18
19
|
initialComponents: {},
|
|
19
20
|
initialContentTypes: {},
|
|
21
|
+
contentStructure: contentStructure.createEmptyContentStructure(),
|
|
22
|
+
initialContentStructure: contentStructure.createEmptyContentStructure(),
|
|
20
23
|
reservedNames: {
|
|
21
24
|
models: [],
|
|
22
25
|
attributes: []
|
|
@@ -62,6 +65,12 @@ const getNewStatus = (oldStatus, newStatus)=>{
|
|
|
62
65
|
}
|
|
63
66
|
return newStatus;
|
|
64
67
|
};
|
|
68
|
+
const getNewGroupStatus = (oldStatus, newStatus)=>{
|
|
69
|
+
if (oldStatus === 'NEW') {
|
|
70
|
+
return oldStatus;
|
|
71
|
+
}
|
|
72
|
+
return newStatus;
|
|
73
|
+
};
|
|
65
74
|
const setAttributeStatus = (attribute, status)=>{
|
|
66
75
|
attribute.status = getNewStatus(attribute.status, status);
|
|
67
76
|
};
|
|
@@ -99,6 +108,9 @@ const replaceAttributeAt = (type, index, attribute)=>{
|
|
|
99
108
|
};
|
|
100
109
|
const removeAttributeByName = (type, name)=>{
|
|
101
110
|
const idx = type.attributes.findIndex((attr)=>attr.name === name);
|
|
111
|
+
if (idx === -1) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
102
114
|
const attr = type.attributes[idx];
|
|
103
115
|
setStatus(type, 'CHANGED');
|
|
104
116
|
if (attr.status === 'NEW') {
|
|
@@ -111,17 +123,156 @@ const updateType = (type, data)=>{
|
|
|
111
123
|
merge__default.default(type, data);
|
|
112
124
|
setStatus(type, 'CHANGED');
|
|
113
125
|
};
|
|
126
|
+
const findGroup = (groups, id)=>{
|
|
127
|
+
return groups.find((group)=>group.id === id);
|
|
128
|
+
};
|
|
129
|
+
const setGroupStatus = (group, status)=>{
|
|
130
|
+
group.status = getNewGroupStatus(group.status, status);
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Remove a content type reference from every group of a section, mark the group as changed if a reference was removed.
|
|
134
|
+
* @returns true if a reference was removed, false otherwise.
|
|
135
|
+
*/ const removeContentTypeChild = (groups, uid)=>{
|
|
136
|
+
let removed = false;
|
|
137
|
+
for (const group of groups){
|
|
138
|
+
const groupIndex = group.children.findIndex((child)=>{
|
|
139
|
+
return child.type === 'contentType' && child.uid === uid;
|
|
140
|
+
});
|
|
141
|
+
if (groupIndex !== -1) {
|
|
142
|
+
group.children.splice(groupIndex, 1);
|
|
143
|
+
setGroupStatus(group, 'CHANGED');
|
|
144
|
+
removed = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return removed;
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Assign a content type to a folder within a single section, optionally creating
|
|
151
|
+
* a new root folder first.
|
|
152
|
+
*
|
|
153
|
+
* Detaches the content type from any current folder then files it under the target group.
|
|
154
|
+
* This is also used by the create/edit content-type modal's folder picker via
|
|
155
|
+
* createSchema/updateSchema.
|
|
156
|
+
*/ const applyFolderAssignment = (section, uid, folder)=>{
|
|
157
|
+
const { groups } = section;
|
|
158
|
+
removeContentTypeChild(groups, uid);
|
|
159
|
+
let targetGroupId;
|
|
160
|
+
if ('newFolderId' in folder) {
|
|
161
|
+
// Reuse an existing root folder of the same name instead of creating a
|
|
162
|
+
// duplicate. The modal picker can only ever makes root folders, so
|
|
163
|
+
// this makes the assumption that a same-named root folder means the
|
|
164
|
+
// user wants to insert into that folder.
|
|
165
|
+
const existing = contentStructure.findSiblingFolderByName(groups, null, folder.newFolderName);
|
|
166
|
+
if (existing) {
|
|
167
|
+
targetGroupId = existing.id;
|
|
168
|
+
} else {
|
|
169
|
+
groups.push({
|
|
170
|
+
name: folder.newFolderName.trim(),
|
|
171
|
+
id: folder.newFolderId,
|
|
172
|
+
parent: null,
|
|
173
|
+
children: [],
|
|
174
|
+
status: 'NEW'
|
|
175
|
+
});
|
|
176
|
+
targetGroupId = folder.newFolderId;
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
targetGroupId = folder.targetGroupId;
|
|
180
|
+
}
|
|
181
|
+
if (!targetGroupId) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const targetGroup = findGroup(groups, targetGroupId);
|
|
185
|
+
if (targetGroup) {
|
|
186
|
+
targetGroup.children.push({
|
|
187
|
+
type: 'contentType',
|
|
188
|
+
uid
|
|
189
|
+
});
|
|
190
|
+
setGroupStatus(targetGroup, 'CHANGED');
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Determines whether `potentialDescendantId` is nested somewhere inside `ancestorId`'s subtree
|
|
195
|
+
*/ const isDescendantGroup = (groups, ancestorId, potentialDescendantId)=>{
|
|
196
|
+
let current = findGroup(groups, potentialDescendantId);
|
|
197
|
+
while(current && current.parent !== null){
|
|
198
|
+
if (current.parent === ancestorId) {
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
current = findGroup(groups, current.parent);
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
};
|
|
205
|
+
const getGroupDepth = (groups, id)=>{
|
|
206
|
+
let depth = 1;
|
|
207
|
+
let current = findGroup(groups, id);
|
|
208
|
+
while(current && current.parent !== null){
|
|
209
|
+
current = findGroup(groups, current.parent);
|
|
210
|
+
depth += 1;
|
|
211
|
+
}
|
|
212
|
+
return depth;
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* Number of folder levels below a group e.g. a group with no sub-folders has height 0.
|
|
216
|
+
*/ const getSubtreeHeight = (groups, id)=>{
|
|
217
|
+
const group = findGroup(groups, id);
|
|
218
|
+
if (!group) {
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
221
|
+
let height = 0;
|
|
222
|
+
for (const child of group.children){
|
|
223
|
+
if (child.type !== 'group') continue;
|
|
224
|
+
height = Math.max(height, 1 + getSubtreeHeight(groups, child.id));
|
|
225
|
+
}
|
|
226
|
+
return height;
|
|
227
|
+
};
|
|
228
|
+
const applyDeleteContentType = (state, uid)=>{
|
|
229
|
+
const type = state.contentTypes[uid];
|
|
230
|
+
// Only application schemas belong to CTB. Folder actions can be crafted outside the UI,
|
|
231
|
+
// so this guard must live at the mutation boundary rather than in the dialog alone.
|
|
232
|
+
if (!type || !type.uid.startsWith('api::') || type.plugin) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
// just drop new content types
|
|
236
|
+
if (type.status === 'NEW') {
|
|
237
|
+
delete state.contentTypes[uid];
|
|
238
|
+
} else {
|
|
239
|
+
setStatus(type, 'REMOVED');
|
|
240
|
+
}
|
|
241
|
+
// remove the content type from the content structure tree
|
|
242
|
+
Object.values(state.contentStructure.sections).forEach((section)=>{
|
|
243
|
+
removeContentTypeChild(section.groups, uid);
|
|
244
|
+
});
|
|
245
|
+
// remove the content type from the components
|
|
246
|
+
Object.keys(state.components).forEach((componentUid)=>{
|
|
247
|
+
const component = state.components[componentUid];
|
|
248
|
+
const namesToRemove = component.attributes.filter((attribute)=>attribute.type === 'relation' && attribute.target === uid).map((attribute)=>attribute.name);
|
|
249
|
+
namesToRemove.forEach((name)=>{
|
|
250
|
+
removeAttributeByName(component, name);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
// remove the content type from the content types
|
|
254
|
+
Object.keys(state.contentTypes).forEach((contentTypeUid)=>{
|
|
255
|
+
const contentType = state.contentTypes[contentTypeUid];
|
|
256
|
+
const namesToRemove = contentType.attributes.filter((attribute)=>attribute.type === 'relation' && attribute.target === uid).map((attribute)=>attribute.name);
|
|
257
|
+
namesToRemove.forEach((name)=>{
|
|
258
|
+
removeAttributeByName(contentType, name);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
};
|
|
114
262
|
const slice = undoRedo.createUndoRedoSlice({
|
|
115
263
|
name: 'data-manager',
|
|
116
264
|
initialState,
|
|
117
265
|
reducers: {
|
|
118
266
|
init: (state, action)=>{
|
|
119
|
-
const { components, contentTypes, reservedNames } = action.payload;
|
|
267
|
+
const { components, contentTypes, reservedNames, contentStructure: contentStructure$1 } = action.payload;
|
|
120
268
|
state.components = components;
|
|
121
269
|
state.initialComponents = components;
|
|
122
270
|
state.initialContentTypes = contentTypes;
|
|
123
271
|
state.contentTypes = contentTypes;
|
|
124
272
|
state.reservedNames = reservedNames;
|
|
273
|
+
const structure = contentStructure$1 ?? contentStructure.createEmptyContentStructure();
|
|
274
|
+
state.initialContentStructure = structure;
|
|
275
|
+
state.contentStructure = structure;
|
|
125
276
|
state.isLoading = false;
|
|
126
277
|
},
|
|
127
278
|
createComponentSchema: (state, action)=>{
|
|
@@ -142,7 +293,7 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
142
293
|
state.components[uid] = newSchema;
|
|
143
294
|
},
|
|
144
295
|
createSchema: (state, action)=>{
|
|
145
|
-
const { uid, data } = action.payload;
|
|
296
|
+
const { uid, data, folder } = action.payload;
|
|
146
297
|
const { displayName, singularName, pluralName, kind, draftAndPublish, pluginOptions } = data;
|
|
147
298
|
const newSchema = {
|
|
148
299
|
uid: uid,
|
|
@@ -165,6 +316,11 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
165
316
|
pluginOptions
|
|
166
317
|
};
|
|
167
318
|
state.contentTypes[uid] = newSchema;
|
|
319
|
+
// File the new content type into a folder in the same transaction it is created,
|
|
320
|
+
// so both are subject to the same undo/redo operatiion.
|
|
321
|
+
if (folder) {
|
|
322
|
+
applyFolderAssignment(state.contentStructure.sections[contentStructure.sectionKeyForKind(kind)], uid, folder);
|
|
323
|
+
}
|
|
168
324
|
},
|
|
169
325
|
addAttribute: (state, action)=>{
|
|
170
326
|
const { attributeToSet, forTarget, targetUid } = action.payload;
|
|
@@ -426,12 +582,13 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
426
582
|
}
|
|
427
583
|
},
|
|
428
584
|
updateSchema: (state, action)=>{
|
|
429
|
-
const { data, uid } = action.payload;
|
|
585
|
+
const { data, uid, folder } = action.payload;
|
|
430
586
|
const { displayName, kind, draftAndPublish, pluginOptions } = data;
|
|
431
587
|
const type = state.contentTypes[uid];
|
|
432
588
|
if (!type) {
|
|
433
589
|
return;
|
|
434
590
|
}
|
|
591
|
+
const previousKind = type.kind;
|
|
435
592
|
updateType(type, {
|
|
436
593
|
info: {
|
|
437
594
|
displayName
|
|
@@ -442,6 +599,15 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
442
599
|
},
|
|
443
600
|
pluginOptions
|
|
444
601
|
});
|
|
602
|
+
if (kind && kind !== previousKind) {
|
|
603
|
+
const oldSection = state.contentStructure.sections[contentStructure.sectionKeyForKind(previousKind)];
|
|
604
|
+
removeContentTypeChild(oldSection.groups, uid);
|
|
605
|
+
}
|
|
606
|
+
// Apply the folder picker's choice after any kind relocation, so the
|
|
607
|
+
// assignment lands in the content type's (possibly new) section.
|
|
608
|
+
if (folder) {
|
|
609
|
+
applyFolderAssignment(state.contentStructure.sections[contentStructure.sectionKeyForKind(kind)], uid, folder);
|
|
610
|
+
}
|
|
445
611
|
},
|
|
446
612
|
deleteComponent: (state, action)=>{
|
|
447
613
|
const uid = action.payload;
|
|
@@ -461,49 +627,251 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
461
627
|
attribute.components = newComponents;
|
|
462
628
|
}
|
|
463
629
|
});
|
|
464
|
-
contentType.attributes.
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
}
|
|
630
|
+
const namesToRemove = contentType.attributes.filter((attribute)=>attribute.type === 'component' && attribute.component === uid).map((attribute)=>attribute.name);
|
|
631
|
+
namesToRemove.forEach((name)=>{
|
|
632
|
+
removeAttributeByName(contentType, name);
|
|
468
633
|
});
|
|
469
634
|
});
|
|
470
635
|
// remove the compo from other components
|
|
471
636
|
Object.keys(state.components).forEach((componentUid)=>{
|
|
472
637
|
const component = state.components[componentUid];
|
|
473
|
-
component.attributes.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}
|
|
638
|
+
const namesToRemove = component.attributes.filter((attribute)=>attribute.type === 'component' && attribute.component === uid).map((attribute)=>attribute.name);
|
|
639
|
+
namesToRemove.forEach((name)=>{
|
|
640
|
+
removeAttributeByName(component, name);
|
|
477
641
|
});
|
|
478
642
|
});
|
|
479
643
|
},
|
|
480
644
|
deleteContentType: (state, action)=>{
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
|
|
645
|
+
applyDeleteContentType(state, action.payload);
|
|
646
|
+
},
|
|
647
|
+
createFolder: (state, action)=>{
|
|
648
|
+
const { section, id, name: rawName, parentId } = action.payload;
|
|
649
|
+
const name = rawName.trim();
|
|
650
|
+
const { groups } = state.contentStructure.sections[section];
|
|
651
|
+
if (parentId && getGroupDepth(groups, parentId) + 1 > contentStructure.MAX_FOLDER_DEPTH) {
|
|
652
|
+
return;
|
|
488
653
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
654
|
+
if (contentStructure.isFolderNameTakenBySibling(groups, parentId, name)) {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
groups.push({
|
|
658
|
+
id,
|
|
659
|
+
name,
|
|
660
|
+
parent: parentId,
|
|
661
|
+
children: [],
|
|
662
|
+
status: 'NEW'
|
|
663
|
+
});
|
|
664
|
+
if (parentId) {
|
|
665
|
+
const parent = findGroup(groups, parentId);
|
|
666
|
+
if (parent) {
|
|
667
|
+
parent.children.push({
|
|
668
|
+
type: 'group',
|
|
669
|
+
id
|
|
670
|
+
});
|
|
671
|
+
setGroupStatus(parent, 'CHANGED');
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
renameFolder: (state, action)=>{
|
|
676
|
+
const { section, id, name: rawName } = action.payload;
|
|
677
|
+
const name = rawName.trim();
|
|
678
|
+
const { groups } = state.contentStructure.sections[section];
|
|
679
|
+
const group = findGroup(groups, id);
|
|
680
|
+
if (!group) {
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
if (group.name === name) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
if (contentStructure.isFolderNameTakenBySibling(groups, group.parent, name, id)) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
group.name = name;
|
|
690
|
+
setGroupStatus(group, 'CHANGED');
|
|
691
|
+
},
|
|
692
|
+
moveFolder: (state, action)=>{
|
|
693
|
+
const { section, id, newParentId, index } = action.payload;
|
|
694
|
+
const { groups } = state.contentStructure.sections[section];
|
|
695
|
+
const group = findGroup(groups, id);
|
|
696
|
+
if (!group) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
// Reject no-ops and cyclic moves
|
|
700
|
+
if (newParentId === id || newParentId && isDescendantGroup(groups, id, newParentId)) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
// Refuse moves that would exceed the maximum allowed depth.
|
|
704
|
+
const newTopDepth = newParentId ? getGroupDepth(groups, newParentId) + 1 : 1;
|
|
705
|
+
if (newTopDepth + getSubtreeHeight(groups, id) > contentStructure.MAX_FOLDER_DEPTH) {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
// Refuse a move that would collide with an identically named sibling.
|
|
709
|
+
if (contentStructure.isFolderNameTakenBySibling(groups, newParentId, group.name, id)) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (group.parent) {
|
|
713
|
+
const oldParent = findGroup(groups, group.parent);
|
|
714
|
+
if (oldParent) {
|
|
715
|
+
// Remove from the old parent's list of children.
|
|
716
|
+
const idx = oldParent.children.findIndex((child)=>{
|
|
717
|
+
return child.type === 'group' && child.id === id;
|
|
718
|
+
});
|
|
719
|
+
if (idx !== -1) {
|
|
720
|
+
oldParent.children.splice(idx, 1);
|
|
495
721
|
}
|
|
496
|
-
|
|
722
|
+
setGroupStatus(oldParent, 'CHANGED');
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
group.parent = newParentId;
|
|
726
|
+
setGroupStatus(group, 'CHANGED');
|
|
727
|
+
if (newParentId) {
|
|
728
|
+
const newParent = findGroup(groups, newParentId);
|
|
729
|
+
if (newParent) {
|
|
730
|
+
const entry = {
|
|
731
|
+
type: 'group',
|
|
732
|
+
id
|
|
733
|
+
};
|
|
734
|
+
if (typeof index === 'number' && index >= 0 && index <= newParent.children.length) {
|
|
735
|
+
newParent.children.splice(index, 0, entry);
|
|
736
|
+
} else {
|
|
737
|
+
newParent.children.push(entry);
|
|
738
|
+
}
|
|
739
|
+
setGroupStatus(newParent, 'CHANGED');
|
|
740
|
+
}
|
|
741
|
+
} else if (typeof index === 'number') {
|
|
742
|
+
// If a content type is dropped into the root:
|
|
743
|
+
// The root display order follows the array order of the
|
|
744
|
+
// section's root-level groups because there's no root level
|
|
745
|
+
// customization yet, so reposition the moved group among them.
|
|
746
|
+
const currentIndex = groups.findIndex((candidate)=>candidate.id === id);
|
|
747
|
+
if (currentIndex !== -1) {
|
|
748
|
+
const [moved] = groups.splice(currentIndex, 1);
|
|
749
|
+
const rootIds = groups.filter((candidate)=>candidate.parent === null).map((candidate)=>candidate.id);
|
|
750
|
+
const anchorId = rootIds[index];
|
|
751
|
+
const insertAt = anchorId ? groups.findIndex((candidate)=>candidate.id === anchorId) : groups.length;
|
|
752
|
+
groups.splice(insertAt === -1 ? groups.length : insertAt, 0, moved);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
},
|
|
756
|
+
deleteFolderOnly: (state, action)=>{
|
|
757
|
+
const { section, id: groupToDeleteId } = action.payload;
|
|
758
|
+
const { groups } = state.contentStructure.sections[section];
|
|
759
|
+
const groupToDelete = findGroup(groups, groupToDeleteId);
|
|
760
|
+
if (!groupToDelete) {
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
const groupToDeleteParentId = groupToDelete.parent;
|
|
764
|
+
// Reparenting a child folder into its grandparent must preserve the same sibling-name
|
|
765
|
+
// invariant as create, rename, and move. Do this before mutating any group state.
|
|
766
|
+
if (contentStructure.getDeleteFolderOnlySiblingNameCollisions(groups, groupToDeleteId).length > 0) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
// It is necessary to clone the children because we must subsequently mutate the original group.children array when we splice it out of its parent.
|
|
770
|
+
const children = groupToDelete.children.map((child)=>({
|
|
771
|
+
...child
|
|
772
|
+
}));
|
|
773
|
+
// Children of a deleted folder inherit the deleted folder's parent.
|
|
774
|
+
children.forEach((child)=>{
|
|
775
|
+
if (child.type === 'group') {
|
|
776
|
+
const childGroup = findGroup(groups, child.id);
|
|
777
|
+
if (childGroup) {
|
|
778
|
+
childGroup.parent = groupToDeleteParentId;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
497
781
|
});
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
782
|
+
if (groupToDeleteParentId) {
|
|
783
|
+
const groupToDeleteParent = findGroup(groups, groupToDeleteParentId);
|
|
784
|
+
if (groupToDeleteParent) {
|
|
785
|
+
// Splice the deleted folder's children of the deleted folder into their new parent at the position of the deleted folder.
|
|
786
|
+
const groupToDeleteIndex = groupToDeleteParent.children.findIndex((child)=>{
|
|
787
|
+
return child.type === 'group' && child.id === groupToDeleteId;
|
|
788
|
+
});
|
|
789
|
+
if (groupToDeleteIndex !== -1) {
|
|
790
|
+
groupToDeleteParent.children.splice(groupToDeleteIndex, 1, ...children);
|
|
791
|
+
} else {
|
|
792
|
+
groupToDeleteParent.children.push(...children);
|
|
504
793
|
}
|
|
505
|
-
|
|
794
|
+
setGroupStatus(groupToDeleteParent, 'CHANGED');
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
// Finally, remove the deleted folder from its section's list of groups.
|
|
798
|
+
const groupIndex = groups.findIndex((candidate)=>{
|
|
799
|
+
return candidate.id === groupToDeleteId;
|
|
506
800
|
});
|
|
801
|
+
if (groupIndex !== -1) {
|
|
802
|
+
groups.splice(groupIndex, 1);
|
|
803
|
+
}
|
|
804
|
+
},
|
|
805
|
+
deleteFolderAndContent: (state, action)=>{
|
|
806
|
+
const { section, id: groupToDeleteId, contentTypeUids } = action.payload;
|
|
807
|
+
contentTypeUids.forEach((uid)=>{
|
|
808
|
+
applyDeleteContentType(state, uid);
|
|
809
|
+
});
|
|
810
|
+
const { groups } = state.contentStructure.sections[section];
|
|
811
|
+
const groupToDelete = findGroup(groups, groupToDeleteId);
|
|
812
|
+
if (!groupToDelete) {
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
const toRemove = new Set();
|
|
816
|
+
const collect = (currentGroupId)=>{
|
|
817
|
+
toRemove.add(currentGroupId);
|
|
818
|
+
const currentGroup = findGroup(groups, currentGroupId);
|
|
819
|
+
for (const child of currentGroup?.children ?? []){
|
|
820
|
+
if (child.type !== 'group') continue;
|
|
821
|
+
collect(child.id);
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
collect(groupToDeleteId);
|
|
825
|
+
if (groupToDelete.parent) {
|
|
826
|
+
const groupToDeleteParent = findGroup(groups, groupToDelete.parent);
|
|
827
|
+
if (groupToDeleteParent) {
|
|
828
|
+
const groupToDeleteIndex = groupToDeleteParent.children.findIndex((child)=>{
|
|
829
|
+
return child.type === 'group' && child.id === groupToDeleteId;
|
|
830
|
+
});
|
|
831
|
+
if (groupToDeleteIndex !== -1) {
|
|
832
|
+
groupToDeleteParent.children.splice(groupToDeleteIndex, 1);
|
|
833
|
+
}
|
|
834
|
+
setGroupStatus(groupToDeleteParent, 'CHANGED');
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
// Removes the group entry and all its descendants from the contentStructure.
|
|
838
|
+
state.contentStructure.sections[section].groups = groups.filter((candidate)=>{
|
|
839
|
+
return !toRemove.has(candidate.id);
|
|
840
|
+
});
|
|
841
|
+
},
|
|
842
|
+
assignContentTypeToFolder: (state, action)=>{
|
|
843
|
+
const { section, uid, targetGroupId, index } = action.payload;
|
|
844
|
+
const { groups } = state.contentStructure.sections[section];
|
|
845
|
+
removeContentTypeChild(groups, uid);
|
|
846
|
+
if (targetGroupId) {
|
|
847
|
+
const targetGroup = findGroup(groups, targetGroupId);
|
|
848
|
+
if (targetGroup) {
|
|
849
|
+
const entry = {
|
|
850
|
+
type: 'contentType',
|
|
851
|
+
uid
|
|
852
|
+
};
|
|
853
|
+
if (typeof index === 'number' && index >= 0 && index <= targetGroup.children.length) {
|
|
854
|
+
targetGroup.children.splice(index, 0, entry);
|
|
855
|
+
} else {
|
|
856
|
+
targetGroup.children.push(entry);
|
|
857
|
+
}
|
|
858
|
+
setGroupStatus(targetGroup, 'CHANGED');
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
reorderFolderChildren: (state, action)=>{
|
|
863
|
+
const { section, groupId, from, to } = action.payload;
|
|
864
|
+
const group = findGroup(state.contentStructure.sections[section].groups, groupId);
|
|
865
|
+
if (!group) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
const child = group.children[from];
|
|
869
|
+
if (!child) {
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
group.children.splice(from, 1);
|
|
873
|
+
group.children.splice(to, 0, child);
|
|
874
|
+
setGroupStatus(group, 'CHANGED');
|
|
507
875
|
},
|
|
508
876
|
applyChange (state, reducerAction) {
|
|
509
877
|
const { action, schema } = reducerAction.payload;
|
|
@@ -530,10 +898,15 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
530
898
|
};
|
|
531
899
|
} else {
|
|
532
900
|
const contentType = state.contentTypes[uid];
|
|
901
|
+
const previousKind = contentType?.kind;
|
|
533
902
|
state.contentTypes[uid] = {
|
|
534
903
|
...schema,
|
|
535
904
|
status: contentType?.status === 'NEW' ? 'NEW' : schema.status
|
|
536
905
|
};
|
|
906
|
+
if (previousKind && schema.kind && schema.kind !== previousKind) {
|
|
907
|
+
const oldSection = state.contentStructure.sections[contentStructure.sectionKeyForKind(previousKind)];
|
|
908
|
+
removeContentTypeChild(oldSection.groups, uid);
|
|
909
|
+
}
|
|
537
910
|
}
|
|
538
911
|
}
|
|
539
912
|
break;
|
|
@@ -564,6 +937,9 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
564
937
|
} else {
|
|
565
938
|
state.contentTypes[uid].status = 'REMOVED';
|
|
566
939
|
}
|
|
940
|
+
Object.values(state.contentStructure.sections).forEach((section)=>{
|
|
941
|
+
removeContentTypeChild(section.groups, uid);
|
|
942
|
+
});
|
|
567
943
|
}
|
|
568
944
|
break;
|
|
569
945
|
}
|
|
@@ -582,12 +958,14 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
582
958
|
}
|
|
583
959
|
return {
|
|
584
960
|
components: state.components,
|
|
585
|
-
contentTypes: state.contentTypes
|
|
961
|
+
contentTypes: state.contentTypes,
|
|
962
|
+
contentStructure: state.contentStructure
|
|
586
963
|
};
|
|
587
964
|
},
|
|
588
965
|
discard: (state)=>{
|
|
589
966
|
state.components = state.initialComponents;
|
|
590
967
|
state.contentTypes = state.initialContentTypes;
|
|
968
|
+
state.contentStructure = state.initialContentStructure;
|
|
591
969
|
}
|
|
592
970
|
});
|
|
593
971
|
const { reducer, actions } = slice;
|