@strapi/upload 5.52.3 → 5.54.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 (220) hide show
  1. package/dist/admin/future/components/Drawer.js +6 -3
  2. package/dist/admin/future/components/Drawer.js.map +1 -1
  3. package/dist/admin/future/components/Drawer.mjs +6 -3
  4. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  5. package/dist/admin/future/components/UploadProgressDialog.js +9 -8
  6. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  7. package/dist/admin/future/components/UploadProgressDialog.mjs +9 -8
  8. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  9. package/dist/admin/future/pages/Assets/AssetsPage.js +16 -4
  10. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  11. package/dist/admin/future/pages/Assets/AssetsPage.mjs +16 -4
  12. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  13. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +5 -0
  14. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
  15. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +5 -0
  16. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
  17. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +19 -4
  18. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +19 -4
  20. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +35 -1
  22. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  23. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +35 -1
  24. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  25. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +10 -0
  26. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +10 -0
  28. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/AssetsTable.js +9 -0
  30. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +10 -1
  32. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +132 -88
  34. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +135 -91
  36. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +7 -0
  38. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +7 -0
  40. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js +4 -1
  42. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs +4 -1
  44. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.js +181 -0
  46. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.js.map +1 -0
  47. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.mjs +179 -0
  48. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.mjs.map +1 -0
  49. package/dist/admin/future/pages/Assets/constants.js +22 -0
  50. package/dist/admin/future/pages/Assets/constants.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/constants.mjs +19 -1
  52. package/dist/admin/future/pages/Assets/constants.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +65 -10
  54. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +66 -11
  56. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.js +111 -0
  58. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.js.map +1 -0
  59. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.mjs +109 -0
  60. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.mjs.map +1 -0
  61. package/dist/admin/future/services/api.js +17 -0
  62. package/dist/admin/future/services/api.js.map +1 -1
  63. package/dist/admin/future/services/api.mjs +18 -1
  64. package/dist/admin/future/services/api.mjs.map +1 -1
  65. package/dist/admin/future/store/uploadProgress.js +31 -22
  66. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  67. package/dist/admin/future/store/uploadProgress.mjs +31 -22
  68. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  69. package/dist/admin/index.js +11 -10
  70. package/dist/admin/index.js.map +1 -1
  71. package/dist/admin/index.mjs +11 -10
  72. package/dist/admin/index.mjs.map +1 -1
  73. package/dist/admin/src/future/components/Drawer.d.ts +10 -0
  74. package/dist/admin/src/future/pages/Assets/components/MainAreaContextMenu.d.ts +28 -0
  75. package/dist/admin/src/future/pages/Assets/constants.d.ts +20 -0
  76. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +4 -0
  77. package/dist/admin/src/future/pages/Assets/hooks/useListScrollRestoration.d.ts +15 -0
  78. package/dist/admin/src/future/store/uploadProgress.d.ts +14 -20
  79. package/dist/admin/translations/en.json.js +1 -3
  80. package/dist/admin/translations/en.json.js.map +1 -1
  81. package/dist/admin/translations/en.json.mjs +1 -3
  82. package/dist/admin/translations/en.json.mjs.map +1 -1
  83. package/dist/server/bootstrap.js +11 -0
  84. package/dist/server/bootstrap.js.map +1 -1
  85. package/dist/server/bootstrap.mjs +11 -0
  86. package/dist/server/bootstrap.mjs.map +1 -1
  87. package/dist/server/controllers/admin-upload.js +31 -1
  88. package/dist/server/controllers/admin-upload.js.map +1 -1
  89. package/dist/server/controllers/admin-upload.mjs +31 -1
  90. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  91. package/dist/server/controllers/utils/find-entity-and-check-permissions.js +15 -4
  92. package/dist/server/controllers/utils/find-entity-and-check-permissions.js.map +1 -1
  93. package/dist/server/controllers/utils/find-entity-and-check-permissions.mjs +15 -4
  94. package/dist/server/controllers/utils/find-entity-and-check-permissions.mjs.map +1 -1
  95. package/dist/server/controllers/utils/folders.js +1 -1
  96. package/dist/server/controllers/utils/folders.js.map +1 -1
  97. package/dist/server/controllers/utils/folders.mjs +1 -1
  98. package/dist/server/controllers/utils/folders.mjs.map +1 -1
  99. package/dist/server/mcp/ambient-instance.js +46 -0
  100. package/dist/server/mcp/ambient-instance.js.map +1 -0
  101. package/dist/server/mcp/ambient-instance.mjs +43 -0
  102. package/dist/server/mcp/ambient-instance.mjs.map +1 -0
  103. package/dist/server/mcp/handlers/constants.js +60 -0
  104. package/dist/server/mcp/handlers/constants.js.map +1 -0
  105. package/dist/server/mcp/handlers/constants.mjs +45 -0
  106. package/dist/server/mcp/handlers/constants.mjs.map +1 -0
  107. package/dist/server/mcp/handlers/folder-handlers.js +308 -0
  108. package/dist/server/mcp/handlers/folder-handlers.js.map +1 -0
  109. package/dist/server/mcp/handlers/folder-handlers.mjs +303 -0
  110. package/dist/server/mcp/handlers/folder-handlers.mjs.map +1 -0
  111. package/dist/server/mcp/handlers/read-handlers.js +134 -0
  112. package/dist/server/mcp/handlers/read-handlers.js.map +1 -0
  113. package/dist/server/mcp/handlers/read-handlers.mjs +130 -0
  114. package/dist/server/mcp/handlers/read-handlers.mjs.map +1 -0
  115. package/dist/server/mcp/handlers/write-handlers.js +315 -0
  116. package/dist/server/mcp/handlers/write-handlers.js.map +1 -0
  117. package/dist/server/mcp/handlers/write-handlers.mjs +311 -0
  118. package/dist/server/mcp/handlers/write-handlers.mjs.map +1 -0
  119. package/dist/server/mcp/permissions.js +36 -0
  120. package/dist/server/mcp/permissions.js.map +1 -0
  121. package/dist/server/mcp/permissions.mjs +33 -0
  122. package/dist/server/mcp/permissions.mjs.map +1 -0
  123. package/dist/server/mcp/register-upload-mcp-tools.js +235 -0
  124. package/dist/server/mcp/register-upload-mcp-tools.js.map +1 -0
  125. package/dist/server/mcp/register-upload-mcp-tools.mjs +232 -0
  126. package/dist/server/mcp/register-upload-mcp-tools.mjs.map +1 -0
  127. package/dist/server/mcp/sanitizers/sanitize-media.js +97 -0
  128. package/dist/server/mcp/sanitizers/sanitize-media.js.map +1 -0
  129. package/dist/server/mcp/sanitizers/sanitize-media.mjs +93 -0
  130. package/dist/server/mcp/sanitizers/sanitize-media.mjs.map +1 -0
  131. package/dist/server/mcp/schemas/input-schemas.js +206 -0
  132. package/dist/server/mcp/schemas/input-schemas.js.map +1 -0
  133. package/dist/server/mcp/schemas/input-schemas.mjs +190 -0
  134. package/dist/server/mcp/schemas/input-schemas.mjs.map +1 -0
  135. package/dist/server/mcp/schemas/output-schemas.js +158 -0
  136. package/dist/server/mcp/schemas/output-schemas.js.map +1 -0
  137. package/dist/server/mcp/schemas/output-schemas.mjs +142 -0
  138. package/dist/server/mcp/schemas/output-schemas.mjs.map +1 -0
  139. package/dist/server/mcp/utils.js +14 -0
  140. package/dist/server/mcp/utils.js.map +1 -0
  141. package/dist/server/mcp/utils.mjs +12 -0
  142. package/dist/server/mcp/utils.mjs.map +1 -0
  143. package/dist/server/media-library-default-notice.js +42 -0
  144. package/dist/server/media-library-default-notice.js.map +1 -0
  145. package/dist/server/media-library-default-notice.mjs +40 -0
  146. package/dist/server/media-library-default-notice.mjs.map +1 -0
  147. package/dist/server/register.js +5 -0
  148. package/dist/server/register.js.map +1 -1
  149. package/dist/server/register.mjs +5 -0
  150. package/dist/server/register.mjs.map +1 -1
  151. package/dist/server/services/folder.js.map +1 -1
  152. package/dist/server/services/folder.mjs.map +1 -1
  153. package/dist/server/services/upload.js +34 -10
  154. package/dist/server/services/upload.js.map +1 -1
  155. package/dist/server/services/upload.mjs +34 -10
  156. package/dist/server/services/upload.mjs.map +1 -1
  157. package/dist/server/src/bootstrap.d.ts.map +1 -1
  158. package/dist/server/src/controllers/admin-upload.d.ts +1 -0
  159. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  160. package/dist/server/src/controllers/utils/find-entity-and-check-permissions.d.ts +14 -1
  161. package/dist/server/src/controllers/utils/find-entity-and-check-permissions.d.ts.map +1 -1
  162. package/dist/server/src/controllers/utils/folders.d.ts +2 -1
  163. package/dist/server/src/controllers/utils/folders.d.ts.map +1 -1
  164. package/dist/server/src/index.d.ts +10 -2
  165. package/dist/server/src/index.d.ts.map +1 -1
  166. package/dist/server/src/mcp/ambient-instance.d.ts +65 -0
  167. package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -0
  168. package/dist/server/src/mcp/handlers/constants.d.ts +48 -0
  169. package/dist/server/src/mcp/handlers/constants.d.ts.map +1 -0
  170. package/dist/server/src/mcp/handlers/folder-handlers.d.ts +49 -0
  171. package/dist/server/src/mcp/handlers/folder-handlers.d.ts.map +1 -0
  172. package/dist/server/src/mcp/handlers/index.d.ts +5 -0
  173. package/dist/server/src/mcp/handlers/index.d.ts.map +1 -0
  174. package/dist/server/src/mcp/handlers/read-handlers.d.ts +35 -0
  175. package/dist/server/src/mcp/handlers/read-handlers.d.ts.map +1 -0
  176. package/dist/server/src/mcp/handlers/write-handlers.d.ts +66 -0
  177. package/dist/server/src/mcp/handlers/write-handlers.d.ts.map +1 -0
  178. package/dist/server/src/mcp/index.d.ts +3 -0
  179. package/dist/server/src/mcp/index.d.ts.map +1 -0
  180. package/dist/server/src/mcp/permissions.d.ts +22 -0
  181. package/dist/server/src/mcp/permissions.d.ts.map +1 -0
  182. package/dist/server/src/mcp/register-upload-mcp-tools.d.ts +29 -0
  183. package/dist/server/src/mcp/register-upload-mcp-tools.d.ts.map +1 -0
  184. package/dist/server/src/mcp/sanitizers/sanitize-media.d.ts +63 -0
  185. package/dist/server/src/mcp/sanitizers/sanitize-media.d.ts.map +1 -0
  186. package/dist/server/src/mcp/schemas/index.d.ts +3 -0
  187. package/dist/server/src/mcp/schemas/index.d.ts.map +1 -0
  188. package/dist/server/src/mcp/schemas/input-schemas.d.ts +148 -0
  189. package/dist/server/src/mcp/schemas/input-schemas.d.ts.map +1 -0
  190. package/dist/server/src/mcp/schemas/output-schemas.d.ts +276 -0
  191. package/dist/server/src/mcp/schemas/output-schemas.d.ts.map +1 -0
  192. package/dist/server/src/mcp/types.d.ts +19 -0
  193. package/dist/server/src/mcp/types.d.ts.map +1 -0
  194. package/dist/server/src/mcp/utils.d.ts +4 -0
  195. package/dist/server/src/mcp/utils.d.ts.map +1 -0
  196. package/dist/server/src/media-library-default-notice.d.ts +19 -0
  197. package/dist/server/src/media-library-default-notice.d.ts.map +1 -0
  198. package/dist/server/src/register.d.ts.map +1 -1
  199. package/dist/server/src/services/folder.d.ts +6 -2
  200. package/dist/server/src/services/folder.d.ts.map +1 -1
  201. package/dist/server/src/services/index.d.ts +10 -2
  202. package/dist/server/src/services/index.d.ts.map +1 -1
  203. package/dist/server/src/services/upload.d.ts +10 -1
  204. package/dist/server/src/services/upload.d.ts.map +1 -1
  205. package/dist/server/src/types.d.ts +2 -2
  206. package/dist/server/src/types.d.ts.map +1 -1
  207. package/dist/server/src/utils/index.d.ts +2 -1
  208. package/dist/server/src/utils/index.d.ts.map +1 -1
  209. package/dist/server/utils/index.js +2 -2
  210. package/dist/server/utils/index.js.map +1 -1
  211. package/dist/server/utils/index.mjs +2 -2
  212. package/dist/server/utils/index.mjs.map +1 -1
  213. package/dist/shared/contracts/files.d.ts +22 -0
  214. package/dist/shared/contracts/files.d.ts.map +1 -1
  215. package/package.json +7 -6
  216. package/dist/admin/future/components/BetaNotice.js +0 -72
  217. package/dist/admin/future/components/BetaNotice.js.map +0 -1
  218. package/dist/admin/future/components/BetaNotice.mjs +0 -70
  219. package/dist/admin/future/components/BetaNotice.mjs.map +0 -1
  220. package/dist/admin/src/future/components/BetaNotice.d.ts +0 -11
@@ -1,7 +1,7 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { useState, useMemo } from 'react';
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useState, useEffect, useMemo } from 'react';
3
3
  import { useNotification } from '@strapi/admin/strapi-admin';
4
- import { Typography, TextButton, Tooltip, Box, Button, IconButton, Flex } from '@strapi/design-system';
4
+ import { Box, Typography, TextButton, Tooltip, Button, IconButton, Flex } from '@strapi/design-system';
5
5
  import { Sparkle, ArrowRight, Trash, Cross } from '@strapi/icons';
6
6
  import { useIntl } from 'react-intl';
7
7
  import { css, styled } from 'styled-components';
@@ -136,6 +136,37 @@ const BulkActionsBar = ({ assets = [], locations = emptyItemLocations, renderedK
136
136
  // state so the rest of the controls stay disabled for the duration.
137
137
  const [isDeleting, setIsDeleting] = useState(false);
138
138
  const count = selectedIds.size + selectedFolderIds.size;
139
+ // `innerHeight - top` rather than the bar's height: it also spans the gap the
140
+ // bar floats above the viewport edge (0 docked on mobile, `spaces[4]` for the
141
+ // desktop pill), so one measurement covers both layouts and no height is
142
+ // hardcoded.
143
+ const [barEl, setBarEl] = useState(null);
144
+ const [reservedSpace, setReservedSpace] = useState(0);
145
+ useEffect(()=>{
146
+ if (!barEl) {
147
+ setReservedSpace(0);
148
+ return;
149
+ }
150
+ const measure = ()=>{
151
+ const rect = barEl.getBoundingClientRect();
152
+ // Zero height means the CSS has hidden the bar (drawer open on mobile) —
153
+ // reserving anything then would leave a gap under nothing.
154
+ setReservedSpace(rect.height === 0 ? 0 : Math.max(0, window.innerHeight - rect.top));
155
+ };
156
+ measure();
157
+ const observer = new ResizeObserver(measure);
158
+ observer.observe(barEl);
159
+ window.addEventListener('resize', measure);
160
+ return ()=>{
161
+ observer.disconnect();
162
+ window.removeEventListener('resize', measure);
163
+ };
164
+ // `isDetailsDrawerOpen` toggles the bar's `display`, which a ResizeObserver
165
+ // is not guaranteed to report.
166
+ }, [
167
+ barEl,
168
+ isDetailsDrawerOpen
169
+ ]);
139
170
  const handleSelectAll = ()=>{
140
171
  trackUsage('didSelectAllMediaLibraryElements');
141
172
  selectAll(renderedKeys);
@@ -275,107 +306,120 @@ const BulkActionsBar = ({ assets = [], locations = emptyItemLocations, renderedK
275
306
  if (count === 0 || !canUpdate) {
276
307
  return null;
277
308
  }
278
- return /*#__PURE__*/ jsxs(Bar, {
279
- $isDrawerOpen: isDetailsDrawerOpen,
280
- $isStacked: isAiMetadataEnabled,
281
- tag: "section",
282
- role: "region",
283
- "aria-label": formatMessage({
284
- id: getTranslationKey('list.bulk-actions.label'),
285
- defaultMessage: 'Bulk actions'
286
- }),
309
+ return /*#__PURE__*/ jsxs(Fragment, {
287
310
  children: [
288
- /*#__PURE__*/ jsx(Typography, {
289
- "data-bar-slot": "count",
290
- fontWeight: "bold",
291
- textColor: "neutral800",
292
- marginRight: 4,
293
- children: formatMessage({
294
- id: getTranslationKey('list.bulk-actions.selected-count'),
295
- defaultMessage: '{count, plural, =1 {# item selected} other {# items selected}}'
296
- }, {
297
- count
298
- })
311
+ /*#__PURE__*/ jsx(Box, {
312
+ "aria-hidden": true,
313
+ height: `${reservedSpace}px`,
314
+ "data-bar-spacer": true
299
315
  }),
300
- /*#__PURE__*/ jsx(TextButton, {
301
- onClick: handleSelectAll,
302
- marginRight: 4,
303
- disabled: isBusy,
304
- children: formatMessage({
305
- id: getTranslationKey('list.bulk-actions.select-all'),
306
- defaultMessage: 'Select all'
307
- })
308
- }),
309
- /*#__PURE__*/ jsxs(ActionCluster, {
310
- "data-bar-slot": "actions",
316
+ /*#__PURE__*/ jsxs(Bar, {
317
+ ref: setBarEl,
318
+ $isDrawerOpen: isDetailsDrawerOpen,
319
+ $isStacked: isAiMetadataEnabled,
320
+ tag: "section",
321
+ role: "region",
322
+ // The bar floats over the list but is not part of its background: a
323
+ // right-click on it keeps the browser's own menu. See MainAreaContextMenu.
324
+ "data-native-context-menu": true,
325
+ "aria-label": formatMessage({
326
+ id: getTranslationKey('list.bulk-actions.label'),
327
+ defaultMessage: 'Bulk actions'
328
+ }),
311
329
  children: [
312
- isAiMetadataEnabled && /*#__PURE__*/ jsx(Tooltip, {
313
- label: metadataDisabledReason,
314
- children: /*#__PURE__*/ jsx(Box, {
315
- children: /*#__PURE__*/ jsx(Button, {
316
- size: "S",
317
- startIcon: /*#__PURE__*/ jsx(Sparkle, {}),
318
- disabled: isBusy || selectedIds.size === 0 || isOverMetadataLimit || hasNoEligibleAssets,
319
- loading: isGeneratingMetadata,
320
- onClick: handleCreateMetadata,
321
- children: formatMessage({
322
- id: getTranslationKey('list.bulk-actions.create-metadata'),
323
- defaultMessage: 'Create metadata'
324
- })
325
- })
330
+ /*#__PURE__*/ jsx(Typography, {
331
+ "data-bar-slot": "count",
332
+ fontWeight: "bold",
333
+ textColor: "neutral800",
334
+ marginRight: 4,
335
+ children: formatMessage({
336
+ id: getTranslationKey('list.bulk-actions.selected-count'),
337
+ defaultMessage: '{count, plural, =1 {# item selected} other {# items selected}}'
338
+ }, {
339
+ count
326
340
  })
327
341
  }),
328
- /*#__PURE__*/ jsx(IconButton, {
329
- variant: "tertiary",
342
+ /*#__PURE__*/ jsx(TextButton, {
343
+ onClick: handleSelectAll,
344
+ marginRight: 4,
330
345
  disabled: isBusy,
331
- label: formatMessage({
332
- id: getTranslationKey('list.bulk-actions.move'),
333
- defaultMessage: 'Move'
334
- }),
335
- onClick: ()=>setIsMoveDialogOpen(true),
336
- children: /*#__PURE__*/ jsx(ArrowRight, {})
346
+ children: formatMessage({
347
+ id: getTranslationKey('list.bulk-actions.select-all'),
348
+ defaultMessage: 'Select all'
349
+ })
350
+ }),
351
+ /*#__PURE__*/ jsxs(ActionCluster, {
352
+ "data-bar-slot": "actions",
353
+ children: [
354
+ isAiMetadataEnabled && /*#__PURE__*/ jsx(Tooltip, {
355
+ label: metadataDisabledReason,
356
+ children: /*#__PURE__*/ jsx(Box, {
357
+ children: /*#__PURE__*/ jsx(Button, {
358
+ size: "S",
359
+ startIcon: /*#__PURE__*/ jsx(Sparkle, {}),
360
+ disabled: isBusy || selectedIds.size === 0 || isOverMetadataLimit || hasNoEligibleAssets,
361
+ loading: isGeneratingMetadata,
362
+ onClick: handleCreateMetadata,
363
+ children: formatMessage({
364
+ id: getTranslationKey('list.bulk-actions.create-metadata'),
365
+ defaultMessage: 'Create metadata'
366
+ })
367
+ })
368
+ })
369
+ }),
370
+ /*#__PURE__*/ jsx(IconButton, {
371
+ variant: "tertiary",
372
+ disabled: isBusy,
373
+ label: formatMessage({
374
+ id: getTranslationKey('list.bulk-actions.move'),
375
+ defaultMessage: 'Move'
376
+ }),
377
+ onClick: ()=>setIsMoveDialogOpen(true),
378
+ children: /*#__PURE__*/ jsx(ArrowRight, {})
379
+ }),
380
+ /*#__PURE__*/ jsx(BulkMoveDialog, {
381
+ open: isMoveDialogOpen,
382
+ onClose: ()=>setIsMoveDialogOpen(false),
383
+ items: moveItems,
384
+ onSuccess: clear
385
+ }),
386
+ /*#__PURE__*/ jsx(IconButton, {
387
+ variant: "danger-light",
388
+ disabled: isBusy,
389
+ label: formatMessage({
390
+ id: getTranslationKey('list.bulk-actions.delete'),
391
+ defaultMessage: 'Delete'
392
+ }),
393
+ onClick: ()=>setIsDeleteDialogOpen(true),
394
+ children: /*#__PURE__*/ jsx(Trash, {})
395
+ }),
396
+ /*#__PURE__*/ jsx(DeleteItemsDialog, {
397
+ open: isDeleteDialogOpen,
398
+ onClose: ()=>setIsDeleteDialogOpen(false),
399
+ target: {
400
+ fileIds: Array.from(selectedIds),
401
+ folderIds: Array.from(selectedFolderIds)
402
+ },
403
+ onSuccess: clear,
404
+ onPendingChange: setIsDeleting
405
+ })
406
+ ]
337
407
  }),
338
- /*#__PURE__*/ jsx(BulkMoveDialog, {
339
- open: isMoveDialogOpen,
340
- onClose: ()=>setIsMoveDialogOpen(false),
341
- items: moveItems,
342
- onSuccess: clear
408
+ /*#__PURE__*/ jsx(VerticalDivider, {
409
+ "data-bar-slot": "divider",
410
+ "aria-hidden": true
343
411
  }),
344
412
  /*#__PURE__*/ jsx(IconButton, {
345
- variant: "danger-light",
346
- disabled: isBusy,
413
+ variant: "ghost",
347
414
  label: formatMessage({
348
- id: getTranslationKey('list.bulk-actions.delete'),
349
- defaultMessage: 'Delete'
415
+ id: getTranslationKey('list.bulk-actions.clear'),
416
+ defaultMessage: 'Clear selection'
350
417
  }),
351
- onClick: ()=>setIsDeleteDialogOpen(true),
352
- children: /*#__PURE__*/ jsx(Trash, {})
353
- }),
354
- /*#__PURE__*/ jsx(DeleteItemsDialog, {
355
- open: isDeleteDialogOpen,
356
- onClose: ()=>setIsDeleteDialogOpen(false),
357
- target: {
358
- fileIds: Array.from(selectedIds),
359
- folderIds: Array.from(selectedFolderIds)
360
- },
361
- onSuccess: clear,
362
- onPendingChange: setIsDeleting
418
+ onClick: clear,
419
+ disabled: isBusy,
420
+ children: /*#__PURE__*/ jsx(Cross, {})
363
421
  })
364
422
  ]
365
- }),
366
- /*#__PURE__*/ jsx(VerticalDivider, {
367
- "data-bar-slot": "divider",
368
- "aria-hidden": true
369
- }),
370
- /*#__PURE__*/ jsx(IconButton, {
371
- variant: "ghost",
372
- label: formatMessage({
373
- id: getTranslationKey('list.bulk-actions.clear'),
374
- defaultMessage: 'Clear selection'
375
- }),
376
- onClick: clear,
377
- disabled: isBusy,
378
- children: /*#__PURE__*/ jsx(Cross, {})
379
423
  })
380
424
  ]
381
425
  });
@@ -1 +1 @@
1
- {"version":3,"file":"BulkActionsBar.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/BulkActionsBar.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\n\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport {\n Box,\n Button,\n Flex,\n IconButton,\n TextButton,\n Tooltip,\n Typography,\n} from '@strapi/design-system';\nimport { ArrowRight, Cross, Sparkle, Trash } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { css, styled } from 'styled-components';\n\nimport {\n AI_METADATA_MAX_FILES,\n isAIMetadataSupportedMime,\n} from '../../../../../../shared/constants';\nimport { useAIMetadataEnabled } from '../../../hooks/useAIMetadataEnabled';\nimport { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions';\nimport { useTracking } from '../../../hooks/useTracking';\nimport { useGenerateAiMetadataMutation } from '../../../services/assets';\nimport { buildDragSetFromSelection } from '../../../utils/buildDragSetFromSelection';\nimport { emptyItemLocations, type ItemLocations } from '../../../utils/itemLocations';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useFolderNavigation } from '../hooks/useFolderNavigation';\nimport { useIsAssetDetailsOpen } from '../hooks/useIsAssetDetailsOpen';\nimport { type ItemKey } from '../utils/selection';\n\nimport { BulkMoveDialog } from './BulkMoveDialog';\nimport { DeleteItemsDialog } from './DeleteItemsDialog';\n\nimport type { File } from '../../../../../../shared/contracts/files';\n\n/**\n * Bulk action bar for the future Media Library. Mobile: docked full-bleed to the\n * bottom edge (no radius, top border only). Desktop (medium+): a floating,\n * centered pill.\n */\nconst Bar = styled(Flex)<{ $isDrawerOpen: boolean; $isStacked: boolean }>`\n position: fixed;\n z-index: ${({ theme }) => theme.zIndices.popover};\n left: 0;\n right: 0;\n bottom: 0;\n align-items: center;\n gap: ${({ theme }) => theme.spaces[2]};\n padding: ${({ theme }) =>\n `${theme.spaces[3]} ${theme.spaces[2]} ${theme.spaces[3]} ${theme.spaces[6]}`};\n background: ${({ theme }) => theme.colors.neutral0};\n border: 0;\n border-top: 1px solid ${({ theme }) => theme.colors.neutral150};\n border-radius: 0;\n box-shadow: ${({ theme }) => theme.shadows.popupShadow};\n\n /* Docked full-bleed at the bottom on mobile, which is exactly where the open\n drawer keeps its own actions — so it steps aside there, and only there. */\n display: ${({ $isDrawerOpen }) => ($isDrawerOpen ? 'none' : 'flex')};\n\n /* Mobile with the metadata action present: the labelled button plus the icons\n no longer fit beside the count on one line, so the count takes a row of its\n own and every button drops to the next.\n\n Addressed by slot rather than by position: these rules used to use\n nth-child, which silently retargeted the moment a control was inserted\n into the row. */\n ${({ $isStacked }) =>\n $isStacked &&\n css`\n flex-wrap: wrap;\n justify-content: space-between;\n\n > [data-bar-slot='count'] {\n flex-basis: 100%;\n margin-right: 0;\n }\n\n > [data-bar-slot='actions'] {\n margin-left: 0;\n }\n\n /* The divider only existed to set the clear action apart from the rest;\n with the row spread it would hang in mid-air between them. */\n > [data-bar-slot='divider'] {\n display: none;\n }\n `}\n\n ${({ theme }) => theme.breakpoints.medium} {\n display: flex;\n left: 50%;\n right: auto;\n bottom: ${({ theme }) => theme.spaces[4]};\n transform: translateX(-50%);\n border: 1px solid ${({ theme }) => theme.colors.neutral150};\n border-radius: ${({ theme }) => theme.borderRadius};\n /* Sized by its content, capped so the pill can never span the whole\n viewport. The nowrap is what lets the content set that width — without it\n the labels wrap and the bar reads as narrow and tall. Inherited, so it\n covers every label inside.\n\n Deliberately not applied on mobile: there the bar is full-bleed and\n cannot grow, so refusing to wrap would clip the last action on a narrow\n phone rather than widen anything. */\n white-space: nowrap;\n max-width: 90%;\n\n /* One line again from tablet up, where it fits. */\n flex-wrap: nowrap;\n\n > [data-bar-slot='count'] {\n flex-basis: auto;\n }\n\n > [data-bar-slot='actions'] {\n margin-left: auto;\n }\n\n > [data-bar-slot='divider'] {\n display: block;\n }\n }\n`;\n\nconst ActionCluster = styled(Flex)`\n margin-left: auto;\n align-items: center;\n gap: ${({ theme }) => theme.spaces[2]};\n`;\n\nconst VerticalDivider = styled(Box)`\n width: 1px;\n align-self: stretch;\n background: ${({ theme }) => theme.colors.neutral150};\n margin-left: ${({ theme }) => theme.spaces[1]};\n`;\n\ninterface BulkActionsBarProps {\n /**\n * The assets currently rendered in the list, used to resolve the mime types\n * of the selection — the bar needs them to know which selected assets the AI\n * provider can actually read.\n *\n * Selection is page-scoped and cleared whenever the list identity changes, so\n * every selected asset id is guaranteed to appear here.\n */\n assets?: File[];\n /**\n * Real location of every loaded row, so the move dialog validates each\n * selected item against its own parent. Defaults to a lookup that misses\n * everything, which falls back to the folder currently open.\n */\n locations?: ItemLocations;\n /**\n * Keys of the items on screen, in render order. Owned by the view so\n * select-all covers exactly what the user can see — in mixed mode that is not\n * every folder.\n */\n renderedKeys?: ItemKey[];\n}\n\nexport const BulkActionsBar = ({\n assets = [],\n locations = emptyItemLocations,\n renderedKeys = [],\n}: BulkActionsBarProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n // No mime argument: the action is selection-scoped, so the per-asset\n // allowlist check happens below in `eligibleCount`.\n const isAiMetadataEnabled = useAIMetadataEnabled();\n // Every bulk action (move, delete, metadata) is an `assets.update` mutation\n // server-side — one flag gates the whole cluster.\n const { canUpdate } = useMediaLibraryPermissions();\n const { selectedIds, selectedFolderIds, selectAll, clear } = useAssetSelection();\n const { trackUsage } = useTracking();\n const { currentFolderId } = useFolderNavigation();\n const isDetailsDrawerOpen = useIsAssetDetailsOpen();\n const [generateAiMetadata, { isLoading: isGeneratingMetadata }] = useGenerateAiMetadataMutation();\n const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);\n const [isMoveDialogOpen, setIsMoveDialogOpen] = useState(false);\n // The delete request lives in DeleteItemsDialog; the bar mirrors its pending\n // state so the rest of the controls stay disabled for the duration.\n const [isDeleting, setIsDeleting] = useState(false);\n\n const count = selectedIds.size + selectedFolderIds.size;\n\n const handleSelectAll = () => {\n trackUsage('didSelectAllMediaLibraryElements');\n selectAll(renderedKeys);\n };\n const isBusy = isDeleting || isGeneratingMetadata;\n\n // Stable identity: the move dialog memoizes its destination walk on it. Each\n // item's real location comes from `locations`, so a selection made under a\n // global search validates against each item's own parent rather than the\n // folder currently open.\n const moveItems = useMemo(\n () => buildDragSetFromSelection(selectedIds, selectedFolderIds, locations, currentFolderId),\n [selectedIds, selectedFolderIds, locations, currentFolderId]\n );\n\n /**\n * The server processes the whole selection inside a single request and\n * rejects anything above the cap with a 400. Disable the action up front so\n * the limit is discoverable before the user commits to a request.\n */\n const isOverMetadataLimit = selectedIds.size > AI_METADATA_MAX_FILES;\n\n /**\n * How many selected assets the AI provider can actually read. The server\n * stays authoritative — it still reports what it skipped — but a selection\n * with nothing eligible (a lone PDF, say) would only ever come back fully\n * skipped, so there is no point sending it.\n */\n const eligibleCount = useMemo(() => {\n const mimeById = new Map(assets.map(({ id, mime }) => [id, mime]));\n\n return [...selectedIds].filter((id) => isAIMetadataSupportedMime(mimeById.get(id))).length;\n }, [assets, selectedIds]);\n\n const hasNoEligibleAssets = selectedIds.size > 0 && eligibleCount === 0;\n\n /**\n * Why the action is unavailable, surfaced on hover. `undefined` leaves the\n * button without a tooltip — either it is usable, or it is disabled for a\n * reason the selection count already makes obvious.\n */\n let metadataDisabledReason: string | undefined;\n\n if (isOverMetadataLimit) {\n metadataDisabledReason = formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.create-metadata.too-many'),\n defaultMessage:\n 'Metadata can be generated for up to {max} assets at a time. Select fewer assets to continue.',\n },\n { max: AI_METADATA_MAX_FILES }\n );\n } else if (hasNoEligibleAssets) {\n metadataDisabledReason = formatMessage({\n id: getTranslationKey('list.bulk-actions.create-metadata.no-eligible'),\n defaultMessage:\n 'Metadata can only be generated for images. None of the selected assets are supported.',\n });\n }\n\n /**\n * Metadata generation only applies to images — folders and unsupported files\n * in the selection are reported back rather than blocking the action.\n */\n const handleCreateMetadata = async () => {\n // Guard re-entry while pending, and never send a selection the server\n // would reject outright or skip in full.\n if (isGeneratingMetadata || isOverMetadataLimit || hasNoEligibleAssets) {\n return;\n }\n\n const fileIds = Array.from(selectedIds);\n const res = await generateAiMetadata({ fileIds });\n\n if ('error' in res) {\n // Keep the selection intact so the user can retry.\n toggleNotification({\n type: 'danger',\n message: formatMessage({\n id: getTranslationKey('list.bulk-actions.create-metadata.error'),\n defaultMessage: 'An error occurred while generating metadata.',\n }),\n });\n return;\n }\n\n const successCount = res.data.filter(({ status }) => status === 'success').length;\n const skippedCount = res.data.filter(({ status }) => status === 'skipped').length;\n const errorCount = res.data.filter(({ status }) => status === 'error').length;\n // Folders are never sent, so they are not part of the response — but the\n // user selected them, and a silent omission reads as a bug.\n const folderCount = selectedFolderIds.size;\n\n if (errorCount === res.data.length) {\n // Nothing was written — treat it like a request-level failure and keep\n // the selection so the user can retry.\n toggleNotification({\n type: 'danger',\n message: formatMessage({\n id: getTranslationKey('list.bulk-actions.create-metadata.error'),\n defaultMessage: 'An error occurred while generating metadata.',\n }),\n });\n return;\n }\n\n if (skippedCount === 0 && errorCount === 0 && folderCount === 0) {\n toggleNotification({\n type: 'success',\n message: formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.create-metadata.success'),\n defaultMessage:\n '{count, plural, =1 {Metadata generated for # asset} other {Metadata generated for # assets}}',\n },\n { count: successCount }\n ),\n });\n } else {\n // Partial outcome: report every bucket so the user knows what was left\n // out — including folders, which were never eligible in the first place.\n toggleNotification({\n type: 'warning',\n message: formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.create-metadata.partial'),\n defaultMessage:\n '{successCount} generated, {skippedCount} skipped (unsupported file type), {errorCount} failed{folderCount, plural, =0 {} one {, # folder ignored} other {, # folders ignored}}',\n },\n { successCount, skippedCount, errorCount, folderCount }\n ),\n });\n }\n\n clear();\n };\n\n // Every bulk action is behind `assets.update`; with nothing selected, or\n // without the permission, the bar has nothing to offer — drop it entirely\n // rather than show a count + \"Clear selection\" over no actions (mirrors the\n // drawer footer, which hides when no permitted action survives).\n //\n // With the drawer open the bar is hidden on mobile only, in CSS: there the two\n // are stacked at `bottom: 0` and the bar (popover, 500) would cover the\n // drawer's footer actions, since the drawer sits below the overlay token at\n // 200. From `medium` up the drawer is a side panel and the bar is a centered\n // pill, so they no longer collide.\n if (count === 0 || !canUpdate) {\n return null;\n }\n\n return (\n <Bar\n $isDrawerOpen={isDetailsDrawerOpen}\n $isStacked={isAiMetadataEnabled}\n tag=\"section\"\n role=\"region\"\n aria-label={formatMessage({\n id: getTranslationKey('list.bulk-actions.label'),\n defaultMessage: 'Bulk actions',\n })}\n >\n <Typography data-bar-slot=\"count\" fontWeight=\"bold\" textColor=\"neutral800\" marginRight={4}>\n {formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.selected-count'),\n defaultMessage: '{count, plural, =1 {# item selected} other {# items selected}}',\n },\n { count }\n )}\n </Typography>\n\n <TextButton onClick={handleSelectAll} marginRight={4} disabled={isBusy}>\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.select-all'),\n defaultMessage: 'Select all',\n })}\n </TextButton>\n\n {/* Past the early return the user always has `assets.update`, so the\n individual actions no longer re-check it. */}\n <ActionCluster data-bar-slot=\"actions\">\n {isAiMetadataEnabled && (\n <Tooltip label={metadataDisabledReason}>\n {/* Wrapped so the tooltip still receives pointer events while the\n button itself is disabled. */}\n <Box>\n <Button\n size=\"S\"\n startIcon={<Sparkle />}\n disabled={\n isBusy || selectedIds.size === 0 || isOverMetadataLimit || hasNoEligibleAssets\n }\n loading={isGeneratingMetadata}\n onClick={handleCreateMetadata}\n >\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.create-metadata'),\n defaultMessage: 'Create metadata',\n })}\n </Button>\n </Box>\n </Tooltip>\n )}\n\n <IconButton\n variant=\"tertiary\"\n disabled={isBusy}\n label={formatMessage({\n id: getTranslationKey('list.bulk-actions.move'),\n defaultMessage: 'Move',\n })}\n onClick={() => setIsMoveDialogOpen(true)}\n >\n <ArrowRight />\n </IconButton>\n <BulkMoveDialog\n open={isMoveDialogOpen}\n onClose={() => setIsMoveDialogOpen(false)}\n items={moveItems}\n onSuccess={clear}\n />\n\n <IconButton\n variant=\"danger-light\"\n disabled={isBusy}\n label={formatMessage({\n id: getTranslationKey('list.bulk-actions.delete'),\n defaultMessage: 'Delete',\n })}\n onClick={() => setIsDeleteDialogOpen(true)}\n >\n <Trash />\n </IconButton>\n <DeleteItemsDialog\n open={isDeleteDialogOpen}\n onClose={() => setIsDeleteDialogOpen(false)}\n target={{\n fileIds: Array.from(selectedIds),\n folderIds: Array.from(selectedFolderIds),\n }}\n onSuccess={clear}\n onPendingChange={setIsDeleting}\n />\n </ActionCluster>\n\n <VerticalDivider data-bar-slot=\"divider\" aria-hidden />\n\n <IconButton\n variant=\"ghost\"\n label={formatMessage({\n id: getTranslationKey('list.bulk-actions.clear'),\n defaultMessage: 'Clear selection',\n })}\n onClick={clear}\n disabled={isBusy}\n >\n <Cross />\n </IconButton>\n </Bar>\n );\n};\n"],"names":["Bar","styled","Flex","theme","zIndices","popover","spaces","colors","neutral0","neutral150","shadows","popupShadow","$isDrawerOpen","$isStacked","css","breakpoints","medium","borderRadius","ActionCluster","VerticalDivider","Box","BulkActionsBar","assets","locations","emptyItemLocations","renderedKeys","formatMessage","useIntl","toggleNotification","useNotification","isAiMetadataEnabled","useAIMetadataEnabled","canUpdate","useMediaLibraryPermissions","selectedIds","selectedFolderIds","selectAll","clear","useAssetSelection","trackUsage","useTracking","currentFolderId","useFolderNavigation","isDetailsDrawerOpen","useIsAssetDetailsOpen","generateAiMetadata","isLoading","isGeneratingMetadata","useGenerateAiMetadataMutation","isDeleteDialogOpen","setIsDeleteDialogOpen","useState","isMoveDialogOpen","setIsMoveDialogOpen","isDeleting","setIsDeleting","count","size","handleSelectAll","isBusy","moveItems","useMemo","buildDragSetFromSelection","isOverMetadataLimit","AI_METADATA_MAX_FILES","eligibleCount","mimeById","Map","map","id","mime","filter","isAIMetadataSupportedMime","get","length","hasNoEligibleAssets","metadataDisabledReason","getTranslationKey","defaultMessage","max","handleCreateMetadata","fileIds","Array","from","res","type","message","successCount","data","status","skippedCount","errorCount","folderCount","_jsxs","tag","role","aria-label","_jsx","Typography","data-bar-slot","fontWeight","textColor","marginRight","TextButton","onClick","disabled","Tooltip","label","Button","startIcon","Sparkle","loading","IconButton","variant","ArrowRight","BulkMoveDialog","open","onClose","items","onSuccess","Trash","DeleteItemsDialog","target","folderIds","onPendingChange","aria-hidden","Cross"],"mappings":"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"}
1
+ {"version":3,"file":"BulkActionsBar.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/BulkActionsBar.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\n\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport {\n Box,\n Button,\n Flex,\n IconButton,\n TextButton,\n Tooltip,\n Typography,\n} from '@strapi/design-system';\nimport { ArrowRight, Cross, Sparkle, Trash } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { css, styled } from 'styled-components';\n\nimport {\n AI_METADATA_MAX_FILES,\n isAIMetadataSupportedMime,\n} from '../../../../../../shared/constants';\nimport { useAIMetadataEnabled } from '../../../hooks/useAIMetadataEnabled';\nimport { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions';\nimport { useTracking } from '../../../hooks/useTracking';\nimport { useGenerateAiMetadataMutation } from '../../../services/assets';\nimport { buildDragSetFromSelection } from '../../../utils/buildDragSetFromSelection';\nimport { emptyItemLocations, type ItemLocations } from '../../../utils/itemLocations';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useFolderNavigation } from '../hooks/useFolderNavigation';\nimport { useIsAssetDetailsOpen } from '../hooks/useIsAssetDetailsOpen';\nimport { type ItemKey } from '../utils/selection';\n\nimport { BulkMoveDialog } from './BulkMoveDialog';\nimport { DeleteItemsDialog } from './DeleteItemsDialog';\n\nimport type { File } from '../../../../../../shared/contracts/files';\n\n/**\n * Bulk action bar for the future Media Library. Mobile: docked full-bleed to the\n * bottom edge (no radius, top border only). Desktop (medium+): a floating,\n * centered pill.\n */\nconst Bar = styled(Flex)<{ $isDrawerOpen: boolean; $isStacked: boolean }>`\n position: fixed;\n z-index: ${({ theme }) => theme.zIndices.popover};\n left: 0;\n right: 0;\n bottom: 0;\n align-items: center;\n gap: ${({ theme }) => theme.spaces[2]};\n padding: ${({ theme }) =>\n `${theme.spaces[3]} ${theme.spaces[2]} ${theme.spaces[3]} ${theme.spaces[6]}`};\n background: ${({ theme }) => theme.colors.neutral0};\n border: 0;\n border-top: 1px solid ${({ theme }) => theme.colors.neutral150};\n border-radius: 0;\n box-shadow: ${({ theme }) => theme.shadows.popupShadow};\n\n /* Docked full-bleed at the bottom on mobile, which is exactly where the open\n drawer keeps its own actions — so it steps aside there, and only there. */\n display: ${({ $isDrawerOpen }) => ($isDrawerOpen ? 'none' : 'flex')};\n\n /* Mobile with the metadata action present: the labelled button plus the icons\n no longer fit beside the count on one line, so the count takes a row of its\n own and every button drops to the next.\n\n Addressed by slot rather than by position: these rules used to use\n nth-child, which silently retargeted the moment a control was inserted\n into the row. */\n ${({ $isStacked }) =>\n $isStacked &&\n css`\n flex-wrap: wrap;\n justify-content: space-between;\n\n > [data-bar-slot='count'] {\n flex-basis: 100%;\n margin-right: 0;\n }\n\n > [data-bar-slot='actions'] {\n margin-left: 0;\n }\n\n /* The divider only existed to set the clear action apart from the rest;\n with the row spread it would hang in mid-air between them. */\n > [data-bar-slot='divider'] {\n display: none;\n }\n `}\n\n ${({ theme }) => theme.breakpoints.medium} {\n display: flex;\n left: 50%;\n right: auto;\n bottom: ${({ theme }) => theme.spaces[4]};\n transform: translateX(-50%);\n border: 1px solid ${({ theme }) => theme.colors.neutral150};\n border-radius: ${({ theme }) => theme.borderRadius};\n /* Sized by its content, capped so the pill can never span the whole\n viewport. The nowrap is what lets the content set that width — without it\n the labels wrap and the bar reads as narrow and tall. Inherited, so it\n covers every label inside.\n\n Deliberately not applied on mobile: there the bar is full-bleed and\n cannot grow, so refusing to wrap would clip the last action on a narrow\n phone rather than widen anything. */\n white-space: nowrap;\n max-width: 90%;\n\n /* One line again from tablet up, where it fits. */\n flex-wrap: nowrap;\n\n > [data-bar-slot='count'] {\n flex-basis: auto;\n }\n\n > [data-bar-slot='actions'] {\n margin-left: auto;\n }\n\n > [data-bar-slot='divider'] {\n display: block;\n }\n }\n`;\n\nconst ActionCluster = styled(Flex)`\n margin-left: auto;\n align-items: center;\n gap: ${({ theme }) => theme.spaces[2]};\n`;\n\nconst VerticalDivider = styled(Box)`\n width: 1px;\n align-self: stretch;\n background: ${({ theme }) => theme.colors.neutral150};\n margin-left: ${({ theme }) => theme.spaces[1]};\n`;\n\ninterface BulkActionsBarProps {\n /**\n * The assets currently rendered in the list, used to resolve the mime types\n * of the selection — the bar needs them to know which selected assets the AI\n * provider can actually read.\n *\n * Selection is page-scoped and cleared whenever the list identity changes, so\n * every selected asset id is guaranteed to appear here.\n */\n assets?: File[];\n /**\n * Real location of every loaded row, so the move dialog validates each\n * selected item against its own parent. Defaults to a lookup that misses\n * everything, which falls back to the folder currently open.\n */\n locations?: ItemLocations;\n /**\n * Keys of the items on screen, in render order. Owned by the view so\n * select-all covers exactly what the user can see — in mixed mode that is not\n * every folder.\n */\n renderedKeys?: ItemKey[];\n}\n\nexport const BulkActionsBar = ({\n assets = [],\n locations = emptyItemLocations,\n renderedKeys = [],\n}: BulkActionsBarProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n // No mime argument: the action is selection-scoped, so the per-asset\n // allowlist check happens below in `eligibleCount`.\n const isAiMetadataEnabled = useAIMetadataEnabled();\n // Every bulk action (move, delete, metadata) is an `assets.update` mutation\n // server-side — one flag gates the whole cluster.\n const { canUpdate } = useMediaLibraryPermissions();\n const { selectedIds, selectedFolderIds, selectAll, clear } = useAssetSelection();\n const { trackUsage } = useTracking();\n const { currentFolderId } = useFolderNavigation();\n const isDetailsDrawerOpen = useIsAssetDetailsOpen();\n const [generateAiMetadata, { isLoading: isGeneratingMetadata }] = useGenerateAiMetadataMutation();\n const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);\n const [isMoveDialogOpen, setIsMoveDialogOpen] = useState(false);\n // The delete request lives in DeleteItemsDialog; the bar mirrors its pending\n // state so the rest of the controls stay disabled for the duration.\n const [isDeleting, setIsDeleting] = useState(false);\n\n const count = selectedIds.size + selectedFolderIds.size;\n\n // `innerHeight - top` rather than the bar's height: it also spans the gap the\n // bar floats above the viewport edge (0 docked on mobile, `spaces[4]` for the\n // desktop pill), so one measurement covers both layouts and no height is\n // hardcoded.\n const [barEl, setBarEl] = useState<HTMLElement | null>(null);\n const [reservedSpace, setReservedSpace] = useState(0);\n\n useEffect(() => {\n if (!barEl) {\n setReservedSpace(0);\n return;\n }\n\n const measure = () => {\n const rect = barEl.getBoundingClientRect();\n // Zero height means the CSS has hidden the bar (drawer open on mobile) —\n // reserving anything then would leave a gap under nothing.\n setReservedSpace(rect.height === 0 ? 0 : Math.max(0, window.innerHeight - rect.top));\n };\n\n measure();\n\n const observer = new ResizeObserver(measure);\n observer.observe(barEl);\n window.addEventListener('resize', measure);\n\n return () => {\n observer.disconnect();\n window.removeEventListener('resize', measure);\n };\n // `isDetailsDrawerOpen` toggles the bar's `display`, which a ResizeObserver\n // is not guaranteed to report.\n }, [barEl, isDetailsDrawerOpen]);\n\n const handleSelectAll = () => {\n trackUsage('didSelectAllMediaLibraryElements');\n selectAll(renderedKeys);\n };\n const isBusy = isDeleting || isGeneratingMetadata;\n\n // Stable identity: the move dialog memoizes its destination walk on it. Each\n // item's real location comes from `locations`, so a selection made under a\n // global search validates against each item's own parent rather than the\n // folder currently open.\n const moveItems = useMemo(\n () => buildDragSetFromSelection(selectedIds, selectedFolderIds, locations, currentFolderId),\n [selectedIds, selectedFolderIds, locations, currentFolderId]\n );\n\n /**\n * The server processes the whole selection inside a single request and\n * rejects anything above the cap with a 400. Disable the action up front so\n * the limit is discoverable before the user commits to a request.\n */\n const isOverMetadataLimit = selectedIds.size > AI_METADATA_MAX_FILES;\n\n /**\n * How many selected assets the AI provider can actually read. The server\n * stays authoritative — it still reports what it skipped — but a selection\n * with nothing eligible (a lone PDF, say) would only ever come back fully\n * skipped, so there is no point sending it.\n */\n const eligibleCount = useMemo(() => {\n const mimeById = new Map(assets.map(({ id, mime }) => [id, mime]));\n\n return [...selectedIds].filter((id) => isAIMetadataSupportedMime(mimeById.get(id))).length;\n }, [assets, selectedIds]);\n\n const hasNoEligibleAssets = selectedIds.size > 0 && eligibleCount === 0;\n\n /**\n * Why the action is unavailable, surfaced on hover. `undefined` leaves the\n * button without a tooltip — either it is usable, or it is disabled for a\n * reason the selection count already makes obvious.\n */\n let metadataDisabledReason: string | undefined;\n\n if (isOverMetadataLimit) {\n metadataDisabledReason = formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.create-metadata.too-many'),\n defaultMessage:\n 'Metadata can be generated for up to {max} assets at a time. Select fewer assets to continue.',\n },\n { max: AI_METADATA_MAX_FILES }\n );\n } else if (hasNoEligibleAssets) {\n metadataDisabledReason = formatMessage({\n id: getTranslationKey('list.bulk-actions.create-metadata.no-eligible'),\n defaultMessage:\n 'Metadata can only be generated for images. None of the selected assets are supported.',\n });\n }\n\n /**\n * Metadata generation only applies to images — folders and unsupported files\n * in the selection are reported back rather than blocking the action.\n */\n const handleCreateMetadata = async () => {\n // Guard re-entry while pending, and never send a selection the server\n // would reject outright or skip in full.\n if (isGeneratingMetadata || isOverMetadataLimit || hasNoEligibleAssets) {\n return;\n }\n\n const fileIds = Array.from(selectedIds);\n const res = await generateAiMetadata({ fileIds });\n\n if ('error' in res) {\n // Keep the selection intact so the user can retry.\n toggleNotification({\n type: 'danger',\n message: formatMessage({\n id: getTranslationKey('list.bulk-actions.create-metadata.error'),\n defaultMessage: 'An error occurred while generating metadata.',\n }),\n });\n return;\n }\n\n const successCount = res.data.filter(({ status }) => status === 'success').length;\n const skippedCount = res.data.filter(({ status }) => status === 'skipped').length;\n const errorCount = res.data.filter(({ status }) => status === 'error').length;\n // Folders are never sent, so they are not part of the response — but the\n // user selected them, and a silent omission reads as a bug.\n const folderCount = selectedFolderIds.size;\n\n if (errorCount === res.data.length) {\n // Nothing was written — treat it like a request-level failure and keep\n // the selection so the user can retry.\n toggleNotification({\n type: 'danger',\n message: formatMessage({\n id: getTranslationKey('list.bulk-actions.create-metadata.error'),\n defaultMessage: 'An error occurred while generating metadata.',\n }),\n });\n return;\n }\n\n if (skippedCount === 0 && errorCount === 0 && folderCount === 0) {\n toggleNotification({\n type: 'success',\n message: formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.create-metadata.success'),\n defaultMessage:\n '{count, plural, =1 {Metadata generated for # asset} other {Metadata generated for # assets}}',\n },\n { count: successCount }\n ),\n });\n } else {\n // Partial outcome: report every bucket so the user knows what was left\n // out — including folders, which were never eligible in the first place.\n toggleNotification({\n type: 'warning',\n message: formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.create-metadata.partial'),\n defaultMessage:\n '{successCount} generated, {skippedCount} skipped (unsupported file type), {errorCount} failed{folderCount, plural, =0 {} one {, # folder ignored} other {, # folders ignored}}',\n },\n { successCount, skippedCount, errorCount, folderCount }\n ),\n });\n }\n\n clear();\n };\n\n // Every bulk action is behind `assets.update`; with nothing selected, or\n // without the permission, the bar has nothing to offer — drop it entirely\n // rather than show a count + \"Clear selection\" over no actions (mirrors the\n // drawer footer, which hides when no permitted action survives).\n //\n // With the drawer open the bar is hidden on mobile only, in CSS: there the two\n // are stacked at `bottom: 0` and the bar (popover, 500) would cover the\n // drawer's footer actions, since the drawer sits below the overlay token at\n // 200. From `medium` up the drawer is a side panel and the bar is a centered\n // pill, so they no longer collide.\n if (count === 0 || !canUpdate) {\n return null;\n }\n\n return (\n <>\n <Box aria-hidden height={`${reservedSpace}px`} data-bar-spacer />\n <Bar\n ref={setBarEl}\n $isDrawerOpen={isDetailsDrawerOpen}\n $isStacked={isAiMetadataEnabled}\n tag=\"section\"\n role=\"region\"\n // The bar floats over the list but is not part of its background: a\n // right-click on it keeps the browser's own menu. See MainAreaContextMenu.\n data-native-context-menu\n aria-label={formatMessage({\n id: getTranslationKey('list.bulk-actions.label'),\n defaultMessage: 'Bulk actions',\n })}\n >\n <Typography data-bar-slot=\"count\" fontWeight=\"bold\" textColor=\"neutral800\" marginRight={4}>\n {formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.selected-count'),\n defaultMessage: '{count, plural, =1 {# item selected} other {# items selected}}',\n },\n { count }\n )}\n </Typography>\n\n <TextButton onClick={handleSelectAll} marginRight={4} disabled={isBusy}>\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.select-all'),\n defaultMessage: 'Select all',\n })}\n </TextButton>\n\n {/* Past the early return the user always has `assets.update`, so the\n individual actions no longer re-check it. */}\n <ActionCluster data-bar-slot=\"actions\">\n {isAiMetadataEnabled && (\n <Tooltip label={metadataDisabledReason}>\n {/* Wrapped so the tooltip still receives pointer events while the\n button itself is disabled. */}\n <Box>\n <Button\n size=\"S\"\n startIcon={<Sparkle />}\n disabled={\n isBusy || selectedIds.size === 0 || isOverMetadataLimit || hasNoEligibleAssets\n }\n loading={isGeneratingMetadata}\n onClick={handleCreateMetadata}\n >\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.create-metadata'),\n defaultMessage: 'Create metadata',\n })}\n </Button>\n </Box>\n </Tooltip>\n )}\n\n <IconButton\n variant=\"tertiary\"\n disabled={isBusy}\n label={formatMessage({\n id: getTranslationKey('list.bulk-actions.move'),\n defaultMessage: 'Move',\n })}\n onClick={() => setIsMoveDialogOpen(true)}\n >\n <ArrowRight />\n </IconButton>\n <BulkMoveDialog\n open={isMoveDialogOpen}\n onClose={() => setIsMoveDialogOpen(false)}\n items={moveItems}\n onSuccess={clear}\n />\n\n <IconButton\n variant=\"danger-light\"\n disabled={isBusy}\n label={formatMessage({\n id: getTranslationKey('list.bulk-actions.delete'),\n defaultMessage: 'Delete',\n })}\n onClick={() => setIsDeleteDialogOpen(true)}\n >\n <Trash />\n </IconButton>\n <DeleteItemsDialog\n open={isDeleteDialogOpen}\n onClose={() => setIsDeleteDialogOpen(false)}\n target={{\n fileIds: Array.from(selectedIds),\n folderIds: Array.from(selectedFolderIds),\n }}\n onSuccess={clear}\n onPendingChange={setIsDeleting}\n />\n </ActionCluster>\n\n <VerticalDivider data-bar-slot=\"divider\" aria-hidden />\n\n <IconButton\n variant=\"ghost\"\n label={formatMessage({\n id: getTranslationKey('list.bulk-actions.clear'),\n defaultMessage: 'Clear selection',\n })}\n onClick={clear}\n disabled={isBusy}\n >\n <Cross />\n </IconButton>\n </Bar>\n </>\n );\n};\n"],"names":["Bar","styled","Flex","theme","zIndices","popover","spaces","colors","neutral0","neutral150","shadows","popupShadow","$isDrawerOpen","$isStacked","css","breakpoints","medium","borderRadius","ActionCluster","VerticalDivider","Box","BulkActionsBar","assets","locations","emptyItemLocations","renderedKeys","formatMessage","useIntl","toggleNotification","useNotification","isAiMetadataEnabled","useAIMetadataEnabled","canUpdate","useMediaLibraryPermissions","selectedIds","selectedFolderIds","selectAll","clear","useAssetSelection","trackUsage","useTracking","currentFolderId","useFolderNavigation","isDetailsDrawerOpen","useIsAssetDetailsOpen","generateAiMetadata","isLoading","isGeneratingMetadata","useGenerateAiMetadataMutation","isDeleteDialogOpen","setIsDeleteDialogOpen","useState","isMoveDialogOpen","setIsMoveDialogOpen","isDeleting","setIsDeleting","count","size","barEl","setBarEl","reservedSpace","setReservedSpace","useEffect","measure","rect","getBoundingClientRect","height","Math","max","window","innerHeight","top","observer","ResizeObserver","observe","addEventListener","disconnect","removeEventListener","handleSelectAll","isBusy","moveItems","useMemo","buildDragSetFromSelection","isOverMetadataLimit","AI_METADATA_MAX_FILES","eligibleCount","mimeById","Map","map","id","mime","filter","isAIMetadataSupportedMime","get","length","hasNoEligibleAssets","metadataDisabledReason","getTranslationKey","defaultMessage","handleCreateMetadata","fileIds","Array","from","res","type","message","successCount","data","status","skippedCount","errorCount","folderCount","_jsxs","_Fragment","_jsx","aria-hidden","data-bar-spacer","ref","tag","role","data-native-context-menu","aria-label","Typography","data-bar-slot","fontWeight","textColor","marginRight","TextButton","onClick","disabled","Tooltip","label","Button","startIcon","Sparkle","loading","IconButton","variant","ArrowRight","BulkMoveDialog","open","onClose","items","onSuccess","Trash","DeleteItemsDialog","target","folderIds","onPendingChange","Cross"],"mappings":"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"}
@@ -3,6 +3,7 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var React = require('react');
5
5
  var core = require('@dnd-kit/core');
6
+ var modifiers = require('@dnd-kit/modifiers');
6
7
  var strapiAdmin = require('@strapi/admin/strapi-admin');
7
8
  var designSystem = require('@strapi/design-system');
8
9
  var reactIntl = require('react-intl');
@@ -48,6 +49,11 @@ const resolveDestination = (overId)=>{
48
49
  // constant-length sensor array while disabling drag for users without the move
49
50
  // permission (see the note in the provider).
50
51
  const DRAG_DISABLED_DISTANCE = Number.MAX_SAFE_INTEGER;
52
+ // Without a modifier the overlay is anchored to the dragged node's top-left, so the
53
+ // chip trails the cursor by however far into the card or row the grab landed.
54
+ const OVERLAY_MODIFIERS = [
55
+ modifiers.snapCenterToCursor
56
+ ];
51
57
  const AssetsDndProvider = ({ children, locations = itemLocations.emptyItemLocations })=>{
52
58
  const { formatMessage } = reactIntl.useIntl();
53
59
  const getErrorMessage = useApiErrorMessage.useApiErrorMessage();
@@ -283,6 +289,7 @@ const AssetsDndProvider = ({ children, locations = itemLocations.emptyItemLocati
283
289
  }),
284
290
  /*#__PURE__*/ jsxRuntime.jsx(core.DragOverlay, {
285
291
  dropAnimation: null,
292
+ modifiers: OVERLAY_MODIFIERS,
286
293
  children: dragItems.length > 0 ? /*#__PURE__*/ jsxRuntime.jsx(DragOverlayChip.DragOverlayChip, {
287
294
  items: dragItems
288
295
  }) : null
@@ -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 { Flex, VisuallyHidden } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nimport { useApiErrorMessage } from '../../../../hooks/useApiErrorMessage';\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 { 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 getErrorMessage = useApiErrorMessage();\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 = getErrorMessage(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 getErrorMessage,\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","getErrorMessage","useApiErrorMessage","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","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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"}
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 { snapCenterToCursor } from '@dnd-kit/modifiers';\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport { Flex, VisuallyHidden } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nimport { useApiErrorMessage } from '../../../../hooks/useApiErrorMessage';\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 { 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\n// Without a modifier the overlay is anchored to the dragged node's top-left, so the\n// chip trails the cursor by however far into the card or row the grab landed.\nconst OVERLAY_MODIFIERS = [snapCenterToCursor];\n\nexport const AssetsDndProvider = ({\n children,\n locations = emptyItemLocations,\n}: AssetsDndProviderProps) => {\n const { formatMessage } = useIntl();\n const getErrorMessage = useApiErrorMessage();\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 = getErrorMessage(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 getErrorMessage,\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} modifiers={OVERLAY_MODIFIERS}>\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","OVERLAY_MODIFIERS","snapCenterToCursor","AssetsDndProvider","children","locations","emptyItemLocations","formatMessage","useIntl","getErrorMessage","useApiErrorMessage","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","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","modifiers","DragOverlayChip"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  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
+ import { snapCenterToCursor } from '@dnd-kit/modifiers';
4
5
  import { useNotification } from '@strapi/admin/strapi-admin';
5
6
  import { VisuallyHidden, Flex } from '@strapi/design-system';
6
7
  import { useIntl } from 'react-intl';
@@ -46,6 +47,11 @@ const resolveDestination = (overId)=>{
46
47
  // constant-length sensor array while disabling drag for users without the move
47
48
  // permission (see the note in the provider).
48
49
  const DRAG_DISABLED_DISTANCE = Number.MAX_SAFE_INTEGER;
50
+ // Without a modifier the overlay is anchored to the dragged node's top-left, so the
51
+ // chip trails the cursor by however far into the card or row the grab landed.
52
+ const OVERLAY_MODIFIERS = [
53
+ snapCenterToCursor
54
+ ];
49
55
  const AssetsDndProvider = ({ children, locations = emptyItemLocations })=>{
50
56
  const { formatMessage } = useIntl();
51
57
  const getErrorMessage = useApiErrorMessage();
@@ -281,6 +287,7 @@ const AssetsDndProvider = ({ children, locations = emptyItemLocations })=>{
281
287
  }),
282
288
  /*#__PURE__*/ jsx(DragOverlay, {
283
289
  dropAnimation: null,
290
+ modifiers: OVERLAY_MODIFIERS,
284
291
  children: dragItems.length > 0 ? /*#__PURE__*/ jsx(DragOverlayChip, {
285
292
  items: dragItems
286
293
  }) : null