@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.
Files changed (208) hide show
  1. package/dist/admin/components/AIChat/hooks/useAIFetch.js +6 -2
  2. package/dist/admin/components/AIChat/hooks/useAIFetch.js.map +1 -1
  3. package/dist/admin/components/AIChat/hooks/useAIFetch.mjs +7 -3
  4. package/dist/admin/components/AIChat/hooks/useAIFetch.mjs.map +1 -1
  5. package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.js +20 -0
  6. package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.js.map +1 -0
  7. package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.mjs +18 -0
  8. package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.mjs.map +1 -0
  9. package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.js +83 -59
  10. package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.js.map +1 -1
  11. package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.mjs +86 -62
  12. package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.mjs.map +1 -1
  13. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.js +112 -0
  14. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.js.map +1 -0
  15. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.mjs +110 -0
  16. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.mjs.map +1 -0
  17. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.js +159 -0
  18. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.js.map +1 -0
  19. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.mjs +157 -0
  20. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.mjs.map +1 -0
  21. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.js +311 -0
  22. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.js.map +1 -0
  23. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.mjs +309 -0
  24. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.mjs.map +1 -0
  25. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.js +379 -0
  26. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.js.map +1 -0
  27. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.mjs +376 -0
  28. package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.mjs.map +1 -0
  29. package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.js +172 -0
  30. package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.js.map +1 -0
  31. package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.mjs +166 -0
  32. package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.mjs.map +1 -0
  33. package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.js +62 -0
  34. package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.js.map +1 -0
  35. package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.mjs +60 -0
  36. package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.mjs.map +1 -0
  37. package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.js +145 -0
  38. package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.js.map +1 -0
  39. package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.mjs +143 -0
  40. package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.mjs.map +1 -0
  41. package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.js +176 -0
  42. package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.js.map +1 -0
  43. package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.mjs +169 -0
  44. package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.mjs.map +1 -0
  45. package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.js +277 -0
  46. package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.js.map +1 -0
  47. package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.mjs +273 -0
  48. package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.mjs.map +1 -0
  49. package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.js +62 -0
  50. package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.js.map +1 -0
  51. package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.mjs +58 -0
  52. package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.mjs.map +1 -0
  53. package/dist/admin/components/DataManager/DataManagerContext.js.map +1 -1
  54. package/dist/admin/components/DataManager/DataManagerContext.mjs.map +1 -1
  55. package/dist/admin/components/DataManager/DataManagerProvider.js +70 -8
  56. package/dist/admin/components/DataManager/DataManagerProvider.js.map +1 -1
  57. package/dist/admin/components/DataManager/DataManagerProvider.mjs +70 -8
  58. package/dist/admin/components/DataManager/DataManagerProvider.mjs.map +1 -1
  59. package/dist/admin/components/DataManager/reducer.js +411 -33
  60. package/dist/admin/components/DataManager/reducer.js.map +1 -1
  61. package/dist/admin/components/DataManager/reducer.mjs +411 -33
  62. package/dist/admin/components/DataManager/reducer.mjs.map +1 -1
  63. package/dist/admin/components/DataManager/utils/cleanData.js +11 -2
  64. package/dist/admin/components/DataManager/utils/cleanData.js.map +1 -1
  65. package/dist/admin/components/DataManager/utils/cleanData.mjs +11 -2
  66. package/dist/admin/components/DataManager/utils/cleanData.mjs.map +1 -1
  67. package/dist/admin/components/DataManager/utils/contentStructure.js +150 -0
  68. package/dist/admin/components/DataManager/utils/contentStructure.js.map +1 -0
  69. package/dist/admin/components/DataManager/utils/contentStructure.mjs +133 -0
  70. package/dist/admin/components/DataManager/utils/contentStructure.mjs.map +1 -0
  71. package/dist/admin/components/FolderSelect.js +200 -0
  72. package/dist/admin/components/FolderSelect.js.map +1 -0
  73. package/dist/admin/components/FolderSelect.mjs +198 -0
  74. package/dist/admin/components/FolderSelect.mjs.map +1 -0
  75. package/dist/admin/components/FormModal/FormModal.js +23 -2
  76. package/dist/admin/components/FormModal/FormModal.js.map +1 -1
  77. package/dist/admin/components/FormModal/FormModal.mjs +23 -2
  78. package/dist/admin/components/FormModal/FormModal.mjs.map +1 -1
  79. package/dist/admin/components/FormModal/contentType/contentTypeForm.js +15 -18
  80. package/dist/admin/components/FormModal/contentType/contentTypeForm.js.map +1 -1
  81. package/dist/admin/components/FormModal/contentType/contentTypeForm.mjs +15 -18
  82. package/dist/admin/components/FormModal/contentType/contentTypeForm.mjs.map +1 -1
  83. package/dist/admin/components/FormModal/reducer.js +6 -0
  84. package/dist/admin/components/FormModal/reducer.js.map +1 -1
  85. package/dist/admin/components/FormModal/reducer.mjs +6 -0
  86. package/dist/admin/components/FormModal/reducer.mjs.map +1 -1
  87. package/dist/admin/pages/App/index.js +2 -5
  88. package/dist/admin/pages/App/index.js.map +1 -1
  89. package/dist/admin/pages/App/index.mjs +2 -5
  90. package/dist/admin/pages/App/index.mjs.map +1 -1
  91. package/dist/admin/src/components/AIChat/hooks/usePrefetchAIToken.d.ts +1 -0
  92. package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.d.ts +16 -0
  93. package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.d.ts +12 -0
  94. package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.d.ts +14 -0
  95. package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.d.ts +38 -0
  96. package/dist/admin/src/components/ContentTypeBuilderNav/{useContentTypeBuilderMenu.d.ts → hooks/useContentTypeBuilderMenu.d.ts} +17 -7
  97. package/dist/admin/src/components/ContentTypeBuilderNav/hooks/useFolderActions.d.ts +42 -0
  98. package/dist/admin/src/components/ContentTypeBuilderNav/hooks/useSortableTree.d.ts +53 -0
  99. package/dist/admin/src/components/ContentTypeBuilderNav/lib/buildFolderTree.d.ts +42 -0
  100. package/dist/admin/src/components/ContentTypeBuilderNav/lib/dropProjection.d.ts +58 -0
  101. package/dist/admin/src/components/ContentTypeBuilderNav/lib/flatModel.d.ts +18 -0
  102. package/dist/admin/src/components/DataManager/DataManagerContext.d.ts +41 -0
  103. package/dist/admin/src/components/DataManager/reducer.d.ts +105 -1
  104. package/dist/admin/src/components/DataManager/utils/cleanData.d.ts +5 -1
  105. package/dist/admin/src/components/DataManager/utils/contentStructure.d.ts +68 -0
  106. package/dist/admin/src/components/FolderSelect.d.ts +18 -0
  107. package/dist/admin/src/components/FormModal/contentType/contentTypeForm.d.ts +0 -21
  108. package/dist/admin/src/components/FormModal/forms/forms.d.ts +0 -10
  109. package/dist/admin/src/components/FormModal/reducer.d.ts +7 -0
  110. package/dist/admin/src/utils/folderTelemetry.d.ts +5 -0
  111. package/dist/admin/translations/en.json.js +23 -0
  112. package/dist/admin/translations/en.json.js.map +1 -1
  113. package/dist/admin/translations/en.json.mjs +23 -0
  114. package/dist/admin/translations/en.json.mjs.map +1 -1
  115. package/dist/admin/utils/folderTelemetry.js +19 -0
  116. package/dist/admin/utils/folderTelemetry.js.map +1 -0
  117. package/dist/admin/utils/folderTelemetry.mjs +16 -0
  118. package/dist/admin/utils/folderTelemetry.mjs.map +1 -0
  119. package/dist/server/controllers/schema.js +1 -1
  120. package/dist/server/controllers/schema.js.map +1 -1
  121. package/dist/server/controllers/schema.mjs +1 -1
  122. package/dist/server/controllers/schema.mjs.map +1 -1
  123. package/dist/server/controllers/validation/common.js +3 -0
  124. package/dist/server/controllers/validation/common.js.map +1 -1
  125. package/dist/server/controllers/validation/common.mjs +3 -1
  126. package/dist/server/controllers/validation/common.mjs.map +1 -1
  127. package/dist/server/controllers/validation/content-structure.js +177 -0
  128. package/dist/server/controllers/validation/content-structure.js.map +1 -0
  129. package/dist/server/controllers/validation/content-structure.mjs +173 -0
  130. package/dist/server/controllers/validation/content-structure.mjs.map +1 -0
  131. package/dist/server/controllers/validation/schema.js +3 -1
  132. package/dist/server/controllers/validation/schema.js.map +1 -1
  133. package/dist/server/controllers/validation/schema.mjs +3 -1
  134. package/dist/server/controllers/validation/schema.mjs.map +1 -1
  135. package/dist/server/register.js +1 -1
  136. package/dist/server/register.js.map +1 -1
  137. package/dist/server/register.mjs +1 -1
  138. package/dist/server/register.mjs.map +1 -1
  139. package/dist/server/routes/content-api.js +4 -4
  140. package/dist/server/routes/content-api.js.map +1 -1
  141. package/dist/server/routes/content-api.mjs +4 -4
  142. package/dist/server/routes/content-api.mjs.map +1 -1
  143. package/dist/server/services/api-handler.js +37 -4
  144. package/dist/server/services/api-handler.js.map +1 -1
  145. package/dist/server/services/api-handler.mjs +36 -5
  146. package/dist/server/services/api-handler.mjs.map +1 -1
  147. package/dist/server/services/content-structure.js +174 -0
  148. package/dist/server/services/content-structure.js.map +1 -0
  149. package/dist/server/services/content-structure.mjs +172 -0
  150. package/dist/server/services/content-structure.mjs.map +1 -0
  151. package/dist/server/services/content-types.js +196 -44
  152. package/dist/server/services/content-types.js.map +1 -1
  153. package/dist/server/services/content-types.mjs +195 -45
  154. package/dist/server/services/content-types.mjs.map +1 -1
  155. package/dist/server/services/index.js +3 -1
  156. package/dist/server/services/index.js.map +1 -1
  157. package/dist/server/services/index.mjs +3 -1
  158. package/dist/server/services/index.mjs.map +1 -1
  159. package/dist/server/services/schema-builder/index.js +4 -2
  160. package/dist/server/services/schema-builder/index.js.map +1 -1
  161. package/dist/server/services/schema-builder/index.mjs +4 -2
  162. package/dist/server/services/schema-builder/index.mjs.map +1 -1
  163. package/dist/server/services/schema-builder/schema-handler.js +19 -17
  164. package/dist/server/services/schema-builder/schema-handler.js.map +1 -1
  165. package/dist/server/services/schema-builder/schema-handler.mjs +19 -17
  166. package/dist/server/services/schema-builder/schema-handler.mjs.map +1 -1
  167. package/dist/server/services/schema-mutation.js +46 -0
  168. package/dist/server/services/schema-mutation.js.map +1 -0
  169. package/dist/server/services/schema-mutation.mjs +43 -0
  170. package/dist/server/services/schema-mutation.mjs.map +1 -0
  171. package/dist/server/services/schema.js +134 -79
  172. package/dist/server/services/schema.js.map +1 -1
  173. package/dist/server/services/schema.mjs +135 -80
  174. package/dist/server/services/schema.mjs.map +1 -1
  175. package/dist/server/src/controllers/schema.d.ts.map +1 -1
  176. package/dist/server/src/controllers/validation/common.d.ts +1 -0
  177. package/dist/server/src/controllers/validation/common.d.ts.map +1 -1
  178. package/dist/server/src/controllers/validation/content-structure.d.ts +56 -0
  179. package/dist/server/src/controllers/validation/content-structure.d.ts.map +1 -0
  180. package/dist/server/src/controllers/validation/schema.d.ts +35 -0
  181. package/dist/server/src/controllers/validation/schema.d.ts.map +1 -1
  182. package/dist/server/src/index.d.ts +3 -0
  183. package/dist/server/src/index.d.ts.map +1 -1
  184. package/dist/server/src/routes/content-api.d.ts.map +1 -1
  185. package/dist/server/src/services/api-handler.d.ts +13 -1
  186. package/dist/server/src/services/api-handler.d.ts.map +1 -1
  187. package/dist/server/src/services/content-structure.d.ts +37 -0
  188. package/dist/server/src/services/content-structure.d.ts.map +1 -0
  189. package/dist/server/src/services/content-types.d.ts +4 -0
  190. package/dist/server/src/services/content-types.d.ts.map +1 -1
  191. package/dist/server/src/services/index.d.ts +4 -0
  192. package/dist/server/src/services/index.d.ts.map +1 -1
  193. package/dist/server/src/services/schema-builder/index.d.ts +3 -1
  194. package/dist/server/src/services/schema-builder/index.d.ts.map +1 -1
  195. package/dist/server/src/services/schema-builder/schema-handler.d.ts.map +1 -1
  196. package/dist/server/src/services/schema-mutation.d.ts +26 -0
  197. package/dist/server/src/services/schema-mutation.d.ts.map +1 -0
  198. package/dist/server/src/services/schema.d.ts +1 -0
  199. package/dist/server/src/services/schema.d.ts.map +1 -1
  200. package/dist/server/src/utils/index.d.ts +2 -0
  201. package/dist/server/src/utils/index.d.ts.map +1 -1
  202. package/dist/server/utils/index.js.map +1 -1
  203. package/dist/server/utils/index.mjs.map +1 -1
  204. package/package.json +8 -8
  205. package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.js +0 -157
  206. package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.js.map +0 -1
  207. package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.mjs +0 -151
  208. package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.mjs.map +0 -1
@@ -0,0 +1,150 @@
1
+ 'use strict';
2
+
3
+ var isEqual = require('lodash/isEqual');
4
+ var omit = require('lodash/omit');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var isEqual__default = /*#__PURE__*/_interopDefault(isEqual);
9
+ var omit__default = /*#__PURE__*/_interopDefault(omit);
10
+
11
+ const CONTENT_STRUCTURE_VERSION = 1;
12
+ const MAX_FOLDER_DEPTH = 3;
13
+ function sectionKeyForKind(kind) {
14
+ return kind === 'singleType' ? 'singleTypes' : 'collectionTypes';
15
+ }
16
+ function createEmptyContentStructure() {
17
+ return {
18
+ version: CONTENT_STRUCTURE_VERSION,
19
+ sections: {
20
+ collectionTypes: {
21
+ groups: []
22
+ },
23
+ singleTypes: {
24
+ groups: []
25
+ }
26
+ }
27
+ };
28
+ }
29
+ const addStatusToSection = (section)=>{
30
+ return {
31
+ groups: (section?.groups || []).map((group)=>({
32
+ ...group,
33
+ status: 'UNCHANGED'
34
+ }))
35
+ };
36
+ };
37
+ function fromServerFile(file) {
38
+ if (!file || !file.sections) {
39
+ return createEmptyContentStructure();
40
+ }
41
+ return {
42
+ version: CONTENT_STRUCTURE_VERSION,
43
+ sections: {
44
+ collectionTypes: addStatusToSection(file.sections.collectionTypes),
45
+ singleTypes: addStatusToSection(file.sections.singleTypes)
46
+ }
47
+ };
48
+ }
49
+ const removeStatusFromSection = (section)=>{
50
+ return {
51
+ groups: section.groups.map((group)=>omit__default.default(group, 'status'))
52
+ };
53
+ };
54
+ function toServerFile(structure) {
55
+ return {
56
+ version: CONTENT_STRUCTURE_VERSION,
57
+ sections: {
58
+ collectionTypes: removeStatusFromSection(structure.sections.collectionTypes),
59
+ singleTypes: removeStatusFromSection(structure.sections.singleTypes)
60
+ }
61
+ };
62
+ }
63
+ /**
64
+ * Determines whether two contentStructure trees differ in any way the server would persist.
65
+ *
66
+ * The client-side state manager removes groups from the tree upon deletion.
67
+ * This means that the "REMOVED" status is impossible to reach. See {@link GroupStatus}.
68
+ */ function hasContentStructureChanged(working, initial) {
69
+ return !isEqual__default.default(toServerFile(working), toServerFile(initial));
70
+ }
71
+ function generateGroupId() {
72
+ const body = (()=>{
73
+ if (crypto.randomUUID) {
74
+ return crypto.randomUUID().replace(/-/g, '').slice(0, 24);
75
+ }
76
+ const bytes = new Uint8Array(Math.ceil(24 / 2));
77
+ crypto.getRandomValues(bytes);
78
+ return Array.from(bytes, (byte)=>byte.toString(16).padStart(2, '0')).join('').slice(0, 24);
79
+ })();
80
+ return `grp_${body}`;
81
+ }
82
+ /**
83
+ * Find a sibling folder that already has a given `name`.
84
+ *
85
+ * `excludeId` skips a group by id (used when renaming, so a folder doesn't
86
+ * collide with itself).
87
+ *
88
+ * Returns `undefined` for an empty name or no match.
89
+ */ function findSiblingFolderByName(groups, parentId, name, excludeId) {
90
+ const target = name.trim().toLowerCase();
91
+ if (target.length === 0) {
92
+ return undefined;
93
+ }
94
+ return groups.find((group)=>{
95
+ if (group.parent !== parentId) {
96
+ return false;
97
+ }
98
+ if (group.id === excludeId) {
99
+ return false;
100
+ }
101
+ return group.name.trim().toLowerCase() === target;
102
+ });
103
+ }
104
+ /**
105
+ * Whether a folder name is already taken by a sibling. See
106
+ * {@link findSiblingFolderByName}.
107
+ */ function isFolderNameTakenBySibling(groups, parentId, name, excludeId) {
108
+ return findSiblingFolderByName(groups, parentId, name, excludeId) !== undefined;
109
+ }
110
+ /**
111
+ * Returns names that would collide if a folder were removed and its child folders inherited its
112
+ * parent. Both the existing destination siblings and the incoming children are compared using
113
+ * the same case-insensitive contract as create, rename, and move.
114
+ */ function getDeleteFolderOnlySiblingNameCollisions(groups, groupId) {
115
+ const groupToDelete = groups.find((group)=>group.id === groupId);
116
+ if (!groupToDelete) {
117
+ return [];
118
+ }
119
+ const siblingNames = new Set(groups.filter((group)=>group.parent === groupToDelete.parent && group.id !== groupId).map((group)=>group.name.trim().toLowerCase()));
120
+ const collisions = [];
121
+ for (const child of groupToDelete.children){
122
+ if (child.type !== 'group') {
123
+ continue;
124
+ }
125
+ const childGroup = groups.find((group)=>group.id === child.id);
126
+ if (!childGroup) {
127
+ continue;
128
+ }
129
+ const name = childGroup.name.trim().toLowerCase();
130
+ if (siblingNames.has(name)) {
131
+ collisions.push(childGroup.name);
132
+ continue;
133
+ }
134
+ siblingNames.add(name);
135
+ }
136
+ return collisions;
137
+ }
138
+
139
+ exports.CONTENT_STRUCTURE_VERSION = CONTENT_STRUCTURE_VERSION;
140
+ exports.MAX_FOLDER_DEPTH = MAX_FOLDER_DEPTH;
141
+ exports.createEmptyContentStructure = createEmptyContentStructure;
142
+ exports.findSiblingFolderByName = findSiblingFolderByName;
143
+ exports.fromServerFile = fromServerFile;
144
+ exports.generateGroupId = generateGroupId;
145
+ exports.getDeleteFolderOnlySiblingNameCollisions = getDeleteFolderOnlySiblingNameCollisions;
146
+ exports.hasContentStructureChanged = hasContentStructureChanged;
147
+ exports.isFolderNameTakenBySibling = isFolderNameTakenBySibling;
148
+ exports.sectionKeyForKind = sectionKeyForKind;
149
+ exports.toServerFile = toServerFile;
150
+ //# sourceMappingURL=contentStructure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentStructure.js","sources":["../../../../../admin/src/components/DataManager/utils/contentStructure.ts"],"sourcesContent":["import isEqual from 'lodash/isEqual';\nimport omit from 'lodash/omit';\n\nimport type { Status } from '../../../types';\nimport type { Modules, Struct } from '@strapi/types';\n\nexport type SectionKey = Modules.ContentStructure.ContentStructureSectionKey;\n\nexport type ContentStructureChild = Modules.ContentStructure.ContentStructureChild;\n\n/**\n * The status of a content structure group, excluding \"REMOVED\".\n * The contentStructure API is declarative, so the server will identify removed groups by their absence in the submitted structure.\n * The admin panel will remove groups from the tree upon deletion, so the \"REMOVED\" status is impossible to reach.\n */\nexport type GroupStatus = Exclude<Status, 'REMOVED'>;\n\nexport type ContentStructureGroup = Modules.ContentStructure.ContentStructureGroup & {\n status: GroupStatus;\n};\n\nexport type ContentStructureSection = {\n groups: ContentStructureGroup[];\n};\n\nexport const CONTENT_STRUCTURE_VERSION = 1;\nexport const MAX_FOLDER_DEPTH = 3;\n\nexport type FolderSelection = { targetGroupId: string | null } | { newFolderName: string };\n\n/**\n * Admin-side model of the content-structure (folder groups).\n *\n * The admin is identical to the server-side structure, but for the inclusion of a `status` field on every group.\n * This allows the admin panel to track changes to the content structure using existing patterns.\n *\n * The `status` field is stripped by {@link toServerFile} before a client-modified structure is sent to the server.\n */\nexport type ContentStructure = {\n version: typeof CONTENT_STRUCTURE_VERSION;\n sections: Record<SectionKey, ContentStructureSection>;\n};\n\nexport const SECTION_KEYS = [\n 'collectionTypes',\n 'singleTypes',\n] as const satisfies readonly SectionKey[];\n\nexport function sectionKeyForKind(kind: Struct.ContentTypeKind): SectionKey {\n return kind === 'singleType' ? 'singleTypes' : 'collectionTypes';\n}\n\nexport function createEmptyContentStructure(): ContentStructure {\n return {\n version: CONTENT_STRUCTURE_VERSION,\n sections: {\n collectionTypes: { groups: [] },\n singleTypes: { groups: [] },\n },\n };\n}\n\nconst addStatusToSection = (\n section: Modules.ContentStructure.ContentStructureSection | undefined\n): ContentStructureSection => {\n return {\n groups: (section?.groups || []).map((group) => ({\n ...group,\n status: 'UNCHANGED',\n })),\n };\n};\n\nexport function fromServerFile(\n file: Modules.ContentStructure.ContentStructureFile | null | undefined\n): ContentStructure {\n if (!file || !file.sections) {\n return createEmptyContentStructure();\n }\n\n return {\n version: CONTENT_STRUCTURE_VERSION,\n sections: {\n collectionTypes: addStatusToSection(file.sections.collectionTypes),\n singleTypes: addStatusToSection(file.sections.singleTypes),\n },\n };\n}\n\nconst removeStatusFromSection = (\n section: ContentStructureSection\n): Modules.ContentStructure.ContentStructureSection => {\n return {\n groups: section.groups.map((group) => omit(group, 'status')),\n };\n};\n\nexport function toServerFile(\n structure: ContentStructure\n): Modules.ContentStructure.ContentStructureFile {\n return {\n version: CONTENT_STRUCTURE_VERSION,\n sections: {\n collectionTypes: removeStatusFromSection(structure.sections.collectionTypes),\n singleTypes: removeStatusFromSection(structure.sections.singleTypes),\n },\n };\n}\n\n/**\n * Determines whether two contentStructure trees differ in any way the server would persist.\n *\n * The client-side state manager removes groups from the tree upon deletion.\n * This means that the \"REMOVED\" status is impossible to reach. See {@link GroupStatus}.\n */\nexport function hasContentStructureChanged(\n working: ContentStructure,\n initial: ContentStructure\n): boolean {\n return !isEqual(toServerFile(working), toServerFile(initial));\n}\n\nexport function generateGroupId(): string {\n const body = (() => {\n if (crypto.randomUUID) {\n return crypto.randomUUID().replace(/-/g, '').slice(0, 24);\n }\n\n const bytes = new Uint8Array(Math.ceil(24 / 2));\n crypto.getRandomValues(bytes);\n\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0'))\n .join('')\n .slice(0, 24);\n })();\n\n return `grp_${body}`;\n}\n\n/**\n * Find a sibling folder that already has a given `name`.\n *\n * `excludeId` skips a group by id (used when renaming, so a folder doesn't\n * collide with itself).\n *\n * Returns `undefined` for an empty name or no match.\n */\nexport function findSiblingFolderByName<\n TGroup extends Pick<ContentStructureGroup, 'id' | 'name' | 'parent'>,\n>(groups: TGroup[], parentId: string | null, name: string, excludeId?: string): TGroup | undefined {\n const target = name.trim().toLowerCase();\n\n if (target.length === 0) {\n return undefined;\n }\n\n return groups.find((group) => {\n if (group.parent !== parentId) {\n return false;\n }\n\n if (group.id === excludeId) {\n return false;\n }\n\n return group.name.trim().toLowerCase() === target;\n });\n}\n\n/**\n * Whether a folder name is already taken by a sibling. See\n * {@link findSiblingFolderByName}.\n */\nexport function isFolderNameTakenBySibling(\n groups: Pick<ContentStructureGroup, 'id' | 'name' | 'parent'>[],\n parentId: string | null,\n name: string,\n excludeId?: string\n): boolean {\n return findSiblingFolderByName(groups, parentId, name, excludeId) !== undefined;\n}\n\n/**\n * Returns names that would collide if a folder were removed and its child folders inherited its\n * parent. Both the existing destination siblings and the incoming children are compared using\n * the same case-insensitive contract as create, rename, and move.\n */\nexport function getDeleteFolderOnlySiblingNameCollisions(\n groups: Pick<ContentStructureGroup, 'id' | 'name' | 'parent' | 'children'>[],\n groupId: string\n): string[] {\n const groupToDelete = groups.find((group) => group.id === groupId);\n\n if (!groupToDelete) {\n return [];\n }\n\n const siblingNames = new Set(\n groups\n .filter((group) => group.parent === groupToDelete.parent && group.id !== groupId)\n .map((group) => group.name.trim().toLowerCase())\n );\n const collisions: string[] = [];\n\n for (const child of groupToDelete.children) {\n if (child.type !== 'group') {\n continue;\n }\n\n const childGroup = groups.find((group) => group.id === child.id);\n if (!childGroup) {\n continue;\n }\n\n const name = childGroup.name.trim().toLowerCase();\n if (siblingNames.has(name)) {\n collisions.push(childGroup.name);\n continue;\n }\n\n siblingNames.add(name);\n }\n\n return collisions;\n}\n"],"names":["CONTENT_STRUCTURE_VERSION","MAX_FOLDER_DEPTH","sectionKeyForKind","kind","createEmptyContentStructure","version","sections","collectionTypes","groups","singleTypes","addStatusToSection","section","map","group","status","fromServerFile","file","removeStatusFromSection","omit","toServerFile","structure","hasContentStructureChanged","working","initial","isEqual","generateGroupId","body","crypto","randomUUID","replace","slice","bytes","Uint8Array","Math","ceil","getRandomValues","Array","from","byte","toString","padStart","join","findSiblingFolderByName","parentId","name","excludeId","target","trim","toLowerCase","length","undefined","find","parent","id","isFolderNameTakenBySibling","getDeleteFolderOnlySiblingNameCollisions","groupId","groupToDelete","siblingNames","Set","filter","collisions","child","children","type","childGroup","has","push","add"],"mappings":";;;;;;;;;;AAyBO,MAAMA,4BAA4B;AAClC,MAAMC,mBAAmB;AAsBzB,SAASC,kBAAkBC,IAA4B,EAAA;IAC5D,OAAOA,IAAAA,KAAS,eAAe,aAAA,GAAgB,iBAAA;AACjD;AAEO,SAASC,2BAAAA,GAAAA;IACd,OAAO;QACLC,OAAAA,EAASL,yBAAAA;QACTM,QAAAA,EAAU;YACRC,eAAAA,EAAiB;AAAEC,gBAAAA,MAAAA,EAAQ;AAAG,aAAA;YAC9BC,WAAAA,EAAa;AAAED,gBAAAA,MAAAA,EAAQ;AAAG;AAC5B;AACF,KAAA;AACF;AAEA,MAAME,qBAAqB,CACzBC,OAAAA,GAAAA;IAEA,OAAO;QACLH,MAAAA,EAASG,CAAAA,OAAAA,EAASH,MAAAA,IAAU,EAAE,EAAEI,GAAG,CAAC,CAACC,KAAAA,IAAW;AAC9C,gBAAA,GAAGA,KAAK;gBACRC,MAAAA,EAAQ;aACV,CAAA;AACF,KAAA;AACF,CAAA;AAEO,SAASC,eACdC,IAAsE,EAAA;AAEtE,IAAA,IAAI,CAACA,IAAAA,IAAQ,CAACA,IAAAA,CAAKV,QAAQ,EAAE;QAC3B,OAAOF,2BAAAA,EAAAA;AACT,IAAA;IAEA,OAAO;QACLC,OAAAA,EAASL,yBAAAA;QACTM,QAAAA,EAAU;AACRC,YAAAA,eAAAA,EAAiBG,kBAAAA,CAAmBM,IAAAA,CAAKV,QAAQ,CAACC,eAAe,CAAA;AACjEE,YAAAA,WAAAA,EAAaC,kBAAAA,CAAmBM,IAAAA,CAAKV,QAAQ,CAACG,WAAW;AAC3D;AACF,KAAA;AACF;AAEA,MAAMQ,0BAA0B,CAC9BN,OAAAA,GAAAA;IAEA,OAAO;QACLH,MAAAA,EAAQG,OAAAA,CAAQH,MAAM,CAACI,GAAG,CAAC,CAACC,KAAAA,GAAUK,sBAAKL,KAAAA,EAAO,QAAA,CAAA;AACpD,KAAA;AACF,CAAA;AAEO,SAASM,aACdC,SAA2B,EAAA;IAE3B,OAAO;QACLf,OAAAA,EAASL,yBAAAA;QACTM,QAAAA,EAAU;AACRC,YAAAA,eAAAA,EAAiBU,uBAAAA,CAAwBG,SAAAA,CAAUd,QAAQ,CAACC,eAAe,CAAA;AAC3EE,YAAAA,WAAAA,EAAaQ,uBAAAA,CAAwBG,SAAAA,CAAUd,QAAQ,CAACG,WAAW;AACrE;AACF,KAAA;AACF;AAEA;;;;;AAKC,IACM,SAASY,0BAAAA,CACdC,OAAyB,EACzBC,OAAyB,EAAA;AAEzB,IAAA,OAAO,CAACC,wBAAAA,CAAQL,YAAAA,CAAaG,OAAAA,CAAAA,EAAUH,YAAAA,CAAaI,OAAAA,CAAAA,CAAAA;AACtD;AAEO,SAASE,eAAAA,GAAAA;IACd,MAAMC,IAAAA,GAAO,CAAC,IAAA;QACZ,IAAIC,MAAAA,CAAOC,UAAU,EAAE;YACrB,OAAOD,MAAAA,CAAOC,UAAU,EAAA,CAAGC,OAAO,CAAC,IAAA,EAAM,EAAA,CAAA,CAAIC,KAAK,CAAC,CAAA,EAAG,EAAA,CAAA;AACxD,QAAA;AAEA,QAAA,MAAMC,QAAQ,IAAIC,UAAAA,CAAWC,IAAAA,CAAKC,IAAI,CAAC,EAAA,GAAK,CAAA,CAAA,CAAA;AAC5CP,QAAAA,MAAAA,CAAOQ,eAAe,CAACJ,KAAAA,CAAAA;AAEvB,QAAA,OAAOK,MAAMC,IAAI,CAACN,OAAO,CAACO,IAAAA,GAASA,KAAKC,QAAQ,CAAC,IAAIC,QAAQ,CAAC,GAAG,GAAA,CAAA,CAAA,CAC9DC,IAAI,CAAC,EAAA,CAAA,CACLX,KAAK,CAAC,CAAA,EAAG,EAAA,CAAA;IACd,CAAA,GAAA;IAEA,OAAO,CAAC,IAAI,EAAEJ,IAAAA,CAAAA,CAAM;AACtB;AAEA;;;;;;;IAQO,SAASgB,uBAAAA,CAEdlC,MAAgB,EAAEmC,QAAuB,EAAEC,IAAY,EAAEC,SAAkB,EAAA;AAC3E,IAAA,MAAMC,MAAAA,GAASF,IAAAA,CAAKG,IAAI,EAAA,CAAGC,WAAW,EAAA;IAEtC,IAAIF,MAAAA,CAAOG,MAAM,KAAK,CAAA,EAAG;QACvB,OAAOC,SAAAA;AACT,IAAA;IAEA,OAAO1C,MAAAA,CAAO2C,IAAI,CAAC,CAACtC,KAAAA,GAAAA;QAClB,IAAIA,KAAAA,CAAMuC,MAAM,KAAKT,QAAAA,EAAU;YAC7B,OAAO,KAAA;AACT,QAAA;QAEA,IAAI9B,KAAAA,CAAMwC,EAAE,KAAKR,SAAAA,EAAW;YAC1B,OAAO,KAAA;AACT,QAAA;AAEA,QAAA,OAAOhC,MAAM+B,IAAI,CAACG,IAAI,EAAA,CAAGC,WAAW,EAAA,KAAOF,MAAAA;AAC7C,IAAA,CAAA,CAAA;AACF;AAEA;;;IAIO,SAASQ,0BAAAA,CACd9C,MAA+D,EAC/DmC,QAAuB,EACvBC,IAAY,EACZC,SAAkB,EAAA;AAElB,IAAA,OAAOH,uBAAAA,CAAwBlC,MAAAA,EAAQmC,QAAAA,EAAUC,IAAAA,EAAMC,SAAAA,CAAAA,KAAeK,SAAAA;AACxE;AAEA;;;;AAIC,IACM,SAASK,wCAAAA,CACd/C,MAA4E,EAC5EgD,OAAe,EAAA;IAEf,MAAMC,aAAAA,GAAgBjD,OAAO2C,IAAI,CAAC,CAACtC,KAAAA,GAAUA,KAAAA,CAAMwC,EAAE,KAAKG,OAAAA,CAAAA;AAE1D,IAAA,IAAI,CAACC,aAAAA,EAAe;AAClB,QAAA,OAAO,EAAE;AACX,IAAA;IAEA,MAAMC,YAAAA,GAAe,IAAIC,GAAAA,CACvBnD,MAAAA,CACGoD,MAAM,CAAC,CAAC/C,KAAAA,GAAUA,KAAAA,CAAMuC,MAAM,KAAKK,aAAAA,CAAcL,MAAM,IAAIvC,KAAAA,CAAMwC,EAAE,KAAKG,OAAAA,CAAAA,CACxE5C,GAAG,CAAC,CAACC,KAAAA,GAAUA,KAAAA,CAAM+B,IAAI,CAACG,IAAI,EAAA,CAAGC,WAAW,EAAA,CAAA,CAAA;AAEjD,IAAA,MAAMa,aAAuB,EAAE;AAE/B,IAAA,KAAK,MAAMC,KAAAA,IAASL,aAAAA,CAAcM,QAAQ,CAAE;QAC1C,IAAID,KAAAA,CAAME,IAAI,KAAK,OAAA,EAAS;AAC1B,YAAA;AACF,QAAA;QAEA,MAAMC,UAAAA,GAAazD,MAAAA,CAAO2C,IAAI,CAAC,CAACtC,QAAUA,KAAAA,CAAMwC,EAAE,KAAKS,KAAAA,CAAMT,EAAE,CAAA;AAC/D,QAAA,IAAI,CAACY,UAAAA,EAAY;AACf,YAAA;AACF,QAAA;AAEA,QAAA,MAAMrB,OAAOqB,UAAAA,CAAWrB,IAAI,CAACG,IAAI,GAAGC,WAAW,EAAA;QAC/C,IAAIU,YAAAA,CAAaQ,GAAG,CAACtB,IAAAA,CAAAA,EAAO;YAC1BiB,UAAAA,CAAWM,IAAI,CAACF,UAAAA,CAAWrB,IAAI,CAAA;AAC/B,YAAA;AACF,QAAA;AAEAc,QAAAA,YAAAA,CAAaU,GAAG,CAACxB,IAAAA,CAAAA;AACnB,IAAA;IAEA,OAAOiB,UAAAA;AACT;;;;;;;;;;;;;;"}
@@ -0,0 +1,133 @@
1
+ import isEqual from 'lodash/isEqual';
2
+ import omit from 'lodash/omit';
3
+
4
+ const CONTENT_STRUCTURE_VERSION = 1;
5
+ const MAX_FOLDER_DEPTH = 3;
6
+ function sectionKeyForKind(kind) {
7
+ return kind === 'singleType' ? 'singleTypes' : 'collectionTypes';
8
+ }
9
+ function createEmptyContentStructure() {
10
+ return {
11
+ version: CONTENT_STRUCTURE_VERSION,
12
+ sections: {
13
+ collectionTypes: {
14
+ groups: []
15
+ },
16
+ singleTypes: {
17
+ groups: []
18
+ }
19
+ }
20
+ };
21
+ }
22
+ const addStatusToSection = (section)=>{
23
+ return {
24
+ groups: (section?.groups || []).map((group)=>({
25
+ ...group,
26
+ status: 'UNCHANGED'
27
+ }))
28
+ };
29
+ };
30
+ function fromServerFile(file) {
31
+ if (!file || !file.sections) {
32
+ return createEmptyContentStructure();
33
+ }
34
+ return {
35
+ version: CONTENT_STRUCTURE_VERSION,
36
+ sections: {
37
+ collectionTypes: addStatusToSection(file.sections.collectionTypes),
38
+ singleTypes: addStatusToSection(file.sections.singleTypes)
39
+ }
40
+ };
41
+ }
42
+ const removeStatusFromSection = (section)=>{
43
+ return {
44
+ groups: section.groups.map((group)=>omit(group, 'status'))
45
+ };
46
+ };
47
+ function toServerFile(structure) {
48
+ return {
49
+ version: CONTENT_STRUCTURE_VERSION,
50
+ sections: {
51
+ collectionTypes: removeStatusFromSection(structure.sections.collectionTypes),
52
+ singleTypes: removeStatusFromSection(structure.sections.singleTypes)
53
+ }
54
+ };
55
+ }
56
+ /**
57
+ * Determines whether two contentStructure trees differ in any way the server would persist.
58
+ *
59
+ * The client-side state manager removes groups from the tree upon deletion.
60
+ * This means that the "REMOVED" status is impossible to reach. See {@link GroupStatus}.
61
+ */ function hasContentStructureChanged(working, initial) {
62
+ return !isEqual(toServerFile(working), toServerFile(initial));
63
+ }
64
+ function generateGroupId() {
65
+ const body = (()=>{
66
+ if (crypto.randomUUID) {
67
+ return crypto.randomUUID().replace(/-/g, '').slice(0, 24);
68
+ }
69
+ const bytes = new Uint8Array(Math.ceil(24 / 2));
70
+ crypto.getRandomValues(bytes);
71
+ return Array.from(bytes, (byte)=>byte.toString(16).padStart(2, '0')).join('').slice(0, 24);
72
+ })();
73
+ return `grp_${body}`;
74
+ }
75
+ /**
76
+ * Find a sibling folder that already has a given `name`.
77
+ *
78
+ * `excludeId` skips a group by id (used when renaming, so a folder doesn't
79
+ * collide with itself).
80
+ *
81
+ * Returns `undefined` for an empty name or no match.
82
+ */ function findSiblingFolderByName(groups, parentId, name, excludeId) {
83
+ const target = name.trim().toLowerCase();
84
+ if (target.length === 0) {
85
+ return undefined;
86
+ }
87
+ return groups.find((group)=>{
88
+ if (group.parent !== parentId) {
89
+ return false;
90
+ }
91
+ if (group.id === excludeId) {
92
+ return false;
93
+ }
94
+ return group.name.trim().toLowerCase() === target;
95
+ });
96
+ }
97
+ /**
98
+ * Whether a folder name is already taken by a sibling. See
99
+ * {@link findSiblingFolderByName}.
100
+ */ function isFolderNameTakenBySibling(groups, parentId, name, excludeId) {
101
+ return findSiblingFolderByName(groups, parentId, name, excludeId) !== undefined;
102
+ }
103
+ /**
104
+ * Returns names that would collide if a folder were removed and its child folders inherited its
105
+ * parent. Both the existing destination siblings and the incoming children are compared using
106
+ * the same case-insensitive contract as create, rename, and move.
107
+ */ function getDeleteFolderOnlySiblingNameCollisions(groups, groupId) {
108
+ const groupToDelete = groups.find((group)=>group.id === groupId);
109
+ if (!groupToDelete) {
110
+ return [];
111
+ }
112
+ const siblingNames = new Set(groups.filter((group)=>group.parent === groupToDelete.parent && group.id !== groupId).map((group)=>group.name.trim().toLowerCase()));
113
+ const collisions = [];
114
+ for (const child of groupToDelete.children){
115
+ if (child.type !== 'group') {
116
+ continue;
117
+ }
118
+ const childGroup = groups.find((group)=>group.id === child.id);
119
+ if (!childGroup) {
120
+ continue;
121
+ }
122
+ const name = childGroup.name.trim().toLowerCase();
123
+ if (siblingNames.has(name)) {
124
+ collisions.push(childGroup.name);
125
+ continue;
126
+ }
127
+ siblingNames.add(name);
128
+ }
129
+ return collisions;
130
+ }
131
+
132
+ export { CONTENT_STRUCTURE_VERSION, MAX_FOLDER_DEPTH, createEmptyContentStructure, findSiblingFolderByName, fromServerFile, generateGroupId, getDeleteFolderOnlySiblingNameCollisions, hasContentStructureChanged, isFolderNameTakenBySibling, sectionKeyForKind, toServerFile };
133
+ //# sourceMappingURL=contentStructure.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentStructure.mjs","sources":["../../../../../admin/src/components/DataManager/utils/contentStructure.ts"],"sourcesContent":["import isEqual from 'lodash/isEqual';\nimport omit from 'lodash/omit';\n\nimport type { Status } from '../../../types';\nimport type { Modules, Struct } from '@strapi/types';\n\nexport type SectionKey = Modules.ContentStructure.ContentStructureSectionKey;\n\nexport type ContentStructureChild = Modules.ContentStructure.ContentStructureChild;\n\n/**\n * The status of a content structure group, excluding \"REMOVED\".\n * The contentStructure API is declarative, so the server will identify removed groups by their absence in the submitted structure.\n * The admin panel will remove groups from the tree upon deletion, so the \"REMOVED\" status is impossible to reach.\n */\nexport type GroupStatus = Exclude<Status, 'REMOVED'>;\n\nexport type ContentStructureGroup = Modules.ContentStructure.ContentStructureGroup & {\n status: GroupStatus;\n};\n\nexport type ContentStructureSection = {\n groups: ContentStructureGroup[];\n};\n\nexport const CONTENT_STRUCTURE_VERSION = 1;\nexport const MAX_FOLDER_DEPTH = 3;\n\nexport type FolderSelection = { targetGroupId: string | null } | { newFolderName: string };\n\n/**\n * Admin-side model of the content-structure (folder groups).\n *\n * The admin is identical to the server-side structure, but for the inclusion of a `status` field on every group.\n * This allows the admin panel to track changes to the content structure using existing patterns.\n *\n * The `status` field is stripped by {@link toServerFile} before a client-modified structure is sent to the server.\n */\nexport type ContentStructure = {\n version: typeof CONTENT_STRUCTURE_VERSION;\n sections: Record<SectionKey, ContentStructureSection>;\n};\n\nexport const SECTION_KEYS = [\n 'collectionTypes',\n 'singleTypes',\n] as const satisfies readonly SectionKey[];\n\nexport function sectionKeyForKind(kind: Struct.ContentTypeKind): SectionKey {\n return kind === 'singleType' ? 'singleTypes' : 'collectionTypes';\n}\n\nexport function createEmptyContentStructure(): ContentStructure {\n return {\n version: CONTENT_STRUCTURE_VERSION,\n sections: {\n collectionTypes: { groups: [] },\n singleTypes: { groups: [] },\n },\n };\n}\n\nconst addStatusToSection = (\n section: Modules.ContentStructure.ContentStructureSection | undefined\n): ContentStructureSection => {\n return {\n groups: (section?.groups || []).map((group) => ({\n ...group,\n status: 'UNCHANGED',\n })),\n };\n};\n\nexport function fromServerFile(\n file: Modules.ContentStructure.ContentStructureFile | null | undefined\n): ContentStructure {\n if (!file || !file.sections) {\n return createEmptyContentStructure();\n }\n\n return {\n version: CONTENT_STRUCTURE_VERSION,\n sections: {\n collectionTypes: addStatusToSection(file.sections.collectionTypes),\n singleTypes: addStatusToSection(file.sections.singleTypes),\n },\n };\n}\n\nconst removeStatusFromSection = (\n section: ContentStructureSection\n): Modules.ContentStructure.ContentStructureSection => {\n return {\n groups: section.groups.map((group) => omit(group, 'status')),\n };\n};\n\nexport function toServerFile(\n structure: ContentStructure\n): Modules.ContentStructure.ContentStructureFile {\n return {\n version: CONTENT_STRUCTURE_VERSION,\n sections: {\n collectionTypes: removeStatusFromSection(structure.sections.collectionTypes),\n singleTypes: removeStatusFromSection(structure.sections.singleTypes),\n },\n };\n}\n\n/**\n * Determines whether two contentStructure trees differ in any way the server would persist.\n *\n * The client-side state manager removes groups from the tree upon deletion.\n * This means that the \"REMOVED\" status is impossible to reach. See {@link GroupStatus}.\n */\nexport function hasContentStructureChanged(\n working: ContentStructure,\n initial: ContentStructure\n): boolean {\n return !isEqual(toServerFile(working), toServerFile(initial));\n}\n\nexport function generateGroupId(): string {\n const body = (() => {\n if (crypto.randomUUID) {\n return crypto.randomUUID().replace(/-/g, '').slice(0, 24);\n }\n\n const bytes = new Uint8Array(Math.ceil(24 / 2));\n crypto.getRandomValues(bytes);\n\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0'))\n .join('')\n .slice(0, 24);\n })();\n\n return `grp_${body}`;\n}\n\n/**\n * Find a sibling folder that already has a given `name`.\n *\n * `excludeId` skips a group by id (used when renaming, so a folder doesn't\n * collide with itself).\n *\n * Returns `undefined` for an empty name or no match.\n */\nexport function findSiblingFolderByName<\n TGroup extends Pick<ContentStructureGroup, 'id' | 'name' | 'parent'>,\n>(groups: TGroup[], parentId: string | null, name: string, excludeId?: string): TGroup | undefined {\n const target = name.trim().toLowerCase();\n\n if (target.length === 0) {\n return undefined;\n }\n\n return groups.find((group) => {\n if (group.parent !== parentId) {\n return false;\n }\n\n if (group.id === excludeId) {\n return false;\n }\n\n return group.name.trim().toLowerCase() === target;\n });\n}\n\n/**\n * Whether a folder name is already taken by a sibling. See\n * {@link findSiblingFolderByName}.\n */\nexport function isFolderNameTakenBySibling(\n groups: Pick<ContentStructureGroup, 'id' | 'name' | 'parent'>[],\n parentId: string | null,\n name: string,\n excludeId?: string\n): boolean {\n return findSiblingFolderByName(groups, parentId, name, excludeId) !== undefined;\n}\n\n/**\n * Returns names that would collide if a folder were removed and its child folders inherited its\n * parent. Both the existing destination siblings and the incoming children are compared using\n * the same case-insensitive contract as create, rename, and move.\n */\nexport function getDeleteFolderOnlySiblingNameCollisions(\n groups: Pick<ContentStructureGroup, 'id' | 'name' | 'parent' | 'children'>[],\n groupId: string\n): string[] {\n const groupToDelete = groups.find((group) => group.id === groupId);\n\n if (!groupToDelete) {\n return [];\n }\n\n const siblingNames = new Set(\n groups\n .filter((group) => group.parent === groupToDelete.parent && group.id !== groupId)\n .map((group) => group.name.trim().toLowerCase())\n );\n const collisions: string[] = [];\n\n for (const child of groupToDelete.children) {\n if (child.type !== 'group') {\n continue;\n }\n\n const childGroup = groups.find((group) => group.id === child.id);\n if (!childGroup) {\n continue;\n }\n\n const name = childGroup.name.trim().toLowerCase();\n if (siblingNames.has(name)) {\n collisions.push(childGroup.name);\n continue;\n }\n\n siblingNames.add(name);\n }\n\n return collisions;\n}\n"],"names":["CONTENT_STRUCTURE_VERSION","MAX_FOLDER_DEPTH","sectionKeyForKind","kind","createEmptyContentStructure","version","sections","collectionTypes","groups","singleTypes","addStatusToSection","section","map","group","status","fromServerFile","file","removeStatusFromSection","omit","toServerFile","structure","hasContentStructureChanged","working","initial","isEqual","generateGroupId","body","crypto","randomUUID","replace","slice","bytes","Uint8Array","Math","ceil","getRandomValues","Array","from","byte","toString","padStart","join","findSiblingFolderByName","parentId","name","excludeId","target","trim","toLowerCase","length","undefined","find","parent","id","isFolderNameTakenBySibling","getDeleteFolderOnlySiblingNameCollisions","groupId","groupToDelete","siblingNames","Set","filter","collisions","child","children","type","childGroup","has","push","add"],"mappings":"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"}
@@ -0,0 +1,200 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
5
+ var designSystem = require('@strapi/design-system');
6
+ var reactIntl = require('react-intl');
7
+ var useDataManager = require('./DataManager/useDataManager.js');
8
+ var contentStructure = require('./DataManager/utils/contentStructure.js');
9
+ var useFormModalNavigation = require('./FormModalNavigation/useFormModalNavigation.js');
10
+
11
+ /**
12
+ * Flatten a section's (flat) folder groups into a list of full folder path strings.
13
+ */ const buildFolderOptions = (groups)=>{
14
+ const groupById = new Map(groups.map((group)=>[
15
+ group.id,
16
+ group
17
+ ]));
18
+ const childGroupIds = new Set();
19
+ for (const group of groups){
20
+ for (const child of group.children){
21
+ if (child.type === 'group') {
22
+ childGroupIds.add(child.id);
23
+ }
24
+ }
25
+ }
26
+ const options = [];
27
+ const walk = (group, ancestors)=>{
28
+ const path = [
29
+ ...ancestors,
30
+ group.name
31
+ ];
32
+ options.push({
33
+ id: group.id,
34
+ label: path.join(' / ')
35
+ });
36
+ for (const child of group.children){
37
+ if (child.type !== 'group') {
38
+ continue;
39
+ }
40
+ const subGroup = groupById.get(child.id);
41
+ if (subGroup) {
42
+ walk(subGroup, path);
43
+ }
44
+ }
45
+ };
46
+ groups.filter((group)=>{
47
+ return !childGroupIds.has(group.id);
48
+ }).forEach((root)=>{
49
+ return walk(root, []);
50
+ });
51
+ return options;
52
+ };
53
+ const FolderSelect = ({ error = null, modifiedData, placeholder, isCreating, intlLabel, targetUid, onChange, value, name })=>{
54
+ const { kind: modalKind } = useFormModalNavigation.useFormModalNavigation();
55
+ const { contentStructure: contentStructure$1 } = useDataManager.useDataManager();
56
+ const { formatMessage } = reactIntl.useIntl();
57
+ const kind = modifiedData?.kind ?? modalKind ?? 'collectionType';
58
+ const section = contentStructure.sectionKeyForKind(kind);
59
+ const groups = contentStructure$1.sections[section].groups;
60
+ const folderOptions = React.useMemo(()=>{
61
+ return buildFolderOptions(groups);
62
+ }, [
63
+ groups
64
+ ]);
65
+ const knownFolderIds = React.useMemo(()=>{
66
+ return new Set(folderOptions.map((option)=>option.id));
67
+ }, [
68
+ folderOptions
69
+ ]);
70
+ const currentFolderId = React.useMemo(()=>{
71
+ if (isCreating || !targetUid) {
72
+ return null;
73
+ }
74
+ const owner = groups.find((group)=>{
75
+ return group.children.some((child)=>{
76
+ return child.type === 'contentType' && child.uid === targetUid;
77
+ });
78
+ });
79
+ return owner?.id ?? null;
80
+ }, [
81
+ groups,
82
+ isCreating,
83
+ targetUid
84
+ ]);
85
+ const change = (next)=>{
86
+ onChange({
87
+ target: {
88
+ name,
89
+ value: next,
90
+ type: 'content-type-folder-select'
91
+ }
92
+ });
93
+ };
94
+ const changeToFolderId = (id)=>{
95
+ change(id === currentFolderId ? undefined : {
96
+ targetGroupId: id
97
+ });
98
+ };
99
+ const selectTypedName = (typed)=>{
100
+ const trimmed = typed.trim();
101
+ if (!trimmed) {
102
+ return;
103
+ }
104
+ const existing = contentStructure.findSiblingFolderByName(groups, null, trimmed);
105
+ if (existing) {
106
+ changeToFolderId(existing.id);
107
+ return;
108
+ }
109
+ change({
110
+ newFolderName: trimmed
111
+ });
112
+ };
113
+ const selectedValue = (()=>{
114
+ if (value === undefined) {
115
+ return currentFolderId ?? '';
116
+ }
117
+ if ('newFolderName' in value) {
118
+ return value.newFolderName;
119
+ }
120
+ return value.targetGroupId ?? '';
121
+ })();
122
+ const selectedLabel = (()=>{
123
+ if (value && 'newFolderName' in value) {
124
+ return value.newFolderName;
125
+ }
126
+ return folderOptions.find(({ id })=>id === selectedValue)?.label ?? '';
127
+ })();
128
+ const handleChange = (nextValue)=>{
129
+ if (!nextValue) {
130
+ return;
131
+ }
132
+ if (knownFolderIds.has(nextValue)) {
133
+ changeToFolderId(nextValue);
134
+ return;
135
+ }
136
+ selectTypedName(nextValue);
137
+ };
138
+ const handleCreateOption = (nextValue)=>{
139
+ if (!nextValue) {
140
+ return;
141
+ }
142
+ selectTypedName(nextValue);
143
+ };
144
+ const handleClear = ()=>{
145
+ // In edit an explicit clear ungroups the content type. In create it just returns the field to its "no folder" default.
146
+ change(isCreating ? undefined : {
147
+ targetGroupId: null
148
+ });
149
+ };
150
+ const errorMessage = error ? formatMessage({
151
+ id: error,
152
+ defaultMessage: error
153
+ }) : '';
154
+ const placeholderText = placeholder ? formatMessage(placeholder) : undefined;
155
+ const label = formatMessage(intlLabel);
156
+ const comboboxOptions = React.useMemo(()=>{
157
+ const options = [];
158
+ for (const option of folderOptions){
159
+ options.push(/*#__PURE__*/ jsxRuntime.jsx(designSystem.ComboboxOption, {
160
+ value: option.id,
161
+ children: option.label
162
+ }, option.id));
163
+ }
164
+ // A new folder name typed into the combobox doesn't exist as a group yet, so we append it as its own option value.
165
+ if (selectedValue && !knownFolderIds.has(selectedValue)) {
166
+ options.push(/*#__PURE__*/ jsxRuntime.jsx(designSystem.ComboboxOption, {
167
+ value: selectedValue,
168
+ children: selectedValue
169
+ }, `new:${selectedValue}`));
170
+ }
171
+ return options;
172
+ }, [
173
+ folderOptions,
174
+ knownFolderIds,
175
+ selectedValue
176
+ ]);
177
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
178
+ error: errorMessage,
179
+ name: name,
180
+ children: [
181
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
182
+ children: label
183
+ }),
184
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Combobox, {
185
+ defaultTextValue: selectedLabel || undefined,
186
+ onCreateOption: handleCreateOption,
187
+ value: selectedValue || undefined,
188
+ placeholder: placeholderText,
189
+ onChange: handleChange,
190
+ onClear: handleClear,
191
+ creatable: true,
192
+ children: comboboxOptions
193
+ }, section),
194
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
195
+ ]
196
+ });
197
+ };
198
+
199
+ exports.FolderSelect = FolderSelect;
200
+ //# sourceMappingURL=FolderSelect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FolderSelect.js","sources":["../../../admin/src/components/FolderSelect.tsx"],"sourcesContent":["import { ReactNode, useMemo } from 'react';\n\nimport { Combobox, ComboboxOption, Field } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nimport { useDataManager } from './DataManager/useDataManager';\nimport { findSiblingFolderByName, sectionKeyForKind } from './DataManager/utils/contentStructure';\nimport { useFormModalNavigation } from './FormModalNavigation/useFormModalNavigation';\n\nimport type { FormChangeHandler, IntlLabel } from '../types';\nimport type { ContentStructureGroup, FolderSelection } from './DataManager/utils/contentStructure';\nimport type { Struct } from '@strapi/types';\n\ninterface FolderSelectProps {\n onChange: FormChangeHandler<FolderSelection | undefined, 'content-type-folder-select'>;\n modifiedData?: { kind?: Struct.ContentTypeKind };\n value?: FolderSelection;\n placeholder?: IntlLabel;\n error?: string | null;\n isCreating?: boolean;\n intlLabel: IntlLabel;\n targetUid?: string;\n name: string;\n}\n\ntype FolderOption = { id: string; label: string };\n\n/**\n * Flatten a section's (flat) folder groups into a list of full folder path strings.\n */\nconst buildFolderOptions = (groups: ContentStructureGroup[]): FolderOption[] => {\n const groupById = new Map(groups.map((group) => [group.id, group]));\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 options: FolderOption[] = [];\n\n const walk = (group: ContentStructureGroup, ancestors: string[]) => {\n const path = [...ancestors, group.name];\n options.push({ id: group.id, label: path.join(' / ') });\n\n for (const child of group.children) {\n if (child.type !== 'group') {\n continue;\n }\n\n const subGroup = groupById.get(child.id);\n\n if (subGroup) {\n walk(subGroup, path);\n }\n }\n };\n\n groups\n .filter((group) => {\n return !childGroupIds.has(group.id);\n })\n .forEach((root) => {\n return walk(root, []);\n });\n\n return options;\n};\n\nexport const FolderSelect = ({\n error = null,\n modifiedData,\n placeholder,\n isCreating,\n intlLabel,\n targetUid,\n onChange,\n value,\n name,\n}: FolderSelectProps) => {\n const { kind: modalKind } = useFormModalNavigation();\n const { contentStructure } = useDataManager();\n const { formatMessage } = useIntl();\n\n const kind = (modifiedData?.kind ?? modalKind ?? 'collectionType') as Struct.ContentTypeKind;\n const section = sectionKeyForKind(kind);\n const groups = contentStructure.sections[section].groups;\n\n const folderOptions = useMemo(() => {\n return buildFolderOptions(groups);\n }, [groups]);\n const knownFolderIds = useMemo(() => {\n return new Set(folderOptions.map((option) => option.id));\n }, [folderOptions]);\n\n const currentFolderId = useMemo(() => {\n if (isCreating || !targetUid) {\n return null;\n }\n\n const owner = groups.find((group) => {\n return group.children.some((child) => {\n return child.type === 'contentType' && child.uid === targetUid;\n });\n });\n\n return owner?.id ?? null;\n }, [groups, isCreating, targetUid]);\n\n const change = (next: FolderSelection | undefined) => {\n onChange({ target: { name, value: next, type: 'content-type-folder-select' } });\n };\n\n const changeToFolderId = (id: string) => {\n change(id === currentFolderId ? undefined : { targetGroupId: id });\n };\n\n const selectTypedName = (typed: string) => {\n const trimmed = typed.trim();\n\n if (!trimmed) {\n return;\n }\n\n const existing = findSiblingFolderByName(groups, null, trimmed);\n\n if (existing) {\n changeToFolderId(existing.id);\n return;\n }\n\n change({ newFolderName: trimmed });\n };\n\n const selectedValue = (() => {\n if (value === undefined) {\n return currentFolderId ?? '';\n }\n\n if ('newFolderName' in value) {\n return value.newFolderName;\n }\n\n return value.targetGroupId ?? '';\n })();\n\n const selectedLabel = (() => {\n if (value && 'newFolderName' in value) {\n return value.newFolderName;\n }\n return folderOptions.find(({ id }) => id === selectedValue)?.label ?? '';\n })();\n\n const handleChange = (nextValue?: string) => {\n if (!nextValue) {\n return;\n }\n\n if (knownFolderIds.has(nextValue)) {\n changeToFolderId(nextValue);\n return;\n }\n\n selectTypedName(nextValue);\n };\n\n const handleCreateOption = (nextValue?: string) => {\n if (!nextValue) {\n return;\n }\n\n selectTypedName(nextValue);\n };\n\n const handleClear = () => {\n // In edit an explicit clear ungroups the content type. In create it just returns the field to its \"no folder\" default.\n change(isCreating ? undefined : { targetGroupId: null });\n };\n\n const errorMessage = error ? formatMessage({ id: error, defaultMessage: error }) : '';\n const placeholderText = placeholder ? formatMessage(placeholder) : undefined;\n const label = formatMessage(intlLabel);\n\n const comboboxOptions = useMemo(() => {\n const options: ReactNode[] = [];\n\n for (const option of folderOptions) {\n options.push(\n <ComboboxOption key={option.id} value={option.id}>\n {option.label}\n </ComboboxOption>\n );\n }\n\n // A new folder name typed into the combobox doesn't exist as a group yet, so we append it as its own option value.\n if (selectedValue && !knownFolderIds.has(selectedValue)) {\n options.push(\n <ComboboxOption key={`new:${selectedValue}`} value={selectedValue}>\n {selectedValue}\n </ComboboxOption>\n );\n }\n\n return options;\n }, [folderOptions, knownFolderIds, selectedValue]);\n\n return (\n <Field.Root error={errorMessage} name={name}>\n <Field.Label>{label}</Field.Label>\n <Combobox\n key={section} // This forces the input to regenerate its defaultTextValue when the kind changes.\n defaultTextValue={selectedLabel || undefined}\n onCreateOption={handleCreateOption}\n value={selectedValue || undefined}\n placeholder={placeholderText}\n onChange={handleChange}\n onClear={handleClear}\n creatable\n >\n {comboboxOptions}\n </Combobox>\n <Field.Error />\n </Field.Root>\n );\n};\n"],"names":["buildFolderOptions","groups","groupById","Map","map","group","id","childGroupIds","Set","child","children","type","add","options","walk","ancestors","path","name","push","label","join","subGroup","get","filter","has","forEach","root","FolderSelect","error","modifiedData","placeholder","isCreating","intlLabel","targetUid","onChange","value","kind","modalKind","useFormModalNavigation","contentStructure","useDataManager","formatMessage","useIntl","section","sectionKeyForKind","sections","folderOptions","useMemo","knownFolderIds","option","currentFolderId","owner","find","some","uid","change","next","target","changeToFolderId","undefined","targetGroupId","selectTypedName","typed","trimmed","trim","existing","findSiblingFolderByName","newFolderName","selectedValue","selectedLabel","handleChange","nextValue","handleCreateOption","handleClear","errorMessage","defaultMessage","placeholderText","comboboxOptions","_jsx","ComboboxOption","_jsxs","Field","Root","Label","Combobox","defaultTextValue","onCreateOption","onClear","creatable","Error"],"mappings":"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"}