@strapi/upload 5.51.2 → 5.52.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/dist/admin/future/App.js +3 -3
  2. package/dist/admin/future/App.js.map +1 -1
  3. package/dist/admin/future/App.mjs +4 -4
  4. package/dist/admin/future/App.mjs.map +1 -1
  5. package/dist/admin/future/components/BetaNotice.js +72 -0
  6. package/dist/admin/future/components/BetaNotice.js.map +1 -0
  7. package/dist/admin/future/components/BetaNotice.mjs +70 -0
  8. package/dist/admin/future/components/BetaNotice.mjs.map +1 -0
  9. package/dist/admin/future/components/Drawer.js +16 -3
  10. package/dist/admin/future/components/Drawer.js.map +1 -1
  11. package/dist/admin/future/components/Drawer.mjs +16 -3
  12. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  13. package/dist/admin/future/components/TruncatedText.js +76 -0
  14. package/dist/admin/future/components/TruncatedText.js.map +1 -0
  15. package/dist/admin/future/components/TruncatedText.mjs +74 -0
  16. package/dist/admin/future/components/TruncatedText.mjs.map +1 -0
  17. package/dist/admin/future/hooks/useAIMetadataEnabled.js +39 -0
  18. package/dist/admin/future/hooks/useAIMetadataEnabled.js.map +1 -0
  19. package/dist/admin/future/hooks/useAIMetadataEnabled.mjs +37 -0
  20. package/dist/admin/future/hooks/useAIMetadataEnabled.mjs.map +1 -0
  21. package/dist/admin/future/hooks/useTracking.js +57 -0
  22. package/dist/admin/future/hooks/useTracking.js.map +1 -0
  23. package/dist/admin/future/hooks/useTracking.mjs +53 -0
  24. package/dist/admin/future/hooks/useTracking.mjs.map +1 -0
  25. package/dist/admin/future/pages/Assets/AssetsPage.js +328 -139
  26. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/AssetsPage.mjs +332 -143
  28. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js +46 -0
  30. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js.map +1 -0
  31. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs +44 -0
  32. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs.map +1 -0
  33. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +328 -0
  34. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -0
  35. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +326 -0
  36. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -0
  37. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +35 -14
  38. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +35 -14
  40. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +74 -53
  42. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +74 -54
  44. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +92 -28
  46. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  47. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +94 -30
  48. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  49. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +26 -4
  50. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +27 -5
  52. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/components/AssetsTable.js +172 -60
  54. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +175 -63
  56. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +64 -132
  58. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  59. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +64 -132
  60. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  61. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +123 -76
  62. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
  63. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +126 -79
  64. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
  65. package/dist/admin/future/pages/Assets/components/BusyOverlay.js +36 -0
  66. package/dist/admin/future/pages/Assets/components/BusyOverlay.js.map +1 -0
  67. package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs +34 -0
  68. package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs.map +1 -0
  69. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js +131 -0
  70. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js.map +1 -0
  71. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs +129 -0
  72. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -0
  73. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +27 -17
  74. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  75. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +28 -18
  76. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  77. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +28 -31
  78. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -1
  79. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +28 -31
  80. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -1
  81. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +10 -15
  82. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
  83. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +10 -15
  84. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
  85. package/dist/admin/future/pages/Assets/components/FilterBadges.js +13 -4
  86. package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -1
  87. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +13 -4
  88. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -1
  89. package/dist/admin/future/pages/Assets/components/FilterMenu.js +259 -63
  90. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
  91. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +262 -66
  92. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
  93. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +146 -0
  94. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -0
  95. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +144 -0
  96. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -0
  97. package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.js → FolderFormDialog.js} +62 -16
  98. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -0
  99. package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.mjs → FolderFormDialog.mjs} +64 -18
  100. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -0
  101. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +132 -229
  102. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  103. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +135 -232
  104. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  105. package/dist/admin/future/pages/Assets/components/SortMenu.js +14 -0
  106. package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
  107. package/dist/admin/future/pages/Assets/components/SortMenu.mjs +14 -0
  108. package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
  109. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +5 -0
  110. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
  111. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +6 -1
  112. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
  113. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js +47 -0
  114. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js.map +1 -0
  115. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs +44 -0
  116. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs.map +1 -0
  117. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +203 -62
  118. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  119. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +204 -63
  120. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  121. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js +75 -0
  122. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js.map +1 -0
  123. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs +73 -0
  124. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs.map +1 -0
  125. package/dist/admin/future/pages/Assets/utils/selection.js +17 -0
  126. package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -1
  127. package/dist/admin/future/pages/Assets/utils/selection.mjs +17 -1
  128. package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -1
  129. package/dist/admin/future/services/api.js +31 -8
  130. package/dist/admin/future/services/api.js.map +1 -1
  131. package/dist/admin/future/services/api.mjs +31 -8
  132. package/dist/admin/future/services/api.mjs.map +1 -1
  133. package/dist/admin/future/services/assets.js +22 -23
  134. package/dist/admin/future/services/assets.js.map +1 -1
  135. package/dist/admin/future/services/assets.mjs +22 -23
  136. package/dist/admin/future/services/assets.mjs.map +1 -1
  137. package/dist/admin/future/services/folders.js +37 -2
  138. package/dist/admin/future/services/folders.js.map +1 -1
  139. package/dist/admin/future/services/folders.mjs +37 -3
  140. package/dist/admin/future/services/folders.mjs.map +1 -1
  141. package/dist/admin/future/utils/buildDragSetFromSelection.js +7 -5
  142. package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -1
  143. package/dist/admin/future/utils/buildDragSetFromSelection.mjs +7 -5
  144. package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -1
  145. package/dist/admin/future/utils/formatMoveSuccessMessage.js +12 -1
  146. package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -1
  147. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +12 -1
  148. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -1
  149. package/dist/admin/future/utils/getRelationId.js +24 -0
  150. package/dist/admin/future/utils/getRelationId.js.map +1 -0
  151. package/dist/admin/future/utils/getRelationId.mjs +22 -0
  152. package/dist/admin/future/utils/getRelationId.mjs.map +1 -0
  153. package/dist/admin/future/utils/isEventFromWithin.js +14 -0
  154. package/dist/admin/future/utils/isEventFromWithin.js.map +1 -0
  155. package/dist/admin/future/utils/isEventFromWithin.mjs +12 -0
  156. package/dist/admin/future/utils/isEventFromWithin.mjs.map +1 -0
  157. package/dist/admin/future/utils/itemLocations.js +40 -0
  158. package/dist/admin/future/utils/itemLocations.js.map +1 -0
  159. package/dist/admin/future/utils/itemLocations.mjs +36 -0
  160. package/dist/admin/future/utils/itemLocations.mjs.map +1 -0
  161. package/dist/admin/future/utils/itemSource.js +36 -0
  162. package/dist/admin/future/utils/itemSource.js.map +1 -0
  163. package/dist/admin/future/utils/itemSource.mjs +32 -0
  164. package/dist/admin/future/utils/itemSource.mjs.map +1 -0
  165. package/dist/admin/hooks/useAIMetadataJob.js +1 -1
  166. package/dist/admin/hooks/useAIMetadataJob.js.map +1 -1
  167. package/dist/admin/hooks/useAIMetadataJob.mjs +1 -1
  168. package/dist/admin/hooks/useAIMetadataJob.mjs.map +1 -1
  169. package/dist/admin/index.js +11 -17
  170. package/dist/admin/index.js.map +1 -1
  171. package/dist/admin/index.mjs +12 -18
  172. package/dist/admin/index.mjs.map +1 -1
  173. package/dist/admin/pages/SettingsPage/SettingsPage.js +2 -2
  174. package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
  175. package/dist/admin/pages/SettingsPage/SettingsPage.mjs +2 -2
  176. package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
  177. package/dist/admin/src/future/App.d.ts +1 -1
  178. package/dist/admin/src/future/components/BetaNotice.d.ts +11 -0
  179. package/dist/admin/src/future/components/Drawer.d.ts +5 -1
  180. package/dist/admin/src/future/components/TruncatedText.d.ts +32 -0
  181. package/dist/admin/src/future/hooks/useAIMetadataEnabled.d.ts +26 -0
  182. package/dist/admin/src/future/hooks/useTracking.d.ts +29 -0
  183. package/dist/admin/src/future/pages/Assets/components/ActionsMenuContent.d.ts +33 -0
  184. package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +26 -0
  185. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +16 -0
  186. package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +8 -1
  187. package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +17 -6
  188. package/dist/admin/src/future/pages/Assets/components/BusyOverlay.d.ts +9 -0
  189. package/dist/admin/src/future/pages/Assets/components/DeleteItemsDialog.d.ts +27 -0
  190. package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +8 -1
  191. package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +18 -3
  192. package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +2 -1
  193. package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +3 -1
  194. package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +4 -0
  195. package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +21 -0
  196. package/dist/admin/src/future/pages/Assets/components/FolderFormDialog.d.ts +16 -0
  197. package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +2 -0
  198. package/dist/admin/src/future/pages/Assets/hooks/useBusyAssets.d.ts +38 -0
  199. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +20 -6
  200. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteScrollSentinel.d.ts +41 -0
  201. package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +10 -0
  202. package/dist/admin/src/future/services/api.d.ts +6 -7
  203. package/dist/admin/src/future/services/folders.d.ts +6 -1
  204. package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +4 -3
  205. package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +7 -1
  206. package/dist/admin/src/future/utils/getRelationId.d.ts +11 -0
  207. package/dist/admin/src/future/utils/isEventFromWithin.d.ts +11 -0
  208. package/dist/admin/src/future/utils/itemLocations.d.ts +31 -0
  209. package/dist/admin/src/future/utils/itemSource.d.ts +22 -0
  210. package/dist/admin/translations/en.json.js +25 -4
  211. package/dist/admin/translations/en.json.js.map +1 -1
  212. package/dist/admin/translations/en.json.mjs +25 -4
  213. package/dist/admin/translations/en.json.mjs.map +1 -1
  214. package/dist/server/controllers/admin-file.js +27 -9
  215. package/dist/server/controllers/admin-file.js.map +1 -1
  216. package/dist/server/controllers/admin-file.mjs +27 -9
  217. package/dist/server/controllers/admin-file.mjs.map +1 -1
  218. package/dist/server/controllers/admin-upload.js +24 -8
  219. package/dist/server/controllers/admin-upload.js.map +1 -1
  220. package/dist/server/controllers/admin-upload.mjs +24 -8
  221. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  222. package/dist/server/routes/admin.js +41 -21
  223. package/dist/server/routes/admin.js.map +1 -1
  224. package/dist/server/routes/admin.mjs +41 -21
  225. package/dist/server/routes/admin.mjs.map +1 -1
  226. package/dist/server/services/ai-metadata.js.map +1 -1
  227. package/dist/server/services/ai-metadata.mjs.map +1 -1
  228. package/dist/server/services/image-manipulation.js.map +1 -1
  229. package/dist/server/services/image-manipulation.mjs.map +1 -1
  230. package/dist/server/src/controllers/admin-file.d.ts +29 -8
  231. package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
  232. package/dist/server/src/controllers/admin-upload.d.ts +20 -4
  233. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  234. package/dist/server/src/controllers/index.d.ts +4 -4
  235. package/dist/server/src/index.d.ts +4 -4
  236. package/dist/server/src/routes/admin.d.ts.map +1 -1
  237. package/dist/server/src/services/ai-metadata.d.ts +2 -2
  238. package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
  239. package/dist/server/src/services/image-manipulation.d.ts +0 -5
  240. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  241. package/dist/shared/contracts/ai-metadata-jobs.d.ts +10 -32
  242. package/dist/shared/contracts/ai-metadata-jobs.d.ts.map +1 -1
  243. package/dist/shared/contracts/files.d.ts +10 -10
  244. package/dist/shared/contracts/files.d.ts.map +1 -1
  245. package/package.json +9 -9
  246. package/dist/admin/future/pages/Assets/components/CreateFolderDialog.js.map +0 -1
  247. package/dist/admin/future/pages/Assets/components/CreateFolderDialog.mjs.map +0 -1
  248. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +0 -33
  249. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +0 -1
  250. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +0 -31
  251. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +0 -1
  252. package/dist/admin/src/future/pages/Assets/components/CreateFolderDialog.d.ts +0 -8
  253. package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +0 -16
@@ -1,22 +1,38 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { Grid, Box, Flex, Checkbox, IconButton, CardBody, Typography, Card, CardHeader } from '@strapi/design-system';
3
- import { Folder, More } from '@strapi/icons';
2
+ import { Grid, Box, Flex, Checkbox, CardBody, Card, CardHeader } from '@strapi/design-system';
3
+ import { Folder } from '@strapi/icons';
4
4
  import { useIntl } from 'react-intl';
5
5
  import { styled, css } from 'styled-components';
6
6
  import { ASSET_TYPES } from '../../../../enums.mjs';
7
+ import { TruncatedText } from '../../../components/TruncatedText.mjs';
7
8
  import { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions.mjs';
8
9
  import { prefixFileUrlWithBackendUrl } from '../../../utils/files.mjs';
9
10
  import { getAssetIcon } from '../../../utils/getAssetIcon.mjs';
11
+ import { isEventFromWithin } from '../../../utils/isEventFromWithin.mjs';
10
12
  import { getTranslationKey } from '../../../utils/translations.mjs';
11
13
  import { useAssetSelection } from '../hooks/useAssetSelection.mjs';
14
+ import { useBusyAssetsOptional } from '../hooks/useBusyAssets.mjs';
12
15
  import { useFolderNavigation } from '../hooks/useFolderNavigation.mjs';
13
16
  import { folderKey, assetKey } from '../utils/selection.mjs';
17
+ import { AssetActionsMenu } from './AssetActionsMenu.mjs';
18
+ import { BusyOverlay } from './BusyOverlay.mjs';
14
19
  import { useAssetsDndOptional } from './Dnd/AssetsDndProvider.mjs';
15
20
  import { useFolderDraggableDroppable, useFileDraggable } from './Dnd/useAssetDnd.mjs';
21
+ import { FolderActionsMenu } from './FolderActionsMenu.mjs';
16
22
 
17
23
  /* -------------------------------------------------------------------------------------------------
18
24
  * AssetsGrid
19
- * -----------------------------------------------------------------------------------------------*/ // Top-left selection checkbox overlaid on the asset preview, always visible.
25
+ * -----------------------------------------------------------------------------------------------*/ // Cards own click, Enter and Space; the controls sitting on them (checkbox, "..."
26
+ // menu) must not let those reach the card. Scoped to the wrapper's own DOM
27
+ // subtree so the menu's portaled content — a React child of the wrapper, but
28
+ // never a DOM descendant — still reaches `document`, where Radix listens in
29
+ // order to dismiss its layers.
30
+ const stopCardEvent = (e)=>{
31
+ if (isEventFromWithin(e)) {
32
+ e.stopPropagation();
33
+ }
34
+ };
35
+ // Top-left selection checkbox overlaid on the asset preview, always visible.
20
36
  const CheckboxOverlay = styled(Flex)`
21
37
  position: absolute;
22
38
  top: ${({ theme })=>theme.spaces[3]};
@@ -29,9 +45,11 @@ const StyledCard = styled(Card)`
29
45
  ${({ theme, $isSelected })=>$isSelected ? theme.colors.primary600 : theme.colors.neutral200};
30
46
  border-radius: 8px;
31
47
  overflow: hidden;
32
- cursor: ${({ $isMovePending })=>$isMovePending ? 'wait' : 'pointer'};
48
+ cursor: ${({ $isMovePending, $isBusy })=>$isMovePending || $isBusy ? 'wait' : 'pointer'};
33
49
  opacity: ${({ $isDragging })=>$isDragging ? 0.4 : 1};
34
- pointer-events: ${({ $isMovePending })=>$isMovePending ? 'none' : 'auto'};
50
+ /* No opacity change while busy the overlay does the dimming, and stacking
51
+ one on the other would wash the card out. */
52
+ pointer-events: ${({ $isMovePending, $isBusy })=>$isMovePending || $isBusy ? 'none' : 'auto'};
35
53
  background: ${({ theme, $isSelected })=>$isSelected ? theme.colors.primary100 : undefined};
36
54
  /* Shift+click range selection must not highlight card text. */
37
55
  user-select: none;
@@ -88,7 +106,7 @@ const FolderIconContainer = styled(Flex)`
88
106
  flex-shrink: 0;
89
107
  color: ${({ theme })=>theme.colors.neutral600};
90
108
  `;
91
- const FolderName = styled(Typography)`
109
+ const FolderName = styled(TruncatedText)`
92
110
  flex: 1;
93
111
  min-width: 0;
94
112
  `;
@@ -100,7 +118,7 @@ const FolderCard = ({ folder, orderedItemKeys })=>{
100
118
  };
101
119
  const { isSelected, toggle, selectRange } = useAssetSelection();
102
120
  const { canUpdate } = useMediaLibraryPermissions();
103
- const { draggable: { attributes, listeners, setNodeRef: setDragRef, isDragging }, droppable: { setNodeRef: setDropRef }, showValidDropHighlight, showInvalidDropCursor } = useFolderDraggableDroppable(folder);
121
+ const { dragData, draggable: { attributes, listeners, setNodeRef: setDragRef, isDragging }, droppable: { setNodeRef: setDropRef }, showValidDropHighlight, showInvalidDropCursor } = useFolderDraggableDroppable(folder);
104
122
  const key = folderKey(folder.id);
105
123
  const setNodeRef = (node)=>{
106
124
  setDragRef(node);
@@ -109,7 +127,14 @@ const FolderCard = ({ folder, orderedItemKeys })=>{
109
127
  // Folders share the selection mechanism with assets (toggle, range,
110
128
  // select-all). Only the plain-click semantic differs: it navigates into the
111
129
  // folder instead of selecting it.
130
+ //
131
+ // The containment guard on this and the handlers below is what keeps the
132
+ // actions menu's portaled dialogs — React children of this card — from
133
+ // navigating into the folder or starting a drag.
112
134
  const handleClick = (e)=>{
135
+ if (!isEventFromWithin(e)) {
136
+ return;
137
+ }
113
138
  if (e.shiftKey) {
114
139
  selectRange(orderedItemKeys, key);
115
140
  } else if (e.metaKey || e.ctrlKey) {
@@ -119,6 +144,9 @@ const FolderCard = ({ folder, orderedItemKeys })=>{
119
144
  }
120
145
  };
121
146
  const handleKeyDown = (e)=>{
147
+ if (!isEventFromWithin(e)) {
148
+ return;
149
+ }
122
150
  if (e.key === 'Enter') {
123
151
  e.preventDefault();
124
152
  navigateToFolder(folder);
@@ -147,6 +175,11 @@ const FolderCard = ({ folder, orderedItemKeys })=>{
147
175
  $isSelected: isSelected(key),
148
176
  onClick: handleClick,
149
177
  onKeyDown: handleKeyDown,
178
+ onPointerDown: (e)=>{
179
+ if (isEventFromWithin(e)) {
180
+ listeners?.onPointerDown?.(e);
181
+ }
182
+ },
150
183
  role: "listitem",
151
184
  tabIndex: 0,
152
185
  children: [
@@ -171,17 +204,16 @@ const FolderCard = ({ folder, orderedItemKeys })=>{
171
204
  }),
172
205
  /*#__PURE__*/ jsx(FolderName, {
173
206
  textColor: "neutral800",
174
- ellipsis: true,
175
207
  children: folder.name
176
208
  }),
177
- /*#__PURE__*/ jsx(IconButton, {
178
- label: formatMessage({
179
- id: getTranslationKey('control-card.more-actions'),
180
- defaultMessage: 'More actions'
181
- }),
182
- variant: "ghost",
183
- onClick: (e)=>e.stopPropagation(),
184
- children: /*#__PURE__*/ jsx(More, {})
209
+ /*#__PURE__*/ jsx(Flex, {
210
+ onClick: stopCardEvent,
211
+ onKeyDown: stopCardEvent,
212
+ onPointerDown: stopCardEvent,
213
+ children: /*#__PURE__*/ jsx(FolderActionsMenu, {
214
+ folder: folder,
215
+ dragData: dragData
216
+ })
185
217
  })
186
218
  ]
187
219
  });
@@ -218,9 +250,21 @@ const IconPreview = styled(Flex)`
218
250
  background: ${({ theme })=>theme.colors.neutral100};
219
251
  `;
220
252
  const AssetPreview = ({ asset })=>{
221
- const { alternativeText, ext, formats, mime, url, isLocal, isUrlSigned } = asset;
253
+ const { alternativeText, ext, formats, mime, url, updatedAt, isLocal, isUrlSigned } = asset;
222
254
  if (mime?.includes(ASSET_TYPES.Image)) {
223
- const mediaURL = prefixFileUrlWithBackendUrl(formats?.thumbnail?.url) ?? prefixFileUrlWithBackendUrl(url);
255
+ // Replace keeps the same hash/URL, so without a cache-buster the browser
256
+ // serves the stale thumbnail after a Replace Media. `updatedAt`
257
+ // changes on replace, so it re-fetches exactly once per replacement.
258
+ // Skipped for signed URLs — an extra query param invalidates the signature.
259
+ const cacheKey = updatedAt && !isUrlSigned ? new Date(updatedAt).getTime() : undefined;
260
+ const appendCacheBuster = (raw)=>{
261
+ if (cacheKey === undefined) {
262
+ return raw;
263
+ }
264
+ return raw.includes('?') ? `${raw}&v=${cacheKey}` : `${raw}?v=${cacheKey}`;
265
+ };
266
+ const rawMediaURL = prefixFileUrlWithBackendUrl(formats?.thumbnail?.url) ?? prefixFileUrlWithBackendUrl(url);
267
+ const mediaURL = rawMediaURL ? appendCacheBuster(rawMediaURL) : rawMediaURL;
224
268
  if (mediaURL) {
225
269
  return /*#__PURE__*/ jsx(PreviewContainer, {
226
270
  children: /*#__PURE__*/ jsx(StyledImage, {
@@ -262,7 +306,7 @@ const FileTypeIcon = styled(Flex)`
262
306
  color: ${({ theme })=>theme.colors.neutral600};
263
307
  flex-shrink: 0;
264
308
  `;
265
- const FileName = styled(Typography)`
309
+ const FileName = styled(TruncatedText)`
266
310
  flex: 1;
267
311
  min-width: 0;
268
312
  `;
@@ -293,15 +337,23 @@ const AssetCard = ({ asset, orderedItemKeys, onAssetItemClick })=>{
293
337
  const { isMovePending } = useAssetsDndOptional() ?? {
294
338
  isMovePending: false
295
339
  };
296
- const { attributes, listeners, setNodeRef, isDragging } = useFileDraggable(asset);
340
+ const { attributes, listeners, setNodeRef, isDragging, dragData } = useFileDraggable(asset);
297
341
  const { isSelected, toggle, selectRange } = useAssetSelection();
298
342
  const { canUpdate } = useMediaLibraryPermissions();
343
+ const busyMessage = useBusyAssetsOptional()?.getBusyMessage(asset.id) ?? null;
299
344
  const key = assetKey(asset.id);
300
345
  const selected = isSelected(key);
301
346
  // Plain click opens the asset details; pointer selection lives on the
302
347
  // checkbox only. Modifier clicks keep the selection semantics: shift selects
303
348
  // a range, cmd/ctrl toggles.
349
+ //
350
+ // The containment guard on this and the handler below is what keeps the
351
+ // actions menu's portaled dialogs — React children of this card — from
352
+ // opening the details drawer or toggling the selection.
304
353
  const handleCardClick = (e)=>{
354
+ if (!isEventFromWithin(e)) {
355
+ return;
356
+ }
305
357
  if (e.shiftKey) {
306
358
  selectRange(orderedItemKeys, key);
307
359
  } else if (e.metaKey || e.ctrlKey) {
@@ -312,6 +364,9 @@ const AssetCard = ({ asset, orderedItemKeys, onAssetItemClick })=>{
312
364
  };
313
365
  // Space toggles selection (additive), Enter opens the details drawer.
314
366
  const handleKeyDown = (e)=>{
367
+ if (!isEventFromWithin(e)) {
368
+ return;
369
+ }
315
370
  if (e.key === 'Enter') {
316
371
  e.preventDefault();
317
372
  onAssetItemClick(asset.id);
@@ -340,12 +395,18 @@ const AssetCard = ({ asset, orderedItemKeys, onAssetItemClick })=>{
340
395
  ...listeners,
341
396
  $isDragging: isDragging,
342
397
  $isMovePending: isMovePending,
398
+ $isBusy: busyMessage !== null,
343
399
  $isSelected: selected,
344
400
  tabIndex: 0,
345
401
  role: "listitem",
346
402
  onDragStart: (e)=>e.preventDefault(),
347
403
  onClick: handleCardClick,
348
404
  onKeyDown: handleKeyDown,
405
+ onPointerDown: (e)=>{
406
+ if (isEventFromWithin(e)) {
407
+ listeners?.onPointerDown?.(e);
408
+ }
409
+ },
349
410
  children: [
350
411
  /*#__PURE__*/ jsxs(StyledCardHeader, {
351
412
  children: [
@@ -364,7 +425,11 @@ const AssetCard = ({ asset, orderedItemKeys, onAssetItemClick })=>{
364
425
  }),
365
426
  /*#__PURE__*/ jsx(AssetPreview, {
366
427
  asset: asset
367
- })
428
+ }),
429
+ busyMessage !== null ? /*#__PURE__*/ jsx(BusyOverlay, {
430
+ zIndex: 2,
431
+ children: busyMessage
432
+ }) : null
368
433
  ]
369
434
  }),
370
435
  /*#__PURE__*/ jsx(CardBody, {
@@ -383,18 +448,17 @@ const AssetCard = ({ asset, orderedItemKeys, onAssetItemClick })=>{
383
448
  onClick: handleNameClick,
384
449
  children: /*#__PURE__*/ jsx(FileName, {
385
450
  textColor: "primary800",
386
- ellipsis: true,
387
451
  children: asset.name
388
452
  })
389
453
  }),
390
- /*#__PURE__*/ jsx(IconButton, {
391
- label: formatMessage({
392
- id: getTranslationKey('control-card.more-actions'),
393
- defaultMessage: 'More actions'
394
- }),
395
- variant: "ghost",
396
- onClick: (e)=>e.stopPropagation(),
397
- children: /*#__PURE__*/ jsx(More, {})
454
+ /*#__PURE__*/ jsx(Flex, {
455
+ onClick: stopCardEvent,
456
+ onKeyDown: stopCardEvent,
457
+ onPointerDown: stopCardEvent,
458
+ children: /*#__PURE__*/ jsx(AssetActionsMenu, {
459
+ asset: asset,
460
+ dragData: dragData
461
+ })
398
462
  })
399
463
  ]
400
464
  })
@@ -1 +1 @@
1
- {"version":3,"file":"AssetsGrid.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/AssetsGrid.tsx"],"sourcesContent":["import {\n Box,\n Card,\n CardBody,\n CardHeader,\n Checkbox,\n Flex,\n Grid,\n IconButton,\n Typography,\n} from '@strapi/design-system';\nimport { Folder as FolderIcon, More } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled, css } from 'styled-components';\n\nimport { ASSET_TYPES } from '../../../../enums';\nimport { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions';\nimport { prefixFileUrlWithBackendUrl } from '../../../utils/files';\nimport { getAssetIcon } from '../../../utils/getAssetIcon';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useFolderNavigation } from '../hooks/useFolderNavigation';\nimport { assetKey, folderKey, type ItemKey } from '../utils/selection';\n\nimport { useAssetsDndOptional } from './Dnd/AssetsDndProvider';\nimport { useFileDraggable, useFolderDraggableDroppable } from './Dnd/useAssetDnd';\n\nimport type { File } from '../../../../../../shared/contracts/files';\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\n/* -------------------------------------------------------------------------------------------------\n * AssetsGrid\n * -----------------------------------------------------------------------------------------------*/\n\n// Top-left selection checkbox overlaid on the asset preview, always visible.\nconst CheckboxOverlay = styled(Flex)`\n position: absolute;\n top: ${({ theme }) => theme.spaces[3]};\n left: ${({ theme }) => theme.spaces[3]};\n z-index: 1;\n box-shadow: ${({ theme }) => theme.shadows.filterShadow};\n`;\n\nconst StyledCard = styled(Card)<{\n $isDragging?: boolean;\n $isMovePending?: boolean;\n $isSelected?: boolean;\n}>`\n border: 1px solid\n ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary600 : theme.colors.neutral200)};\n border-radius: 8px;\n overflow: hidden;\n cursor: ${({ $isMovePending }) => ($isMovePending ? 'wait' : 'pointer')};\n opacity: ${({ $isDragging }) => ($isDragging ? 0.4 : 1)};\n pointer-events: ${({ $isMovePending }) => ($isMovePending ? 'none' : 'auto')};\n background: ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary100 : undefined)};\n /* Shift+click range selection must not highlight card text. */\n user-select: none;\n\n &:hover {\n background: ${({ theme }) => theme.colors.primary100};\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * FolderCard\n * -----------------------------------------------------------------------------------------------*/\n\nconst FoldersRow = styled(Box)`\n grid-column: 1 / -1;\n`;\n\nconst StyledFolderCard = styled(Flex)<{\n $isDragging?: boolean;\n $isMovePending?: boolean;\n $isValidDropTarget?: boolean;\n $isInvalidDropTarget?: boolean;\n $isSelected?: boolean;\n}>`\n width: 100%;\n user-select: none;\n padding: ${({ theme }) => `${theme.spaces[2]} ${theme.spaces[3]}`}; // 8px 12px\n align-items: center;\n gap: ${({ theme }) => theme.spaces[2]}; // 8px\n border: 1px solid\n ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary600 : theme.colors.neutral200)};\n border-radius: ${({ theme }) => theme.borderRadius};\n background: ${({ theme, $isSelected }) =>\n $isSelected ? theme.colors.primary100 : theme.colors.neutral0};\n cursor: ${({ $isMovePending, $isInvalidDropTarget }) => {\n if ($isMovePending) {\n return 'wait';\n }\n\n return $isInvalidDropTarget ? 'not-allowed' : 'pointer';\n }};\n opacity: ${({ $isDragging }) => ($isDragging ? 0.4 : 1)};\n pointer-events: ${({ $isMovePending }) => ($isMovePending ? 'none' : 'auto')};\n transition: background 0.2s;\n\n ${({ $isValidDropTarget, theme }) =>\n $isValidDropTarget &&\n css`\n background: ${theme.colors.primary100};\n border: 1px dashed ${theme.colors.primary600};\n `}\n\n &:hover {\n background: ${({ theme }) => theme.colors.primary100};\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n }\n`;\n\nconst FolderIconContainer = styled(Flex)`\n flex-shrink: 0;\n color: ${({ theme }) => theme.colors.neutral600};\n`;\n\nconst FolderName = styled(Typography)`\n flex: 1;\n min-width: 0;\n`;\n\ninterface FolderCardProps {\n folder: Folder;\n orderedItemKeys: ItemKey[];\n}\n\nconst FolderCard = ({ folder, orderedItemKeys }: FolderCardProps) => {\n const { formatMessage } = useIntl();\n const { navigateToFolder } = useFolderNavigation();\n const { isMovePending } = useAssetsDndOptional() ?? { isMovePending: false };\n const { isSelected, toggle, selectRange } = useAssetSelection();\n const { canUpdate } = useMediaLibraryPermissions();\n const {\n draggable: { attributes, listeners, setNodeRef: setDragRef, isDragging },\n droppable: { setNodeRef: setDropRef },\n showValidDropHighlight,\n showInvalidDropCursor,\n } = useFolderDraggableDroppable(folder);\n\n const key = folderKey(folder.id);\n\n const setNodeRef = (node: HTMLElement | null) => {\n setDragRef(node);\n setDropRef(node);\n };\n\n // Folders share the selection mechanism with assets (toggle, range,\n // select-all). Only the plain-click semantic differs: it navigates into the\n // folder instead of selecting it.\n const handleClick = (e: React.MouseEvent) => {\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else if (e.metaKey || e.ctrlKey) {\n toggle(key);\n } else {\n navigateToFolder(folder);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n navigateToFolder(folder);\n } else if (e.key === ' ') {\n e.preventDefault();\n toggle(key);\n }\n };\n\n // Checkbox is the pointer path to selection; Shift extends the range.\n const handleCheckboxClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else {\n toggle(key);\n }\n };\n\n return (\n <StyledFolderCard\n ref={setNodeRef}\n {...attributes}\n {...listeners}\n $isDragging={isDragging}\n $isMovePending={isMovePending}\n $isValidDropTarget={showValidDropHighlight}\n $isInvalidDropTarget={showInvalidDropCursor}\n $isSelected={isSelected(key)}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n role=\"listitem\"\n tabIndex={0}\n >\n {canUpdate && (\n <Flex onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()}>\n <Checkbox\n checked={isSelected(key)}\n onClick={handleCheckboxClick}\n aria-label={formatMessage(\n {\n id: getTranslationKey('list.table.row.select'),\n defaultMessage: 'Select {name}',\n },\n { name: folder.name }\n )}\n />\n </Flex>\n )}\n <FolderIconContainer>\n <FolderIcon width={20} height={20} />\n </FolderIconContainer>\n <FolderName textColor=\"neutral800\" ellipsis>\n {folder.name}\n </FolderName>\n <IconButton\n label={formatMessage({\n id: getTranslationKey('control-card.more-actions'),\n defaultMessage: 'More actions',\n })}\n variant=\"ghost\"\n onClick={(e: React.MouseEvent) => e.stopPropagation()}\n >\n <More />\n </IconButton>\n </StyledFolderCard>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetPreview\n * -----------------------------------------------------------------------------------------------*/\n\nconst PreviewContainer = styled(Box)`\n position: relative;\n width: 100%;\n padding-bottom: 62.5%;\n height: 0;\n overflow: hidden;\n background: repeating-conic-gradient(\n ${({ theme }) => theme.colors.neutral100} 0% 25%,\n transparent 0% 50%\n )\n 50% / 20px 20px;\n`;\n\nconst StyledImage = styled.img`\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n`;\n\nconst IconPreview = styled(Flex)`\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n color: ${({ theme }) => theme.colors.neutral500};\n background: ${({ theme }) => theme.colors.neutral100};\n`;\n\ninterface AssetPreviewProps {\n asset: File;\n}\n\nconst AssetPreview = ({ asset }: AssetPreviewProps) => {\n const { alternativeText, ext, formats, mime, url, isLocal, isUrlSigned } = asset;\n\n if (mime?.includes(ASSET_TYPES.Image)) {\n const mediaURL =\n prefixFileUrlWithBackendUrl(formats?.thumbnail?.url) ?? prefixFileUrlWithBackendUrl(url);\n\n if (mediaURL) {\n return (\n <PreviewContainer>\n <StyledImage\n src={mediaURL}\n alt={alternativeText || ''}\n // Only signed remote URLs need crossOrigin (cache collision with\n // the preview). Public/unsigned remote thumbnails must render\n // without a bucket CORS rule. See #26581.\n crossOrigin={!isLocal && isUrlSigned ? 'anonymous' : undefined}\n draggable={false}\n onDragStart={(e) => e.preventDefault()}\n />\n </PreviewContainer>\n );\n }\n }\n\n const DocIcon = getAssetIcon(mime, ext);\n\n return (\n <PreviewContainer>\n <IconPreview justifyContent=\"center\" alignItems=\"center\">\n <DocIcon width={48} height={48} />\n </IconPreview>\n </PreviewContainer>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetCard\n * -----------------------------------------------------------------------------------------------*/\n\nconst StyledCardHeader = styled(CardHeader)`\n position: relative;\n border-bottom: 1px solid ${({ theme }) => theme.colors.neutral200};\n`;\n\nconst CardFooter = styled(Flex)`\n min-width: 0;\n width: 100%;\n`;\n\nconst FileTypeIcon = styled(Flex)`\n color: ${({ theme }) => theme.colors.neutral600};\n flex-shrink: 0;\n`;\n\nconst FileName = styled(Typography)`\n flex: 1;\n min-width: 0;\n`;\n\n// The asset filename is its own interactive element: clicking it opens the\n// details drawer instead of selecting the card (mirrors the table view).\nconst NameButton = styled.button`\n display: inline-flex;\n flex: 1;\n min-width: 0;\n border: none;\n background: transparent;\n padding: 0;\n margin: 0;\n cursor: pointer;\n text-align: left;\n color: inherit;\n font: inherit;\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n border-radius: 2px;\n }\n`;\n\ninterface AssetCardProps {\n asset: File;\n orderedItemKeys: ItemKey[];\n onAssetItemClick: (assetId: number) => void;\n}\n\nconst AssetCard = ({ asset, orderedItemKeys, onAssetItemClick }: AssetCardProps) => {\n const { formatMessage } = useIntl();\n const TypeIcon = getAssetIcon(asset.mime, asset.ext);\n const { isMovePending } = useAssetsDndOptional() ?? { isMovePending: false };\n const { attributes, listeners, setNodeRef, isDragging } = useFileDraggable(asset);\n const { isSelected, toggle, selectRange } = useAssetSelection();\n const { canUpdate } = useMediaLibraryPermissions();\n\n const key = assetKey(asset.id);\n const selected = isSelected(key);\n\n // Plain click opens the asset details; pointer selection lives on the\n // checkbox only. Modifier clicks keep the selection semantics: shift selects\n // a range, cmd/ctrl toggles.\n const handleCardClick = (e: React.MouseEvent) => {\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else if (e.metaKey || e.ctrlKey) {\n toggle(key);\n } else {\n onAssetItemClick(asset.id);\n }\n };\n\n // Space toggles selection (additive), Enter opens the details drawer.\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n onAssetItemClick(asset.id);\n } else if (e.key === ' ') {\n e.preventDefault();\n toggle(key);\n }\n };\n\n const handleNameClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n onAssetItemClick(asset.id);\n };\n\n // Same semantics as the table's row checkbox: Shift extends the range,\n // otherwise a plain additive toggle. Never bubbles into the card click.\n const handleCheckboxClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else {\n toggle(key);\n }\n };\n\n return (\n <StyledCard\n ref={setNodeRef}\n {...attributes}\n {...listeners}\n $isDragging={isDragging}\n $isMovePending={isMovePending}\n $isSelected={selected}\n tabIndex={0}\n role=\"listitem\"\n onDragStart={(e) => e.preventDefault()}\n onClick={handleCardClick}\n onKeyDown={handleKeyDown}\n >\n <StyledCardHeader>\n {canUpdate && (\n <CheckboxOverlay onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()}>\n <Checkbox\n checked={selected}\n onClick={handleCheckboxClick}\n aria-label={formatMessage(\n {\n id: getTranslationKey('list.table.row.select'),\n defaultMessage: 'Select {name}',\n },\n { name: asset.name }\n )}\n />\n </CheckboxOverlay>\n )}\n <AssetPreview asset={asset} />\n </StyledCardHeader>\n <CardBody>\n <CardFooter alignItems=\"center\" gap={2}>\n <FileTypeIcon>\n <TypeIcon width={20} height={20} />\n </FileTypeIcon>\n <NameButton type=\"button\" onClick={handleNameClick}>\n <FileName textColor=\"primary800\" ellipsis>\n {asset.name}\n </FileName>\n </NameButton>\n <IconButton\n label={formatMessage({\n id: getTranslationKey('control-card.more-actions'),\n defaultMessage: 'More actions',\n })}\n variant=\"ghost\"\n onClick={(e: React.MouseEvent) => e.stopPropagation()}\n >\n <More />\n </IconButton>\n </CardFooter>\n </CardBody>\n </StyledCard>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetsGrid\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsGridProps {\n assets: File[];\n folders?: Folder[];\n onAssetItemClick: (assetId: number) => void;\n}\n\nexport const AssetsGrid = ({ assets, folders = [], onAssetItemClick }: AssetsGridProps) => {\n const totalItems = folders.length + assets.length;\n\n // Render order: folders always on top in the grid (mixing is table-only) —\n // range selection follows it.\n const orderedItemKeys: ItemKey[] = [\n ...folders.map((folder) => folderKey(folder.id)),\n ...assets.map((asset) => assetKey(asset.id)),\n ];\n\n // The empty state is owned by the page (`AssetsView` renders `EmptyState`) — an\n // empty grid renders nothing at all.\n if (totalItems === 0) {\n return null;\n }\n\n return (\n <Grid.Root gap={4} role=\"list\" data-testid=\"assets-grid\">\n {folders.length > 0 && (\n <FoldersRow>\n <Grid.Root gap={4}>\n {folders.map((folder) => (\n <Grid.Item col={3} m={4} s={6} xs={12} key={`folder-${folder.id}`}>\n <FolderCard folder={folder} orderedItemKeys={orderedItemKeys} />\n </Grid.Item>\n ))}\n </Grid.Root>\n </FoldersRow>\n )}\n {assets.map((asset) => (\n <Grid.Item\n col={3}\n m={4}\n s={6}\n xs={12}\n key={asset.id}\n direction=\"column\"\n alignItems=\"stretch\"\n >\n <AssetCard\n asset={asset}\n orderedItemKeys={orderedItemKeys}\n onAssetItemClick={onAssetItemClick}\n />\n </Grid.Item>\n ))}\n </Grid.Root>\n );\n};\n"],"names":["CheckboxOverlay","styled","Flex","theme","spaces","shadows","filterShadow","StyledCard","Card","$isSelected","colors","primary600","neutral200","$isMovePending","$isDragging","primary100","undefined","FoldersRow","Box","StyledFolderCard","borderRadius","neutral0","$isInvalidDropTarget","$isValidDropTarget","css","FolderIconContainer","neutral600","FolderName","Typography","FolderCard","folder","orderedItemKeys","formatMessage","useIntl","navigateToFolder","useFolderNavigation","isMovePending","useAssetsDndOptional","isSelected","toggle","selectRange","useAssetSelection","canUpdate","useMediaLibraryPermissions","draggable","attributes","listeners","setNodeRef","setDragRef","isDragging","droppable","setDropRef","showValidDropHighlight","showInvalidDropCursor","useFolderDraggableDroppable","key","folderKey","id","node","handleClick","e","shiftKey","metaKey","ctrlKey","handleKeyDown","preventDefault","handleCheckboxClick","stopPropagation","_jsxs","ref","onClick","onKeyDown","role","tabIndex","_jsx","Checkbox","checked","aria-label","getTranslationKey","defaultMessage","name","FolderIcon","width","height","textColor","ellipsis","IconButton","label","variant","More","PreviewContainer","neutral100","StyledImage","img","IconPreview","neutral500","AssetPreview","asset","alternativeText","ext","formats","mime","url","isLocal","isUrlSigned","includes","ASSET_TYPES","Image","mediaURL","prefixFileUrlWithBackendUrl","thumbnail","src","alt","crossOrigin","onDragStart","DocIcon","getAssetIcon","justifyContent","alignItems","StyledCardHeader","CardHeader","CardFooter","FileTypeIcon","FileName","NameButton","button","AssetCard","onAssetItemClick","TypeIcon","useFileDraggable","assetKey","selected","handleCardClick","handleNameClick","CardBody","gap","type","AssetsGrid","assets","folders","totalItems","length","map","Grid","Root","data-testid","Item","col","m","s","xs","direction"],"mappings":"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"}
1
+ {"version":3,"file":"AssetsGrid.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/AssetsGrid.tsx"],"sourcesContent":["import { Box, Card, CardBody, CardHeader, Checkbox, Flex, Grid } from '@strapi/design-system';\nimport { Folder as FolderIcon } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled, css } from 'styled-components';\n\nimport { ASSET_TYPES } from '../../../../enums';\nimport { TruncatedText } from '../../../components/TruncatedText';\nimport { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions';\nimport { prefixFileUrlWithBackendUrl } from '../../../utils/files';\nimport { getAssetIcon } from '../../../utils/getAssetIcon';\nimport { isEventFromWithin } from '../../../utils/isEventFromWithin';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useBusyAssetsOptional } from '../hooks/useBusyAssets';\nimport { useFolderNavigation } from '../hooks/useFolderNavigation';\nimport { assetKey, folderKey, type ItemKey } from '../utils/selection';\n\nimport { AssetActionsMenu } from './AssetActionsMenu';\nimport { BusyOverlay } from './BusyOverlay';\nimport { useAssetsDndOptional } from './Dnd/AssetsDndProvider';\nimport { useFileDraggable, useFolderDraggableDroppable } from './Dnd/useAssetDnd';\nimport { FolderActionsMenu } from './FolderActionsMenu';\n\nimport type { File } from '../../../../../../shared/contracts/files';\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\n/* -------------------------------------------------------------------------------------------------\n * AssetsGrid\n * -----------------------------------------------------------------------------------------------*/\n\n// Cards own click, Enter and Space; the controls sitting on them (checkbox, \"...\"\n// menu) must not let those reach the card. Scoped to the wrapper's own DOM\n// subtree so the menu's portaled content — a React child of the wrapper, but\n// never a DOM descendant — still reaches `document`, where Radix listens in\n// order to dismiss its layers.\nconst stopCardEvent = (e: React.SyntheticEvent) => {\n if (isEventFromWithin(e)) {\n e.stopPropagation();\n }\n};\n\n// Top-left selection checkbox overlaid on the asset preview, always visible.\nconst CheckboxOverlay = styled(Flex)`\n position: absolute;\n top: ${({ theme }) => theme.spaces[3]};\n left: ${({ theme }) => theme.spaces[3]};\n z-index: 1;\n box-shadow: ${({ theme }) => theme.shadows.filterShadow};\n`;\n\nconst StyledCard = styled(Card)<{\n $isDragging?: boolean;\n $isMovePending?: boolean;\n $isBusy?: boolean;\n $isSelected?: boolean;\n}>`\n border: 1px solid\n ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary600 : theme.colors.neutral200)};\n border-radius: 8px;\n overflow: hidden;\n cursor: ${({ $isMovePending, $isBusy }) => ($isMovePending || $isBusy ? 'wait' : 'pointer')};\n opacity: ${({ $isDragging }) => ($isDragging ? 0.4 : 1)};\n /* No opacity change while busy — the overlay does the dimming, and stacking\n one on the other would wash the card out. */\n pointer-events: ${({ $isMovePending, $isBusy }) => ($isMovePending || $isBusy ? 'none' : 'auto')};\n background: ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary100 : undefined)};\n /* Shift+click range selection must not highlight card text. */\n user-select: none;\n\n &:hover {\n background: ${({ theme }) => theme.colors.primary100};\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * FolderCard\n * -----------------------------------------------------------------------------------------------*/\n\nconst FoldersRow = styled(Box)`\n grid-column: 1 / -1;\n`;\n\nconst StyledFolderCard = styled(Flex)<{\n $isDragging?: boolean;\n $isMovePending?: boolean;\n $isValidDropTarget?: boolean;\n $isInvalidDropTarget?: boolean;\n $isSelected?: boolean;\n}>`\n width: 100%;\n user-select: none;\n padding: ${({ theme }) => `${theme.spaces[2]} ${theme.spaces[3]}`}; // 8px 12px\n align-items: center;\n gap: ${({ theme }) => theme.spaces[2]}; // 8px\n border: 1px solid\n ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary600 : theme.colors.neutral200)};\n border-radius: ${({ theme }) => theme.borderRadius};\n background: ${({ theme, $isSelected }) =>\n $isSelected ? theme.colors.primary100 : theme.colors.neutral0};\n cursor: ${({ $isMovePending, $isInvalidDropTarget }) => {\n if ($isMovePending) {\n return 'wait';\n }\n\n return $isInvalidDropTarget ? 'not-allowed' : 'pointer';\n }};\n opacity: ${({ $isDragging }) => ($isDragging ? 0.4 : 1)};\n pointer-events: ${({ $isMovePending }) => ($isMovePending ? 'none' : 'auto')};\n transition: background 0.2s;\n\n ${({ $isValidDropTarget, theme }) =>\n $isValidDropTarget &&\n css`\n background: ${theme.colors.primary100};\n border: 1px dashed ${theme.colors.primary600};\n `}\n\n &:hover {\n background: ${({ theme }) => theme.colors.primary100};\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n }\n`;\n\nconst FolderIconContainer = styled(Flex)`\n flex-shrink: 0;\n color: ${({ theme }) => theme.colors.neutral600};\n`;\n\nconst FolderName = styled(TruncatedText)`\n flex: 1;\n min-width: 0;\n`;\n\ninterface FolderCardProps {\n folder: Folder;\n orderedItemKeys: ItemKey[];\n}\n\nconst FolderCard = ({ folder, orderedItemKeys }: FolderCardProps) => {\n const { formatMessage } = useIntl();\n const { navigateToFolder } = useFolderNavigation();\n const { isMovePending } = useAssetsDndOptional() ?? { isMovePending: false };\n const { isSelected, toggle, selectRange } = useAssetSelection();\n const { canUpdate } = useMediaLibraryPermissions();\n const {\n dragData,\n draggable: { attributes, listeners, setNodeRef: setDragRef, isDragging },\n droppable: { setNodeRef: setDropRef },\n showValidDropHighlight,\n showInvalidDropCursor,\n } = useFolderDraggableDroppable(folder);\n\n const key = folderKey(folder.id);\n\n const setNodeRef = (node: HTMLElement | null) => {\n setDragRef(node);\n setDropRef(node);\n };\n\n // Folders share the selection mechanism with assets (toggle, range,\n // select-all). Only the plain-click semantic differs: it navigates into the\n // folder instead of selecting it.\n //\n // The containment guard on this and the handlers below is what keeps the\n // actions menu's portaled dialogs — React children of this card — from\n // navigating into the folder or starting a drag.\n const handleClick = (e: React.MouseEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else if (e.metaKey || e.ctrlKey) {\n toggle(key);\n } else {\n navigateToFolder(folder);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.key === 'Enter') {\n e.preventDefault();\n navigateToFolder(folder);\n } else if (e.key === ' ') {\n e.preventDefault();\n toggle(key);\n }\n };\n\n // Checkbox is the pointer path to selection; Shift extends the range.\n const handleCheckboxClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else {\n toggle(key);\n }\n };\n\n return (\n <StyledFolderCard\n ref={setNodeRef}\n {...attributes}\n {...listeners}\n $isDragging={isDragging}\n $isMovePending={isMovePending}\n $isValidDropTarget={showValidDropHighlight}\n $isInvalidDropTarget={showInvalidDropCursor}\n $isSelected={isSelected(key)}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n onPointerDown={(e: React.PointerEvent) => {\n if (isEventFromWithin(e)) {\n listeners?.onPointerDown?.(e);\n }\n }}\n role=\"listitem\"\n tabIndex={0}\n >\n {canUpdate && (\n <Flex onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()}>\n <Checkbox\n checked={isSelected(key)}\n onClick={handleCheckboxClick}\n aria-label={formatMessage(\n {\n id: getTranslationKey('list.table.row.select'),\n defaultMessage: 'Select {name}',\n },\n { name: folder.name }\n )}\n />\n </Flex>\n )}\n <FolderIconContainer>\n <FolderIcon width={20} height={20} />\n </FolderIconContainer>\n <FolderName textColor=\"neutral800\">{folder.name}</FolderName>\n <Flex onClick={stopCardEvent} onKeyDown={stopCardEvent} onPointerDown={stopCardEvent}>\n <FolderActionsMenu folder={folder} dragData={dragData} />\n </Flex>\n </StyledFolderCard>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetPreview\n * -----------------------------------------------------------------------------------------------*/\n\nconst PreviewContainer = styled(Box)`\n position: relative;\n width: 100%;\n padding-bottom: 62.5%;\n height: 0;\n overflow: hidden;\n background: repeating-conic-gradient(\n ${({ theme }) => theme.colors.neutral100} 0% 25%,\n transparent 0% 50%\n )\n 50% / 20px 20px;\n`;\n\nconst StyledImage = styled.img`\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n`;\n\nconst IconPreview = styled(Flex)`\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n color: ${({ theme }) => theme.colors.neutral500};\n background: ${({ theme }) => theme.colors.neutral100};\n`;\n\ninterface AssetPreviewProps {\n asset: File;\n}\n\nconst AssetPreview = ({ asset }: AssetPreviewProps) => {\n const { alternativeText, ext, formats, mime, url, updatedAt, isLocal, isUrlSigned } = asset;\n\n if (mime?.includes(ASSET_TYPES.Image)) {\n // Replace keeps the same hash/URL, so without a cache-buster the browser\n // serves the stale thumbnail after a Replace Media. `updatedAt`\n // changes on replace, so it re-fetches exactly once per replacement.\n // Skipped for signed URLs — an extra query param invalidates the signature.\n const cacheKey = updatedAt && !isUrlSigned ? new Date(updatedAt).getTime() : undefined;\n const appendCacheBuster = (raw: string) => {\n if (cacheKey === undefined) {\n return raw;\n }\n return raw.includes('?') ? `${raw}&v=${cacheKey}` : `${raw}?v=${cacheKey}`;\n };\n\n const rawMediaURL =\n prefixFileUrlWithBackendUrl(formats?.thumbnail?.url) ?? prefixFileUrlWithBackendUrl(url);\n const mediaURL = rawMediaURL ? appendCacheBuster(rawMediaURL) : rawMediaURL;\n\n if (mediaURL) {\n return (\n <PreviewContainer>\n <StyledImage\n src={mediaURL}\n alt={alternativeText || ''}\n // Only signed remote URLs need crossOrigin (cache collision with\n // the preview). Public/unsigned remote thumbnails must render\n // without a bucket CORS rule. See #26581.\n crossOrigin={!isLocal && isUrlSigned ? 'anonymous' : undefined}\n draggable={false}\n onDragStart={(e) => e.preventDefault()}\n />\n </PreviewContainer>\n );\n }\n }\n\n const DocIcon = getAssetIcon(mime, ext);\n\n return (\n <PreviewContainer>\n <IconPreview justifyContent=\"center\" alignItems=\"center\">\n <DocIcon width={48} height={48} />\n </IconPreview>\n </PreviewContainer>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetCard\n * -----------------------------------------------------------------------------------------------*/\n\nconst StyledCardHeader = styled(CardHeader)`\n position: relative;\n border-bottom: 1px solid ${({ theme }) => theme.colors.neutral200};\n`;\n\nconst CardFooter = styled(Flex)`\n min-width: 0;\n width: 100%;\n`;\n\nconst FileTypeIcon = styled(Flex)`\n color: ${({ theme }) => theme.colors.neutral600};\n flex-shrink: 0;\n`;\n\nconst FileName = styled(TruncatedText)`\n flex: 1;\n min-width: 0;\n`;\n\n// The asset filename is its own interactive element: clicking it opens the\n// details drawer instead of selecting the card (mirrors the table view).\nconst NameButton = styled.button`\n display: inline-flex;\n flex: 1;\n min-width: 0;\n border: none;\n background: transparent;\n padding: 0;\n margin: 0;\n cursor: pointer;\n text-align: left;\n color: inherit;\n font: inherit;\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n border-radius: 2px;\n }\n`;\n\ninterface AssetCardProps {\n asset: File;\n orderedItemKeys: ItemKey[];\n onAssetItemClick: (assetId: number) => void;\n}\n\nconst AssetCard = ({ asset, orderedItemKeys, onAssetItemClick }: AssetCardProps) => {\n const { formatMessage } = useIntl();\n const TypeIcon = getAssetIcon(asset.mime, asset.ext);\n const { isMovePending } = useAssetsDndOptional() ?? { isMovePending: false };\n const { attributes, listeners, setNodeRef, isDragging, dragData } = useFileDraggable(asset);\n const { isSelected, toggle, selectRange } = useAssetSelection();\n const { canUpdate } = useMediaLibraryPermissions();\n const busyMessage = useBusyAssetsOptional()?.getBusyMessage(asset.id) ?? null;\n\n const key = assetKey(asset.id);\n const selected = isSelected(key);\n\n // Plain click opens the asset details; pointer selection lives on the\n // checkbox only. Modifier clicks keep the selection semantics: shift selects\n // a range, cmd/ctrl toggles.\n //\n // The containment guard on this and the handler below is what keeps the\n // actions menu's portaled dialogs — React children of this card — from\n // opening the details drawer or toggling the selection.\n const handleCardClick = (e: React.MouseEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else if (e.metaKey || e.ctrlKey) {\n toggle(key);\n } else {\n onAssetItemClick(asset.id);\n }\n };\n\n // Space toggles selection (additive), Enter opens the details drawer.\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.key === 'Enter') {\n e.preventDefault();\n onAssetItemClick(asset.id);\n } else if (e.key === ' ') {\n e.preventDefault();\n toggle(key);\n }\n };\n\n const handleNameClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n onAssetItemClick(asset.id);\n };\n\n // Same semantics as the table's row checkbox: Shift extends the range,\n // otherwise a plain additive toggle. Never bubbles into the card click.\n const handleCheckboxClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else {\n toggle(key);\n }\n };\n\n return (\n <StyledCard\n ref={setNodeRef}\n {...attributes}\n {...listeners}\n $isDragging={isDragging}\n $isMovePending={isMovePending}\n $isBusy={busyMessage !== null}\n $isSelected={selected}\n tabIndex={0}\n role=\"listitem\"\n onDragStart={(e) => e.preventDefault()}\n onClick={handleCardClick}\n onKeyDown={handleKeyDown}\n onPointerDown={(e: React.PointerEvent) => {\n if (isEventFromWithin(e)) {\n listeners?.onPointerDown?.(e);\n }\n }}\n >\n <StyledCardHeader>\n {canUpdate && (\n <CheckboxOverlay onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()}>\n <Checkbox\n checked={selected}\n onClick={handleCheckboxClick}\n aria-label={formatMessage(\n {\n id: getTranslationKey('list.table.row.select'),\n defaultMessage: 'Select {name}',\n },\n { name: asset.name }\n )}\n />\n </CheckboxOverlay>\n )}\n <AssetPreview asset={asset} />\n {/* The header is the card's only positioned ancestor, and it's the part\n worth covering — the footer keeps the name legible while the preview\n is mid-replace. Below the checkbox overlay (z-index 1) would hide\n the spinner, so it sits above it. */}\n {busyMessage !== null ? <BusyOverlay zIndex={2}>{busyMessage}</BusyOverlay> : null}\n </StyledCardHeader>\n <CardBody>\n <CardFooter alignItems=\"center\" gap={2}>\n <FileTypeIcon>\n <TypeIcon width={20} height={20} />\n </FileTypeIcon>\n <NameButton type=\"button\" onClick={handleNameClick}>\n <FileName textColor=\"primary800\">{asset.name}</FileName>\n </NameButton>\n <Flex onClick={stopCardEvent} onKeyDown={stopCardEvent} onPointerDown={stopCardEvent}>\n <AssetActionsMenu asset={asset} dragData={dragData} />\n </Flex>\n </CardFooter>\n </CardBody>\n </StyledCard>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetsGrid\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsGridProps {\n assets: File[];\n folders?: Folder[];\n onAssetItemClick: (assetId: number) => void;\n}\n\nexport const AssetsGrid = ({ assets, folders = [], onAssetItemClick }: AssetsGridProps) => {\n const totalItems = folders.length + assets.length;\n\n // Render order: folders always on top in the grid (mixing is table-only) —\n // range selection follows it.\n const orderedItemKeys: ItemKey[] = [\n ...folders.map((folder) => folderKey(folder.id)),\n ...assets.map((asset) => assetKey(asset.id)),\n ];\n\n // The empty state is owned by the page (`AssetsView` renders `EmptyState`) — an\n // empty grid renders nothing at all.\n if (totalItems === 0) {\n return null;\n }\n\n return (\n <Grid.Root gap={4} role=\"list\" data-testid=\"assets-grid\">\n {folders.length > 0 && (\n <FoldersRow>\n <Grid.Root gap={4}>\n {folders.map((folder) => (\n <Grid.Item col={3} m={4} s={6} xs={12} key={`folder-${folder.id}`}>\n <FolderCard folder={folder} orderedItemKeys={orderedItemKeys} />\n </Grid.Item>\n ))}\n </Grid.Root>\n </FoldersRow>\n )}\n {assets.map((asset) => (\n <Grid.Item\n col={3}\n m={4}\n s={6}\n xs={12}\n key={asset.id}\n direction=\"column\"\n alignItems=\"stretch\"\n >\n <AssetCard\n asset={asset}\n orderedItemKeys={orderedItemKeys}\n onAssetItemClick={onAssetItemClick}\n />\n </Grid.Item>\n ))}\n </Grid.Root>\n );\n};\n"],"names":["stopCardEvent","e","isEventFromWithin","stopPropagation","CheckboxOverlay","styled","Flex","theme","spaces","shadows","filterShadow","StyledCard","Card","$isSelected","colors","primary600","neutral200","$isMovePending","$isBusy","$isDragging","primary100","undefined","FoldersRow","Box","StyledFolderCard","borderRadius","neutral0","$isInvalidDropTarget","$isValidDropTarget","css","FolderIconContainer","neutral600","FolderName","TruncatedText","FolderCard","folder","orderedItemKeys","formatMessage","useIntl","navigateToFolder","useFolderNavigation","isMovePending","useAssetsDndOptional","isSelected","toggle","selectRange","useAssetSelection","canUpdate","useMediaLibraryPermissions","dragData","draggable","attributes","listeners","setNodeRef","setDragRef","isDragging","droppable","setDropRef","showValidDropHighlight","showInvalidDropCursor","useFolderDraggableDroppable","key","folderKey","id","node","handleClick","shiftKey","metaKey","ctrlKey","handleKeyDown","preventDefault","handleCheckboxClick","_jsxs","ref","onClick","onKeyDown","onPointerDown","role","tabIndex","_jsx","Checkbox","checked","aria-label","getTranslationKey","defaultMessage","name","FolderIcon","width","height","textColor","FolderActionsMenu","PreviewContainer","neutral100","StyledImage","img","IconPreview","neutral500","AssetPreview","asset","alternativeText","ext","formats","mime","url","updatedAt","isLocal","isUrlSigned","includes","ASSET_TYPES","Image","cacheKey","Date","getTime","appendCacheBuster","raw","rawMediaURL","prefixFileUrlWithBackendUrl","thumbnail","mediaURL","src","alt","crossOrigin","onDragStart","DocIcon","getAssetIcon","justifyContent","alignItems","StyledCardHeader","CardHeader","CardFooter","FileTypeIcon","FileName","NameButton","button","AssetCard","onAssetItemClick","TypeIcon","useFileDraggable","busyMessage","useBusyAssetsOptional","getBusyMessage","assetKey","selected","handleCardClick","handleNameClick","BusyOverlay","zIndex","CardBody","gap","type","AssetActionsMenu","AssetsGrid","assets","folders","totalItems","length","map","Grid","Root","data-testid","Item","col","m","s","xs","direction"],"mappings":"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"}
@@ -5,16 +5,30 @@ var React = require('react');
5
5
  var strapiAdmin = require('@strapi/admin/strapi-admin');
6
6
  var designSystem = require('@strapi/design-system');
7
7
  var reactIntl = require('react-intl');
8
+ var styledComponents = require('styled-components');
9
+ var useTracking = require('../../../hooks/useTracking.js');
8
10
  var translations = require('../../../utils/translations.js');
9
11
  var useAssetSearch = require('../hooks/useAssetSearch.js');
10
12
 
11
13
  const DEBOUNCE_MS = 300;
14
+ // The DS Searchbar nests two bordered boxes: its own shell (neutral150) wrapping
15
+ // the field (neutral200). Side by side they read as a single 2px, two-tone
16
+ // border, which sits oddly next to the 1px neutral200 on the Filter button in the
17
+ // same toolbar. Dropping the shell's border leaves the field's own — same width
18
+ // and colour as the button — and costs nothing, since the focus ring lives on the
19
+ // field too.
20
+ const StyledSearchForm = styledComponents.styled(designSystem.SearchForm)`
21
+ > div {
22
+ border: none;
23
+ }
24
+ `;
12
25
  /**
13
26
  * Keyword search over the whole Media Library. Owns the typing state and the
14
27
  * debounce; `useAssetSearch` owns the `?_q=` URL param.
15
28
  */ const AssetsSearchInput = ()=>{
16
29
  const { formatMessage } = reactIntl.useIntl();
17
30
  const { searchQuery, setSearchQuery } = useAssetSearch.useAssetSearch();
31
+ const { trackUsage } = useTracking.useTracking();
18
32
  const isMobile = strapiAdmin.useIsMobile();
19
33
  const [value, setValue] = React.useState(searchQuery);
20
34
  const debouncedValue = strapiAdmin.useDebounce(value, DEBOUNCE_MS);
@@ -33,10 +47,18 @@ const DEBOUNCE_MS = 300;
33
47
  return;
34
48
  }
35
49
  lastCommittedRef.current = debouncedValue;
50
+ // Only a real query counts as a search — clearing the box (empty value) is
51
+ // a reset, not a `didSearch`.
52
+ if (debouncedValue) {
53
+ trackUsage('didSearchMediaLibraryElements', {
54
+ location: useTracking.MEDIA_LIBRARY_LOCATION
55
+ });
56
+ }
36
57
  setSearchQuery(debouncedValue);
37
58
  }, [
38
59
  debouncedValue,
39
- setSearchQuery
60
+ setSearchQuery,
61
+ trackUsage
40
62
  ]);
41
63
  // Re-sync on external changes — back/forward, or "Clear search" in the empty
42
64
  // state.
@@ -63,7 +85,7 @@ const DEBOUNCE_MS = 300;
63
85
  folderParam,
64
86
  searchQuery
65
87
  ]);
66
- const searchForm = /*#__PURE__*/ jsxRuntime.jsx(designSystem.SearchForm, {
88
+ const searchForm = /*#__PURE__*/ jsxRuntime.jsx(StyledSearchForm, {
67
89
  onSubmit: (event)=>event.preventDefault(),
68
90
  children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Searchbar, {
69
91
  name: "search-assets",
@@ -85,8 +107,8 @@ const DEBOUNCE_MS = 300;
85
107
  })
86
108
  })
87
109
  });
88
- // TODO: (design question)The input stays visible at every breakpoint; on mobile it takes the full
89
- // width of the toolbar instead of its intrinsic size.
110
+ // Mobile: fills the toolbar row (own line). Desktop: keeps its intrinsic size,
111
+ // with empty space between it and the right-aligned Sort/Toggle group.
90
112
  if (isMobile) {
91
113
  return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
92
114
  width: "100%",
@@ -1 +1 @@
1
- {"version":3,"file":"AssetsSearchInput.js","sources":["../../../../../../admin/src/future/pages/Assets/components/AssetsSearchInput.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ChangeEvent, type FormEvent } from 'react';\n\nimport { useDebounce, useIsMobile, useQueryParams } from '@strapi/admin/strapi-admin';\nimport { Box, Searchbar, SearchForm } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSearch } from '../hooks/useAssetSearch';\n\nconst DEBOUNCE_MS = 300;\n\n/**\n * Keyword search over the whole Media Library. Owns the typing state and the\n * debounce; `useAssetSearch` owns the `?_q=` URL param.\n */\nexport const AssetsSearchInput = () => {\n const { formatMessage } = useIntl();\n const { searchQuery, setSearchQuery } = useAssetSearch();\n const isMobile = useIsMobile();\n\n const [value, setValue] = useState(searchQuery);\n const debouncedValue = useDebounce(value, DEBOUNCE_MS);\n\n // Tracks the last value the URL and the input agreed on. Without it the two\n // effects below would ping-pong: each would see the other's write as a change\n // worth reacting to.\n const lastCommittedRef = useRef(searchQuery);\n\n // Read directly rather than through `useFolderNavigation`, which strips\n // malformed `?folder=` values as a side effect.\n const [{ query }] = useQueryParams<{ folder?: string }>();\n const folderParam = query?.folder ?? '';\n const lastFolderRef = useRef(folderParam);\n\n // Commit settled input to the URL.\n useEffect(() => {\n if (debouncedValue === lastCommittedRef.current) {\n return;\n }\n\n lastCommittedRef.current = debouncedValue;\n setSearchQuery(debouncedValue);\n }, [debouncedValue, setSearchQuery]);\n\n // Re-sync on external changes — back/forward, or \"Clear search\" in the empty\n // state.\n useEffect(() => {\n if (searchQuery === lastCommittedRef.current) {\n return;\n }\n\n lastCommittedRef.current = searchQuery;\n setValue(searchQuery);\n }, [searchQuery]);\n\n // A folder navigation clears `_q`, but a term typed and never committed lives\n // only in the debounce — without this it lands afterwards and reactivates the\n // global search. `_q` alone can't see it: it was never non-empty.\n useEffect(() => {\n if (folderParam === lastFolderRef.current) {\n return;\n }\n\n lastFolderRef.current = folderParam;\n lastCommittedRef.current = searchQuery;\n setValue(searchQuery);\n }, [folderParam, searchQuery]);\n\n const searchForm = (\n <SearchForm onSubmit={(event: FormEvent<HTMLFormElement>) => event.preventDefault()}>\n <Searchbar\n name=\"search-assets\"\n value={value}\n onChange={(event: ChangeEvent<HTMLInputElement>) => setValue(event.target.value)}\n onClear={() => setValue('')}\n clearLabel={formatMessage({ id: 'clearLabel', defaultMessage: 'Clear' })}\n placeholder={formatMessage({\n id: getTranslationKey('header.search.placeholder'),\n defaultMessage: 'Search',\n })}\n size=\"S\"\n >\n {formatMessage({\n id: getTranslationKey('search.label'),\n defaultMessage: 'Search for an asset',\n })}\n </Searchbar>\n </SearchForm>\n );\n\n // TODO: (design question)The input stays visible at every breakpoint; on mobile it takes the full\n // width of the toolbar instead of its intrinsic size.\n if (isMobile) {\n return <Box width=\"100%\">{searchForm}</Box>;\n }\n\n return searchForm;\n};\n"],"names":["DEBOUNCE_MS","AssetsSearchInput","formatMessage","useIntl","searchQuery","setSearchQuery","useAssetSearch","isMobile","useIsMobile","value","setValue","useState","debouncedValue","useDebounce","lastCommittedRef","useRef","query","useQueryParams","folderParam","folder","lastFolderRef","useEffect","current","searchForm","_jsx","SearchForm","onSubmit","event","preventDefault","Searchbar","name","onChange","target","onClear","clearLabel","id","defaultMessage","placeholder","getTranslationKey","size","Box","width"],"mappings":"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"}
1
+ {"version":3,"file":"AssetsSearchInput.js","sources":["../../../../../../admin/src/future/pages/Assets/components/AssetsSearchInput.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ChangeEvent, type FormEvent } from 'react';\n\nimport { useDebounce, useIsMobile, useQueryParams } from '@strapi/admin/strapi-admin';\nimport { Box, Searchbar, SearchForm } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { useTracking, MEDIA_LIBRARY_LOCATION } from '../../../hooks/useTracking';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSearch } from '../hooks/useAssetSearch';\n\nconst DEBOUNCE_MS = 300;\n\n// The DS Searchbar nests two bordered boxes: its own shell (neutral150) wrapping\n// the field (neutral200). Side by side they read as a single 2px, two-tone\n// border, which sits oddly next to the 1px neutral200 on the Filter button in the\n// same toolbar. Dropping the shell's border leaves the field's own — same width\n// and colour as the button — and costs nothing, since the focus ring lives on the\n// field too.\nconst StyledSearchForm = styled(SearchForm)`\n > div {\n border: none;\n }\n`;\n\n/**\n * Keyword search over the whole Media Library. Owns the typing state and the\n * debounce; `useAssetSearch` owns the `?_q=` URL param.\n */\nexport const AssetsSearchInput = () => {\n const { formatMessage } = useIntl();\n const { searchQuery, setSearchQuery } = useAssetSearch();\n const { trackUsage } = useTracking();\n const isMobile = useIsMobile();\n\n const [value, setValue] = useState(searchQuery);\n const debouncedValue = useDebounce(value, DEBOUNCE_MS);\n\n // Tracks the last value the URL and the input agreed on. Without it the two\n // effects below would ping-pong: each would see the other's write as a change\n // worth reacting to.\n const lastCommittedRef = useRef(searchQuery);\n\n // Read directly rather than through `useFolderNavigation`, which strips\n // malformed `?folder=` values as a side effect.\n const [{ query }] = useQueryParams<{ folder?: string }>();\n const folderParam = query?.folder ?? '';\n const lastFolderRef = useRef(folderParam);\n\n // Commit settled input to the URL.\n useEffect(() => {\n if (debouncedValue === lastCommittedRef.current) {\n return;\n }\n\n lastCommittedRef.current = debouncedValue;\n // Only a real query counts as a search — clearing the box (empty value) is\n // a reset, not a `didSearch`.\n if (debouncedValue) {\n trackUsage('didSearchMediaLibraryElements', { location: MEDIA_LIBRARY_LOCATION });\n }\n setSearchQuery(debouncedValue);\n }, [debouncedValue, setSearchQuery, trackUsage]);\n\n // Re-sync on external changes — back/forward, or \"Clear search\" in the empty\n // state.\n useEffect(() => {\n if (searchQuery === lastCommittedRef.current) {\n return;\n }\n\n lastCommittedRef.current = searchQuery;\n setValue(searchQuery);\n }, [searchQuery]);\n\n // A folder navigation clears `_q`, but a term typed and never committed lives\n // only in the debounce — without this it lands afterwards and reactivates the\n // global search. `_q` alone can't see it: it was never non-empty.\n useEffect(() => {\n if (folderParam === lastFolderRef.current) {\n return;\n }\n\n lastFolderRef.current = folderParam;\n lastCommittedRef.current = searchQuery;\n setValue(searchQuery);\n }, [folderParam, searchQuery]);\n\n const searchForm = (\n <StyledSearchForm onSubmit={(event: FormEvent<HTMLFormElement>) => event.preventDefault()}>\n <Searchbar\n name=\"search-assets\"\n value={value}\n onChange={(event: ChangeEvent<HTMLInputElement>) => setValue(event.target.value)}\n onClear={() => setValue('')}\n clearLabel={formatMessage({ id: 'clearLabel', defaultMessage: 'Clear' })}\n placeholder={formatMessage({\n id: getTranslationKey('header.search.placeholder'),\n defaultMessage: 'Search',\n })}\n size=\"S\"\n >\n {formatMessage({\n id: getTranslationKey('search.label'),\n defaultMessage: 'Search for an asset',\n })}\n </Searchbar>\n </StyledSearchForm>\n );\n\n // Mobile: fills the toolbar row (own line). Desktop: keeps its intrinsic size,\n // with empty space between it and the right-aligned Sort/Toggle group.\n if (isMobile) {\n return <Box width=\"100%\">{searchForm}</Box>;\n }\n\n return searchForm;\n};\n"],"names":["DEBOUNCE_MS","StyledSearchForm","styled","SearchForm","AssetsSearchInput","formatMessage","useIntl","searchQuery","setSearchQuery","useAssetSearch","trackUsage","useTracking","isMobile","useIsMobile","value","setValue","useState","debouncedValue","useDebounce","lastCommittedRef","useRef","query","useQueryParams","folderParam","folder","lastFolderRef","useEffect","current","location","MEDIA_LIBRARY_LOCATION","searchForm","_jsx","onSubmit","event","preventDefault","Searchbar","name","onChange","target","onClear","clearLabel","id","defaultMessage","placeholder","getTranslationKey","size","Box","width"],"mappings":"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"}
@@ -1,18 +1,32 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { useState, useRef, useEffect } from 'react';
3
3
  import { useIsMobile, useDebounce, useQueryParams } from '@strapi/admin/strapi-admin';
4
- import { SearchForm, Searchbar, Box } from '@strapi/design-system';
4
+ import { Searchbar, Box, SearchForm } from '@strapi/design-system';
5
5
  import { useIntl } from 'react-intl';
6
+ import { styled } from 'styled-components';
7
+ import { useTracking, MEDIA_LIBRARY_LOCATION } from '../../../hooks/useTracking.mjs';
6
8
  import { getTranslationKey } from '../../../utils/translations.mjs';
7
9
  import { useAssetSearch } from '../hooks/useAssetSearch.mjs';
8
10
 
9
11
  const DEBOUNCE_MS = 300;
12
+ // The DS Searchbar nests two bordered boxes: its own shell (neutral150) wrapping
13
+ // the field (neutral200). Side by side they read as a single 2px, two-tone
14
+ // border, which sits oddly next to the 1px neutral200 on the Filter button in the
15
+ // same toolbar. Dropping the shell's border leaves the field's own — same width
16
+ // and colour as the button — and costs nothing, since the focus ring lives on the
17
+ // field too.
18
+ const StyledSearchForm = styled(SearchForm)`
19
+ > div {
20
+ border: none;
21
+ }
22
+ `;
10
23
  /**
11
24
  * Keyword search over the whole Media Library. Owns the typing state and the
12
25
  * debounce; `useAssetSearch` owns the `?_q=` URL param.
13
26
  */ const AssetsSearchInput = ()=>{
14
27
  const { formatMessage } = useIntl();
15
28
  const { searchQuery, setSearchQuery } = useAssetSearch();
29
+ const { trackUsage } = useTracking();
16
30
  const isMobile = useIsMobile();
17
31
  const [value, setValue] = useState(searchQuery);
18
32
  const debouncedValue = useDebounce(value, DEBOUNCE_MS);
@@ -31,10 +45,18 @@ const DEBOUNCE_MS = 300;
31
45
  return;
32
46
  }
33
47
  lastCommittedRef.current = debouncedValue;
48
+ // Only a real query counts as a search — clearing the box (empty value) is
49
+ // a reset, not a `didSearch`.
50
+ if (debouncedValue) {
51
+ trackUsage('didSearchMediaLibraryElements', {
52
+ location: MEDIA_LIBRARY_LOCATION
53
+ });
54
+ }
34
55
  setSearchQuery(debouncedValue);
35
56
  }, [
36
57
  debouncedValue,
37
- setSearchQuery
58
+ setSearchQuery,
59
+ trackUsage
38
60
  ]);
39
61
  // Re-sync on external changes — back/forward, or "Clear search" in the empty
40
62
  // state.
@@ -61,7 +83,7 @@ const DEBOUNCE_MS = 300;
61
83
  folderParam,
62
84
  searchQuery
63
85
  ]);
64
- const searchForm = /*#__PURE__*/ jsx(SearchForm, {
86
+ const searchForm = /*#__PURE__*/ jsx(StyledSearchForm, {
65
87
  onSubmit: (event)=>event.preventDefault(),
66
88
  children: /*#__PURE__*/ jsx(Searchbar, {
67
89
  name: "search-assets",
@@ -83,8 +105,8 @@ const DEBOUNCE_MS = 300;
83
105
  })
84
106
  })
85
107
  });
86
- // TODO: (design question)The input stays visible at every breakpoint; on mobile it takes the full
87
- // width of the toolbar instead of its intrinsic size.
108
+ // Mobile: fills the toolbar row (own line). Desktop: keeps its intrinsic size,
109
+ // with empty space between it and the right-aligned Sort/Toggle group.
88
110
  if (isMobile) {
89
111
  return /*#__PURE__*/ jsx(Box, {
90
112
  width: "100%",