@strapi/upload 5.51.2 → 5.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/dist/admin/future/App.js +3 -3
  2. package/dist/admin/future/App.js.map +1 -1
  3. package/dist/admin/future/App.mjs +4 -4
  4. package/dist/admin/future/App.mjs.map +1 -1
  5. package/dist/admin/future/components/BetaNotice.js +72 -0
  6. package/dist/admin/future/components/BetaNotice.js.map +1 -0
  7. package/dist/admin/future/components/BetaNotice.mjs +70 -0
  8. package/dist/admin/future/components/BetaNotice.mjs.map +1 -0
  9. package/dist/admin/future/components/Drawer.js +16 -3
  10. package/dist/admin/future/components/Drawer.js.map +1 -1
  11. package/dist/admin/future/components/Drawer.mjs +16 -3
  12. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  13. package/dist/admin/future/hooks/useAIMetadataEnabled.js +39 -0
  14. package/dist/admin/future/hooks/useAIMetadataEnabled.js.map +1 -0
  15. package/dist/admin/future/hooks/useAIMetadataEnabled.mjs +37 -0
  16. package/dist/admin/future/hooks/useAIMetadataEnabled.mjs.map +1 -0
  17. package/dist/admin/future/pages/Assets/AssetsPage.js +297 -139
  18. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/AssetsPage.mjs +301 -143
  20. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js +46 -0
  22. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js.map +1 -0
  23. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs +44 -0
  24. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs.map +1 -0
  25. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +325 -0
  26. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -0
  27. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +323 -0
  28. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -0
  29. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +35 -14
  30. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +35 -14
  32. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +54 -53
  34. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +54 -54
  36. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +89 -24
  38. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +91 -26
  40. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +15 -3
  42. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +16 -4
  44. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/AssetsTable.js +168 -58
  46. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  47. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +171 -61
  48. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  49. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +64 -132
  50. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +64 -132
  52. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +123 -76
  54. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +126 -79
  56. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/components/BusyOverlay.js +36 -0
  58. package/dist/admin/future/pages/Assets/components/BusyOverlay.js.map +1 -0
  59. package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs +34 -0
  60. package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs.map +1 -0
  61. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js +131 -0
  62. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js.map +1 -0
  63. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs +129 -0
  64. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -0
  65. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +27 -17
  66. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  67. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +28 -18
  68. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  69. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +28 -31
  70. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -1
  71. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +28 -31
  72. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -1
  73. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +10 -15
  74. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
  75. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +10 -15
  76. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
  77. package/dist/admin/future/pages/Assets/components/FilterBadges.js +13 -4
  78. package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -1
  79. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +13 -4
  80. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -1
  81. package/dist/admin/future/pages/Assets/components/FilterMenu.js +241 -61
  82. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
  83. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +244 -64
  84. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
  85. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +144 -0
  86. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -0
  87. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +142 -0
  88. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -0
  89. package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.js → FolderFormDialog.js} +50 -16
  90. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -0
  91. package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.mjs → FolderFormDialog.mjs} +52 -18
  92. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -0
  93. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +111 -191
  94. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  95. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +114 -194
  96. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  97. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js +47 -0
  98. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js.map +1 -0
  99. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs +44 -0
  100. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs.map +1 -0
  101. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +203 -62
  102. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  103. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +204 -63
  104. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  105. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js +75 -0
  106. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js.map +1 -0
  107. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs +73 -0
  108. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs.map +1 -0
  109. package/dist/admin/future/services/api.js +24 -0
  110. package/dist/admin/future/services/api.js.map +1 -1
  111. package/dist/admin/future/services/api.mjs +24 -0
  112. package/dist/admin/future/services/api.mjs.map +1 -1
  113. package/dist/admin/future/services/assets.js +11 -0
  114. package/dist/admin/future/services/assets.js.map +1 -1
  115. package/dist/admin/future/services/assets.mjs +11 -0
  116. package/dist/admin/future/services/assets.mjs.map +1 -1
  117. package/dist/admin/future/services/folders.js +37 -2
  118. package/dist/admin/future/services/folders.js.map +1 -1
  119. package/dist/admin/future/services/folders.mjs +37 -3
  120. package/dist/admin/future/services/folders.mjs.map +1 -1
  121. package/dist/admin/future/utils/buildDragSetFromSelection.js +7 -5
  122. package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -1
  123. package/dist/admin/future/utils/buildDragSetFromSelection.mjs +7 -5
  124. package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -1
  125. package/dist/admin/future/utils/formatMoveSuccessMessage.js +12 -1
  126. package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -1
  127. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +12 -1
  128. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -1
  129. package/dist/admin/future/utils/getRelationId.js +24 -0
  130. package/dist/admin/future/utils/getRelationId.js.map +1 -0
  131. package/dist/admin/future/utils/getRelationId.mjs +22 -0
  132. package/dist/admin/future/utils/getRelationId.mjs.map +1 -0
  133. package/dist/admin/future/utils/isEventFromWithin.js +14 -0
  134. package/dist/admin/future/utils/isEventFromWithin.js.map +1 -0
  135. package/dist/admin/future/utils/isEventFromWithin.mjs +12 -0
  136. package/dist/admin/future/utils/isEventFromWithin.mjs.map +1 -0
  137. package/dist/admin/future/utils/itemLocations.js +40 -0
  138. package/dist/admin/future/utils/itemLocations.js.map +1 -0
  139. package/dist/admin/future/utils/itemLocations.mjs +36 -0
  140. package/dist/admin/future/utils/itemLocations.mjs.map +1 -0
  141. package/dist/admin/future/utils/itemSource.js +36 -0
  142. package/dist/admin/future/utils/itemSource.js.map +1 -0
  143. package/dist/admin/future/utils/itemSource.mjs +32 -0
  144. package/dist/admin/future/utils/itemSource.mjs.map +1 -0
  145. package/dist/admin/index.js +11 -17
  146. package/dist/admin/index.js.map +1 -1
  147. package/dist/admin/index.mjs +12 -18
  148. package/dist/admin/index.mjs.map +1 -1
  149. package/dist/admin/src/future/App.d.ts +1 -1
  150. package/dist/admin/src/future/components/BetaNotice.d.ts +11 -0
  151. package/dist/admin/src/future/components/Drawer.d.ts +5 -1
  152. package/dist/admin/src/future/hooks/useAIMetadataEnabled.d.ts +26 -0
  153. package/dist/admin/src/future/pages/Assets/components/ActionsMenuContent.d.ts +33 -0
  154. package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +24 -0
  155. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +16 -0
  156. package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +8 -1
  157. package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +17 -6
  158. package/dist/admin/src/future/pages/Assets/components/BusyOverlay.d.ts +9 -0
  159. package/dist/admin/src/future/pages/Assets/components/DeleteItemsDialog.d.ts +27 -0
  160. package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +8 -1
  161. package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +18 -3
  162. package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +2 -1
  163. package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +3 -1
  164. package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +4 -0
  165. package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +20 -0
  166. package/dist/admin/src/future/pages/Assets/components/FolderFormDialog.d.ts +16 -0
  167. package/dist/admin/src/future/pages/Assets/hooks/useBusyAssets.d.ts +38 -0
  168. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +20 -6
  169. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteScrollSentinel.d.ts +41 -0
  170. package/dist/admin/src/future/services/folders.d.ts +6 -1
  171. package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +4 -3
  172. package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +7 -1
  173. package/dist/admin/src/future/utils/getRelationId.d.ts +11 -0
  174. package/dist/admin/src/future/utils/isEventFromWithin.d.ts +11 -0
  175. package/dist/admin/src/future/utils/itemLocations.d.ts +31 -0
  176. package/dist/admin/src/future/utils/itemSource.d.ts +22 -0
  177. package/dist/admin/translations/en.json.js +25 -4
  178. package/dist/admin/translations/en.json.js.map +1 -1
  179. package/dist/admin/translations/en.json.mjs +25 -4
  180. package/dist/admin/translations/en.json.mjs.map +1 -1
  181. package/dist/server/services/image-manipulation.js.map +1 -1
  182. package/dist/server/services/image-manipulation.mjs.map +1 -1
  183. package/dist/server/src/services/image-manipulation.d.ts +0 -5
  184. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  185. package/package.json +9 -9
  186. package/dist/admin/future/pages/Assets/components/CreateFolderDialog.js.map +0 -1
  187. package/dist/admin/future/pages/Assets/components/CreateFolderDialog.mjs.map +0 -1
  188. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +0 -33
  189. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +0 -1
  190. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +0 -31
  191. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +0 -1
  192. package/dist/admin/src/future/pages/Assets/components/CreateFolderDialog.d.ts +0 -8
  193. package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +0 -16
@@ -8,33 +8,37 @@ var designSystem = require('@strapi/design-system');
8
8
  var icons = require('@strapi/icons');
9
9
  var reactIntl = require('react-intl');
10
10
  var styledComponents = require('styled-components');
11
- var useAiAvailability = require('../../../hooks/useAiAvailability.js');
11
+ var BetaNotice = require('../../components/BetaNotice.js');
12
+ var useAIMetadataEnabled = require('../../hooks/useAIMetadataEnabled.js');
12
13
  var useMediaLibraryPermissions = require('../../hooks/useMediaLibraryPermissions.js');
13
14
  var api = require('../../services/api.js');
14
15
  var folders = require('../../services/folders.js');
15
16
  var settings = require('../../services/settings.js');
17
+ var itemLocations = require('../../utils/itemLocations.js');
16
18
  var translations = require('../../utils/translations.js');
17
19
  var AssetDetailsDrawer = require('./components/AssetDetails/AssetDetailsDrawer.js');
18
20
  var AssetsGrid = require('./components/AssetsGrid.js');
19
21
  var AssetsSearchInput = require('./components/AssetsSearchInput.js');
20
22
  var AssetsTable = require('./components/AssetsTable.js');
21
23
  var BulkActionsBar = require('./components/BulkActionsBar.js');
22
- var CreateFolderDialog = require('./components/CreateFolderDialog.js');
23
24
  var AssetsDndProvider = require('./components/Dnd/AssetsDndProvider.js');
24
25
  var UploadDropZone = require('./components/DropZone/UploadDropZone.js');
25
26
  var UploadDropZoneContext = require('./components/DropZone/UploadDropZoneContext.js');
26
27
  var EmptyState = require('./components/EmptyState.js');
27
28
  var FilterBadges = require('./components/FilterBadges.js');
28
29
  var FilterMenu = require('./components/FilterMenu.js');
30
+ var FolderFormDialog = require('./components/FolderFormDialog.js');
29
31
  var FolderTree = require('./components/FolderTree/FolderTree.js');
30
32
  var ImportFromUrlDialog = require('./components/ImportFromUrlDialog.js');
31
33
  var SortMenu = require('./components/SortMenu.js');
32
34
  var constants = require('./constants.js');
33
35
  var useAssetSearch = require('./hooks/useAssetSearch.js');
34
36
  var useAssetSelection = require('./hooks/useAssetSelection.js');
37
+ var useBusyAssets = require('./hooks/useBusyAssets.js');
35
38
  var useFolderInfo = require('./hooks/useFolderInfo.js');
36
39
  var useFolderNavigation = require('./hooks/useFolderNavigation.js');
37
40
  var useInfiniteAssets = require('./hooks/useInfiniteAssets.js');
41
+ var useInfiniteScrollSentinel = require('./hooks/useInfiniteScrollSentinel.js');
38
42
  var useListFilters = require('./hooks/useListFilters.js');
39
43
  var useListSort = require('./hooks/useListSort.js');
40
44
  var buildAssetFilters = require('./utils/buildAssetFilters.js');
@@ -61,8 +65,18 @@ function _interopNamespace(e) {
61
65
 
62
66
  var ToggleGroup__namespace = /*#__PURE__*/_interopNamespace(ToggleGroup);
63
67
 
68
+ // The negative bottom margin shrinks the trigger area by a pixel so the 1px
69
+ // sentinel sitting exactly at the fold (when a page's rows happen to fill the
70
+ // viewport) reads as "not visible" and doesn't pull an extra page. It still
71
+ // triggers as soon as the sentinel scrolls a hair into view. Tune the bottom
72
+ // value if the list over- or under-fetches near the fold.
64
73
  const INTERSECTION_OPTIONS = {
65
- threshold: 0.1
74
+ threshold: 0,
75
+ rootMargin: '0px 0px -1px 0px'
76
+ };
77
+ // Module-level so the header sentinel's observer isn't rebuilt every render.
78
+ const HEADER_SENTINEL_OPTIONS = {
79
+ threshold: 0
66
80
  };
67
81
  const ITEM_COUNT_MESSAGE = {
68
82
  id: translations.getTranslationKey('header.content.item-count'),
@@ -91,7 +105,7 @@ const getSearchResultsCountMessage = (numberFolders, numberAssets)=>{
91
105
  }
92
106
  return SEARCH_RESULTS_COUNT_MESSAGES.both;
93
107
  };
94
- const AssetsView = ({ view, folders, isLoadingFolders, assets, isLoadingAssets, isFetchingMore, hasNextPage, fetchNextPage, error, searchQuery, assetsSort, foldersPosition, hasActiveFilters, onClearFilters, onAssetItemClick, onAddAssets, canAddAssets, onClearSearch })=>{
108
+ const AssetsView = ({ view, folders, isLoadingFolders, assets, isLoadingAssets, isFetchingMore, hasNextPage, fetchNextPage, error, locations, searchQuery, assetsSort, foldersPosition, hasActiveFilters, onClearFilters, onAssetItemClick, onAddAssets, canAddAssets, onClearSearch })=>{
95
109
  const { formatMessage } = reactIntl.useIntl();
96
110
  const isGridView = view === constants.viewOptions.GRID;
97
111
  const isLoading = isLoadingAssets || isLoadingFolders;
@@ -111,15 +125,12 @@ const AssetsView = ({ view, folders, isLoadingFolders, assets, isLoadingAssets,
111
125
  assetsSort,
112
126
  hasNextPage
113
127
  ]);
114
- const loadMoreRef = strapiAdmin.useElementOnScreen(React.useCallback((isVisible)=>{
115
- if (isVisible && hasNextPage && !isFetchingMore) {
116
- fetchNextPage();
117
- }
118
- }, [
128
+ const loadMoreRef = useInfiniteScrollSentinel.useInfiniteScrollSentinel({
119
129
  hasNextPage,
120
130
  isFetchingMore,
121
- fetchNextPage
122
- ]), INTERSECTION_OPTIONS);
131
+ onLoadMore: fetchNextPage,
132
+ options: INTERSECTION_OPTIONS
133
+ });
123
134
  if (isLoading) {
124
135
  return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
125
136
  justifyContent: "center",
@@ -185,7 +196,8 @@ const AssetsView = ({ view, folders, isLoadingFolders, assets, isLoadingAssets,
185
196
  })
186
197
  }),
187
198
  /*#__PURE__*/ jsxRuntime.jsx(BulkActionsBar.BulkActionsBar, {
188
- assets: assets
199
+ assets: assets,
200
+ locations: locations
189
201
  })
190
202
  ]
191
203
  });
@@ -245,13 +257,122 @@ const StyledToggleItem = styledComponents.styled(ToggleGroup__namespace.Item)`
245
257
  height: 1.6rem;
246
258
  }
247
259
  `;
248
- const HeaderWrapper = styledComponents.styled(designSystem.Box)`
249
- [data-strapi-header] {
250
- background: ${({ theme })=>theme.colors.neutral0};
260
+ // A single sticky header — same layout scrolled or not, so the toolbar
261
+ // alignment can't reflow the way the shared sticky header did. Once the list
262
+ // scrolls under it, it shrinks (reduced vertical padding + a shadow to lift it
263
+ // off the rows), like the sticky headers elsewhere in the admin. Horizontal
264
+ // padding stays responsive (passed as props); only the vertical rhythm + shadow
265
+ // change here so they can animate.
266
+ const StickyHeader = styledComponents.styled(designSystem.Box)`
267
+ position: sticky;
268
+ top: 0;
269
+ z-index: 2;
270
+ /* Transparent at rest (the grey page shows through); an opaque background +
271
+ shadow appear only once it sticks and content scrolls under it. */
272
+ background: transparent;
273
+ /* Horizontal padding matches the list's default spacing (Layouts.Content /
274
+ RESPONSIVE_DEFAULT_SPACING: 4 / 6 / 10) so the header lines up with the rows. */
275
+ padding: ${({ theme })=>`${theme.spaces[6]} ${theme.spaces[4]}`};
276
+ transition:
277
+ padding 0.2s ease,
278
+ background 0.2s ease,
279
+ box-shadow 0.2s ease;
251
280
 
252
- h1 {
253
- font-size: 1.8rem;
254
- }
281
+ ${({ theme })=>theme.breakpoints.medium} {
282
+ padding-left: ${({ theme })=>theme.spaces[6]};
283
+ padding-right: ${({ theme })=>theme.spaces[6]};
284
+ }
285
+ ${({ theme })=>theme.breakpoints.large} {
286
+ padding-left: ${({ theme })=>theme.spaces[10]};
287
+ padding-right: ${({ theme })=>theme.spaces[10]};
288
+ }
289
+
290
+ /* Compacting is scoped to medium and up, where the header actually sticks. On
291
+ mobile it scrolls away with the list, so shrinking it mid-scroll animated a
292
+ header the user could no longer see — the transition read as a glitch on the
293
+ way back up rather than as the header settling. */
294
+ ${({ $compact, theme })=>$compact && styledComponents.css`
295
+ ${theme.breakpoints.medium} {
296
+ padding-top: ${theme.spaces[3]};
297
+ padding-bottom: ${theme.spaces[3]};
298
+ padding-left: ${theme.spaces[4]};
299
+ padding-right: ${theme.spaces[4]};
300
+ background: ${theme.colors.neutral0};
301
+ box-shadow: ${theme.shadows.tableShadow};
302
+ }
303
+ ${theme.breakpoints.large} {
304
+ padding-left: ${theme.spaces[6]};
305
+ padding-right: ${theme.spaces[6]};
306
+ }
307
+ `}
308
+ `;
309
+ // New button aligns with the title, not the toolbar.
310
+ const TitleRow = styledComponents.styled(designSystem.Flex)`
311
+ justify-content: space-between;
312
+ align-items: flex-start;
313
+ gap: ${({ theme })=>theme.spaces[4]};
314
+
315
+ h1 {
316
+ font-size: 1.8rem;
317
+ }
318
+ `;
319
+ // Two groups in source order (Filter, Search / Sort, Toggle). Mobile: stacked —
320
+ // Filter+Search on row 1, Sort+Toggle on row 2. Desktop (large+): one row,
321
+ // Filter+Search on the left, Sort+Toggle pushed right.
322
+ const Toolbar = styledComponents.styled(designSystem.Flex)`
323
+ margin-top: ${({ theme })=>theme.spaces[5]};
324
+ flex-direction: column;
325
+ align-items: stretch;
326
+ gap: ${({ theme })=>theme.spaces[3]};
327
+ transition: margin-top 0.2s ease;
328
+
329
+ /* Tightening the gap to the title belongs to the compact header, so it is
330
+ scoped to the breakpoints that compact. On mobile the header never sticks,
331
+ and this was the last thing still shifting as the page scrolled. */
332
+ ${({ $compact, theme })=>$compact && styledComponents.css`
333
+ ${theme.breakpoints.medium} {
334
+ margin-top: ${theme.spaces[2]};
335
+ }
336
+ `}
337
+
338
+ ${({ theme })=>theme.breakpoints.large} {
339
+ flex-direction: row;
340
+ align-items: center;
341
+ justify-content: space-between;
342
+ }
343
+ `;
344
+ const ToolbarGroup = styledComponents.styled(designSystem.Flex)`
345
+ align-items: center;
346
+ gap: ${({ theme })=>theme.spaces[3]};
347
+ `;
348
+ // Filter + Search. On mobile the group fills the row (search grows into it);
349
+ // on desktop it keeps its intrinsic size and sits at the left edge.
350
+ const FilterSearchGroup = styledComponents.styled(ToolbarGroup)``;
351
+ // Sort + Toggle. Mobile: spread across the row (Sort left, Toggle right).
352
+ // Desktop: sit together at the right edge (space-between on the toolbar leaves
353
+ // the empty space between the search and here).
354
+ const SortToggleGroup = styledComponents.styled(ToolbarGroup)`
355
+ justify-content: space-between;
356
+
357
+ ${({ theme })=>theme.breakpoints.large} {
358
+ justify-content: flex-end;
359
+ flex: 0 0 auto;
360
+ }
361
+ `;
362
+ // Fills the row on mobile; intrinsic width on desktop.
363
+ const SearchSlot = styledComponents.styled(designSystem.Box)`
364
+ flex: 1;
365
+
366
+ ${({ theme })=>theme.breakpoints.large} {
367
+ flex: 0 1 auto;
368
+ }
369
+ `;
370
+ // Toggle labels are hidden below desktop — icons only (buttons keep aria-label).
371
+ const ToggleLabel = styledComponents.styled.span`
372
+ display: none;
373
+
374
+ ${({ theme })=>theme.breakpoints.large} {
375
+ display: inline;
255
376
  }
256
377
  `;
257
378
  const AssetsPage = ()=>{
@@ -284,7 +405,7 @@ const AssetsPage = ()=>{
284
405
  [
285
406
  listFilters.serialized
286
407
  ]);
287
- const { assets, pagination, isLoading: isLoadingAssets, isFetchingMore, hasNextPage, fetchNextPage, error: assetsError } = useInfiniteAssets.useInfiniteAssets({
408
+ const { assets, subscribers: assetPageSubscribers, pagination, isLoading: isLoadingAssets, isFetchingMore, hasNextPage, fetchNextPage, error: assetsError } = useInfiniteAssets.useInfiniteAssets({
288
409
  // The real folder is passed even while searching: the service drops the folder
289
410
  // filter when `search` is set, so results are still global. List filters
290
411
  // compose with both modes.
@@ -292,7 +413,6 @@ const AssetsPage = ()=>{
292
413
  search: searchQuery || undefined,
293
414
  sort: listSort.assetsSort,
294
415
  filters: builtFilters.fileClauses,
295
- filtersKey: listFilters.serialized,
296
416
  enabled: builtFilters.showFiles
297
417
  });
298
418
  const { data: fetchedFolders = [], isLoading: isLoadingFolders } = folders.useGetFoldersQuery({
@@ -308,6 +428,13 @@ const AssetsPage = ()=>{
308
428
  builtFilters.showFolders,
309
429
  fetchedFolders
310
430
  ]);
431
+ // Both move affordances (drag and the bulk bar) resolve each item's parent
432
+ // from the rows on screen — while searching, results are global and the
433
+ // folder currently open says nothing about where an item lives.
434
+ const itemLocations$1 = React.useMemo(()=>itemLocations.buildItemLocations(assets, folders$1), [
435
+ assets,
436
+ folders$1
437
+ ]);
311
438
  const itemCountLabel = formatMessage(ITEM_COUNT_MESSAGE, {
312
439
  count: itemCount
313
440
  });
@@ -343,6 +470,12 @@ const AssetsPage = ()=>{
343
470
  // Refs
344
471
  const fileInputRef = React.useRef(null);
345
472
  const uploadDropZoneRef = React.useRef(null);
473
+ // Shrink the sticky header once the list scrolls beneath it. A zero-height
474
+ // sentinel sits at the very top of the scroll content; when it leaves the
475
+ // viewport we're scrolled → compact.
476
+ const [isHeaderCompact, setIsHeaderCompact] = React.useState(false);
477
+ const onHeaderSentinelVisibility = React.useCallback((isVisible)=>setIsHeaderCompact(!isVisible), []);
478
+ const headerSentinelRef = strapiAdmin.useElementOnScreen(onHeaderSentinelVisibility, HEADER_SENTINEL_OPTIONS);
346
479
  // Upload handlers
347
480
  const [uploadFiles] = api.useUploadFilesMutation();
348
481
  const [uploadFromUrls] = api.useUploadFromUrlsMutation();
@@ -351,8 +484,10 @@ const AssetsPage = ()=>{
351
484
  // server asked for.
352
485
  const { data: settings$1 } = settings.useGetUploadSettingsQuery();
353
486
  const concurrency = settings$1?.data?.concurrentUploadRequests ?? 1;
354
- // Drives the post-upload AI metadata phase shown per row in the progress dialog.
355
- const { isEnabled: isAiMetadataEnabled } = useAiAvailability.useAIAvailability();
487
+ // Drives the post-upload AI metadata phase shown per row in the progress
488
+ // dialog. No mime argument: the files are not known until they are picked,
489
+ // and the server filters the batch on the same allowlist anyway.
490
+ const isAiMetadataEnabled = useAIMetadataEnabled.useAIMetadataEnabled();
356
491
  const uploadFilesToFolder = async (files, folderId)=>{
357
492
  if (files.length === 0) return;
358
493
  const formData = new FormData();
@@ -421,93 +556,105 @@ const AssetsPage = ()=>{
421
556
  disabled: !canCreate,
422
557
  children: /*#__PURE__*/ jsxRuntime.jsx(useAssetSelection.AssetSelectionProvider, {
423
558
  disabled: !canUpdate,
424
- children: /*#__PURE__*/ jsxRuntime.jsxs(AssetsDndProvider.AssetsDndProvider, {
425
- children: [
426
- /*#__PURE__*/ jsxRuntime.jsx(ClearSelectionOnChange, {
427
- listQueryKey: listQueryKey$1
428
- }),
429
- /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
430
- ref: uploadDropZoneRef,
431
- children: /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Layouts.Root, {
432
- minHeight: "100vh",
433
- background: "neutral0",
559
+ children: /*#__PURE__*/ jsxRuntime.jsx(useBusyAssets.BusyAssetsProvider, {
560
+ children: /*#__PURE__*/ jsxRuntime.jsxs(AssetsDndProvider.AssetsDndProvider, {
561
+ locations: itemLocations$1,
562
+ children: [
563
+ /*#__PURE__*/ jsxRuntime.jsx(ClearSelectionOnChange, {
564
+ listQueryKey: listQueryKey$1
565
+ }),
566
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.Root, {
434
567
  sideNav: /*#__PURE__*/ jsxRuntime.jsx(FolderTree.FolderTree, {
435
568
  currentFolderId: currentFolderId,
436
569
  showActiveFolder: !isSearching,
437
570
  onSelectFolder: navigateToFolderId
438
571
  }),
439
- children: [
440
- /*#__PURE__*/ jsxRuntime.jsx(designSystem.VisuallyHidden, {
441
- children: /*#__PURE__*/ jsxRuntime.jsx("input", {
442
- type: "file",
443
- ref: fileInputRef,
444
- onChange: handleFileChange,
445
- multiple: true
446
- })
447
- }),
448
- /*#__PURE__*/ jsxRuntime.jsx(HeaderWrapper, {
449
- children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.Header, {
450
- title: pageHeaderTitle,
451
- primaryAction: canCreate && /*#__PURE__*/ jsxRuntime.jsxs(designSystem.SimpleMenu, {
452
- popoverPlacement: "bottom-end",
453
- variant: "default",
454
- endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.ChevronDown, {}),
455
- label: formatMessage({
456
- id: translations.getTranslationKey('new'),
457
- defaultMessage: 'New'
458
- }),
459
- children: [
460
- /*#__PURE__*/ jsxRuntime.jsx(designSystem.MenuItem, {
461
- onSelect: ()=>setIsCreateFolderDialogOpen(true),
462
- startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.Folder, {}),
463
- children: formatMessage({
464
- id: translations.getTranslationKey('folder.create.title'),
465
- defaultMessage: 'New folder'
466
- })
467
- }),
468
- /*#__PURE__*/ jsxRuntime.jsx(designSystem.MenuItem, {
469
- onSelect: handleFileSelect,
470
- startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.Files, {}),
471
- children: formatMessage({
472
- id: translations.getTranslationKey('import-files'),
473
- defaultMessage: 'Import files'
474
- })
475
- }),
476
- /*#__PURE__*/ jsxRuntime.jsx(designSystem.MenuItem, {
477
- onSelect: ()=>setIsUrlDialogOpen(true),
478
- startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.Link, {}),
479
- children: formatMessage({
480
- id: translations.getTranslationKey('import-from-url'),
481
- defaultMessage: 'Import from URL'
482
- })
483
- })
484
- ]
572
+ children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Main, {
573
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
574
+ ref: uploadDropZoneRef,
575
+ children: [
576
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.VisuallyHidden, {
577
+ children: /*#__PURE__*/ jsxRuntime.jsx("input", {
578
+ type: "file",
579
+ ref: fileInputRef,
580
+ onChange: handleFileChange,
581
+ multiple: true
582
+ })
583
+ }),
584
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
585
+ ref: headerSentinelRef,
586
+ height: 0,
587
+ "aria-hidden": true
485
588
  }),
486
- subtitle: /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
589
+ /*#__PURE__*/ jsxRuntime.jsxs(StickyHeader, {
590
+ $compact: isHeaderCompact,
487
591
  children: [
488
- /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
489
- justifyContent: "space-between",
490
- alignItems: "center",
491
- gap: 4,
492
- width: "100%",
592
+ /*#__PURE__*/ jsxRuntime.jsxs(TitleRow, {
593
+ children: [
594
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
595
+ variant: "alpha",
596
+ tag: "h1",
597
+ children: pageHeaderTitle
598
+ }),
599
+ canCreate && /*#__PURE__*/ jsxRuntime.jsxs(designSystem.SimpleMenu, {
600
+ popoverPlacement: "bottom-end",
601
+ variant: "default",
602
+ endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.ChevronDown, {}),
603
+ label: formatMessage({
604
+ id: translations.getTranslationKey('new'),
605
+ defaultMessage: 'New'
606
+ }),
607
+ children: [
608
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.MenuItem, {
609
+ onSelect: ()=>setIsCreateFolderDialogOpen(true),
610
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.Folder, {}),
611
+ children: formatMessage({
612
+ id: translations.getTranslationKey('folder.create.title'),
613
+ defaultMessage: 'New folder'
614
+ })
615
+ }),
616
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.MenuItem, {
617
+ onSelect: handleFileSelect,
618
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.Files, {}),
619
+ children: formatMessage({
620
+ id: translations.getTranslationKey('import-files'),
621
+ defaultMessage: 'Import files'
622
+ })
623
+ }),
624
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.MenuItem, {
625
+ onSelect: ()=>setIsUrlDialogOpen(true),
626
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.Link, {}),
627
+ children: formatMessage({
628
+ id: translations.getTranslationKey('import-from-url'),
629
+ defaultMessage: 'Import from URL'
630
+ })
631
+ })
632
+ ]
633
+ })
634
+ ]
635
+ }),
636
+ /*#__PURE__*/ jsxRuntime.jsxs(Toolbar, {
637
+ $compact: isHeaderCompact,
493
638
  children: [
494
- /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
495
- gap: 4,
496
- alignItems: "center",
639
+ /*#__PURE__*/ jsxRuntime.jsxs(FilterSearchGroup, {
497
640
  children: [
498
- /*#__PURE__*/ jsxRuntime.jsx(FilterMenu.FilterMenu, {
499
- listFilters: listFilters
641
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
642
+ children: /*#__PURE__*/ jsxRuntime.jsx(FilterMenu.FilterMenu, {
643
+ listFilters: listFilters
644
+ })
500
645
  }),
501
- /*#__PURE__*/ jsxRuntime.jsx(AssetsSearchInput.AssetsSearchInput, {})
646
+ /*#__PURE__*/ jsxRuntime.jsx(SearchSlot, {
647
+ children: /*#__PURE__*/ jsxRuntime.jsx(AssetsSearchInput.AssetsSearchInput, {})
648
+ })
502
649
  ]
503
650
  }),
504
- /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
505
- gap: 4,
506
- alignItems: "center",
651
+ /*#__PURE__*/ jsxRuntime.jsxs(SortToggleGroup, {
507
652
  children: [
508
- /*#__PURE__*/ jsxRuntime.jsx(SortMenu.SortMenu, {
509
- sort: listSort,
510
- showFoldersGroup: !isGridView
653
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
654
+ children: /*#__PURE__*/ jsxRuntime.jsx(SortMenu.SortMenu, {
655
+ sort: listSort,
656
+ showFoldersGroup: !isGridView
657
+ })
511
658
  }),
512
659
  /*#__PURE__*/ jsxRuntime.jsxs(StyledToggleGroup, {
513
660
  type: "single",
@@ -526,9 +673,11 @@ const AssetsPage = ()=>{
526
673
  }),
527
674
  children: [
528
675
  /*#__PURE__*/ jsxRuntime.jsx(icons.List, {}),
529
- formatMessage({
530
- id: translations.getTranslationKey('view.table'),
531
- defaultMessage: 'Table view'
676
+ /*#__PURE__*/ jsxRuntime.jsx(ToggleLabel, {
677
+ children: formatMessage({
678
+ id: translations.getTranslationKey('view.table'),
679
+ defaultMessage: 'Table view'
680
+ })
532
681
  })
533
682
  ]
534
683
  }),
@@ -540,9 +689,11 @@ const AssetsPage = ()=>{
540
689
  }),
541
690
  children: [
542
691
  /*#__PURE__*/ jsxRuntime.jsx(icons.GridFour, {}),
543
- formatMessage({
544
- id: translations.getTranslationKey('view.grid'),
545
- defaultMessage: 'Grid view'
692
+ /*#__PURE__*/ jsxRuntime.jsx(ToggleLabel, {
693
+ children: formatMessage({
694
+ id: translations.getTranslationKey('view.grid'),
695
+ defaultMessage: 'Grid view'
696
+ })
546
697
  })
547
698
  ]
548
699
  })
@@ -553,52 +704,59 @@ const AssetsPage = ()=>{
553
704
  ]
554
705
  }),
555
706
  /*#__PURE__*/ jsxRuntime.jsx(FilterBadges.FilterBadges, {
556
- listFilters: listFilters
707
+ listFilters: listFilters,
708
+ compact: isHeaderCompact
709
+ })
710
+ ]
711
+ }),
712
+ /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Layouts.Content, {
713
+ children: [
714
+ /*#__PURE__*/ jsxRuntime.jsx(BetaNotice.BetaNotice, {}),
715
+ assetPageSubscribers,
716
+ /*#__PURE__*/ jsxRuntime.jsxs(UploadDropZone.DropZoneWithOverlay, {
717
+ children: [
718
+ /*#__PURE__*/ jsxRuntime.jsx(UploadDropZone.DropFilesMessage, {
719
+ uploadDropZoneRef: uploadDropZoneRef,
720
+ folderName: title
721
+ }),
722
+ /*#__PURE__*/ jsxRuntime.jsx(AssetsView, {
723
+ view: view,
724
+ folders: folders$1,
725
+ isLoadingFolders: isLoadingFolders,
726
+ assets: assets,
727
+ isLoadingAssets: isLoadingAssets,
728
+ isFetchingMore: isFetchingMore,
729
+ hasNextPage: hasNextPage,
730
+ fetchNextPage: fetchNextPage,
731
+ error: assetsError,
732
+ locations: itemLocations$1,
733
+ searchQuery: searchQuery,
734
+ assetsSort: listSort.assetsSort,
735
+ foldersPosition: listSort.foldersPosition,
736
+ hasActiveFilters: listFilters.filters.length > 0,
737
+ onClearFilters: listFilters.clearFilters,
738
+ onAssetItemClick: openDetails,
739
+ onAddAssets: handleFileSelect,
740
+ canAddAssets: canCreate,
741
+ onClearSearch: clearSearch
742
+ })
743
+ ]
557
744
  })
558
745
  ]
559
746
  })
560
- })
561
- }),
562
- /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.Content, {
563
- children: /*#__PURE__*/ jsxRuntime.jsxs(UploadDropZone.DropZoneWithOverlay, {
564
- children: [
565
- /*#__PURE__*/ jsxRuntime.jsx(UploadDropZone.DropFilesMessage, {
566
- uploadDropZoneRef: uploadDropZoneRef,
567
- folderName: title
568
- }),
569
- /*#__PURE__*/ jsxRuntime.jsx(AssetsView, {
570
- view: view,
571
- folders: folders$1,
572
- isLoadingFolders: isLoadingFolders,
573
- assets: assets,
574
- isLoadingAssets: isLoadingAssets,
575
- isFetchingMore: isFetchingMore,
576
- hasNextPage: hasNextPage,
577
- fetchNextPage: fetchNextPage,
578
- error: assetsError,
579
- searchQuery: searchQuery,
580
- assetsSort: listSort.assetsSort,
581
- foldersPosition: listSort.foldersPosition,
582
- hasActiveFilters: listFilters.filters.length > 0,
583
- onClearFilters: listFilters.clearFilters,
584
- onAssetItemClick: openDetails,
585
- onAddAssets: handleFileSelect,
586
- canAddAssets: canCreate,
587
- onClearSearch: clearSearch
588
- })
589
- ]
590
- })
747
+ ]
591
748
  })
592
- ]
749
+ })
593
750
  })
594
- })
595
- ]
751
+ ]
752
+ })
596
753
  })
597
754
  })
598
755
  }),
599
- /*#__PURE__*/ jsxRuntime.jsx(CreateFolderDialog.CreateFolderDialog, {
756
+ /*#__PURE__*/ jsxRuntime.jsx(FolderFormDialog.FolderFormDialog, {
600
757
  open: isCreateFolderDialogOpen,
601
- folderName: title,
758
+ mode: "create",
759
+ parentFolderName: title,
602
760
  parentFolderId: currentFolderId,
603
761
  onClose: ()=>setIsCreateFolderDialogOpen(false)
604
762
  }),