@strapi/upload 5.51.1 → 5.51.2

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 (262) hide show
  1. package/dist/admin/components/AssetCard/VideoPreview.js +1 -1
  2. package/dist/admin/components/AssetCard/VideoPreview.js.map +1 -1
  3. package/dist/admin/components/AssetCard/VideoPreview.mjs +1 -1
  4. package/dist/admin/components/AssetCard/VideoPreview.mjs.map +1 -1
  5. package/dist/admin/components/TableList/TableList.js +1 -1
  6. package/dist/admin/components/TableList/TableList.js.map +1 -1
  7. package/dist/admin/components/TableList/TableList.mjs +1 -1
  8. package/dist/admin/components/TableList/TableList.mjs.map +1 -1
  9. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js +21 -4
  10. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js.map +1 -1
  11. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs +21 -4
  12. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs.map +1 -1
  13. package/dist/admin/future/components/Drawer.js +3 -1
  14. package/dist/admin/future/components/Drawer.js.map +1 -1
  15. package/dist/admin/future/components/Drawer.mjs +3 -1
  16. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  17. package/dist/admin/future/components/UploadProgressDialog.js +200 -27
  18. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  19. package/dist/admin/future/components/UploadProgressDialog.mjs +203 -30
  20. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  21. package/dist/admin/future/hooks/useMediaLibraryPermissions.js +35 -0
  22. package/dist/admin/future/hooks/useMediaLibraryPermissions.js.map +1 -0
  23. package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs +33 -0
  24. package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs.map +1 -0
  25. package/dist/admin/future/pages/Assets/AssetsPage.js +203 -78
  26. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/AssetsPage.mjs +200 -75
  28. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +75 -32
  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 +75 -32
  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 +46 -19
  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 +47 -20
  36. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +5 -2
  38. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +5 -2
  40. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +100 -0
  42. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -0
  43. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +98 -0
  44. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -0
  45. package/dist/admin/future/pages/Assets/components/AssetsTable.js +10 -4
  46. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  47. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +10 -4
  48. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  49. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +140 -22
  50. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +142 -24
  52. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js +193 -0
  54. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js.map +1 -0
  55. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs +191 -0
  56. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs.map +1 -0
  57. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +18 -1
  58. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  59. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +18 -1
  60. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  61. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js +13 -3
  62. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js.map +1 -1
  63. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs +13 -3
  64. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs.map +1 -1
  65. package/dist/admin/future/pages/Assets/components/EmptyState.js +62 -3
  66. package/dist/admin/future/pages/Assets/components/EmptyState.js.map +1 -1
  67. package/dist/admin/future/pages/Assets/components/EmptyState.mjs +62 -4
  68. package/dist/admin/future/pages/Assets/components/EmptyState.mjs.map +1 -1
  69. package/dist/admin/future/pages/Assets/components/FilterBadges.js +408 -0
  70. package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -0
  71. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +406 -0
  72. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -0
  73. package/dist/admin/future/pages/Assets/components/FilterMenu.js +273 -0
  74. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -0
  75. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +267 -0
  76. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -0
  77. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +76 -19
  78. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  79. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +78 -21
  80. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  81. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +33 -0
  82. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +1 -0
  83. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +31 -0
  84. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +1 -0
  85. package/dist/admin/future/pages/Assets/components/SortMenu.js +21 -32
  86. package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
  87. package/dist/admin/future/pages/Assets/components/SortMenu.mjs +22 -33
  88. package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
  89. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js +40 -0
  90. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js.map +1 -0
  91. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs +38 -0
  92. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs.map +1 -0
  93. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +21 -5
  94. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
  95. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +21 -5
  96. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
  97. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +33 -12
  98. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
  99. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +33 -12
  100. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
  101. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +40 -4
  102. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  103. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +40 -4
  104. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  105. package/dist/admin/future/pages/Assets/hooks/useListFilters.js +168 -0
  106. package/dist/admin/future/pages/Assets/hooks/useListFilters.js.map +1 -0
  107. package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs +162 -0
  108. package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs.map +1 -0
  109. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js +201 -0
  110. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js.map +1 -0
  111. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs +198 -0
  112. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs.map +1 -0
  113. package/dist/admin/future/pages/Assets/utils/listQueryKey.js +2 -2
  114. package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -1
  115. package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +2 -2
  116. package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -1
  117. package/dist/admin/future/services/api.js +163 -28
  118. package/dist/admin/future/services/api.js.map +1 -1
  119. package/dist/admin/future/services/api.mjs +163 -30
  120. package/dist/admin/future/services/api.mjs.map +1 -1
  121. package/dist/admin/future/services/assets.js +29 -17
  122. package/dist/admin/future/services/assets.js.map +1 -1
  123. package/dist/admin/future/services/assets.mjs +29 -17
  124. package/dist/admin/future/services/assets.mjs.map +1 -1
  125. package/dist/admin/future/services/folders.js +29 -17
  126. package/dist/admin/future/services/folders.js.map +1 -1
  127. package/dist/admin/future/services/folders.mjs +29 -17
  128. package/dist/admin/future/services/folders.mjs.map +1 -1
  129. package/dist/admin/future/services/settings.js +10 -3
  130. package/dist/admin/future/services/settings.js.map +1 -1
  131. package/dist/admin/future/services/settings.mjs +10 -3
  132. package/dist/admin/future/services/settings.mjs.map +1 -1
  133. package/dist/admin/future/store/uploadProgress.js +109 -2
  134. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  135. package/dist/admin/future/store/uploadProgress.mjs +105 -3
  136. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  137. package/dist/admin/future/utils/searchQueryParam.js +11 -0
  138. package/dist/admin/future/utils/searchQueryParam.js.map +1 -0
  139. package/dist/admin/future/utils/searchQueryParam.mjs +9 -0
  140. package/dist/admin/future/utils/searchQueryParam.mjs.map +1 -0
  141. package/dist/admin/hooks/useSelectionState.js +2 -4
  142. package/dist/admin/hooks/useSelectionState.js.map +1 -1
  143. package/dist/admin/hooks/useSelectionState.mjs +2 -4
  144. package/dist/admin/hooks/useSelectionState.mjs.map +1 -1
  145. package/dist/admin/shared/constants.js +43 -0
  146. package/dist/admin/shared/constants.js.map +1 -0
  147. package/dist/admin/shared/constants.mjs +38 -0
  148. package/dist/admin/shared/constants.mjs.map +1 -0
  149. package/dist/admin/src/future/hooks/useMediaLibraryPermissions.d.ts +21 -0
  150. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetCropEditor.d.ts +7 -1
  151. package/dist/admin/src/future/pages/Assets/components/AssetsSearchInput.d.ts +5 -0
  152. package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +14 -1
  153. package/dist/admin/src/future/pages/Assets/components/DateRangeCalendar.d.ts +8 -0
  154. package/dist/admin/src/future/pages/Assets/components/DropZone/UploadDropZoneContext.d.ts +8 -1
  155. package/dist/admin/src/future/pages/Assets/components/EmptyState.d.ts +18 -1
  156. package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +6 -0
  157. package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +22 -0
  158. package/dist/admin/src/future/pages/Assets/components/FolderTree/FolderTree.d.ts +13 -4
  159. package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +16 -0
  160. package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +6 -0
  161. package/dist/admin/src/future/pages/Assets/hooks/useAssetSearch.d.ts +12 -0
  162. package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +8 -1
  163. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +13 -3
  164. package/dist/admin/src/future/pages/Assets/hooks/useListFilters.d.ts +69 -0
  165. package/dist/admin/src/future/pages/Assets/utils/buildAssetFilters.d.ts +46 -0
  166. package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +3 -4
  167. package/dist/admin/src/future/services/api.d.ts +53 -5
  168. package/dist/admin/src/future/services/assets.d.ts +9 -1
  169. package/dist/admin/src/future/services/folders.d.ts +3 -0
  170. package/dist/admin/src/future/services/settings.d.ts +2 -2
  171. package/dist/admin/src/future/store/uploadProgress.d.ts +83 -1
  172. package/dist/admin/src/future/utils/searchQueryParam.d.ts +7 -0
  173. package/dist/admin/src/hooks/useAiAvailability.d.ts +1 -1
  174. package/dist/admin/src/hooks/useEditAsset.d.ts +4 -4
  175. package/dist/admin/src/hooks/useEditFolder.d.ts +4 -4
  176. package/dist/admin/src/hooks/useSettings.d.ts +1 -0
  177. package/dist/admin/src/hooks/useTracking.d.ts +1 -1
  178. package/dist/admin/src/hooks/useUpload.d.ts +1 -1
  179. package/dist/admin/translations/en.json.js +53 -3
  180. package/dist/admin/translations/en.json.js.map +1 -1
  181. package/dist/admin/translations/en.json.mjs +53 -3
  182. package/dist/admin/translations/en.json.mjs.map +1 -1
  183. package/dist/admin/utils/urlYupSchema.js +33 -9
  184. package/dist/admin/utils/urlYupSchema.js.map +1 -1
  185. package/dist/admin/utils/urlYupSchema.mjs +33 -9
  186. package/dist/admin/utils/urlYupSchema.mjs.map +1 -1
  187. package/dist/server/config.js +13 -5
  188. package/dist/server/config.js.map +1 -1
  189. package/dist/server/config.mjs +13 -5
  190. package/dist/server/config.mjs.map +1 -1
  191. package/dist/server/constants.js.map +1 -1
  192. package/dist/server/constants.mjs.map +1 -1
  193. package/dist/server/controllers/admin-file.js +30 -0
  194. package/dist/server/controllers/admin-file.js.map +1 -1
  195. package/dist/server/controllers/admin-file.mjs +30 -0
  196. package/dist/server/controllers/admin-file.mjs.map +1 -1
  197. package/dist/server/controllers/admin-settings.js +20 -2
  198. package/dist/server/controllers/admin-settings.js.map +1 -1
  199. package/dist/server/controllers/admin-settings.mjs +21 -3
  200. package/dist/server/controllers/admin-settings.mjs.map +1 -1
  201. package/dist/server/controllers/content-api.js +1 -2
  202. package/dist/server/controllers/content-api.js.map +1 -1
  203. package/dist/server/controllers/content-api.mjs +2 -2
  204. package/dist/server/controllers/content-api.mjs.map +1 -1
  205. package/dist/server/controllers/validation/admin/ai-metadata.js +12 -0
  206. package/dist/server/controllers/validation/admin/ai-metadata.js.map +1 -0
  207. package/dist/server/controllers/validation/admin/ai-metadata.mjs +10 -0
  208. package/dist/server/controllers/validation/admin/ai-metadata.mjs.map +1 -0
  209. package/dist/server/controllers/validation/admin/settings.js +4 -1
  210. package/dist/server/controllers/validation/admin/settings.js.map +1 -1
  211. package/dist/server/controllers/validation/admin/settings.mjs +1 -1
  212. package/dist/server/controllers/validation/admin/settings.mjs.map +1 -1
  213. package/dist/server/routes/admin.js +18 -0
  214. package/dist/server/routes/admin.js.map +1 -1
  215. package/dist/server/routes/admin.mjs +18 -0
  216. package/dist/server/routes/admin.mjs.map +1 -1
  217. package/dist/server/services/ai-metadata.js +108 -11
  218. package/dist/server/services/ai-metadata.js.map +1 -1
  219. package/dist/server/services/ai-metadata.mjs +108 -11
  220. package/dist/server/services/ai-metadata.mjs.map +1 -1
  221. package/dist/server/services/file.js +86 -13
  222. package/dist/server/services/file.js.map +1 -1
  223. package/dist/server/services/file.mjs +87 -14
  224. package/dist/server/services/file.mjs.map +1 -1
  225. package/dist/server/services/image-manipulation.js +4 -4
  226. package/dist/server/services/image-manipulation.js.map +1 -1
  227. package/dist/server/services/image-manipulation.mjs +4 -4
  228. package/dist/server/services/image-manipulation.mjs.map +1 -1
  229. package/dist/server/shared/constants.js +43 -0
  230. package/dist/server/shared/constants.js.map +1 -0
  231. package/dist/server/shared/constants.mjs +38 -0
  232. package/dist/server/shared/constants.mjs.map +1 -0
  233. package/dist/server/src/config.d.ts +2 -0
  234. package/dist/server/src/config.d.ts.map +1 -1
  235. package/dist/server/src/constants.d.ts +2 -1
  236. package/dist/server/src/constants.d.ts.map +1 -1
  237. package/dist/server/src/controllers/admin-file.d.ts +10 -0
  238. package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
  239. package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
  240. package/dist/server/src/controllers/index.d.ts +1 -0
  241. package/dist/server/src/controllers/index.d.ts.map +1 -1
  242. package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts +2 -0
  243. package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts.map +1 -0
  244. package/dist/server/src/controllers/validation/admin/settings.d.ts +1 -1
  245. package/dist/server/src/controllers/validation/admin/settings.d.ts.map +1 -1
  246. package/dist/server/src/index.d.ts +7 -1
  247. package/dist/server/src/index.d.ts.map +1 -1
  248. package/dist/server/src/routes/admin.d.ts.map +1 -1
  249. package/dist/server/src/services/ai-metadata.d.ts +19 -0
  250. package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
  251. package/dist/server/src/services/file.d.ts +12 -2
  252. package/dist/server/src/services/file.d.ts.map +1 -1
  253. package/dist/server/src/services/index.d.ts +4 -1
  254. package/dist/server/src/services/index.d.ts.map +1 -1
  255. package/dist/server/src/types.d.ts +3 -0
  256. package/dist/server/src/types.d.ts.map +1 -1
  257. package/dist/shared/constants.d.ts +35 -0
  258. package/dist/shared/constants.d.ts.map +1 -0
  259. package/dist/shared/contracts/files.d.ts +25 -0
  260. package/dist/shared/contracts/files.d.ts.map +1 -1
  261. package/dist/shared/contracts/settings.d.ts +7 -0
  262. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"SortMenu.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/SortMenu.tsx"],"sourcesContent":["import { Menu, VisuallyHidden } from '@strapi/design-system';\nimport { Check, ChevronDown } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { getTranslationKey } from '../../../utils/translations';\nimport {\n type FoldersPosition,\n type ListSort,\n type SortByKey,\n type SortDirectionKey,\n} from '../hooks/useListSort';\n\nimport type { MessageDescriptor } from 'react-intl';\n\nconst SORT_BY_LABELS: Record<SortByKey, MessageDescriptor> = {\n oldestUploads: {\n id: getTranslationKey('list.sort.oldest-uploads'),\n defaultMessage: 'Oldest uploads',\n },\n mostRecentUpdates: {\n id: getTranslationKey('list.sort.most-recent-updates'),\n defaultMessage: 'Most recent updates',\n },\n};\n\nconst SORT_DIRECTION_LABELS: Record<SortDirectionKey, MessageDescriptor> = {\n nameAsc: { id: getTranslationKey('list.sort.name-asc'), defaultMessage: 'A to Z' },\n nameDesc: { id: getTranslationKey('list.sort.name-desc'), defaultMessage: 'Z to A' },\n sizeAsc: {\n id: getTranslationKey('list.sort.size-asc'),\n defaultMessage: 'File size ascending',\n },\n sizeDesc: {\n id: getTranslationKey('list.sort.size-desc'),\n defaultMessage: 'File size descending',\n },\n};\n\nconst FOLDERS_LABELS: Record<FoldersPosition, MessageDescriptor> = {\n top: { id: getTranslationKey('list.sort.folders-on-top'), defaultMessage: 'On top' },\n mixed: {\n id: getTranslationKey('list.sort.folders-mixed'),\n defaultMessage: 'Mixed with files',\n },\n};\n\n// Stretch to the toolbar row height so the trigger matches the view toggle\n// (the parent Flex uses alignItems=\"stretch\").\nconst SortTrigger = styled(Menu.Trigger)`\n height: auto;\n`;\n\n// Full-width section band, like the design mock (sits inside the Menu.Content\n// padding, so the band stops short of the panel edges).\nconst GroupLabel = styled(Menu.Label)`\n width: 100%;\n display: block;\n background: ${({ theme }) =>\n theme.colorScheme === 'dark' ? theme.colors.neutral150 : theme.colors.neutral100};\n padding-inline: ${({ theme }) => theme.spaces[3]};\n border-radius: ${({ theme }) => theme.borderRadius};\n`;\n\ninterface SortMenuProps {\n sort: ListSort;\n /**\n * The grid view forces folders on top (mixing folder cards with asset cards\n * is not supported there), so it hides the Folders group entirely.\n */\n showFoldersGroup?: boolean;\n}\n\n/**\n * Toolbar \"Sort\" dropdown: single-select sections (Sort / Folders). Picking an\n * option keeps the menu open (`onSelect` preventDefault) so several facets can\n * be tuned in one visit; clicking a checked facet clears it (the hook\n * guarantees at least one sort rule stays active).\n */\nexport const SortMenu = ({ sort, showFoldersGroup = true }: SortMenuProps) => {\n const { formatMessage } = useIntl();\n\n const triggerLabel = formatMessage(\n { id: getTranslationKey('list.sort.trigger'), defaultMessage: 'Sort: {active}' },\n {\n active: sort.sortBy\n ? formatMessage(SORT_BY_LABELS[sort.sortBy])\n : formatMessage(SORT_DIRECTION_LABELS[sort.direction!]),\n }\n );\n\n const checkmark = <Check aria-hidden width=\"1.6rem\" height=\"1.6rem\" fill=\"primary600\" />;\n\n // The checkmark icon is aria-hidden, so the active option carries a\n // visually-hidden suffix for screen readers instead (menuitem does not\n // allow aria-checked).\n const activeMark = (\n <VisuallyHidden>\n {' '}\n {formatMessage({ id: getTranslationKey('list.sort.active'), defaultMessage: '(active)' })}\n </VisuallyHidden>\n );\n\n return (\n <Menu.Root>\n <SortTrigger variant=\"tertiary\" endIcon={<ChevronDown aria-hidden />}>\n {triggerLabel}\n </SortTrigger>\n {/* The DS default maxHeight (15rem) folds everything after the first\n group behind an invisible scroll — all groups must be visible at\n once. 70vh keeps a scroll on very short viewports. */}\n <Menu.Content popoverPlacement=\"bottom-end\" zIndex={2} maxHeight=\"70vh\" width=\"25rem\">\n {/* One single-select \"Sort\" section: the two rule families were always\n mutually exclusive (one checkmark total), so they share a group. */}\n <GroupLabel>\n {formatMessage({ id: getTranslationKey('list.sort.section'), defaultMessage: 'Sort' })}\n </GroupLabel>\n {(Object.keys(SORT_BY_LABELS) as SortByKey[]).map((key) => (\n <Menu.Item\n key={key}\n onSelect={(e: Event) => {\n e.preventDefault();\n sort.setSortBy(sort.sortBy === key ? null : key);\n }}\n endIcon={sort.sortBy === key ? checkmark : null}\n >\n {formatMessage(SORT_BY_LABELS[key])}\n {sort.sortBy === key && activeMark}\n </Menu.Item>\n ))}\n {(Object.keys(SORT_DIRECTION_LABELS) as SortDirectionKey[]).map((key) => (\n <Menu.Item\n key={key}\n onSelect={(e: Event) => {\n e.preventDefault();\n sort.setDirection(sort.direction === key ? null : key);\n }}\n endIcon={sort.direction === key ? checkmark : null}\n >\n {formatMessage(SORT_DIRECTION_LABELS[key])}\n {sort.direction === key && activeMark}\n </Menu.Item>\n ))}\n\n {showFoldersGroup && (\n <>\n <Menu.Separator />\n <GroupLabel>\n {formatMessage({\n id: getTranslationKey('list.sort.folders'),\n defaultMessage: 'Folders',\n })}\n </GroupLabel>\n {(Object.keys(FOLDERS_LABELS) as FoldersPosition[]).map((position) => (\n <Menu.Item\n key={position}\n onSelect={(e: Event) => {\n e.preventDefault();\n sort.setFoldersPosition(position);\n }}\n endIcon={sort.foldersPosition === position ? checkmark : null}\n >\n {formatMessage(FOLDERS_LABELS[position])}\n {sort.foldersPosition === position && activeMark}\n </Menu.Item>\n ))}\n </>\n )}\n </Menu.Content>\n </Menu.Root>\n );\n};\n"],"names":["SORT_BY_LABELS","oldestUploads","id","getTranslationKey","defaultMessage","mostRecentUpdates","SORT_DIRECTION_LABELS","nameAsc","nameDesc","sizeAsc","sizeDesc","FOLDERS_LABELS","top","mixed","SortTrigger","styled","Menu","Trigger","GroupLabel","Label","theme","colorScheme","colors","neutral150","neutral100","spaces","borderRadius","SortMenu","sort","showFoldersGroup","formatMessage","useIntl","triggerLabel","active","sortBy","direction","checkmark","_jsx","Check","aria-hidden","width","height","fill","activeMark","_jsxs","VisuallyHidden","Root","variant","endIcon","ChevronDown","Content","popoverPlacement","zIndex","maxHeight","Object","keys","map","key","Item","onSelect","e","preventDefault","setSortBy","setDirection","_Fragment","Separator","position","setFoldersPosition","foldersPosition"],"mappings":"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"}
1
+ {"version":3,"file":"SortMenu.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/SortMenu.tsx"],"sourcesContent":["import { Menu } from '@strapi/design-system';\nimport { Check, ChevronDown } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { getTranslationKey } from '../../../utils/translations';\nimport {\n type FoldersPosition,\n type ListSort,\n type SortByKey,\n type SortDirectionKey,\n} from '../hooks/useListSort';\n\nimport type { MessageDescriptor } from 'react-intl';\n\nconst SORT_BY_LABELS: Record<SortByKey, MessageDescriptor> = {\n oldestUploads: {\n id: getTranslationKey('list.sort.oldest-uploads'),\n defaultMessage: 'Oldest uploads',\n },\n mostRecentUpdates: {\n id: getTranslationKey('list.sort.most-recent-updates'),\n defaultMessage: 'Most recent updates',\n },\n};\n\nconst SORT_DIRECTION_LABELS: Record<SortDirectionKey, MessageDescriptor> = {\n nameAsc: { id: getTranslationKey('list.sort.name-asc'), defaultMessage: 'A to Z' },\n nameDesc: { id: getTranslationKey('list.sort.name-desc'), defaultMessage: 'Z to A' },\n sizeAsc: {\n id: getTranslationKey('list.sort.size-asc'),\n defaultMessage: 'File size ascending',\n },\n sizeDesc: {\n id: getTranslationKey('list.sort.size-desc'),\n defaultMessage: 'File size descending',\n },\n};\n\nconst FOLDERS_LABELS: Record<FoldersPosition, MessageDescriptor> = {\n top: { id: getTranslationKey('list.sort.folders-on-top'), defaultMessage: 'On top' },\n mixed: {\n id: getTranslationKey('list.sort.folders-mixed'),\n defaultMessage: 'Mixed with files',\n },\n};\n\n// Stretch to the toolbar row height so the trigger matches the view toggle\n// (the parent Flex uses alignItems=\"stretch\").\nconst SortTrigger = styled(Menu.Trigger)``;\n\n// Full-width section band, like the design mock (sits inside the Menu.Content\n// padding, so the band stops short of the panel edges).\nconst GroupLabel = styled(Menu.Label)`\n width: 100%;\n display: block;\n background: ${({ theme }) =>\n theme.colorScheme === 'dark' ? theme.colors.neutral150 : theme.colors.neutral100};\n padding-inline: ${({ theme }) => theme.spaces[3]};\n border-radius: ${({ theme }) => theme.borderRadius};\n`;\n\ninterface SortMenuProps {\n sort: ListSort;\n /**\n * The grid view forces folders on top (mixing folder cards with asset cards\n * is not supported there), so it hides the Folders group entirely.\n */\n showFoldersGroup?: boolean;\n}\n\n/**\n * Toolbar \"Sort\" dropdown: single-select sections (Sort / Folders). Picking an\n * option keeps the menu open (`onSelect` preventDefault) so several facets can\n * be tuned in one visit; clicking a checked facet clears it (the hook\n * guarantees at least one sort rule stays active).\n *\n * Each section is single-select, so its items are radios: the checkmark icon is\n * decorative, and `aria-checked` is what assistive tech reads. The DS `Menu`\n * exposes no `RadioItem`, but `Menu.Item` forwards extra props onto the element\n * it renders through Radix's `asChild`, where they take precedence over Radix's\n * own `role=\"menuitem\"`.\n */\nexport const SortMenu = ({ sort, showFoldersGroup = true }: SortMenuProps) => {\n const { formatMessage } = useIntl();\n\n const triggerLabel = formatMessage(\n { id: getTranslationKey('list.sort.trigger'), defaultMessage: 'Sort: {active}' },\n {\n active: sort.sortBy\n ? formatMessage(SORT_BY_LABELS[sort.sortBy])\n : formatMessage(SORT_DIRECTION_LABELS[sort.direction!]),\n }\n );\n\n const checkmark = <Check aria-hidden width=\"1.6rem\" height=\"1.6rem\" fill=\"primary600\" />;\n\n return (\n <Menu.Root>\n <SortTrigger variant=\"ghost\" endIcon={<ChevronDown aria-hidden />}>\n {triggerLabel}\n </SortTrigger>\n {/* The DS default maxHeight (15rem) folds everything after the first\n group behind an invisible scroll — all groups must be visible at\n once. 70vh keeps a scroll on very short viewports. */}\n <Menu.Content popoverPlacement=\"bottom-end\" zIndex={2} maxHeight=\"70vh\" minWidth=\"25rem\">\n {/* One single-select \"Sort\" section: the two rule families were always\n mutually exclusive (one checkmark total), so they share a group. */}\n <GroupLabel>\n {formatMessage({ id: getTranslationKey('list.sort.section'), defaultMessage: 'Sort' })}\n </GroupLabel>\n {(Object.keys(SORT_BY_LABELS) as SortByKey[]).map((key) => (\n <Menu.Item\n key={key}\n role=\"menuitemradio\"\n aria-checked={sort.sortBy === key}\n onSelect={(e: Event) => {\n e.preventDefault();\n sort.setSortBy(sort.sortBy === key ? null : key);\n }}\n endIcon={sort.sortBy === key ? checkmark : null}\n >\n {formatMessage(SORT_BY_LABELS[key])}\n </Menu.Item>\n ))}\n {(Object.keys(SORT_DIRECTION_LABELS) as SortDirectionKey[]).map((key) => (\n <Menu.Item\n key={key}\n role=\"menuitemradio\"\n aria-checked={sort.direction === key}\n onSelect={(e: Event) => {\n e.preventDefault();\n sort.setDirection(sort.direction === key ? null : key);\n }}\n endIcon={sort.direction === key ? checkmark : null}\n >\n {formatMessage(SORT_DIRECTION_LABELS[key])}\n </Menu.Item>\n ))}\n\n {showFoldersGroup && (\n <>\n <Menu.Separator />\n <GroupLabel>\n {formatMessage({\n id: getTranslationKey('list.sort.folders'),\n defaultMessage: 'Folders',\n })}\n </GroupLabel>\n {(Object.keys(FOLDERS_LABELS) as FoldersPosition[]).map((position) => (\n <Menu.Item\n key={position}\n role=\"menuitemradio\"\n aria-checked={sort.foldersPosition === position}\n onSelect={(e: Event) => {\n e.preventDefault();\n sort.setFoldersPosition(position);\n }}\n endIcon={sort.foldersPosition === position ? checkmark : null}\n >\n {formatMessage(FOLDERS_LABELS[position])}\n </Menu.Item>\n ))}\n </>\n )}\n </Menu.Content>\n </Menu.Root>\n );\n};\n"],"names":["SORT_BY_LABELS","oldestUploads","id","getTranslationKey","defaultMessage","mostRecentUpdates","SORT_DIRECTION_LABELS","nameAsc","nameDesc","sizeAsc","sizeDesc","FOLDERS_LABELS","top","mixed","SortTrigger","styled","Menu","Trigger","GroupLabel","Label","theme","colorScheme","colors","neutral150","neutral100","spaces","borderRadius","SortMenu","sort","showFoldersGroup","formatMessage","useIntl","triggerLabel","active","sortBy","direction","checkmark","_jsx","Check","aria-hidden","width","height","fill","_jsxs","Root","variant","endIcon","ChevronDown","Content","popoverPlacement","zIndex","maxHeight","minWidth","Object","keys","map","key","Item","role","aria-checked","onSelect","e","preventDefault","setSortBy","setDirection","_Fragment","Separator","position","foldersPosition","setFoldersPosition"],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var strapiAdmin = require('@strapi/admin/strapi-admin');
5
+ var searchQueryParam = require('../../../utils/searchQueryParam.js');
6
+
7
+ /**
8
+ * Reads and writes the global asset search term held in `?_q=`.
9
+ *
10
+ * Debouncing is the input component's job (see `AssetsSearchInput`) — this hook
11
+ * is a thin URL accessor so every consumer reads the same source of truth.
12
+ */ const useAssetSearch = ()=>{
13
+ const [{ query }, setQuery] = strapiAdmin.useQueryParams();
14
+ const searchQuery = query?._q ?? '';
15
+ const setSearchQuery = React.useCallback((value)=>{
16
+ if (value) {
17
+ setQuery({
18
+ _q: searchQueryParam.encodeSearchQuery(value)
19
+ }, 'push', true);
20
+ } else {
21
+ setQuery({
22
+ _q: ''
23
+ }, 'remove', true);
24
+ }
25
+ }, [
26
+ setQuery
27
+ ]);
28
+ const clearSearch = React.useCallback(()=>setSearchQuery(''), [
29
+ setSearchQuery
30
+ ]);
31
+ return {
32
+ searchQuery,
33
+ isSearching: searchQuery !== '',
34
+ setSearchQuery,
35
+ clearSearch
36
+ };
37
+ };
38
+
39
+ exports.useAssetSearch = useAssetSearch;
40
+ //# sourceMappingURL=useAssetSearch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAssetSearch.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useAssetSearch.ts"],"sourcesContent":["import { useCallback } from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\n\nimport { encodeSearchQuery } from '../../../utils/searchQueryParam';\n\n/**\n * Reads and writes the global asset search term held in `?_q=`.\n *\n * Debouncing is the input component's job (see `AssetsSearchInput`) — this hook\n * is a thin URL accessor so every consumer reads the same source of truth.\n */\nexport const useAssetSearch = () => {\n const [{ query }, setQuery] = useQueryParams<{ _q?: string }>();\n\n const searchQuery = query?._q ?? '';\n\n const setSearchQuery = useCallback(\n (value: string) => {\n if (value) {\n setQuery({ _q: encodeSearchQuery(value) }, 'push', true);\n } else {\n setQuery({ _q: '' }, 'remove', true);\n }\n },\n [setQuery]\n );\n\n const clearSearch = useCallback(() => setSearchQuery(''), [setSearchQuery]);\n\n return {\n searchQuery,\n isSearching: searchQuery !== '',\n setSearchQuery,\n clearSearch,\n };\n};\n"],"names":["useAssetSearch","query","setQuery","useQueryParams","searchQuery","_q","setSearchQuery","useCallback","value","encodeSearchQuery","clearSearch","isSearching"],"mappings":";;;;;;AAMA;;;;;UAMaA,cAAAA,GAAiB,IAAA;AAC5B,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,0BAAAA,EAAAA;IAE9B,MAAMC,WAAAA,GAAcH,OAAOI,EAAAA,IAAM,EAAA;IAEjC,MAAMC,cAAAA,GAAiBC,kBACrB,CAACC,KAAAA,GAAAA;AACC,QAAA,IAAIA,KAAAA,EAAO;YACTN,QAAAA,CAAS;AAAEG,gBAAAA,EAAAA,EAAII,kCAAAA,CAAkBD,KAAAA;AAAO,aAAA,EAAG,MAAA,EAAQ,IAAA,CAAA;QACrD,CAAA,MAAO;YACLN,QAAAA,CAAS;gBAAEG,EAAAA,EAAI;AAAG,aAAA,EAAG,QAAA,EAAU,IAAA,CAAA;AACjC,QAAA;IACF,CAAA,EACA;AAACH,QAAAA;AAAS,KAAA,CAAA;AAGZ,IAAA,MAAMQ,WAAAA,GAAcH,iBAAAA,CAAY,IAAMD,cAAAA,CAAe,EAAA,CAAA,EAAK;AAACA,QAAAA;AAAe,KAAA,CAAA;IAE1E,OAAO;AACLF,QAAAA,WAAAA;AACAO,QAAAA,WAAAA,EAAaP,WAAAA,KAAgB,EAAA;AAC7BE,QAAAA,cAAAA;AACAI,QAAAA;AACF,KAAA;AACF;;;;"}
@@ -0,0 +1,38 @@
1
+ import { useCallback } from 'react';
2
+ import { useQueryParams } from '@strapi/admin/strapi-admin';
3
+ import { encodeSearchQuery } from '../../../utils/searchQueryParam.mjs';
4
+
5
+ /**
6
+ * Reads and writes the global asset search term held in `?_q=`.
7
+ *
8
+ * Debouncing is the input component's job (see `AssetsSearchInput`) — this hook
9
+ * is a thin URL accessor so every consumer reads the same source of truth.
10
+ */ const useAssetSearch = ()=>{
11
+ const [{ query }, setQuery] = useQueryParams();
12
+ const searchQuery = query?._q ?? '';
13
+ const setSearchQuery = useCallback((value)=>{
14
+ if (value) {
15
+ setQuery({
16
+ _q: encodeSearchQuery(value)
17
+ }, 'push', true);
18
+ } else {
19
+ setQuery({
20
+ _q: ''
21
+ }, 'remove', true);
22
+ }
23
+ }, [
24
+ setQuery
25
+ ]);
26
+ const clearSearch = useCallback(()=>setSearchQuery(''), [
27
+ setSearchQuery
28
+ ]);
29
+ return {
30
+ searchQuery,
31
+ isSearching: searchQuery !== '',
32
+ setSearchQuery,
33
+ clearSearch
34
+ };
35
+ };
36
+
37
+ export { useAssetSearch };
38
+ //# sourceMappingURL=useAssetSearch.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAssetSearch.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useAssetSearch.ts"],"sourcesContent":["import { useCallback } from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\n\nimport { encodeSearchQuery } from '../../../utils/searchQueryParam';\n\n/**\n * Reads and writes the global asset search term held in `?_q=`.\n *\n * Debouncing is the input component's job (see `AssetsSearchInput`) — this hook\n * is a thin URL accessor so every consumer reads the same source of truth.\n */\nexport const useAssetSearch = () => {\n const [{ query }, setQuery] = useQueryParams<{ _q?: string }>();\n\n const searchQuery = query?._q ?? '';\n\n const setSearchQuery = useCallback(\n (value: string) => {\n if (value) {\n setQuery({ _q: encodeSearchQuery(value) }, 'push', true);\n } else {\n setQuery({ _q: '' }, 'remove', true);\n }\n },\n [setQuery]\n );\n\n const clearSearch = useCallback(() => setSearchQuery(''), [setSearchQuery]);\n\n return {\n searchQuery,\n isSearching: searchQuery !== '',\n setSearchQuery,\n clearSearch,\n };\n};\n"],"names":["useAssetSearch","query","setQuery","useQueryParams","searchQuery","_q","setSearchQuery","useCallback","value","encodeSearchQuery","clearSearch","isSearching"],"mappings":";;;;AAMA;;;;;UAMaA,cAAAA,GAAiB,IAAA;AAC5B,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,cAAAA,EAAAA;IAE9B,MAAMC,WAAAA,GAAcH,OAAOI,EAAAA,IAAM,EAAA;IAEjC,MAAMC,cAAAA,GAAiBC,YACrB,CAACC,KAAAA,GAAAA;AACC,QAAA,IAAIA,KAAAA,EAAO;YACTN,QAAAA,CAAS;AAAEG,gBAAAA,EAAAA,EAAII,iBAAAA,CAAkBD,KAAAA;AAAO,aAAA,EAAG,MAAA,EAAQ,IAAA,CAAA;QACrD,CAAA,MAAO;YACLN,QAAAA,CAAS;gBAAEG,EAAAA,EAAI;AAAG,aAAA,EAAG,QAAA,EAAU,IAAA,CAAA;AACjC,QAAA;IACF,CAAA,EACA;AAACH,QAAAA;AAAS,KAAA,CAAA;AAGZ,IAAA,MAAMQ,WAAAA,GAAcH,WAAAA,CAAY,IAAMD,cAAAA,CAAe,EAAA,CAAA,EAAK;AAACA,QAAAA;AAAe,KAAA,CAAA;IAE1E,OAAO;AACLF,QAAAA,WAAAA;AACAO,QAAAA,WAAAA,EAAaP,WAAAA,KAAgB,EAAA;AAC7BE,QAAAA,cAAAA;AACAI,QAAAA;AACF,KAAA;AACF;;;;"}
@@ -4,14 +4,30 @@ var React = require('react');
4
4
  var selection = require('../utils/selection.js');
5
5
 
6
6
  const AssetSelectionContext = React.createContext(null);
7
- const AssetSelectionProvider = ({ children })=>{
7
+ const AssetSelectionProvider = ({ children, disabled = false })=>{
8
8
  const [state, setState] = React.useState(selection.createEmptySelection);
9
- const isSelected = React.useCallback((key)=>state.selectedKeys.has(key), [
9
+ const isSelected = React.useCallback((key)=>!disabled && state.selectedKeys.has(key), [
10
+ disabled,
10
11
  state.selectedKeys
11
12
  ]);
12
- const toggle = React.useCallback((key)=>setState((prev)=>selection.toggleSelection(prev, key)), []);
13
- const selectRange = React.useCallback((orderedKeys, targetKey)=>setState((prev)=>selection.selectRange(prev, orderedKeys, targetKey)), []);
14
- const selectAll = React.useCallback((orderedKeys)=>setState(selection.selectAll(orderedKeys)), []);
13
+ const toggle = React.useCallback((key)=>{
14
+ if (disabled) return;
15
+ setState((prev)=>selection.toggleSelection(prev, key));
16
+ }, [
17
+ disabled
18
+ ]);
19
+ const selectRange = React.useCallback((orderedKeys, targetKey)=>{
20
+ if (disabled) return;
21
+ setState((prev)=>selection.selectRange(prev, orderedKeys, targetKey));
22
+ }, [
23
+ disabled
24
+ ]);
25
+ const selectAll = React.useCallback((orderedKeys)=>{
26
+ if (disabled) return;
27
+ setState(selection.selectAll(orderedKeys));
28
+ }, [
29
+ disabled
30
+ ]);
15
31
  const clear = React.useCallback(()=>setState(selection.clearSelection()), []);
16
32
  const selectedIds = React.useMemo(()=>selection.getIdsOfKind(state.selectedKeys, 'asset'), [
17
33
  state.selectedKeys
@@ -1 +1 @@
1
- {"version":3,"file":"useAssetSelection.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useAssetSelection.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n clearSelection,\n createEmptySelection,\n getIdsOfKind,\n selectAll as selectAllState,\n selectRange as selectRangeState,\n toggleSelection,\n type ItemKey,\n type SelectionState,\n} from '../utils/selection';\n\n/**\n * Ephemeral, view-state-only multi-select for the future Media Library. Selection\n * is page-scoped and reset on folder navigation or sort/filter changes (the\n * table/grid toggle keeps it — same list, different presentation).\n *\n * The actual computation lives in `../utils/selection.ts` (pure + unit-tested);\n * this hook is a thin React wrapper. Assets and folders share one mechanism —\n * items are tracked as namespaced keys (`asset:1` / `folder:1`) so toggle, range\n * and select-all behave the same for both. Range / select-all receive the\n * ordered key array from the caller (the view owns the rendered order), keeping\n * the context data-source-agnostic.\n */\nexport interface AssetSelection {\n /** Every selected item key (assets and folders), render-order agnostic. */\n selectedKeys: Set<ItemKey>;\n /** Selected asset ids, derived from {@link selectedKeys}. */\n selectedIds: Set<number>;\n /** Selected folder ids, derived from {@link selectedKeys}. */\n selectedFolderIds: Set<number>;\n anchorKey: ItemKey | null;\n isSelected: (key: ItemKey) => boolean;\n /** Additive toggle (Cmd/Ctrl+click, item checkbox). */\n toggle: (key: ItemKey) => void;\n /** Shift+click — selects the contiguous range from the anchor to the target. */\n selectRange: (orderedKeys: ItemKey[], targetKey: ItemKey) => void;\n /** Header checkbox — selects every rendered item (folders and assets). */\n selectAll: (orderedKeys: ItemKey[]) => void;\n /** Close button / folder navigation / list-identity changes. */\n clear: () => void;\n}\n\nconst AssetSelectionContext = createContext<AssetSelection | null>(null);\n\ninterface AssetSelectionProviderProps {\n children: ReactNode;\n}\n\nexport const AssetSelectionProvider = ({ children }: AssetSelectionProviderProps) => {\n const [state, setState] = useState<SelectionState>(createEmptySelection);\n\n const isSelected = useCallback(\n (key: ItemKey) => state.selectedKeys.has(key),\n [state.selectedKeys]\n );\n\n const toggle = useCallback((key: ItemKey) => setState((prev) => toggleSelection(prev, key)), []);\n\n const selectRange = useCallback(\n (orderedKeys: ItemKey[], targetKey: ItemKey) =>\n setState((prev) => selectRangeState(prev, orderedKeys, targetKey)),\n []\n );\n\n const selectAll = useCallback(\n (orderedKeys: ItemKey[]) => setState(selectAllState(orderedKeys)),\n []\n );\n\n const clear = useCallback(() => setState(clearSelection()), []);\n\n const selectedIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'asset'),\n [state.selectedKeys]\n );\n const selectedFolderIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'folder'),\n [state.selectedKeys]\n );\n\n const value = useMemo<AssetSelection>(\n () => ({\n selectedKeys: state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n anchorKey: state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n }),\n [\n state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n ]\n );\n\n return createElement(AssetSelectionContext.Provider, { value }, children);\n};\n\nexport const useAssetSelection = () => {\n const context = useContext(AssetSelectionContext);\n\n if (!context) {\n throw new Error('useAssetSelection must be used within an AssetSelectionProvider');\n }\n\n return context;\n};\n\nexport const useAssetSelectionOptional = () => useContext(AssetSelectionContext);\n"],"names":["AssetSelectionContext","createContext","AssetSelectionProvider","children","state","setState","useState","createEmptySelection","isSelected","useCallback","key","selectedKeys","has","toggle","prev","toggleSelection","selectRange","orderedKeys","targetKey","selectRangeState","selectAll","selectAllState","clear","clearSelection","selectedIds","useMemo","getIdsOfKind","selectedFolderIds","value","anchorKey","createElement","Provider","useAssetSelection","context","useContext","Error","useAssetSelectionOptional"],"mappings":";;;;;AAoDA,MAAMA,wBAAwBC,mBAAAA,CAAqC,IAAA,CAAA;AAM5D,MAAMC,sBAAAA,GAAyB,CAAC,EAAEC,QAAQ,EAA+B,GAAA;AAC9E,IAAA,MAAM,CAACC,KAAAA,EAAOC,QAAAA,CAAS,GAAGC,cAAAA,CAAyBC,8BAAAA,CAAAA;IAEnD,MAAMC,UAAAA,GAAaC,kBACjB,CAACC,GAAAA,GAAiBN,MAAMO,YAAY,CAACC,GAAG,CAACF,GAAAA,CAAAA,EACzC;AAACN,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;IAGtB,MAAME,MAAAA,GAASJ,iBAAAA,CAAY,CAACC,GAAAA,GAAiBL,QAAAA,CAAS,CAACS,IAAAA,GAASC,yBAAAA,CAAgBD,IAAAA,EAAMJ,GAAAA,CAAAA,CAAAA,EAAO,EAAE,CAAA;AAE/F,IAAA,MAAMM,WAAAA,GAAcP,iBAAAA,CAClB,CAACQ,WAAAA,EAAwBC,SAAAA,GACvBb,QAAAA,CAAS,CAACS,IAAAA,GAASK,qBAAAA,CAAiBL,IAAAA,EAAMG,WAAAA,EAAaC,SAAAA,CAAAA,CAAAA,EACzD,EAAE,CAAA;AAGJ,IAAA,MAAME,YAAYX,iBAAAA,CAChB,CAACQ,cAA2BZ,QAAAA,CAASgB,mBAAAA,CAAeJ,eACpD,EAAE,CAAA;AAGJ,IAAA,MAAMK,KAAAA,GAAQb,iBAAAA,CAAY,IAAMJ,QAAAA,CAASkB,6BAAmB,EAAE,CAAA;AAE9D,IAAA,MAAMC,cAAcC,aAAAA,CAClB,IAAMC,uBAAatB,KAAAA,CAAMO,YAAY,EAAE,OAAA,CAAA,EACvC;AAACP,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;AAEtB,IAAA,MAAMgB,oBAAoBF,aAAAA,CACxB,IAAMC,uBAAatB,KAAAA,CAAMO,YAAY,EAAE,QAAA,CAAA,EACvC;AAACP,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;IAGtB,MAAMiB,KAAAA,GAAQH,aAAAA,CACZ,KAAO;AACLd,YAAAA,YAAAA,EAAcP,MAAMO,YAAY;AAChCa,YAAAA,WAAAA;AACAG,YAAAA,iBAAAA;AACAE,YAAAA,SAAAA,EAAWzB,MAAMyB,SAAS;AAC1BrB,YAAAA,UAAAA;AACAK,YAAAA,MAAAA;AACAG,YAAAA,WAAAA;AACAI,YAAAA,SAAAA;AACAE,YAAAA;AACF,SAAA,CAAA,EACA;AACElB,QAAAA,KAAAA,CAAMO,YAAY;AAClBa,QAAAA,WAAAA;AACAG,QAAAA,iBAAAA;AACAvB,QAAAA,KAAAA,CAAMyB,SAAS;AACfrB,QAAAA,UAAAA;AACAK,QAAAA,MAAAA;AACAG,QAAAA,WAAAA;AACAI,QAAAA,SAAAA;AACAE,QAAAA;AACD,KAAA,CAAA;IAGH,OAAOQ,mBAAAA,CAAc9B,qBAAAA,CAAsB+B,QAAQ,EAAE;AAAEH,QAAAA;KAAM,EAAGzB,QAAAA,CAAAA;AAClE;MAEa6B,iBAAAA,GAAoB,IAAA;AAC/B,IAAA,MAAMC,UAAUC,gBAAAA,CAAWlC,qBAAAA,CAAAA;AAE3B,IAAA,IAAI,CAACiC,OAAAA,EAAS;AACZ,QAAA,MAAM,IAAIE,KAAAA,CAAM,iEAAA,CAAA;AAClB,IAAA;IAEA,OAAOF,OAAAA;AACT;AAEO,MAAMG,yBAAAA,GAA4B,IAAMF,gBAAAA,CAAWlC,qBAAAA;;;;;;"}
1
+ {"version":3,"file":"useAssetSelection.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useAssetSelection.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n clearSelection,\n createEmptySelection,\n getIdsOfKind,\n selectAll as selectAllState,\n selectRange as selectRangeState,\n toggleSelection,\n type ItemKey,\n type SelectionState,\n} from '../utils/selection';\n\n/**\n * Ephemeral, view-state-only multi-select for the future Media Library. Selection\n * is page-scoped and reset on folder navigation or sort/filter changes (the\n * table/grid toggle keeps it — same list, different presentation).\n *\n * The actual computation lives in `../utils/selection.ts` (pure + unit-tested);\n * this hook is a thin React wrapper. Assets and folders share one mechanism —\n * items are tracked as namespaced keys (`asset:1` / `folder:1`) so toggle, range\n * and select-all behave the same for both. Range / select-all receive the\n * ordered key array from the caller (the view owns the rendered order), keeping\n * the context data-source-agnostic.\n */\nexport interface AssetSelection {\n /** Every selected item key (assets and folders), render-order agnostic. */\n selectedKeys: Set<ItemKey>;\n /** Selected asset ids, derived from {@link selectedKeys}. */\n selectedIds: Set<number>;\n /** Selected folder ids, derived from {@link selectedKeys}. */\n selectedFolderIds: Set<number>;\n anchorKey: ItemKey | null;\n isSelected: (key: ItemKey) => boolean;\n /** Additive toggle (Cmd/Ctrl+click, item checkbox). */\n toggle: (key: ItemKey) => void;\n /** Shift+click — selects the contiguous range from the anchor to the target. */\n selectRange: (orderedKeys: ItemKey[], targetKey: ItemKey) => void;\n /** Header checkbox — selects every rendered item (folders and assets). */\n selectAll: (orderedKeys: ItemKey[]) => void;\n /** Close button / folder navigation / list-identity changes. */\n clear: () => void;\n}\n\nconst AssetSelectionContext = createContext<AssetSelection | null>(null);\n\ninterface AssetSelectionProviderProps {\n children: ReactNode;\n /**\n * When true, selection is inert: every mutator is a no-op and nothing ever\n * reads as selected. Every bulk action is gated on `assets.update`, so a user\n * without it has nothing to select for — the views also hide the checkboxes,\n * and this makes the remaining paths (click-to-select, Space key) do nothing.\n */\n disabled?: boolean;\n}\n\nexport const AssetSelectionProvider = ({\n children,\n disabled = false,\n}: AssetSelectionProviderProps) => {\n const [state, setState] = useState<SelectionState>(createEmptySelection);\n\n const isSelected = useCallback(\n (key: ItemKey) => !disabled && state.selectedKeys.has(key),\n [disabled, state.selectedKeys]\n );\n\n const toggle = useCallback(\n (key: ItemKey) => {\n if (disabled) return;\n setState((prev) => toggleSelection(prev, key));\n },\n [disabled]\n );\n\n const selectRange = useCallback(\n (orderedKeys: ItemKey[], targetKey: ItemKey) => {\n if (disabled) return;\n setState((prev) => selectRangeState(prev, orderedKeys, targetKey));\n },\n [disabled]\n );\n\n const selectAll = useCallback(\n (orderedKeys: ItemKey[]) => {\n if (disabled) return;\n setState(selectAllState(orderedKeys));\n },\n [disabled]\n );\n\n const clear = useCallback(() => setState(clearSelection()), []);\n\n const selectedIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'asset'),\n [state.selectedKeys]\n );\n const selectedFolderIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'folder'),\n [state.selectedKeys]\n );\n\n const value = useMemo<AssetSelection>(\n () => ({\n selectedKeys: state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n anchorKey: state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n }),\n [\n state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n ]\n );\n\n return createElement(AssetSelectionContext.Provider, { value }, children);\n};\n\nexport const useAssetSelection = () => {\n const context = useContext(AssetSelectionContext);\n\n if (!context) {\n throw new Error('useAssetSelection must be used within an AssetSelectionProvider');\n }\n\n return context;\n};\n\nexport const useAssetSelectionOptional = () => useContext(AssetSelectionContext);\n"],"names":["AssetSelectionContext","createContext","AssetSelectionProvider","children","disabled","state","setState","useState","createEmptySelection","isSelected","useCallback","key","selectedKeys","has","toggle","prev","toggleSelection","selectRange","orderedKeys","targetKey","selectRangeState","selectAll","selectAllState","clear","clearSelection","selectedIds","useMemo","getIdsOfKind","selectedFolderIds","value","anchorKey","createElement","Provider","useAssetSelection","context","useContext","Error","useAssetSelectionOptional"],"mappings":"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"}
@@ -2,14 +2,30 @@ import { useState, useCallback, useMemo, createElement, createContext, useContex
2
2
  import { createEmptySelection, toggleSelection, selectRange, selectAll, clearSelection, getIdsOfKind } from '../utils/selection.mjs';
3
3
 
4
4
  const AssetSelectionContext = createContext(null);
5
- const AssetSelectionProvider = ({ children })=>{
5
+ const AssetSelectionProvider = ({ children, disabled = false })=>{
6
6
  const [state, setState] = useState(createEmptySelection);
7
- const isSelected = useCallback((key)=>state.selectedKeys.has(key), [
7
+ const isSelected = useCallback((key)=>!disabled && state.selectedKeys.has(key), [
8
+ disabled,
8
9
  state.selectedKeys
9
10
  ]);
10
- const toggle = useCallback((key)=>setState((prev)=>toggleSelection(prev, key)), []);
11
- const selectRange$1 = useCallback((orderedKeys, targetKey)=>setState((prev)=>selectRange(prev, orderedKeys, targetKey)), []);
12
- const selectAll$1 = useCallback((orderedKeys)=>setState(selectAll(orderedKeys)), []);
11
+ const toggle = useCallback((key)=>{
12
+ if (disabled) return;
13
+ setState((prev)=>toggleSelection(prev, key));
14
+ }, [
15
+ disabled
16
+ ]);
17
+ const selectRange$1 = useCallback((orderedKeys, targetKey)=>{
18
+ if (disabled) return;
19
+ setState((prev)=>selectRange(prev, orderedKeys, targetKey));
20
+ }, [
21
+ disabled
22
+ ]);
23
+ const selectAll$1 = useCallback((orderedKeys)=>{
24
+ if (disabled) return;
25
+ setState(selectAll(orderedKeys));
26
+ }, [
27
+ disabled
28
+ ]);
13
29
  const clear = useCallback(()=>setState(clearSelection()), []);
14
30
  const selectedIds = useMemo(()=>getIdsOfKind(state.selectedKeys, 'asset'), [
15
31
  state.selectedKeys
@@ -1 +1 @@
1
- {"version":3,"file":"useAssetSelection.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useAssetSelection.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n clearSelection,\n createEmptySelection,\n getIdsOfKind,\n selectAll as selectAllState,\n selectRange as selectRangeState,\n toggleSelection,\n type ItemKey,\n type SelectionState,\n} from '../utils/selection';\n\n/**\n * Ephemeral, view-state-only multi-select for the future Media Library. Selection\n * is page-scoped and reset on folder navigation or sort/filter changes (the\n * table/grid toggle keeps it — same list, different presentation).\n *\n * The actual computation lives in `../utils/selection.ts` (pure + unit-tested);\n * this hook is a thin React wrapper. Assets and folders share one mechanism —\n * items are tracked as namespaced keys (`asset:1` / `folder:1`) so toggle, range\n * and select-all behave the same for both. Range / select-all receive the\n * ordered key array from the caller (the view owns the rendered order), keeping\n * the context data-source-agnostic.\n */\nexport interface AssetSelection {\n /** Every selected item key (assets and folders), render-order agnostic. */\n selectedKeys: Set<ItemKey>;\n /** Selected asset ids, derived from {@link selectedKeys}. */\n selectedIds: Set<number>;\n /** Selected folder ids, derived from {@link selectedKeys}. */\n selectedFolderIds: Set<number>;\n anchorKey: ItemKey | null;\n isSelected: (key: ItemKey) => boolean;\n /** Additive toggle (Cmd/Ctrl+click, item checkbox). */\n toggle: (key: ItemKey) => void;\n /** Shift+click — selects the contiguous range from the anchor to the target. */\n selectRange: (orderedKeys: ItemKey[], targetKey: ItemKey) => void;\n /** Header checkbox — selects every rendered item (folders and assets). */\n selectAll: (orderedKeys: ItemKey[]) => void;\n /** Close button / folder navigation / list-identity changes. */\n clear: () => void;\n}\n\nconst AssetSelectionContext = createContext<AssetSelection | null>(null);\n\ninterface AssetSelectionProviderProps {\n children: ReactNode;\n}\n\nexport const AssetSelectionProvider = ({ children }: AssetSelectionProviderProps) => {\n const [state, setState] = useState<SelectionState>(createEmptySelection);\n\n const isSelected = useCallback(\n (key: ItemKey) => state.selectedKeys.has(key),\n [state.selectedKeys]\n );\n\n const toggle = useCallback((key: ItemKey) => setState((prev) => toggleSelection(prev, key)), []);\n\n const selectRange = useCallback(\n (orderedKeys: ItemKey[], targetKey: ItemKey) =>\n setState((prev) => selectRangeState(prev, orderedKeys, targetKey)),\n []\n );\n\n const selectAll = useCallback(\n (orderedKeys: ItemKey[]) => setState(selectAllState(orderedKeys)),\n []\n );\n\n const clear = useCallback(() => setState(clearSelection()), []);\n\n const selectedIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'asset'),\n [state.selectedKeys]\n );\n const selectedFolderIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'folder'),\n [state.selectedKeys]\n );\n\n const value = useMemo<AssetSelection>(\n () => ({\n selectedKeys: state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n anchorKey: state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n }),\n [\n state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n ]\n );\n\n return createElement(AssetSelectionContext.Provider, { value }, children);\n};\n\nexport const useAssetSelection = () => {\n const context = useContext(AssetSelectionContext);\n\n if (!context) {\n throw new Error('useAssetSelection must be used within an AssetSelectionProvider');\n }\n\n return context;\n};\n\nexport const useAssetSelectionOptional = () => useContext(AssetSelectionContext);\n"],"names":["AssetSelectionContext","createContext","AssetSelectionProvider","children","state","setState","useState","createEmptySelection","isSelected","useCallback","key","selectedKeys","has","toggle","prev","toggleSelection","selectRange","orderedKeys","targetKey","selectRangeState","selectAll","selectAllState","clear","clearSelection","selectedIds","useMemo","getIdsOfKind","selectedFolderIds","value","anchorKey","createElement","Provider","useAssetSelection","context","useContext","Error","useAssetSelectionOptional"],"mappings":";;;AAoDA,MAAMA,wBAAwBC,aAAAA,CAAqC,IAAA,CAAA;AAM5D,MAAMC,sBAAAA,GAAyB,CAAC,EAAEC,QAAQ,EAA+B,GAAA;AAC9E,IAAA,MAAM,CAACC,KAAAA,EAAOC,QAAAA,CAAS,GAAGC,QAAAA,CAAyBC,oBAAAA,CAAAA;IAEnD,MAAMC,UAAAA,GAAaC,YACjB,CAACC,GAAAA,GAAiBN,MAAMO,YAAY,CAACC,GAAG,CAACF,GAAAA,CAAAA,EACzC;AAACN,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;IAGtB,MAAME,MAAAA,GAASJ,WAAAA,CAAY,CAACC,GAAAA,GAAiBL,QAAAA,CAAS,CAACS,IAAAA,GAASC,eAAAA,CAAgBD,IAAAA,EAAMJ,GAAAA,CAAAA,CAAAA,EAAO,EAAE,CAAA;AAE/F,IAAA,MAAMM,aAAAA,GAAcP,WAAAA,CAClB,CAACQ,WAAAA,EAAwBC,SAAAA,GACvBb,QAAAA,CAAS,CAACS,IAAAA,GAASK,WAAAA,CAAiBL,IAAAA,EAAMG,WAAAA,EAAaC,SAAAA,CAAAA,CAAAA,EACzD,EAAE,CAAA;AAGJ,IAAA,MAAME,cAAYX,WAAAA,CAChB,CAACQ,cAA2BZ,QAAAA,CAASgB,SAAAA,CAAeJ,eACpD,EAAE,CAAA;AAGJ,IAAA,MAAMK,KAAAA,GAAQb,WAAAA,CAAY,IAAMJ,QAAAA,CAASkB,mBAAmB,EAAE,CAAA;AAE9D,IAAA,MAAMC,cAAcC,OAAAA,CAClB,IAAMC,aAAatB,KAAAA,CAAMO,YAAY,EAAE,OAAA,CAAA,EACvC;AAACP,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;AAEtB,IAAA,MAAMgB,oBAAoBF,OAAAA,CACxB,IAAMC,aAAatB,KAAAA,CAAMO,YAAY,EAAE,QAAA,CAAA,EACvC;AAACP,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;IAGtB,MAAMiB,KAAAA,GAAQH,OAAAA,CACZ,KAAO;AACLd,YAAAA,YAAAA,EAAcP,MAAMO,YAAY;AAChCa,YAAAA,WAAAA;AACAG,YAAAA,iBAAAA;AACAE,YAAAA,SAAAA,EAAWzB,MAAMyB,SAAS;AAC1BrB,YAAAA,UAAAA;AACAK,YAAAA,MAAAA;AACAG,yBAAAA,aAAAA;AACAI,uBAAAA,WAAAA;AACAE,YAAAA;AACF,SAAA,CAAA,EACA;AACElB,QAAAA,KAAAA,CAAMO,YAAY;AAClBa,QAAAA,WAAAA;AACAG,QAAAA,iBAAAA;AACAvB,QAAAA,KAAAA,CAAMyB,SAAS;AACfrB,QAAAA,UAAAA;AACAK,QAAAA,MAAAA;AACAG,QAAAA,aAAAA;AACAI,QAAAA,WAAAA;AACAE,QAAAA;AACD,KAAA,CAAA;IAGH,OAAOQ,aAAAA,CAAc9B,qBAAAA,CAAsB+B,QAAQ,EAAE;AAAEH,QAAAA;KAAM,EAAGzB,QAAAA,CAAAA;AAClE;MAEa6B,iBAAAA,GAAoB,IAAA;AAC/B,IAAA,MAAMC,UAAUC,UAAAA,CAAWlC,qBAAAA,CAAAA;AAE3B,IAAA,IAAI,CAACiC,OAAAA,EAAS;AACZ,QAAA,MAAM,IAAIE,KAAAA,CAAM,iEAAA,CAAA;AAClB,IAAA;IAEA,OAAOF,OAAAA;AACT;AAEO,MAAMG,yBAAAA,GAA4B,IAAMF,UAAAA,CAAWlC,qBAAAA;;;;"}
1
+ {"version":3,"file":"useAssetSelection.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useAssetSelection.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n clearSelection,\n createEmptySelection,\n getIdsOfKind,\n selectAll as selectAllState,\n selectRange as selectRangeState,\n toggleSelection,\n type ItemKey,\n type SelectionState,\n} from '../utils/selection';\n\n/**\n * Ephemeral, view-state-only multi-select for the future Media Library. Selection\n * is page-scoped and reset on folder navigation or sort/filter changes (the\n * table/grid toggle keeps it — same list, different presentation).\n *\n * The actual computation lives in `../utils/selection.ts` (pure + unit-tested);\n * this hook is a thin React wrapper. Assets and folders share one mechanism —\n * items are tracked as namespaced keys (`asset:1` / `folder:1`) so toggle, range\n * and select-all behave the same for both. Range / select-all receive the\n * ordered key array from the caller (the view owns the rendered order), keeping\n * the context data-source-agnostic.\n */\nexport interface AssetSelection {\n /** Every selected item key (assets and folders), render-order agnostic. */\n selectedKeys: Set<ItemKey>;\n /** Selected asset ids, derived from {@link selectedKeys}. */\n selectedIds: Set<number>;\n /** Selected folder ids, derived from {@link selectedKeys}. */\n selectedFolderIds: Set<number>;\n anchorKey: ItemKey | null;\n isSelected: (key: ItemKey) => boolean;\n /** Additive toggle (Cmd/Ctrl+click, item checkbox). */\n toggle: (key: ItemKey) => void;\n /** Shift+click — selects the contiguous range from the anchor to the target. */\n selectRange: (orderedKeys: ItemKey[], targetKey: ItemKey) => void;\n /** Header checkbox — selects every rendered item (folders and assets). */\n selectAll: (orderedKeys: ItemKey[]) => void;\n /** Close button / folder navigation / list-identity changes. */\n clear: () => void;\n}\n\nconst AssetSelectionContext = createContext<AssetSelection | null>(null);\n\ninterface AssetSelectionProviderProps {\n children: ReactNode;\n /**\n * When true, selection is inert: every mutator is a no-op and nothing ever\n * reads as selected. Every bulk action is gated on `assets.update`, so a user\n * without it has nothing to select for — the views also hide the checkboxes,\n * and this makes the remaining paths (click-to-select, Space key) do nothing.\n */\n disabled?: boolean;\n}\n\nexport const AssetSelectionProvider = ({\n children,\n disabled = false,\n}: AssetSelectionProviderProps) => {\n const [state, setState] = useState<SelectionState>(createEmptySelection);\n\n const isSelected = useCallback(\n (key: ItemKey) => !disabled && state.selectedKeys.has(key),\n [disabled, state.selectedKeys]\n );\n\n const toggle = useCallback(\n (key: ItemKey) => {\n if (disabled) return;\n setState((prev) => toggleSelection(prev, key));\n },\n [disabled]\n );\n\n const selectRange = useCallback(\n (orderedKeys: ItemKey[], targetKey: ItemKey) => {\n if (disabled) return;\n setState((prev) => selectRangeState(prev, orderedKeys, targetKey));\n },\n [disabled]\n );\n\n const selectAll = useCallback(\n (orderedKeys: ItemKey[]) => {\n if (disabled) return;\n setState(selectAllState(orderedKeys));\n },\n [disabled]\n );\n\n const clear = useCallback(() => setState(clearSelection()), []);\n\n const selectedIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'asset'),\n [state.selectedKeys]\n );\n const selectedFolderIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'folder'),\n [state.selectedKeys]\n );\n\n const value = useMemo<AssetSelection>(\n () => ({\n selectedKeys: state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n anchorKey: state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n }),\n [\n state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n ]\n );\n\n return createElement(AssetSelectionContext.Provider, { value }, children);\n};\n\nexport const useAssetSelection = () => {\n const context = useContext(AssetSelectionContext);\n\n if (!context) {\n throw new Error('useAssetSelection must be used within an AssetSelectionProvider');\n }\n\n return context;\n};\n\nexport const useAssetSelectionOptional = () => useContext(AssetSelectionContext);\n"],"names":["AssetSelectionContext","createContext","AssetSelectionProvider","children","disabled","state","setState","useState","createEmptySelection","isSelected","useCallback","key","selectedKeys","has","toggle","prev","toggleSelection","selectRange","orderedKeys","targetKey","selectRangeState","selectAll","selectAllState","clear","clearSelection","selectedIds","useMemo","getIdsOfKind","selectedFolderIds","value","anchorKey","createElement","Provider","useAssetSelection","context","useContext","Error","useAssetSelectionOptional"],"mappings":"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"}
@@ -13,17 +13,34 @@ const parseFolderIdFromQuery = (folder)=>{
13
13
  const useFolderNavigation = ()=>{
14
14
  const [{ query }, setQuery] = strapiAdmin.useQueryParams();
15
15
  const currentFolderId = parseFolderIdFromQuery(query?.folder);
16
- const navigateToFolder = (folder)=>{
16
+ /**
17
+ * Asset search is global, so keeping `_q` after a folder navigation would show
18
+ * byte-identical results and make the navigation look broken. Clearing it here
19
+ * — rather than at each call site — means no caller can forget.
20
+ */ const navigateToFolder = React.useCallback((folder)=>{
17
21
  setQuery({
18
- folder: String(folder.id)
22
+ folder: String(folder.id),
23
+ _q: undefined
19
24
  });
20
- };
25
+ }, [
26
+ setQuery
27
+ ]);
21
28
  /**
22
- * Move to the Media Library root by removing the `folder` query param.
23
- * `setQuery(_, 'remove')` deletes the keys present on the first argument
24
- * (value is ignored), so any other query state (filters, pagination, etc.)
25
- * is preserved.
29
+ * Move to the Media Library root by removing the `folder` query param, and the
30
+ * search term with it. `setQuery(_, 'remove')` deletes the keys present on the
31
+ * first argument (values are ignored) in one navigation, so any other query
32
+ * state (pagination, the details drawer, etc.) is preserved.
26
33
  */ const navigateToRoot = React.useCallback(()=>{
34
+ setQuery({
35
+ folder: '',
36
+ _q: ''
37
+ }, 'remove');
38
+ }, [
39
+ setQuery
40
+ ]);
41
+ /**
42
+ * Strips a `?folder=` value we can't parse.
43
+ */ const stripFolderParam = React.useCallback(()=>{
27
44
  setQuery({
28
45
  folder: ''
29
46
  }, 'remove');
@@ -34,26 +51,30 @@ const useFolderNavigation = ()=>{
34
51
  // Deleted/missing folders (404) are handled in AssetsPage — that needs a fetch.
35
52
  React.useEffect(()=>{
36
53
  if (query?.folder && currentFolderId === null) {
37
- navigateToRoot();
54
+ stripFolderParam();
38
55
  }
39
56
  }, [
40
57
  query?.folder,
41
58
  currentFolderId,
42
- navigateToRoot
59
+ stripFolderParam
43
60
  ]);
44
61
  /**
45
62
  * Convenience for the FolderTree sidebar: a single entry point that handles
46
63
  * both "go home" and "go to folder X" without forcing the caller to know
47
64
  * whether to call navigateToFolder or navigateToRoot.
48
- */ const navigateToFolderId = (folderId)=>{
65
+ */ const navigateToFolderId = React.useCallback((folderId)=>{
49
66
  if (folderId == null) {
50
67
  navigateToRoot();
51
68
  } else {
52
69
  setQuery({
53
- folder: String(folderId)
70
+ folder: String(folderId),
71
+ _q: undefined
54
72
  });
55
73
  }
56
- };
74
+ }, [
75
+ navigateToRoot,
76
+ setQuery
77
+ ]);
57
78
  return {
58
79
  currentFolderId,
59
80
  navigateToFolder,
@@ -1 +1 @@
1
- {"version":3,"file":"useFolderNavigation.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useFolderNavigation.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\n\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\nconst parseFolderIdFromQuery = (folder: string | undefined): number | null => {\n if (!folder) {\n return null;\n }\n\n const id = Number(folder);\n\n return Number.isFinite(id) ? id : null;\n};\n\nexport const useFolderNavigation = () => {\n const [{ query }, setQuery] = useQueryParams<{ folder?: string }>();\n\n const currentFolderId = parseFolderIdFromQuery(query?.folder);\n\n const navigateToFolder = (folder: Folder) => {\n setQuery({ folder: String(folder.id) });\n };\n\n /**\n * Move to the Media Library root by removing the `folder` query param.\n * `setQuery(_, 'remove')` deletes the keys present on the first argument\n * (value is ignored), so any other query state (filters, pagination, etc.)\n * is preserved.\n */\n const navigateToRoot = useCallback(() => {\n setQuery({ folder: '' }, 'remove');\n }, [setQuery]);\n\n // Malformed ?folder= values (e.g. abc) parse as null; strip the param from the URL.\n // Deleted/missing folders (404) are handled in AssetsPage — that needs a fetch.\n useEffect(() => {\n if (query?.folder && currentFolderId === null) {\n navigateToRoot();\n }\n }, [query?.folder, currentFolderId, navigateToRoot]);\n\n /**\n * Convenience for the FolderTree sidebar: a single entry point that handles\n * both \"go home\" and \"go to folder X\" without forcing the caller to know\n * whether to call navigateToFolder or navigateToRoot.\n */\n const navigateToFolderId = (folderId: number | null) => {\n if (folderId == null) {\n navigateToRoot();\n } else {\n setQuery({ folder: String(folderId) });\n }\n };\n\n return {\n currentFolderId,\n navigateToFolder,\n navigateToRoot,\n navigateToFolderId,\n };\n};\n"],"names":["parseFolderIdFromQuery","folder","id","Number","isFinite","useFolderNavigation","query","setQuery","useQueryParams","currentFolderId","navigateToFolder","String","navigateToRoot","useCallback","useEffect","navigateToFolderId","folderId"],"mappings":";;;;;AAMA,MAAMA,yBAAyB,CAACC,MAAAA,GAAAA;AAC9B,IAAA,IAAI,CAACA,MAAAA,EAAQ;QACX,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,MAAMC,KAAKC,MAAAA,CAAOF,MAAAA,CAAAA;AAElB,IAAA,OAAOE,MAAAA,CAAOC,QAAQ,CAACF,EAAAA,CAAAA,GAAMA,EAAAA,GAAK,IAAA;AACpC,CAAA;MAEaG,mBAAAA,GAAsB,IAAA;AACjC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,0BAAAA,EAAAA;IAE9B,MAAMC,eAAAA,GAAkBT,uBAAuBM,KAAAA,EAAOL,MAAAA,CAAAA;AAEtD,IAAA,MAAMS,mBAAmB,CAACT,MAAAA,GAAAA;QACxBM,QAAAA,CAAS;YAAEN,MAAAA,EAAQU,MAAAA,CAAOV,OAAOC,EAAE;AAAE,SAAA,CAAA;AACvC,IAAA,CAAA;AAEA;;;;;MAMA,MAAMU,iBAAiBC,iBAAAA,CAAY,IAAA;QACjCN,QAAAA,CAAS;YAAEN,MAAAA,EAAQ;SAAG,EAAG,QAAA,CAAA;IAC3B,CAAA,EAAG;AAACM,QAAAA;AAAS,KAAA,CAAA;;;IAIbO,eAAAA,CAAU,IAAA;QACR,IAAIR,KAAAA,EAAOL,MAAAA,IAAUQ,eAAAA,KAAoB,IAAA,EAAM;AAC7CG,YAAAA,cAAAA,EAAAA;AACF,QAAA;IACF,CAAA,EAAG;QAACN,KAAAA,EAAOL,MAAAA;AAAQQ,QAAAA,eAAAA;AAAiBG,QAAAA;AAAe,KAAA,CAAA;AAEnD;;;;MAKA,MAAMG,qBAAqB,CAACC,QAAAA,GAAAA;AAC1B,QAAA,IAAIA,YAAY,IAAA,EAAM;AACpBJ,YAAAA,cAAAA,EAAAA;QACF,CAAA,MAAO;YACLL,QAAAA,CAAS;AAAEN,gBAAAA,MAAAA,EAAQU,MAAAA,CAAOK,QAAAA;AAAU,aAAA,CAAA;AACtC,QAAA;AACF,IAAA,CAAA;IAEA,OAAO;AACLP,QAAAA,eAAAA;AACAC,QAAAA,gBAAAA;AACAE,QAAAA,cAAAA;AACAG,QAAAA;AACF,KAAA;AACF;;;;"}
1
+ {"version":3,"file":"useFolderNavigation.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useFolderNavigation.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\n\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\nconst parseFolderIdFromQuery = (folder: string | undefined): number | null => {\n if (!folder) {\n return null;\n }\n\n const id = Number(folder);\n\n return Number.isFinite(id) ? id : null;\n};\n\ninterface FolderNavigationQuery {\n folder?: string;\n _q?: string;\n}\n\nexport const useFolderNavigation = () => {\n const [{ query }, setQuery] = useQueryParams<FolderNavigationQuery>();\n\n const currentFolderId = parseFolderIdFromQuery(query?.folder);\n\n /**\n * Asset search is global, so keeping `_q` after a folder navigation would show\n * byte-identical results and make the navigation look broken. Clearing it here\n * — rather than at each call site — means no caller can forget.\n */\n const navigateToFolder = useCallback(\n (folder: Folder) => {\n setQuery({ folder: String(folder.id), _q: undefined });\n },\n [setQuery]\n );\n\n /**\n * Move to the Media Library root by removing the `folder` query param, and the\n * search term with it. `setQuery(_, 'remove')` deletes the keys present on the\n * first argument (values are ignored) in one navigation, so any other query\n * state (pagination, the details drawer, etc.) is preserved.\n */\n const navigateToRoot = useCallback(() => {\n setQuery({ folder: '', _q: '' }, 'remove');\n }, [setQuery]);\n\n /**\n * Strips a `?folder=` value we can't parse.\n */\n const stripFolderParam = useCallback(() => {\n setQuery({ folder: '' }, 'remove');\n }, [setQuery]);\n\n // Malformed ?folder= values (e.g. abc) parse as null; strip the param from the URL.\n // Deleted/missing folders (404) are handled in AssetsPage — that needs a fetch.\n useEffect(() => {\n if (query?.folder && currentFolderId === null) {\n stripFolderParam();\n }\n }, [query?.folder, currentFolderId, stripFolderParam]);\n\n /**\n * Convenience for the FolderTree sidebar: a single entry point that handles\n * both \"go home\" and \"go to folder X\" without forcing the caller to know\n * whether to call navigateToFolder or navigateToRoot.\n */\n const navigateToFolderId = useCallback(\n (folderId: number | null) => {\n if (folderId == null) {\n navigateToRoot();\n } else {\n setQuery({ folder: String(folderId), _q: undefined });\n }\n },\n [navigateToRoot, setQuery]\n );\n\n return {\n currentFolderId,\n navigateToFolder,\n navigateToRoot,\n navigateToFolderId,\n };\n};\n"],"names":["parseFolderIdFromQuery","folder","id","Number","isFinite","useFolderNavigation","query","setQuery","useQueryParams","currentFolderId","navigateToFolder","useCallback","String","_q","undefined","navigateToRoot","stripFolderParam","useEffect","navigateToFolderId","folderId"],"mappings":";;;;;AAMA,MAAMA,yBAAyB,CAACC,MAAAA,GAAAA;AAC9B,IAAA,IAAI,CAACA,MAAAA,EAAQ;QACX,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,MAAMC,KAAKC,MAAAA,CAAOF,MAAAA,CAAAA;AAElB,IAAA,OAAOE,MAAAA,CAAOC,QAAQ,CAACF,EAAAA,CAAAA,GAAMA,EAAAA,GAAK,IAAA;AACpC,CAAA;MAOaG,mBAAAA,GAAsB,IAAA;AACjC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,0BAAAA,EAAAA;IAE9B,MAAMC,eAAAA,GAAkBT,uBAAuBM,KAAAA,EAAOL,MAAAA,CAAAA;AAEtD;;;;MAKA,MAAMS,gBAAAA,GAAmBC,iBAAAA,CACvB,CAACV,MAAAA,GAAAA;QACCM,QAAAA,CAAS;YAAEN,MAAAA,EAAQW,MAAAA,CAAOX,OAAOC,EAAE,CAAA;YAAGW,EAAAA,EAAIC;AAAU,SAAA,CAAA;IACtD,CAAA,EACA;AAACP,QAAAA;AAAS,KAAA,CAAA;AAGZ;;;;;MAMA,MAAMQ,iBAAiBJ,iBAAAA,CAAY,IAAA;QACjCJ,QAAAA,CAAS;YAAEN,MAAAA,EAAQ,EAAA;YAAIY,EAAAA,EAAI;SAAG,EAAG,QAAA,CAAA;IACnC,CAAA,EAAG;AAACN,QAAAA;AAAS,KAAA,CAAA;AAEb;;MAGA,MAAMS,mBAAmBL,iBAAAA,CAAY,IAAA;QACnCJ,QAAAA,CAAS;YAAEN,MAAAA,EAAQ;SAAG,EAAG,QAAA,CAAA;IAC3B,CAAA,EAAG;AAACM,QAAAA;AAAS,KAAA,CAAA;;;IAIbU,eAAAA,CAAU,IAAA;QACR,IAAIX,KAAAA,EAAOL,MAAAA,IAAUQ,eAAAA,KAAoB,IAAA,EAAM;AAC7CO,YAAAA,gBAAAA,EAAAA;AACF,QAAA;IACF,CAAA,EAAG;QAACV,KAAAA,EAAOL,MAAAA;AAAQQ,QAAAA,eAAAA;AAAiBO,QAAAA;AAAiB,KAAA,CAAA;AAErD;;;;MAKA,MAAME,kBAAAA,GAAqBP,iBAAAA,CACzB,CAACQ,QAAAA,GAAAA;AACC,QAAA,IAAIA,YAAY,IAAA,EAAM;AACpBJ,YAAAA,cAAAA,EAAAA;QACF,CAAA,MAAO;YACLR,QAAAA,CAAS;AAAEN,gBAAAA,MAAAA,EAAQW,MAAAA,CAAOO,QAAAA,CAAAA;gBAAWN,EAAAA,EAAIC;AAAU,aAAA,CAAA;AACrD,QAAA;IACF,CAAA,EACA;AAACC,QAAAA,cAAAA;AAAgBR,QAAAA;AAAS,KAAA,CAAA;IAG5B,OAAO;AACLE,QAAAA,eAAAA;AACAC,QAAAA,gBAAAA;AACAK,QAAAA,cAAAA;AACAG,QAAAA;AACF,KAAA;AACF;;;;"}
@@ -11,17 +11,34 @@ const parseFolderIdFromQuery = (folder)=>{
11
11
  const useFolderNavigation = ()=>{
12
12
  const [{ query }, setQuery] = useQueryParams();
13
13
  const currentFolderId = parseFolderIdFromQuery(query?.folder);
14
- const navigateToFolder = (folder)=>{
14
+ /**
15
+ * Asset search is global, so keeping `_q` after a folder navigation would show
16
+ * byte-identical results and make the navigation look broken. Clearing it here
17
+ * — rather than at each call site — means no caller can forget.
18
+ */ const navigateToFolder = useCallback((folder)=>{
15
19
  setQuery({
16
- folder: String(folder.id)
20
+ folder: String(folder.id),
21
+ _q: undefined
17
22
  });
18
- };
23
+ }, [
24
+ setQuery
25
+ ]);
19
26
  /**
20
- * Move to the Media Library root by removing the `folder` query param.
21
- * `setQuery(_, 'remove')` deletes the keys present on the first argument
22
- * (value is ignored), so any other query state (filters, pagination, etc.)
23
- * is preserved.
27
+ * Move to the Media Library root by removing the `folder` query param, and the
28
+ * search term with it. `setQuery(_, 'remove')` deletes the keys present on the
29
+ * first argument (values are ignored) in one navigation, so any other query
30
+ * state (pagination, the details drawer, etc.) is preserved.
24
31
  */ const navigateToRoot = useCallback(()=>{
32
+ setQuery({
33
+ folder: '',
34
+ _q: ''
35
+ }, 'remove');
36
+ }, [
37
+ setQuery
38
+ ]);
39
+ /**
40
+ * Strips a `?folder=` value we can't parse.
41
+ */ const stripFolderParam = useCallback(()=>{
25
42
  setQuery({
26
43
  folder: ''
27
44
  }, 'remove');
@@ -32,26 +49,30 @@ const useFolderNavigation = ()=>{
32
49
  // Deleted/missing folders (404) are handled in AssetsPage — that needs a fetch.
33
50
  useEffect(()=>{
34
51
  if (query?.folder && currentFolderId === null) {
35
- navigateToRoot();
52
+ stripFolderParam();
36
53
  }
37
54
  }, [
38
55
  query?.folder,
39
56
  currentFolderId,
40
- navigateToRoot
57
+ stripFolderParam
41
58
  ]);
42
59
  /**
43
60
  * Convenience for the FolderTree sidebar: a single entry point that handles
44
61
  * both "go home" and "go to folder X" without forcing the caller to know
45
62
  * whether to call navigateToFolder or navigateToRoot.
46
- */ const navigateToFolderId = (folderId)=>{
63
+ */ const navigateToFolderId = useCallback((folderId)=>{
47
64
  if (folderId == null) {
48
65
  navigateToRoot();
49
66
  } else {
50
67
  setQuery({
51
- folder: String(folderId)
68
+ folder: String(folderId),
69
+ _q: undefined
52
70
  });
53
71
  }
54
- };
72
+ }, [
73
+ navigateToRoot,
74
+ setQuery
75
+ ]);
55
76
  return {
56
77
  currentFolderId,
57
78
  navigateToFolder,
@@ -1 +1 @@
1
- {"version":3,"file":"useFolderNavigation.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useFolderNavigation.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\n\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\nconst parseFolderIdFromQuery = (folder: string | undefined): number | null => {\n if (!folder) {\n return null;\n }\n\n const id = Number(folder);\n\n return Number.isFinite(id) ? id : null;\n};\n\nexport const useFolderNavigation = () => {\n const [{ query }, setQuery] = useQueryParams<{ folder?: string }>();\n\n const currentFolderId = parseFolderIdFromQuery(query?.folder);\n\n const navigateToFolder = (folder: Folder) => {\n setQuery({ folder: String(folder.id) });\n };\n\n /**\n * Move to the Media Library root by removing the `folder` query param.\n * `setQuery(_, 'remove')` deletes the keys present on the first argument\n * (value is ignored), so any other query state (filters, pagination, etc.)\n * is preserved.\n */\n const navigateToRoot = useCallback(() => {\n setQuery({ folder: '' }, 'remove');\n }, [setQuery]);\n\n // Malformed ?folder= values (e.g. abc) parse as null; strip the param from the URL.\n // Deleted/missing folders (404) are handled in AssetsPage — that needs a fetch.\n useEffect(() => {\n if (query?.folder && currentFolderId === null) {\n navigateToRoot();\n }\n }, [query?.folder, currentFolderId, navigateToRoot]);\n\n /**\n * Convenience for the FolderTree sidebar: a single entry point that handles\n * both \"go home\" and \"go to folder X\" without forcing the caller to know\n * whether to call navigateToFolder or navigateToRoot.\n */\n const navigateToFolderId = (folderId: number | null) => {\n if (folderId == null) {\n navigateToRoot();\n } else {\n setQuery({ folder: String(folderId) });\n }\n };\n\n return {\n currentFolderId,\n navigateToFolder,\n navigateToRoot,\n navigateToFolderId,\n };\n};\n"],"names":["parseFolderIdFromQuery","folder","id","Number","isFinite","useFolderNavigation","query","setQuery","useQueryParams","currentFolderId","navigateToFolder","String","navigateToRoot","useCallback","useEffect","navigateToFolderId","folderId"],"mappings":";;;AAMA,MAAMA,yBAAyB,CAACC,MAAAA,GAAAA;AAC9B,IAAA,IAAI,CAACA,MAAAA,EAAQ;QACX,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,MAAMC,KAAKC,MAAAA,CAAOF,MAAAA,CAAAA;AAElB,IAAA,OAAOE,MAAAA,CAAOC,QAAQ,CAACF,EAAAA,CAAAA,GAAMA,EAAAA,GAAK,IAAA;AACpC,CAAA;MAEaG,mBAAAA,GAAsB,IAAA;AACjC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,cAAAA,EAAAA;IAE9B,MAAMC,eAAAA,GAAkBT,uBAAuBM,KAAAA,EAAOL,MAAAA,CAAAA;AAEtD,IAAA,MAAMS,mBAAmB,CAACT,MAAAA,GAAAA;QACxBM,QAAAA,CAAS;YAAEN,MAAAA,EAAQU,MAAAA,CAAOV,OAAOC,EAAE;AAAE,SAAA,CAAA;AACvC,IAAA,CAAA;AAEA;;;;;MAMA,MAAMU,iBAAiBC,WAAAA,CAAY,IAAA;QACjCN,QAAAA,CAAS;YAAEN,MAAAA,EAAQ;SAAG,EAAG,QAAA,CAAA;IAC3B,CAAA,EAAG;AAACM,QAAAA;AAAS,KAAA,CAAA;;;IAIbO,SAAAA,CAAU,IAAA;QACR,IAAIR,KAAAA,EAAOL,MAAAA,IAAUQ,eAAAA,KAAoB,IAAA,EAAM;AAC7CG,YAAAA,cAAAA,EAAAA;AACF,QAAA;IACF,CAAA,EAAG;QAACN,KAAAA,EAAOL,MAAAA;AAAQQ,QAAAA,eAAAA;AAAiBG,QAAAA;AAAe,KAAA,CAAA;AAEnD;;;;MAKA,MAAMG,qBAAqB,CAACC,QAAAA,GAAAA;AAC1B,QAAA,IAAIA,YAAY,IAAA,EAAM;AACpBJ,YAAAA,cAAAA,EAAAA;QACF,CAAA,MAAO;YACLL,QAAAA,CAAS;AAAEN,gBAAAA,MAAAA,EAAQU,MAAAA,CAAOK,QAAAA;AAAU,aAAA,CAAA;AACtC,QAAA;AACF,IAAA,CAAA;IAEA,OAAO;AACLP,QAAAA,eAAAA;AACAC,QAAAA,gBAAAA;AACAE,QAAAA,cAAAA;AACAG,QAAAA;AACF,KAAA;AACF;;;;"}
1
+ {"version":3,"file":"useFolderNavigation.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useFolderNavigation.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\n\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\nconst parseFolderIdFromQuery = (folder: string | undefined): number | null => {\n if (!folder) {\n return null;\n }\n\n const id = Number(folder);\n\n return Number.isFinite(id) ? id : null;\n};\n\ninterface FolderNavigationQuery {\n folder?: string;\n _q?: string;\n}\n\nexport const useFolderNavigation = () => {\n const [{ query }, setQuery] = useQueryParams<FolderNavigationQuery>();\n\n const currentFolderId = parseFolderIdFromQuery(query?.folder);\n\n /**\n * Asset search is global, so keeping `_q` after a folder navigation would show\n * byte-identical results and make the navigation look broken. Clearing it here\n * — rather than at each call site — means no caller can forget.\n */\n const navigateToFolder = useCallback(\n (folder: Folder) => {\n setQuery({ folder: String(folder.id), _q: undefined });\n },\n [setQuery]\n );\n\n /**\n * Move to the Media Library root by removing the `folder` query param, and the\n * search term with it. `setQuery(_, 'remove')` deletes the keys present on the\n * first argument (values are ignored) in one navigation, so any other query\n * state (pagination, the details drawer, etc.) is preserved.\n */\n const navigateToRoot = useCallback(() => {\n setQuery({ folder: '', _q: '' }, 'remove');\n }, [setQuery]);\n\n /**\n * Strips a `?folder=` value we can't parse.\n */\n const stripFolderParam = useCallback(() => {\n setQuery({ folder: '' }, 'remove');\n }, [setQuery]);\n\n // Malformed ?folder= values (e.g. abc) parse as null; strip the param from the URL.\n // Deleted/missing folders (404) are handled in AssetsPage — that needs a fetch.\n useEffect(() => {\n if (query?.folder && currentFolderId === null) {\n stripFolderParam();\n }\n }, [query?.folder, currentFolderId, stripFolderParam]);\n\n /**\n * Convenience for the FolderTree sidebar: a single entry point that handles\n * both \"go home\" and \"go to folder X\" without forcing the caller to know\n * whether to call navigateToFolder or navigateToRoot.\n */\n const navigateToFolderId = useCallback(\n (folderId: number | null) => {\n if (folderId == null) {\n navigateToRoot();\n } else {\n setQuery({ folder: String(folderId), _q: undefined });\n }\n },\n [navigateToRoot, setQuery]\n );\n\n return {\n currentFolderId,\n navigateToFolder,\n navigateToRoot,\n navigateToFolderId,\n };\n};\n"],"names":["parseFolderIdFromQuery","folder","id","Number","isFinite","useFolderNavigation","query","setQuery","useQueryParams","currentFolderId","navigateToFolder","useCallback","String","_q","undefined","navigateToRoot","stripFolderParam","useEffect","navigateToFolderId","folderId"],"mappings":";;;AAMA,MAAMA,yBAAyB,CAACC,MAAAA,GAAAA;AAC9B,IAAA,IAAI,CAACA,MAAAA,EAAQ;QACX,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,MAAMC,KAAKC,MAAAA,CAAOF,MAAAA,CAAAA;AAElB,IAAA,OAAOE,MAAAA,CAAOC,QAAQ,CAACF,EAAAA,CAAAA,GAAMA,EAAAA,GAAK,IAAA;AACpC,CAAA;MAOaG,mBAAAA,GAAsB,IAAA;AACjC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,cAAAA,EAAAA;IAE9B,MAAMC,eAAAA,GAAkBT,uBAAuBM,KAAAA,EAAOL,MAAAA,CAAAA;AAEtD;;;;MAKA,MAAMS,gBAAAA,GAAmBC,WAAAA,CACvB,CAACV,MAAAA,GAAAA;QACCM,QAAAA,CAAS;YAAEN,MAAAA,EAAQW,MAAAA,CAAOX,OAAOC,EAAE,CAAA;YAAGW,EAAAA,EAAIC;AAAU,SAAA,CAAA;IACtD,CAAA,EACA;AAACP,QAAAA;AAAS,KAAA,CAAA;AAGZ;;;;;MAMA,MAAMQ,iBAAiBJ,WAAAA,CAAY,IAAA;QACjCJ,QAAAA,CAAS;YAAEN,MAAAA,EAAQ,EAAA;YAAIY,EAAAA,EAAI;SAAG,EAAG,QAAA,CAAA;IACnC,CAAA,EAAG;AAACN,QAAAA;AAAS,KAAA,CAAA;AAEb;;MAGA,MAAMS,mBAAmBL,WAAAA,CAAY,IAAA;QACnCJ,QAAAA,CAAS;YAAEN,MAAAA,EAAQ;SAAG,EAAG,QAAA,CAAA;IAC3B,CAAA,EAAG;AAACM,QAAAA;AAAS,KAAA,CAAA;;;IAIbU,SAAAA,CAAU,IAAA;QACR,IAAIX,KAAAA,EAAOL,MAAAA,IAAUQ,eAAAA,KAAoB,IAAA,EAAM;AAC7CO,YAAAA,gBAAAA,EAAAA;AACF,QAAA;IACF,CAAA,EAAG;QAACV,KAAAA,EAAOL,MAAAA;AAAQQ,QAAAA,eAAAA;AAAiBO,QAAAA;AAAiB,KAAA,CAAA;AAErD;;;;MAKA,MAAME,kBAAAA,GAAqBP,WAAAA,CACzB,CAACQ,QAAAA,GAAAA;AACC,QAAA,IAAIA,YAAY,IAAA,EAAM;AACpBJ,YAAAA,cAAAA,EAAAA;QACF,CAAA,MAAO;YACLR,QAAAA,CAAS;AAAEN,gBAAAA,MAAAA,EAAQW,MAAAA,CAAOO,QAAAA,CAAAA;gBAAWN,EAAAA,EAAIC;AAAU,aAAA,CAAA;AACrD,QAAA;IACF,CAAA,EACA;AAACC,QAAAA,cAAAA;AAAgBR,QAAAA;AAAS,KAAA,CAAA;IAG5B,OAAO;AACLE,QAAAA,eAAAA;AACAC,QAAAA,gBAAAA;AACAK,QAAAA,cAAAA;AACAG,QAAAA;AACF,KAAA;AACF;;;;"}
@@ -4,17 +4,26 @@ var React = require('react');
4
4
  var assets = require('../../../services/assets.js');
5
5
 
6
6
  const PAGE_SIZE = 20;
7
- const useInfiniteAssets = ({ folder = null, sort } = {})=>{
7
+ const useInfiniteAssets = ({ folder = null, sort, search, filters, filtersKey = '', enabled = true } = {})=>{
8
8
  const [page, setPage] = React.useState(1);
9
9
  const lastResultsRef = React.useRef([]);
10
+ const lastPaginationRef = React.useRef(undefined);
10
11
  const isMountRef = React.useRef(true);
12
+ const isSearchMountRef = React.useRef(true);
11
13
  const { currentData: data, isLoading, isFetching, error } = assets.useGetAssetsQuery({
12
14
  folder,
13
15
  page,
14
16
  pageSize: PAGE_SIZE,
15
- sort
17
+ sort,
18
+ search,
19
+ filters
20
+ }, {
21
+ skip: !enabled
16
22
  });
17
23
  const pagination = data?.pagination;
24
+ if (pagination) {
25
+ lastPaginationRef.current = pagination;
26
+ }
18
27
  // Accumulate pages. When cache is invalidated the current page is refetched
19
28
  // detect this and reset to avoid a gap in the results.
20
29
  const assets$1 = React.useMemo(()=>{
@@ -54,16 +63,43 @@ const useInfiniteAssets = ({ folder = null, sort } = {})=>{
54
63
  lastResultsRef.current = [];
55
64
  }, [
56
65
  folder,
57
- sort
66
+ sort,
67
+ filtersKey
58
68
  ]);
69
+ // A search change resets pagination but keeps the previous results rendered
70
+ // until the new page 1 lands, which replaces them.
71
+ React.useEffect(()=>{
72
+ if (isSearchMountRef.current) {
73
+ isSearchMountRef.current = false;
74
+ return;
75
+ }
76
+ setPage(1);
77
+ }, [
78
+ search
79
+ ]);
80
+ // Deliberately the live value, not the stale-tolerant one below: paging must
81
+ // never be driven by a total that belongs to the previous query.
59
82
  const hasNextPage = pagination ? page < pagination.pageCount : false;
60
83
  const isFetchingMore = isFetching && page > 1;
61
84
  const fetchNextPage = React.useCallback(()=>{
62
85
  setPage((prev)=>prev + 1);
63
86
  }, []);
87
+ if (!enabled) {
88
+ return {
89
+ assets: [],
90
+ pagination: undefined,
91
+ isLoading: false,
92
+ isFetchingMore: false,
93
+ hasNextPage: false,
94
+ fetchNextPage,
95
+ error: undefined
96
+ };
97
+ }
64
98
  return {
65
99
  assets: assets$1,
66
- pagination,
100
+ // Reported alongside the stale `assets` above so a consumer showing the total
101
+ // doesn't flash zero mid-transition.
102
+ pagination: pagination ?? lastPaginationRef.current,
67
103
  isLoading,
68
104
  isFetchingMore,
69
105
  hasNextPage,