@strapi/upload 5.50.2 → 5.51.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/dist/admin/future/pages/Assets/AssetsPage.js +201 -137
  2. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  3. package/dist/admin/future/pages/Assets/AssetsPage.mjs +202 -138
  4. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  5. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +161 -30
  6. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  7. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +163 -32
  8. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  9. package/dist/admin/future/pages/Assets/components/AssetsTable.js +222 -59
  10. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  11. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +223 -60
  12. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  13. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +252 -0
  14. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -0
  15. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +250 -0
  16. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -0
  17. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +209 -0
  18. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -0
  19. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +207 -0
  20. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -0
  21. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +111 -36
  22. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  23. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +113 -38
  24. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  25. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js +120 -13
  26. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs +120 -13
  28. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +71 -0
  30. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -0
  31. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +69 -0
  32. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -0
  33. package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js +34 -0
  34. package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js.map +1 -0
  35. package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs +32 -0
  36. package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs.map +1 -0
  37. package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js +15 -0
  38. package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs +13 -1
  40. package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +19 -25
  42. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +19 -25
  44. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js +35 -0
  46. package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js.map +1 -0
  47. package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs +33 -0
  48. package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs.map +1 -0
  49. package/dist/admin/future/pages/Assets/components/EmptyState.js +63 -0
  50. package/dist/admin/future/pages/Assets/components/EmptyState.js.map +1 -0
  51. package/dist/admin/future/pages/Assets/components/EmptyState.mjs +61 -0
  52. package/dist/admin/future/pages/Assets/components/EmptyState.mjs.map +1 -0
  53. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +120 -22
  54. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +121 -23
  56. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js +22 -0
  58. package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js.map +1 -0
  59. package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs +19 -0
  60. package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs.map +1 -0
  61. package/dist/admin/future/pages/Assets/components/SortMenu.js +165 -0
  62. package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -0
  63. package/dist/admin/future/pages/Assets/components/SortMenu.mjs +163 -0
  64. package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -0
  65. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +59 -0
  66. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -0
  67. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +55 -0
  68. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -0
  69. package/dist/admin/future/pages/Assets/hooks/useListSort.js +124 -0
  70. package/dist/admin/future/pages/Assets/hooks/useListSort.js.map +1 -0
  71. package/dist/admin/future/pages/Assets/hooks/useListSort.mjs +120 -0
  72. package/dist/admin/future/pages/Assets/hooks/useListSort.mjs.map +1 -0
  73. package/dist/admin/future/pages/Assets/utils/listQueryKey.js +29 -0
  74. package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -0
  75. package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +27 -0
  76. package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -0
  77. package/dist/admin/future/pages/Assets/utils/mergeMixedList.js +84 -0
  78. package/dist/admin/future/pages/Assets/utils/mergeMixedList.js.map +1 -0
  79. package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs +81 -0
  80. package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs.map +1 -0
  81. package/dist/admin/future/pages/Assets/utils/selection.js +104 -0
  82. package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -0
  83. package/dist/admin/future/pages/Assets/utils/selection.mjs +94 -0
  84. package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -0
  85. package/dist/admin/future/services/assets.js +33 -1
  86. package/dist/admin/future/services/assets.js.map +1 -1
  87. package/dist/admin/future/services/assets.mjs +33 -2
  88. package/dist/admin/future/services/assets.mjs.map +1 -1
  89. package/dist/admin/future/services/folders.js +3 -3
  90. package/dist/admin/future/services/folders.js.map +1 -1
  91. package/dist/admin/future/services/folders.mjs +3 -3
  92. package/dist/admin/future/services/folders.mjs.map +1 -1
  93. package/dist/admin/future/services/uploadFileViaXHR.js +23 -2
  94. package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -1
  95. package/dist/admin/future/services/uploadFileViaXHR.mjs +23 -2
  96. package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -1
  97. package/dist/admin/future/utils/buildDragSetFromSelection.js +33 -0
  98. package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -0
  99. package/dist/admin/future/utils/buildDragSetFromSelection.mjs +31 -0
  100. package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -0
  101. package/dist/admin/future/utils/canDropItemOnFolder.js +4 -0
  102. package/dist/admin/future/utils/canDropItemOnFolder.js.map +1 -1
  103. package/dist/admin/future/utils/canDropItemOnFolder.mjs +4 -0
  104. package/dist/admin/future/utils/canDropItemOnFolder.mjs.map +1 -1
  105. package/dist/admin/future/utils/flattenFolderStructure.js +28 -0
  106. package/dist/admin/future/utils/flattenFolderStructure.js.map +1 -0
  107. package/dist/admin/future/utils/flattenFolderStructure.mjs +26 -0
  108. package/dist/admin/future/utils/flattenFolderStructure.mjs.map +1 -0
  109. package/dist/admin/future/utils/formatMoveSuccessMessage.js +19 -0
  110. package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -0
  111. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +17 -0
  112. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -0
  113. package/dist/admin/future/utils/getFolderLabel.js +31 -0
  114. package/dist/admin/future/utils/getFolderLabel.js.map +1 -0
  115. package/dist/admin/future/utils/getFolderLabel.mjs +29 -0
  116. package/dist/admin/future/utils/getFolderLabel.mjs.map +1 -0
  117. package/dist/admin/hooks/useUpload.js +15 -9
  118. package/dist/admin/hooks/useUpload.js.map +1 -1
  119. package/dist/admin/hooks/useUpload.mjs +17 -11
  120. package/dist/admin/hooks/useUpload.mjs.map +1 -1
  121. package/dist/admin/src/future/pages/Assets/components/AssetsGrid.d.ts +1 -1
  122. package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +7 -1
  123. package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +1 -0
  124. package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +13 -0
  125. package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +2 -0
  126. package/dist/admin/src/future/pages/Assets/components/Dnd/DragOverlayChip.d.ts +2 -2
  127. package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +17 -0
  128. package/dist/admin/src/future/pages/Assets/components/Dnd/computeValidDropTargets.d.ts +7 -0
  129. package/dist/admin/src/future/pages/Assets/components/Dnd/dndIds.d.ts +3 -0
  130. package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +3 -1
  131. package/dist/admin/src/future/pages/Assets/components/Dnd/useFolderTreeDroppable.d.ts +19 -0
  132. package/dist/admin/src/future/pages/Assets/components/EmptyState.d.ts +11 -0
  133. package/dist/admin/src/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.d.ts +6 -0
  134. package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +17 -0
  135. package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +39 -0
  136. package/dist/admin/src/future/pages/Assets/hooks/useListSort.d.ts +47 -0
  137. package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +30 -0
  138. package/dist/admin/src/future/pages/Assets/utils/mergeMixedList.d.ts +40 -0
  139. package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +50 -0
  140. package/dist/admin/src/future/services/assets.d.ts +4 -1
  141. package/dist/admin/src/future/services/folders.d.ts +4 -1
  142. package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +1 -17
  143. package/dist/admin/src/future/types/dnd.d.ts +5 -0
  144. package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +11 -0
  145. package/dist/admin/src/future/utils/canDropItemOnFolder.d.ts +1 -1
  146. package/dist/admin/src/future/utils/flattenFolderStructure.d.ts +18 -0
  147. package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +14 -0
  148. package/dist/admin/src/future/utils/getFolderLabel.d.ts +11 -0
  149. package/dist/admin/translations/en.json.js +36 -0
  150. package/dist/admin/translations/en.json.js.map +1 -1
  151. package/dist/admin/translations/en.json.mjs +36 -0
  152. package/dist/admin/translations/en.json.mjs.map +1 -1
  153. package/dist/shared/contracts/folders.d.ts +1 -1
  154. package/package.json +8 -8
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Flattens the nested folder structure (`/upload/folder-structure`) into a
5
+ * depth-first list of `{ id, label }` options where the label carries the full
6
+ * ancestry path. Used by destination selects so folders sharing a name stay
7
+ * distinguishable ("About / Images" vs "Tech / Images"), with children listed
8
+ * right under their parent.
9
+ *
10
+ * `excludeSubtreeIds` prunes a folder *and everything below it* in the same
11
+ * single pass — used to drop the selected folders (and their descendants) from
12
+ * bulk-move destinations without re-walking the tree per option.
13
+ */ const flattenFolderStructure = (nodes, excludeSubtreeIds = new Set(), parentLabel = '')=>nodes.flatMap((node)=>{
14
+ if (node.id == null || excludeSubtreeIds.has(node.id)) {
15
+ return [];
16
+ }
17
+ const label = parentLabel ? `${parentLabel} / ${node.name ?? ''}` : node.name ?? '';
18
+ return [
19
+ {
20
+ id: node.id,
21
+ label
22
+ },
23
+ ...flattenFolderStructure(node.children ?? [], excludeSubtreeIds, label)
24
+ ];
25
+ });
26
+
27
+ exports.flattenFolderStructure = flattenFolderStructure;
28
+ //# sourceMappingURL=flattenFolderStructure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flattenFolderStructure.js","sources":["../../../../admin/src/future/utils/flattenFolderStructure.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\n\nexport interface FlatFolderOption {\n id: number;\n /** Full ancestry label, e.g. \"About / Images\". */\n label: string;\n}\n\n/**\n * Flattens the nested folder structure (`/upload/folder-structure`) into a\n * depth-first list of `{ id, label }` options where the label carries the full\n * ancestry path. Used by destination selects so folders sharing a name stay\n * distinguishable (\"About / Images\" vs \"Tech / Images\"), with children listed\n * right under their parent.\n *\n * `excludeSubtreeIds` prunes a folder *and everything below it* in the same\n * single pass — used to drop the selected folders (and their descendants) from\n * bulk-move destinations without re-walking the tree per option.\n */\nexport const flattenFolderStructure = (\n nodes: FolderNode[],\n excludeSubtreeIds: ReadonlySet<number> = new Set(),\n parentLabel = ''\n): FlatFolderOption[] =>\n nodes.flatMap((node) => {\n if (node.id == null || excludeSubtreeIds.has(node.id)) {\n return [];\n }\n\n const label = parentLabel ? `${parentLabel} / ${node.name ?? ''}` : (node.name ?? '');\n\n return [\n { id: node.id, label },\n ...flattenFolderStructure(node.children ?? [], excludeSubtreeIds, label),\n ];\n });\n"],"names":["flattenFolderStructure","nodes","excludeSubtreeIds","Set","parentLabel","flatMap","node","id","has","label","name","children"],"mappings":";;AAQA;;;;;;;;;;AAUC,IACM,MAAMA,sBAAAA,GAAyB,CACpCC,OACAC,iBAAAA,GAAyC,IAAIC,GAAAA,EAAK,EAClDC,cAAc,EAAE,GAEhBH,KAAAA,CAAMI,OAAO,CAAC,CAACC,IAAAA,GAAAA;QACb,IAAIA,IAAAA,CAAKC,EAAE,IAAI,IAAA,IAAQL,kBAAkBM,GAAG,CAACF,IAAAA,CAAKC,EAAE,CAAA,EAAG;AACrD,YAAA,OAAO,EAAE;AACX,QAAA;AAEA,QAAA,MAAME,KAAAA,GAAQL,WAAAA,GAAc,CAAA,EAAGA,WAAAA,CAAY,GAAG,EAAEE,IAAAA,CAAKI,IAAI,IAAI,EAAA,CAAA,CAAI,GAAIJ,IAAAA,CAAKI,IAAI,IAAI,EAAA;QAElF,OAAO;AACL,YAAA;AAAEH,gBAAAA,EAAAA,EAAID,KAAKC,EAAE;AAAEE,gBAAAA;AAAM,aAAA;AAClBT,YAAAA,GAAAA,sBAAAA,CAAuBM,IAAAA,CAAKK,QAAQ,IAAI,EAAE,EAAET,iBAAAA,EAAmBO,KAAAA;AACnE,SAAA;IACH,CAAA;;;;"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Flattens the nested folder structure (`/upload/folder-structure`) into a
3
+ * depth-first list of `{ id, label }` options where the label carries the full
4
+ * ancestry path. Used by destination selects so folders sharing a name stay
5
+ * distinguishable ("About / Images" vs "Tech / Images"), with children listed
6
+ * right under their parent.
7
+ *
8
+ * `excludeSubtreeIds` prunes a folder *and everything below it* in the same
9
+ * single pass — used to drop the selected folders (and their descendants) from
10
+ * bulk-move destinations without re-walking the tree per option.
11
+ */ const flattenFolderStructure = (nodes, excludeSubtreeIds = new Set(), parentLabel = '')=>nodes.flatMap((node)=>{
12
+ if (node.id == null || excludeSubtreeIds.has(node.id)) {
13
+ return [];
14
+ }
15
+ const label = parentLabel ? `${parentLabel} / ${node.name ?? ''}` : node.name ?? '';
16
+ return [
17
+ {
18
+ id: node.id,
19
+ label
20
+ },
21
+ ...flattenFolderStructure(node.children ?? [], excludeSubtreeIds, label)
22
+ ];
23
+ });
24
+
25
+ export { flattenFolderStructure };
26
+ //# sourceMappingURL=flattenFolderStructure.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flattenFolderStructure.mjs","sources":["../../../../admin/src/future/utils/flattenFolderStructure.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\n\nexport interface FlatFolderOption {\n id: number;\n /** Full ancestry label, e.g. \"About / Images\". */\n label: string;\n}\n\n/**\n * Flattens the nested folder structure (`/upload/folder-structure`) into a\n * depth-first list of `{ id, label }` options where the label carries the full\n * ancestry path. Used by destination selects so folders sharing a name stay\n * distinguishable (\"About / Images\" vs \"Tech / Images\"), with children listed\n * right under their parent.\n *\n * `excludeSubtreeIds` prunes a folder *and everything below it* in the same\n * single pass — used to drop the selected folders (and their descendants) from\n * bulk-move destinations without re-walking the tree per option.\n */\nexport const flattenFolderStructure = (\n nodes: FolderNode[],\n excludeSubtreeIds: ReadonlySet<number> = new Set(),\n parentLabel = ''\n): FlatFolderOption[] =>\n nodes.flatMap((node) => {\n if (node.id == null || excludeSubtreeIds.has(node.id)) {\n return [];\n }\n\n const label = parentLabel ? `${parentLabel} / ${node.name ?? ''}` : (node.name ?? '');\n\n return [\n { id: node.id, label },\n ...flattenFolderStructure(node.children ?? [], excludeSubtreeIds, label),\n ];\n });\n"],"names":["flattenFolderStructure","nodes","excludeSubtreeIds","Set","parentLabel","flatMap","node","id","has","label","name","children"],"mappings":"AAQA;;;;;;;;;;AAUC,IACM,MAAMA,sBAAAA,GAAyB,CACpCC,OACAC,iBAAAA,GAAyC,IAAIC,GAAAA,EAAK,EAClDC,cAAc,EAAE,GAEhBH,KAAAA,CAAMI,OAAO,CAAC,CAACC,IAAAA,GAAAA;QACb,IAAIA,IAAAA,CAAKC,EAAE,IAAI,IAAA,IAAQL,kBAAkBM,GAAG,CAACF,IAAAA,CAAKC,EAAE,CAAA,EAAG;AACrD,YAAA,OAAO,EAAE;AACX,QAAA;AAEA,QAAA,MAAME,KAAAA,GAAQL,WAAAA,GAAc,CAAA,EAAGA,WAAAA,CAAY,GAAG,EAAEE,IAAAA,CAAKI,IAAI,IAAI,EAAA,CAAA,CAAI,GAAIJ,IAAAA,CAAKI,IAAI,IAAI,EAAA;QAElF,OAAO;AACL,YAAA;AAAEH,gBAAAA,EAAAA,EAAID,KAAKC,EAAE;AAAEE,gBAAAA;AAAM,aAAA;AAClBT,YAAAA,GAAAA,sBAAAA,CAAuBM,IAAAA,CAAKK,QAAQ,IAAI,EAAE,EAAET,iBAAAA,EAAmBO,KAAAA;AACnE,SAAA;IACH,CAAA;;;;"}
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ var translations = require('./translations.js');
4
+
5
+ /**
6
+ * Single source of truth for the bulk-move success wording. Both the DnD drop
7
+ * and the BulkMoveDialog format the toast (and DnD live-region announcement)
8
+ * through here so the two paths stay identical for the same move.
9
+ */ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>formatMessage({
10
+ id: translations.getTranslationKey('list.bulk-actions.move.success'),
11
+ defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}'
12
+ }, {
13
+ count,
14
+ source,
15
+ destination
16
+ });
17
+
18
+ exports.formatMoveSuccessMessage = formatMoveSuccessMessage;
19
+ //# sourceMappingURL=formatMoveSuccessMessage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatMoveSuccessMessage.js","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n source: string;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string =>\n formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source, destination }\n );\n"],"names":["formatMoveSuccessMessage","formatMessage","count","source","destination","id","getTranslationKey","defaultMessage"],"mappings":";;;;AAWA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,WAAW,EACW,GACtBH,aAAAA,CACE;AACEI,QAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,gCAAA,CAAA;QACtBC,cAAAA,EACE;KACJ,EACA;AAAEL,QAAAA,KAAAA;AAAOC,QAAAA,MAAAA;AAAQC,QAAAA;KAAY;;;;"}
@@ -0,0 +1,17 @@
1
+ import { getTranslationKey } from './translations.mjs';
2
+
3
+ /**
4
+ * Single source of truth for the bulk-move success wording. Both the DnD drop
5
+ * and the BulkMoveDialog format the toast (and DnD live-region announcement)
6
+ * through here so the two paths stay identical for the same move.
7
+ */ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>formatMessage({
8
+ id: getTranslationKey('list.bulk-actions.move.success'),
9
+ defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}'
10
+ }, {
11
+ count,
12
+ source,
13
+ destination
14
+ });
15
+
16
+ export { formatMoveSuccessMessage };
17
+ //# sourceMappingURL=formatMoveSuccessMessage.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatMoveSuccessMessage.mjs","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n source: string;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string =>\n formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source, destination }\n );\n"],"names":["formatMoveSuccessMessage","formatMessage","count","source","destination","id","getTranslationKey","defaultMessage"],"mappings":";;AAWA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,WAAW,EACW,GACtBH,aAAAA,CACE;AACEI,QAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,gCAAA,CAAA;QACtBC,cAAAA,EACE;KACJ,EACA;AAAEL,QAAAA,KAAAA;AAAOC,QAAAA,MAAAA;AAAQC,QAAAA;KAAY;;;;"}
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Leaf display name for a folder id. `null` (root) → the Media Library label.
5
+ * Used by both the DnD success toast and the dialog success toast so the two
6
+ * paths name folders identically. Ancestry paths belong only in the dialog's
7
+ * select options (see `flattenFolderStructure`).
8
+ *
9
+ * `id === null` returns `rootLabel`; an unknown id falls back to `rootLabel`
10
+ * too (defensive — mirrors the dialog's previous `?? rootLabel`).
11
+ */ const getFolderLabel = (folderStructure, id, rootLabel)=>{
12
+ if (id === null) {
13
+ return rootLabel;
14
+ }
15
+ const findNode = (nodes)=>{
16
+ for (const node of nodes){
17
+ if (node.id === id) {
18
+ return node;
19
+ }
20
+ const found = findNode(node.children ?? []);
21
+ if (found) {
22
+ return found;
23
+ }
24
+ }
25
+ return null;
26
+ };
27
+ return findNode(folderStructure)?.name ?? rootLabel;
28
+ };
29
+
30
+ exports.getFolderLabel = getFolderLabel;
31
+ //# sourceMappingURL=getFolderLabel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getFolderLabel.js","sources":["../../../../admin/src/future/utils/getFolderLabel.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\n\n/**\n * Leaf display name for a folder id. `null` (root) → the Media Library label.\n * Used by both the DnD success toast and the dialog success toast so the two\n * paths name folders identically. Ancestry paths belong only in the dialog's\n * select options (see `flattenFolderStructure`).\n *\n * `id === null` returns `rootLabel`; an unknown id falls back to `rootLabel`\n * too (defensive — mirrors the dialog's previous `?? rootLabel`).\n */\nexport const getFolderLabel = (\n folderStructure: FolderNode[],\n id: number | null,\n rootLabel: string\n): string => {\n if (id === null) {\n return rootLabel;\n }\n\n const findNode = (nodes: FolderNode[]): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children ?? []);\n if (found) {\n return found;\n }\n }\n\n return null;\n };\n\n return findNode(folderStructure)?.name ?? rootLabel;\n};\n"],"names":["getFolderLabel","folderStructure","id","rootLabel","findNode","nodes","node","found","children","name"],"mappings":";;AAEA;;;;;;;;AAQC,IACM,MAAMA,cAAAA,GAAiB,CAC5BC,iBACAC,EAAAA,EACAC,SAAAA,GAAAA;AAEA,IAAA,IAAID,OAAO,IAAA,EAAM;QACf,OAAOC,SAAAA;AACT,IAAA;AAEA,IAAA,MAAMC,WAAW,CAACC,KAAAA,GAAAA;QAChB,KAAK,MAAMC,QAAQD,KAAAA,CAAO;YACxB,IAAIC,IAAAA,CAAKJ,EAAE,KAAKA,EAAAA,EAAI;gBAClB,OAAOI,IAAAA;AACT,YAAA;AAEA,YAAA,MAAMC,KAAAA,GAAQH,QAAAA,CAASE,IAAAA,CAAKE,QAAQ,IAAI,EAAE,CAAA;AAC1C,YAAA,IAAID,KAAAA,EAAO;gBACT,OAAOA,KAAAA;AACT,YAAA;AACF,QAAA;QAEA,OAAO,IAAA;AACT,IAAA,CAAA;IAEA,OAAOH,QAAAA,CAASH,kBAAkBQ,IAAAA,IAAQN,SAAAA;AAC5C;;;;"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Leaf display name for a folder id. `null` (root) → the Media Library label.
3
+ * Used by both the DnD success toast and the dialog success toast so the two
4
+ * paths name folders identically. Ancestry paths belong only in the dialog's
5
+ * select options (see `flattenFolderStructure`).
6
+ *
7
+ * `id === null` returns `rootLabel`; an unknown id falls back to `rootLabel`
8
+ * too (defensive — mirrors the dialog's previous `?? rootLabel`).
9
+ */ const getFolderLabel = (folderStructure, id, rootLabel)=>{
10
+ if (id === null) {
11
+ return rootLabel;
12
+ }
13
+ const findNode = (nodes)=>{
14
+ for (const node of nodes){
15
+ if (node.id === id) {
16
+ return node;
17
+ }
18
+ const found = findNode(node.children ?? []);
19
+ if (found) {
20
+ return found;
21
+ }
22
+ }
23
+ return null;
24
+ };
25
+ return findNode(folderStructure)?.name ?? rootLabel;
26
+ };
27
+
28
+ export { getFolderLabel };
29
+ //# sourceMappingURL=getFolderLabel.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getFolderLabel.mjs","sources":["../../../../admin/src/future/utils/getFolderLabel.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\n\n/**\n * Leaf display name for a folder id. `null` (root) → the Media Library label.\n * Used by both the DnD success toast and the dialog success toast so the two\n * paths name folders identically. Ancestry paths belong only in the dialog's\n * select options (see `flattenFolderStructure`).\n *\n * `id === null` returns `rootLabel`; an unknown id falls back to `rootLabel`\n * too (defensive — mirrors the dialog's previous `?? rootLabel`).\n */\nexport const getFolderLabel = (\n folderStructure: FolderNode[],\n id: number | null,\n rootLabel: string\n): string => {\n if (id === null) {\n return rootLabel;\n }\n\n const findNode = (nodes: FolderNode[]): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children ?? []);\n if (found) {\n return found;\n }\n }\n\n return null;\n };\n\n return findNode(folderStructure)?.name ?? rootLabel;\n};\n"],"names":["getFolderLabel","folderStructure","id","rootLabel","findNode","nodes","node","found","children","name"],"mappings":"AAEA;;;;;;;;AAQC,IACM,MAAMA,cAAAA,GAAiB,CAC5BC,iBACAC,EAAAA,EACAC,SAAAA,GAAAA;AAEA,IAAA,IAAID,OAAO,IAAA,EAAM;QACf,OAAOC,SAAAA;AACT,IAAA;AAEA,IAAA,MAAMC,WAAW,CAACC,KAAAA,GAAAA;QAChB,KAAK,MAAMC,QAAQD,KAAAA,CAAO;YACxB,IAAIC,IAAAA,CAAKJ,EAAE,KAAKA,EAAAA,EAAI;gBAClB,OAAOI,IAAAA;AACT,YAAA;AAEA,YAAA,MAAMC,KAAAA,GAAQH,QAAAA,CAASE,IAAAA,CAAKE,QAAQ,IAAI,EAAE,CAAA;AAC1C,YAAA,IAAID,KAAAA,EAAO;gBACT,OAAOA,KAAAA;AACT,YAAA;AACF,QAAA;QAEA,OAAO,IAAA;AACT,IAAA,CAAA;IAEA,OAAOH,QAAAA,CAASH,kBAAkBQ,IAAAA,IAAQN,SAAAA;AAC5C;;;;"}
@@ -4,6 +4,7 @@ var React = require('react');
4
4
  var strapiAdmin = require('@strapi/admin/strapi-admin');
5
5
  var reactQuery = require('react-query');
6
6
  var reactRedux = require('react-redux');
7
+ var uploadFileViaXHR = require('../future/services/uploadFileViaXHR.js');
7
8
  var pluginId = require('../pluginId.js');
8
9
 
9
10
  function _interopNamespace(e) {
@@ -27,7 +28,7 @@ function _interopNamespace(e) {
27
28
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
28
29
 
29
30
  const endpoint = `/${pluginId.pluginId}`;
30
- const uploadAssets = (assets, folderId, signal, onProgress, post)=>{
31
+ const uploadAssets = (assets, folderId, signal, onProgress, token)=>{
31
32
  const assetsArray = Array.isArray(assets) ? assets : [
32
33
  assets
33
34
  ];
@@ -48,12 +49,13 @@ const uploadAssets = (assets, folderId, signal, onProgress, post)=>{
48
49
  }));
49
50
  });
50
51
  /**
51
- * onProgress is not possible using native fetch
52
- * need to look into an alternative to make it work
53
- * perhaps using xhr like Axios does
54
- */ return post(endpoint, formData, {
55
- signal
56
- }).then((res)=>res.data);
52
+ * Native fetch cannot report upload progress, so upload via the shared XHR
53
+ * service and surface its byte-level progress as a 0-100 percentage.
54
+ */ return uploadFileViaXHR.uploadFileViaXHR(`${window.strapi.backendURL}${endpoint}`, token, formData, signal, (loaded, total)=>{
55
+ if (total > 0) {
56
+ onProgress(Math.round(loaded / total * 100));
57
+ }
58
+ });
57
59
  };
58
60
  const useUpload = ()=>{
59
61
  const dispatch = reactRedux.useDispatch();
@@ -61,10 +63,14 @@ const useUpload = ()=>{
61
63
  const queryClient = reactQuery.useQueryClient();
62
64
  const abortController = new AbortController();
63
65
  const signal = abortController.signal;
64
- const { post } = strapiAdmin.useFetchClient();
66
+ const store = reactRedux.useStore();
65
67
  const mutation = reactQuery.useMutation(({ assets, folderId })=>{
66
- return uploadAssets(assets, folderId, signal, setProgress, post);
68
+ return uploadAssets(assets, folderId, signal, setProgress, store.getState().admin_app?.token);
67
69
  }, {
70
+ mutationKey: [
71
+ pluginId.pluginId,
72
+ 'upload'
73
+ ],
68
74
  onSuccess () {
69
75
  queryClient.refetchQueries([
70
76
  pluginId.pluginId,
@@ -1 +1 @@
1
- {"version":3,"file":"useUpload.js","sources":["../../../admin/src/hooks/useUpload.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { useFetchClient, FetchClient, adminApi } from '@strapi/admin/strapi-admin';\nimport { useMutation, useQueryClient } from 'react-query';\nimport { useDispatch } from 'react-redux';\n\nimport { File, RawFile, CreateFile } from '../../../shared/contracts/files';\nimport { pluginId } from '../pluginId';\n\nconst endpoint = `/${pluginId}`;\n\ninterface Asset extends Omit<File, 'id' | 'hash'> {\n rawFile?: RawFile;\n id?: File['id'];\n hash?: File['hash'];\n}\n\nconst uploadAssets = (\n assets: Asset | Asset[],\n folderId: number | null,\n signal: AbortSignal,\n onProgress: (progress: number) => void,\n post: FetchClient['post']\n): Promise<CreateFile.Response['data']> => {\n const assetsArray = Array.isArray(assets) ? assets : [assets];\n const formData = new FormData();\n\n // Add all files to the form data\n assetsArray.forEach((asset) => {\n if (asset.rawFile) {\n formData.append('files', asset.rawFile);\n }\n });\n\n // Add each fileInfo as a separate stringified field\n assetsArray.forEach((asset) => {\n formData.append(\n 'fileInfo',\n JSON.stringify({\n name: asset.name,\n caption: asset.caption,\n alternativeText: asset.alternativeText,\n folder: folderId,\n })\n );\n });\n\n /**\n * onProgress is not possible using native fetch\n * need to look into an alternative to make it work\n * perhaps using xhr like Axios does\n */\n return post<CreateFile.Response['data'], CreateFile.Request['body']>(endpoint, formData, {\n signal,\n }).then((res) => res.data);\n};\n\nexport const useUpload = () => {\n const dispatch = useDispatch();\n const [progress, setProgress] = React.useState(0);\n const queryClient = useQueryClient();\n const abortController = new AbortController();\n const signal = abortController.signal;\n const { post } = useFetchClient();\n\n const mutation = useMutation<\n CreateFile.Response['data'],\n CreateFile.Response['error'],\n { assets: Asset | Asset[]; folderId: number | null }\n >(\n ({ assets, folderId }) => {\n return uploadAssets(assets, folderId, signal, setProgress, post);\n },\n {\n onSuccess() {\n queryClient.refetchQueries([pluginId, 'assets'], { active: true });\n queryClient.refetchQueries([pluginId, 'asset-count'], { active: true });\n dispatch(adminApi.util.invalidateTags(['HomepageKeyStatistics', 'AiUsage']));\n },\n }\n );\n\n const upload = (assets: Asset | Asset[], folderId: number | null) =>\n mutation.mutateAsync({ assets, folderId });\n\n const cancel = () => abortController.abort();\n\n return {\n upload,\n isLoading: mutation.isLoading,\n cancel,\n error: mutation.error,\n progress,\n status: mutation.status,\n };\n};\n"],"names":["endpoint","pluginId","uploadAssets","assets","folderId","signal","onProgress","post","assetsArray","Array","isArray","formData","FormData","forEach","asset","rawFile","append","JSON","stringify","name","caption","alternativeText","folder","then","res","data","useUpload","dispatch","useDispatch","progress","setProgress","React","useState","queryClient","useQueryClient","abortController","AbortController","useFetchClient","mutation","useMutation","onSuccess","refetchQueries","active","adminApi","util","invalidateTags","upload","mutateAsync","cancel","abort","isLoading","error","status"],"mappings":"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"}
1
+ {"version":3,"file":"useUpload.js","sources":["../../../admin/src/hooks/useUpload.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { adminApi } from '@strapi/admin/strapi-admin';\nimport { useMutation, useQueryClient } from 'react-query';\nimport { useDispatch, useStore } from 'react-redux';\n\nimport { File, RawFile, CreateFile } from '../../../shared/contracts/files';\nimport { uploadFileViaXHR } from '../future/services/uploadFileViaXHR';\nimport { pluginId } from '../pluginId';\n\nconst endpoint = `/${pluginId}`;\n\ninterface Asset extends Omit<File, 'id' | 'hash'> {\n rawFile?: RawFile;\n id?: File['id'];\n hash?: File['hash'];\n}\n\ninterface StoreState {\n admin_app: {\n token?: string | null;\n };\n}\n\nconst uploadAssets = (\n assets: Asset | Asset[],\n folderId: number | null,\n signal: AbortSignal,\n onProgress: (progress: number) => void,\n token: string | null | undefined\n): Promise<CreateFile.Response['data']> => {\n const assetsArray = Array.isArray(assets) ? assets : [assets];\n const formData = new FormData();\n\n // Add all files to the form data\n assetsArray.forEach((asset) => {\n if (asset.rawFile) {\n formData.append('files', asset.rawFile);\n }\n });\n\n // Add each fileInfo as a separate stringified field\n assetsArray.forEach((asset) => {\n formData.append(\n 'fileInfo',\n JSON.stringify({\n name: asset.name,\n caption: asset.caption,\n alternativeText: asset.alternativeText,\n folder: folderId,\n })\n );\n });\n\n /**\n * Native fetch cannot report upload progress, so upload via the shared XHR\n * service and surface its byte-level progress as a 0-100 percentage.\n */\n return uploadFileViaXHR<CreateFile.Response['data']>(\n `${window.strapi.backendURL}${endpoint}`,\n token,\n formData,\n signal,\n (loaded, total) => {\n if (total > 0) {\n onProgress(Math.round((loaded / total) * 100));\n }\n }\n );\n};\n\nexport const useUpload = () => {\n const dispatch = useDispatch();\n const [progress, setProgress] = React.useState(0);\n const queryClient = useQueryClient();\n const abortController = new AbortController();\n const signal = abortController.signal;\n const store = useStore<StoreState>();\n\n const mutation = useMutation<\n CreateFile.Response['data'],\n CreateFile.Response['error'],\n { assets: Asset | Asset[]; folderId: number | null }\n >(\n ({ assets, folderId }) => {\n return uploadAssets(assets, folderId, signal, setProgress, store.getState().admin_app?.token);\n },\n {\n mutationKey: [pluginId, 'upload'],\n onSuccess() {\n queryClient.refetchQueries([pluginId, 'assets'], { active: true });\n queryClient.refetchQueries([pluginId, 'asset-count'], { active: true });\n dispatch(adminApi.util.invalidateTags(['HomepageKeyStatistics', 'AiUsage']));\n },\n }\n );\n\n const upload = (assets: Asset | Asset[], folderId: number | null) =>\n mutation.mutateAsync({ assets, folderId });\n\n const cancel = () => abortController.abort();\n\n return {\n upload,\n isLoading: mutation.isLoading,\n cancel,\n error: mutation.error,\n progress,\n status: mutation.status,\n };\n};\n"],"names":["endpoint","pluginId","uploadAssets","assets","folderId","signal","onProgress","token","assetsArray","Array","isArray","formData","FormData","forEach","asset","rawFile","append","JSON","stringify","name","caption","alternativeText","folder","uploadFileViaXHR","window","strapi","backendURL","loaded","total","Math","round","useUpload","dispatch","useDispatch","progress","setProgress","React","useState","queryClient","useQueryClient","abortController","AbortController","store","useStore","mutation","useMutation","getState","admin_app","mutationKey","onSuccess","refetchQueries","active","adminApi","util","invalidateTags","upload","mutateAsync","cancel","abort","isLoading","error","status"],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  import * as React from 'react';
2
- import { useFetchClient, adminApi } from '@strapi/admin/strapi-admin';
2
+ import { adminApi } from '@strapi/admin/strapi-admin';
3
3
  import { useQueryClient, useMutation } from 'react-query';
4
- import { useDispatch } from 'react-redux';
4
+ import { useDispatch, useStore } from 'react-redux';
5
+ import { uploadFileViaXHR } from '../future/services/uploadFileViaXHR.mjs';
5
6
  import { pluginId } from '../pluginId.mjs';
6
7
 
7
8
  const endpoint = `/${pluginId}`;
8
- const uploadAssets = (assets, folderId, signal, onProgress, post)=>{
9
+ const uploadAssets = (assets, folderId, signal, onProgress, token)=>{
9
10
  const assetsArray = Array.isArray(assets) ? assets : [
10
11
  assets
11
12
  ];
@@ -26,12 +27,13 @@ const uploadAssets = (assets, folderId, signal, onProgress, post)=>{
26
27
  }));
27
28
  });
28
29
  /**
29
- * onProgress is not possible using native fetch
30
- * need to look into an alternative to make it work
31
- * perhaps using xhr like Axios does
32
- */ return post(endpoint, formData, {
33
- signal
34
- }).then((res)=>res.data);
30
+ * Native fetch cannot report upload progress, so upload via the shared XHR
31
+ * service and surface its byte-level progress as a 0-100 percentage.
32
+ */ return uploadFileViaXHR(`${window.strapi.backendURL}${endpoint}`, token, formData, signal, (loaded, total)=>{
33
+ if (total > 0) {
34
+ onProgress(Math.round(loaded / total * 100));
35
+ }
36
+ });
35
37
  };
36
38
  const useUpload = ()=>{
37
39
  const dispatch = useDispatch();
@@ -39,10 +41,14 @@ const useUpload = ()=>{
39
41
  const queryClient = useQueryClient();
40
42
  const abortController = new AbortController();
41
43
  const signal = abortController.signal;
42
- const { post } = useFetchClient();
44
+ const store = useStore();
43
45
  const mutation = useMutation(({ assets, folderId })=>{
44
- return uploadAssets(assets, folderId, signal, setProgress, post);
46
+ return uploadAssets(assets, folderId, signal, setProgress, store.getState().admin_app?.token);
45
47
  }, {
48
+ mutationKey: [
49
+ pluginId,
50
+ 'upload'
51
+ ],
46
52
  onSuccess () {
47
53
  queryClient.refetchQueries([
48
54
  pluginId,
@@ -1 +1 @@
1
- {"version":3,"file":"useUpload.mjs","sources":["../../../admin/src/hooks/useUpload.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { useFetchClient, FetchClient, adminApi } from '@strapi/admin/strapi-admin';\nimport { useMutation, useQueryClient } from 'react-query';\nimport { useDispatch } from 'react-redux';\n\nimport { File, RawFile, CreateFile } from '../../../shared/contracts/files';\nimport { pluginId } from '../pluginId';\n\nconst endpoint = `/${pluginId}`;\n\ninterface Asset extends Omit<File, 'id' | 'hash'> {\n rawFile?: RawFile;\n id?: File['id'];\n hash?: File['hash'];\n}\n\nconst uploadAssets = (\n assets: Asset | Asset[],\n folderId: number | null,\n signal: AbortSignal,\n onProgress: (progress: number) => void,\n post: FetchClient['post']\n): Promise<CreateFile.Response['data']> => {\n const assetsArray = Array.isArray(assets) ? assets : [assets];\n const formData = new FormData();\n\n // Add all files to the form data\n assetsArray.forEach((asset) => {\n if (asset.rawFile) {\n formData.append('files', asset.rawFile);\n }\n });\n\n // Add each fileInfo as a separate stringified field\n assetsArray.forEach((asset) => {\n formData.append(\n 'fileInfo',\n JSON.stringify({\n name: asset.name,\n caption: asset.caption,\n alternativeText: asset.alternativeText,\n folder: folderId,\n })\n );\n });\n\n /**\n * onProgress is not possible using native fetch\n * need to look into an alternative to make it work\n * perhaps using xhr like Axios does\n */\n return post<CreateFile.Response['data'], CreateFile.Request['body']>(endpoint, formData, {\n signal,\n }).then((res) => res.data);\n};\n\nexport const useUpload = () => {\n const dispatch = useDispatch();\n const [progress, setProgress] = React.useState(0);\n const queryClient = useQueryClient();\n const abortController = new AbortController();\n const signal = abortController.signal;\n const { post } = useFetchClient();\n\n const mutation = useMutation<\n CreateFile.Response['data'],\n CreateFile.Response['error'],\n { assets: Asset | Asset[]; folderId: number | null }\n >(\n ({ assets, folderId }) => {\n return uploadAssets(assets, folderId, signal, setProgress, post);\n },\n {\n onSuccess() {\n queryClient.refetchQueries([pluginId, 'assets'], { active: true });\n queryClient.refetchQueries([pluginId, 'asset-count'], { active: true });\n dispatch(adminApi.util.invalidateTags(['HomepageKeyStatistics', 'AiUsage']));\n },\n }\n );\n\n const upload = (assets: Asset | Asset[], folderId: number | null) =>\n mutation.mutateAsync({ assets, folderId });\n\n const cancel = () => abortController.abort();\n\n return {\n upload,\n isLoading: mutation.isLoading,\n cancel,\n error: mutation.error,\n progress,\n status: mutation.status,\n };\n};\n"],"names":["endpoint","pluginId","uploadAssets","assets","folderId","signal","onProgress","post","assetsArray","Array","isArray","formData","FormData","forEach","asset","rawFile","append","JSON","stringify","name","caption","alternativeText","folder","then","res","data","useUpload","dispatch","useDispatch","progress","setProgress","React","useState","queryClient","useQueryClient","abortController","AbortController","useFetchClient","mutation","useMutation","onSuccess","refetchQueries","active","adminApi","util","invalidateTags","upload","mutateAsync","cancel","abort","isLoading","error","status"],"mappings":"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"}
1
+ {"version":3,"file":"useUpload.mjs","sources":["../../../admin/src/hooks/useUpload.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { adminApi } from '@strapi/admin/strapi-admin';\nimport { useMutation, useQueryClient } from 'react-query';\nimport { useDispatch, useStore } from 'react-redux';\n\nimport { File, RawFile, CreateFile } from '../../../shared/contracts/files';\nimport { uploadFileViaXHR } from '../future/services/uploadFileViaXHR';\nimport { pluginId } from '../pluginId';\n\nconst endpoint = `/${pluginId}`;\n\ninterface Asset extends Omit<File, 'id' | 'hash'> {\n rawFile?: RawFile;\n id?: File['id'];\n hash?: File['hash'];\n}\n\ninterface StoreState {\n admin_app: {\n token?: string | null;\n };\n}\n\nconst uploadAssets = (\n assets: Asset | Asset[],\n folderId: number | null,\n signal: AbortSignal,\n onProgress: (progress: number) => void,\n token: string | null | undefined\n): Promise<CreateFile.Response['data']> => {\n const assetsArray = Array.isArray(assets) ? assets : [assets];\n const formData = new FormData();\n\n // Add all files to the form data\n assetsArray.forEach((asset) => {\n if (asset.rawFile) {\n formData.append('files', asset.rawFile);\n }\n });\n\n // Add each fileInfo as a separate stringified field\n assetsArray.forEach((asset) => {\n formData.append(\n 'fileInfo',\n JSON.stringify({\n name: asset.name,\n caption: asset.caption,\n alternativeText: asset.alternativeText,\n folder: folderId,\n })\n );\n });\n\n /**\n * Native fetch cannot report upload progress, so upload via the shared XHR\n * service and surface its byte-level progress as a 0-100 percentage.\n */\n return uploadFileViaXHR<CreateFile.Response['data']>(\n `${window.strapi.backendURL}${endpoint}`,\n token,\n formData,\n signal,\n (loaded, total) => {\n if (total > 0) {\n onProgress(Math.round((loaded / total) * 100));\n }\n }\n );\n};\n\nexport const useUpload = () => {\n const dispatch = useDispatch();\n const [progress, setProgress] = React.useState(0);\n const queryClient = useQueryClient();\n const abortController = new AbortController();\n const signal = abortController.signal;\n const store = useStore<StoreState>();\n\n const mutation = useMutation<\n CreateFile.Response['data'],\n CreateFile.Response['error'],\n { assets: Asset | Asset[]; folderId: number | null }\n >(\n ({ assets, folderId }) => {\n return uploadAssets(assets, folderId, signal, setProgress, store.getState().admin_app?.token);\n },\n {\n mutationKey: [pluginId, 'upload'],\n onSuccess() {\n queryClient.refetchQueries([pluginId, 'assets'], { active: true });\n queryClient.refetchQueries([pluginId, 'asset-count'], { active: true });\n dispatch(adminApi.util.invalidateTags(['HomepageKeyStatistics', 'AiUsage']));\n },\n }\n );\n\n const upload = (assets: Asset | Asset[], folderId: number | null) =>\n mutation.mutateAsync({ assets, folderId });\n\n const cancel = () => abortController.abort();\n\n return {\n upload,\n isLoading: mutation.isLoading,\n cancel,\n error: mutation.error,\n progress,\n status: mutation.status,\n };\n};\n"],"names":["endpoint","pluginId","uploadAssets","assets","folderId","signal","onProgress","token","assetsArray","Array","isArray","formData","FormData","forEach","asset","rawFile","append","JSON","stringify","name","caption","alternativeText","folder","uploadFileViaXHR","window","strapi","backendURL","loaded","total","Math","round","useUpload","dispatch","useDispatch","progress","setProgress","React","useState","queryClient","useQueryClient","abortController","AbortController","store","useStore","mutation","useMutation","getState","admin_app","mutationKey","onSuccess","refetchQueries","active","adminApi","util","invalidateTags","upload","mutateAsync","cancel","abort","isLoading","error","status"],"mappings":"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"}
@@ -5,5 +5,5 @@ interface AssetsGridProps {
5
5
  folders?: Folder[];
6
6
  onAssetItemClick: (assetId: number) => void;
7
7
  }
8
- export declare const AssetsGrid: ({ assets, folders, onAssetItemClick }: AssetsGridProps) => import("react/jsx-runtime").JSX.Element;
8
+ export declare const AssetsGrid: ({ assets, folders, onAssetItemClick }: AssetsGridProps) => import("react/jsx-runtime").JSX.Element | null;
9
9
  export {};
@@ -1,9 +1,15 @@
1
+ import { type MixedItem } from '../utils/mergeMixedList';
1
2
  import type { File } from '../../../../../../shared/contracts/files';
2
3
  import type { Folder } from '../../../../../../shared/contracts/folders';
3
4
  interface AssetsTableProps {
4
5
  assets: File[];
5
6
  folders?: Folder[];
7
+ /**
8
+ * When set ("Folders: Mixed with files"), rows render in this interleaved
9
+ * order instead of folders-first. Range selection follows the same order.
10
+ */
11
+ mixedItems?: MixedItem[] | null;
6
12
  onAssetItemClick: (assetId: number) => void;
7
13
  }
8
- export declare const AssetsTable: ({ assets, folders, onAssetItemClick }: AssetsTableProps) => import("react/jsx-runtime").JSX.Element;
14
+ export declare const AssetsTable: ({ assets, folders, mixedItems, onAssetItemClick, }: AssetsTableProps) => import("react/jsx-runtime").JSX.Element | null;
9
15
  export {};
@@ -0,0 +1 @@
1
+ export declare const BulkActionsBar: () => import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,13 @@
1
+ interface BulkMoveDialogProps {
2
+ open: boolean;
3
+ onClose: () => void;
4
+ }
5
+ /**
6
+ * "Move elements to" modal for the bulk actions bar. Moves the current
7
+ * selection (assets and/or folders) into the picked destination folder via
8
+ * `POST /upload/actions/bulk-move`. The mutation invalidates the asset list,
9
+ * folder list and the sidebar folder-structure, so both panes refresh on
10
+ * success. On failure the modal stays open so the user can retry or cancel.
11
+ */
12
+ export declare const BulkMoveDialog: ({ open, onClose }: BulkMoveDialogProps) => import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -2,6 +2,8 @@ import { type ReactNode } from 'react';
2
2
  interface AssetsDndContextValue {
3
3
  isInternalDragActive: boolean;
4
4
  isMovePending: boolean;
5
+ /** O(1) lookup of whether the current drag set may drop on this destination. */
6
+ isValidDropTarget: (targetFolderId: number | null) => boolean;
5
7
  }
6
8
  export declare const useAssetsDnd: () => AssetsDndContextValue;
7
9
  export declare const useAssetsDndOptional: () => AssetsDndContextValue | null;
@@ -1,6 +1,6 @@
1
1
  import type { DragItemData } from '../../../../types/dnd';
2
2
  interface DragOverlayChipProps {
3
- item: DragItemData;
3
+ items: DragItemData[];
4
4
  }
5
- export declare const DragOverlayChip: ({ item }: DragOverlayChipProps) => import("react/jsx-runtime").JSX.Element;
5
+ export declare const DragOverlayChip: ({ items }: DragOverlayChipProps) => import("react/jsx-runtime").JSX.Element | null;
6
6
  export {};
@@ -0,0 +1,17 @@
1
+ import { type ItemKey } from '../../utils/selection';
2
+ import type { DragItemData } from '../../../../types/dnd';
3
+ export interface DragSet {
4
+ items: DragItemData[];
5
+ /** True when the active item was already selected — membership is the full selection. */
6
+ fromSelection: boolean;
7
+ }
8
+ /**
9
+ * Derive the drag payload once at drag start.
10
+ *
11
+ * - Dragging a selected item moves the complete current selection.
12
+ * - Dragging an unselected item moves only that item.
13
+ *
14
+ * Selection is page-scoped to the current folder, so every item is stamped with
15
+ * `currentFolderId` as its location for move validation.
16
+ */
17
+ export declare const buildDragSet: (activeData: DragItemData, selectedKeys: Set<ItemKey> | undefined, currentFolderId: number | null) => DragSet;
@@ -0,0 +1,7 @@
1
+ import type { FolderNode } from '../../../../../../../shared/contracts/folders';
2
+ import type { DragItemData } from '../../../../types/dnd';
3
+ /**
4
+ * Evaluate destination validity once for the full drag set. Callers look up
5
+ * targets in O(1) via {@link Set.has} — `null` represents the Home/root drop.
6
+ */
7
+ export declare const computeValidDropTargets: (items: DragItemData[], folderStructure: FolderNode[]) => Set<number | null>;
@@ -2,3 +2,6 @@ export declare const toFileDraggableId: (id: number) => `file:${number}`;
2
2
  export declare const toFolderDraggableId: (id: number) => `folder:${number}`;
3
3
  export declare const toFolderTargetId: (id: number) => `folder-target:${number}`;
4
4
  export declare const parseFolderTargetId: (dndId: string | number) => number | null;
5
+ export declare const toFolderTreeTargetId: (id: number) => `folder-tree-target:${number}`;
6
+ export declare const HOME_TREE_TARGET_ID: "folder-tree-target:home";
7
+ export declare const parseFolderTreeTargetId: (dndId: string | number) => number | "root" | null;
@@ -2,7 +2,9 @@ import type { DragFolderData } from '../../../../types/dnd';
2
2
  export declare const useFileDraggable: (asset: {
3
3
  id: number;
4
4
  name: string;
5
- folder?: number | string | null;
5
+ folder?: number | string | {
6
+ id?: number;
7
+ } | null;
6
8
  }) => {
7
9
  active: import("@dnd-kit/core").Active | null;
8
10
  activatorEvent: Event | null;
@@ -0,0 +1,19 @@
1
+ export declare const useFolderTreeDroppable: (target: {
2
+ id: number;
3
+ name: string;
4
+ } | {
5
+ id: null;
6
+ name: string;
7
+ }) => {
8
+ droppable: {
9
+ active: import("@dnd-kit/core").Active | null;
10
+ rect: import("react").MutableRefObject<import("@dnd-kit/core").ClientRect | null>;
11
+ isOver: boolean;
12
+ node: import("react").MutableRefObject<HTMLElement | null>;
13
+ over: import("@dnd-kit/core").Over | null;
14
+ setNodeRef: (element: HTMLElement | null) => void;
15
+ };
16
+ isOver: boolean;
17
+ showValidDropHighlight: boolean;
18
+ showInvalidDropCursor: boolean;
19
+ };
@@ -0,0 +1,11 @@
1
+ interface EmptyStateProps {
2
+ onAddAssets: () => void;
3
+ }
4
+ /**
5
+ * Empty state for the assets list (both views) when the current folder holds
6
+ * no assets and no folders. The "Add assets" button opens the same file picker
7
+ * as New > File upload; drag-and-drop upload keeps working since the page-wide
8
+ * drop zone wraps this component.
9
+ */
10
+ export declare const EmptyState: ({ onAddAssets }: EmptyStateProps) => import("react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,6 @@
1
+ export declare const SPRING_LOAD_DELAY_MS = 600;
2
+ export declare const useSpringLoadedExpand: ({ isOver, canExpand, onExpand, }: {
3
+ isOver: boolean;
4
+ canExpand: boolean;
5
+ onExpand: () => void;
6
+ }) => void;
@@ -0,0 +1,17 @@
1
+ import { type ListSort } from '../hooks/useListSort';
2
+ interface SortMenuProps {
3
+ sort: ListSort;
4
+ /**
5
+ * The grid view forces folders on top (mixing folder cards with asset cards
6
+ * is not supported there), so it hides the Folders group entirely.
7
+ */
8
+ showFoldersGroup?: boolean;
9
+ }
10
+ /**
11
+ * Toolbar "Sort" dropdown: single-select sections (Sort / Folders). Picking an
12
+ * option keeps the menu open (`onSelect` preventDefault) so several facets can
13
+ * be tuned in one visit; clicking a checked facet clears it (the hook
14
+ * guarantees at least one sort rule stays active).
15
+ */
16
+ export declare const SortMenu: ({ sort, showFoldersGroup }: SortMenuProps) => import("react/jsx-runtime").JSX.Element;
17
+ export {};
@@ -0,0 +1,39 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type ItemKey } from '../utils/selection';
3
+ /**
4
+ * Ephemeral, view-state-only multi-select for the future Media Library. Selection
5
+ * is page-scoped and reset on folder navigation or sort/filter changes (the
6
+ * table/grid toggle keeps it — same list, different presentation).
7
+ *
8
+ * The actual computation lives in `../utils/selection.ts` (pure + unit-tested);
9
+ * this hook is a thin React wrapper. Assets and folders share one mechanism —
10
+ * items are tracked as namespaced keys (`asset:1` / `folder:1`) so toggle, range
11
+ * and select-all behave the same for both. Range / select-all receive the
12
+ * ordered key array from the caller (the view owns the rendered order), keeping
13
+ * the context data-source-agnostic.
14
+ */
15
+ export interface AssetSelection {
16
+ /** Every selected item key (assets and folders), render-order agnostic. */
17
+ selectedKeys: Set<ItemKey>;
18
+ /** Selected asset ids, derived from {@link selectedKeys}. */
19
+ selectedIds: Set<number>;
20
+ /** Selected folder ids, derived from {@link selectedKeys}. */
21
+ selectedFolderIds: Set<number>;
22
+ anchorKey: ItemKey | null;
23
+ isSelected: (key: ItemKey) => boolean;
24
+ /** Additive toggle (Cmd/Ctrl+click, item checkbox). */
25
+ toggle: (key: ItemKey) => void;
26
+ /** Shift+click — selects the contiguous range from the anchor to the target. */
27
+ selectRange: (orderedKeys: ItemKey[], targetKey: ItemKey) => void;
28
+ /** Header checkbox — selects every rendered item (folders and assets). */
29
+ selectAll: (orderedKeys: ItemKey[]) => void;
30
+ /** Close button / folder navigation / list-identity changes. */
31
+ clear: () => void;
32
+ }
33
+ interface AssetSelectionProviderProps {
34
+ children: ReactNode;
35
+ }
36
+ export declare const AssetSelectionProvider: ({ children }: AssetSelectionProviderProps) => import("react").FunctionComponentElement<import("react").ProviderProps<AssetSelection | null>>;
37
+ export declare const useAssetSelection: () => AssetSelection;
38
+ export declare const useAssetSelectionOptional: () => AssetSelection | null;
39
+ export {};