@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 +1 @@
1
- {"version":3,"file":"AssetsSearchInput.mjs","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.mjs","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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"}
@@ -6,16 +6,22 @@ var designSystem = require('@strapi/design-system');
6
6
  var icons = require('@strapi/icons');
7
7
  var reactIntl = require('react-intl');
8
8
  var styledComponents = require('styled-components');
9
+ var TruncatedText = require('../../../components/TruncatedText.js');
9
10
  var useMediaLibraryPermissions = require('../../../hooks/useMediaLibraryPermissions.js');
11
+ var useTracking = require('../../../hooks/useTracking.js');
10
12
  var files = require('../../../utils/files.js');
11
13
  var getAssetIcon = require('../../../utils/getAssetIcon.js');
14
+ var isEventFromWithin = require('../../../utils/isEventFromWithin.js');
12
15
  var translations = require('../../../utils/translations.js');
13
16
  var constants = require('../constants.js');
14
17
  var useAssetSelection = require('../hooks/useAssetSelection.js');
18
+ var useBusyAssets = require('../hooks/useBusyAssets.js');
15
19
  var useFolderNavigation = require('../hooks/useFolderNavigation.js');
16
20
  var selection = require('../utils/selection.js');
21
+ var AssetActionsMenu = require('./AssetActionsMenu.js');
17
22
  var AssetsDndProvider = require('./Dnd/AssetsDndProvider.js');
18
23
  var useAssetDnd = require('./Dnd/useAssetDnd.js');
24
+ var FolderActionsMenu = require('./FolderActionsMenu.js');
19
25
 
20
26
  const StyledTable = styledComponents.styled(designSystem.RawTable)`
21
27
  width: 100%;
@@ -24,6 +30,28 @@ const StyledTable = styledComponents.styled(designSystem.RawTable)`
24
30
  border: 1px solid ${({ theme })=>theme.colors.neutral150};
25
31
  border-radius: 4px;
26
32
  overflow: hidden;
33
+
34
+ /* Below desktop only the name column remains. A fixed layout makes a long
35
+ name ellipsize instead of widening the table past the viewport — the
36
+ checkbox (first) and actions (last) columns keep a fixed width and the name
37
+ takes the rest. Desktop keeps the content-sized auto layout. */
38
+ table-layout: fixed;
39
+
40
+ & td:last-child,
41
+ & th:last-child {
42
+ width: 5.6rem;
43
+ white-space: nowrap;
44
+ }
45
+
46
+ ${({ theme })=>theme.breakpoints.large} {
47
+ table-layout: auto;
48
+
49
+ & td:last-child,
50
+ & th:last-child {
51
+ width: auto;
52
+ white-space: normal;
53
+ }
54
+ }
27
55
  `;
28
56
  const StyledThead = styledComponents.styled(designSystem.RawThead)`
29
57
  background: ${({ theme })=>theme.colors.neutral100};
@@ -45,14 +73,14 @@ const StyledTr = styledComponents.styled.tr`
45
73
  height: 48px;
46
74
  user-select: none;
47
75
  background: ${({ theme, $isSelected })=>$isSelected ? theme.colors.primary100 : theme.colors.neutral0};
48
- cursor: ${({ $isMovePending, $isInvalidDropTarget })=>{
49
- if ($isMovePending) {
76
+ cursor: ${({ $isMovePending, $isBusy, $isInvalidDropTarget })=>{
77
+ if ($isMovePending || $isBusy) {
50
78
  return 'wait';
51
79
  }
52
80
  return $isInvalidDropTarget ? 'not-allowed' : 'pointer';
53
81
  }};
54
- opacity: ${({ $isDragging })=>$isDragging ? 0.4 : 1};
55
- pointer-events: ${({ $isMovePending })=>$isMovePending ? 'none' : 'auto'};
82
+ opacity: ${({ $isDragging, $isBusy })=>$isDragging || $isBusy ? 0.4 : 1};
83
+ pointer-events: ${({ $isMovePending, $isBusy })=>$isMovePending || $isBusy ? 'none' : 'auto'};
56
84
 
57
85
  ${({ $isValidDropTarget, theme })=>$isValidDropTarget && styledComponents.css`
58
86
  background: ${theme.colors.primary100};
@@ -75,16 +103,29 @@ const StyledTr = styledComponents.styled.tr`
75
103
  }
76
104
  }
77
105
  `;
78
- // Leading checkbox column. Narrow + centred so the control sits flush against the
79
- // row's left edge.
106
+ // Leading checkbox column. Fixed narrow width so it holds its own column under
107
+ // the mobile fixed table-layout (a 1% width would collapse and let the name
108
+ // cell's preview overlap the control).
80
109
  const CheckboxTd = styledComponents.styled(StyledTd)`
81
- width: 1%;
110
+ width: 5.6rem;
82
111
  white-space: nowrap;
83
112
  `;
84
113
  const CheckboxTh = styledComponents.styled(StyledTh)`
85
- width: 1%;
114
+ width: 5.6rem;
86
115
  white-space: nowrap;
87
116
  `;
117
+ // Flags an asset whose caption or alternative text is empty — mirrors the
118
+ // per-field warning shown in the details drawer. Pushed to the far end of the
119
+ // name cell (shrink 0 so a long name never crowds it out).
120
+ const MetadataWarning = styledComponents.styled(icons.WarningCircle)`
121
+ flex-shrink: 0;
122
+ width: 1.6rem;
123
+ height: 1.6rem;
124
+
125
+ path {
126
+ fill: ${({ theme })=>theme.colors.warning500};
127
+ }
128
+ `;
88
129
  // The asset filename is its own interactive element: clicking it opens the
89
130
  // details drawer instead of selecting the row.
90
131
  const NameButton = styledComponents.styled.button`
@@ -105,8 +146,14 @@ const NameButton = styledComponents.styled.button`
105
146
  border-radius: 2px;
106
147
  }
107
148
  `;
149
+ // Shields the row from its own controls (checkbox, "..." trigger) without
150
+ // severing propagation for the menu's portaled content — those events are React
151
+ // children of the cell but not DOM descendants, and Radix needs them to reach
152
+ // `document` to dismiss its layers.
108
153
  const stopRowEvent = (e)=>{
109
- e.stopPropagation();
154
+ if (isEventFromWithin.isEventFromWithin(e)) {
155
+ e.stopPropagation();
156
+ }
110
157
  };
111
158
  const AssetPreviewCell = ({ asset })=>{
112
159
  const { ext, mime } = asset;
@@ -126,20 +173,35 @@ const AssetPreviewCell = ({ asset })=>{
126
173
  });
127
174
  };
128
175
  const AssetRow = ({ asset, orderedItemKeys, onAssetItemClick })=>{
129
- const isMobile = strapiAdmin.useIsMobile();
176
+ const isDesktop = strapiAdmin.useIsDesktop();
130
177
  const { formatDate, formatMessage } = reactIntl.useIntl();
131
178
  const { isMovePending } = AssetsDndProvider.useAssetsDndOptional() ?? {
132
179
  isMovePending: false
133
180
  };
134
- const { attributes, listeners, setNodeRef, isDragging } = useAssetDnd.useFileDraggable(asset);
181
+ const { attributes, listeners, setNodeRef, isDragging, dragData } = useAssetDnd.useFileDraggable(asset);
135
182
  const { isSelected, toggle, selectRange } = useAssetSelection.useAssetSelection();
136
183
  const { canUpdate } = useMediaLibraryPermissions.useMediaLibraryPermissions();
184
+ const busyMessage = useBusyAssets.useBusyAssetsOptional()?.getBusyMessage(asset.id) ?? null;
137
185
  const key = selection.assetKey(asset.id);
138
186
  const selected = isSelected(key);
187
+ // Caption and alternative text apply to every file type — same "empty =
188
+ // falsy" rule as the drawer's per-field warning.
189
+ const isMetadataMissing = !asset.caption || !asset.alternativeText;
190
+ const metadataMissingLabel = formatMessage({
191
+ id: translations.getTranslationKey('list.table.row.metadata-missing'),
192
+ defaultMessage: 'This asset is missing metadata (caption or alternative text).'
193
+ });
139
194
  // Plain click opens the asset details; pointer selection lives on the
140
195
  // checkbox only. Modifier clicks keep the selection semantics: shift selects
141
196
  // a range, cmd/ctrl toggles.
197
+ //
198
+ // The containment guard on this and the handler below is what keeps the
199
+ // actions menu's portaled dialogs — React children of this row — from
200
+ // opening the details drawer or toggling the selection.
142
201
  const handleRowClick = (e)=>{
202
+ if (!isEventFromWithin.isEventFromWithin(e)) {
203
+ return;
204
+ }
143
205
  if (e.shiftKey) {
144
206
  selectRange(orderedItemKeys, key);
145
207
  } else if (e.metaKey || e.ctrlKey) {
@@ -150,6 +212,9 @@ const AssetRow = ({ asset, orderedItemKeys, onAssetItemClick })=>{
150
212
  };
151
213
  // Desktop: Space toggles selection (additive), Enter opens the details drawer.
152
214
  const handleKeyDown = (e)=>{
215
+ if (!isEventFromWithin.isEventFromWithin(e)) {
216
+ return;
217
+ }
153
218
  if (e.key === 'Enter') {
154
219
  e.preventDefault();
155
220
  onAssetItemClick(asset.id);
@@ -176,14 +241,20 @@ const AssetRow = ({ asset, orderedItemKeys, onAssetItemClick })=>{
176
241
  ...listeners,
177
242
  $isDragging: isDragging,
178
243
  $isMovePending: isMovePending,
244
+ $isBusy: busyMessage !== null,
179
245
  $isSelected: selected,
180
246
  tabIndex: 0,
181
247
  role: "row",
182
248
  onDragStart: (e)=>e.preventDefault(),
183
249
  onClick: handleRowClick,
184
250
  onKeyDown: handleKeyDown,
251
+ onPointerDown: (e)=>{
252
+ if (isEventFromWithin.isEventFromWithin(e)) {
253
+ listeners?.onPointerDown?.(e);
254
+ }
255
+ },
185
256
  children: [
186
- !isMobile && canUpdate && /*#__PURE__*/ jsxRuntime.jsx(CheckboxTd, {
257
+ canUpdate && /*#__PURE__*/ jsxRuntime.jsx(CheckboxTd, {
187
258
  onClick: stopRowEvent,
188
259
  onKeyDown: stopRowEvent,
189
260
  children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
@@ -201,38 +272,61 @@ const AssetRow = ({ asset, orderedItemKeys, onAssetItemClick })=>{
201
272
  }),
202
273
  /*#__PURE__*/ jsxRuntime.jsx(StyledTd, {
203
274
  children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
204
- gap: 3,
205
275
  alignItems: "center",
276
+ justifyContent: "space-between",
277
+ gap: 2,
278
+ minWidth: 0,
206
279
  children: [
207
- /*#__PURE__*/ jsxRuntime.jsx(AssetPreviewCell, {
208
- asset: asset
209
- }),
210
280
  /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
211
- direction: "column",
212
- alignItems: "flex-start",
281
+ gap: 3,
282
+ alignItems: "center",
213
283
  minWidth: 0,
214
284
  children: [
215
- /*#__PURE__*/ jsxRuntime.jsx(NameButton, {
216
- type: "button",
217
- onClick: handleNameClick,
218
- children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
219
- textColor: "neutral800",
220
- fontWeight: "semiBold",
221
- ellipsis: true,
222
- children: asset.name
285
+ busyMessage !== null ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
286
+ justifyContent: "center",
287
+ width: "3.2rem",
288
+ height: "3.2rem",
289
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Loader, {
290
+ small: true,
291
+ children: busyMessage
223
292
  })
293
+ }) : /*#__PURE__*/ jsxRuntime.jsx(AssetPreviewCell, {
294
+ asset: asset
224
295
  }),
225
- isMobile && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
226
- textColor: "neutral600",
227
- variant: "pi",
228
- children: asset.size ? files.formatBytes(asset.size, 1) : '-'
296
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
297
+ direction: "column",
298
+ alignItems: "flex-start",
299
+ minWidth: 0,
300
+ children: [
301
+ /*#__PURE__*/ jsxRuntime.jsx(NameButton, {
302
+ type: "button",
303
+ onClick: handleNameClick,
304
+ children: /*#__PURE__*/ jsxRuntime.jsx(TruncatedText.TruncatedText, {
305
+ textColor: "neutral800",
306
+ fontWeight: "semiBold",
307
+ children: asset.name
308
+ })
309
+ }),
310
+ !isDesktop && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
311
+ textColor: "neutral600",
312
+ variant: "pi",
313
+ children: asset.size ? files.formatBytes(asset.size, 1) : '-'
314
+ })
315
+ ]
229
316
  })
230
317
  ]
318
+ }),
319
+ isMetadataMissing && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tooltip, {
320
+ label: metadataMissingLabel,
321
+ children: /*#__PURE__*/ jsxRuntime.jsx(MetadataWarning, {
322
+ "aria-label": metadataMissingLabel,
323
+ role: "img"
324
+ })
231
325
  })
232
326
  ]
233
327
  })
234
328
  }),
235
- !isMobile && /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
329
+ isDesktop && /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
236
330
  children: [
237
331
  /*#__PURE__*/ jsxRuntime.jsx(StyledTd, {
238
332
  children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
@@ -259,16 +353,14 @@ const AssetRow = ({ asset, orderedItemKeys, onAssetItemClick })=>{
259
353
  ]
260
354
  }),
261
355
  /*#__PURE__*/ jsxRuntime.jsx(StyledTd, {
356
+ onClick: stopRowEvent,
357
+ onKeyDown: stopRowEvent,
358
+ onPointerDown: stopRowEvent,
262
359
  children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
263
360
  justifyContent: "flex-end",
264
- children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
265
- label: formatMessage({
266
- id: translations.getTranslationKey('control-card.more-actions'),
267
- defaultMessage: 'More actions'
268
- }),
269
- variant: "ghost",
270
- onClick: (e)=>e.stopPropagation(),
271
- children: /*#__PURE__*/ jsxRuntime.jsx(icons.More, {})
361
+ children: /*#__PURE__*/ jsxRuntime.jsx(AssetActionsMenu.AssetActionsMenu, {
362
+ asset: asset,
363
+ dragData: dragData
272
364
  })
273
365
  })
274
366
  })
@@ -281,7 +373,7 @@ const FolderTr = styledComponents.styled(StyledTr)`
281
373
  }
282
374
  `;
283
375
  const FolderRow = ({ folder, orderedItemKeys })=>{
284
- const isMobile = strapiAdmin.useIsMobile();
376
+ const isDesktop = strapiAdmin.useIsDesktop();
285
377
  const { formatDate, formatMessage } = reactIntl.useIntl();
286
378
  const { navigateToFolder } = useFolderNavigation.useFolderNavigation();
287
379
  const { isSelected, toggle, selectRange } = useAssetSelection.useAssetSelection();
@@ -289,11 +381,18 @@ const FolderRow = ({ folder, orderedItemKeys })=>{
289
381
  const { isMovePending } = AssetsDndProvider.useAssetsDndOptional() ?? {
290
382
  isMovePending: false
291
383
  };
292
- const { draggable: { attributes, listeners, setNodeRef: setDragRef, isDragging }, droppable: { setNodeRef: setDropRef }, showValidDropHighlight, showInvalidDropCursor } = useAssetDnd.useFolderDraggableDroppable(folder);
384
+ const { dragData, draggable: { attributes, listeners, setNodeRef: setDragRef, isDragging }, droppable: { setNodeRef: setDropRef }, showValidDropHighlight, showInvalidDropCursor } = useAssetDnd.useFolderDraggableDroppable(folder);
293
385
  const key = selection.folderKey(folder.id);
294
386
  // Folders share the selection mechanism with assets. Only the plain-click
295
387
  // semantic differs: it navigates into the folder instead of selecting it.
388
+ //
389
+ // The containment guard on this and the handlers below is what keeps the
390
+ // actions menu's portaled dialogs — React children of this row — from
391
+ // navigating into the folder or starting a drag.
296
392
  const handleRowClick = (e)=>{
393
+ if (!isEventFromWithin.isEventFromWithin(e)) {
394
+ return;
395
+ }
297
396
  if (e.shiftKey) {
298
397
  selectRange(orderedItemKeys, key);
299
398
  } else if (e.metaKey || e.ctrlKey) {
@@ -304,6 +403,9 @@ const FolderRow = ({ folder, orderedItemKeys })=>{
304
403
  };
305
404
  // Enter navigates into the folder, Space toggles selection (same as assets).
306
405
  const handleKeyDown = (e)=>{
406
+ if (!isEventFromWithin.isEventFromWithin(e)) {
407
+ return;
408
+ }
307
409
  if (e.key === 'Enter') {
308
410
  e.preventDefault();
309
411
  navigateToFolder(folder);
@@ -334,11 +436,20 @@ const FolderRow = ({ folder, orderedItemKeys })=>{
334
436
  $isSelected: isSelected(key),
335
437
  tabIndex: 0,
336
438
  role: "row",
337
- onDragStart: (e)=>e.preventDefault(),
439
+ onDragStart: (e)=>{
440
+ if (isEventFromWithin.isEventFromWithin(e)) {
441
+ e.preventDefault();
442
+ }
443
+ },
338
444
  onClick: handleRowClick,
339
445
  onKeyDown: handleKeyDown,
446
+ onPointerDown: (e)=>{
447
+ if (isEventFromWithin.isEventFromWithin(e)) {
448
+ listeners?.onPointerDown?.(e);
449
+ }
450
+ },
340
451
  children: [
341
- !isMobile && canUpdate && /*#__PURE__*/ jsxRuntime.jsx(CheckboxTd, {
452
+ canUpdate && /*#__PURE__*/ jsxRuntime.jsx(CheckboxTd, {
342
453
  onClick: stopRowEvent,
343
454
  onKeyDown: stopRowEvent,
344
455
  children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
@@ -358,6 +469,7 @@ const FolderRow = ({ folder, orderedItemKeys })=>{
358
469
  children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
359
470
  gap: 3,
360
471
  alignItems: "center",
472
+ minWidth: 0,
361
473
  children: [
362
474
  /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
363
475
  justifyContent: "center",
@@ -372,16 +484,15 @@ const FolderRow = ({ folder, orderedItemKeys })=>{
372
484
  height: 20
373
485
  })
374
486
  }),
375
- /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
487
+ /*#__PURE__*/ jsxRuntime.jsx(TruncatedText.TruncatedText, {
376
488
  textColor: "neutral800",
377
489
  fontWeight: "semiBold",
378
- ellipsis: true,
379
490
  children: folder.name
380
491
  })
381
492
  ]
382
493
  })
383
494
  }),
384
- !isMobile && /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
495
+ isDesktop && /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
385
496
  children: [
386
497
  /*#__PURE__*/ jsxRuntime.jsx(StyledTd, {
387
498
  children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
@@ -408,16 +519,14 @@ const FolderRow = ({ folder, orderedItemKeys })=>{
408
519
  ]
409
520
  }),
410
521
  /*#__PURE__*/ jsxRuntime.jsx(StyledTd, {
522
+ onClick: stopRowEvent,
523
+ onKeyDown: stopRowEvent,
524
+ onPointerDown: stopRowEvent,
411
525
  children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
412
526
  justifyContent: "flex-end",
413
- children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
414
- label: formatMessage({
415
- id: translations.getTranslationKey('control-card.more-actions'),
416
- defaultMessage: 'More actions'
417
- }),
418
- variant: "ghost",
419
- onClick: (e)=>e.stopPropagation(),
420
- children: /*#__PURE__*/ jsxRuntime.jsx(icons.More, {})
527
+ children: /*#__PURE__*/ jsxRuntime.jsx(FolderActionsMenu.FolderActionsMenu, {
528
+ folder: folder,
529
+ dragData: dragData
421
530
  })
422
531
  })
423
532
  })
@@ -425,16 +534,18 @@ const FolderRow = ({ folder, orderedItemKeys })=>{
425
534
  });
426
535
  };
427
536
  const AssetsTable = ({ assets, folders = [], mixedItems = null, onAssetItemClick })=>{
428
- const isMobile = strapiAdmin.useIsMobile();
537
+ const isDesktop = strapiAdmin.useIsDesktop();
429
538
  const { formatMessage } = reactIntl.useIntl();
430
539
  const { selectedKeys, selectAll, clear } = useAssetSelection.useAssetSelection();
431
540
  const { canUpdate } = useMediaLibraryPermissions.useMediaLibraryPermissions();
432
- const visibleHeaders = isMobile ? constants.TABLE_HEADERS.filter((h)=>h.name === 'name' || h.name === 'actions') : constants.TABLE_HEADERS;
541
+ const { trackUsage } = useTracking.useTracking();
542
+ // Below the desktop breakpoint only the name (and actions) column is kept —
543
+ // the date/size columns don't fit; size moves under the name as a subtitle.
544
+ const visibleHeaders = isDesktop ? constants.TABLE_HEADERS : constants.TABLE_HEADERS.filter((h)=>h.name === 'name' || h.name === 'actions');
433
545
  // The checkbox column is a dedicated structural column (not part of
434
- // TABLE_HEADERS). Hidden on mobile (multi-select deferred) and without the
435
- // update permission — every bulk action needs `assets.update`, so a
436
- // read-only user has nothing to select for.
437
- const showCheckboxColumn = !isMobile && canUpdate;
546
+ // TABLE_HEADERS). Hidden only without the update permission every bulk
547
+ // action needs `assets.update`, so a read-only user has nothing to select for.
548
+ const showCheckboxColumn = canUpdate;
438
549
  const colCount = visibleHeaders.length + (showCheckboxColumn ? 1 : 0);
439
550
  const totalRows = folders.length + assets.length;
440
551
  // Render order — folders first by default, or the interleaved mixed order.
@@ -448,6 +559,7 @@ const AssetsTable = ({ assets, folders = [], mixedItems = null, onAssetItemClick
448
559
  if (allSelected) {
449
560
  clear();
450
561
  } else {
562
+ trackUsage('didSelectAllMediaLibraryElements');
451
563
  selectAll(orderedItemKeys);
452
564
  }
453
565
  };