@strapi/upload 5.51.2 → 5.52.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 (193) hide show
  1. package/dist/admin/future/App.js +3 -3
  2. package/dist/admin/future/App.js.map +1 -1
  3. package/dist/admin/future/App.mjs +4 -4
  4. package/dist/admin/future/App.mjs.map +1 -1
  5. package/dist/admin/future/components/BetaNotice.js +72 -0
  6. package/dist/admin/future/components/BetaNotice.js.map +1 -0
  7. package/dist/admin/future/components/BetaNotice.mjs +70 -0
  8. package/dist/admin/future/components/BetaNotice.mjs.map +1 -0
  9. package/dist/admin/future/components/Drawer.js +16 -3
  10. package/dist/admin/future/components/Drawer.js.map +1 -1
  11. package/dist/admin/future/components/Drawer.mjs +16 -3
  12. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  13. package/dist/admin/future/hooks/useAIMetadataEnabled.js +39 -0
  14. package/dist/admin/future/hooks/useAIMetadataEnabled.js.map +1 -0
  15. package/dist/admin/future/hooks/useAIMetadataEnabled.mjs +37 -0
  16. package/dist/admin/future/hooks/useAIMetadataEnabled.mjs.map +1 -0
  17. package/dist/admin/future/pages/Assets/AssetsPage.js +297 -139
  18. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/AssetsPage.mjs +301 -143
  20. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js +46 -0
  22. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js.map +1 -0
  23. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs +44 -0
  24. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs.map +1 -0
  25. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +325 -0
  26. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -0
  27. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +323 -0
  28. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -0
  29. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +35 -14
  30. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +35 -14
  32. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +54 -53
  34. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +54 -54
  36. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +89 -24
  38. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +91 -26
  40. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +15 -3
  42. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +16 -4
  44. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/AssetsTable.js +168 -58
  46. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  47. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +171 -61
  48. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  49. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +64 -132
  50. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +64 -132
  52. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +123 -76
  54. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +126 -79
  56. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/components/BusyOverlay.js +36 -0
  58. package/dist/admin/future/pages/Assets/components/BusyOverlay.js.map +1 -0
  59. package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs +34 -0
  60. package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs.map +1 -0
  61. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js +131 -0
  62. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js.map +1 -0
  63. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs +129 -0
  64. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -0
  65. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +27 -17
  66. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  67. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +28 -18
  68. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  69. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +28 -31
  70. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -1
  71. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +28 -31
  72. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -1
  73. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +10 -15
  74. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
  75. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +10 -15
  76. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
  77. package/dist/admin/future/pages/Assets/components/FilterBadges.js +13 -4
  78. package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -1
  79. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +13 -4
  80. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -1
  81. package/dist/admin/future/pages/Assets/components/FilterMenu.js +241 -61
  82. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
  83. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +244 -64
  84. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
  85. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +144 -0
  86. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -0
  87. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +142 -0
  88. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -0
  89. package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.js → FolderFormDialog.js} +50 -16
  90. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -0
  91. package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.mjs → FolderFormDialog.mjs} +52 -18
  92. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -0
  93. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +111 -191
  94. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  95. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +114 -194
  96. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  97. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js +47 -0
  98. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js.map +1 -0
  99. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs +44 -0
  100. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs.map +1 -0
  101. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +203 -62
  102. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  103. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +204 -63
  104. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  105. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js +75 -0
  106. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js.map +1 -0
  107. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs +73 -0
  108. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs.map +1 -0
  109. package/dist/admin/future/services/api.js +24 -0
  110. package/dist/admin/future/services/api.js.map +1 -1
  111. package/dist/admin/future/services/api.mjs +24 -0
  112. package/dist/admin/future/services/api.mjs.map +1 -1
  113. package/dist/admin/future/services/assets.js +11 -0
  114. package/dist/admin/future/services/assets.js.map +1 -1
  115. package/dist/admin/future/services/assets.mjs +11 -0
  116. package/dist/admin/future/services/assets.mjs.map +1 -1
  117. package/dist/admin/future/services/folders.js +37 -2
  118. package/dist/admin/future/services/folders.js.map +1 -1
  119. package/dist/admin/future/services/folders.mjs +37 -3
  120. package/dist/admin/future/services/folders.mjs.map +1 -1
  121. package/dist/admin/future/utils/buildDragSetFromSelection.js +7 -5
  122. package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -1
  123. package/dist/admin/future/utils/buildDragSetFromSelection.mjs +7 -5
  124. package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -1
  125. package/dist/admin/future/utils/formatMoveSuccessMessage.js +12 -1
  126. package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -1
  127. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +12 -1
  128. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -1
  129. package/dist/admin/future/utils/getRelationId.js +24 -0
  130. package/dist/admin/future/utils/getRelationId.js.map +1 -0
  131. package/dist/admin/future/utils/getRelationId.mjs +22 -0
  132. package/dist/admin/future/utils/getRelationId.mjs.map +1 -0
  133. package/dist/admin/future/utils/isEventFromWithin.js +14 -0
  134. package/dist/admin/future/utils/isEventFromWithin.js.map +1 -0
  135. package/dist/admin/future/utils/isEventFromWithin.mjs +12 -0
  136. package/dist/admin/future/utils/isEventFromWithin.mjs.map +1 -0
  137. package/dist/admin/future/utils/itemLocations.js +40 -0
  138. package/dist/admin/future/utils/itemLocations.js.map +1 -0
  139. package/dist/admin/future/utils/itemLocations.mjs +36 -0
  140. package/dist/admin/future/utils/itemLocations.mjs.map +1 -0
  141. package/dist/admin/future/utils/itemSource.js +36 -0
  142. package/dist/admin/future/utils/itemSource.js.map +1 -0
  143. package/dist/admin/future/utils/itemSource.mjs +32 -0
  144. package/dist/admin/future/utils/itemSource.mjs.map +1 -0
  145. package/dist/admin/index.js +11 -17
  146. package/dist/admin/index.js.map +1 -1
  147. package/dist/admin/index.mjs +12 -18
  148. package/dist/admin/index.mjs.map +1 -1
  149. package/dist/admin/src/future/App.d.ts +1 -1
  150. package/dist/admin/src/future/components/BetaNotice.d.ts +11 -0
  151. package/dist/admin/src/future/components/Drawer.d.ts +5 -1
  152. package/dist/admin/src/future/hooks/useAIMetadataEnabled.d.ts +26 -0
  153. package/dist/admin/src/future/pages/Assets/components/ActionsMenuContent.d.ts +33 -0
  154. package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +24 -0
  155. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +16 -0
  156. package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +8 -1
  157. package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +17 -6
  158. package/dist/admin/src/future/pages/Assets/components/BusyOverlay.d.ts +9 -0
  159. package/dist/admin/src/future/pages/Assets/components/DeleteItemsDialog.d.ts +27 -0
  160. package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +8 -1
  161. package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +18 -3
  162. package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +2 -1
  163. package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +3 -1
  164. package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +4 -0
  165. package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +20 -0
  166. package/dist/admin/src/future/pages/Assets/components/FolderFormDialog.d.ts +16 -0
  167. package/dist/admin/src/future/pages/Assets/hooks/useBusyAssets.d.ts +38 -0
  168. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +20 -6
  169. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteScrollSentinel.d.ts +41 -0
  170. package/dist/admin/src/future/services/folders.d.ts +6 -1
  171. package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +4 -3
  172. package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +7 -1
  173. package/dist/admin/src/future/utils/getRelationId.d.ts +11 -0
  174. package/dist/admin/src/future/utils/isEventFromWithin.d.ts +11 -0
  175. package/dist/admin/src/future/utils/itemLocations.d.ts +31 -0
  176. package/dist/admin/src/future/utils/itemSource.d.ts +22 -0
  177. package/dist/admin/translations/en.json.js +25 -4
  178. package/dist/admin/translations/en.json.js.map +1 -1
  179. package/dist/admin/translations/en.json.mjs +25 -4
  180. package/dist/admin/translations/en.json.mjs.map +1 -1
  181. package/dist/server/services/image-manipulation.js.map +1 -1
  182. package/dist/server/services/image-manipulation.mjs.map +1 -1
  183. package/dist/server/src/services/image-manipulation.d.ts +0 -5
  184. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  185. package/package.json +9 -9
  186. package/dist/admin/future/pages/Assets/components/CreateFolderDialog.js.map +0 -1
  187. package/dist/admin/future/pages/Assets/components/CreateFolderDialog.mjs.map +0 -1
  188. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +0 -33
  189. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +0 -1
  190. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +0 -31
  191. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +0 -1
  192. package/dist/admin/src/future/pages/Assets/components/CreateFolderDialog.d.ts +0 -8
  193. package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +0 -16
@@ -0,0 +1,129 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useEffect } from 'react';
3
+ import { useNotification } from '@strapi/admin/strapi-admin';
4
+ import { Dialog, Typography, Button } from '@strapi/design-system';
5
+ import { WarningCircle } from '@strapi/icons';
6
+ import { useIntl } from 'react-intl';
7
+ import { useBulkDeleteItemsMutation } from '../../../services/assets.mjs';
8
+ import { getTranslationKey } from '../../../utils/translations.mjs';
9
+
10
+ /**
11
+ * Confirm dialog for `POST /upload/actions/bulk-delete`. Fully controlled: the
12
+ * caller owns `open` and renders its own trigger. Shared by the bulk actions bar
13
+ * (selection-scoped) and the per-folder actions menu (item-scoped), so the
14
+ * target is always passed in explicitly rather than read from the selection.
15
+ *
16
+ * Deleting a folder cascades server-side, which is why the copy warns about
17
+ * folder contents even for a single item.
18
+ */ const DeleteItemsDialog = ({ open, onClose, target, onSuccess, onPendingChange })=>{
19
+ const { formatMessage } = useIntl();
20
+ const { toggleNotification } = useNotification();
21
+ const [bulkDeleteItems, { isLoading: isDeleting }] = useBulkDeleteItemsMutation();
22
+ const count = target.fileIds.length + target.folderIds.length;
23
+ useEffect(()=>{
24
+ onPendingChange?.(isDeleting);
25
+ }, [
26
+ isDeleting,
27
+ onPendingChange
28
+ ]);
29
+ const handleConfirmDelete = async (e)=>{
30
+ // Radix AlertDialog.Action closes the dialog on click by default; prevent
31
+ // that so the dialog stays open showing the loader while the request runs
32
+ // (a bulk delete can take a while depending on the number of assets).
33
+ e.preventDefault();
34
+ // Guard re-entry while pending.
35
+ if (isDeleting) {
36
+ return;
37
+ }
38
+ const res = await bulkDeleteItems(target);
39
+ if ('error' in res) {
40
+ // Keep the dialog open and the selection intact so the user can retry
41
+ // (Confirm again) or Cancel; only surface the error toast.
42
+ toggleNotification({
43
+ type: 'danger',
44
+ message: formatMessage({
45
+ id: getTranslationKey('list.bulk-actions.delete.error'),
46
+ defaultMessage: 'An error occurred while deleting the items.'
47
+ })
48
+ });
49
+ return;
50
+ }
51
+ onClose();
52
+ toggleNotification({
53
+ type: 'success',
54
+ message: formatMessage({
55
+ id: getTranslationKey('list.bulk-actions.delete.success'),
56
+ defaultMessage: '{count, plural, =1 {# item has been deleted} other {# items have been deleted}}'
57
+ }, {
58
+ count
59
+ })
60
+ });
61
+ onSuccess?.();
62
+ };
63
+ return /*#__PURE__*/ jsx(Dialog.Root, {
64
+ open: open,
65
+ onOpenChange: (nextOpen)=>{
66
+ // The dialog must stay open while the request runs — a bulk delete
67
+ // can't be aborted halfway, so the loader is the source of truth.
68
+ if (!nextOpen && !isDeleting) {
69
+ onClose();
70
+ }
71
+ },
72
+ children: /*#__PURE__*/ jsxs(Dialog.Content, {
73
+ children: [
74
+ /*#__PURE__*/ jsx(Dialog.Header, {
75
+ children: formatMessage({
76
+ id: getTranslationKey('list.bulk-actions.delete.confirm.title'),
77
+ defaultMessage: 'Delete {count, plural, =1 {# item} other {# items}}?'
78
+ }, {
79
+ count
80
+ })
81
+ }),
82
+ /*#__PURE__*/ jsx(Dialog.Body, {
83
+ icon: /*#__PURE__*/ jsx(WarningCircle, {
84
+ width: "24px",
85
+ height: "24px",
86
+ fill: "danger600"
87
+ }),
88
+ textAlign: "center",
89
+ children: /*#__PURE__*/ jsx(Typography, {
90
+ children: formatMessage({
91
+ id: getTranslationKey('list.bulk-actions.delete.confirm.description.are-you-sure'),
92
+ defaultMessage: 'These items cannot be recovered once deleted, and deleting a folder also deletes everything inside it. If they are currently in use, linked content will break and image containers will be empty.'
93
+ })
94
+ })
95
+ }),
96
+ /*#__PURE__*/ jsxs(Dialog.Footer, {
97
+ children: [
98
+ /*#__PURE__*/ jsx(Dialog.Cancel, {
99
+ children: /*#__PURE__*/ jsx(Button, {
100
+ variant: "tertiary",
101
+ disabled: isDeleting,
102
+ fullWidth: true,
103
+ children: formatMessage({
104
+ id: 'app.components.Button.cancel',
105
+ defaultMessage: 'Cancel'
106
+ })
107
+ })
108
+ }),
109
+ /*#__PURE__*/ jsx(Dialog.Action, {
110
+ children: /*#__PURE__*/ jsx(Button, {
111
+ variant: "danger-light",
112
+ loading: isDeleting,
113
+ onClick: handleConfirmDelete,
114
+ fullWidth: true,
115
+ children: formatMessage({
116
+ id: 'app.components.Button.confirm',
117
+ defaultMessage: 'Confirm'
118
+ })
119
+ })
120
+ })
121
+ ]
122
+ })
123
+ ]
124
+ })
125
+ });
126
+ };
127
+
128
+ export { DeleteItemsDialog };
129
+ //# sourceMappingURL=DeleteItemsDialog.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeleteItemsDialog.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/DeleteItemsDialog.tsx"],"sourcesContent":["import { useEffect, type MouseEvent } from 'react';\n\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport { Button, Dialog, Typography } from '@strapi/design-system';\nimport { WarningCircle } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { useBulkDeleteItemsMutation } from '../../../services/assets';\nimport { getTranslationKey } from '../../../utils/translations';\n\ninterface DeleteItemsDialogProps {\n open: boolean;\n onClose: () => void;\n /** Explicit delete target — assets and/or folders, by id. */\n target: { fileIds: number[]; folderIds: number[] };\n /** Called after a successful delete, before the toast. */\n onSuccess?: () => void;\n /**\n * Mirrors the in-flight state out to the caller, so a host that has its own\n * controls (the bulk actions bar) can disable them for the duration.\n */\n onPendingChange?: (pending: boolean) => void;\n}\n\n/**\n * Confirm dialog for `POST /upload/actions/bulk-delete`. Fully controlled: the\n * caller owns `open` and renders its own trigger. Shared by the bulk actions bar\n * (selection-scoped) and the per-folder actions menu (item-scoped), so the\n * target is always passed in explicitly rather than read from the selection.\n *\n * Deleting a folder cascades server-side, which is why the copy warns about\n * folder contents even for a single item.\n */\nexport const DeleteItemsDialog = ({\n open,\n onClose,\n target,\n onSuccess,\n onPendingChange,\n}: DeleteItemsDialogProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n const [bulkDeleteItems, { isLoading: isDeleting }] = useBulkDeleteItemsMutation();\n\n const count = target.fileIds.length + target.folderIds.length;\n\n useEffect(() => {\n onPendingChange?.(isDeleting);\n }, [isDeleting, onPendingChange]);\n\n const handleConfirmDelete = async (e: MouseEvent) => {\n // Radix AlertDialog.Action closes the dialog on click by default; prevent\n // that so the dialog stays open showing the loader while the request runs\n // (a bulk delete can take a while depending on the number of assets).\n e.preventDefault();\n\n // Guard re-entry while pending.\n if (isDeleting) {\n return;\n }\n\n const res = await bulkDeleteItems(target);\n\n if ('error' in res) {\n // Keep the dialog open and the selection intact so the user can retry\n // (Confirm again) or Cancel; only surface the error toast.\n toggleNotification({\n type: 'danger',\n message: formatMessage({\n id: getTranslationKey('list.bulk-actions.delete.error'),\n defaultMessage: 'An error occurred while deleting the items.',\n }),\n });\n return;\n }\n\n onClose();\n toggleNotification({\n type: 'success',\n message: formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.delete.success'),\n defaultMessage:\n '{count, plural, =1 {# item has been deleted} other {# items have been deleted}}',\n },\n { count }\n ),\n });\n onSuccess?.();\n };\n\n return (\n <Dialog.Root\n open={open}\n onOpenChange={(nextOpen: boolean) => {\n // The dialog must stay open while the request runs — a bulk delete\n // can't be aborted halfway, so the loader is the source of truth.\n if (!nextOpen && !isDeleting) {\n onClose();\n }\n }}\n >\n <Dialog.Content>\n <Dialog.Header>\n {formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.delete.confirm.title'),\n defaultMessage: 'Delete {count, plural, =1 {# item} other {# items}}?',\n },\n { count }\n )}\n </Dialog.Header>\n <Dialog.Body\n icon={<WarningCircle width=\"24px\" height=\"24px\" fill=\"danger600\" />}\n textAlign=\"center\"\n >\n <Typography>\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.delete.confirm.description.are-you-sure'),\n defaultMessage:\n 'These items cannot be recovered once deleted, and deleting a folder also deletes everything inside it. If they are currently in use, linked content will break and image containers will be empty.',\n })}\n </Typography>\n </Dialog.Body>\n <Dialog.Footer>\n <Dialog.Cancel>\n <Button variant=\"tertiary\" disabled={isDeleting} fullWidth>\n {formatMessage({ id: 'app.components.Button.cancel', defaultMessage: 'Cancel' })}\n </Button>\n </Dialog.Cancel>\n <Dialog.Action>\n <Button\n variant=\"danger-light\"\n loading={isDeleting}\n onClick={handleConfirmDelete}\n fullWidth\n >\n {formatMessage({\n id: 'app.components.Button.confirm',\n defaultMessage: 'Confirm',\n })}\n </Button>\n </Dialog.Action>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog.Root>\n );\n};\n"],"names":["DeleteItemsDialog","open","onClose","target","onSuccess","onPendingChange","formatMessage","useIntl","toggleNotification","useNotification","bulkDeleteItems","isLoading","isDeleting","useBulkDeleteItemsMutation","count","fileIds","length","folderIds","useEffect","handleConfirmDelete","e","preventDefault","res","type","message","id","getTranslationKey","defaultMessage","_jsx","Dialog","Root","onOpenChange","nextOpen","_jsxs","Content","Header","Body","icon","WarningCircle","width","height","fill","textAlign","Typography","Footer","Cancel","Button","variant","disabled","fullWidth","Action","loading","onClick"],"mappings":"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"}
@@ -13,6 +13,7 @@ var canDropItemOnFolder = require('../../../../utils/canDropItemOnFolder.js');
13
13
  var formatMoveSuccessMessage = require('../../../../utils/formatMoveSuccessMessage.js');
14
14
  var getBulkMoveErrorMessage = require('../../../../utils/getBulkMoveErrorMessage.js');
15
15
  var getFolderLabel = require('../../../../utils/getFolderLabel.js');
16
+ var itemLocations = require('../../../../utils/itemLocations.js');
16
17
  var translations = require('../../../../utils/translations.js');
17
18
  var useAssetSelection = require('../../hooks/useAssetSelection.js');
18
19
  var useFolderNavigation = require('../../hooks/useFolderNavigation.js');
@@ -47,7 +48,7 @@ const resolveDestination = (overId)=>{
47
48
  // constant-length sensor array while disabling drag for users without the move
48
49
  // permission (see the note in the provider).
49
50
  const DRAG_DISABLED_DISTANCE = Number.MAX_SAFE_INTEGER;
50
- const AssetsDndProvider = ({ children })=>{
51
+ const AssetsDndProvider = ({ children, locations = itemLocations.emptyItemLocations })=>{
51
52
  const { formatMessage } = reactIntl.useIntl();
52
53
  const { toggleNotification } = strapiAdmin.useNotification();
53
54
  const selection = useAssetSelection.useAssetSelectionOptional();
@@ -63,7 +64,9 @@ const AssetsDndProvider = ({ children })=>{
63
64
  // Sync session for drag-end — state alone can lag one render behind dragStart.
64
65
  const dragSessionRef = React.useRef({
65
66
  items: [],
66
- fromSelection: false
67
+ fromSelection: false,
68
+ activeSourceFolderId: null,
69
+ spansMultipleSources: false
67
70
  });
68
71
  const announceToLiveRegion = React.useCallback((message)=>{
69
72
  setLiveAnnouncement('');
@@ -105,7 +108,9 @@ const AssetsDndProvider = ({ children })=>{
105
108
  const clearDragState = React.useCallback(()=>{
106
109
  dragSessionRef.current = {
107
110
  items: [],
108
- fromSelection: false
111
+ fromSelection: false,
112
+ activeSourceFolderId: null,
113
+ spansMultipleSources: false
109
114
  };
110
115
  setDragItems([]);
111
116
  }, []);
@@ -115,20 +120,18 @@ const AssetsDndProvider = ({ children })=>{
115
120
  clearDragState();
116
121
  return;
117
122
  }
118
- const { items, fromSelection } = buildDragSet.buildDragSet(data, selection?.selectedKeys, currentFolderId);
119
- dragSessionRef.current = {
120
- items,
121
- fromSelection
122
- };
123
- setDragItems(items);
123
+ const dragSet = buildDragSet.buildDragSet(data, selection?.selectedKeys, locations, currentFolderId);
124
+ dragSessionRef.current = dragSet;
125
+ setDragItems(dragSet.items);
124
126
  }, [
125
127
  clearDragState,
126
128
  currentFolderId,
129
+ locations,
127
130
  selection?.selectedKeys
128
131
  ]);
129
132
  const handleDragEnd = React.useCallback(async (event)=>{
130
133
  const { over } = event;
131
- const { items, fromSelection } = dragSessionRef.current;
134
+ const { items, fromSelection, activeSourceFolderId, spansMultipleSources } = dragSessionRef.current;
132
135
  clearDragState();
133
136
  if (isMovePending || !over || items.length === 0) {
134
137
  return;
@@ -146,13 +149,17 @@ const AssetsDndProvider = ({ children })=>{
146
149
  return;
147
150
  }
148
151
  const payload = buildBulkMovePayload.buildBulkMovePayload(items);
149
- // Source is always the current folder dragged items are, by definition,
150
- // visible in the current view. Destination is the drop target. Both use
151
- // leaf names so the DnD toast reads identically to the dialog's.
152
+ // Source is the grabbed row's own location, not the folder currently open:
153
+ // a global search result can be dragged out of a folder the user isn't in.
154
+ // A search-built selection can also span folders, and then no single
155
+ // source is true for the whole set — `null` drops it from the wording
156
+ // instead of naming one item's folder for all of them. Destination is the
157
+ // drop target. Both use leaf names so the DnD toast reads identically to
158
+ // the dialog's.
152
159
  const successMessage = formatMoveSuccessMessage.formatMoveSuccessMessage({
153
160
  formatMessage,
154
161
  count: items.length,
155
- source: getFolderLabel.getFolderLabel(folderStructure, currentFolderId, rootLabel),
162
+ source: spansMultipleSources ? null : getFolderLabel.getFolderLabel(folderStructure, activeSourceFolderId, rootLabel),
156
163
  destination: getFolderLabel.getFolderLabel(folderStructure, targetFolderId, rootLabel)
157
164
  });
158
165
  const errorFallback = formatMessage({
@@ -189,7 +196,6 @@ const AssetsDndProvider = ({ children })=>{
189
196
  announceToLiveRegion,
190
197
  bulkMove,
191
198
  clearDragState,
192
- currentFolderId,
193
199
  folderStructure,
194
200
  formatMessage,
195
201
  isMovePending,
@@ -225,7 +231,7 @@ const AssetsDndProvider = ({ children })=>{
225
231
  if (!destination || !activeData) {
226
232
  return '';
227
233
  }
228
- const { items } = buildDragSet.buildDragSet(activeData, selection?.selectedKeys, currentFolderId);
234
+ const { items } = buildDragSet.buildDragSet(activeData, selection?.selectedKeys, locations, currentFolderId);
229
235
  if (!canDropItemOnFolder.canDropItemOnFolder({
230
236
  items,
231
237
  targetFolderId: destination.destinationFolderId,
@@ -246,6 +252,7 @@ const AssetsDndProvider = ({ children })=>{
246
252
  currentFolderId,
247
253
  folderStructure,
248
254
  formatMessage,
255
+ locations,
249
256
  selection?.selectedKeys
250
257
  ]);
251
258
  return /*#__PURE__*/ jsxRuntime.jsx(AssetsDndContext.Provider, {
@@ -265,8 +272,11 @@ const AssetsDndProvider = ({ children })=>{
265
272
  "aria-atomic": "true",
266
273
  children: liveAnnouncement
267
274
  }),
268
- /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
275
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
269
276
  position: "relative",
277
+ alignItems: "stretch",
278
+ direction: "column",
279
+ height: "100%",
270
280
  children: children
271
281
  }),
272
282
  /*#__PURE__*/ jsxRuntime.jsx(core.DragOverlay, {
@@ -1 +1 @@
1
- {"version":3,"file":"AssetsDndProvider.js","sources":["../../../../../../../admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n DndContext,\n DragOverlay,\n PointerSensor,\n pointerWithin,\n useSensor,\n useSensors,\n type DragEndEvent,\n type DragStartEvent,\n} from '@dnd-kit/core';\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport { Box, VisuallyHidden } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nimport { useMediaLibraryPermissions } from '../../../../hooks/useMediaLibraryPermissions';\nimport { useBulkMoveMutation, useGetFolderStructureQuery } from '../../../../services/folders';\nimport { buildBulkMovePayload } from '../../../../utils/buildBulkMovePayload';\nimport { canDropItemOnFolder } from '../../../../utils/canDropItemOnFolder';\nimport { formatMoveSuccessMessage } from '../../../../utils/formatMoveSuccessMessage';\nimport { getBulkMoveErrorMessage } from '../../../../utils/getBulkMoveErrorMessage';\nimport { getFolderLabel } from '../../../../utils/getFolderLabel';\nimport { getTranslationKey } from '../../../../utils/translations';\nimport { useAssetSelectionOptional } from '../../hooks/useAssetSelection';\nimport { useFolderNavigation } from '../../hooks/useFolderNavigation';\n\nimport { buildDragSet } from './buildDragSet';\nimport { computeValidDropTargets } from './computeValidDropTargets';\nimport { parseFolderTargetId, parseFolderTreeTargetId } from './dndIds';\nimport { DragOverlayChip } from './DragOverlayChip';\n\nimport type { DragItemData } from '../../../../types/dnd';\n\n/* -------------------------------------------------------------------------------------------------\n * Context — shared with UploadDropZoneProvider for bidirectional drag guard.\n * See UploadDropZoneContext.tsx TODO linking both providers.\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsDndContextValue {\n isInternalDragActive: boolean;\n isMovePending: boolean;\n /** O(1) lookup of whether the current drag set may drop on this destination. */\n isValidDropTarget: (targetFolderId: number | null) => boolean;\n}\n\nconst AssetsDndContext = createContext<AssetsDndContextValue | null>(null);\n\nexport const useAssetsDnd = () => {\n const context = useContext(AssetsDndContext);\n\n if (!context) {\n throw new Error('useAssetsDnd must be used within AssetsDndProvider');\n }\n\n return context;\n};\n\nexport const useAssetsDndOptional = () => useContext(AssetsDndContext);\n\n/* -------------------------------------------------------------------------------------------------\n * Provider\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsDndProviderProps {\n children: ReactNode;\n}\n\ninterface DragSession {\n items: DragItemData[];\n fromSelection: boolean;\n}\n\nconst resolveDestination = (\n overId: string | number\n): { destinationFolderId: number | null } | null => {\n const inView = parseFolderTargetId(overId);\n\n if (inView != null) {\n return { destinationFolderId: inView };\n }\n\n const tree = parseFolderTreeTargetId(overId);\n\n if (tree === 'root') {\n return { destinationFolderId: null };\n }\n\n if (typeof tree === 'number') {\n return { destinationFolderId: tree };\n }\n\n return null;\n};\n\n// Activation distance no real pointer gesture will ever reach — used to keep a\n// constant-length sensor array while disabling drag for users without the move\n// permission (see the note in the provider).\nconst DRAG_DISABLED_DISTANCE = Number.MAX_SAFE_INTEGER;\n\nexport const AssetsDndProvider = ({ children }: AssetsDndProviderProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n const selection = useAssetSelectionOptional();\n const { currentFolderId } = useFolderNavigation();\n const { data: folderStructure = [] } = useGetFolderStructureQuery();\n const rootLabel = formatMessage({\n id: getTranslationKey('plugin.name'),\n defaultMessage: 'Media Library',\n });\n const [bulkMove, { isLoading: isMovePending }] = useBulkMoveMutation();\n const [dragItems, setDragItems] = useState<DragItemData[]>([]);\n const [liveAnnouncement, setLiveAnnouncement] = useState('');\n // Sync session for drag-end — state alone can lag one render behind dragStart.\n const dragSessionRef = useRef<DragSession>({ items: [], fromSelection: false });\n\n const announceToLiveRegion = useCallback((message: string) => {\n setLiveAnnouncement('');\n requestAnimationFrame(() => setLiveAnnouncement(message));\n }, []);\n\n // Moving is a mutation (bulk-move → `assets.update`); without the permission\n // pointer drag must never activate. Keyboard moves go through BulkMoveDialog,\n // which is itself gated.\n //\n // The gate has to keep BOTH the `useSensor`/`useSensors` calls AND the sensor\n // array's length constant across renders: `canUpdate` starts false and flips\n // true once the RBAC check resolves, and dnd-kit keys internal memos/effects\n // on the sensor list, erroring if its size changes (\"argument changed size\n // between renders\"). So adding/removing a sensor is out — instead keep one\n // PointerSensor always and push its activation distance out of reach when the\n // user can't move, which no real drag gesture will ever satisfy.\n const { canUpdate } = useMediaLibraryPermissions();\n const sensors = useSensors(\n useSensor(PointerSensor, {\n activationConstraint: { distance: canUpdate ? 8 : DRAG_DISABLED_DISTANCE },\n })\n );\n\n const validDropTargets = useMemo(\n () => computeValidDropTargets(dragItems, folderStructure),\n [dragItems, folderStructure]\n );\n\n const isValidDropTarget = useCallback(\n (targetFolderId: number | null) => validDropTargets.has(targetFolderId),\n [validDropTargets]\n );\n\n const contextValue = useMemo<AssetsDndContextValue>(\n () => ({\n isInternalDragActive: dragItems.length > 0,\n isMovePending,\n isValidDropTarget,\n }),\n [dragItems.length, isMovePending, isValidDropTarget]\n );\n\n const clearDragState = useCallback(() => {\n dragSessionRef.current = { items: [], fromSelection: false };\n setDragItems([]);\n }, []);\n\n const handleDragStart = useCallback(\n (event: DragStartEvent) => {\n const data = event.active.data.current as DragItemData | undefined;\n\n if (!data) {\n clearDragState();\n return;\n }\n\n const { items, fromSelection } = buildDragSet(data, selection?.selectedKeys, currentFolderId);\n dragSessionRef.current = { items, fromSelection };\n setDragItems(items);\n },\n [clearDragState, currentFolderId, selection?.selectedKeys]\n );\n\n const handleDragEnd = useCallback(\n async (event: DragEndEvent) => {\n const { over } = event;\n const { items, fromSelection } = dragSessionRef.current;\n clearDragState();\n\n if (isMovePending || !over || items.length === 0) {\n return;\n }\n\n const destination = resolveDestination(over.id);\n\n if (!destination) {\n return;\n }\n\n const { destinationFolderId: targetFolderId } = destination;\n\n if (\n !canDropItemOnFolder({\n items,\n targetFolderId,\n folderStructure,\n })\n ) {\n return;\n }\n\n const payload = buildBulkMovePayload(items);\n // Source is always the current folder — dragged items are, by definition,\n // visible in the current view. Destination is the drop target. Both use\n // leaf names so the DnD toast reads identically to the dialog's.\n const successMessage = formatMoveSuccessMessage({\n formatMessage,\n count: items.length,\n source: getFolderLabel(folderStructure, currentFolderId, rootLabel),\n destination: getFolderLabel(folderStructure, targetFolderId, rootLabel),\n });\n const errorFallback = formatMessage({\n id: getTranslationKey('list.bulk-actions.move.error'),\n defaultMessage: 'An error occurred while moving the items.',\n });\n\n try {\n await bulkMove({ ...payload, destinationFolderId: targetFolderId }).unwrap();\n\n if (fromSelection) {\n selection?.clear();\n }\n\n announceToLiveRegion(successMessage);\n\n toggleNotification({\n type: 'success',\n message: successMessage,\n });\n } catch (error) {\n const errorMessage = getBulkMoveErrorMessage(error, errorFallback);\n\n announceToLiveRegion(\n formatMessage(\n {\n id: getTranslationKey('dnd.announce.move-failure'),\n defaultMessage: 'Move failed. {message}',\n },\n { message: errorMessage }\n )\n );\n\n toggleNotification({\n type: 'danger',\n message: errorMessage,\n });\n }\n },\n [\n announceToLiveRegion,\n bulkMove,\n clearDragState,\n currentFolderId,\n folderStructure,\n formatMessage,\n isMovePending,\n rootLabel,\n selection,\n toggleNotification,\n ]\n );\n\n const handleDragCancel = useCallback(() => {\n clearDragState();\n }, [clearDragState]);\n\n const announcements = useMemo(\n () => ({\n onDragStart: ({ active }: DragStartEvent) => {\n const data = active.data.current as DragItemData | undefined;\n return data\n ? formatMessage(\n {\n id: getTranslationKey('dnd.announce.drag-start'),\n defaultMessage: 'Picked up {name}. Drop on a folder to move.',\n },\n { name: data.name }\n )\n : '';\n },\n onDragOver: () => '',\n onDragEnd: ({ active, over }: DragEndEvent) => {\n if (!over) {\n return formatMessage({\n id: getTranslationKey('dnd.announce.cancel'),\n defaultMessage: 'Drag cancelled.',\n });\n }\n\n const destination = resolveDestination(over.id);\n const activeData = active.data.current as DragItemData | undefined;\n\n if (!destination || !activeData) {\n return '';\n }\n\n const { items } = buildDragSet(activeData, selection?.selectedKeys, currentFolderId);\n\n if (\n !canDropItemOnFolder({\n items,\n targetFolderId: destination.destinationFolderId,\n folderStructure,\n })\n ) {\n return formatMessage({\n id: getTranslationKey('dnd.announce.invalid-drop'),\n defaultMessage: 'Cannot move item to this folder.',\n });\n }\n\n return '';\n },\n onDragCancel: () =>\n formatMessage({\n id: getTranslationKey('dnd.announce.cancel'),\n defaultMessage: 'Drag cancelled.',\n }),\n }),\n [currentFolderId, folderStructure, formatMessage, selection?.selectedKeys]\n );\n\n return (\n <AssetsDndContext.Provider value={contextValue}>\n <DndContext\n sensors={sensors}\n collisionDetection={pointerWithin}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n accessibility={{ announcements }}\n >\n <VisuallyHidden aria-live=\"polite\" aria-atomic=\"true\">\n {liveAnnouncement}\n </VisuallyHidden>\n <Box position=\"relative\">{children}</Box>\n <DragOverlay dropAnimation={null}>\n {dragItems.length > 0 ? <DragOverlayChip items={dragItems} /> : null}\n </DragOverlay>\n </DndContext>\n </AssetsDndContext.Provider>\n );\n};\n\n// Keyboard-accessible moves are handled by `BulkMoveDialog` (reachable from the\n// bulk-actions bar): pointer drag is a mouse-only enhancement, the dialog is the\n// a11y path. No `KeyboardSensor` by design.\n"],"names":["AssetsDndContext","createContext","useAssetsDndOptional","useContext","resolveDestination","overId","inView","parseFolderTargetId","destinationFolderId","tree","parseFolderTreeTargetId","DRAG_DISABLED_DISTANCE","Number","MAX_SAFE_INTEGER","AssetsDndProvider","children","formatMessage","useIntl","toggleNotification","useNotification","selection","useAssetSelectionOptional","currentFolderId","useFolderNavigation","data","folderStructure","useGetFolderStructureQuery","rootLabel","id","getTranslationKey","defaultMessage","bulkMove","isLoading","isMovePending","useBulkMoveMutation","dragItems","setDragItems","useState","liveAnnouncement","setLiveAnnouncement","dragSessionRef","useRef","items","fromSelection","announceToLiveRegion","useCallback","message","requestAnimationFrame","canUpdate","useMediaLibraryPermissions","sensors","useSensors","useSensor","PointerSensor","activationConstraint","distance","validDropTargets","useMemo","computeValidDropTargets","isValidDropTarget","targetFolderId","has","contextValue","isInternalDragActive","length","clearDragState","current","handleDragStart","event","active","buildDragSet","selectedKeys","handleDragEnd","over","destination","canDropItemOnFolder","payload","buildBulkMovePayload","successMessage","formatMoveSuccessMessage","count","source","getFolderLabel","errorFallback","unwrap","clear","type","error","errorMessage","getBulkMoveErrorMessage","handleDragCancel","announcements","onDragStart","name","onDragOver","onDragEnd","activeData","onDragCancel","_jsx","Provider","value","_jsxs","DndContext","collisionDetection","pointerWithin","accessibility","VisuallyHidden","aria-live","aria-atomic","Box","position","DragOverlay","dropAnimation","DragOverlayChip"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsDA,MAAMA,iCAAmBC,mBAAAA,CAA4C,IAAA,CAAA;AAY9D,MAAMC,oBAAAA,GAAuB,IAAMC,gBAAAA,CAAWH,gBAAAA;AAerD,MAAMI,qBAAqB,CACzBC,MAAAA,GAAAA;AAEA,IAAA,MAAMC,SAASC,0BAAAA,CAAoBF,MAAAA,CAAAA;AAEnC,IAAA,IAAIC,UAAU,IAAA,EAAM;QAClB,OAAO;YAAEE,mBAAAA,EAAqBF;AAAO,SAAA;AACvC,IAAA;AAEA,IAAA,MAAMG,OAAOC,8BAAAA,CAAwBL,MAAAA,CAAAA;AAErC,IAAA,IAAII,SAAS,MAAA,EAAQ;QACnB,OAAO;YAAED,mBAAAA,EAAqB;AAAK,SAAA;AACrC,IAAA;IAEA,IAAI,OAAOC,SAAS,QAAA,EAAU;QAC5B,OAAO;YAAED,mBAAAA,EAAqBC;AAAK,SAAA;AACrC,IAAA;IAEA,OAAO,IAAA;AACT,CAAA;AAEA;AACA;AACA;AACA,MAAME,sBAAAA,GAAyBC,OAAOC,gBAAgB;AAE/C,MAAMC,iBAAAA,GAAoB,CAAC,EAAEC,QAAQ,EAA0B,GAAA;IACpE,MAAM,EAAEC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;IAC1B,MAAM,EAAEC,kBAAkB,EAAE,GAAGC,2BAAAA,EAAAA;AAC/B,IAAA,MAAMC,SAAAA,GAAYC,2CAAAA,EAAAA;IAClB,MAAM,EAAEC,eAAe,EAAE,GAAGC,uCAAAA,EAAAA;AAC5B,IAAA,MAAM,EAAEC,IAAAA,EAAMC,eAAAA,GAAkB,EAAE,EAAE,GAAGC,kCAAAA,EAAAA;AACvC,IAAA,MAAMC,YAAYX,aAAAA,CAAc;AAC9BY,QAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,aAAA,CAAA;QACtBC,cAAAA,EAAgB;AAClB,KAAA,CAAA;AACA,IAAA,MAAM,CAACC,QAAAA,EAAU,EAAEC,WAAWC,aAAa,EAAE,CAAC,GAAGC,2BAAAA,EAAAA;AACjD,IAAA,MAAM,CAACC,SAAAA,EAAWC,YAAAA,CAAa,GAAGC,eAAyB,EAAE,CAAA;AAC7D,IAAA,MAAM,CAACC,gBAAAA,EAAkBC,mBAAAA,CAAoB,GAAGF,cAAAA,CAAS,EAAA,CAAA;;AAEzD,IAAA,MAAMG,iBAAiBC,YAAAA,CAAoB;AAAEC,QAAAA,KAAAA,EAAO,EAAE;QAAEC,aAAAA,EAAe;AAAM,KAAA,CAAA;IAE7E,MAAMC,oBAAAA,GAAuBC,kBAAY,CAACC,OAAAA,GAAAA;QACxCP,mBAAAA,CAAoB,EAAA,CAAA;AACpBQ,QAAAA,qBAAAA,CAAsB,IAAMR,mBAAAA,CAAoBO,OAAAA,CAAAA,CAAAA;AAClD,IAAA,CAAA,EAAG,EAAE,CAAA;;;;;;;;;;;;IAaL,MAAM,EAAEE,SAAS,EAAE,GAAGC,qDAAAA,EAAAA;IACtB,MAAMC,OAAAA,GAAUC,eAAAA,CACdC,cAAAA,CAAUC,kBAAAA,EAAe;QACvBC,oBAAAA,EAAsB;AAAEC,YAAAA,QAAAA,EAAUP,YAAY,CAAA,GAAIrC;AAAuB;AAC3E,KAAA,CAAA,CAAA;AAGF,IAAA,MAAM6C,gBAAAA,GAAmBC,aAAAA,CACvB,IAAMC,+CAAAA,CAAwBvB,WAAWV,eAAAA,CAAAA,EACzC;AAACU,QAAAA,SAAAA;AAAWV,QAAAA;AAAgB,KAAA,CAAA;AAG9B,IAAA,MAAMkC,oBAAoBd,iBAAAA,CACxB,CAACe,iBAAkCJ,gBAAAA,CAAiBK,GAAG,CAACD,cAAAA,CAAAA,EACxD;AAACJ,QAAAA;AAAiB,KAAA,CAAA;IAGpB,MAAMM,YAAAA,GAAeL,aAAAA,CACnB,KAAO;YACLM,oBAAAA,EAAsB5B,SAAAA,CAAU6B,MAAM,GAAG,CAAA;AACzC/B,YAAAA,aAAAA;AACA0B,YAAAA;AACF,SAAA,CAAA,EACA;AAACxB,QAAAA,SAAAA,CAAU6B,MAAM;AAAE/B,QAAAA,aAAAA;AAAe0B,QAAAA;AAAkB,KAAA,CAAA;AAGtD,IAAA,MAAMM,iBAAiBpB,iBAAAA,CAAY,IAAA;AACjCL,QAAAA,cAAAA,CAAe0B,OAAO,GAAG;AAAExB,YAAAA,KAAAA,EAAO,EAAE;YAAEC,aAAAA,EAAe;AAAM,SAAA;AAC3DP,QAAAA,YAAAA,CAAa,EAAE,CAAA;AACjB,IAAA,CAAA,EAAG,EAAE,CAAA;IAEL,MAAM+B,eAAAA,GAAkBtB,kBACtB,CAACuB,KAAAA,GAAAA;AACC,QAAA,MAAM5C,OAAO4C,KAAAA,CAAMC,MAAM,CAAC7C,IAAI,CAAC0C,OAAO;AAEtC,QAAA,IAAI,CAAC1C,IAAAA,EAAM;AACTyC,YAAAA,cAAAA,EAAAA;AACA,YAAA;AACF,QAAA;QAEA,MAAM,EAAEvB,KAAK,EAAEC,aAAa,EAAE,GAAG2B,yBAAAA,CAAa9C,IAAAA,EAAMJ,SAAAA,EAAWmD,YAAAA,EAAcjD,eAAAA,CAAAA;AAC7EkB,QAAAA,cAAAA,CAAe0B,OAAO,GAAG;AAAExB,YAAAA,KAAAA;AAAOC,YAAAA;AAAc,SAAA;QAChDP,YAAAA,CAAaM,KAAAA,CAAAA;IACf,CAAA,EACA;AAACuB,QAAAA,cAAAA;AAAgB3C,QAAAA,eAAAA;QAAiBF,SAAAA,EAAWmD;AAAa,KAAA,CAAA;IAG5D,MAAMC,aAAAA,GAAgB3B,kBACpB,OAAOuB,KAAAA,GAAAA;QACL,MAAM,EAAEK,IAAI,EAAE,GAAGL,KAAAA;AACjB,QAAA,MAAM,EAAE1B,KAAK,EAAEC,aAAa,EAAE,GAAGH,eAAe0B,OAAO;AACvDD,QAAAA,cAAAA,EAAAA;AAEA,QAAA,IAAIhC,iBAAiB,CAACwC,IAAAA,IAAQ/B,KAAAA,CAAMsB,MAAM,KAAK,CAAA,EAAG;AAChD,YAAA;AACF,QAAA;QAEA,MAAMU,WAAAA,GAActE,kBAAAA,CAAmBqE,IAAAA,CAAK7C,EAAE,CAAA;AAE9C,QAAA,IAAI,CAAC8C,WAAAA,EAAa;AAChB,YAAA;AACF,QAAA;AAEA,QAAA,MAAM,EAAElE,mBAAAA,EAAqBoD,cAAc,EAAE,GAAGc,WAAAA;AAEhD,QAAA,IACE,CAACC,uCAAAA,CAAoB;AACnBjC,YAAAA,KAAAA;AACAkB,YAAAA,cAAAA;AACAnC,YAAAA;SACF,CAAA,EACA;AACA,YAAA;AACF,QAAA;AAEA,QAAA,MAAMmD,UAAUC,yCAAAA,CAAqBnC,KAAAA,CAAAA;;;;AAIrC,QAAA,MAAMoC,iBAAiBC,iDAAAA,CAAyB;AAC9C/D,YAAAA,aAAAA;AACAgE,YAAAA,KAAAA,EAAOtC,MAAMsB,MAAM;YACnBiB,MAAAA,EAAQC,6BAAAA,CAAezD,iBAAiBH,eAAAA,EAAiBK,SAAAA,CAAAA;YACzD+C,WAAAA,EAAaQ,6BAAAA,CAAezD,iBAAiBmC,cAAAA,EAAgBjC,SAAAA;AAC/D,SAAA,CAAA;AACA,QAAA,MAAMwD,gBAAgBnE,aAAAA,CAAc;AAClCY,YAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,8BAAA,CAAA;YACtBC,cAAAA,EAAgB;AAClB,SAAA,CAAA;QAEA,IAAI;AACF,YAAA,MAAMC,QAAAA,CAAS;AAAE,gBAAA,GAAG6C,OAAO;gBAAEpE,mBAAAA,EAAqBoD;AAAe,aAAA,CAAA,CAAGwB,MAAM,EAAA;AAE1E,YAAA,IAAIzC,aAAAA,EAAe;gBACjBvB,SAAAA,EAAWiE,KAAAA,EAAAA;AACb,YAAA;YAEAzC,oBAAAA,CAAqBkC,cAAAA,CAAAA;YAErB5D,kBAAAA,CAAmB;gBACjBoE,IAAAA,EAAM,SAAA;gBACNxC,OAAAA,EAASgC;AACX,aAAA,CAAA;AACF,QAAA,CAAA,CAAE,OAAOS,KAAAA,EAAO;YACd,MAAMC,YAAAA,GAAeC,gDAAwBF,KAAAA,EAAOJ,aAAAA,CAAAA;AAEpDvC,YAAAA,oBAAAA,CACE5B,aAAAA,CACE;AACEY,gBAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,2BAAA,CAAA;gBACtBC,cAAAA,EAAgB;aAClB,EACA;gBAAEgB,OAAAA,EAAS0C;AAAa,aAAA,CAAA,CAAA;YAI5BtE,kBAAAA,CAAmB;gBACjBoE,IAAAA,EAAM,QAAA;gBACNxC,OAAAA,EAAS0C;AACX,aAAA,CAAA;AACF,QAAA;IACF,CAAA,EACA;AACE5C,QAAAA,oBAAAA;AACAb,QAAAA,QAAAA;AACAkC,QAAAA,cAAAA;AACA3C,QAAAA,eAAAA;AACAG,QAAAA,eAAAA;AACAT,QAAAA,aAAAA;AACAiB,QAAAA,aAAAA;AACAN,QAAAA,SAAAA;AACAP,QAAAA,SAAAA;AACAF,QAAAA;AACD,KAAA,CAAA;AAGH,IAAA,MAAMwE,mBAAmB7C,iBAAAA,CAAY,IAAA;AACnCoB,QAAAA,cAAAA,EAAAA;IACF,CAAA,EAAG;AAACA,QAAAA;AAAe,KAAA,CAAA;IAEnB,MAAM0B,aAAAA,GAAgBlC,aAAAA,CACpB,KAAO;YACLmC,WAAAA,EAAa,CAAC,EAAEvB,MAAM,EAAkB,GAAA;AACtC,gBAAA,MAAM7C,IAAAA,GAAO6C,MAAAA,CAAO7C,IAAI,CAAC0C,OAAO;AAChC,gBAAA,OAAO1C,OACHR,aAAAA,CACE;AACEY,oBAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,yBAAA,CAAA;oBACtBC,cAAAA,EAAgB;iBAClB,EACA;AAAE+D,oBAAAA,IAAAA,EAAMrE,KAAKqE;iBAAK,CAAA,GAEpB,EAAA;AACN,YAAA,CAAA;AACAC,YAAAA,UAAAA,EAAY,IAAM,EAAA;AAClBC,YAAAA,SAAAA,EAAW,CAAC,EAAE1B,MAAM,EAAEI,IAAI,EAAgB,GAAA;AACxC,gBAAA,IAAI,CAACA,IAAAA,EAAM;AACT,oBAAA,OAAOzD,aAAAA,CAAc;AACnBY,wBAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,qBAAA,CAAA;wBACtBC,cAAAA,EAAgB;AAClB,qBAAA,CAAA;AACF,gBAAA;gBAEA,MAAM4C,WAAAA,GAActE,kBAAAA,CAAmBqE,IAAAA,CAAK7C,EAAE,CAAA;AAC9C,gBAAA,MAAMoE,UAAAA,GAAa3B,MAAAA,CAAO7C,IAAI,CAAC0C,OAAO;gBAEtC,IAAI,CAACQ,WAAAA,IAAe,CAACsB,UAAAA,EAAY;oBAC/B,OAAO,EAAA;AACT,gBAAA;AAEA,gBAAA,MAAM,EAAEtD,KAAK,EAAE,GAAG4B,yBAAAA,CAAa0B,UAAAA,EAAY5E,WAAWmD,YAAAA,EAAcjD,eAAAA,CAAAA;AAEpE,gBAAA,IACE,CAACqD,uCAAAA,CAAoB;AACnBjC,oBAAAA,KAAAA;AACAkB,oBAAAA,cAAAA,EAAgBc,YAAYlE,mBAAmB;AAC/CiB,oBAAAA;iBACF,CAAA,EACA;AACA,oBAAA,OAAOT,aAAAA,CAAc;AACnBY,wBAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,2BAAA,CAAA;wBACtBC,cAAAA,EAAgB;AAClB,qBAAA,CAAA;AACF,gBAAA;gBAEA,OAAO,EAAA;AACT,YAAA,CAAA;AACAmE,YAAAA,YAAAA,EAAc,IACZjF,aAAAA,CAAc;AACZY,oBAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,qBAAA,CAAA;oBACtBC,cAAAA,EAAgB;AAClB,iBAAA;AACJ,SAAA,CAAA,EACA;AAACR,QAAAA,eAAAA;AAAiBG,QAAAA,eAAAA;AAAiBT,QAAAA,aAAAA;QAAeI,SAAAA,EAAWmD;AAAa,KAAA,CAAA;IAG5E,qBACE2B,cAAA,CAAClG,iBAAiBmG,QAAQ,EAAA;QAACC,KAAAA,EAAOtC,YAAAA;AAChC,QAAA,QAAA,gBAAAuC,eAAA,CAACC,eAAAA,EAAAA;YACCpD,OAAAA,EAASA,OAAAA;YACTqD,kBAAAA,EAAoBC,kBAAAA;YACpBZ,WAAAA,EAAazB,eAAAA;YACb4B,SAAAA,EAAWvB,aAAAA;YACXyB,YAAAA,EAAcP,gBAAAA;YACde,aAAAA,EAAe;AAAEd,gBAAAA;AAAc,aAAA;;8BAE/BO,cAAA,CAACQ,2BAAAA,EAAAA;oBAAeC,WAAAA,EAAU,QAAA;oBAASC,aAAAA,EAAY,MAAA;AAC5CtE,oBAAAA,QAAAA,EAAAA;;8BAEH4D,cAAA,CAACW,gBAAAA,EAAAA;oBAAIC,QAAAA,EAAS,UAAA;AAAY/F,oBAAAA,QAAAA,EAAAA;;8BAC1BmF,cAAA,CAACa,gBAAAA,EAAAA;oBAAYC,aAAAA,EAAe,IAAA;8BACzB7E,SAAAA,CAAU6B,MAAM,GAAG,CAAA,iBAAIkC,cAAA,CAACe,+BAAAA,EAAAA;wBAAgBvE,KAAAA,EAAOP;AAAgB,qBAAA,CAAA,GAAA;;;;;AAK1E,EAAE;;;;;;;"}
1
+ {"version":3,"file":"AssetsDndProvider.js","sources":["../../../../../../../admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n DndContext,\n DragOverlay,\n PointerSensor,\n pointerWithin,\n useSensor,\n useSensors,\n type DragEndEvent,\n type DragStartEvent,\n} from '@dnd-kit/core';\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport { Flex, VisuallyHidden } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nimport { useMediaLibraryPermissions } from '../../../../hooks/useMediaLibraryPermissions';\nimport { useBulkMoveMutation, useGetFolderStructureQuery } from '../../../../services/folders';\nimport { buildBulkMovePayload } from '../../../../utils/buildBulkMovePayload';\nimport { canDropItemOnFolder } from '../../../../utils/canDropItemOnFolder';\nimport { formatMoveSuccessMessage } from '../../../../utils/formatMoveSuccessMessage';\nimport { getBulkMoveErrorMessage } from '../../../../utils/getBulkMoveErrorMessage';\nimport { getFolderLabel } from '../../../../utils/getFolderLabel';\nimport { emptyItemLocations, type ItemLocations } from '../../../../utils/itemLocations';\nimport { getTranslationKey } from '../../../../utils/translations';\nimport { useAssetSelectionOptional } from '../../hooks/useAssetSelection';\nimport { useFolderNavigation } from '../../hooks/useFolderNavigation';\n\nimport { buildDragSet } from './buildDragSet';\nimport { computeValidDropTargets } from './computeValidDropTargets';\nimport { parseFolderTargetId, parseFolderTreeTargetId } from './dndIds';\nimport { DragOverlayChip } from './DragOverlayChip';\n\nimport type { DragItemData } from '../../../../types/dnd';\n\n/* -------------------------------------------------------------------------------------------------\n * Context — shared with UploadDropZoneProvider for bidirectional drag guard.\n * See UploadDropZoneContext.tsx TODO linking both providers.\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsDndContextValue {\n isInternalDragActive: boolean;\n isMovePending: boolean;\n /** O(1) lookup of whether the current drag set may drop on this destination. */\n isValidDropTarget: (targetFolderId: number | null) => boolean;\n}\n\nconst AssetsDndContext = createContext<AssetsDndContextValue | null>(null);\n\nexport const useAssetsDnd = () => {\n const context = useContext(AssetsDndContext);\n\n if (!context) {\n throw new Error('useAssetsDnd must be used within AssetsDndProvider');\n }\n\n return context;\n};\n\nexport const useAssetsDndOptional = () => useContext(AssetsDndContext);\n\n/* -------------------------------------------------------------------------------------------------\n * Provider\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsDndProviderProps {\n children: ReactNode;\n /**\n * Real location of every loaded row, so a multi-item drag validates against\n * each item's own parent. Defaults to a lookup that misses everything, which\n * falls back to the folder currently open.\n */\n locations?: ItemLocations;\n}\n\ninterface DragSession {\n items: DragItemData[];\n fromSelection: boolean;\n /** Location of the grabbed row — names the source in the success toast. */\n activeSourceFolderId: number | null;\n /** True when the set spans several source folders, so none can be named. */\n spansMultipleSources: boolean;\n}\n\nconst resolveDestination = (\n overId: string | number\n): { destinationFolderId: number | null } | null => {\n const inView = parseFolderTargetId(overId);\n\n if (inView != null) {\n return { destinationFolderId: inView };\n }\n\n const tree = parseFolderTreeTargetId(overId);\n\n if (tree === 'root') {\n return { destinationFolderId: null };\n }\n\n if (typeof tree === 'number') {\n return { destinationFolderId: tree };\n }\n\n return null;\n};\n\n// Activation distance no real pointer gesture will ever reach — used to keep a\n// constant-length sensor array while disabling drag for users without the move\n// permission (see the note in the provider).\nconst DRAG_DISABLED_DISTANCE = Number.MAX_SAFE_INTEGER;\n\nexport const AssetsDndProvider = ({\n children,\n locations = emptyItemLocations,\n}: AssetsDndProviderProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n const selection = useAssetSelectionOptional();\n const { currentFolderId } = useFolderNavigation();\n const { data: folderStructure = [] } = useGetFolderStructureQuery();\n const rootLabel = formatMessage({\n id: getTranslationKey('plugin.name'),\n defaultMessage: 'Media Library',\n });\n const [bulkMove, { isLoading: isMovePending }] = useBulkMoveMutation();\n const [dragItems, setDragItems] = useState<DragItemData[]>([]);\n const [liveAnnouncement, setLiveAnnouncement] = useState('');\n // Sync session for drag-end — state alone can lag one render behind dragStart.\n const dragSessionRef = useRef<DragSession>({\n items: [],\n fromSelection: false,\n activeSourceFolderId: null,\n spansMultipleSources: false,\n });\n\n const announceToLiveRegion = useCallback((message: string) => {\n setLiveAnnouncement('');\n requestAnimationFrame(() => setLiveAnnouncement(message));\n }, []);\n\n // Moving is a mutation (bulk-move → `assets.update`); without the permission\n // pointer drag must never activate. Keyboard moves go through BulkMoveDialog,\n // which is itself gated.\n //\n // The gate has to keep BOTH the `useSensor`/`useSensors` calls AND the sensor\n // array's length constant across renders: `canUpdate` starts false and flips\n // true once the RBAC check resolves, and dnd-kit keys internal memos/effects\n // on the sensor list, erroring if its size changes (\"argument changed size\n // between renders\"). So adding/removing a sensor is out — instead keep one\n // PointerSensor always and push its activation distance out of reach when the\n // user can't move, which no real drag gesture will ever satisfy.\n const { canUpdate } = useMediaLibraryPermissions();\n const sensors = useSensors(\n useSensor(PointerSensor, {\n activationConstraint: { distance: canUpdate ? 8 : DRAG_DISABLED_DISTANCE },\n })\n );\n\n const validDropTargets = useMemo(\n () => computeValidDropTargets(dragItems, folderStructure),\n [dragItems, folderStructure]\n );\n\n const isValidDropTarget = useCallback(\n (targetFolderId: number | null) => validDropTargets.has(targetFolderId),\n [validDropTargets]\n );\n\n const contextValue = useMemo<AssetsDndContextValue>(\n () => ({\n isInternalDragActive: dragItems.length > 0,\n isMovePending,\n isValidDropTarget,\n }),\n [dragItems.length, isMovePending, isValidDropTarget]\n );\n\n const clearDragState = useCallback(() => {\n dragSessionRef.current = {\n items: [],\n fromSelection: false,\n activeSourceFolderId: null,\n spansMultipleSources: false,\n };\n setDragItems([]);\n }, []);\n\n const handleDragStart = useCallback(\n (event: DragStartEvent) => {\n const data = event.active.data.current as DragItemData | undefined;\n\n if (!data) {\n clearDragState();\n return;\n }\n\n const dragSet = buildDragSet(data, selection?.selectedKeys, locations, currentFolderId);\n dragSessionRef.current = dragSet;\n setDragItems(dragSet.items);\n },\n [clearDragState, currentFolderId, locations, selection?.selectedKeys]\n );\n\n const handleDragEnd = useCallback(\n async (event: DragEndEvent) => {\n const { over } = event;\n const { items, fromSelection, activeSourceFolderId, spansMultipleSources } =\n dragSessionRef.current;\n clearDragState();\n\n if (isMovePending || !over || items.length === 0) {\n return;\n }\n\n const destination = resolveDestination(over.id);\n\n if (!destination) {\n return;\n }\n\n const { destinationFolderId: targetFolderId } = destination;\n\n if (\n !canDropItemOnFolder({\n items,\n targetFolderId,\n folderStructure,\n })\n ) {\n return;\n }\n\n const payload = buildBulkMovePayload(items);\n // Source is the grabbed row's own location, not the folder currently open:\n // a global search result can be dragged out of a folder the user isn't in.\n // A search-built selection can also span folders, and then no single\n // source is true for the whole set — `null` drops it from the wording\n // instead of naming one item's folder for all of them. Destination is the\n // drop target. Both use leaf names so the DnD toast reads identically to\n // the dialog's.\n const successMessage = formatMoveSuccessMessage({\n formatMessage,\n count: items.length,\n source: spansMultipleSources\n ? null\n : getFolderLabel(folderStructure, activeSourceFolderId, rootLabel),\n destination: getFolderLabel(folderStructure, targetFolderId, rootLabel),\n });\n const errorFallback = formatMessage({\n id: getTranslationKey('list.bulk-actions.move.error'),\n defaultMessage: 'An error occurred while moving the items.',\n });\n\n try {\n await bulkMove({ ...payload, destinationFolderId: targetFolderId }).unwrap();\n\n if (fromSelection) {\n selection?.clear();\n }\n\n announceToLiveRegion(successMessage);\n\n toggleNotification({\n type: 'success',\n message: successMessage,\n });\n } catch (error) {\n const errorMessage = getBulkMoveErrorMessage(error, errorFallback);\n\n announceToLiveRegion(\n formatMessage(\n {\n id: getTranslationKey('dnd.announce.move-failure'),\n defaultMessage: 'Move failed. {message}',\n },\n { message: errorMessage }\n )\n );\n\n toggleNotification({\n type: 'danger',\n message: errorMessage,\n });\n }\n },\n [\n announceToLiveRegion,\n bulkMove,\n clearDragState,\n folderStructure,\n formatMessage,\n isMovePending,\n rootLabel,\n selection,\n toggleNotification,\n ]\n );\n\n const handleDragCancel = useCallback(() => {\n clearDragState();\n }, [clearDragState]);\n\n const announcements = useMemo(\n () => ({\n onDragStart: ({ active }: DragStartEvent) => {\n const data = active.data.current as DragItemData | undefined;\n return data\n ? formatMessage(\n {\n id: getTranslationKey('dnd.announce.drag-start'),\n defaultMessage: 'Picked up {name}. Drop on a folder to move.',\n },\n { name: data.name }\n )\n : '';\n },\n onDragOver: () => '',\n onDragEnd: ({ active, over }: DragEndEvent) => {\n if (!over) {\n return formatMessage({\n id: getTranslationKey('dnd.announce.cancel'),\n defaultMessage: 'Drag cancelled.',\n });\n }\n\n const destination = resolveDestination(over.id);\n const activeData = active.data.current as DragItemData | undefined;\n\n if (!destination || !activeData) {\n return '';\n }\n\n const { items } = buildDragSet(\n activeData,\n selection?.selectedKeys,\n locations,\n currentFolderId\n );\n\n if (\n !canDropItemOnFolder({\n items,\n targetFolderId: destination.destinationFolderId,\n folderStructure,\n })\n ) {\n return formatMessage({\n id: getTranslationKey('dnd.announce.invalid-drop'),\n defaultMessage: 'Cannot move item to this folder.',\n });\n }\n\n return '';\n },\n onDragCancel: () =>\n formatMessage({\n id: getTranslationKey('dnd.announce.cancel'),\n defaultMessage: 'Drag cancelled.',\n }),\n }),\n [currentFolderId, folderStructure, formatMessage, locations, selection?.selectedKeys]\n );\n\n return (\n <AssetsDndContext.Provider value={contextValue}>\n <DndContext\n sensors={sensors}\n collisionDetection={pointerWithin}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n accessibility={{ announcements }}\n >\n <VisuallyHidden aria-live=\"polite\" aria-atomic=\"true\">\n {liveAnnouncement}\n </VisuallyHidden>\n <Flex position=\"relative\" alignItems=\"stretch\" direction=\"column\" height=\"100%\">\n {children}\n </Flex>\n <DragOverlay dropAnimation={null}>\n {dragItems.length > 0 ? <DragOverlayChip items={dragItems} /> : null}\n </DragOverlay>\n </DndContext>\n </AssetsDndContext.Provider>\n );\n};\n\n// Keyboard-accessible moves are handled by `BulkMoveDialog` (reachable from the\n// bulk-actions bar): pointer drag is a mouse-only enhancement, the dialog is the\n// a11y path. No `KeyboardSensor` by design.\n"],"names":["AssetsDndContext","createContext","useAssetsDndOptional","useContext","resolveDestination","overId","inView","parseFolderTargetId","destinationFolderId","tree","parseFolderTreeTargetId","DRAG_DISABLED_DISTANCE","Number","MAX_SAFE_INTEGER","AssetsDndProvider","children","locations","emptyItemLocations","formatMessage","useIntl","toggleNotification","useNotification","selection","useAssetSelectionOptional","currentFolderId","useFolderNavigation","data","folderStructure","useGetFolderStructureQuery","rootLabel","id","getTranslationKey","defaultMessage","bulkMove","isLoading","isMovePending","useBulkMoveMutation","dragItems","setDragItems","useState","liveAnnouncement","setLiveAnnouncement","dragSessionRef","useRef","items","fromSelection","activeSourceFolderId","spansMultipleSources","announceToLiveRegion","useCallback","message","requestAnimationFrame","canUpdate","useMediaLibraryPermissions","sensors","useSensors","useSensor","PointerSensor","activationConstraint","distance","validDropTargets","useMemo","computeValidDropTargets","isValidDropTarget","targetFolderId","has","contextValue","isInternalDragActive","length","clearDragState","current","handleDragStart","event","active","dragSet","buildDragSet","selectedKeys","handleDragEnd","over","destination","canDropItemOnFolder","payload","buildBulkMovePayload","successMessage","formatMoveSuccessMessage","count","source","getFolderLabel","errorFallback","unwrap","clear","type","error","errorMessage","getBulkMoveErrorMessage","handleDragCancel","announcements","onDragStart","name","onDragOver","onDragEnd","activeData","onDragCancel","_jsx","Provider","value","_jsxs","DndContext","collisionDetection","pointerWithin","accessibility","VisuallyHidden","aria-live","aria-atomic","Flex","position","alignItems","direction","height","DragOverlay","dropAnimation","DragOverlayChip"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { useState, useRef, useCallback, useMemo, createContext, useContext } from 'react';
3
3
  import { useSensors, useSensor, PointerSensor, DndContext, pointerWithin, DragOverlay } from '@dnd-kit/core';
4
4
  import { useNotification } from '@strapi/admin/strapi-admin';
5
- import { VisuallyHidden, Box } from '@strapi/design-system';
5
+ import { VisuallyHidden, Flex } from '@strapi/design-system';
6
6
  import { useIntl } from 'react-intl';
7
7
  import { useMediaLibraryPermissions } from '../../../../hooks/useMediaLibraryPermissions.mjs';
8
8
  import { useGetFolderStructureQuery, useBulkMoveMutation } from '../../../../services/folders.mjs';
@@ -11,6 +11,7 @@ import { canDropItemOnFolder } from '../../../../utils/canDropItemOnFolder.mjs';
11
11
  import { formatMoveSuccessMessage } from '../../../../utils/formatMoveSuccessMessage.mjs';
12
12
  import { getBulkMoveErrorMessage } from '../../../../utils/getBulkMoveErrorMessage.mjs';
13
13
  import { getFolderLabel } from '../../../../utils/getFolderLabel.mjs';
14
+ import { emptyItemLocations } from '../../../../utils/itemLocations.mjs';
14
15
  import { getTranslationKey } from '../../../../utils/translations.mjs';
15
16
  import { useAssetSelectionOptional } from '../../hooks/useAssetSelection.mjs';
16
17
  import { useFolderNavigation } from '../../hooks/useFolderNavigation.mjs';
@@ -45,7 +46,7 @@ const resolveDestination = (overId)=>{
45
46
  // constant-length sensor array while disabling drag for users without the move
46
47
  // permission (see the note in the provider).
47
48
  const DRAG_DISABLED_DISTANCE = Number.MAX_SAFE_INTEGER;
48
- const AssetsDndProvider = ({ children })=>{
49
+ const AssetsDndProvider = ({ children, locations = emptyItemLocations })=>{
49
50
  const { formatMessage } = useIntl();
50
51
  const { toggleNotification } = useNotification();
51
52
  const selection = useAssetSelectionOptional();
@@ -61,7 +62,9 @@ const AssetsDndProvider = ({ children })=>{
61
62
  // Sync session for drag-end — state alone can lag one render behind dragStart.
62
63
  const dragSessionRef = useRef({
63
64
  items: [],
64
- fromSelection: false
65
+ fromSelection: false,
66
+ activeSourceFolderId: null,
67
+ spansMultipleSources: false
65
68
  });
66
69
  const announceToLiveRegion = useCallback((message)=>{
67
70
  setLiveAnnouncement('');
@@ -103,7 +106,9 @@ const AssetsDndProvider = ({ children })=>{
103
106
  const clearDragState = useCallback(()=>{
104
107
  dragSessionRef.current = {
105
108
  items: [],
106
- fromSelection: false
109
+ fromSelection: false,
110
+ activeSourceFolderId: null,
111
+ spansMultipleSources: false
107
112
  };
108
113
  setDragItems([]);
109
114
  }, []);
@@ -113,20 +118,18 @@ const AssetsDndProvider = ({ children })=>{
113
118
  clearDragState();
114
119
  return;
115
120
  }
116
- const { items, fromSelection } = buildDragSet(data, selection?.selectedKeys, currentFolderId);
117
- dragSessionRef.current = {
118
- items,
119
- fromSelection
120
- };
121
- setDragItems(items);
121
+ const dragSet = buildDragSet(data, selection?.selectedKeys, locations, currentFolderId);
122
+ dragSessionRef.current = dragSet;
123
+ setDragItems(dragSet.items);
122
124
  }, [
123
125
  clearDragState,
124
126
  currentFolderId,
127
+ locations,
125
128
  selection?.selectedKeys
126
129
  ]);
127
130
  const handleDragEnd = useCallback(async (event)=>{
128
131
  const { over } = event;
129
- const { items, fromSelection } = dragSessionRef.current;
132
+ const { items, fromSelection, activeSourceFolderId, spansMultipleSources } = dragSessionRef.current;
130
133
  clearDragState();
131
134
  if (isMovePending || !over || items.length === 0) {
132
135
  return;
@@ -144,13 +147,17 @@ const AssetsDndProvider = ({ children })=>{
144
147
  return;
145
148
  }
146
149
  const payload = buildBulkMovePayload(items);
147
- // Source is always the current folder dragged items are, by definition,
148
- // visible in the current view. Destination is the drop target. Both use
149
- // leaf names so the DnD toast reads identically to the dialog's.
150
+ // Source is the grabbed row's own location, not the folder currently open:
151
+ // a global search result can be dragged out of a folder the user isn't in.
152
+ // A search-built selection can also span folders, and then no single
153
+ // source is true for the whole set — `null` drops it from the wording
154
+ // instead of naming one item's folder for all of them. Destination is the
155
+ // drop target. Both use leaf names so the DnD toast reads identically to
156
+ // the dialog's.
150
157
  const successMessage = formatMoveSuccessMessage({
151
158
  formatMessage,
152
159
  count: items.length,
153
- source: getFolderLabel(folderStructure, currentFolderId, rootLabel),
160
+ source: spansMultipleSources ? null : getFolderLabel(folderStructure, activeSourceFolderId, rootLabel),
154
161
  destination: getFolderLabel(folderStructure, targetFolderId, rootLabel)
155
162
  });
156
163
  const errorFallback = formatMessage({
@@ -187,7 +194,6 @@ const AssetsDndProvider = ({ children })=>{
187
194
  announceToLiveRegion,
188
195
  bulkMove,
189
196
  clearDragState,
190
- currentFolderId,
191
197
  folderStructure,
192
198
  formatMessage,
193
199
  isMovePending,
@@ -223,7 +229,7 @@ const AssetsDndProvider = ({ children })=>{
223
229
  if (!destination || !activeData) {
224
230
  return '';
225
231
  }
226
- const { items } = buildDragSet(activeData, selection?.selectedKeys, currentFolderId);
232
+ const { items } = buildDragSet(activeData, selection?.selectedKeys, locations, currentFolderId);
227
233
  if (!canDropItemOnFolder({
228
234
  items,
229
235
  targetFolderId: destination.destinationFolderId,
@@ -244,6 +250,7 @@ const AssetsDndProvider = ({ children })=>{
244
250
  currentFolderId,
245
251
  folderStructure,
246
252
  formatMessage,
253
+ locations,
247
254
  selection?.selectedKeys
248
255
  ]);
249
256
  return /*#__PURE__*/ jsx(AssetsDndContext.Provider, {
@@ -263,8 +270,11 @@ const AssetsDndProvider = ({ children })=>{
263
270
  "aria-atomic": "true",
264
271
  children: liveAnnouncement
265
272
  }),
266
- /*#__PURE__*/ jsx(Box, {
273
+ /*#__PURE__*/ jsx(Flex, {
267
274
  position: "relative",
275
+ alignItems: "stretch",
276
+ direction: "column",
277
+ height: "100%",
268
278
  children: children
269
279
  }),
270
280
  /*#__PURE__*/ jsx(DragOverlay, {