@strapi/upload 5.51.1 → 5.51.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/components/AssetCard/VideoPreview.js +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.js.map +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.mjs +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.mjs.map +1 -1
- package/dist/admin/components/TableList/TableList.js +1 -1
- package/dist/admin/components/TableList/TableList.js.map +1 -1
- package/dist/admin/components/TableList/TableList.mjs +1 -1
- package/dist/admin/components/TableList/TableList.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js +21 -4
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs +21 -4
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs.map +1 -1
- package/dist/admin/future/components/Drawer.js +3 -1
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +3 -1
- package/dist/admin/future/components/Drawer.mjs.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.js +200 -27
- package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.mjs +203 -30
- package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
- package/dist/admin/future/hooks/useMediaLibraryPermissions.js +35 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.js.map +1 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs +33 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js +203 -78
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +200 -75
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +75 -32
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +75 -32
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +46 -19
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +47 -20
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +5 -2
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +5 -2
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +100 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +98 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +10 -4
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +10 -4
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +140 -22
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +142 -24
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js +193 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs +191 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +18 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +18 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js +13 -3
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs +13 -3
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/EmptyState.js +62 -3
- package/dist/admin/future/pages/Assets/components/EmptyState.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs +62 -4
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterBadges.js +408 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +406 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.js +273 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +267 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +76 -19
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +78 -21
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +33 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +31 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js +21 -32
- package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs +22 -33
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js +40 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs +38 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +21 -5
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +21 -5
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +33 -12
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +33 -12
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +40 -4
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +40 -4
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js +168 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs +162 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js +201 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs +198 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js +2 -2
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -1
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +2 -2
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -1
- package/dist/admin/future/services/api.js +163 -28
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +163 -30
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +29 -17
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +29 -17
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +29 -17
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +29 -17
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/services/settings.js +10 -3
- package/dist/admin/future/services/settings.js.map +1 -1
- package/dist/admin/future/services/settings.mjs +10 -3
- package/dist/admin/future/services/settings.mjs.map +1 -1
- package/dist/admin/future/store/uploadProgress.js +109 -2
- package/dist/admin/future/store/uploadProgress.js.map +1 -1
- package/dist/admin/future/store/uploadProgress.mjs +105 -3
- package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
- package/dist/admin/future/utils/searchQueryParam.js +11 -0
- package/dist/admin/future/utils/searchQueryParam.js.map +1 -0
- package/dist/admin/future/utils/searchQueryParam.mjs +9 -0
- package/dist/admin/future/utils/searchQueryParam.mjs.map +1 -0
- package/dist/admin/hooks/useSelectionState.js +2 -4
- package/dist/admin/hooks/useSelectionState.js.map +1 -1
- package/dist/admin/hooks/useSelectionState.mjs +2 -4
- package/dist/admin/hooks/useSelectionState.mjs.map +1 -1
- package/dist/admin/shared/constants.js +43 -0
- package/dist/admin/shared/constants.js.map +1 -0
- package/dist/admin/shared/constants.mjs +38 -0
- package/dist/admin/shared/constants.mjs.map +1 -0
- package/dist/admin/src/future/hooks/useMediaLibraryPermissions.d.ts +21 -0
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetCropEditor.d.ts +7 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsSearchInput.d.ts +5 -0
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +14 -1
- package/dist/admin/src/future/pages/Assets/components/DateRangeCalendar.d.ts +8 -0
- package/dist/admin/src/future/pages/Assets/components/DropZone/UploadDropZoneContext.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/components/EmptyState.d.ts +18 -1
- package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +22 -0
- package/dist/admin/src/future/pages/Assets/components/FolderTree/FolderTree.d.ts +13 -4
- package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +16 -0
- package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSearch.d.ts +12 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +13 -3
- package/dist/admin/src/future/pages/Assets/hooks/useListFilters.d.ts +69 -0
- package/dist/admin/src/future/pages/Assets/utils/buildAssetFilters.d.ts +46 -0
- package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +3 -4
- package/dist/admin/src/future/services/api.d.ts +53 -5
- package/dist/admin/src/future/services/assets.d.ts +9 -1
- package/dist/admin/src/future/services/folders.d.ts +3 -0
- package/dist/admin/src/future/services/settings.d.ts +2 -2
- package/dist/admin/src/future/store/uploadProgress.d.ts +83 -1
- package/dist/admin/src/future/utils/searchQueryParam.d.ts +7 -0
- package/dist/admin/src/hooks/useAiAvailability.d.ts +1 -1
- package/dist/admin/src/hooks/useEditAsset.d.ts +4 -4
- package/dist/admin/src/hooks/useEditFolder.d.ts +4 -4
- package/dist/admin/src/hooks/useSettings.d.ts +1 -0
- package/dist/admin/src/hooks/useTracking.d.ts +1 -1
- package/dist/admin/src/hooks/useUpload.d.ts +1 -1
- package/dist/admin/translations/en.json.js +53 -3
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +53 -3
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/urlYupSchema.js +33 -9
- package/dist/admin/utils/urlYupSchema.js.map +1 -1
- package/dist/admin/utils/urlYupSchema.mjs +33 -9
- package/dist/admin/utils/urlYupSchema.mjs.map +1 -1
- package/dist/server/config.js +13 -5
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +13 -5
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/constants.js.map +1 -1
- package/dist/server/constants.mjs.map +1 -1
- package/dist/server/controllers/admin-file.js +30 -0
- package/dist/server/controllers/admin-file.js.map +1 -1
- package/dist/server/controllers/admin-file.mjs +30 -0
- package/dist/server/controllers/admin-file.mjs.map +1 -1
- package/dist/server/controllers/admin-settings.js +20 -2
- package/dist/server/controllers/admin-settings.js.map +1 -1
- package/dist/server/controllers/admin-settings.mjs +21 -3
- package/dist/server/controllers/admin-settings.mjs.map +1 -1
- package/dist/server/controllers/content-api.js +1 -2
- package/dist/server/controllers/content-api.js.map +1 -1
- package/dist/server/controllers/content-api.mjs +2 -2
- package/dist/server/controllers/content-api.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/ai-metadata.js +12 -0
- package/dist/server/controllers/validation/admin/ai-metadata.js.map +1 -0
- package/dist/server/controllers/validation/admin/ai-metadata.mjs +10 -0
- package/dist/server/controllers/validation/admin/ai-metadata.mjs.map +1 -0
- package/dist/server/controllers/validation/admin/settings.js +4 -1
- package/dist/server/controllers/validation/admin/settings.js.map +1 -1
- package/dist/server/controllers/validation/admin/settings.mjs +1 -1
- package/dist/server/controllers/validation/admin/settings.mjs.map +1 -1
- package/dist/server/routes/admin.js +18 -0
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/admin.mjs +18 -0
- package/dist/server/routes/admin.mjs.map +1 -1
- package/dist/server/services/ai-metadata.js +108 -11
- package/dist/server/services/ai-metadata.js.map +1 -1
- package/dist/server/services/ai-metadata.mjs +108 -11
- package/dist/server/services/ai-metadata.mjs.map +1 -1
- package/dist/server/services/file.js +86 -13
- package/dist/server/services/file.js.map +1 -1
- package/dist/server/services/file.mjs +87 -14
- package/dist/server/services/file.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js +4 -4
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs +4 -4
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/shared/constants.js +43 -0
- package/dist/server/shared/constants.js.map +1 -0
- package/dist/server/shared/constants.mjs +38 -0
- package/dist/server/shared/constants.mjs.map +1 -0
- package/dist/server/src/config.d.ts +2 -0
- package/dist/server/src/config.d.ts.map +1 -1
- package/dist/server/src/constants.d.ts +2 -1
- package/dist/server/src/constants.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-file.d.ts +10 -0
- package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +1 -0
- package/dist/server/src/controllers/index.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts +2 -0
- package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/admin/settings.d.ts +1 -1
- package/dist/server/src/controllers/validation/admin/settings.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +7 -1
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/routes/admin.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata.d.ts +19 -0
- package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
- package/dist/server/src/services/file.d.ts +12 -2
- package/dist/server/src/services/file.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +4 -1
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/types.d.ts +3 -0
- package/dist/server/src/types.d.ts.map +1 -1
- package/dist/shared/constants.d.ts +35 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/contracts/files.d.ts +25 -0
- package/dist/shared/contracts/files.d.ts.map +1 -1
- package/dist/shared/contracts/settings.d.ts +7 -0
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInfiniteAssets.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useInfiniteAssets.ts"],"sourcesContent":["import { useState, useCallback, useMemo, useEffect, useRef } from 'react';\n\nimport { useGetAssetsQuery } from '../../../services/assets';\n\nimport type { File } from '../../../../../../shared/contracts/files';\n\nconst PAGE_SIZE = 20;\n\ninterface UseInfiniteAssetsOptions {\n folder?: number | null;\n sort?: string;\n}\n\nconst useInfiniteAssets = ({
|
|
1
|
+
{"version":3,"file":"useInfiniteAssets.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useInfiniteAssets.ts"],"sourcesContent":["import { useState, useCallback, useMemo, useEffect, useRef } from 'react';\n\nimport { useGetAssetsQuery } from '../../../services/assets';\n\nimport type { File, Pagination } from '../../../../../../shared/contracts/files';\n\nconst PAGE_SIZE = 20;\n\ninterface UseInfiniteAssetsOptions {\n folder?: number | null;\n sort?: string;\n search?: string;\n /** Extra `filters[$and]` entries for the files query. */\n filters?: Record<string, unknown>[];\n /**\n * Stable fingerprint of `filters` — a change resets the accumulated pages\n * (the array identity itself changes every render).\n */\n filtersKey?: string;\n /** Structural switch: an `is [folder]`-only type badge means no file can match. */\n enabled?: boolean;\n}\n\nconst useInfiniteAssets = ({\n folder = null,\n sort,\n search,\n filters,\n filtersKey = '',\n enabled = true,\n}: UseInfiniteAssetsOptions = {}) => {\n const [page, setPage] = useState(1);\n const lastResultsRef = useRef<File[]>([]);\n const lastPaginationRef = useRef<Pagination | undefined>(undefined);\n const isMountRef = useRef(true);\n const isSearchMountRef = useRef(true);\n\n const {\n currentData: data,\n isLoading,\n isFetching,\n error,\n } = useGetAssetsQuery(\n {\n folder,\n page,\n pageSize: PAGE_SIZE,\n sort,\n search,\n filters,\n },\n { skip: !enabled }\n );\n\n const pagination = data?.pagination;\n\n if (pagination) {\n lastPaginationRef.current = pagination;\n }\n\n // Accumulate pages. When cache is invalidated the current page is refetched\n // detect this and reset to avoid a gap in the results.\n const assets = useMemo(() => {\n if (!data) {\n return lastResultsRef.current;\n }\n\n const currentPageResults = data.results;\n\n if (page === 1) {\n lastResultsRef.current = currentPageResults;\n } else {\n // If accumulated length doesn't match expectation, cache was cleared\n const expectedPrior = (page - 1) * PAGE_SIZE;\n if (lastResultsRef.current.length < expectedPrior - PAGE_SIZE) {\n return lastResultsRef.current;\n }\n\n // Only append if these aren't already accumulated\n if (lastResultsRef.current.length < page * PAGE_SIZE) {\n lastResultsRef.current = [...lastResultsRef.current, ...currentPageResults];\n }\n }\n\n return lastResultsRef.current;\n }, [data, page]);\n\n // Reset on filter/sort change — skip the initial mount since the memo\n // already handles page 1 correctly\n useEffect(() => {\n if (isMountRef.current) {\n isMountRef.current = false;\n\n return;\n }\n setPage(1);\n lastResultsRef.current = [];\n }, [folder, sort, filtersKey]);\n\n // A search change resets pagination but keeps the previous results rendered\n // until the new page 1 lands, which replaces them.\n useEffect(() => {\n if (isSearchMountRef.current) {\n isSearchMountRef.current = false;\n\n return;\n }\n setPage(1);\n }, [search]);\n\n // Deliberately the live value, not the stale-tolerant one below: paging must\n // never be driven by a total that belongs to the previous query.\n const hasNextPage = pagination ? page < pagination.pageCount : false;\n const isFetchingMore = isFetching && page > 1;\n\n const fetchNextPage = useCallback(() => {\n setPage((prev) => prev + 1);\n }, []);\n\n if (!enabled) {\n return {\n assets: [] as File[],\n pagination: undefined,\n isLoading: false,\n isFetchingMore: false,\n hasNextPage: false,\n fetchNextPage,\n error: undefined,\n };\n }\n\n return {\n assets,\n // Reported alongside the stale `assets` above so a consumer showing the total\n // doesn't flash zero mid-transition.\n pagination: pagination ?? lastPaginationRef.current,\n isLoading,\n isFetchingMore,\n hasNextPage,\n fetchNextPage,\n error,\n };\n};\n\nexport { useInfiniteAssets };\nexport { PAGE_SIZE };\n"],"names":["PAGE_SIZE","useInfiniteAssets","folder","sort","search","filters","filtersKey","enabled","page","setPage","useState","lastResultsRef","useRef","lastPaginationRef","undefined","isMountRef","isSearchMountRef","currentData","data","isLoading","isFetching","error","useGetAssetsQuery","pageSize","skip","pagination","current","assets","useMemo","currentPageResults","results","expectedPrior","length","useEffect","hasNextPage","pageCount","isFetchingMore","fetchNextPage","useCallback","prev"],"mappings":"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"}
|
|
@@ -2,17 +2,26 @@ import { useState, useRef, useMemo, useEffect, useCallback } from 'react';
|
|
|
2
2
|
import { useGetAssetsQuery } from '../../../services/assets.mjs';
|
|
3
3
|
|
|
4
4
|
const PAGE_SIZE = 20;
|
|
5
|
-
const useInfiniteAssets = ({ folder = null, sort } = {})=>{
|
|
5
|
+
const useInfiniteAssets = ({ folder = null, sort, search, filters, filtersKey = '', enabled = true } = {})=>{
|
|
6
6
|
const [page, setPage] = useState(1);
|
|
7
7
|
const lastResultsRef = useRef([]);
|
|
8
|
+
const lastPaginationRef = useRef(undefined);
|
|
8
9
|
const isMountRef = useRef(true);
|
|
10
|
+
const isSearchMountRef = useRef(true);
|
|
9
11
|
const { currentData: data, isLoading, isFetching, error } = useGetAssetsQuery({
|
|
10
12
|
folder,
|
|
11
13
|
page,
|
|
12
14
|
pageSize: PAGE_SIZE,
|
|
13
|
-
sort
|
|
15
|
+
sort,
|
|
16
|
+
search,
|
|
17
|
+
filters
|
|
18
|
+
}, {
|
|
19
|
+
skip: !enabled
|
|
14
20
|
});
|
|
15
21
|
const pagination = data?.pagination;
|
|
22
|
+
if (pagination) {
|
|
23
|
+
lastPaginationRef.current = pagination;
|
|
24
|
+
}
|
|
16
25
|
// Accumulate pages. When cache is invalidated the current page is refetched
|
|
17
26
|
// detect this and reset to avoid a gap in the results.
|
|
18
27
|
const assets = useMemo(()=>{
|
|
@@ -52,16 +61,43 @@ const useInfiniteAssets = ({ folder = null, sort } = {})=>{
|
|
|
52
61
|
lastResultsRef.current = [];
|
|
53
62
|
}, [
|
|
54
63
|
folder,
|
|
55
|
-
sort
|
|
64
|
+
sort,
|
|
65
|
+
filtersKey
|
|
56
66
|
]);
|
|
67
|
+
// A search change resets pagination but keeps the previous results rendered
|
|
68
|
+
// until the new page 1 lands, which replaces them.
|
|
69
|
+
useEffect(()=>{
|
|
70
|
+
if (isSearchMountRef.current) {
|
|
71
|
+
isSearchMountRef.current = false;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
setPage(1);
|
|
75
|
+
}, [
|
|
76
|
+
search
|
|
77
|
+
]);
|
|
78
|
+
// Deliberately the live value, not the stale-tolerant one below: paging must
|
|
79
|
+
// never be driven by a total that belongs to the previous query.
|
|
57
80
|
const hasNextPage = pagination ? page < pagination.pageCount : false;
|
|
58
81
|
const isFetchingMore = isFetching && page > 1;
|
|
59
82
|
const fetchNextPage = useCallback(()=>{
|
|
60
83
|
setPage((prev)=>prev + 1);
|
|
61
84
|
}, []);
|
|
85
|
+
if (!enabled) {
|
|
86
|
+
return {
|
|
87
|
+
assets: [],
|
|
88
|
+
pagination: undefined,
|
|
89
|
+
isLoading: false,
|
|
90
|
+
isFetchingMore: false,
|
|
91
|
+
hasNextPage: false,
|
|
92
|
+
fetchNextPage,
|
|
93
|
+
error: undefined
|
|
94
|
+
};
|
|
95
|
+
}
|
|
62
96
|
return {
|
|
63
97
|
assets,
|
|
64
|
-
|
|
98
|
+
// Reported alongside the stale `assets` above so a consumer showing the total
|
|
99
|
+
// doesn't flash zero mid-transition.
|
|
100
|
+
pagination: pagination ?? lastPaginationRef.current,
|
|
65
101
|
isLoading,
|
|
66
102
|
isFetchingMore,
|
|
67
103
|
hasNextPage,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInfiniteAssets.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useInfiniteAssets.ts"],"sourcesContent":["import { useState, useCallback, useMemo, useEffect, useRef } from 'react';\n\nimport { useGetAssetsQuery } from '../../../services/assets';\n\nimport type { File } from '../../../../../../shared/contracts/files';\n\nconst PAGE_SIZE = 20;\n\ninterface UseInfiniteAssetsOptions {\n folder?: number | null;\n sort?: string;\n}\n\nconst useInfiniteAssets = ({
|
|
1
|
+
{"version":3,"file":"useInfiniteAssets.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useInfiniteAssets.ts"],"sourcesContent":["import { useState, useCallback, useMemo, useEffect, useRef } from 'react';\n\nimport { useGetAssetsQuery } from '../../../services/assets';\n\nimport type { File, Pagination } from '../../../../../../shared/contracts/files';\n\nconst PAGE_SIZE = 20;\n\ninterface UseInfiniteAssetsOptions {\n folder?: number | null;\n sort?: string;\n search?: string;\n /** Extra `filters[$and]` entries for the files query. */\n filters?: Record<string, unknown>[];\n /**\n * Stable fingerprint of `filters` — a change resets the accumulated pages\n * (the array identity itself changes every render).\n */\n filtersKey?: string;\n /** Structural switch: an `is [folder]`-only type badge means no file can match. */\n enabled?: boolean;\n}\n\nconst useInfiniteAssets = ({\n folder = null,\n sort,\n search,\n filters,\n filtersKey = '',\n enabled = true,\n}: UseInfiniteAssetsOptions = {}) => {\n const [page, setPage] = useState(1);\n const lastResultsRef = useRef<File[]>([]);\n const lastPaginationRef = useRef<Pagination | undefined>(undefined);\n const isMountRef = useRef(true);\n const isSearchMountRef = useRef(true);\n\n const {\n currentData: data,\n isLoading,\n isFetching,\n error,\n } = useGetAssetsQuery(\n {\n folder,\n page,\n pageSize: PAGE_SIZE,\n sort,\n search,\n filters,\n },\n { skip: !enabled }\n );\n\n const pagination = data?.pagination;\n\n if (pagination) {\n lastPaginationRef.current = pagination;\n }\n\n // Accumulate pages. When cache is invalidated the current page is refetched\n // detect this and reset to avoid a gap in the results.\n const assets = useMemo(() => {\n if (!data) {\n return lastResultsRef.current;\n }\n\n const currentPageResults = data.results;\n\n if (page === 1) {\n lastResultsRef.current = currentPageResults;\n } else {\n // If accumulated length doesn't match expectation, cache was cleared\n const expectedPrior = (page - 1) * PAGE_SIZE;\n if (lastResultsRef.current.length < expectedPrior - PAGE_SIZE) {\n return lastResultsRef.current;\n }\n\n // Only append if these aren't already accumulated\n if (lastResultsRef.current.length < page * PAGE_SIZE) {\n lastResultsRef.current = [...lastResultsRef.current, ...currentPageResults];\n }\n }\n\n return lastResultsRef.current;\n }, [data, page]);\n\n // Reset on filter/sort change — skip the initial mount since the memo\n // already handles page 1 correctly\n useEffect(() => {\n if (isMountRef.current) {\n isMountRef.current = false;\n\n return;\n }\n setPage(1);\n lastResultsRef.current = [];\n }, [folder, sort, filtersKey]);\n\n // A search change resets pagination but keeps the previous results rendered\n // until the new page 1 lands, which replaces them.\n useEffect(() => {\n if (isSearchMountRef.current) {\n isSearchMountRef.current = false;\n\n return;\n }\n setPage(1);\n }, [search]);\n\n // Deliberately the live value, not the stale-tolerant one below: paging must\n // never be driven by a total that belongs to the previous query.\n const hasNextPage = pagination ? page < pagination.pageCount : false;\n const isFetchingMore = isFetching && page > 1;\n\n const fetchNextPage = useCallback(() => {\n setPage((prev) => prev + 1);\n }, []);\n\n if (!enabled) {\n return {\n assets: [] as File[],\n pagination: undefined,\n isLoading: false,\n isFetchingMore: false,\n hasNextPage: false,\n fetchNextPage,\n error: undefined,\n };\n }\n\n return {\n assets,\n // Reported alongside the stale `assets` above so a consumer showing the total\n // doesn't flash zero mid-transition.\n pagination: pagination ?? lastPaginationRef.current,\n isLoading,\n isFetchingMore,\n hasNextPage,\n fetchNextPage,\n error,\n };\n};\n\nexport { useInfiniteAssets };\nexport { PAGE_SIZE };\n"],"names":["PAGE_SIZE","useInfiniteAssets","folder","sort","search","filters","filtersKey","enabled","page","setPage","useState","lastResultsRef","useRef","lastPaginationRef","undefined","isMountRef","isSearchMountRef","currentData","data","isLoading","isFetching","error","useGetAssetsQuery","pageSize","skip","pagination","current","assets","useMemo","currentPageResults","results","expectedPrior","length","useEffect","hasNextPage","pageCount","isFetchingMore","fetchNextPage","useCallback","prev"],"mappings":"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"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Filter state for the assets list, backed by a single `?filters=` query param
|
|
7
|
+
* so the applied filters are shareable and survive Back/refresh.
|
|
8
|
+
*
|
|
9
|
+
* Serialized grammar — badges separated by `;`, one badge is
|
|
10
|
+
* `<field>:<condition>:<value>`:
|
|
11
|
+
*
|
|
12
|
+
* - `type:is:picture,audio` / `type:not:folder`
|
|
13
|
+
* - `created:within:1week` / `updated:notwithin:3months` / `created:exact:1day`
|
|
14
|
+
* - `created:rangeis:2024-01-01..2024-04-07` / `created:rangenot:...`
|
|
15
|
+
*
|
|
16
|
+
* Relative presets stay relative in the URL on purpose: the badge label
|
|
17
|
+
* ("1 week ago") remains truthful forever and a shared link means "within the
|
|
18
|
+
* last week from whenever it is opened". Resolution to absolute timestamps
|
|
19
|
+
* happens at query-build time (see utils/buildAssetFilters.ts).
|
|
20
|
+
*
|
|
21
|
+
* Several badges for the same field are allowed, even contradictory ones —
|
|
22
|
+
* everything is combined with AND; contradictions simply produce the filtered
|
|
23
|
+
* empty state.
|
|
24
|
+
*/ const TYPE_VALUES = [
|
|
25
|
+
'folder',
|
|
26
|
+
'picture',
|
|
27
|
+
'audio',
|
|
28
|
+
'video',
|
|
29
|
+
'document'
|
|
30
|
+
];
|
|
31
|
+
const DATE_PRESETS = [
|
|
32
|
+
'1day',
|
|
33
|
+
'3days',
|
|
34
|
+
'1week',
|
|
35
|
+
'1month',
|
|
36
|
+
'3months',
|
|
37
|
+
'6months',
|
|
38
|
+
'1year'
|
|
39
|
+
];
|
|
40
|
+
const FIELD_TOKENS = {
|
|
41
|
+
created: 'createdAt',
|
|
42
|
+
updated: 'updatedAt'
|
|
43
|
+
};
|
|
44
|
+
const FIELD_TOKEN_BY_FIELD = {
|
|
45
|
+
createdAt: 'created',
|
|
46
|
+
updatedAt: 'updated'
|
|
47
|
+
};
|
|
48
|
+
const PRESET_CONDITION_TOKENS = {
|
|
49
|
+
exact: 'isExactly',
|
|
50
|
+
within: 'withinLast',
|
|
51
|
+
notwithin: 'notWithinLast'
|
|
52
|
+
};
|
|
53
|
+
const PRESET_TOKEN_BY_CONDITION = {
|
|
54
|
+
isExactly: 'exact',
|
|
55
|
+
withinLast: 'within',
|
|
56
|
+
notWithinLast: 'notwithin'
|
|
57
|
+
};
|
|
58
|
+
const RANGE_CONDITION_TOKENS = {
|
|
59
|
+
rangeis: 'is',
|
|
60
|
+
rangenot: 'isNot'
|
|
61
|
+
};
|
|
62
|
+
const RANGE_TOKEN_BY_CONDITION = {
|
|
63
|
+
is: 'rangeis',
|
|
64
|
+
isNot: 'rangenot'
|
|
65
|
+
};
|
|
66
|
+
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
67
|
+
const isTypeValue = (value)=>TYPE_VALUES.includes(value);
|
|
68
|
+
const isDatePreset = (value)=>DATE_PRESETS.includes(value);
|
|
69
|
+
const parseBadge = (raw)=>{
|
|
70
|
+
const [field, condition, value] = raw.split(':');
|
|
71
|
+
if (!field || !condition || !value) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (field === 'type') {
|
|
75
|
+
if (condition !== 'is' && condition !== 'not') {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const values = value.split(',').filter(isTypeValue);
|
|
79
|
+
return values.length > 0 ? {
|
|
80
|
+
kind: 'type',
|
|
81
|
+
condition: condition === 'is' ? 'is' : 'isNot',
|
|
82
|
+
values
|
|
83
|
+
} : null;
|
|
84
|
+
}
|
|
85
|
+
const dateField = FIELD_TOKENS[field];
|
|
86
|
+
if (!dateField) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (condition in PRESET_CONDITION_TOKENS) {
|
|
90
|
+
return isDatePreset(value) ? {
|
|
91
|
+
kind: 'date',
|
|
92
|
+
field: dateField,
|
|
93
|
+
mode: 'preset',
|
|
94
|
+
condition: PRESET_CONDITION_TOKENS[condition],
|
|
95
|
+
preset: value
|
|
96
|
+
} : null;
|
|
97
|
+
}
|
|
98
|
+
if (condition in RANGE_CONDITION_TOKENS) {
|
|
99
|
+
const [from, to] = value.split('..');
|
|
100
|
+
return DATE_RE.test(from ?? '') && DATE_RE.test(to ?? '') ? {
|
|
101
|
+
kind: 'date',
|
|
102
|
+
field: dateField,
|
|
103
|
+
mode: 'range',
|
|
104
|
+
condition: RANGE_CONDITION_TOKENS[condition],
|
|
105
|
+
from,
|
|
106
|
+
to
|
|
107
|
+
} : null;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* `unknown` on purpose: the query string is user-editable, and qs parses
|
|
113
|
+
* shapes like `?filters[]=a&filters[]=b` into arrays — the guard is what
|
|
114
|
+
* makes the caller's `?filters=` type an assertion rather than a crash.
|
|
115
|
+
*/ const parseFiltersParam = (raw)=>{
|
|
116
|
+
if (typeof raw !== 'string' || raw === '') {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
return raw.split(';').map(parseBadge).filter((filter)=>filter !== null);
|
|
120
|
+
};
|
|
121
|
+
const serializeBadge = (filter)=>{
|
|
122
|
+
if (filter.kind === 'type') {
|
|
123
|
+
return `type:${filter.condition === 'is' ? 'is' : 'not'}:${filter.values.join(',')}`;
|
|
124
|
+
}
|
|
125
|
+
const field = FIELD_TOKEN_BY_FIELD[filter.field];
|
|
126
|
+
if (filter.mode === 'preset') {
|
|
127
|
+
return `${field}:${PRESET_TOKEN_BY_CONDITION[filter.condition]}:${filter.preset}`;
|
|
128
|
+
}
|
|
129
|
+
return `${field}:${RANGE_TOKEN_BY_CONDITION[filter.condition]}:${filter.from}..${filter.to}`;
|
|
130
|
+
};
|
|
131
|
+
const serializeFilters = (filters)=>filters.map(serializeBadge).join(';');
|
|
132
|
+
const useListFilters = ()=>{
|
|
133
|
+
const [{ query }, setQuery] = strapiAdmin.useQueryParams();
|
|
134
|
+
const filters = parseFiltersParam(query?.filters);
|
|
135
|
+
// `replace: true` on every write: filters are edited in rapid micro-steps
|
|
136
|
+
// (each checkbox of the keep-open Type submenu, each badge segment edit), so
|
|
137
|
+
// pushing would turn one logical change into a stack of Back entries. The
|
|
138
|
+
// URL stays shareable and refresh-safe either way.
|
|
139
|
+
const writeFilters = (next)=>{
|
|
140
|
+
if (next.length === 0) {
|
|
141
|
+
setQuery({
|
|
142
|
+
filters: ''
|
|
143
|
+
}, 'remove', true);
|
|
144
|
+
} else {
|
|
145
|
+
setQuery({
|
|
146
|
+
filters: serializeFilters(next)
|
|
147
|
+
}, 'push', true);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
return {
|
|
151
|
+
filters,
|
|
152
|
+
serialized: serializeFilters(filters),
|
|
153
|
+
addFilter: (filter)=>writeFilters([
|
|
154
|
+
...filters,
|
|
155
|
+
filter
|
|
156
|
+
]),
|
|
157
|
+
updateFilter: (index, filter)=>writeFilters(filters.map((f, i)=>i === index ? filter : f)),
|
|
158
|
+
removeFilter: (index)=>writeFilters(filters.filter((_, i)=>i !== index)),
|
|
159
|
+
clearFilters: ()=>writeFilters([])
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
exports.DATE_PRESETS = DATE_PRESETS;
|
|
164
|
+
exports.TYPE_VALUES = TYPE_VALUES;
|
|
165
|
+
exports.parseFiltersParam = parseFiltersParam;
|
|
166
|
+
exports.serializeFilters = serializeFilters;
|
|
167
|
+
exports.useListFilters = useListFilters;
|
|
168
|
+
//# sourceMappingURL=useListFilters.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { useQueryParams } from '@strapi/admin/strapi-admin';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Filter state for the assets list, backed by a single `?filters=` query param
|
|
5
|
+
* so the applied filters are shareable and survive Back/refresh.
|
|
6
|
+
*
|
|
7
|
+
* Serialized grammar — badges separated by `;`, one badge is
|
|
8
|
+
* `<field>:<condition>:<value>`:
|
|
9
|
+
*
|
|
10
|
+
* - `type:is:picture,audio` / `type:not:folder`
|
|
11
|
+
* - `created:within:1week` / `updated:notwithin:3months` / `created:exact:1day`
|
|
12
|
+
* - `created:rangeis:2024-01-01..2024-04-07` / `created:rangenot:...`
|
|
13
|
+
*
|
|
14
|
+
* Relative presets stay relative in the URL on purpose: the badge label
|
|
15
|
+
* ("1 week ago") remains truthful forever and a shared link means "within the
|
|
16
|
+
* last week from whenever it is opened". Resolution to absolute timestamps
|
|
17
|
+
* happens at query-build time (see utils/buildAssetFilters.ts).
|
|
18
|
+
*
|
|
19
|
+
* Several badges for the same field are allowed, even contradictory ones —
|
|
20
|
+
* everything is combined with AND; contradictions simply produce the filtered
|
|
21
|
+
* empty state.
|
|
22
|
+
*/ const TYPE_VALUES = [
|
|
23
|
+
'folder',
|
|
24
|
+
'picture',
|
|
25
|
+
'audio',
|
|
26
|
+
'video',
|
|
27
|
+
'document'
|
|
28
|
+
];
|
|
29
|
+
const DATE_PRESETS = [
|
|
30
|
+
'1day',
|
|
31
|
+
'3days',
|
|
32
|
+
'1week',
|
|
33
|
+
'1month',
|
|
34
|
+
'3months',
|
|
35
|
+
'6months',
|
|
36
|
+
'1year'
|
|
37
|
+
];
|
|
38
|
+
const FIELD_TOKENS = {
|
|
39
|
+
created: 'createdAt',
|
|
40
|
+
updated: 'updatedAt'
|
|
41
|
+
};
|
|
42
|
+
const FIELD_TOKEN_BY_FIELD = {
|
|
43
|
+
createdAt: 'created',
|
|
44
|
+
updatedAt: 'updated'
|
|
45
|
+
};
|
|
46
|
+
const PRESET_CONDITION_TOKENS = {
|
|
47
|
+
exact: 'isExactly',
|
|
48
|
+
within: 'withinLast',
|
|
49
|
+
notwithin: 'notWithinLast'
|
|
50
|
+
};
|
|
51
|
+
const PRESET_TOKEN_BY_CONDITION = {
|
|
52
|
+
isExactly: 'exact',
|
|
53
|
+
withinLast: 'within',
|
|
54
|
+
notWithinLast: 'notwithin'
|
|
55
|
+
};
|
|
56
|
+
const RANGE_CONDITION_TOKENS = {
|
|
57
|
+
rangeis: 'is',
|
|
58
|
+
rangenot: 'isNot'
|
|
59
|
+
};
|
|
60
|
+
const RANGE_TOKEN_BY_CONDITION = {
|
|
61
|
+
is: 'rangeis',
|
|
62
|
+
isNot: 'rangenot'
|
|
63
|
+
};
|
|
64
|
+
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
65
|
+
const isTypeValue = (value)=>TYPE_VALUES.includes(value);
|
|
66
|
+
const isDatePreset = (value)=>DATE_PRESETS.includes(value);
|
|
67
|
+
const parseBadge = (raw)=>{
|
|
68
|
+
const [field, condition, value] = raw.split(':');
|
|
69
|
+
if (!field || !condition || !value) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
if (field === 'type') {
|
|
73
|
+
if (condition !== 'is' && condition !== 'not') {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const values = value.split(',').filter(isTypeValue);
|
|
77
|
+
return values.length > 0 ? {
|
|
78
|
+
kind: 'type',
|
|
79
|
+
condition: condition === 'is' ? 'is' : 'isNot',
|
|
80
|
+
values
|
|
81
|
+
} : null;
|
|
82
|
+
}
|
|
83
|
+
const dateField = FIELD_TOKENS[field];
|
|
84
|
+
if (!dateField) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
if (condition in PRESET_CONDITION_TOKENS) {
|
|
88
|
+
return isDatePreset(value) ? {
|
|
89
|
+
kind: 'date',
|
|
90
|
+
field: dateField,
|
|
91
|
+
mode: 'preset',
|
|
92
|
+
condition: PRESET_CONDITION_TOKENS[condition],
|
|
93
|
+
preset: value
|
|
94
|
+
} : null;
|
|
95
|
+
}
|
|
96
|
+
if (condition in RANGE_CONDITION_TOKENS) {
|
|
97
|
+
const [from, to] = value.split('..');
|
|
98
|
+
return DATE_RE.test(from ?? '') && DATE_RE.test(to ?? '') ? {
|
|
99
|
+
kind: 'date',
|
|
100
|
+
field: dateField,
|
|
101
|
+
mode: 'range',
|
|
102
|
+
condition: RANGE_CONDITION_TOKENS[condition],
|
|
103
|
+
from,
|
|
104
|
+
to
|
|
105
|
+
} : null;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* `unknown` on purpose: the query string is user-editable, and qs parses
|
|
111
|
+
* shapes like `?filters[]=a&filters[]=b` into arrays — the guard is what
|
|
112
|
+
* makes the caller's `?filters=` type an assertion rather than a crash.
|
|
113
|
+
*/ const parseFiltersParam = (raw)=>{
|
|
114
|
+
if (typeof raw !== 'string' || raw === '') {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
return raw.split(';').map(parseBadge).filter((filter)=>filter !== null);
|
|
118
|
+
};
|
|
119
|
+
const serializeBadge = (filter)=>{
|
|
120
|
+
if (filter.kind === 'type') {
|
|
121
|
+
return `type:${filter.condition === 'is' ? 'is' : 'not'}:${filter.values.join(',')}`;
|
|
122
|
+
}
|
|
123
|
+
const field = FIELD_TOKEN_BY_FIELD[filter.field];
|
|
124
|
+
if (filter.mode === 'preset') {
|
|
125
|
+
return `${field}:${PRESET_TOKEN_BY_CONDITION[filter.condition]}:${filter.preset}`;
|
|
126
|
+
}
|
|
127
|
+
return `${field}:${RANGE_TOKEN_BY_CONDITION[filter.condition]}:${filter.from}..${filter.to}`;
|
|
128
|
+
};
|
|
129
|
+
const serializeFilters = (filters)=>filters.map(serializeBadge).join(';');
|
|
130
|
+
const useListFilters = ()=>{
|
|
131
|
+
const [{ query }, setQuery] = useQueryParams();
|
|
132
|
+
const filters = parseFiltersParam(query?.filters);
|
|
133
|
+
// `replace: true` on every write: filters are edited in rapid micro-steps
|
|
134
|
+
// (each checkbox of the keep-open Type submenu, each badge segment edit), so
|
|
135
|
+
// pushing would turn one logical change into a stack of Back entries. The
|
|
136
|
+
// URL stays shareable and refresh-safe either way.
|
|
137
|
+
const writeFilters = (next)=>{
|
|
138
|
+
if (next.length === 0) {
|
|
139
|
+
setQuery({
|
|
140
|
+
filters: ''
|
|
141
|
+
}, 'remove', true);
|
|
142
|
+
} else {
|
|
143
|
+
setQuery({
|
|
144
|
+
filters: serializeFilters(next)
|
|
145
|
+
}, 'push', true);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
return {
|
|
149
|
+
filters,
|
|
150
|
+
serialized: serializeFilters(filters),
|
|
151
|
+
addFilter: (filter)=>writeFilters([
|
|
152
|
+
...filters,
|
|
153
|
+
filter
|
|
154
|
+
]),
|
|
155
|
+
updateFilter: (index, filter)=>writeFilters(filters.map((f, i)=>i === index ? filter : f)),
|
|
156
|
+
removeFilter: (index)=>writeFilters(filters.filter((_, i)=>i !== index)),
|
|
157
|
+
clearFilters: ()=>writeFilters([])
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export { DATE_PRESETS, TYPE_VALUES, parseFiltersParam, serializeFilters, useListFilters };
|
|
162
|
+
//# sourceMappingURL=useListFilters.mjs.map
|
|
@@ -0,0 +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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"}
|