@strapi/upload 5.52.2 → 5.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/components/AssetCard/AssetCard.js +1 -0
- package/dist/admin/components/AssetCard/AssetCard.js.map +1 -1
- package/dist/admin/components/AssetCard/AssetCard.mjs +1 -0
- package/dist/admin/components/AssetCard/AssetCard.mjs.map +1 -1
- package/dist/admin/components/AssetCard/AssetCardBase.js +1 -0
- package/dist/admin/components/AssetCard/AssetCardBase.js.map +1 -1
- package/dist/admin/components/AssetCard/AssetCardBase.mjs +1 -0
- package/dist/admin/components/AssetCard/AssetCardBase.mjs.map +1 -1
- package/dist/admin/components/AssetCard/ImageAssetCard.js +1 -0
- package/dist/admin/components/AssetCard/ImageAssetCard.js.map +1 -1
- package/dist/admin/components/AssetCard/ImageAssetCard.mjs +1 -0
- package/dist/admin/components/AssetCard/ImageAssetCard.mjs.map +1 -1
- package/dist/admin/components/AssetCard/UploadingAssetCard.js +1 -0
- package/dist/admin/components/AssetCard/UploadingAssetCard.js.map +1 -1
- package/dist/admin/components/AssetCard/UploadingAssetCard.mjs +1 -0
- package/dist/admin/components/AssetCard/UploadingAssetCard.mjs.map +1 -1
- package/dist/admin/components/AssetCard/VideoAssetCard.js +1 -0
- package/dist/admin/components/AssetCard/VideoAssetCard.js.map +1 -1
- package/dist/admin/components/AssetCard/VideoAssetCard.mjs +1 -0
- package/dist/admin/components/AssetCard/VideoAssetCard.mjs.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.js +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.js.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.mjs +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.mjs.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/Filters.js +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/Filters.js.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/Filters.mjs +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/Filters.mjs.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.js +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.js.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.mjs +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.mjs.map +1 -1
- package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.js +1 -0
- package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.js.map +1 -1
- package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.mjs +1 -0
- package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.mjs.map +1 -1
- package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.js +1 -0
- package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.js.map +1 -1
- package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.mjs +1 -0
- package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/EditAssetContent.js +1 -0
- package/dist/admin/components/EditAssetDialog/EditAssetContent.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.js +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.js +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.js +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.js +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.js +1 -0
- package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.mjs.map +1 -1
- package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.js +1 -0
- package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.js.map +1 -1
- package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.mjs +1 -0
- package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.mjs.map +1 -1
- package/dist/admin/components/FilterList/FilterList.js +2 -2
- package/dist/admin/components/FilterList/FilterList.js.map +1 -1
- package/dist/admin/components/FilterList/FilterList.mjs +2 -2
- package/dist/admin/components/FilterList/FilterList.mjs.map +1 -1
- package/dist/admin/components/FilterPopover/FilterPopover.js +4 -4
- package/dist/admin/components/FilterPopover/FilterPopover.js.map +1 -1
- package/dist/admin/components/FilterPopover/FilterPopover.mjs +4 -4
- package/dist/admin/components/FilterPopover/FilterPopover.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.js +1 -0
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.mjs +1 -0
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.js +1 -0
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.mjs +1 -0
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.js +4 -10
- package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.mjs +4 -10
- package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.mjs.map +1 -1
- package/dist/admin/components/SortPicker/SortPicker.js +1 -0
- package/dist/admin/components/SortPicker/SortPicker.js.map +1 -1
- package/dist/admin/components/SortPicker/SortPicker.mjs +1 -0
- package/dist/admin/components/SortPicker/SortPicker.mjs.map +1 -1
- package/dist/admin/components/TableList/CellContent.js +1 -0
- package/dist/admin/components/TableList/CellContent.js.map +1 -1
- package/dist/admin/components/TableList/CellContent.mjs +1 -0
- package/dist/admin/components/TableList/CellContent.mjs.map +1 -1
- package/dist/admin/components/TableList/PreviewCell.js +1 -0
- package/dist/admin/components/TableList/PreviewCell.js.map +1 -1
- package/dist/admin/components/TableList/PreviewCell.mjs +1 -0
- package/dist/admin/components/TableList/PreviewCell.mjs.map +1 -1
- package/dist/admin/components/TableList/TableList.js +1 -0
- package/dist/admin/components/TableList/TableList.js.map +1 -1
- package/dist/admin/components/TableList/TableList.mjs +1 -0
- package/dist/admin/components/TableList/TableList.mjs.map +1 -1
- package/dist/admin/components/TableList/TableRows.js +1 -0
- package/dist/admin/components/TableList/TableRows.js.map +1 -1
- package/dist/admin/components/TableList/TableRows.mjs +1 -0
- package/dist/admin/components/TableList/TableRows.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.js +1 -0
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.mjs +1 -0
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.js +9 -21
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.mjs +9 -21
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js +1 -0
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs +1 -0
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.js +1 -0
- package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.mjs +1 -0
- package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.mjs.map +1 -1
- package/dist/admin/constants.js +1 -0
- package/dist/admin/constants.js.map +1 -1
- package/dist/admin/constants.mjs +1 -0
- package/dist/admin/constants.mjs.map +1 -1
- package/dist/admin/future/components/Drawer.js +17 -3
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +17 -3
- package/dist/admin/future/components/Drawer.mjs.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.js +46 -12
- package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.mjs +47 -13
- package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.js +27 -2
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +27 -2
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +9 -1
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +9 -1
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +19 -4
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +19 -4
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +64 -10
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +66 -12
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +17 -5
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +17 -5
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +56 -28
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +58 -30
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +203 -83
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +207 -87
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +21 -3
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +21 -3
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.js +181 -0
- package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.mjs +179 -0
- package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/constants.js +22 -0
- package/dist/admin/future/pages/Assets/constants.js.map +1 -1
- package/dist/admin/future/pages/Assets/constants.mjs +19 -1
- package/dist/admin/future/pages/Assets/constants.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +4 -3
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +5 -4
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +105 -12
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +106 -13
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js +5 -5
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs +6 -6
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.js +111 -0
- package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.mjs +109 -0
- package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.js +10 -10
- package/dist/admin/future/pages/Assets/hooks/useListSort.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs +11 -11
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.js +86 -0
- package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.mjs +83 -0
- package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/renderedKeys.js +19 -0
- package/dist/admin/future/pages/Assets/utils/renderedKeys.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/renderedKeys.mjs +17 -0
- package/dist/admin/future/pages/Assets/utils/renderedKeys.mjs.map +1 -0
- package/dist/admin/future/services/api.js +19 -6
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +19 -6
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/store/uploadProgress.js +55 -2
- package/dist/admin/future/store/uploadProgress.js.map +1 -1
- package/dist/admin/future/store/uploadProgress.mjs +53 -3
- package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
- package/dist/admin/index.js +1 -0
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +1 -0
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/pages/App/ConfigureTheView/components/Settings.js +1 -0
- package/dist/admin/pages/App/ConfigureTheView/components/Settings.js.map +1 -1
- package/dist/admin/pages/App/ConfigureTheView/components/Settings.mjs +1 -0
- package/dist/admin/pages/App/ConfigureTheView/components/Settings.mjs.map +1 -1
- package/dist/admin/pages/App/MediaLibrary.js +7 -9
- package/dist/admin/pages/App/MediaLibrary.js.map +1 -1
- package/dist/admin/pages/App/MediaLibrary.mjs +8 -10
- package/dist/admin/pages/App/MediaLibrary.mjs.map +1 -1
- package/dist/admin/pages/App/components/BulkActions.js +1 -0
- package/dist/admin/pages/App/components/BulkActions.js.map +1 -1
- package/dist/admin/pages/App/components/BulkActions.mjs +1 -0
- package/dist/admin/pages/App/components/BulkActions.mjs.map +1 -1
- package/dist/admin/pages/App/components/EmptyOrNoPermissions.js +1 -0
- package/dist/admin/pages/App/components/EmptyOrNoPermissions.js.map +1 -1
- package/dist/admin/pages/App/components/EmptyOrNoPermissions.mjs +1 -0
- package/dist/admin/pages/App/components/EmptyOrNoPermissions.mjs.map +1 -1
- package/dist/admin/pages/App/components/Filters.js +8 -8
- package/dist/admin/pages/App/components/Filters.js.map +1 -1
- package/dist/admin/pages/App/components/Filters.mjs +9 -9
- package/dist/admin/pages/App/components/Filters.mjs.map +1 -1
- package/dist/admin/pages/App/components/Header.js +1 -1
- package/dist/admin/pages/App/components/Header.js.map +1 -1
- package/dist/admin/pages/App/components/Header.mjs +2 -2
- package/dist/admin/pages/App/components/Header.mjs.map +1 -1
- package/dist/admin/src/future/components/Drawer.d.ts +10 -0
- package/dist/admin/src/future/pages/Assets/components/AssetsGrid.d.ts +4 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +4 -1
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/components/MainAreaContextMenu.d.ts +28 -0
- package/dist/admin/src/future/pages/Assets/constants.d.ts +20 -0
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +4 -0
- package/dist/admin/src/future/pages/Assets/hooks/useListScrollRestoration.d.ts +15 -0
- package/dist/admin/src/future/pages/Assets/utils/mergeUploadedAssets.d.ts +35 -0
- package/dist/admin/src/future/pages/Assets/utils/renderedKeys.d.ts +19 -0
- package/dist/admin/src/future/store/uploadProgress.d.ts +51 -0
- package/dist/admin/src/utils/index.d.ts +1 -0
- package/dist/admin/src/utils/rawFileToAsset.d.ts +2 -2
- package/dist/admin/src/utils/resolveFileMime.d.ts +13 -0
- package/dist/admin/translations/en.json.js +5 -1
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +5 -1
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/getFolderURL.js +2 -1
- package/dist/admin/utils/getFolderURL.js.map +1 -1
- package/dist/admin/utils/getFolderURL.mjs +2 -1
- package/dist/admin/utils/getFolderURL.mjs.map +1 -1
- package/dist/admin/utils/rawFileToAsset.js +5 -3
- package/dist/admin/utils/rawFileToAsset.js.map +1 -1
- package/dist/admin/utils/rawFileToAsset.mjs +5 -3
- package/dist/admin/utils/rawFileToAsset.mjs.map +1 -1
- package/dist/admin/utils/resolveFileMime.js +69 -0
- package/dist/admin/utils/resolveFileMime.js.map +1 -0
- package/dist/admin/utils/resolveFileMime.mjs +64 -0
- package/dist/admin/utils/resolveFileMime.mjs.map +1 -0
- package/dist/admin/utils/urlsToAssets.js +8 -4
- package/dist/admin/utils/urlsToAssets.js.map +1 -1
- package/dist/admin/utils/urlsToAssets.mjs +8 -4
- package/dist/admin/utils/urlsToAssets.mjs.map +1 -1
- package/dist/server/services/upload.js +20 -8
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs +20 -8
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/src/index.d.ts +2 -0
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +2 -0
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/upload.d.ts +2 -0
- package/dist/server/src/services/upload.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The admin layout marks the element that scrolls the main column (see
|
|
7
|
+
* `Layouts.Root`). The media library has no scroll container of its own, so the
|
|
8
|
+
* offset to save and restore belongs to that ancestor.
|
|
9
|
+
*/ const SCROLL_ROOT_SELECTOR = '[data-strapi-main-content]';
|
|
10
|
+
/**
|
|
11
|
+
* How long a restore keeps retrying after the list key changes.
|
|
12
|
+
*
|
|
13
|
+
* A returning list usually comes back from memory in one commit, so the first
|
|
14
|
+
* attempt lands. When it doesn't — pages still loading, so the content isn't yet
|
|
15
|
+
* tall enough to scroll that far — later commits retry. Past this window the
|
|
16
|
+
* offset is dropped rather than yanked out from under someone already reading.
|
|
17
|
+
*/ const RESTORE_WINDOW_MS = 2000;
|
|
18
|
+
/**
|
|
19
|
+
* How many list offsets to keep. Matches `MAX_REMEMBERED_LISTS` in
|
|
20
|
+
* `useInfiniteAssets`: the two are keyed identically, so they should forget on
|
|
21
|
+
* the same terms.
|
|
22
|
+
*/ const MAX_REMEMBERED_OFFSETS = 10;
|
|
23
|
+
/**
|
|
24
|
+
* Remembers where each list was scrolled to and puts it back on return.
|
|
25
|
+
*
|
|
26
|
+
* `key` fingerprints the list on screen (folder, search, sort, filters). While
|
|
27
|
+
* it holds steady the current offset is recorded; when it changes the incoming
|
|
28
|
+
* key's saved offset is restored, or the list is sent to the top if it has none
|
|
29
|
+
* — a first visit, or one whose entry has been dropped.
|
|
30
|
+
*
|
|
31
|
+
* Returns a callback ref to place on any element inside the scrolling column;
|
|
32
|
+
* it only locates that column, it is never scrolled itself.
|
|
33
|
+
*
|
|
34
|
+
* Restoring a *position* only helps if the rows that filled it come back too —
|
|
35
|
+
* that is `useInfiniteAssets`' job.
|
|
36
|
+
*/ const useListScrollRestoration = (key)=>{
|
|
37
|
+
const offsetsRef = React.useRef(new Map());
|
|
38
|
+
const containerRef = React.useRef(null);
|
|
39
|
+
const detachRef = React.useRef(null);
|
|
40
|
+
// The key offsets are currently filed under. In a ref because the scroll
|
|
41
|
+
// listener is attached once and must read the current value.
|
|
42
|
+
const keyRef = React.useRef(key);
|
|
43
|
+
const pendingRef = React.useRef(null);
|
|
44
|
+
const anchorRef = React.useCallback((node)=>{
|
|
45
|
+
detachRef.current?.();
|
|
46
|
+
detachRef.current = null;
|
|
47
|
+
containerRef.current = null;
|
|
48
|
+
const container = node?.closest(SCROLL_ROOT_SELECTOR);
|
|
49
|
+
if (!container) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const handleScroll = ()=>{
|
|
53
|
+
const offsets = offsetsRef.current;
|
|
54
|
+
// Delete before set so the entry moves to the end. Map iterates in
|
|
55
|
+
// insertion order, so the first key is the least recently scrolled.
|
|
56
|
+
offsets.delete(keyRef.current);
|
|
57
|
+
offsets.set(keyRef.current, container.scrollTop);
|
|
58
|
+
while(offsets.size > MAX_REMEMBERED_OFFSETS){
|
|
59
|
+
const oldest = offsets.keys().next().value;
|
|
60
|
+
if (oldest === undefined) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
offsets.delete(oldest);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
container.addEventListener('scroll', handleScroll, {
|
|
67
|
+
passive: true
|
|
68
|
+
});
|
|
69
|
+
containerRef.current = container;
|
|
70
|
+
detachRef.current = ()=>container.removeEventListener('scroll', handleScroll);
|
|
71
|
+
}, []);
|
|
72
|
+
React.useEffect(()=>()=>detachRef.current?.(), []);
|
|
73
|
+
// Runs on every commit, not just when `key` changes: a restore the first
|
|
74
|
+
// commit was too short to satisfy must be retried as the rows arrive, and
|
|
75
|
+
// there is no state to key that off. Layout phase so the list is never
|
|
76
|
+
// painted at the wrong offset.
|
|
77
|
+
React.useLayoutEffect(()=>{
|
|
78
|
+
const container = containerRef.current;
|
|
79
|
+
if (!container) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (keyRef.current !== key) {
|
|
83
|
+
keyRef.current = key;
|
|
84
|
+
pendingRef.current = {
|
|
85
|
+
top: offsetsRef.current.get(key) ?? 0,
|
|
86
|
+
deadline: Date.now() + RESTORE_WINDOW_MS
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const pending = pendingRef.current;
|
|
90
|
+
if (!pending) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Before the assignment, not alongside it: past the window the offset has to
|
|
94
|
+
// be dropped rather than applied one final time to a list the user has since
|
|
95
|
+
// scrolled themselves.
|
|
96
|
+
if (Date.now() > pending.deadline) {
|
|
97
|
+
pendingRef.current = null;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
container.scrollTop = pending.top;
|
|
101
|
+
// The browser clamps to the content that exists now, so a target the list
|
|
102
|
+
// isn't tall enough for yet stays pending for the next commit.
|
|
103
|
+
if (container.scrollTop >= pending.top) {
|
|
104
|
+
pendingRef.current = null;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
return anchorRef;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
exports.useListScrollRestoration = useListScrollRestoration;
|
|
111
|
+
//# sourceMappingURL=useListScrollRestoration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useListScrollRestoration.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListScrollRestoration.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';\n\n/**\n * The admin layout marks the element that scrolls the main column (see\n * `Layouts.Root`). The media library has no scroll container of its own, so the\n * offset to save and restore belongs to that ancestor.\n */\nconst SCROLL_ROOT_SELECTOR = '[data-strapi-main-content]';\n\n/**\n * How long a restore keeps retrying after the list key changes.\n *\n * A returning list usually comes back from memory in one commit, so the first\n * attempt lands. When it doesn't — pages still loading, so the content isn't yet\n * tall enough to scroll that far — later commits retry. Past this window the\n * offset is dropped rather than yanked out from under someone already reading.\n */\nconst RESTORE_WINDOW_MS = 2000;\n\n/**\n * How many list offsets to keep. Matches `MAX_REMEMBERED_LISTS` in\n * `useInfiniteAssets`: the two are keyed identically, so they should forget on\n * the same terms.\n */\nconst MAX_REMEMBERED_OFFSETS = 10;\n\ninterface PendingRestore {\n top: number;\n deadline: number;\n}\n\n/**\n * Remembers where each list was scrolled to and puts it back on return.\n *\n * `key` fingerprints the list on screen (folder, search, sort, filters). While\n * it holds steady the current offset is recorded; when it changes the incoming\n * key's saved offset is restored, or the list is sent to the top if it has none\n * — a first visit, or one whose entry has been dropped.\n *\n * Returns a callback ref to place on any element inside the scrolling column;\n * it only locates that column, it is never scrolled itself.\n *\n * Restoring a *position* only helps if the rows that filled it come back too —\n * that is `useInfiniteAssets`' job.\n */\nexport const useListScrollRestoration = (key: string) => {\n const offsetsRef = useRef(new Map<string, number>());\n const containerRef = useRef<HTMLElement | null>(null);\n const detachRef = useRef<(() => void) | null>(null);\n // The key offsets are currently filed under. In a ref because the scroll\n // listener is attached once and must read the current value.\n const keyRef = useRef(key);\n const pendingRef = useRef<PendingRestore | null>(null);\n\n const anchorRef = useCallback((node: HTMLElement | null) => {\n detachRef.current?.();\n detachRef.current = null;\n containerRef.current = null;\n\n const container = node?.closest<HTMLElement>(SCROLL_ROOT_SELECTOR);\n\n if (!container) {\n return;\n }\n\n const handleScroll = () => {\n const offsets = offsetsRef.current;\n\n // Delete before set so the entry moves to the end. Map iterates in\n // insertion order, so the first key is the least recently scrolled.\n offsets.delete(keyRef.current);\n offsets.set(keyRef.current, container.scrollTop);\n\n while (offsets.size > MAX_REMEMBERED_OFFSETS) {\n const oldest = offsets.keys().next().value;\n\n if (oldest === undefined) {\n break;\n }\n\n offsets.delete(oldest);\n }\n };\n\n container.addEventListener('scroll', handleScroll, { passive: true });\n containerRef.current = container;\n detachRef.current = () => container.removeEventListener('scroll', handleScroll);\n }, []);\n\n useEffect(() => () => detachRef.current?.(), []);\n\n // Runs on every commit, not just when `key` changes: a restore the first\n // commit was too short to satisfy must be retried as the rows arrive, and\n // there is no state to key that off. Layout phase so the list is never\n // painted at the wrong offset.\n useLayoutEffect(() => {\n const container = containerRef.current;\n\n if (!container) {\n return;\n }\n\n if (keyRef.current !== key) {\n keyRef.current = key;\n pendingRef.current = {\n top: offsetsRef.current.get(key) ?? 0,\n deadline: Date.now() + RESTORE_WINDOW_MS,\n };\n }\n\n const pending = pendingRef.current;\n\n if (!pending) {\n return;\n }\n\n // Before the assignment, not alongside it: past the window the offset has to\n // be dropped rather than applied one final time to a list the user has since\n // scrolled themselves.\n if (Date.now() > pending.deadline) {\n pendingRef.current = null;\n return;\n }\n\n container.scrollTop = pending.top;\n\n // The browser clamps to the content that exists now, so a target the list\n // isn't tall enough for yet stays pending for the next commit.\n if (container.scrollTop >= pending.top) {\n pendingRef.current = null;\n }\n });\n\n return anchorRef;\n};\n"],"names":["SCROLL_ROOT_SELECTOR","RESTORE_WINDOW_MS","MAX_REMEMBERED_OFFSETS","useListScrollRestoration","key","offsetsRef","useRef","Map","containerRef","detachRef","keyRef","pendingRef","anchorRef","useCallback","node","current","container","closest","handleScroll","offsets","delete","set","scrollTop","size","oldest","keys","next","value","undefined","addEventListener","passive","removeEventListener","useEffect","useLayoutEffect","top","get","deadline","Date","now","pending"],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { useRef, useCallback, useEffect, useLayoutEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The admin layout marks the element that scrolls the main column (see
|
|
5
|
+
* `Layouts.Root`). The media library has no scroll container of its own, so the
|
|
6
|
+
* offset to save and restore belongs to that ancestor.
|
|
7
|
+
*/ const SCROLL_ROOT_SELECTOR = '[data-strapi-main-content]';
|
|
8
|
+
/**
|
|
9
|
+
* How long a restore keeps retrying after the list key changes.
|
|
10
|
+
*
|
|
11
|
+
* A returning list usually comes back from memory in one commit, so the first
|
|
12
|
+
* attempt lands. When it doesn't — pages still loading, so the content isn't yet
|
|
13
|
+
* tall enough to scroll that far — later commits retry. Past this window the
|
|
14
|
+
* offset is dropped rather than yanked out from under someone already reading.
|
|
15
|
+
*/ const RESTORE_WINDOW_MS = 2000;
|
|
16
|
+
/**
|
|
17
|
+
* How many list offsets to keep. Matches `MAX_REMEMBERED_LISTS` in
|
|
18
|
+
* `useInfiniteAssets`: the two are keyed identically, so they should forget on
|
|
19
|
+
* the same terms.
|
|
20
|
+
*/ const MAX_REMEMBERED_OFFSETS = 10;
|
|
21
|
+
/**
|
|
22
|
+
* Remembers where each list was scrolled to and puts it back on return.
|
|
23
|
+
*
|
|
24
|
+
* `key` fingerprints the list on screen (folder, search, sort, filters). While
|
|
25
|
+
* it holds steady the current offset is recorded; when it changes the incoming
|
|
26
|
+
* key's saved offset is restored, or the list is sent to the top if it has none
|
|
27
|
+
* — a first visit, or one whose entry has been dropped.
|
|
28
|
+
*
|
|
29
|
+
* Returns a callback ref to place on any element inside the scrolling column;
|
|
30
|
+
* it only locates that column, it is never scrolled itself.
|
|
31
|
+
*
|
|
32
|
+
* Restoring a *position* only helps if the rows that filled it come back too —
|
|
33
|
+
* that is `useInfiniteAssets`' job.
|
|
34
|
+
*/ const useListScrollRestoration = (key)=>{
|
|
35
|
+
const offsetsRef = useRef(new Map());
|
|
36
|
+
const containerRef = useRef(null);
|
|
37
|
+
const detachRef = useRef(null);
|
|
38
|
+
// The key offsets are currently filed under. In a ref because the scroll
|
|
39
|
+
// listener is attached once and must read the current value.
|
|
40
|
+
const keyRef = useRef(key);
|
|
41
|
+
const pendingRef = useRef(null);
|
|
42
|
+
const anchorRef = useCallback((node)=>{
|
|
43
|
+
detachRef.current?.();
|
|
44
|
+
detachRef.current = null;
|
|
45
|
+
containerRef.current = null;
|
|
46
|
+
const container = node?.closest(SCROLL_ROOT_SELECTOR);
|
|
47
|
+
if (!container) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const handleScroll = ()=>{
|
|
51
|
+
const offsets = offsetsRef.current;
|
|
52
|
+
// Delete before set so the entry moves to the end. Map iterates in
|
|
53
|
+
// insertion order, so the first key is the least recently scrolled.
|
|
54
|
+
offsets.delete(keyRef.current);
|
|
55
|
+
offsets.set(keyRef.current, container.scrollTop);
|
|
56
|
+
while(offsets.size > MAX_REMEMBERED_OFFSETS){
|
|
57
|
+
const oldest = offsets.keys().next().value;
|
|
58
|
+
if (oldest === undefined) {
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
offsets.delete(oldest);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
container.addEventListener('scroll', handleScroll, {
|
|
65
|
+
passive: true
|
|
66
|
+
});
|
|
67
|
+
containerRef.current = container;
|
|
68
|
+
detachRef.current = ()=>container.removeEventListener('scroll', handleScroll);
|
|
69
|
+
}, []);
|
|
70
|
+
useEffect(()=>()=>detachRef.current?.(), []);
|
|
71
|
+
// Runs on every commit, not just when `key` changes: a restore the first
|
|
72
|
+
// commit was too short to satisfy must be retried as the rows arrive, and
|
|
73
|
+
// there is no state to key that off. Layout phase so the list is never
|
|
74
|
+
// painted at the wrong offset.
|
|
75
|
+
useLayoutEffect(()=>{
|
|
76
|
+
const container = containerRef.current;
|
|
77
|
+
if (!container) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (keyRef.current !== key) {
|
|
81
|
+
keyRef.current = key;
|
|
82
|
+
pendingRef.current = {
|
|
83
|
+
top: offsetsRef.current.get(key) ?? 0,
|
|
84
|
+
deadline: Date.now() + RESTORE_WINDOW_MS
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const pending = pendingRef.current;
|
|
88
|
+
if (!pending) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Before the assignment, not alongside it: past the window the offset has to
|
|
92
|
+
// be dropped rather than applied one final time to a list the user has since
|
|
93
|
+
// scrolled themselves.
|
|
94
|
+
if (Date.now() > pending.deadline) {
|
|
95
|
+
pendingRef.current = null;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
container.scrollTop = pending.top;
|
|
99
|
+
// The browser clamps to the content that exists now, so a target the list
|
|
100
|
+
// isn't tall enough for yet stays pending for the next commit.
|
|
101
|
+
if (container.scrollTop >= pending.top) {
|
|
102
|
+
pendingRef.current = null;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return anchorRef;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export { useListScrollRestoration };
|
|
109
|
+
//# sourceMappingURL=useListScrollRestoration.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useListScrollRestoration.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListScrollRestoration.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';\n\n/**\n * The admin layout marks the element that scrolls the main column (see\n * `Layouts.Root`). The media library has no scroll container of its own, so the\n * offset to save and restore belongs to that ancestor.\n */\nconst SCROLL_ROOT_SELECTOR = '[data-strapi-main-content]';\n\n/**\n * How long a restore keeps retrying after the list key changes.\n *\n * A returning list usually comes back from memory in one commit, so the first\n * attempt lands. When it doesn't — pages still loading, so the content isn't yet\n * tall enough to scroll that far — later commits retry. Past this window the\n * offset is dropped rather than yanked out from under someone already reading.\n */\nconst RESTORE_WINDOW_MS = 2000;\n\n/**\n * How many list offsets to keep. Matches `MAX_REMEMBERED_LISTS` in\n * `useInfiniteAssets`: the two are keyed identically, so they should forget on\n * the same terms.\n */\nconst MAX_REMEMBERED_OFFSETS = 10;\n\ninterface PendingRestore {\n top: number;\n deadline: number;\n}\n\n/**\n * Remembers where each list was scrolled to and puts it back on return.\n *\n * `key` fingerprints the list on screen (folder, search, sort, filters). While\n * it holds steady the current offset is recorded; when it changes the incoming\n * key's saved offset is restored, or the list is sent to the top if it has none\n * — a first visit, or one whose entry has been dropped.\n *\n * Returns a callback ref to place on any element inside the scrolling column;\n * it only locates that column, it is never scrolled itself.\n *\n * Restoring a *position* only helps if the rows that filled it come back too —\n * that is `useInfiniteAssets`' job.\n */\nexport const useListScrollRestoration = (key: string) => {\n const offsetsRef = useRef(new Map<string, number>());\n const containerRef = useRef<HTMLElement | null>(null);\n const detachRef = useRef<(() => void) | null>(null);\n // The key offsets are currently filed under. In a ref because the scroll\n // listener is attached once and must read the current value.\n const keyRef = useRef(key);\n const pendingRef = useRef<PendingRestore | null>(null);\n\n const anchorRef = useCallback((node: HTMLElement | null) => {\n detachRef.current?.();\n detachRef.current = null;\n containerRef.current = null;\n\n const container = node?.closest<HTMLElement>(SCROLL_ROOT_SELECTOR);\n\n if (!container) {\n return;\n }\n\n const handleScroll = () => {\n const offsets = offsetsRef.current;\n\n // Delete before set so the entry moves to the end. Map iterates in\n // insertion order, so the first key is the least recently scrolled.\n offsets.delete(keyRef.current);\n offsets.set(keyRef.current, container.scrollTop);\n\n while (offsets.size > MAX_REMEMBERED_OFFSETS) {\n const oldest = offsets.keys().next().value;\n\n if (oldest === undefined) {\n break;\n }\n\n offsets.delete(oldest);\n }\n };\n\n container.addEventListener('scroll', handleScroll, { passive: true });\n containerRef.current = container;\n detachRef.current = () => container.removeEventListener('scroll', handleScroll);\n }, []);\n\n useEffect(() => () => detachRef.current?.(), []);\n\n // Runs on every commit, not just when `key` changes: a restore the first\n // commit was too short to satisfy must be retried as the rows arrive, and\n // there is no state to key that off. Layout phase so the list is never\n // painted at the wrong offset.\n useLayoutEffect(() => {\n const container = containerRef.current;\n\n if (!container) {\n return;\n }\n\n if (keyRef.current !== key) {\n keyRef.current = key;\n pendingRef.current = {\n top: offsetsRef.current.get(key) ?? 0,\n deadline: Date.now() + RESTORE_WINDOW_MS,\n };\n }\n\n const pending = pendingRef.current;\n\n if (!pending) {\n return;\n }\n\n // Before the assignment, not alongside it: past the window the offset has to\n // be dropped rather than applied one final time to a list the user has since\n // scrolled themselves.\n if (Date.now() > pending.deadline) {\n pendingRef.current = null;\n return;\n }\n\n container.scrollTop = pending.top;\n\n // The browser clamps to the content that exists now, so a target the list\n // isn't tall enough for yet stays pending for the next commit.\n if (container.scrollTop >= pending.top) {\n pendingRef.current = null;\n }\n });\n\n return anchorRef;\n};\n"],"names":["SCROLL_ROOT_SELECTOR","RESTORE_WINDOW_MS","MAX_REMEMBERED_OFFSETS","useListScrollRestoration","key","offsetsRef","useRef","Map","containerRef","detachRef","keyRef","pendingRef","anchorRef","useCallback","node","current","container","closest","handleScroll","offsets","delete","set","scrollTop","size","oldest","keys","next","value","undefined","addEventListener","passive","removeEventListener","useEffect","useLayoutEffect","top","get","deadline","Date","now","pending"],"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
|
-
}
|
|
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
|
-
}
|
|
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<{
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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<{
|
|
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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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merges just-uploaded assets into the loaded asset list so each one appears as
|
|
3
|
+
* soon as its own upload finishes, rather than when the whole batch settles.
|
|
4
|
+
*
|
|
5
|
+
* The upload is one mutation per batch, so its cache invalidation fires once at
|
|
6
|
+
* the end. These helpers bridge that gap purely client-side: the completed
|
|
7
|
+
* uploads already sit in the progress store as full `File` objects, so they can
|
|
8
|
+
* be placed into the list at their sort position with no extra request.
|
|
9
|
+
*
|
|
10
|
+
* The server list stays authoritative — merging never reorders it, and an id it
|
|
11
|
+
* already contains wins over the local copy, so the end-of-batch refetch
|
|
12
|
+
* silently takes over.
|
|
13
|
+
*/ const SORT_FIELDS = [
|
|
14
|
+
'createdAt',
|
|
15
|
+
'updatedAt',
|
|
16
|
+
'name',
|
|
17
|
+
'size'
|
|
18
|
+
];
|
|
19
|
+
const isSortField = (value)=>SORT_FIELDS.includes(value);
|
|
20
|
+
const DEFAULT_RULE = 'updatedAt:DESC';
|
|
21
|
+
const compareByField = (field, a, b)=>{
|
|
22
|
+
if (field === 'size') {
|
|
23
|
+
return (a.size ?? 0) - (b.size ?? 0);
|
|
24
|
+
}
|
|
25
|
+
if (field === 'name') {
|
|
26
|
+
return a.name.localeCompare(b.name);
|
|
27
|
+
}
|
|
28
|
+
// `createdAt` / `updatedAt` are ISO-8601, which sorts correctly as text.
|
|
29
|
+
return (a[field] ?? '').localeCompare(b[field] ?? '');
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Comparator matching a `<field>:<ASC|DESC>` sort rule, as sent to the API.
|
|
33
|
+
*
|
|
34
|
+
* Ties break on id so the order is total: without it, two files sharing a
|
|
35
|
+
* timestamp could swap places between renders.
|
|
36
|
+
*/ const getAssetComparator = (rule = DEFAULT_RULE)=>{
|
|
37
|
+
const [field, requestedDirection] = rule.split(':');
|
|
38
|
+
// An unrecognised field discards the direction too: half-applying a rule we
|
|
39
|
+
// cannot read would order the list differently from the default it stands in for.
|
|
40
|
+
const isKnownField = isSortField(field);
|
|
41
|
+
const sortField = isKnownField ? field : 'updatedAt';
|
|
42
|
+
const direction = isKnownField ? requestedDirection : 'DESC';
|
|
43
|
+
const sign = direction === 'ASC' ? 1 : -1;
|
|
44
|
+
return (a, b)=>{
|
|
45
|
+
const byField = compareByField(sortField, a, b);
|
|
46
|
+
return byField !== 0 ? sign * byField : sign * (a.id - b.id);
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Places `uploaded` into `assets` at their sort position.
|
|
51
|
+
*
|
|
52
|
+
* An upload that sorts past everything loaded is dropped unless the list is
|
|
53
|
+
* fully loaded: it belongs on a page the user has not reached, and appending it
|
|
54
|
+
* would show it adjacent to the last loaded item instead of at its real place.
|
|
55
|
+
*/ const mergeUploadedAssets = ({ assets, uploaded, sort, hasNextPage })=>{
|
|
56
|
+
if (uploaded.length === 0) {
|
|
57
|
+
return assets;
|
|
58
|
+
}
|
|
59
|
+
const loadedIds = new Set(assets.map((asset)=>asset.id));
|
|
60
|
+
const compare = getAssetComparator(sort);
|
|
61
|
+
const fresh = uploaded.filter((asset)=>!loadedIds.has(asset.id)).sort(compare);
|
|
62
|
+
if (fresh.length === 0) {
|
|
63
|
+
return assets;
|
|
64
|
+
}
|
|
65
|
+
const merged = [
|
|
66
|
+
...assets
|
|
67
|
+
];
|
|
68
|
+
for (const asset of fresh){
|
|
69
|
+
const at = merged.findIndex((loaded)=>compare(asset, loaded) < 0);
|
|
70
|
+
if (at === -1) {
|
|
71
|
+
if (hasNextPage) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
merged.push(asset);
|
|
75
|
+
} else {
|
|
76
|
+
merged.splice(at, 0, asset);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return merged;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export { getAssetComparator, mergeUploadedAssets };
|
|
83
|
+
//# sourceMappingURL=mergeUploadedAssets.mjs.map
|