@strapi/upload 5.52.1 → 5.52.3

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 (326) hide show
  1. package/dist/admin/components/AssetCard/AssetCard.js +1 -0
  2. package/dist/admin/components/AssetCard/AssetCard.js.map +1 -1
  3. package/dist/admin/components/AssetCard/AssetCard.mjs +1 -0
  4. package/dist/admin/components/AssetCard/AssetCard.mjs.map +1 -1
  5. package/dist/admin/components/AssetCard/AssetCardBase.js +1 -0
  6. package/dist/admin/components/AssetCard/AssetCardBase.js.map +1 -1
  7. package/dist/admin/components/AssetCard/AssetCardBase.mjs +1 -0
  8. package/dist/admin/components/AssetCard/AssetCardBase.mjs.map +1 -1
  9. package/dist/admin/components/AssetCard/ImageAssetCard.js +1 -0
  10. package/dist/admin/components/AssetCard/ImageAssetCard.js.map +1 -1
  11. package/dist/admin/components/AssetCard/ImageAssetCard.mjs +1 -0
  12. package/dist/admin/components/AssetCard/ImageAssetCard.mjs.map +1 -1
  13. package/dist/admin/components/AssetCard/UploadingAssetCard.js +1 -0
  14. package/dist/admin/components/AssetCard/UploadingAssetCard.js.map +1 -1
  15. package/dist/admin/components/AssetCard/UploadingAssetCard.mjs +1 -0
  16. package/dist/admin/components/AssetCard/UploadingAssetCard.mjs.map +1 -1
  17. package/dist/admin/components/AssetCard/VideoAssetCard.js +1 -0
  18. package/dist/admin/components/AssetCard/VideoAssetCard.js.map +1 -1
  19. package/dist/admin/components/AssetCard/VideoAssetCard.mjs +1 -0
  20. package/dist/admin/components/AssetCard/VideoAssetCard.mjs.map +1 -1
  21. package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.js +1 -0
  22. package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.js.map +1 -1
  23. package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.mjs +1 -0
  24. package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.mjs.map +1 -1
  25. package/dist/admin/components/AssetDialog/BrowseStep/Filters.js +1 -0
  26. package/dist/admin/components/AssetDialog/BrowseStep/Filters.js.map +1 -1
  27. package/dist/admin/components/AssetDialog/BrowseStep/Filters.mjs +1 -0
  28. package/dist/admin/components/AssetDialog/BrowseStep/Filters.mjs.map +1 -1
  29. package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.js +1 -0
  30. package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.js.map +1 -1
  31. package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.mjs +1 -0
  32. package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.mjs.map +1 -1
  33. package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.js +1 -0
  34. package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.js.map +1 -1
  35. package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.mjs +1 -0
  36. package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.mjs.map +1 -1
  37. package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.js +1 -0
  38. package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.js.map +1 -1
  39. package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.mjs +1 -0
  40. package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.mjs.map +1 -1
  41. package/dist/admin/components/EditAssetDialog/EditAssetContent.js +1 -0
  42. package/dist/admin/components/EditAssetDialog/EditAssetContent.js.map +1 -1
  43. package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs +1 -0
  44. package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs.map +1 -1
  45. package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.js +1 -0
  46. package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.js.map +1 -1
  47. package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.mjs +1 -0
  48. package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.mjs.map +1 -1
  49. package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.js +1 -0
  50. package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.js.map +1 -1
  51. package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.mjs +1 -0
  52. package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.mjs.map +1 -1
  53. package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.js +1 -0
  54. package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.js.map +1 -1
  55. package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.mjs +1 -0
  56. package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.mjs.map +1 -1
  57. package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.js +1 -0
  58. package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.js.map +1 -1
  59. package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.mjs +1 -0
  60. package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.mjs.map +1 -1
  61. package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.js +1 -0
  62. package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.js.map +1 -1
  63. package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.mjs +1 -0
  64. package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.mjs.map +1 -1
  65. package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.js +1 -0
  66. package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.js.map +1 -1
  67. package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.mjs +1 -0
  68. package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.mjs.map +1 -1
  69. package/dist/admin/components/FilterList/FilterList.js +2 -2
  70. package/dist/admin/components/FilterList/FilterList.js.map +1 -1
  71. package/dist/admin/components/FilterList/FilterList.mjs +2 -2
  72. package/dist/admin/components/FilterList/FilterList.mjs.map +1 -1
  73. package/dist/admin/components/FilterPopover/FilterPopover.js +4 -4
  74. package/dist/admin/components/FilterPopover/FilterPopover.js.map +1 -1
  75. package/dist/admin/components/FilterPopover/FilterPopover.mjs +4 -4
  76. package/dist/admin/components/FilterPopover/FilterPopover.mjs.map +1 -1
  77. package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.js +1 -0
  78. package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.js.map +1 -1
  79. package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.mjs +1 -0
  80. package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.mjs.map +1 -1
  81. package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.js +1 -0
  82. package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.js.map +1 -1
  83. package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.mjs +1 -0
  84. package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.mjs.map +1 -1
  85. package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.js +4 -10
  86. package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.js.map +1 -1
  87. package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.mjs +4 -10
  88. package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.mjs.map +1 -1
  89. package/dist/admin/components/SortPicker/SortPicker.js +1 -0
  90. package/dist/admin/components/SortPicker/SortPicker.js.map +1 -1
  91. package/dist/admin/components/SortPicker/SortPicker.mjs +1 -0
  92. package/dist/admin/components/SortPicker/SortPicker.mjs.map +1 -1
  93. package/dist/admin/components/TableList/CellContent.js +1 -0
  94. package/dist/admin/components/TableList/CellContent.js.map +1 -1
  95. package/dist/admin/components/TableList/CellContent.mjs +1 -0
  96. package/dist/admin/components/TableList/CellContent.mjs.map +1 -1
  97. package/dist/admin/components/TableList/PreviewCell.js +1 -0
  98. package/dist/admin/components/TableList/PreviewCell.js.map +1 -1
  99. package/dist/admin/components/TableList/PreviewCell.mjs +1 -0
  100. package/dist/admin/components/TableList/PreviewCell.mjs.map +1 -1
  101. package/dist/admin/components/TableList/TableList.js +1 -0
  102. package/dist/admin/components/TableList/TableList.js.map +1 -1
  103. package/dist/admin/components/TableList/TableList.mjs +1 -0
  104. package/dist/admin/components/TableList/TableList.mjs.map +1 -1
  105. package/dist/admin/components/TableList/TableRows.js +1 -0
  106. package/dist/admin/components/TableList/TableRows.js.map +1 -1
  107. package/dist/admin/components/TableList/TableRows.mjs +1 -0
  108. package/dist/admin/components/TableList/TableRows.mjs.map +1 -1
  109. package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.js +1 -0
  110. package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.js.map +1 -1
  111. package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.mjs +1 -0
  112. package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.mjs.map +1 -1
  113. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.js +9 -21
  114. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.js.map +1 -1
  115. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.mjs +9 -21
  116. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.mjs.map +1 -1
  117. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js +1 -0
  118. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js.map +1 -1
  119. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs +1 -0
  120. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs.map +1 -1
  121. package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.js +1 -0
  122. package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.js.map +1 -1
  123. package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.mjs +1 -0
  124. package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.mjs.map +1 -1
  125. package/dist/admin/constants.js +1 -0
  126. package/dist/admin/constants.js.map +1 -1
  127. package/dist/admin/constants.mjs +1 -0
  128. package/dist/admin/constants.mjs.map +1 -1
  129. package/dist/admin/future/components/Drawer.js +11 -0
  130. package/dist/admin/future/components/Drawer.js.map +1 -1
  131. package/dist/admin/future/components/Drawer.mjs +11 -0
  132. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  133. package/dist/admin/future/components/UploadProgressDialog.js +82 -13
  134. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  135. package/dist/admin/future/components/UploadProgressDialog.mjs +84 -15
  136. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  137. package/dist/admin/future/hooks/useApiErrorMessage.js +61 -0
  138. package/dist/admin/future/hooks/useApiErrorMessage.js.map +1 -0
  139. package/dist/admin/future/hooks/useApiErrorMessage.mjs +39 -0
  140. package/dist/admin/future/hooks/useApiErrorMessage.mjs.map +1 -0
  141. package/dist/admin/future/pages/Assets/AssetsPage.js +11 -0
  142. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  143. package/dist/admin/future/pages/Assets/AssetsPage.mjs +11 -0
  144. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  145. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +8 -6
  146. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
  147. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +8 -6
  148. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
  149. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +70 -59
  150. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  151. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +72 -61
  152. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  153. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +7 -5
  154. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  155. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +7 -5
  156. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  157. package/dist/admin/future/pages/Assets/components/AssetsTable.js +47 -28
  158. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  159. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +48 -29
  160. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  161. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +87 -2
  162. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  163. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +89 -4
  164. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  165. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +3 -2
  166. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
  167. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +3 -2
  168. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
  169. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +4 -2
  170. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  171. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +4 -2
  172. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  173. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +21 -3
  174. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  175. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +21 -3
  176. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  177. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +4 -3
  178. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
  179. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +5 -4
  180. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
  181. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +40 -2
  182. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  183. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +40 -2
  184. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  185. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.js +30 -0
  186. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.js.map +1 -0
  187. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.mjs +26 -0
  188. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.mjs.map +1 -0
  189. package/dist/admin/future/pages/Assets/hooks/useListFilters.js +5 -5
  190. package/dist/admin/future/pages/Assets/hooks/useListFilters.js.map +1 -1
  191. package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs +6 -6
  192. package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs.map +1 -1
  193. package/dist/admin/future/pages/Assets/hooks/useListSort.js +10 -10
  194. package/dist/admin/future/pages/Assets/hooks/useListSort.js.map +1 -1
  195. package/dist/admin/future/pages/Assets/hooks/useListSort.mjs +11 -11
  196. package/dist/admin/future/pages/Assets/hooks/useListSort.mjs.map +1 -1
  197. package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.js +86 -0
  198. package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.js.map +1 -0
  199. package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.mjs +83 -0
  200. package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.mjs.map +1 -0
  201. package/dist/admin/future/pages/Assets/utils/renderedKeys.js +19 -0
  202. package/dist/admin/future/pages/Assets/utils/renderedKeys.js.map +1 -0
  203. package/dist/admin/future/pages/Assets/utils/renderedKeys.mjs +17 -0
  204. package/dist/admin/future/pages/Assets/utils/renderedKeys.mjs.map +1 -0
  205. package/dist/admin/future/services/api.js +131 -13
  206. package/dist/admin/future/services/api.js.map +1 -1
  207. package/dist/admin/future/services/api.mjs +133 -15
  208. package/dist/admin/future/services/api.mjs.map +1 -1
  209. package/dist/admin/future/services/uploadFileViaXHR.js +9 -3
  210. package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -1
  211. package/dist/admin/future/services/uploadFileViaXHR.mjs +9 -3
  212. package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -1
  213. package/dist/admin/future/store/uploadProgress.js +98 -6
  214. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  215. package/dist/admin/future/store/uploadProgress.mjs +94 -7
  216. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  217. package/dist/admin/future/utils/getApiErrorMessage.js +20 -0
  218. package/dist/admin/future/utils/getApiErrorMessage.js.map +1 -0
  219. package/dist/admin/future/utils/getApiErrorMessage.mjs +18 -0
  220. package/dist/admin/future/utils/getApiErrorMessage.mjs.map +1 -0
  221. package/dist/admin/index.js +1 -0
  222. package/dist/admin/index.js.map +1 -1
  223. package/dist/admin/index.mjs +1 -0
  224. package/dist/admin/index.mjs.map +1 -1
  225. package/dist/admin/pages/App/ConfigureTheView/components/Settings.js +1 -0
  226. package/dist/admin/pages/App/ConfigureTheView/components/Settings.js.map +1 -1
  227. package/dist/admin/pages/App/ConfigureTheView/components/Settings.mjs +1 -0
  228. package/dist/admin/pages/App/ConfigureTheView/components/Settings.mjs.map +1 -1
  229. package/dist/admin/pages/App/MediaLibrary.js +7 -9
  230. package/dist/admin/pages/App/MediaLibrary.js.map +1 -1
  231. package/dist/admin/pages/App/MediaLibrary.mjs +8 -10
  232. package/dist/admin/pages/App/MediaLibrary.mjs.map +1 -1
  233. package/dist/admin/pages/App/components/BulkActions.js +1 -0
  234. package/dist/admin/pages/App/components/BulkActions.js.map +1 -1
  235. package/dist/admin/pages/App/components/BulkActions.mjs +1 -0
  236. package/dist/admin/pages/App/components/BulkActions.mjs.map +1 -1
  237. package/dist/admin/pages/App/components/EmptyOrNoPermissions.js +1 -0
  238. package/dist/admin/pages/App/components/EmptyOrNoPermissions.js.map +1 -1
  239. package/dist/admin/pages/App/components/EmptyOrNoPermissions.mjs +1 -0
  240. package/dist/admin/pages/App/components/EmptyOrNoPermissions.mjs.map +1 -1
  241. package/dist/admin/pages/App/components/Filters.js +8 -8
  242. package/dist/admin/pages/App/components/Filters.js.map +1 -1
  243. package/dist/admin/pages/App/components/Filters.mjs +9 -9
  244. package/dist/admin/pages/App/components/Filters.mjs.map +1 -1
  245. package/dist/admin/pages/App/components/Header.js +1 -1
  246. package/dist/admin/pages/App/components/Header.js.map +1 -1
  247. package/dist/admin/pages/App/components/Header.mjs +2 -2
  248. package/dist/admin/pages/App/components/Header.mjs.map +1 -1
  249. package/dist/admin/src/future/hooks/useApiErrorMessage.d.ts +17 -0
  250. package/dist/admin/src/future/pages/Assets/components/AssetsGrid.d.ts +4 -1
  251. package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +4 -1
  252. package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +8 -1
  253. package/dist/admin/src/future/pages/Assets/hooks/useIsAssetDetailsOpen.d.ts +18 -0
  254. package/dist/admin/src/future/pages/Assets/utils/mergeUploadedAssets.d.ts +35 -0
  255. package/dist/admin/src/future/pages/Assets/utils/renderedKeys.d.ts +19 -0
  256. package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +6 -1
  257. package/dist/admin/src/future/store/uploadProgress.d.ts +67 -1
  258. package/dist/admin/src/future/utils/getApiErrorMessage.d.ts +1 -0
  259. package/dist/admin/src/utils/index.d.ts +1 -0
  260. package/dist/admin/src/utils/rawFileToAsset.d.ts +2 -2
  261. package/dist/admin/src/utils/resolveFileMime.d.ts +13 -0
  262. package/dist/admin/translations/ca.json.js +1 -1
  263. package/dist/admin/translations/ca.json.mjs +1 -1
  264. package/dist/admin/translations/de.json.js +1 -1
  265. package/dist/admin/translations/de.json.mjs +1 -1
  266. package/dist/admin/translations/en.json.js +6 -2
  267. package/dist/admin/translations/en.json.js.map +1 -1
  268. package/dist/admin/translations/en.json.mjs +6 -2
  269. package/dist/admin/translations/en.json.mjs.map +1 -1
  270. package/dist/admin/translations/es.json.js +1 -1
  271. package/dist/admin/translations/es.json.mjs +1 -1
  272. package/dist/admin/translations/fr.json.js +1 -1
  273. package/dist/admin/translations/fr.json.mjs +1 -1
  274. package/dist/admin/translations/ja.json.js +1 -1
  275. package/dist/admin/translations/ja.json.mjs +1 -1
  276. package/dist/admin/translations/ko.json.js +1 -1
  277. package/dist/admin/translations/ko.json.mjs +1 -1
  278. package/dist/admin/translations/nl.json.js +1 -1
  279. package/dist/admin/translations/nl.json.mjs +1 -1
  280. package/dist/admin/translations/pl.json.js +1 -1
  281. package/dist/admin/translations/pl.json.mjs +1 -1
  282. package/dist/admin/translations/pt-BR.json.js +1 -1
  283. package/dist/admin/translations/pt-BR.json.mjs +1 -1
  284. package/dist/admin/translations/ru.json.js +1 -1
  285. package/dist/admin/translations/ru.json.mjs +1 -1
  286. package/dist/admin/translations/sk.json.js +1 -1
  287. package/dist/admin/translations/sk.json.mjs +1 -1
  288. package/dist/admin/translations/tr.json.js +1 -1
  289. package/dist/admin/translations/tr.json.mjs +1 -1
  290. package/dist/admin/translations/uk.json.js +1 -1
  291. package/dist/admin/translations/uk.json.mjs +1 -1
  292. package/dist/admin/translations/zh-Hans.json.js +1 -1
  293. package/dist/admin/translations/zh-Hans.json.mjs +1 -1
  294. package/dist/admin/translations/zh.json.js +1 -1
  295. package/dist/admin/translations/zh.json.mjs +1 -1
  296. package/dist/admin/utils/getFolderURL.js +2 -1
  297. package/dist/admin/utils/getFolderURL.js.map +1 -1
  298. package/dist/admin/utils/getFolderURL.mjs +2 -1
  299. package/dist/admin/utils/getFolderURL.mjs.map +1 -1
  300. package/dist/admin/utils/rawFileToAsset.js +5 -3
  301. package/dist/admin/utils/rawFileToAsset.js.map +1 -1
  302. package/dist/admin/utils/rawFileToAsset.mjs +5 -3
  303. package/dist/admin/utils/rawFileToAsset.mjs.map +1 -1
  304. package/dist/admin/utils/resolveFileMime.js +69 -0
  305. package/dist/admin/utils/resolveFileMime.js.map +1 -0
  306. package/dist/admin/utils/resolveFileMime.mjs +64 -0
  307. package/dist/admin/utils/resolveFileMime.mjs.map +1 -0
  308. package/dist/admin/utils/urlsToAssets.js +8 -4
  309. package/dist/admin/utils/urlsToAssets.js.map +1 -1
  310. package/dist/admin/utils/urlsToAssets.mjs +8 -4
  311. package/dist/admin/utils/urlsToAssets.mjs.map +1 -1
  312. package/dist/server/services/ai-metadata.js +25 -5
  313. package/dist/server/services/ai-metadata.js.map +1 -1
  314. package/dist/server/services/ai-metadata.mjs +1 -1
  315. package/dist/server/services/ai-metadata.mjs.map +1 -1
  316. package/dist/server/services/upload.js +4 -0
  317. package/dist/server/services/upload.js.map +1 -1
  318. package/dist/server/services/upload.mjs +4 -0
  319. package/dist/server/services/upload.mjs.map +1 -1
  320. package/dist/server/src/services/upload.d.ts.map +1 -1
  321. package/package.json +7 -7
  322. package/dist/admin/future/utils/getBulkMoveErrorMessage.js +0 -23
  323. package/dist/admin/future/utils/getBulkMoveErrorMessage.js.map +0 -1
  324. package/dist/admin/future/utils/getBulkMoveErrorMessage.mjs +0 -21
  325. package/dist/admin/future/utils/getBulkMoveErrorMessage.mjs.map +0 -1
  326. package/dist/admin/src/future/utils/getBulkMoveErrorMessage.d.ts +0 -1
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ var strapiAdmin = require('@strapi/admin/strapi-admin');
4
+
5
+ /** Name of the parameter to look for in the URL to open the asset details drawer. */ const ASSET_DETAILS_URL_PARAM = 'assetId';
6
+ /**
7
+ * The asset id in the URL, or `null` when the param is absent or unparseable.
8
+ *
9
+ * One definition shared by the drawer (which needs the id) and the "is it open?"
10
+ * hook below, so the two can never disagree about what counts as open.
11
+ */ const parseAssetDetailsId = (raw)=>{
12
+ const parsed = raw ? parseInt(raw, 10) : NaN;
13
+ return Number.isNaN(parsed) ? null : parsed;
14
+ };
15
+ /**
16
+ * Whether the asset details drawer is open, derived purely from the URL.
17
+ *
18
+ * Deliberately separate from `useAssetDetailsParam`, which additionally owns the
19
+ * drawer's mount lifecycle: its `shouldRenderDrawer` only stays correct in the
20
+ * component wired to the close animation, so a second caller would strand it at
21
+ * `true`. Anything that just needs the answer reads this instead.
22
+ */ const useIsAssetDetailsOpen = ()=>{
23
+ const [{ query }] = strapiAdmin.useQueryParams();
24
+ return parseAssetDetailsId(query?.[ASSET_DETAILS_URL_PARAM]) !== null;
25
+ };
26
+
27
+ exports.ASSET_DETAILS_URL_PARAM = ASSET_DETAILS_URL_PARAM;
28
+ exports.parseAssetDetailsId = parseAssetDetailsId;
29
+ exports.useIsAssetDetailsOpen = useIsAssetDetailsOpen;
30
+ //# sourceMappingURL=useIsAssetDetailsOpen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIsAssetDetailsOpen.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useIsAssetDetailsOpen.ts"],"sourcesContent":["import { useQueryParams } from '@strapi/admin/strapi-admin';\n\n/** Name of the parameter to look for in the URL to open the asset details drawer. */\nexport const ASSET_DETAILS_URL_PARAM = 'assetId';\n\n/**\n * The asset id in the URL, or `null` when the param is absent or unparseable.\n *\n * One definition shared by the drawer (which needs the id) and the \"is it open?\"\n * hook below, so the two can never disagree about what counts as open.\n */\nexport const parseAssetDetailsId = (raw: string | undefined): number | null => {\n const parsed = raw ? parseInt(raw, 10) : NaN;\n\n return Number.isNaN(parsed) ? null : parsed;\n};\n\n/**\n * Whether the asset details drawer is open, derived purely from the URL.\n *\n * Deliberately separate from `useAssetDetailsParam`, which additionally owns the\n * drawer's mount lifecycle: its `shouldRenderDrawer` only stays correct in the\n * component wired to the close animation, so a second caller would strand it at\n * `true`. Anything that just needs the answer reads this instead.\n */\nexport const useIsAssetDetailsOpen = (): boolean => {\n const [{ query }] = useQueryParams<{ [ASSET_DETAILS_URL_PARAM]?: string }>();\n\n return parseAssetDetailsId(query?.[ASSET_DETAILS_URL_PARAM]) !== null;\n};\n"],"names":["ASSET_DETAILS_URL_PARAM","parseAssetDetailsId","raw","parsed","parseInt","NaN","Number","isNaN","useIsAssetDetailsOpen","query","useQueryParams"],"mappings":";;;;AAEA,sFACO,MAAMA,uBAAAA,GAA0B;AAEvC;;;;;IAMO,MAAMC,mBAAAA,GAAsB,CAACC,GAAAA,GAAAA;AAClC,IAAA,MAAMC,MAAAA,GAASD,GAAAA,GAAME,QAAAA,CAASF,GAAAA,EAAK,EAAA,CAAA,GAAMG,GAAAA;AAEzC,IAAA,OAAOC,MAAAA,CAAOC,KAAK,CAACJ,MAAAA,CAAAA,GAAU,IAAA,GAAOA,MAAAA;AACvC;AAEA;;;;;;;UAQaK,qBAAAA,GAAwB,IAAA;AACnC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,0BAAAA,EAAAA;AAEpB,IAAA,OAAOT,mBAAAA,CAAoBQ,KAAAA,GAAQT,uBAAAA,CAAwB,CAAA,KAAM,IAAA;AACnE;;;;;;"}
@@ -0,0 +1,26 @@
1
+ import { useQueryParams } from '@strapi/admin/strapi-admin';
2
+
3
+ /** Name of the parameter to look for in the URL to open the asset details drawer. */ const ASSET_DETAILS_URL_PARAM = 'assetId';
4
+ /**
5
+ * The asset id in the URL, or `null` when the param is absent or unparseable.
6
+ *
7
+ * One definition shared by the drawer (which needs the id) and the "is it open?"
8
+ * hook below, so the two can never disagree about what counts as open.
9
+ */ const parseAssetDetailsId = (raw)=>{
10
+ const parsed = raw ? parseInt(raw, 10) : NaN;
11
+ return Number.isNaN(parsed) ? null : parsed;
12
+ };
13
+ /**
14
+ * Whether the asset details drawer is open, derived purely from the URL.
15
+ *
16
+ * Deliberately separate from `useAssetDetailsParam`, which additionally owns the
17
+ * drawer's mount lifecycle: its `shouldRenderDrawer` only stays correct in the
18
+ * component wired to the close animation, so a second caller would strand it at
19
+ * `true`. Anything that just needs the answer reads this instead.
20
+ */ const useIsAssetDetailsOpen = ()=>{
21
+ const [{ query }] = useQueryParams();
22
+ return parseAssetDetailsId(query?.[ASSET_DETAILS_URL_PARAM]) !== null;
23
+ };
24
+
25
+ export { ASSET_DETAILS_URL_PARAM, parseAssetDetailsId, useIsAssetDetailsOpen };
26
+ //# sourceMappingURL=useIsAssetDetailsOpen.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIsAssetDetailsOpen.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useIsAssetDetailsOpen.ts"],"sourcesContent":["import { useQueryParams } from '@strapi/admin/strapi-admin';\n\n/** Name of the parameter to look for in the URL to open the asset details drawer. */\nexport const ASSET_DETAILS_URL_PARAM = 'assetId';\n\n/**\n * The asset id in the URL, or `null` when the param is absent or unparseable.\n *\n * One definition shared by the drawer (which needs the id) and the \"is it open?\"\n * hook below, so the two can never disagree about what counts as open.\n */\nexport const parseAssetDetailsId = (raw: string | undefined): number | null => {\n const parsed = raw ? parseInt(raw, 10) : NaN;\n\n return Number.isNaN(parsed) ? null : parsed;\n};\n\n/**\n * Whether the asset details drawer is open, derived purely from the URL.\n *\n * Deliberately separate from `useAssetDetailsParam`, which additionally owns the\n * drawer's mount lifecycle: its `shouldRenderDrawer` only stays correct in the\n * component wired to the close animation, so a second caller would strand it at\n * `true`. Anything that just needs the answer reads this instead.\n */\nexport const useIsAssetDetailsOpen = (): boolean => {\n const [{ query }] = useQueryParams<{ [ASSET_DETAILS_URL_PARAM]?: string }>();\n\n return parseAssetDetailsId(query?.[ASSET_DETAILS_URL_PARAM]) !== null;\n};\n"],"names":["ASSET_DETAILS_URL_PARAM","parseAssetDetailsId","raw","parsed","parseInt","NaN","Number","isNaN","useIsAssetDetailsOpen","query","useQueryParams"],"mappings":";;AAEA,sFACO,MAAMA,uBAAAA,GAA0B;AAEvC;;;;;IAMO,MAAMC,mBAAAA,GAAsB,CAACC,GAAAA,GAAAA;AAClC,IAAA,MAAMC,MAAAA,GAASD,GAAAA,GAAME,QAAAA,CAASF,GAAAA,EAAK,EAAA,CAAA,GAAMG,GAAAA;AAEzC,IAAA,OAAOC,MAAAA,CAAOC,KAAK,CAACJ,MAAAA,CAAAA,GAAU,IAAA,GAAOA,MAAAA;AACvC;AAEA;;;;;;;UAQaK,qBAAAA,GAAwB,IAAA;AACnC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,cAAAA,EAAAA;AAEpB,IAAA,OAAOT,mBAAAA,CAAoBQ,KAAAA,GAAQT,uBAAAA,CAAwB,CAAA,KAAM,IAAA;AACnE;;;;"}
@@ -138,13 +138,13 @@ const useListFilters = ()=>{
138
138
  // URL stays shareable and refresh-safe either way.
139
139
  const writeFilters = (next)=>{
140
140
  if (next.length === 0) {
141
- setQuery({
142
- filters: ''
143
- }, 'remove', true);
141
+ setQuery(strapiAdmin.withEncodedUserParams(query, {
142
+ filters: undefined
143
+ }), 'push', true);
144
144
  } else {
145
- setQuery({
145
+ setQuery(strapiAdmin.withEncodedUserParams(query, {
146
146
  filters: serializeFilters(next)
147
- }, 'push', true);
147
+ }), 'push', true);
148
148
  }
149
149
  };
150
150
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"useListFilters.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListFilters.ts"],"sourcesContent":["import { useQueryParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Filter state for the assets list, backed by a single `?filters=` query param\n * so the applied filters are shareable and survive Back/refresh.\n *\n * Serialized grammar — badges separated by `;`, one badge is\n * `<field>:<condition>:<value>`:\n *\n * - `type:is:picture,audio` / `type:not:folder`\n * - `created:within:1week` / `updated:notwithin:3months` / `created:exact:1day`\n * - `created:rangeis:2024-01-01..2024-04-07` / `created:rangenot:...`\n *\n * Relative presets stay relative in the URL on purpose: the badge label\n * (\"1 week ago\") remains truthful forever and a shared link means \"within the\n * last week from whenever it is opened\". Resolution to absolute timestamps\n * happens at query-build time (see utils/buildAssetFilters.ts).\n *\n * Several badges for the same field are allowed, even contradictory ones —\n * everything is combined with AND; contradictions simply produce the filtered\n * empty state.\n */\n\nexport const TYPE_VALUES = ['folder', 'picture', 'audio', 'video', 'document'] as const;\nexport type TypeValue = (typeof TYPE_VALUES)[number];\n\nexport const DATE_PRESETS = [\n '1day',\n '3days',\n '1week',\n '1month',\n '3months',\n '6months',\n '1year',\n] as const;\nexport type DatePreset = (typeof DATE_PRESETS)[number];\n\nexport type TypeCondition = 'is' | 'isNot';\nexport type PresetCondition = 'isExactly' | 'withinLast' | 'notWithinLast';\nexport type RangeCondition = 'is' | 'isNot';\n\nexport type DateField = 'createdAt' | 'updatedAt';\n\nexport interface TypeFilter {\n kind: 'type';\n condition: TypeCondition;\n values: TypeValue[];\n}\n\nexport interface DatePresetFilter {\n kind: 'date';\n field: DateField;\n mode: 'preset';\n condition: PresetCondition;\n preset: DatePreset;\n}\n\nexport interface DateRangeFilter {\n kind: 'date';\n field: DateField;\n mode: 'range';\n condition: RangeCondition;\n /** Calendar dates, `YYYY-MM-DD`, inclusive on both ends. */\n from: string;\n to: string;\n}\n\nexport type DateFilter = DatePresetFilter | DateRangeFilter;\nexport type ListFilter = TypeFilter | DateFilter;\n\nconst FIELD_TOKENS: Record<string, DateField> = {\n created: 'createdAt',\n updated: 'updatedAt',\n};\nconst FIELD_TOKEN_BY_FIELD: Record<DateField, string> = {\n createdAt: 'created',\n updatedAt: 'updated',\n};\n\nconst PRESET_CONDITION_TOKENS: Record<string, PresetCondition> = {\n exact: 'isExactly',\n within: 'withinLast',\n notwithin: 'notWithinLast',\n};\nconst PRESET_TOKEN_BY_CONDITION: Record<PresetCondition, string> = {\n isExactly: 'exact',\n withinLast: 'within',\n notWithinLast: 'notwithin',\n};\n\nconst RANGE_CONDITION_TOKENS: Record<string, RangeCondition> = {\n rangeis: 'is',\n rangenot: 'isNot',\n};\nconst RANGE_TOKEN_BY_CONDITION: Record<RangeCondition, string> = {\n is: 'rangeis',\n isNot: 'rangenot',\n};\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isTypeValue = (value: string): value is TypeValue =>\n (TYPE_VALUES as readonly string[]).includes(value);\n\nconst isDatePreset = (value: string): value is DatePreset =>\n (DATE_PRESETS as readonly string[]).includes(value);\n\nconst parseBadge = (raw: string): ListFilter | null => {\n const [field, condition, value] = raw.split(':');\n\n if (!field || !condition || !value) {\n return null;\n }\n\n if (field === 'type') {\n if (condition !== 'is' && condition !== 'not') {\n return null;\n }\n const values = value.split(',').filter(isTypeValue);\n\n return values.length > 0\n ? { kind: 'type', condition: condition === 'is' ? 'is' : 'isNot', values }\n : null;\n }\n\n const dateField = FIELD_TOKENS[field];\n if (!dateField) {\n return null;\n }\n\n if (condition in PRESET_CONDITION_TOKENS) {\n return isDatePreset(value)\n ? {\n kind: 'date',\n field: dateField,\n mode: 'preset',\n condition: PRESET_CONDITION_TOKENS[condition],\n preset: value,\n }\n : null;\n }\n\n if (condition in RANGE_CONDITION_TOKENS) {\n const [from, to] = value.split('..');\n\n return DATE_RE.test(from ?? '') && DATE_RE.test(to ?? '')\n ? {\n kind: 'date',\n field: dateField,\n mode: 'range',\n condition: RANGE_CONDITION_TOKENS[condition],\n from,\n to,\n }\n : null;\n }\n\n return null;\n};\n\n/**\n * `unknown` on purpose: the query string is user-editable, and qs parses\n * shapes like `?filters[]=a&filters[]=b` into arrays — the guard is what\n * makes the caller's `?filters=` type an assertion rather than a crash.\n */\nexport const parseFiltersParam = (raw: unknown): ListFilter[] => {\n if (typeof raw !== 'string' || raw === '') {\n return [];\n }\n\n return raw\n .split(';')\n .map(parseBadge)\n .filter((filter): filter is ListFilter => filter !== null);\n};\n\nconst serializeBadge = (filter: ListFilter): string => {\n if (filter.kind === 'type') {\n return `type:${filter.condition === 'is' ? 'is' : 'not'}:${filter.values.join(',')}`;\n }\n\n const field = FIELD_TOKEN_BY_FIELD[filter.field];\n\n if (filter.mode === 'preset') {\n return `${field}:${PRESET_TOKEN_BY_CONDITION[filter.condition]}:${filter.preset}`;\n }\n\n return `${field}:${RANGE_TOKEN_BY_CONDITION[filter.condition]}:${filter.from}..${filter.to}`;\n};\n\nexport const serializeFilters = (filters: ListFilter[]): string =>\n filters.map(serializeBadge).join(';');\n\nexport interface ListFilters {\n filters: ListFilter[];\n /** Serialized form — stable fingerprint for list-identity (selection reset). */\n serialized: string;\n addFilter: (filter: ListFilter) => void;\n /** Replaces the badge at `index` (condition or value edited in place). */\n updateFilter: (index: number, filter: ListFilter) => void;\n removeFilter: (index: number) => void;\n clearFilters: () => void;\n}\n\nexport const useListFilters = (): ListFilters => {\n const [{ query }, setQuery] = useQueryParams<{ filters?: string }>();\n\n const filters = parseFiltersParam(query?.filters);\n\n // `replace: true` on every write: filters are edited in rapid micro-steps\n // (each checkbox of the keep-open Type submenu, each badge segment edit), so\n // pushing would turn one logical change into a stack of Back entries. The\n // URL stays shareable and refresh-safe either way.\n const writeFilters = (next: ListFilter[]) => {\n if (next.length === 0) {\n setQuery({ filters: '' }, 'remove', true);\n } else {\n setQuery({ filters: serializeFilters(next) }, 'push', true);\n }\n };\n\n return {\n filters,\n serialized: serializeFilters(filters),\n addFilter: (filter) => writeFilters([...filters, filter]),\n updateFilter: (index, filter) =>\n writeFilters(filters.map((f, i) => (i === index ? filter : f))),\n removeFilter: (index) => writeFilters(filters.filter((_, i) => i !== index)),\n clearFilters: () => writeFilters([]),\n };\n};\n"],"names":["TYPE_VALUES","DATE_PRESETS","FIELD_TOKENS","created","updated","FIELD_TOKEN_BY_FIELD","createdAt","updatedAt","PRESET_CONDITION_TOKENS","exact","within","notwithin","PRESET_TOKEN_BY_CONDITION","isExactly","withinLast","notWithinLast","RANGE_CONDITION_TOKENS","rangeis","rangenot","RANGE_TOKEN_BY_CONDITION","is","isNot","DATE_RE","isTypeValue","value","includes","isDatePreset","parseBadge","raw","field","condition","split","values","filter","length","kind","dateField","mode","preset","from","to","test","parseFiltersParam","map","serializeBadge","join","serializeFilters","filters","useListFilters","query","setQuery","useQueryParams","writeFilters","next","serialized","addFilter","updateFilter","index","f","i","removeFilter","_","clearFilters"],"mappings":"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"}
1
+ {"version":3,"file":"useListFilters.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListFilters.ts"],"sourcesContent":["import { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Filter state for the assets list, backed by a single `?filters=` query param\n * so the applied filters are shareable and survive Back/refresh.\n *\n * Serialized grammar — badges separated by `;`, one badge is\n * `<field>:<condition>:<value>`:\n *\n * - `type:is:picture,audio` / `type:not:folder`\n * - `created:within:1week` / `updated:notwithin:3months` / `created:exact:1day`\n * - `created:rangeis:2024-01-01..2024-04-07` / `created:rangenot:...`\n *\n * Relative presets stay relative in the URL on purpose: the badge label\n * (\"1 week ago\") remains truthful forever and a shared link means \"within the\n * last week from whenever it is opened\". Resolution to absolute timestamps\n * happens at query-build time (see utils/buildAssetFilters.ts).\n *\n * Several badges for the same field are allowed, even contradictory ones —\n * everything is combined with AND; contradictions simply produce the filtered\n * empty state.\n */\n\nexport const TYPE_VALUES = ['folder', 'picture', 'audio', 'video', 'document'] as const;\nexport type TypeValue = (typeof TYPE_VALUES)[number];\n\nexport const DATE_PRESETS = [\n '1day',\n '3days',\n '1week',\n '1month',\n '3months',\n '6months',\n '1year',\n] as const;\nexport type DatePreset = (typeof DATE_PRESETS)[number];\n\nexport type TypeCondition = 'is' | 'isNot';\nexport type PresetCondition = 'isExactly' | 'withinLast' | 'notWithinLast';\nexport type RangeCondition = 'is' | 'isNot';\n\nexport type DateField = 'createdAt' | 'updatedAt';\n\nexport interface TypeFilter {\n kind: 'type';\n condition: TypeCondition;\n values: TypeValue[];\n}\n\nexport interface DatePresetFilter {\n kind: 'date';\n field: DateField;\n mode: 'preset';\n condition: PresetCondition;\n preset: DatePreset;\n}\n\nexport interface DateRangeFilter {\n kind: 'date';\n field: DateField;\n mode: 'range';\n condition: RangeCondition;\n /** Calendar dates, `YYYY-MM-DD`, inclusive on both ends. */\n from: string;\n to: string;\n}\n\nexport type DateFilter = DatePresetFilter | DateRangeFilter;\nexport type ListFilter = TypeFilter | DateFilter;\n\nconst FIELD_TOKENS: Record<string, DateField> = {\n created: 'createdAt',\n updated: 'updatedAt',\n};\nconst FIELD_TOKEN_BY_FIELD: Record<DateField, string> = {\n createdAt: 'created',\n updatedAt: 'updated',\n};\n\nconst PRESET_CONDITION_TOKENS: Record<string, PresetCondition> = {\n exact: 'isExactly',\n within: 'withinLast',\n notwithin: 'notWithinLast',\n};\nconst PRESET_TOKEN_BY_CONDITION: Record<PresetCondition, string> = {\n isExactly: 'exact',\n withinLast: 'within',\n notWithinLast: 'notwithin',\n};\n\nconst RANGE_CONDITION_TOKENS: Record<string, RangeCondition> = {\n rangeis: 'is',\n rangenot: 'isNot',\n};\nconst RANGE_TOKEN_BY_CONDITION: Record<RangeCondition, string> = {\n is: 'rangeis',\n isNot: 'rangenot',\n};\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isTypeValue = (value: string): value is TypeValue =>\n (TYPE_VALUES as readonly string[]).includes(value);\n\nconst isDatePreset = (value: string): value is DatePreset =>\n (DATE_PRESETS as readonly string[]).includes(value);\n\nconst parseBadge = (raw: string): ListFilter | null => {\n const [field, condition, value] = raw.split(':');\n\n if (!field || !condition || !value) {\n return null;\n }\n\n if (field === 'type') {\n if (condition !== 'is' && condition !== 'not') {\n return null;\n }\n const values = value.split(',').filter(isTypeValue);\n\n return values.length > 0\n ? { kind: 'type', condition: condition === 'is' ? 'is' : 'isNot', values }\n : null;\n }\n\n const dateField = FIELD_TOKENS[field];\n if (!dateField) {\n return null;\n }\n\n if (condition in PRESET_CONDITION_TOKENS) {\n return isDatePreset(value)\n ? {\n kind: 'date',\n field: dateField,\n mode: 'preset',\n condition: PRESET_CONDITION_TOKENS[condition],\n preset: value,\n }\n : null;\n }\n\n if (condition in RANGE_CONDITION_TOKENS) {\n const [from, to] = value.split('..');\n\n return DATE_RE.test(from ?? '') && DATE_RE.test(to ?? '')\n ? {\n kind: 'date',\n field: dateField,\n mode: 'range',\n condition: RANGE_CONDITION_TOKENS[condition],\n from,\n to,\n }\n : null;\n }\n\n return null;\n};\n\n/**\n * `unknown` on purpose: the query string is user-editable, and qs parses\n * shapes like `?filters[]=a&filters[]=b` into arrays — the guard is what\n * makes the caller's `?filters=` type an assertion rather than a crash.\n */\nexport const parseFiltersParam = (raw: unknown): ListFilter[] => {\n if (typeof raw !== 'string' || raw === '') {\n return [];\n }\n\n return raw\n .split(';')\n .map(parseBadge)\n .filter((filter): filter is ListFilter => filter !== null);\n};\n\nconst serializeBadge = (filter: ListFilter): string => {\n if (filter.kind === 'type') {\n return `type:${filter.condition === 'is' ? 'is' : 'not'}:${filter.values.join(',')}`;\n }\n\n const field = FIELD_TOKEN_BY_FIELD[filter.field];\n\n if (filter.mode === 'preset') {\n return `${field}:${PRESET_TOKEN_BY_CONDITION[filter.condition]}:${filter.preset}`;\n }\n\n return `${field}:${RANGE_TOKEN_BY_CONDITION[filter.condition]}:${filter.from}..${filter.to}`;\n};\n\nexport const serializeFilters = (filters: ListFilter[]): string =>\n filters.map(serializeBadge).join(';');\n\nexport interface ListFilters {\n filters: ListFilter[];\n /** Serialized form — stable fingerprint for list-identity (selection reset). */\n serialized: string;\n addFilter: (filter: ListFilter) => void;\n /** Replaces the badge at `index` (condition or value edited in place). */\n updateFilter: (index: number, filter: ListFilter) => void;\n removeFilter: (index: number) => void;\n clearFilters: () => void;\n}\n\nexport const useListFilters = (): ListFilters => {\n const [{ query }, setQuery] = useQueryParams<{ filters?: string; _q?: string }>();\n\n const filters = parseFiltersParam(query?.filters);\n\n // `replace: true` on every write: filters are edited in rapid micro-steps\n // (each checkbox of the keep-open Type submenu, each badge segment edit), so\n // pushing would turn one logical change into a stack of Back entries. The\n // URL stays shareable and refresh-safe either way.\n const writeFilters = (next: ListFilter[]) => {\n if (next.length === 0) {\n setQuery(withEncodedUserParams(query, { filters: undefined }), 'push', true);\n } else {\n setQuery(withEncodedUserParams(query, { filters: serializeFilters(next) }), 'push', true);\n }\n };\n\n return {\n filters,\n serialized: serializeFilters(filters),\n addFilter: (filter) => writeFilters([...filters, filter]),\n updateFilter: (index, filter) =>\n writeFilters(filters.map((f, i) => (i === index ? filter : f))),\n removeFilter: (index) => writeFilters(filters.filter((_, i) => i !== index)),\n clearFilters: () => writeFilters([]),\n };\n};\n"],"names":["TYPE_VALUES","DATE_PRESETS","FIELD_TOKENS","created","updated","FIELD_TOKEN_BY_FIELD","createdAt","updatedAt","PRESET_CONDITION_TOKENS","exact","within","notwithin","PRESET_TOKEN_BY_CONDITION","isExactly","withinLast","notWithinLast","RANGE_CONDITION_TOKENS","rangeis","rangenot","RANGE_TOKEN_BY_CONDITION","is","isNot","DATE_RE","isTypeValue","value","includes","isDatePreset","parseBadge","raw","field","condition","split","values","filter","length","kind","dateField","mode","preset","from","to","test","parseFiltersParam","map","serializeBadge","join","serializeFilters","filters","useListFilters","query","setQuery","useQueryParams","writeFilters","next","withEncodedUserParams","undefined","serialized","addFilter","updateFilter","index","f","i","removeFilter","_","clearFilters"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { useQueryParams } from '@strapi/admin/strapi-admin';
1
+ import { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';
2
2
 
3
3
  /**
4
4
  * Filter state for the assets list, backed by a single `?filters=` query param
@@ -136,13 +136,13 @@ const useListFilters = ()=>{
136
136
  // URL stays shareable and refresh-safe either way.
137
137
  const writeFilters = (next)=>{
138
138
  if (next.length === 0) {
139
- setQuery({
140
- filters: ''
141
- }, 'remove', true);
139
+ setQuery(withEncodedUserParams(query, {
140
+ filters: undefined
141
+ }), 'push', true);
142
142
  } else {
143
- setQuery({
143
+ setQuery(withEncodedUserParams(query, {
144
144
  filters: serializeFilters(next)
145
- }, 'push', true);
145
+ }), 'push', true);
146
146
  }
147
147
  };
148
148
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"useListFilters.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListFilters.ts"],"sourcesContent":["import { useQueryParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Filter state for the assets list, backed by a single `?filters=` query param\n * so the applied filters are shareable and survive Back/refresh.\n *\n * Serialized grammar — badges separated by `;`, one badge is\n * `<field>:<condition>:<value>`:\n *\n * - `type:is:picture,audio` / `type:not:folder`\n * - `created:within:1week` / `updated:notwithin:3months` / `created:exact:1day`\n * - `created:rangeis:2024-01-01..2024-04-07` / `created:rangenot:...`\n *\n * Relative presets stay relative in the URL on purpose: the badge label\n * (\"1 week ago\") remains truthful forever and a shared link means \"within the\n * last week from whenever it is opened\". Resolution to absolute timestamps\n * happens at query-build time (see utils/buildAssetFilters.ts).\n *\n * Several badges for the same field are allowed, even contradictory ones —\n * everything is combined with AND; contradictions simply produce the filtered\n * empty state.\n */\n\nexport const TYPE_VALUES = ['folder', 'picture', 'audio', 'video', 'document'] as const;\nexport type TypeValue = (typeof TYPE_VALUES)[number];\n\nexport const DATE_PRESETS = [\n '1day',\n '3days',\n '1week',\n '1month',\n '3months',\n '6months',\n '1year',\n] as const;\nexport type DatePreset = (typeof DATE_PRESETS)[number];\n\nexport type TypeCondition = 'is' | 'isNot';\nexport type PresetCondition = 'isExactly' | 'withinLast' | 'notWithinLast';\nexport type RangeCondition = 'is' | 'isNot';\n\nexport type DateField = 'createdAt' | 'updatedAt';\n\nexport interface TypeFilter {\n kind: 'type';\n condition: TypeCondition;\n values: TypeValue[];\n}\n\nexport interface DatePresetFilter {\n kind: 'date';\n field: DateField;\n mode: 'preset';\n condition: PresetCondition;\n preset: DatePreset;\n}\n\nexport interface DateRangeFilter {\n kind: 'date';\n field: DateField;\n mode: 'range';\n condition: RangeCondition;\n /** Calendar dates, `YYYY-MM-DD`, inclusive on both ends. */\n from: string;\n to: string;\n}\n\nexport type DateFilter = DatePresetFilter | DateRangeFilter;\nexport type ListFilter = TypeFilter | DateFilter;\n\nconst FIELD_TOKENS: Record<string, DateField> = {\n created: 'createdAt',\n updated: 'updatedAt',\n};\nconst FIELD_TOKEN_BY_FIELD: Record<DateField, string> = {\n createdAt: 'created',\n updatedAt: 'updated',\n};\n\nconst PRESET_CONDITION_TOKENS: Record<string, PresetCondition> = {\n exact: 'isExactly',\n within: 'withinLast',\n notwithin: 'notWithinLast',\n};\nconst PRESET_TOKEN_BY_CONDITION: Record<PresetCondition, string> = {\n isExactly: 'exact',\n withinLast: 'within',\n notWithinLast: 'notwithin',\n};\n\nconst RANGE_CONDITION_TOKENS: Record<string, RangeCondition> = {\n rangeis: 'is',\n rangenot: 'isNot',\n};\nconst RANGE_TOKEN_BY_CONDITION: Record<RangeCondition, string> = {\n is: 'rangeis',\n isNot: 'rangenot',\n};\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isTypeValue = (value: string): value is TypeValue =>\n (TYPE_VALUES as readonly string[]).includes(value);\n\nconst isDatePreset = (value: string): value is DatePreset =>\n (DATE_PRESETS as readonly string[]).includes(value);\n\nconst parseBadge = (raw: string): ListFilter | null => {\n const [field, condition, value] = raw.split(':');\n\n if (!field || !condition || !value) {\n return null;\n }\n\n if (field === 'type') {\n if (condition !== 'is' && condition !== 'not') {\n return null;\n }\n const values = value.split(',').filter(isTypeValue);\n\n return values.length > 0\n ? { kind: 'type', condition: condition === 'is' ? 'is' : 'isNot', values }\n : null;\n }\n\n const dateField = FIELD_TOKENS[field];\n if (!dateField) {\n return null;\n }\n\n if (condition in PRESET_CONDITION_TOKENS) {\n return isDatePreset(value)\n ? {\n kind: 'date',\n field: dateField,\n mode: 'preset',\n condition: PRESET_CONDITION_TOKENS[condition],\n preset: value,\n }\n : null;\n }\n\n if (condition in RANGE_CONDITION_TOKENS) {\n const [from, to] = value.split('..');\n\n return DATE_RE.test(from ?? '') && DATE_RE.test(to ?? '')\n ? {\n kind: 'date',\n field: dateField,\n mode: 'range',\n condition: RANGE_CONDITION_TOKENS[condition],\n from,\n to,\n }\n : null;\n }\n\n return null;\n};\n\n/**\n * `unknown` on purpose: the query string is user-editable, and qs parses\n * shapes like `?filters[]=a&filters[]=b` into arrays — the guard is what\n * makes the caller's `?filters=` type an assertion rather than a crash.\n */\nexport const parseFiltersParam = (raw: unknown): ListFilter[] => {\n if (typeof raw !== 'string' || raw === '') {\n return [];\n }\n\n return raw\n .split(';')\n .map(parseBadge)\n .filter((filter): filter is ListFilter => filter !== null);\n};\n\nconst serializeBadge = (filter: ListFilter): string => {\n if (filter.kind === 'type') {\n return `type:${filter.condition === 'is' ? 'is' : 'not'}:${filter.values.join(',')}`;\n }\n\n const field = FIELD_TOKEN_BY_FIELD[filter.field];\n\n if (filter.mode === 'preset') {\n return `${field}:${PRESET_TOKEN_BY_CONDITION[filter.condition]}:${filter.preset}`;\n }\n\n return `${field}:${RANGE_TOKEN_BY_CONDITION[filter.condition]}:${filter.from}..${filter.to}`;\n};\n\nexport const serializeFilters = (filters: ListFilter[]): string =>\n filters.map(serializeBadge).join(';');\n\nexport interface ListFilters {\n filters: ListFilter[];\n /** Serialized form — stable fingerprint for list-identity (selection reset). */\n serialized: string;\n addFilter: (filter: ListFilter) => void;\n /** Replaces the badge at `index` (condition or value edited in place). */\n updateFilter: (index: number, filter: ListFilter) => void;\n removeFilter: (index: number) => void;\n clearFilters: () => void;\n}\n\nexport const useListFilters = (): ListFilters => {\n const [{ query }, setQuery] = useQueryParams<{ filters?: string }>();\n\n const filters = parseFiltersParam(query?.filters);\n\n // `replace: true` on every write: filters are edited in rapid micro-steps\n // (each checkbox of the keep-open Type submenu, each badge segment edit), so\n // pushing would turn one logical change into a stack of Back entries. The\n // URL stays shareable and refresh-safe either way.\n const writeFilters = (next: ListFilter[]) => {\n if (next.length === 0) {\n setQuery({ filters: '' }, 'remove', true);\n } else {\n setQuery({ filters: serializeFilters(next) }, 'push', true);\n }\n };\n\n return {\n filters,\n serialized: serializeFilters(filters),\n addFilter: (filter) => writeFilters([...filters, filter]),\n updateFilter: (index, filter) =>\n writeFilters(filters.map((f, i) => (i === index ? filter : f))),\n removeFilter: (index) => writeFilters(filters.filter((_, i) => i !== index)),\n clearFilters: () => writeFilters([]),\n };\n};\n"],"names":["TYPE_VALUES","DATE_PRESETS","FIELD_TOKENS","created","updated","FIELD_TOKEN_BY_FIELD","createdAt","updatedAt","PRESET_CONDITION_TOKENS","exact","within","notwithin","PRESET_TOKEN_BY_CONDITION","isExactly","withinLast","notWithinLast","RANGE_CONDITION_TOKENS","rangeis","rangenot","RANGE_TOKEN_BY_CONDITION","is","isNot","DATE_RE","isTypeValue","value","includes","isDatePreset","parseBadge","raw","field","condition","split","values","filter","length","kind","dateField","mode","preset","from","to","test","parseFiltersParam","map","serializeBadge","join","serializeFilters","filters","useListFilters","query","setQuery","useQueryParams","writeFilters","next","serialized","addFilter","updateFilter","index","f","i","removeFilter","_","clearFilters"],"mappings":"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"}
1
+ {"version":3,"file":"useListFilters.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListFilters.ts"],"sourcesContent":["import { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Filter state for the assets list, backed by a single `?filters=` query param\n * so the applied filters are shareable and survive Back/refresh.\n *\n * Serialized grammar — badges separated by `;`, one badge is\n * `<field>:<condition>:<value>`:\n *\n * - `type:is:picture,audio` / `type:not:folder`\n * - `created:within:1week` / `updated:notwithin:3months` / `created:exact:1day`\n * - `created:rangeis:2024-01-01..2024-04-07` / `created:rangenot:...`\n *\n * Relative presets stay relative in the URL on purpose: the badge label\n * (\"1 week ago\") remains truthful forever and a shared link means \"within the\n * last week from whenever it is opened\". Resolution to absolute timestamps\n * happens at query-build time (see utils/buildAssetFilters.ts).\n *\n * Several badges for the same field are allowed, even contradictory ones —\n * everything is combined with AND; contradictions simply produce the filtered\n * empty state.\n */\n\nexport const TYPE_VALUES = ['folder', 'picture', 'audio', 'video', 'document'] as const;\nexport type TypeValue = (typeof TYPE_VALUES)[number];\n\nexport const DATE_PRESETS = [\n '1day',\n '3days',\n '1week',\n '1month',\n '3months',\n '6months',\n '1year',\n] as const;\nexport type DatePreset = (typeof DATE_PRESETS)[number];\n\nexport type TypeCondition = 'is' | 'isNot';\nexport type PresetCondition = 'isExactly' | 'withinLast' | 'notWithinLast';\nexport type RangeCondition = 'is' | 'isNot';\n\nexport type DateField = 'createdAt' | 'updatedAt';\n\nexport interface TypeFilter {\n kind: 'type';\n condition: TypeCondition;\n values: TypeValue[];\n}\n\nexport interface DatePresetFilter {\n kind: 'date';\n field: DateField;\n mode: 'preset';\n condition: PresetCondition;\n preset: DatePreset;\n}\n\nexport interface DateRangeFilter {\n kind: 'date';\n field: DateField;\n mode: 'range';\n condition: RangeCondition;\n /** Calendar dates, `YYYY-MM-DD`, inclusive on both ends. */\n from: string;\n to: string;\n}\n\nexport type DateFilter = DatePresetFilter | DateRangeFilter;\nexport type ListFilter = TypeFilter | DateFilter;\n\nconst FIELD_TOKENS: Record<string, DateField> = {\n created: 'createdAt',\n updated: 'updatedAt',\n};\nconst FIELD_TOKEN_BY_FIELD: Record<DateField, string> = {\n createdAt: 'created',\n updatedAt: 'updated',\n};\n\nconst PRESET_CONDITION_TOKENS: Record<string, PresetCondition> = {\n exact: 'isExactly',\n within: 'withinLast',\n notwithin: 'notWithinLast',\n};\nconst PRESET_TOKEN_BY_CONDITION: Record<PresetCondition, string> = {\n isExactly: 'exact',\n withinLast: 'within',\n notWithinLast: 'notwithin',\n};\n\nconst RANGE_CONDITION_TOKENS: Record<string, RangeCondition> = {\n rangeis: 'is',\n rangenot: 'isNot',\n};\nconst RANGE_TOKEN_BY_CONDITION: Record<RangeCondition, string> = {\n is: 'rangeis',\n isNot: 'rangenot',\n};\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isTypeValue = (value: string): value is TypeValue =>\n (TYPE_VALUES as readonly string[]).includes(value);\n\nconst isDatePreset = (value: string): value is DatePreset =>\n (DATE_PRESETS as readonly string[]).includes(value);\n\nconst parseBadge = (raw: string): ListFilter | null => {\n const [field, condition, value] = raw.split(':');\n\n if (!field || !condition || !value) {\n return null;\n }\n\n if (field === 'type') {\n if (condition !== 'is' && condition !== 'not') {\n return null;\n }\n const values = value.split(',').filter(isTypeValue);\n\n return values.length > 0\n ? { kind: 'type', condition: condition === 'is' ? 'is' : 'isNot', values }\n : null;\n }\n\n const dateField = FIELD_TOKENS[field];\n if (!dateField) {\n return null;\n }\n\n if (condition in PRESET_CONDITION_TOKENS) {\n return isDatePreset(value)\n ? {\n kind: 'date',\n field: dateField,\n mode: 'preset',\n condition: PRESET_CONDITION_TOKENS[condition],\n preset: value,\n }\n : null;\n }\n\n if (condition in RANGE_CONDITION_TOKENS) {\n const [from, to] = value.split('..');\n\n return DATE_RE.test(from ?? '') && DATE_RE.test(to ?? '')\n ? {\n kind: 'date',\n field: dateField,\n mode: 'range',\n condition: RANGE_CONDITION_TOKENS[condition],\n from,\n to,\n }\n : null;\n }\n\n return null;\n};\n\n/**\n * `unknown` on purpose: the query string is user-editable, and qs parses\n * shapes like `?filters[]=a&filters[]=b` into arrays — the guard is what\n * makes the caller's `?filters=` type an assertion rather than a crash.\n */\nexport const parseFiltersParam = (raw: unknown): ListFilter[] => {\n if (typeof raw !== 'string' || raw === '') {\n return [];\n }\n\n return raw\n .split(';')\n .map(parseBadge)\n .filter((filter): filter is ListFilter => filter !== null);\n};\n\nconst serializeBadge = (filter: ListFilter): string => {\n if (filter.kind === 'type') {\n return `type:${filter.condition === 'is' ? 'is' : 'not'}:${filter.values.join(',')}`;\n }\n\n const field = FIELD_TOKEN_BY_FIELD[filter.field];\n\n if (filter.mode === 'preset') {\n return `${field}:${PRESET_TOKEN_BY_CONDITION[filter.condition]}:${filter.preset}`;\n }\n\n return `${field}:${RANGE_TOKEN_BY_CONDITION[filter.condition]}:${filter.from}..${filter.to}`;\n};\n\nexport const serializeFilters = (filters: ListFilter[]): string =>\n filters.map(serializeBadge).join(';');\n\nexport interface ListFilters {\n filters: ListFilter[];\n /** Serialized form — stable fingerprint for list-identity (selection reset). */\n serialized: string;\n addFilter: (filter: ListFilter) => void;\n /** Replaces the badge at `index` (condition or value edited in place). */\n updateFilter: (index: number, filter: ListFilter) => void;\n removeFilter: (index: number) => void;\n clearFilters: () => void;\n}\n\nexport const useListFilters = (): ListFilters => {\n const [{ query }, setQuery] = useQueryParams<{ filters?: string; _q?: string }>();\n\n const filters = parseFiltersParam(query?.filters);\n\n // `replace: true` on every write: filters are edited in rapid micro-steps\n // (each checkbox of the keep-open Type submenu, each badge segment edit), so\n // pushing would turn one logical change into a stack of Back entries. The\n // URL stays shareable and refresh-safe either way.\n const writeFilters = (next: ListFilter[]) => {\n if (next.length === 0) {\n setQuery(withEncodedUserParams(query, { filters: undefined }), 'push', true);\n } else {\n setQuery(withEncodedUserParams(query, { filters: serializeFilters(next) }), 'push', true);\n }\n };\n\n return {\n filters,\n serialized: serializeFilters(filters),\n addFilter: (filter) => writeFilters([...filters, filter]),\n updateFilter: (index, filter) =>\n writeFilters(filters.map((f, i) => (i === index ? filter : f))),\n removeFilter: (index) => writeFilters(filters.filter((_, i) => i !== index)),\n clearFilters: () => writeFilters([]),\n };\n};\n"],"names":["TYPE_VALUES","DATE_PRESETS","FIELD_TOKENS","created","updated","FIELD_TOKEN_BY_FIELD","createdAt","updatedAt","PRESET_CONDITION_TOKENS","exact","within","notwithin","PRESET_TOKEN_BY_CONDITION","isExactly","withinLast","notWithinLast","RANGE_CONDITION_TOKENS","rangeis","rangenot","RANGE_TOKEN_BY_CONDITION","is","isNot","DATE_RE","isTypeValue","value","includes","isDatePreset","parseBadge","raw","field","condition","split","values","filter","length","kind","dateField","mode","preset","from","to","test","parseFiltersParam","map","serializeBadge","join","serializeFilters","filters","useListFilters","query","setQuery","useQueryParams","writeFilters","next","withEncodedUserParams","undefined","serialized","addFilter","updateFilter","index","f","i","removeFilter","_","clearFilters"],"mappings":"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"}
@@ -74,13 +74,13 @@ const useListSort = ()=>{
74
74
  }
75
75
  const serialized = serializeSort(nextSortBy, nextDirection);
76
76
  if (nextSortBy === DEFAULT_SORT_BY && nextDirection === null) {
77
- setQuery({
78
- sort: ''
79
- }, 'remove');
77
+ setQuery(strapiAdmin.withEncodedUserParams(query, {
78
+ sort: undefined
79
+ }));
80
80
  } else {
81
- setQuery({
81
+ setQuery(strapiAdmin.withEncodedUserParams(query, {
82
82
  sort: serialized
83
- });
83
+ }));
84
84
  }
85
85
  };
86
86
  // Groups are mutually exclusive — picking in one clears the other.
@@ -88,13 +88,13 @@ const useListSort = ()=>{
88
88
  const setDirection = (key)=>writeSort(null, key);
89
89
  const setFoldersPosition = (position)=>{
90
90
  if (position === 'mixed') {
91
- setQuery({
91
+ setQuery(strapiAdmin.withEncodedUserParams(query, {
92
92
  folders: 'mixed'
93
- });
93
+ }));
94
94
  } else {
95
- setQuery({
96
- folders: ''
97
- }, 'remove');
95
+ setQuery(strapiAdmin.withEncodedUserParams(query, {
96
+ folders: undefined
97
+ }));
98
98
  }
99
99
  };
100
100
  const assetsSort = serializeSort(sortBy, direction);
@@ -1 +1 @@
1
- {"version":3,"file":"useListSort.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListSort.ts"],"sourcesContent":["import { useQueryParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Sort state for the assets list, backed by URL query params so the current\n * ordering is shareable and survives Back/refresh:\n *\n * - `?sort=<rule>` — a single whitelisted rule. Omitted → default (most\n * recent updates).\n * - `?folders=mixed` — folders interleaved with files. Omitted → folders on top.\n *\n * The date rules and the direction rules are mutually exclusive: exactly one\n * rule is active at a time (one checkmark total, like the design mock). Picking\n * an option in one group clears the other; clearing the active option falls\n * back to the default — an unsorted list is not a state the UI offers.\n */\n\nexport const SORT_BY_RULES = {\n oldestUploads: 'createdAt:ASC',\n mostRecentUpdates: 'updatedAt:DESC',\n} as const;\n\nexport const SORT_DIRECTION_RULES = {\n nameAsc: 'name:ASC',\n nameDesc: 'name:DESC',\n sizeAsc: 'size:ASC',\n sizeDesc: 'size:DESC',\n} as const;\n\nexport type SortByKey = keyof typeof SORT_BY_RULES;\nexport type SortDirectionKey = keyof typeof SORT_DIRECTION_RULES;\nexport type FoldersPosition = 'top' | 'mixed';\n\nconst DEFAULT_SORT_BY: SortByKey = 'mostRecentUpdates';\n\nconst SORT_BY_BY_RULE = Object.fromEntries(\n Object.entries(SORT_BY_RULES).map(([key, rule]) => [rule, key])\n) as Record<string, SortByKey>;\n\nconst SORT_DIRECTION_BY_RULE = Object.fromEntries(\n Object.entries(SORT_DIRECTION_RULES).map(([key, rule]) => [rule, key])\n) as Record<string, SortDirectionKey>;\n\ninterface ParsedSort {\n sortBy: SortByKey | null;\n direction: SortDirectionKey | null;\n /** False when the state is the fallback default (no recognized rule in the URL). */\n isExplicit: boolean;\n}\n\n/** Parses `?sort=` — the first recognized rule wins, unknown rules are dropped. */\nconst parseSortParam = (raw: string | undefined): ParsedSort => {\n for (const rule of (raw ?? '').split(',')) {\n if (rule in SORT_BY_BY_RULE) {\n return { sortBy: SORT_BY_BY_RULE[rule], direction: null, isExplicit: true };\n }\n if (rule in SORT_DIRECTION_BY_RULE) {\n return { sortBy: null, direction: SORT_DIRECTION_BY_RULE[rule], isExplicit: true };\n }\n }\n\n return { sortBy: DEFAULT_SORT_BY, direction: null, isExplicit: false };\n};\n\nconst serializeSort = (sortBy: SortByKey | null, direction: SortDirectionKey | null): string => {\n const rules: (string | null)[] = [\n sortBy && SORT_BY_RULES[sortBy],\n direction && SORT_DIRECTION_RULES[direction],\n ];\n\n return rules.filter((rule): rule is string => Boolean(rule)).join(',');\n};\n\nexport interface ListSort {\n sortBy: SortByKey | null;\n direction: SortDirectionKey | null;\n foldersPosition: FoldersPosition;\n /**\n * The active rule for the files query, e.g. `updatedAt:DESC` — always a\n * single rule since the sort groups are mutually exclusive.\n */\n assetsSort: string;\n /**\n * Rule applicable to the folders query. In the default state (no sort in the\n * URL) folders keep their alphabetical order so the band matches the sidebar\n * tree; an explicit user sort applies, except size rules (folders have no\n * `size` column), which fall back to alphabetical.\n */\n foldersSort: string;\n setSortBy: (key: SortByKey | null) => void;\n setDirection: (key: SortDirectionKey | null) => void;\n setFoldersPosition: (position: FoldersPosition) => void;\n}\n\nexport const useListSort = (): ListSort => {\n const [{ query }, setQuery] = useQueryParams<{ sort?: string; folders?: string }>();\n\n const { sortBy, direction, isExplicit } = parseSortParam(query?.sort);\n const foldersPosition: FoldersPosition = query?.folders === 'mixed' ? 'mixed' : 'top';\n\n const writeSort = (nextSortBy: SortByKey | null, nextDirection: SortDirectionKey | null) => {\n // Exactly one active rule: clearing the active one falls back to default.\n if (nextSortBy === null && nextDirection === null) {\n nextSortBy = DEFAULT_SORT_BY;\n }\n\n const serialized = serializeSort(nextSortBy, nextDirection);\n\n if (nextSortBy === DEFAULT_SORT_BY && nextDirection === null) {\n setQuery({ sort: '' }, 'remove');\n } else {\n setQuery({ sort: serialized });\n }\n };\n\n // Groups are mutually exclusive — picking in one clears the other.\n const setSortBy = (key: SortByKey | null) => writeSort(key, null);\n const setDirection = (key: SortDirectionKey | null) => writeSort(null, key);\n\n const setFoldersPosition = (position: FoldersPosition) => {\n if (position === 'mixed') {\n setQuery({ folders: 'mixed' });\n } else {\n setQuery({ folders: '' }, 'remove');\n }\n };\n\n const assetsSort = serializeSort(sortBy, direction);\n const folderRuleCandidates: (string | null)[] = [\n sortBy && SORT_BY_RULES[sortBy],\n direction && !direction.startsWith('size') ? SORT_DIRECTION_RULES[direction] : null,\n ];\n const folderRules = folderRuleCandidates.filter((rule): rule is string => Boolean(rule));\n // Default state keeps folders alphabetical (matches the sidebar tree) — only\n // an explicit user sort reorders the folders band.\n const foldersSort = isExplicit && folderRules.length > 0 ? folderRules.join(',') : 'name:ASC';\n\n return {\n sortBy,\n direction,\n foldersPosition,\n assetsSort,\n foldersSort,\n setSortBy,\n setDirection,\n setFoldersPosition,\n };\n};\n"],"names":["SORT_BY_RULES","oldestUploads","mostRecentUpdates","SORT_DIRECTION_RULES","nameAsc","nameDesc","sizeAsc","sizeDesc","DEFAULT_SORT_BY","SORT_BY_BY_RULE","Object","fromEntries","entries","map","key","rule","SORT_DIRECTION_BY_RULE","parseSortParam","raw","split","sortBy","direction","isExplicit","serializeSort","rules","filter","Boolean","join","useListSort","query","setQuery","useQueryParams","sort","foldersPosition","folders","writeSort","nextSortBy","nextDirection","serialized","setSortBy","setDirection","setFoldersPosition","position","assetsSort","folderRuleCandidates","startsWith","folderRules","foldersSort","length"],"mappings":"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"}
1
+ {"version":3,"file":"useListSort.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListSort.ts"],"sourcesContent":["import { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Sort state for the assets list, backed by URL query params so the current\n * ordering is shareable and survives Back/refresh:\n *\n * - `?sort=<rule>` — a single whitelisted rule. Omitted → default (most\n * recent updates).\n * - `?folders=mixed` — folders interleaved with files. Omitted → folders on top.\n *\n * The date rules and the direction rules are mutually exclusive: exactly one\n * rule is active at a time (one checkmark total, like the design mock). Picking\n * an option in one group clears the other; clearing the active option falls\n * back to the default — an unsorted list is not a state the UI offers.\n */\n\nexport const SORT_BY_RULES = {\n oldestUploads: 'createdAt:ASC',\n mostRecentUpdates: 'updatedAt:DESC',\n} as const;\n\nexport const SORT_DIRECTION_RULES = {\n nameAsc: 'name:ASC',\n nameDesc: 'name:DESC',\n sizeAsc: 'size:ASC',\n sizeDesc: 'size:DESC',\n} as const;\n\nexport type SortByKey = keyof typeof SORT_BY_RULES;\nexport type SortDirectionKey = keyof typeof SORT_DIRECTION_RULES;\nexport type FoldersPosition = 'top' | 'mixed';\n\nconst DEFAULT_SORT_BY: SortByKey = 'mostRecentUpdates';\n\nconst SORT_BY_BY_RULE = Object.fromEntries(\n Object.entries(SORT_BY_RULES).map(([key, rule]) => [rule, key])\n) as Record<string, SortByKey>;\n\nconst SORT_DIRECTION_BY_RULE = Object.fromEntries(\n Object.entries(SORT_DIRECTION_RULES).map(([key, rule]) => [rule, key])\n) as Record<string, SortDirectionKey>;\n\ninterface ParsedSort {\n sortBy: SortByKey | null;\n direction: SortDirectionKey | null;\n /** False when the state is the fallback default (no recognized rule in the URL). */\n isExplicit: boolean;\n}\n\n/** Parses `?sort=` — the first recognized rule wins, unknown rules are dropped. */\nconst parseSortParam = (raw: string | undefined): ParsedSort => {\n for (const rule of (raw ?? '').split(',')) {\n if (rule in SORT_BY_BY_RULE) {\n return { sortBy: SORT_BY_BY_RULE[rule], direction: null, isExplicit: true };\n }\n if (rule in SORT_DIRECTION_BY_RULE) {\n return { sortBy: null, direction: SORT_DIRECTION_BY_RULE[rule], isExplicit: true };\n }\n }\n\n return { sortBy: DEFAULT_SORT_BY, direction: null, isExplicit: false };\n};\n\nconst serializeSort = (sortBy: SortByKey | null, direction: SortDirectionKey | null): string => {\n const rules: (string | null)[] = [\n sortBy && SORT_BY_RULES[sortBy],\n direction && SORT_DIRECTION_RULES[direction],\n ];\n\n return rules.filter((rule): rule is string => Boolean(rule)).join(',');\n};\n\nexport interface ListSort {\n sortBy: SortByKey | null;\n direction: SortDirectionKey | null;\n foldersPosition: FoldersPosition;\n /**\n * The active rule for the files query, e.g. `updatedAt:DESC` — always a\n * single rule since the sort groups are mutually exclusive.\n */\n assetsSort: string;\n /**\n * Rule applicable to the folders query. In the default state (no sort in the\n * URL) folders keep their alphabetical order so the band matches the sidebar\n * tree; an explicit user sort applies, except size rules (folders have no\n * `size` column), which fall back to alphabetical.\n */\n foldersSort: string;\n setSortBy: (key: SortByKey | null) => void;\n setDirection: (key: SortDirectionKey | null) => void;\n setFoldersPosition: (position: FoldersPosition) => void;\n}\n\nexport const useListSort = (): ListSort => {\n const [{ query }, setQuery] = useQueryParams<{\n sort?: string;\n folders?: string;\n _q?: string;\n }>();\n\n const { sortBy, direction, isExplicit } = parseSortParam(query?.sort);\n const foldersPosition: FoldersPosition = query?.folders === 'mixed' ? 'mixed' : 'top';\n\n const writeSort = (nextSortBy: SortByKey | null, nextDirection: SortDirectionKey | null) => {\n // Exactly one active rule: clearing the active one falls back to default.\n if (nextSortBy === null && nextDirection === null) {\n nextSortBy = DEFAULT_SORT_BY;\n }\n\n const serialized = serializeSort(nextSortBy, nextDirection);\n\n if (nextSortBy === DEFAULT_SORT_BY && nextDirection === null) {\n setQuery(withEncodedUserParams(query, { sort: undefined }));\n } else {\n setQuery(withEncodedUserParams(query, { sort: serialized }));\n }\n };\n\n // Groups are mutually exclusive — picking in one clears the other.\n const setSortBy = (key: SortByKey | null) => writeSort(key, null);\n const setDirection = (key: SortDirectionKey | null) => writeSort(null, key);\n\n const setFoldersPosition = (position: FoldersPosition) => {\n if (position === 'mixed') {\n setQuery(withEncodedUserParams(query, { folders: 'mixed' }));\n } else {\n setQuery(withEncodedUserParams(query, { folders: undefined }));\n }\n };\n\n const assetsSort = serializeSort(sortBy, direction);\n const folderRuleCandidates: (string | null)[] = [\n sortBy && SORT_BY_RULES[sortBy],\n direction && !direction.startsWith('size') ? SORT_DIRECTION_RULES[direction] : null,\n ];\n const folderRules = folderRuleCandidates.filter((rule): rule is string => Boolean(rule));\n // Default state keeps folders alphabetical (matches the sidebar tree) — only\n // an explicit user sort reorders the folders band.\n const foldersSort = isExplicit && folderRules.length > 0 ? folderRules.join(',') : 'name:ASC';\n\n return {\n sortBy,\n direction,\n foldersPosition,\n assetsSort,\n foldersSort,\n setSortBy,\n setDirection,\n setFoldersPosition,\n };\n};\n"],"names":["SORT_BY_RULES","oldestUploads","mostRecentUpdates","SORT_DIRECTION_RULES","nameAsc","nameDesc","sizeAsc","sizeDesc","DEFAULT_SORT_BY","SORT_BY_BY_RULE","Object","fromEntries","entries","map","key","rule","SORT_DIRECTION_BY_RULE","parseSortParam","raw","split","sortBy","direction","isExplicit","serializeSort","rules","filter","Boolean","join","useListSort","query","setQuery","useQueryParams","sort","foldersPosition","folders","writeSort","nextSortBy","nextDirection","serialized","withEncodedUserParams","undefined","setSortBy","setDirection","setFoldersPosition","position","assetsSort","folderRuleCandidates","startsWith","folderRules","foldersSort","length"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { useQueryParams } from '@strapi/admin/strapi-admin';
1
+ import { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';
2
2
 
3
3
  /**
4
4
  * Sort state for the assets list, backed by URL query params so the current
@@ -72,13 +72,13 @@ const useListSort = ()=>{
72
72
  }
73
73
  const serialized = serializeSort(nextSortBy, nextDirection);
74
74
  if (nextSortBy === DEFAULT_SORT_BY && nextDirection === null) {
75
- setQuery({
76
- sort: ''
77
- }, 'remove');
75
+ setQuery(withEncodedUserParams(query, {
76
+ sort: undefined
77
+ }));
78
78
  } else {
79
- setQuery({
79
+ setQuery(withEncodedUserParams(query, {
80
80
  sort: serialized
81
- });
81
+ }));
82
82
  }
83
83
  };
84
84
  // Groups are mutually exclusive — picking in one clears the other.
@@ -86,13 +86,13 @@ const useListSort = ()=>{
86
86
  const setDirection = (key)=>writeSort(null, key);
87
87
  const setFoldersPosition = (position)=>{
88
88
  if (position === 'mixed') {
89
- setQuery({
89
+ setQuery(withEncodedUserParams(query, {
90
90
  folders: 'mixed'
91
- });
91
+ }));
92
92
  } else {
93
- setQuery({
94
- folders: ''
95
- }, 'remove');
93
+ setQuery(withEncodedUserParams(query, {
94
+ folders: undefined
95
+ }));
96
96
  }
97
97
  };
98
98
  const assetsSort = serializeSort(sortBy, direction);
@@ -1 +1 @@
1
- {"version":3,"file":"useListSort.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListSort.ts"],"sourcesContent":["import { useQueryParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Sort state for the assets list, backed by URL query params so the current\n * ordering is shareable and survives Back/refresh:\n *\n * - `?sort=<rule>` — a single whitelisted rule. Omitted → default (most\n * recent updates).\n * - `?folders=mixed` — folders interleaved with files. Omitted → folders on top.\n *\n * The date rules and the direction rules are mutually exclusive: exactly one\n * rule is active at a time (one checkmark total, like the design mock). Picking\n * an option in one group clears the other; clearing the active option falls\n * back to the default — an unsorted list is not a state the UI offers.\n */\n\nexport const SORT_BY_RULES = {\n oldestUploads: 'createdAt:ASC',\n mostRecentUpdates: 'updatedAt:DESC',\n} as const;\n\nexport const SORT_DIRECTION_RULES = {\n nameAsc: 'name:ASC',\n nameDesc: 'name:DESC',\n sizeAsc: 'size:ASC',\n sizeDesc: 'size:DESC',\n} as const;\n\nexport type SortByKey = keyof typeof SORT_BY_RULES;\nexport type SortDirectionKey = keyof typeof SORT_DIRECTION_RULES;\nexport type FoldersPosition = 'top' | 'mixed';\n\nconst DEFAULT_SORT_BY: SortByKey = 'mostRecentUpdates';\n\nconst SORT_BY_BY_RULE = Object.fromEntries(\n Object.entries(SORT_BY_RULES).map(([key, rule]) => [rule, key])\n) as Record<string, SortByKey>;\n\nconst SORT_DIRECTION_BY_RULE = Object.fromEntries(\n Object.entries(SORT_DIRECTION_RULES).map(([key, rule]) => [rule, key])\n) as Record<string, SortDirectionKey>;\n\ninterface ParsedSort {\n sortBy: SortByKey | null;\n direction: SortDirectionKey | null;\n /** False when the state is the fallback default (no recognized rule in the URL). */\n isExplicit: boolean;\n}\n\n/** Parses `?sort=` — the first recognized rule wins, unknown rules are dropped. */\nconst parseSortParam = (raw: string | undefined): ParsedSort => {\n for (const rule of (raw ?? '').split(',')) {\n if (rule in SORT_BY_BY_RULE) {\n return { sortBy: SORT_BY_BY_RULE[rule], direction: null, isExplicit: true };\n }\n if (rule in SORT_DIRECTION_BY_RULE) {\n return { sortBy: null, direction: SORT_DIRECTION_BY_RULE[rule], isExplicit: true };\n }\n }\n\n return { sortBy: DEFAULT_SORT_BY, direction: null, isExplicit: false };\n};\n\nconst serializeSort = (sortBy: SortByKey | null, direction: SortDirectionKey | null): string => {\n const rules: (string | null)[] = [\n sortBy && SORT_BY_RULES[sortBy],\n direction && SORT_DIRECTION_RULES[direction],\n ];\n\n return rules.filter((rule): rule is string => Boolean(rule)).join(',');\n};\n\nexport interface ListSort {\n sortBy: SortByKey | null;\n direction: SortDirectionKey | null;\n foldersPosition: FoldersPosition;\n /**\n * The active rule for the files query, e.g. `updatedAt:DESC` — always a\n * single rule since the sort groups are mutually exclusive.\n */\n assetsSort: string;\n /**\n * Rule applicable to the folders query. In the default state (no sort in the\n * URL) folders keep their alphabetical order so the band matches the sidebar\n * tree; an explicit user sort applies, except size rules (folders have no\n * `size` column), which fall back to alphabetical.\n */\n foldersSort: string;\n setSortBy: (key: SortByKey | null) => void;\n setDirection: (key: SortDirectionKey | null) => void;\n setFoldersPosition: (position: FoldersPosition) => void;\n}\n\nexport const useListSort = (): ListSort => {\n const [{ query }, setQuery] = useQueryParams<{ sort?: string; folders?: string }>();\n\n const { sortBy, direction, isExplicit } = parseSortParam(query?.sort);\n const foldersPosition: FoldersPosition = query?.folders === 'mixed' ? 'mixed' : 'top';\n\n const writeSort = (nextSortBy: SortByKey | null, nextDirection: SortDirectionKey | null) => {\n // Exactly one active rule: clearing the active one falls back to default.\n if (nextSortBy === null && nextDirection === null) {\n nextSortBy = DEFAULT_SORT_BY;\n }\n\n const serialized = serializeSort(nextSortBy, nextDirection);\n\n if (nextSortBy === DEFAULT_SORT_BY && nextDirection === null) {\n setQuery({ sort: '' }, 'remove');\n } else {\n setQuery({ sort: serialized });\n }\n };\n\n // Groups are mutually exclusive — picking in one clears the other.\n const setSortBy = (key: SortByKey | null) => writeSort(key, null);\n const setDirection = (key: SortDirectionKey | null) => writeSort(null, key);\n\n const setFoldersPosition = (position: FoldersPosition) => {\n if (position === 'mixed') {\n setQuery({ folders: 'mixed' });\n } else {\n setQuery({ folders: '' }, 'remove');\n }\n };\n\n const assetsSort = serializeSort(sortBy, direction);\n const folderRuleCandidates: (string | null)[] = [\n sortBy && SORT_BY_RULES[sortBy],\n direction && !direction.startsWith('size') ? SORT_DIRECTION_RULES[direction] : null,\n ];\n const folderRules = folderRuleCandidates.filter((rule): rule is string => Boolean(rule));\n // Default state keeps folders alphabetical (matches the sidebar tree) — only\n // an explicit user sort reorders the folders band.\n const foldersSort = isExplicit && folderRules.length > 0 ? folderRules.join(',') : 'name:ASC';\n\n return {\n sortBy,\n direction,\n foldersPosition,\n assetsSort,\n foldersSort,\n setSortBy,\n setDirection,\n setFoldersPosition,\n };\n};\n"],"names":["SORT_BY_RULES","oldestUploads","mostRecentUpdates","SORT_DIRECTION_RULES","nameAsc","nameDesc","sizeAsc","sizeDesc","DEFAULT_SORT_BY","SORT_BY_BY_RULE","Object","fromEntries","entries","map","key","rule","SORT_DIRECTION_BY_RULE","parseSortParam","raw","split","sortBy","direction","isExplicit","serializeSort","rules","filter","Boolean","join","useListSort","query","setQuery","useQueryParams","sort","foldersPosition","folders","writeSort","nextSortBy","nextDirection","serialized","setSortBy","setDirection","setFoldersPosition","position","assetsSort","folderRuleCandidates","startsWith","folderRules","foldersSort","length"],"mappings":"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"}
1
+ {"version":3,"file":"useListSort.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListSort.ts"],"sourcesContent":["import { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Sort state for the assets list, backed by URL query params so the current\n * ordering is shareable and survives Back/refresh:\n *\n * - `?sort=<rule>` — a single whitelisted rule. Omitted → default (most\n * recent updates).\n * - `?folders=mixed` — folders interleaved with files. Omitted → folders on top.\n *\n * The date rules and the direction rules are mutually exclusive: exactly one\n * rule is active at a time (one checkmark total, like the design mock). Picking\n * an option in one group clears the other; clearing the active option falls\n * back to the default — an unsorted list is not a state the UI offers.\n */\n\nexport const SORT_BY_RULES = {\n oldestUploads: 'createdAt:ASC',\n mostRecentUpdates: 'updatedAt:DESC',\n} as const;\n\nexport const SORT_DIRECTION_RULES = {\n nameAsc: 'name:ASC',\n nameDesc: 'name:DESC',\n sizeAsc: 'size:ASC',\n sizeDesc: 'size:DESC',\n} as const;\n\nexport type SortByKey = keyof typeof SORT_BY_RULES;\nexport type SortDirectionKey = keyof typeof SORT_DIRECTION_RULES;\nexport type FoldersPosition = 'top' | 'mixed';\n\nconst DEFAULT_SORT_BY: SortByKey = 'mostRecentUpdates';\n\nconst SORT_BY_BY_RULE = Object.fromEntries(\n Object.entries(SORT_BY_RULES).map(([key, rule]) => [rule, key])\n) as Record<string, SortByKey>;\n\nconst SORT_DIRECTION_BY_RULE = Object.fromEntries(\n Object.entries(SORT_DIRECTION_RULES).map(([key, rule]) => [rule, key])\n) as Record<string, SortDirectionKey>;\n\ninterface ParsedSort {\n sortBy: SortByKey | null;\n direction: SortDirectionKey | null;\n /** False when the state is the fallback default (no recognized rule in the URL). */\n isExplicit: boolean;\n}\n\n/** Parses `?sort=` — the first recognized rule wins, unknown rules are dropped. */\nconst parseSortParam = (raw: string | undefined): ParsedSort => {\n for (const rule of (raw ?? '').split(',')) {\n if (rule in SORT_BY_BY_RULE) {\n return { sortBy: SORT_BY_BY_RULE[rule], direction: null, isExplicit: true };\n }\n if (rule in SORT_DIRECTION_BY_RULE) {\n return { sortBy: null, direction: SORT_DIRECTION_BY_RULE[rule], isExplicit: true };\n }\n }\n\n return { sortBy: DEFAULT_SORT_BY, direction: null, isExplicit: false };\n};\n\nconst serializeSort = (sortBy: SortByKey | null, direction: SortDirectionKey | null): string => {\n const rules: (string | null)[] = [\n sortBy && SORT_BY_RULES[sortBy],\n direction && SORT_DIRECTION_RULES[direction],\n ];\n\n return rules.filter((rule): rule is string => Boolean(rule)).join(',');\n};\n\nexport interface ListSort {\n sortBy: SortByKey | null;\n direction: SortDirectionKey | null;\n foldersPosition: FoldersPosition;\n /**\n * The active rule for the files query, e.g. `updatedAt:DESC` — always a\n * single rule since the sort groups are mutually exclusive.\n */\n assetsSort: string;\n /**\n * Rule applicable to the folders query. In the default state (no sort in the\n * URL) folders keep their alphabetical order so the band matches the sidebar\n * tree; an explicit user sort applies, except size rules (folders have no\n * `size` column), which fall back to alphabetical.\n */\n foldersSort: string;\n setSortBy: (key: SortByKey | null) => void;\n setDirection: (key: SortDirectionKey | null) => void;\n setFoldersPosition: (position: FoldersPosition) => void;\n}\n\nexport const useListSort = (): ListSort => {\n const [{ query }, setQuery] = useQueryParams<{\n sort?: string;\n folders?: string;\n _q?: string;\n }>();\n\n const { sortBy, direction, isExplicit } = parseSortParam(query?.sort);\n const foldersPosition: FoldersPosition = query?.folders === 'mixed' ? 'mixed' : 'top';\n\n const writeSort = (nextSortBy: SortByKey | null, nextDirection: SortDirectionKey | null) => {\n // Exactly one active rule: clearing the active one falls back to default.\n if (nextSortBy === null && nextDirection === null) {\n nextSortBy = DEFAULT_SORT_BY;\n }\n\n const serialized = serializeSort(nextSortBy, nextDirection);\n\n if (nextSortBy === DEFAULT_SORT_BY && nextDirection === null) {\n setQuery(withEncodedUserParams(query, { sort: undefined }));\n } else {\n setQuery(withEncodedUserParams(query, { sort: serialized }));\n }\n };\n\n // Groups are mutually exclusive — picking in one clears the other.\n const setSortBy = (key: SortByKey | null) => writeSort(key, null);\n const setDirection = (key: SortDirectionKey | null) => writeSort(null, key);\n\n const setFoldersPosition = (position: FoldersPosition) => {\n if (position === 'mixed') {\n setQuery(withEncodedUserParams(query, { folders: 'mixed' }));\n } else {\n setQuery(withEncodedUserParams(query, { folders: undefined }));\n }\n };\n\n const assetsSort = serializeSort(sortBy, direction);\n const folderRuleCandidates: (string | null)[] = [\n sortBy && SORT_BY_RULES[sortBy],\n direction && !direction.startsWith('size') ? SORT_DIRECTION_RULES[direction] : null,\n ];\n const folderRules = folderRuleCandidates.filter((rule): rule is string => Boolean(rule));\n // Default state keeps folders alphabetical (matches the sidebar tree) — only\n // an explicit user sort reorders the folders band.\n const foldersSort = isExplicit && folderRules.length > 0 ? folderRules.join(',') : 'name:ASC';\n\n return {\n sortBy,\n direction,\n foldersPosition,\n assetsSort,\n foldersSort,\n setSortBy,\n setDirection,\n setFoldersPosition,\n };\n};\n"],"names":["SORT_BY_RULES","oldestUploads","mostRecentUpdates","SORT_DIRECTION_RULES","nameAsc","nameDesc","sizeAsc","sizeDesc","DEFAULT_SORT_BY","SORT_BY_BY_RULE","Object","fromEntries","entries","map","key","rule","SORT_DIRECTION_BY_RULE","parseSortParam","raw","split","sortBy","direction","isExplicit","serializeSort","rules","filter","Boolean","join","useListSort","query","setQuery","useQueryParams","sort","foldersPosition","folders","writeSort","nextSortBy","nextDirection","serialized","withEncodedUserParams","undefined","setSortBy","setDirection","setFoldersPosition","position","assetsSort","folderRuleCandidates","startsWith","folderRules","foldersSort","length"],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Merges just-uploaded assets into the loaded asset list so each one appears as
5
+ * soon as its own upload finishes, rather than when the whole batch settles.
6
+ *
7
+ * The upload is one mutation per batch, so its cache invalidation fires once at
8
+ * the end. These helpers bridge that gap purely client-side: the completed
9
+ * uploads already sit in the progress store as full `File` objects, so they can
10
+ * be placed into the list at their sort position with no extra request.
11
+ *
12
+ * The server list stays authoritative — merging never reorders it, and an id it
13
+ * already contains wins over the local copy, so the end-of-batch refetch
14
+ * silently takes over.
15
+ */ const SORT_FIELDS = [
16
+ 'createdAt',
17
+ 'updatedAt',
18
+ 'name',
19
+ 'size'
20
+ ];
21
+ const isSortField = (value)=>SORT_FIELDS.includes(value);
22
+ const DEFAULT_RULE = 'updatedAt:DESC';
23
+ const compareByField = (field, a, b)=>{
24
+ if (field === 'size') {
25
+ return (a.size ?? 0) - (b.size ?? 0);
26
+ }
27
+ if (field === 'name') {
28
+ return a.name.localeCompare(b.name);
29
+ }
30
+ // `createdAt` / `updatedAt` are ISO-8601, which sorts correctly as text.
31
+ return (a[field] ?? '').localeCompare(b[field] ?? '');
32
+ };
33
+ /**
34
+ * Comparator matching a `<field>:<ASC|DESC>` sort rule, as sent to the API.
35
+ *
36
+ * Ties break on id so the order is total: without it, two files sharing a
37
+ * timestamp could swap places between renders.
38
+ */ const getAssetComparator = (rule = DEFAULT_RULE)=>{
39
+ const [field, requestedDirection] = rule.split(':');
40
+ // An unrecognised field discards the direction too: half-applying a rule we
41
+ // cannot read would order the list differently from the default it stands in for.
42
+ const isKnownField = isSortField(field);
43
+ const sortField = isKnownField ? field : 'updatedAt';
44
+ const direction = isKnownField ? requestedDirection : 'DESC';
45
+ const sign = direction === 'ASC' ? 1 : -1;
46
+ return (a, b)=>{
47
+ const byField = compareByField(sortField, a, b);
48
+ return byField !== 0 ? sign * byField : sign * (a.id - b.id);
49
+ };
50
+ };
51
+ /**
52
+ * Places `uploaded` into `assets` at their sort position.
53
+ *
54
+ * An upload that sorts past everything loaded is dropped unless the list is
55
+ * fully loaded: it belongs on a page the user has not reached, and appending it
56
+ * would show it adjacent to the last loaded item instead of at its real place.
57
+ */ const mergeUploadedAssets = ({ assets, uploaded, sort, hasNextPage })=>{
58
+ if (uploaded.length === 0) {
59
+ return assets;
60
+ }
61
+ const loadedIds = new Set(assets.map((asset)=>asset.id));
62
+ const compare = getAssetComparator(sort);
63
+ const fresh = uploaded.filter((asset)=>!loadedIds.has(asset.id)).sort(compare);
64
+ if (fresh.length === 0) {
65
+ return assets;
66
+ }
67
+ const merged = [
68
+ ...assets
69
+ ];
70
+ for (const asset of fresh){
71
+ const at = merged.findIndex((loaded)=>compare(asset, loaded) < 0);
72
+ if (at === -1) {
73
+ if (hasNextPage) {
74
+ continue;
75
+ }
76
+ merged.push(asset);
77
+ } else {
78
+ merged.splice(at, 0, asset);
79
+ }
80
+ }
81
+ return merged;
82
+ };
83
+
84
+ exports.getAssetComparator = getAssetComparator;
85
+ exports.mergeUploadedAssets = mergeUploadedAssets;
86
+ //# sourceMappingURL=mergeUploadedAssets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeUploadedAssets.js","sources":["../../../../../../admin/src/future/pages/Assets/utils/mergeUploadedAssets.ts"],"sourcesContent":["/**\n * Merges just-uploaded assets into the loaded asset list so each one appears as\n * soon as its own upload finishes, rather than when the whole batch settles.\n *\n * The upload is one mutation per batch, so its cache invalidation fires once at\n * the end. These helpers bridge that gap purely client-side: the completed\n * uploads already sit in the progress store as full `File` objects, so they can\n * be placed into the list at their sort position with no extra request.\n *\n * The server list stays authoritative — merging never reorders it, and an id it\n * already contains wins over the local copy, so the end-of-batch refetch\n * silently takes over.\n */\n\nimport type { File } from '../../../../../../shared/contracts/files';\n\ntype SortField = 'createdAt' | 'updatedAt' | 'name' | 'size';\n\nconst SORT_FIELDS: SortField[] = ['createdAt', 'updatedAt', 'name', 'size'];\n\nconst isSortField = (value: string): value is SortField =>\n (SORT_FIELDS as string[]).includes(value);\n\nconst DEFAULT_RULE = 'updatedAt:DESC';\n\nconst compareByField = (field: SortField, a: File, b: File): number => {\n if (field === 'size') {\n return (a.size ?? 0) - (b.size ?? 0);\n }\n\n if (field === 'name') {\n return a.name.localeCompare(b.name);\n }\n\n // `createdAt` / `updatedAt` are ISO-8601, which sorts correctly as text.\n return (a[field] ?? '').localeCompare(b[field] ?? '');\n};\n\n/**\n * Comparator matching a `<field>:<ASC|DESC>` sort rule, as sent to the API.\n *\n * Ties break on id so the order is total: without it, two files sharing a\n * timestamp could swap places between renders.\n */\nexport const getAssetComparator = (rule: string = DEFAULT_RULE) => {\n const [field, requestedDirection] = rule.split(':');\n // An unrecognised field discards the direction too: half-applying a rule we\n // cannot read would order the list differently from the default it stands in for.\n const isKnownField = isSortField(field);\n const sortField: SortField = isKnownField ? field : 'updatedAt';\n const direction = isKnownField ? requestedDirection : 'DESC';\n const sign = direction === 'ASC' ? 1 : -1;\n\n return (a: File, b: File): number => {\n const byField = compareByField(sortField, a, b);\n\n return byField !== 0 ? sign * byField : sign * (a.id - b.id);\n };\n};\n\n/**\n * Places `uploaded` into `assets` at their sort position.\n *\n * An upload that sorts past everything loaded is dropped unless the list is\n * fully loaded: it belongs on a page the user has not reached, and appending it\n * would show it adjacent to the last loaded item instead of at its real place.\n */\nexport const mergeUploadedAssets = ({\n assets,\n uploaded,\n sort,\n hasNextPage,\n}: {\n assets: File[];\n uploaded: File[];\n sort?: string;\n /** Whether more pages remain, i.e. the tail of the list is not loaded. */\n hasNextPage: boolean;\n}): File[] => {\n if (uploaded.length === 0) {\n return assets;\n }\n\n const loadedIds = new Set(assets.map((asset) => asset.id));\n const compare = getAssetComparator(sort);\n const fresh = uploaded.filter((asset) => !loadedIds.has(asset.id)).sort(compare);\n\n if (fresh.length === 0) {\n return assets;\n }\n\n const merged = [...assets];\n\n for (const asset of fresh) {\n const at = merged.findIndex((loaded) => compare(asset, loaded) < 0);\n\n if (at === -1) {\n if (hasNextPage) {\n continue;\n }\n merged.push(asset);\n } else {\n merged.splice(at, 0, asset);\n }\n }\n\n return merged;\n};\n"],"names":["SORT_FIELDS","isSortField","value","includes","DEFAULT_RULE","compareByField","field","a","b","size","name","localeCompare","getAssetComparator","rule","requestedDirection","split","isKnownField","sortField","direction","sign","byField","id","mergeUploadedAssets","assets","uploaded","sort","hasNextPage","length","loadedIds","Set","map","asset","compare","fresh","filter","has","merged","at","findIndex","loaded","push","splice"],"mappings":"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"}