@strapi/upload 5.59.0 → 5.60.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/AssetPickerDialog/AssetPickerDialog.js +434 -0
- package/dist/admin/components/AssetPickerDialog/AssetPickerDialog.js.map +1 -0
- package/dist/admin/components/AssetPickerDialog/AssetPickerDialog.mjs +432 -0
- package/dist/admin/components/AssetPickerDialog/AssetPickerDialog.mjs.map +1 -0
- package/dist/admin/components/AssetPickerDialog/FolderBreadcrumbs.js +91 -0
- package/dist/admin/components/AssetPickerDialog/FolderBreadcrumbs.js.map +1 -0
- package/dist/admin/components/AssetPickerDialog/FolderBreadcrumbs.mjs +89 -0
- package/dist/admin/components/AssetPickerDialog/FolderBreadcrumbs.mjs.map +1 -0
- package/dist/admin/components/AssetPickerDialog/PickerSelectionProvider.js +178 -0
- package/dist/admin/components/AssetPickerDialog/PickerSelectionProvider.js.map +1 -0
- package/dist/admin/components/AssetPickerDialog/PickerSelectionProvider.mjs +175 -0
- package/dist/admin/components/AssetPickerDialog/PickerSelectionProvider.mjs.map +1 -0
- package/dist/admin/components/AssetPickerDialog/SortableSelection.js +143 -0
- package/dist/admin/components/AssetPickerDialog/SortableSelection.js.map +1 -0
- package/dist/admin/components/AssetPickerDialog/SortableSelection.mjs +141 -0
- package/dist/admin/components/AssetPickerDialog/SortableSelection.mjs.map +1 -0
- package/dist/admin/components/AssetPickerDialog/pickerSelection.js +49 -0
- package/dist/admin/components/AssetPickerDialog/pickerSelection.js.map +1 -0
- package/dist/admin/components/AssetPickerDialog/pickerSelection.mjs +41 -0
- package/dist/admin/components/AssetPickerDialog/pickerSelection.mjs.map +1 -0
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js +4 -3
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs +4 -3
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/AssetCard.js +240 -0
- package/dist/admin/components/MediaLibraryInput/AssetCard.js.map +1 -0
- package/dist/admin/components/MediaLibraryInput/AssetCard.mjs +237 -0
- package/dist/admin/components/MediaLibraryInput/AssetCard.mjs.map +1 -0
- package/dist/admin/components/MediaLibraryInput/AssetCarousel.js +161 -0
- package/dist/admin/components/MediaLibraryInput/AssetCarousel.js.map +1 -0
- package/dist/admin/components/MediaLibraryInput/AssetCarousel.mjs +159 -0
- package/dist/admin/components/MediaLibraryInput/AssetCarousel.mjs.map +1 -0
- package/dist/admin/components/MediaLibraryInput/AssetDropZone.js +76 -49
- package/dist/admin/components/MediaLibraryInput/AssetDropZone.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/AssetDropZone.mjs +77 -50
- package/dist/admin/components/MediaLibraryInput/AssetDropZone.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/AssetPreview.js +109 -0
- package/dist/admin/components/MediaLibraryInput/AssetPreview.js.map +1 -0
- package/dist/admin/components/MediaLibraryInput/AssetPreview.mjs +106 -0
- package/dist/admin/components/MediaLibraryInput/AssetPreview.mjs.map +1 -0
- package/dist/admin/components/MediaLibraryInput/AssetRow.js +146 -79
- package/dist/admin/components/MediaLibraryInput/AssetRow.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/AssetRow.mjs +148 -82
- package/dist/admin/components/MediaLibraryInput/AssetRow.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/FieldControls.js +32 -0
- package/dist/admin/components/MediaLibraryInput/FieldControls.js.map +1 -0
- package/dist/admin/components/MediaLibraryInput/FieldControls.mjs +29 -0
- package/dist/admin/components/MediaLibraryInput/FieldControls.mjs.map +1 -0
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js +130 -88
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs +129 -91
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/UploadProgressBar.js +42 -0
- package/dist/admin/components/MediaLibraryInput/UploadProgressBar.js.map +1 -0
- package/dist/admin/components/MediaLibraryInput/UploadProgressBar.mjs +40 -0
- package/dist/admin/components/MediaLibraryInput/UploadProgressBar.mjs.map +1 -0
- package/dist/admin/components/MediaLibraryInput/useFieldUploads.js +122 -0
- package/dist/admin/components/MediaLibraryInput/useFieldUploads.js.map +1 -0
- package/dist/admin/components/MediaLibraryInput/useFieldUploads.mjs +120 -0
- package/dist/admin/components/MediaLibraryInput/useFieldUploads.mjs.map +1 -0
- package/dist/admin/components/UploadProgressDialog.js +2 -1
- package/dist/admin/components/UploadProgressDialog.js.map +1 -1
- package/dist/admin/components/UploadProgressDialog.mjs +2 -1
- package/dist/admin/components/UploadProgressDialog.mjs.map +1 -1
- package/dist/admin/hooks/useMediaLibraryPermissions.js +12 -2
- package/dist/admin/hooks/useMediaLibraryPermissions.js.map +1 -1
- package/dist/admin/hooks/useMediaLibraryPermissions.mjs +12 -2
- package/dist/admin/hooks/useMediaLibraryPermissions.mjs.map +1 -1
- package/dist/admin/pages/Assets/AssetsPage.js +13 -302
- package/dist/admin/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/pages/Assets/AssetsPage.mjs +11 -280
- package/dist/admin/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsGrid.js +142 -31
- package/dist/admin/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsGrid.mjs +145 -34
- package/dist/admin/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsSearchInput.js +43 -2
- package/dist/admin/pages/Assets/components/AssetsSearchInput.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsSearchInput.mjs +45 -4
- package/dist/admin/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsTable.js +58 -34
- package/dist/admin/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsTable.mjs +60 -36
- package/dist/admin/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsView.js +145 -0
- package/dist/admin/pages/Assets/components/AssetsView.js.map +1 -0
- package/dist/admin/pages/Assets/components/AssetsView.mjs +143 -0
- package/dist/admin/pages/Assets/components/AssetsView.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/BulkActionsBar.mjs +1 -1
- package/dist/admin/pages/Assets/components/BusyOverlay.mjs +1 -1
- package/dist/admin/pages/Assets/components/Dnd/DragOverlayChip.mjs +1 -1
- package/dist/admin/pages/Assets/components/Dnd/ReorderHandle.js +40 -0
- package/dist/admin/pages/Assets/components/Dnd/ReorderHandle.js.map +1 -0
- package/dist/admin/pages/Assets/components/Dnd/ReorderHandle.mjs +38 -0
- package/dist/admin/pages/Assets/components/Dnd/ReorderHandle.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/Dnd/assetSortable.js +29 -0
- package/dist/admin/pages/Assets/components/Dnd/assetSortable.js.map +1 -0
- package/dist/admin/pages/Assets/components/Dnd/assetSortable.mjs +24 -0
- package/dist/admin/pages/Assets/components/Dnd/assetSortable.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/Dnd/dndIds.js +5 -0
- package/dist/admin/pages/Assets/components/Dnd/dndIds.js.map +1 -1
- package/dist/admin/pages/Assets/components/Dnd/dndIds.mjs +5 -1
- package/dist/admin/pages/Assets/components/Dnd/dndIds.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/Dnd/useAssetDnd.js +41 -13
- package/dist/admin/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
- package/dist/admin/pages/Assets/components/Dnd/useAssetDnd.mjs +41 -13
- package/dist/admin/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/FilterBadges.mjs +2 -2
- package/dist/admin/pages/Assets/components/FilterMenu.mjs +2 -2
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.js +274 -55
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.mjs +277 -59
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/FolderTree/collapsedBranch.js +32 -0
- package/dist/admin/pages/Assets/components/FolderTree/collapsedBranch.js.map +1 -0
- package/dist/admin/pages/Assets/components/FolderTree/collapsedBranch.mjs +29 -0
- package/dist/admin/pages/Assets/components/FolderTree/collapsedBranch.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/ViewToggle.js +126 -0
- package/dist/admin/pages/Assets/components/ViewToggle.js.map +1 -0
- package/dist/admin/pages/Assets/components/ViewToggle.mjs +104 -0
- package/dist/admin/pages/Assets/components/ViewToggle.mjs.map +1 -0
- package/dist/admin/pages/Assets/hooks/useAssetSearch.js +2 -2
- package/dist/admin/pages/Assets/hooks/useAssetSearch.js.map +1 -1
- package/dist/admin/pages/Assets/hooks/useAssetSearch.mjs +2 -2
- package/dist/admin/pages/Assets/hooks/useAssetSearch.mjs.map +1 -1
- package/dist/admin/pages/Assets/hooks/useAssetSelection.js +19 -1
- package/dist/admin/pages/Assets/hooks/useAssetSelection.js.map +1 -1
- package/dist/admin/pages/Assets/hooks/useAssetSelection.mjs +20 -3
- package/dist/admin/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
- package/dist/admin/pages/Assets/hooks/useAssetsList.js +87 -0
- package/dist/admin/pages/Assets/hooks/useAssetsList.js.map +1 -0
- package/dist/admin/pages/Assets/hooks/useAssetsList.mjs +85 -0
- package/dist/admin/pages/Assets/hooks/useAssetsList.mjs.map +1 -0
- package/dist/admin/pages/Assets/hooks/useBusyAssets.mjs +1 -1
- package/dist/admin/pages/Assets/hooks/useFolderNavigation.js +2 -1
- package/dist/admin/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
- package/dist/admin/pages/Assets/hooks/useFolderNavigation.mjs +3 -2
- package/dist/admin/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
- package/dist/admin/pages/Assets/hooks/useListFilters.js +2 -1
- package/dist/admin/pages/Assets/hooks/useListFilters.js.map +1 -1
- package/dist/admin/pages/Assets/hooks/useListFilters.mjs +3 -2
- package/dist/admin/pages/Assets/hooks/useListFilters.mjs.map +1 -1
- package/dist/admin/pages/Assets/hooks/useListQueryParams.js +80 -0
- package/dist/admin/pages/Assets/hooks/useListQueryParams.js.map +1 -0
- package/dist/admin/pages/Assets/hooks/useListQueryParams.mjs +77 -0
- package/dist/admin/pages/Assets/hooks/useListQueryParams.mjs.map +1 -0
- package/dist/admin/pages/Assets/hooks/useListSettings.js +32 -0
- package/dist/admin/pages/Assets/hooks/useListSettings.js.map +1 -0
- package/dist/admin/pages/Assets/hooks/useListSettings.mjs +29 -0
- package/dist/admin/pages/Assets/hooks/useListSettings.mjs.map +1 -0
- package/dist/admin/pages/Assets/hooks/useListSort.js +2 -1
- package/dist/admin/pages/Assets/hooks/useListSort.js.map +1 -1
- package/dist/admin/pages/Assets/hooks/useListSort.mjs +3 -2
- package/dist/admin/pages/Assets/hooks/useListSort.mjs.map +1 -1
- package/dist/admin/services/api.js +92 -7
- package/dist/admin/services/api.js.map +1 -1
- package/dist/admin/services/api.mjs +91 -9
- package/dist/admin/services/api.mjs.map +1 -1
- package/dist/admin/src/components/AssetPickerDialog/AssetPickerDialog.d.ts +19 -0
- package/dist/admin/src/components/AssetPickerDialog/FolderBreadcrumbs.d.ts +10 -0
- package/dist/admin/src/components/AssetPickerDialog/PickerSelectionProvider.d.ts +28 -0
- package/dist/admin/src/components/AssetPickerDialog/SortableSelection.d.ts +12 -0
- package/dist/admin/src/components/AssetPickerDialog/pickerSelection.d.ts +26 -0
- package/dist/admin/src/components/MediaLibraryInput/AssetCard.d.ts +25 -0
- package/dist/admin/src/components/MediaLibraryInput/AssetCarousel.d.ts +16 -0
- package/dist/admin/src/components/MediaLibraryInput/AssetDropZone.d.ts +11 -3
- package/dist/admin/src/components/MediaLibraryInput/AssetPreview.d.ts +17 -0
- package/dist/admin/src/components/MediaLibraryInput/AssetRow.d.ts +13 -3
- package/dist/admin/src/components/MediaLibraryInput/FieldControls.d.ts +19 -0
- package/dist/admin/src/components/MediaLibraryInput/MediaLibraryInput.d.ts +3 -1
- package/dist/admin/src/components/MediaLibraryInput/UploadProgressBar.d.ts +7 -0
- package/dist/admin/src/components/MediaLibraryInput/useFieldUploads.d.ts +31 -0
- package/dist/admin/src/hooks/useMediaLibraryPermissions.d.ts +2 -0
- package/dist/admin/src/pages/Assets/components/AssetsGrid.d.ts +3 -1
- package/dist/admin/src/pages/Assets/components/AssetsSearchInput.d.ts +11 -1
- package/dist/admin/src/pages/Assets/components/AssetsView.d.ts +29 -0
- package/dist/admin/src/pages/Assets/components/Dnd/ReorderHandle.d.ts +12 -0
- package/dist/admin/src/pages/Assets/components/Dnd/assetSortable.d.ts +37 -0
- package/dist/admin/src/pages/Assets/components/Dnd/dndIds.d.ts +5 -0
- package/dist/admin/src/pages/Assets/components/Dnd/useAssetDnd.d.ts +23 -2
- package/dist/admin/src/pages/Assets/components/FolderTree/FolderTree.d.ts +1 -0
- package/dist/admin/src/pages/Assets/components/FolderTree/collapsedBranch.d.ts +21 -0
- package/dist/admin/src/pages/Assets/components/ViewToggle.d.ts +8 -0
- package/dist/admin/src/pages/Assets/hooks/useAssetSelection.d.ts +26 -0
- package/dist/admin/src/pages/Assets/hooks/useAssetsList.d.ts +30 -0
- package/dist/admin/src/pages/Assets/hooks/useListQueryParams.d.ts +27 -0
- package/dist/admin/src/pages/Assets/hooks/useListSettings.d.ts +29 -0
- package/dist/admin/src/services/api.d.ts +24 -1
- package/dist/admin/src/store/uploadProgress.d.ts +14 -1
- package/dist/admin/src/utils/findFolderPath.d.ts +6 -0
- package/dist/admin/src/utils/isAllowedAssetType.d.ts +14 -0
- package/dist/admin/store/uploadProgress.js +30 -3
- package/dist/admin/store/uploadProgress.js.map +1 -1
- package/dist/admin/store/uploadProgress.mjs +29 -4
- package/dist/admin/store/uploadProgress.mjs.map +1 -1
- package/dist/admin/translations/en.json.js +20 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +20 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/findFolderPath.js +26 -0
- package/dist/admin/utils/findFolderPath.js.map +1 -0
- package/dist/admin/utils/findFolderPath.mjs +24 -0
- package/dist/admin/utils/findFolderPath.mjs.map +1 -0
- package/dist/admin/utils/isAllowedAssetType.js +33 -0
- package/dist/admin/utils/isAllowedAssetType.js.map +1 -0
- package/dist/admin/utils/isAllowedAssetType.mjs +31 -0
- package/dist/admin/utils/isAllowedAssetType.mjs.map +1 -0
- package/dist/server/controllers/admin-settings.js +5 -1
- package/dist/server/controllers/admin-settings.js.map +1 -1
- package/dist/server/controllers/admin-settings.mjs +5 -1
- package/dist/server/controllers/admin-settings.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js +2 -2
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs +2 -2
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +1 -1
- package/dist/server/src/services/extensions/utils.d.ts +11 -11
- package/dist/server/src/services/image-manipulation.d.ts +1 -1
- package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +1 -1
- package/dist/server/src/types.d.ts +63 -12
- package/dist/server/src/types.d.ts.map +1 -1
- package/package.json +8 -6
- package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.js +0 -45
- package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.js.map +0 -1
- package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.mjs +0 -43
- package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.mjs.map +0 -1
- package/dist/admin/src/components/MediaLibraryInput/NoLibraryAccessDialog.d.ts +0 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListFilters.js","sources":["../../../../../admin/src/pages/Assets/hooks/useListFilters.ts"],"sourcesContent":["import { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Filter state for the assets list, backed by a single `?filters=` query param\n * so the applied filters are shareable and survive Back/refresh.\n *\n * Serialized grammar — badges separated by `;`, one badge is\n * `<field>:<condition>:<value>`:\n *\n * - `type:is:picture,audio` / `type:not:folder`\n * - `created:within:1week` / `updated:notwithin:3months` / `created:exact:1day`\n * - `created:rangeis:2024-01-01..2024-04-07` / `created:rangenot:...`\n *\n * Relative presets stay relative in the URL on purpose: the badge label\n * (\"1 week ago\") remains truthful forever and a shared link means \"within the\n * last week from whenever it is opened\". Resolution to absolute timestamps\n * happens at query-build time (see utils/buildAssetFilters.ts).\n *\n * Several badges for the same field are allowed, even contradictory ones —\n * everything is combined with AND; contradictions simply produce the filtered\n * empty state.\n */\n\nexport const TYPE_VALUES = ['folder', 'picture', 'audio', 'video', 'document'] as const;\nexport type TypeValue = (typeof TYPE_VALUES)[number];\n\nexport const DATE_PRESETS = [\n '1day',\n '3days',\n '1week',\n '1month',\n '3months',\n '6months',\n '1year',\n] as const;\nexport type DatePreset = (typeof DATE_PRESETS)[number];\n\nexport type TypeCondition = 'is' | 'isNot';\nexport type PresetCondition = 'isExactly' | 'withinLast' | 'notWithinLast';\nexport type RangeCondition = 'is' | 'isNot';\n\nexport type DateField = 'createdAt' | 'updatedAt';\n\nexport interface TypeFilter {\n kind: 'type';\n condition: TypeCondition;\n values: TypeValue[];\n}\n\nexport interface DatePresetFilter {\n kind: 'date';\n field: DateField;\n mode: 'preset';\n condition: PresetCondition;\n preset: DatePreset;\n}\n\nexport interface DateRangeFilter {\n kind: 'date';\n field: DateField;\n mode: 'range';\n condition: RangeCondition;\n /** Calendar dates, `YYYY-MM-DD`, inclusive on both ends. */\n from: string;\n to: string;\n}\n\nexport type DateFilter = DatePresetFilter | DateRangeFilter;\nexport type ListFilter = TypeFilter | DateFilter;\n\nconst FIELD_TOKENS: Record<string, DateField> = {\n created: 'createdAt',\n updated: 'updatedAt',\n};\nconst FIELD_TOKEN_BY_FIELD: Record<DateField, string> = {\n createdAt: 'created',\n updatedAt: 'updated',\n};\n\nconst PRESET_CONDITION_TOKENS: Record<string, PresetCondition> = {\n exact: 'isExactly',\n within: 'withinLast',\n notwithin: 'notWithinLast',\n};\nconst PRESET_TOKEN_BY_CONDITION: Record<PresetCondition, string> = {\n isExactly: 'exact',\n withinLast: 'within',\n notWithinLast: 'notwithin',\n};\n\nconst RANGE_CONDITION_TOKENS: Record<string, RangeCondition> = {\n rangeis: 'is',\n rangenot: 'isNot',\n};\nconst RANGE_TOKEN_BY_CONDITION: Record<RangeCondition, string> = {\n is: 'rangeis',\n isNot: 'rangenot',\n};\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isTypeValue = (value: string): value is TypeValue =>\n (TYPE_VALUES as readonly string[]).includes(value);\n\nconst isDatePreset = (value: string): value is DatePreset =>\n (DATE_PRESETS as readonly string[]).includes(value);\n\nconst parseBadge = (raw: string): ListFilter | null => {\n const [field, condition, value] = raw.split(':');\n\n if (!field || !condition || !value) {\n return null;\n }\n\n if (field === 'type') {\n if (condition !== 'is' && condition !== 'not') {\n return null;\n }\n const values = value.split(',').filter(isTypeValue);\n\n return values.length > 0\n ? { kind: 'type', condition: condition === 'is' ? 'is' : 'isNot', values }\n : null;\n }\n\n const dateField = FIELD_TOKENS[field];\n if (!dateField) {\n return null;\n }\n\n if (condition in PRESET_CONDITION_TOKENS) {\n return isDatePreset(value)\n ? {\n kind: 'date',\n field: dateField,\n mode: 'preset',\n condition: PRESET_CONDITION_TOKENS[condition],\n preset: value,\n }\n : null;\n }\n\n if (condition in RANGE_CONDITION_TOKENS) {\n const [from, to] = value.split('..');\n\n return DATE_RE.test(from ?? '') && DATE_RE.test(to ?? '')\n ? {\n kind: 'date',\n field: dateField,\n mode: 'range',\n condition: RANGE_CONDITION_TOKENS[condition],\n from,\n to,\n }\n : null;\n }\n\n return null;\n};\n\n/**\n * `unknown` on purpose: the query string is user-editable, and qs parses\n * shapes like `?filters[]=a&filters[]=b` into arrays — the guard is what\n * makes the caller's `?filters=` type an assertion rather than a crash.\n */\nexport const parseFiltersParam = (raw: unknown): ListFilter[] => {\n if (typeof raw !== 'string' || raw === '') {\n return [];\n }\n\n return raw\n .split(';')\n .map(parseBadge)\n .filter((filter): filter is ListFilter => filter !== null);\n};\n\nconst serializeBadge = (filter: ListFilter): string => {\n if (filter.kind === 'type') {\n return `type:${filter.condition === 'is' ? 'is' : 'not'}:${filter.values.join(',')}`;\n }\n\n const field = FIELD_TOKEN_BY_FIELD[filter.field];\n\n if (filter.mode === 'preset') {\n return `${field}:${PRESET_TOKEN_BY_CONDITION[filter.condition]}:${filter.preset}`;\n }\n\n return `${field}:${RANGE_TOKEN_BY_CONDITION[filter.condition]}:${filter.from}..${filter.to}`;\n};\n\nexport const serializeFilters = (filters: ListFilter[]): string =>\n filters.map(serializeBadge).join(';');\n\nexport interface ListFilters {\n filters: ListFilter[];\n /** Serialized form — stable fingerprint for list-identity (selection reset). */\n serialized: string;\n addFilter: (filter: ListFilter) => void;\n /** Replaces the badge at `index` (condition or value edited in place). */\n updateFilter: (index: number, filter: ListFilter) => void;\n removeFilter: (index: number) => void;\n clearFilters: () => void;\n}\n\nexport const useListFilters = (): ListFilters => {\n const [{ query }, setQuery] = useQueryParams<{ filters?: string; _q?: string }>();\n\n const filters = parseFiltersParam(query?.filters);\n\n // `replace: true` on every write: filters are edited in rapid micro-steps\n // (each checkbox of the keep-open Type submenu, each badge segment edit), so\n // pushing would turn one logical change into a stack of Back entries. The\n // URL stays shareable and refresh-safe either way.\n const writeFilters = (next: ListFilter[]) => {\n if (next.length === 0) {\n setQuery(withEncodedUserParams(query, { filters: undefined }), 'push', true);\n } else {\n setQuery(withEncodedUserParams(query, { filters: serializeFilters(next) }), 'push', true);\n }\n };\n\n return {\n filters,\n serialized: serializeFilters(filters),\n addFilter: (filter) => writeFilters([...filters, filter]),\n updateFilter: (index, filter) =>\n writeFilters(filters.map((f, i) => (i === index ? filter : f))),\n removeFilter: (index) => writeFilters(filters.filter((_, i) => i !== index)),\n clearFilters: () => writeFilters([]),\n };\n};\n"],"names":["TYPE_VALUES","DATE_PRESETS","FIELD_TOKENS","created","updated","FIELD_TOKEN_BY_FIELD","createdAt","updatedAt","PRESET_CONDITION_TOKENS","exact","within","notwithin","PRESET_TOKEN_BY_CONDITION","isExactly","withinLast","notWithinLast","RANGE_CONDITION_TOKENS","rangeis","rangenot","RANGE_TOKEN_BY_CONDITION","is","isNot","DATE_RE","isTypeValue","value","includes","isDatePreset","parseBadge","raw","field","condition","split","values","filter","length","kind","dateField","mode","preset","from","to","test","parseFiltersParam","map","serializeBadge","join","serializeFilters","filters","useListFilters","query","setQuery","useQueryParams","writeFilters","next","withEncodedUserParams","undefined","serialized","addFilter","updateFilter","index","f","i","removeFilter","_","clearFilters"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useListFilters.js","sources":["../../../../../admin/src/pages/Assets/hooks/useListFilters.ts"],"sourcesContent":["import { withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\nimport { useListQueryParams } from './useListQueryParams';\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] = useListQueryParams<{ filters?: string; _q?: string }>();\n\n const filters = parseFiltersParam(query?.filters);\n\n // `replace: true` on every write: filters are edited in rapid micro-steps\n // (each checkbox of the keep-open Type submenu, each badge segment edit), so\n // pushing would turn one logical change into a stack of Back entries. The\n // URL stays shareable and refresh-safe either way.\n const writeFilters = (next: ListFilter[]) => {\n if (next.length === 0) {\n setQuery(withEncodedUserParams(query, { filters: undefined }), 'push', true);\n } else {\n setQuery(withEncodedUserParams(query, { filters: serializeFilters(next) }), 'push', true);\n }\n };\n\n return {\n filters,\n serialized: serializeFilters(filters),\n addFilter: (filter) => writeFilters([...filters, filter]),\n updateFilter: (index, filter) =>\n writeFilters(filters.map((f, i) => (i === index ? filter : f))),\n removeFilter: (index) => writeFilters(filters.filter((_, i) => i !== index)),\n clearFilters: () => writeFilters([]),\n };\n};\n"],"names":["TYPE_VALUES","DATE_PRESETS","FIELD_TOKENS","created","updated","FIELD_TOKEN_BY_FIELD","createdAt","updatedAt","PRESET_CONDITION_TOKENS","exact","within","notwithin","PRESET_TOKEN_BY_CONDITION","isExactly","withinLast","notWithinLast","RANGE_CONDITION_TOKENS","rangeis","rangenot","RANGE_TOKEN_BY_CONDITION","is","isNot","DATE_RE","isTypeValue","value","includes","isDatePreset","parseBadge","raw","field","condition","split","values","filter","length","kind","dateField","mode","preset","from","to","test","parseFiltersParam","map","serializeBadge","join","serializeFilters","filters","useListFilters","query","setQuery","useListQueryParams","writeFilters","next","withEncodedUserParams","undefined","serialized","addFilter","updateFilter","index","f","i","removeFilter","_","clearFilters"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { withEncodedUserParams } from '@strapi/admin/strapi-admin';
|
|
2
|
+
import { useListQueryParams } from './useListQueryParams.mjs';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Filter state for the assets list, backed by a single `?filters=` query param
|
|
@@ -128,7 +129,7 @@ const serializeBadge = (filter)=>{
|
|
|
128
129
|
};
|
|
129
130
|
const serializeFilters = (filters)=>filters.map(serializeBadge).join(';');
|
|
130
131
|
const useListFilters = ()=>{
|
|
131
|
-
const [{ query }, setQuery] =
|
|
132
|
+
const [{ query }, setQuery] = useListQueryParams();
|
|
132
133
|
const filters = parseFiltersParam(query?.filters);
|
|
133
134
|
// `replace: true` on every write: filters are edited in rapid micro-steps
|
|
134
135
|
// (each checkbox of the keep-open Type submenu, each badge segment edit), so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListFilters.mjs","sources":["../../../../../admin/src/pages/Assets/hooks/useListFilters.ts"],"sourcesContent":["import { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Filter state for the assets list, backed by a single `?filters=` query param\n * so the applied filters are shareable and survive Back/refresh.\n *\n * Serialized grammar — badges separated by `;`, one badge is\n * `<field>:<condition>:<value>`:\n *\n * - `type:is:picture,audio` / `type:not:folder`\n * - `created:within:1week` / `updated:notwithin:3months` / `created:exact:1day`\n * - `created:rangeis:2024-01-01..2024-04-07` / `created:rangenot:...`\n *\n * Relative presets stay relative in the URL on purpose: the badge label\n * (\"1 week ago\") remains truthful forever and a shared link means \"within the\n * last week from whenever it is opened\". Resolution to absolute timestamps\n * happens at query-build time (see utils/buildAssetFilters.ts).\n *\n * Several badges for the same field are allowed, even contradictory ones —\n * everything is combined with AND; contradictions simply produce the filtered\n * empty state.\n */\n\nexport const TYPE_VALUES = ['folder', 'picture', 'audio', 'video', 'document'] as const;\nexport type TypeValue = (typeof TYPE_VALUES)[number];\n\nexport const DATE_PRESETS = [\n '1day',\n '3days',\n '1week',\n '1month',\n '3months',\n '6months',\n '1year',\n] as const;\nexport type DatePreset = (typeof DATE_PRESETS)[number];\n\nexport type TypeCondition = 'is' | 'isNot';\nexport type PresetCondition = 'isExactly' | 'withinLast' | 'notWithinLast';\nexport type RangeCondition = 'is' | 'isNot';\n\nexport type DateField = 'createdAt' | 'updatedAt';\n\nexport interface TypeFilter {\n kind: 'type';\n condition: TypeCondition;\n values: TypeValue[];\n}\n\nexport interface DatePresetFilter {\n kind: 'date';\n field: DateField;\n mode: 'preset';\n condition: PresetCondition;\n preset: DatePreset;\n}\n\nexport interface DateRangeFilter {\n kind: 'date';\n field: DateField;\n mode: 'range';\n condition: RangeCondition;\n /** Calendar dates, `YYYY-MM-DD`, inclusive on both ends. */\n from: string;\n to: string;\n}\n\nexport type DateFilter = DatePresetFilter | DateRangeFilter;\nexport type ListFilter = TypeFilter | DateFilter;\n\nconst FIELD_TOKENS: Record<string, DateField> = {\n created: 'createdAt',\n updated: 'updatedAt',\n};\nconst FIELD_TOKEN_BY_FIELD: Record<DateField, string> = {\n createdAt: 'created',\n updatedAt: 'updated',\n};\n\nconst PRESET_CONDITION_TOKENS: Record<string, PresetCondition> = {\n exact: 'isExactly',\n within: 'withinLast',\n notwithin: 'notWithinLast',\n};\nconst PRESET_TOKEN_BY_CONDITION: Record<PresetCondition, string> = {\n isExactly: 'exact',\n withinLast: 'within',\n notWithinLast: 'notwithin',\n};\n\nconst RANGE_CONDITION_TOKENS: Record<string, RangeCondition> = {\n rangeis: 'is',\n rangenot: 'isNot',\n};\nconst RANGE_TOKEN_BY_CONDITION: Record<RangeCondition, string> = {\n is: 'rangeis',\n isNot: 'rangenot',\n};\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isTypeValue = (value: string): value is TypeValue =>\n (TYPE_VALUES as readonly string[]).includes(value);\n\nconst isDatePreset = (value: string): value is DatePreset =>\n (DATE_PRESETS as readonly string[]).includes(value);\n\nconst parseBadge = (raw: string): ListFilter | null => {\n const [field, condition, value] = raw.split(':');\n\n if (!field || !condition || !value) {\n return null;\n }\n\n if (field === 'type') {\n if (condition !== 'is' && condition !== 'not') {\n return null;\n }\n const values = value.split(',').filter(isTypeValue);\n\n return values.length > 0\n ? { kind: 'type', condition: condition === 'is' ? 'is' : 'isNot', values }\n : null;\n }\n\n const dateField = FIELD_TOKENS[field];\n if (!dateField) {\n return null;\n }\n\n if (condition in PRESET_CONDITION_TOKENS) {\n return isDatePreset(value)\n ? {\n kind: 'date',\n field: dateField,\n mode: 'preset',\n condition: PRESET_CONDITION_TOKENS[condition],\n preset: value,\n }\n : null;\n }\n\n if (condition in RANGE_CONDITION_TOKENS) {\n const [from, to] = value.split('..');\n\n return DATE_RE.test(from ?? '') && DATE_RE.test(to ?? '')\n ? {\n kind: 'date',\n field: dateField,\n mode: 'range',\n condition: RANGE_CONDITION_TOKENS[condition],\n from,\n to,\n }\n : null;\n }\n\n return null;\n};\n\n/**\n * `unknown` on purpose: the query string is user-editable, and qs parses\n * shapes like `?filters[]=a&filters[]=b` into arrays — the guard is what\n * makes the caller's `?filters=` type an assertion rather than a crash.\n */\nexport const parseFiltersParam = (raw: unknown): ListFilter[] => {\n if (typeof raw !== 'string' || raw === '') {\n return [];\n }\n\n return raw\n .split(';')\n .map(parseBadge)\n .filter((filter): filter is ListFilter => filter !== null);\n};\n\nconst serializeBadge = (filter: ListFilter): string => {\n if (filter.kind === 'type') {\n return `type:${filter.condition === 'is' ? 'is' : 'not'}:${filter.values.join(',')}`;\n }\n\n const field = FIELD_TOKEN_BY_FIELD[filter.field];\n\n if (filter.mode === 'preset') {\n return `${field}:${PRESET_TOKEN_BY_CONDITION[filter.condition]}:${filter.preset}`;\n }\n\n return `${field}:${RANGE_TOKEN_BY_CONDITION[filter.condition]}:${filter.from}..${filter.to}`;\n};\n\nexport const serializeFilters = (filters: ListFilter[]): string =>\n filters.map(serializeBadge).join(';');\n\nexport interface ListFilters {\n filters: ListFilter[];\n /** Serialized form — stable fingerprint for list-identity (selection reset). */\n serialized: string;\n addFilter: (filter: ListFilter) => void;\n /** Replaces the badge at `index` (condition or value edited in place). */\n updateFilter: (index: number, filter: ListFilter) => void;\n removeFilter: (index: number) => void;\n clearFilters: () => void;\n}\n\nexport const useListFilters = (): ListFilters => {\n const [{ query }, setQuery] = useQueryParams<{ filters?: string; _q?: string }>();\n\n const filters = parseFiltersParam(query?.filters);\n\n // `replace: true` on every write: filters are edited in rapid micro-steps\n // (each checkbox of the keep-open Type submenu, each badge segment edit), so\n // pushing would turn one logical change into a stack of Back entries. The\n // URL stays shareable and refresh-safe either way.\n const writeFilters = (next: ListFilter[]) => {\n if (next.length === 0) {\n setQuery(withEncodedUserParams(query, { filters: undefined }), 'push', true);\n } else {\n setQuery(withEncodedUserParams(query, { filters: serializeFilters(next) }), 'push', true);\n }\n };\n\n return {\n filters,\n serialized: serializeFilters(filters),\n addFilter: (filter) => writeFilters([...filters, filter]),\n updateFilter: (index, filter) =>\n writeFilters(filters.map((f, i) => (i === index ? filter : f))),\n removeFilter: (index) => writeFilters(filters.filter((_, i) => i !== index)),\n clearFilters: () => writeFilters([]),\n };\n};\n"],"names":["TYPE_VALUES","DATE_PRESETS","FIELD_TOKENS","created","updated","FIELD_TOKEN_BY_FIELD","createdAt","updatedAt","PRESET_CONDITION_TOKENS","exact","within","notwithin","PRESET_TOKEN_BY_CONDITION","isExactly","withinLast","notWithinLast","RANGE_CONDITION_TOKENS","rangeis","rangenot","RANGE_TOKEN_BY_CONDITION","is","isNot","DATE_RE","isTypeValue","value","includes","isDatePreset","parseBadge","raw","field","condition","split","values","filter","length","kind","dateField","mode","preset","from","to","test","parseFiltersParam","map","serializeBadge","join","serializeFilters","filters","useListFilters","query","setQuery","useQueryParams","writeFilters","next","withEncodedUserParams","undefined","serialized","addFilter","updateFilter","index","f","i","removeFilter","_","clearFilters"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useListFilters.mjs","sources":["../../../../../admin/src/pages/Assets/hooks/useListFilters.ts"],"sourcesContent":["import { withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\nimport { useListQueryParams } from './useListQueryParams';\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] = useListQueryParams<{ filters?: string; _q?: string }>();\n\n const filters = parseFiltersParam(query?.filters);\n\n // `replace: true` on every write: filters are edited in rapid micro-steps\n // (each checkbox of the keep-open Type submenu, each badge segment edit), so\n // pushing would turn one logical change into a stack of Back entries. The\n // URL stays shareable and refresh-safe either way.\n const writeFilters = (next: ListFilter[]) => {\n if (next.length === 0) {\n setQuery(withEncodedUserParams(query, { filters: undefined }), 'push', true);\n } else {\n setQuery(withEncodedUserParams(query, { filters: serializeFilters(next) }), 'push', true);\n }\n };\n\n return {\n filters,\n serialized: serializeFilters(filters),\n addFilter: (filter) => writeFilters([...filters, filter]),\n updateFilter: (index, filter) =>\n writeFilters(filters.map((f, i) => (i === index ? filter : f))),\n removeFilter: (index) => writeFilters(filters.filter((_, i) => i !== index)),\n clearFilters: () => writeFilters([]),\n };\n};\n"],"names":["TYPE_VALUES","DATE_PRESETS","FIELD_TOKENS","created","updated","FIELD_TOKEN_BY_FIELD","createdAt","updatedAt","PRESET_CONDITION_TOKENS","exact","within","notwithin","PRESET_TOKEN_BY_CONDITION","isExactly","withinLast","notWithinLast","RANGE_CONDITION_TOKENS","rangeis","rangenot","RANGE_TOKEN_BY_CONDITION","is","isNot","DATE_RE","isTypeValue","value","includes","isDatePreset","parseBadge","raw","field","condition","split","values","filter","length","kind","dateField","mode","preset","from","to","test","parseFiltersParam","map","serializeBadge","join","serializeFilters","filters","useListFilters","query","setQuery","useListQueryParams","writeFilters","next","withEncodedUserParams","undefined","serialized","addFilter","updateFilter","index","f","i","removeFilter","_","clearFilters"],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
|
5
|
+
var qs = require('qs');
|
|
6
|
+
|
|
7
|
+
const ListQueryStateContext = React.createContext(null);
|
|
8
|
+
/**
|
|
9
|
+
* Keeps the list query (folder, search, filters, sort) in memory instead of the
|
|
10
|
+
* URL, for surfaces hosted inside another page — the Content Manager asset
|
|
11
|
+
* picker would otherwise overwrite the entry's own URL. Without this provider,
|
|
12
|
+
* `useListQueryParams` falls back to the URL, which is what the Media Library
|
|
13
|
+
* page relies on.
|
|
14
|
+
*/ const ListQueryStateProvider = ({ children })=>{
|
|
15
|
+
const [search, setSearch] = React.useState('');
|
|
16
|
+
const value = React.useMemo(()=>({
|
|
17
|
+
search,
|
|
18
|
+
setSearch
|
|
19
|
+
}), [
|
|
20
|
+
search
|
|
21
|
+
]);
|
|
22
|
+
return React.createElement(ListQueryStateContext.Provider, {
|
|
23
|
+
value
|
|
24
|
+
}, children);
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Drop-in for `useQueryParams` used by the list hooks. The in-memory store keeps
|
|
28
|
+
* a serialized search string and goes through the same `qs` round trip as the
|
|
29
|
+
* URL, so callers that pre-encode values (`withEncodedUserParams`,
|
|
30
|
+
* `encodeSearchQuery`) read back exactly what they would from the URL.
|
|
31
|
+
*/ const useListQueryParams = ()=>{
|
|
32
|
+
const urlQueryParams = strapiAdmin.useQueryParams();
|
|
33
|
+
const memoryState = React.useContext(ListQueryStateContext);
|
|
34
|
+
const memorySearch = memoryState?.search ?? '';
|
|
35
|
+
const setMemorySearch = memoryState?.setSearch;
|
|
36
|
+
const memoryQuery = React.useMemo(()=>qs.parse(memorySearch), [
|
|
37
|
+
memorySearch
|
|
38
|
+
]);
|
|
39
|
+
const setMemoryQuery = React.useCallback((nextParams, method = 'push')=>{
|
|
40
|
+
if (!setMemorySearch) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
setMemorySearch((currentSearch)=>{
|
|
44
|
+
const currentQuery = qs.parse(currentSearch);
|
|
45
|
+
let nextQuery;
|
|
46
|
+
if (method === 'remove') {
|
|
47
|
+
nextQuery = {
|
|
48
|
+
...currentQuery
|
|
49
|
+
};
|
|
50
|
+
Object.keys(nextParams).forEach((key)=>{
|
|
51
|
+
delete nextQuery[key];
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
nextQuery = {
|
|
55
|
+
...currentQuery,
|
|
56
|
+
...nextParams
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return qs.stringify(nextQuery, {
|
|
60
|
+
encode: false
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}, [
|
|
64
|
+
setMemorySearch
|
|
65
|
+
]);
|
|
66
|
+
if (!memoryState) {
|
|
67
|
+
return urlQueryParams;
|
|
68
|
+
}
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
query: memoryQuery,
|
|
72
|
+
rawQuery: memorySearch
|
|
73
|
+
},
|
|
74
|
+
setMemoryQuery
|
|
75
|
+
];
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
exports.ListQueryStateProvider = ListQueryStateProvider;
|
|
79
|
+
exports.useListQueryParams = useListQueryParams;
|
|
80
|
+
//# sourceMappingURL=useListQueryParams.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useListQueryParams.js","sources":["../../../../../admin/src/pages/Assets/hooks/useListQueryParams.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useMemo,\n useState,\n type Dispatch,\n type ReactNode,\n type SetStateAction,\n} from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\nimport { parse, stringify } from 'qs';\n\ntype SetQuery<TQuery> = (nextParams: TQuery, method?: 'push' | 'remove', replace?: boolean) => void;\n\ninterface ListQueryState {\n search: string;\n setSearch: Dispatch<SetStateAction<string>>;\n}\n\nconst ListQueryStateContext = createContext<ListQueryState | null>(null);\n\ninterface ListQueryStateProviderProps {\n children: ReactNode;\n}\n\n/**\n * Keeps the list query (folder, search, filters, sort) in memory instead of the\n * URL, for surfaces hosted inside another page — the Content Manager asset\n * picker would otherwise overwrite the entry's own URL. Without this provider,\n * `useListQueryParams` falls back to the URL, which is what the Media Library\n * page relies on.\n */\nexport const ListQueryStateProvider = ({ children }: ListQueryStateProviderProps) => {\n const [search, setSearch] = useState('');\n const value = useMemo(() => ({ search, setSearch }), [search]);\n\n return createElement(ListQueryStateContext.Provider, { value }, children);\n};\n\n/**\n * Drop-in for `useQueryParams` used by the list hooks. The in-memory store keeps\n * a serialized search string and goes through the same `qs` round trip as the\n * URL, so callers that pre-encode values (`withEncodedUserParams`,\n * `encodeSearchQuery`) read back exactly what they would from the URL.\n */\nexport const useListQueryParams = <TQuery extends object>() => {\n const urlQueryParams = useQueryParams<TQuery>();\n const memoryState = useContext(ListQueryStateContext);\n\n const memorySearch = memoryState?.search ?? '';\n const setMemorySearch = memoryState?.setSearch;\n\n const memoryQuery = useMemo(() => parse(memorySearch) as TQuery, [memorySearch]);\n\n const setMemoryQuery = useCallback<SetQuery<TQuery>>(\n (nextParams, method = 'push') => {\n if (!setMemorySearch) {\n return;\n }\n\n setMemorySearch((currentSearch) => {\n const currentQuery = parse(currentSearch) as Partial<TQuery>;\n let nextQuery: Partial<TQuery>;\n\n if (method === 'remove') {\n nextQuery = { ...currentQuery };\n Object.keys(nextParams).forEach((key) => {\n delete nextQuery[key as keyof TQuery];\n });\n } else {\n nextQuery = { ...currentQuery, ...nextParams };\n }\n\n return stringify(nextQuery, { encode: false });\n });\n },\n [setMemorySearch]\n );\n\n if (!memoryState) {\n return urlQueryParams;\n }\n\n return [{ query: memoryQuery, rawQuery: memorySearch }, setMemoryQuery] as const;\n};\n"],"names":["ListQueryStateContext","createContext","ListQueryStateProvider","children","search","setSearch","useState","value","useMemo","createElement","Provider","useListQueryParams","urlQueryParams","useQueryParams","memoryState","useContext","memorySearch","setMemorySearch","memoryQuery","parse","setMemoryQuery","useCallback","nextParams","method","currentSearch","currentQuery","nextQuery","Object","keys","forEach","key","stringify","encode","query","rawQuery"],"mappings":";;;;;;AAsBA,MAAMA,wBAAwBC,mBAAAA,CAAqC,IAAA,CAAA;AAMnE;;;;;;AAMC,IACM,MAAMC,sBAAAA,GAAyB,CAAC,EAAEC,QAAQ,EAA+B,GAAA;AAC9E,IAAA,MAAM,CAACC,MAAAA,EAAQC,SAAAA,CAAU,GAAGC,cAAAA,CAAS,EAAA,CAAA;IACrC,MAAMC,KAAAA,GAAQC,aAAAA,CAAQ,KAAO;AAAEJ,YAAAA,MAAAA;AAAQC,YAAAA;AAAU,SAAA,CAAA,EAAI;AAACD,QAAAA;AAAO,KAAA,CAAA;IAE7D,OAAOK,mBAAAA,CAAcT,qBAAAA,CAAsBU,QAAQ,EAAE;AAAEH,QAAAA;KAAM,EAAGJ,QAAAA,CAAAA;AAClE;AAEA;;;;;UAMaQ,kBAAAA,GAAqB,IAAA;AAChC,IAAA,MAAMC,cAAAA,GAAiBC,0BAAAA,EAAAA;AACvB,IAAA,MAAMC,cAAcC,gBAAAA,CAAWf,qBAAAA,CAAAA;IAE/B,MAAMgB,YAAAA,GAAeF,aAAaV,MAAAA,IAAU,EAAA;AAC5C,IAAA,MAAMa,kBAAkBH,WAAAA,EAAaT,SAAAA;AAErC,IAAA,MAAMa,WAAAA,GAAcV,aAAAA,CAAQ,IAAMW,QAAAA,CAAMH,YAAAA,CAAAA,EAAyB;AAACA,QAAAA;AAAa,KAAA,CAAA;AAE/E,IAAA,MAAMI,cAAAA,GAAiBC,iBAAAA,CACrB,CAACC,UAAAA,EAAYC,SAAS,MAAM,GAAA;AAC1B,QAAA,IAAI,CAACN,eAAAA,EAAiB;AACpB,YAAA;AACF,QAAA;AAEAA,QAAAA,eAAAA,CAAgB,CAACO,aAAAA,GAAAA;AACf,YAAA,MAAMC,eAAeN,QAAAA,CAAMK,aAAAA,CAAAA;YAC3B,IAAIE,SAAAA;AAEJ,YAAA,IAAIH,WAAW,QAAA,EAAU;gBACvBG,SAAAA,GAAY;AAAE,oBAAA,GAAGD;AAAa,iBAAA;AAC9BE,gBAAAA,MAAAA,CAAOC,IAAI,CAACN,UAAAA,CAAAA,CAAYO,OAAO,CAAC,CAACC,GAAAA,GAAAA;oBAC/B,OAAOJ,SAAS,CAACI,GAAAA,CAAoB;AACvC,gBAAA,CAAA,CAAA;YACF,CAAA,MAAO;gBACLJ,SAAAA,GAAY;AAAE,oBAAA,GAAGD,YAAY;AAAE,oBAAA,GAAGH;AAAW,iBAAA;AAC/C,YAAA;AAEA,YAAA,OAAOS,aAAUL,SAAAA,EAAW;gBAAEM,MAAAA,EAAQ;AAAM,aAAA,CAAA;AAC9C,QAAA,CAAA,CAAA;IACF,CAAA,EACA;AAACf,QAAAA;AAAgB,KAAA,CAAA;AAGnB,IAAA,IAAI,CAACH,WAAAA,EAAa;QAChB,OAAOF,cAAAA;AACT,IAAA;IAEA,OAAO;AAAC,QAAA;YAAEqB,KAAAA,EAAOf,WAAAA;YAAagB,QAAAA,EAAUlB;AAAa,SAAA;AAAGI,QAAAA;AAAe,KAAA;AACzE;;;;;"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createContext, useState, useMemo, createElement, useContext, useCallback } from 'react';
|
|
2
|
+
import { useQueryParams } from '@strapi/admin/strapi-admin';
|
|
3
|
+
import { parse, stringify } from 'qs';
|
|
4
|
+
|
|
5
|
+
const ListQueryStateContext = createContext(null);
|
|
6
|
+
/**
|
|
7
|
+
* Keeps the list query (folder, search, filters, sort) in memory instead of the
|
|
8
|
+
* URL, for surfaces hosted inside another page — the Content Manager asset
|
|
9
|
+
* picker would otherwise overwrite the entry's own URL. Without this provider,
|
|
10
|
+
* `useListQueryParams` falls back to the URL, which is what the Media Library
|
|
11
|
+
* page relies on.
|
|
12
|
+
*/ const ListQueryStateProvider = ({ children })=>{
|
|
13
|
+
const [search, setSearch] = useState('');
|
|
14
|
+
const value = useMemo(()=>({
|
|
15
|
+
search,
|
|
16
|
+
setSearch
|
|
17
|
+
}), [
|
|
18
|
+
search
|
|
19
|
+
]);
|
|
20
|
+
return createElement(ListQueryStateContext.Provider, {
|
|
21
|
+
value
|
|
22
|
+
}, children);
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Drop-in for `useQueryParams` used by the list hooks. The in-memory store keeps
|
|
26
|
+
* a serialized search string and goes through the same `qs` round trip as the
|
|
27
|
+
* URL, so callers that pre-encode values (`withEncodedUserParams`,
|
|
28
|
+
* `encodeSearchQuery`) read back exactly what they would from the URL.
|
|
29
|
+
*/ const useListQueryParams = ()=>{
|
|
30
|
+
const urlQueryParams = useQueryParams();
|
|
31
|
+
const memoryState = useContext(ListQueryStateContext);
|
|
32
|
+
const memorySearch = memoryState?.search ?? '';
|
|
33
|
+
const setMemorySearch = memoryState?.setSearch;
|
|
34
|
+
const memoryQuery = useMemo(()=>parse(memorySearch), [
|
|
35
|
+
memorySearch
|
|
36
|
+
]);
|
|
37
|
+
const setMemoryQuery = useCallback((nextParams, method = 'push')=>{
|
|
38
|
+
if (!setMemorySearch) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
setMemorySearch((currentSearch)=>{
|
|
42
|
+
const currentQuery = parse(currentSearch);
|
|
43
|
+
let nextQuery;
|
|
44
|
+
if (method === 'remove') {
|
|
45
|
+
nextQuery = {
|
|
46
|
+
...currentQuery
|
|
47
|
+
};
|
|
48
|
+
Object.keys(nextParams).forEach((key)=>{
|
|
49
|
+
delete nextQuery[key];
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
nextQuery = {
|
|
53
|
+
...currentQuery,
|
|
54
|
+
...nextParams
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return stringify(nextQuery, {
|
|
58
|
+
encode: false
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}, [
|
|
62
|
+
setMemorySearch
|
|
63
|
+
]);
|
|
64
|
+
if (!memoryState) {
|
|
65
|
+
return urlQueryParams;
|
|
66
|
+
}
|
|
67
|
+
return [
|
|
68
|
+
{
|
|
69
|
+
query: memoryQuery,
|
|
70
|
+
rawQuery: memorySearch
|
|
71
|
+
},
|
|
72
|
+
setMemoryQuery
|
|
73
|
+
];
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export { ListQueryStateProvider, useListQueryParams };
|
|
77
|
+
//# sourceMappingURL=useListQueryParams.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useListQueryParams.mjs","sources":["../../../../../admin/src/pages/Assets/hooks/useListQueryParams.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useMemo,\n useState,\n type Dispatch,\n type ReactNode,\n type SetStateAction,\n} from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\nimport { parse, stringify } from 'qs';\n\ntype SetQuery<TQuery> = (nextParams: TQuery, method?: 'push' | 'remove', replace?: boolean) => void;\n\ninterface ListQueryState {\n search: string;\n setSearch: Dispatch<SetStateAction<string>>;\n}\n\nconst ListQueryStateContext = createContext<ListQueryState | null>(null);\n\ninterface ListQueryStateProviderProps {\n children: ReactNode;\n}\n\n/**\n * Keeps the list query (folder, search, filters, sort) in memory instead of the\n * URL, for surfaces hosted inside another page — the Content Manager asset\n * picker would otherwise overwrite the entry's own URL. Without this provider,\n * `useListQueryParams` falls back to the URL, which is what the Media Library\n * page relies on.\n */\nexport const ListQueryStateProvider = ({ children }: ListQueryStateProviderProps) => {\n const [search, setSearch] = useState('');\n const value = useMemo(() => ({ search, setSearch }), [search]);\n\n return createElement(ListQueryStateContext.Provider, { value }, children);\n};\n\n/**\n * Drop-in for `useQueryParams` used by the list hooks. The in-memory store keeps\n * a serialized search string and goes through the same `qs` round trip as the\n * URL, so callers that pre-encode values (`withEncodedUserParams`,\n * `encodeSearchQuery`) read back exactly what they would from the URL.\n */\nexport const useListQueryParams = <TQuery extends object>() => {\n const urlQueryParams = useQueryParams<TQuery>();\n const memoryState = useContext(ListQueryStateContext);\n\n const memorySearch = memoryState?.search ?? '';\n const setMemorySearch = memoryState?.setSearch;\n\n const memoryQuery = useMemo(() => parse(memorySearch) as TQuery, [memorySearch]);\n\n const setMemoryQuery = useCallback<SetQuery<TQuery>>(\n (nextParams, method = 'push') => {\n if (!setMemorySearch) {\n return;\n }\n\n setMemorySearch((currentSearch) => {\n const currentQuery = parse(currentSearch) as Partial<TQuery>;\n let nextQuery: Partial<TQuery>;\n\n if (method === 'remove') {\n nextQuery = { ...currentQuery };\n Object.keys(nextParams).forEach((key) => {\n delete nextQuery[key as keyof TQuery];\n });\n } else {\n nextQuery = { ...currentQuery, ...nextParams };\n }\n\n return stringify(nextQuery, { encode: false });\n });\n },\n [setMemorySearch]\n );\n\n if (!memoryState) {\n return urlQueryParams;\n }\n\n return [{ query: memoryQuery, rawQuery: memorySearch }, setMemoryQuery] as const;\n};\n"],"names":["ListQueryStateContext","createContext","ListQueryStateProvider","children","search","setSearch","useState","value","useMemo","createElement","Provider","useListQueryParams","urlQueryParams","useQueryParams","memoryState","useContext","memorySearch","setMemorySearch","memoryQuery","parse","setMemoryQuery","useCallback","nextParams","method","currentSearch","currentQuery","nextQuery","Object","keys","forEach","key","stringify","encode","query","rawQuery"],"mappings":";;;;AAsBA,MAAMA,wBAAwBC,aAAAA,CAAqC,IAAA,CAAA;AAMnE;;;;;;AAMC,IACM,MAAMC,sBAAAA,GAAyB,CAAC,EAAEC,QAAQ,EAA+B,GAAA;AAC9E,IAAA,MAAM,CAACC,MAAAA,EAAQC,SAAAA,CAAU,GAAGC,QAAAA,CAAS,EAAA,CAAA;IACrC,MAAMC,KAAAA,GAAQC,OAAAA,CAAQ,KAAO;AAAEJ,YAAAA,MAAAA;AAAQC,YAAAA;AAAU,SAAA,CAAA,EAAI;AAACD,QAAAA;AAAO,KAAA,CAAA;IAE7D,OAAOK,aAAAA,CAAcT,qBAAAA,CAAsBU,QAAQ,EAAE;AAAEH,QAAAA;KAAM,EAAGJ,QAAAA,CAAAA;AAClE;AAEA;;;;;UAMaQ,kBAAAA,GAAqB,IAAA;AAChC,IAAA,MAAMC,cAAAA,GAAiBC,cAAAA,EAAAA;AACvB,IAAA,MAAMC,cAAcC,UAAAA,CAAWf,qBAAAA,CAAAA;IAE/B,MAAMgB,YAAAA,GAAeF,aAAaV,MAAAA,IAAU,EAAA;AAC5C,IAAA,MAAMa,kBAAkBH,WAAAA,EAAaT,SAAAA;AAErC,IAAA,MAAMa,WAAAA,GAAcV,OAAAA,CAAQ,IAAMW,KAAAA,CAAMH,YAAAA,CAAAA,EAAyB;AAACA,QAAAA;AAAa,KAAA,CAAA;AAE/E,IAAA,MAAMI,cAAAA,GAAiBC,WAAAA,CACrB,CAACC,UAAAA,EAAYC,SAAS,MAAM,GAAA;AAC1B,QAAA,IAAI,CAACN,eAAAA,EAAiB;AACpB,YAAA;AACF,QAAA;AAEAA,QAAAA,eAAAA,CAAgB,CAACO,aAAAA,GAAAA;AACf,YAAA,MAAMC,eAAeN,KAAAA,CAAMK,aAAAA,CAAAA;YAC3B,IAAIE,SAAAA;AAEJ,YAAA,IAAIH,WAAW,QAAA,EAAU;gBACvBG,SAAAA,GAAY;AAAE,oBAAA,GAAGD;AAAa,iBAAA;AAC9BE,gBAAAA,MAAAA,CAAOC,IAAI,CAACN,UAAAA,CAAAA,CAAYO,OAAO,CAAC,CAACC,GAAAA,GAAAA;oBAC/B,OAAOJ,SAAS,CAACI,GAAAA,CAAoB;AACvC,gBAAA,CAAA,CAAA;YACF,CAAA,MAAO;gBACLJ,SAAAA,GAAY;AAAE,oBAAA,GAAGD,YAAY;AAAE,oBAAA,GAAGH;AAAW,iBAAA;AAC/C,YAAA;AAEA,YAAA,OAAOS,UAAUL,SAAAA,EAAW;gBAAEM,MAAAA,EAAQ;AAAM,aAAA,CAAA;AAC9C,QAAA,CAAA,CAAA;IACF,CAAA,EACA;AAACf,QAAAA;AAAgB,KAAA,CAAA;AAGnB,IAAA,IAAI,CAACH,WAAAA,EAAa;QAChB,OAAOF,cAAAA;AACT,IAAA;IAEA,OAAO;AAAC,QAAA;YAAEqB,KAAAA,EAAOf,WAAAA;YAAagB,QAAAA,EAAUlB;AAAa,SAAA;AAAGI,QAAAA;AAAe,KAAA;AACzE;;;;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
|
|
5
|
+
const DEFAULT_LIST_SETTINGS = {
|
|
6
|
+
showItemActions: true,
|
|
7
|
+
showSectionHeadings: false,
|
|
8
|
+
gridMinTileWidth: '240px',
|
|
9
|
+
showGridSelectAll: false
|
|
10
|
+
};
|
|
11
|
+
const ListSettingsContext = React.createContext(DEFAULT_LIST_SETTINGS);
|
|
12
|
+
const ListSettingsProvider = ({ children, showItemActions = DEFAULT_LIST_SETTINGS.showItemActions, showSectionHeadings = DEFAULT_LIST_SETTINGS.showSectionHeadings, gridMinTileWidth = DEFAULT_LIST_SETTINGS.gridMinTileWidth, showGridSelectAll = DEFAULT_LIST_SETTINGS.showGridSelectAll })=>{
|
|
13
|
+
const value = React.useMemo(()=>({
|
|
14
|
+
showItemActions,
|
|
15
|
+
showSectionHeadings,
|
|
16
|
+
gridMinTileWidth,
|
|
17
|
+
showGridSelectAll
|
|
18
|
+
}), [
|
|
19
|
+
showItemActions,
|
|
20
|
+
showSectionHeadings,
|
|
21
|
+
gridMinTileWidth,
|
|
22
|
+
showGridSelectAll
|
|
23
|
+
]);
|
|
24
|
+
return React.createElement(ListSettingsContext.Provider, {
|
|
25
|
+
value
|
|
26
|
+
}, children);
|
|
27
|
+
};
|
|
28
|
+
/** Without a provider, returns the Media Library page's settings. */ const useListSettings = ()=>React.useContext(ListSettingsContext);
|
|
29
|
+
|
|
30
|
+
exports.ListSettingsProvider = ListSettingsProvider;
|
|
31
|
+
exports.useListSettings = useListSettings;
|
|
32
|
+
//# sourceMappingURL=useListSettings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useListSettings.js","sources":["../../../../../admin/src/pages/Assets/hooks/useListSettings.ts"],"sourcesContent":["import { createContext, createElement, useContext, useMemo, type ReactNode } from 'react';\n\ninterface ListSettings {\n /**\n * Management affordances on list items: the right-click menu and the bulk\n * actions bar. A picker hosted elsewhere (the Content Manager asset picker)\n * turns them off — choosing an asset is not the place to delete or move it.\n */\n showItemActions: boolean;\n /**\n * \"Folders (n)\" / \"Assets (n)\" headings above each grid section, with a\n * divider between them. The page carries the count in its own header instead.\n */\n showSectionHeadings: boolean;\n /** Narrowest a grid tile may get before the grid drops a column. */\n gridMinTileWidth: string;\n /**\n * A \"Select all\" checkbox beside the grid's \"Assets (n)\" heading, the grid's\n * counterpart of the table's header checkbox. The page reaches select-all\n * from its bulk actions bar instead.\n */\n showGridSelectAll: boolean;\n}\n\nconst DEFAULT_LIST_SETTINGS: ListSettings = {\n showItemActions: true,\n showSectionHeadings: false,\n gridMinTileWidth: '240px',\n showGridSelectAll: false,\n};\n\nconst ListSettingsContext = createContext<ListSettings>(DEFAULT_LIST_SETTINGS);\n\ninterface ListSettingsProviderProps extends Partial<ListSettings> {\n children: ReactNode;\n}\n\nexport const ListSettingsProvider = ({\n children,\n showItemActions = DEFAULT_LIST_SETTINGS.showItemActions,\n showSectionHeadings = DEFAULT_LIST_SETTINGS.showSectionHeadings,\n gridMinTileWidth = DEFAULT_LIST_SETTINGS.gridMinTileWidth,\n showGridSelectAll = DEFAULT_LIST_SETTINGS.showGridSelectAll,\n}: ListSettingsProviderProps) => {\n const value = useMemo(\n () => ({ showItemActions, showSectionHeadings, gridMinTileWidth, showGridSelectAll }),\n [showItemActions, showSectionHeadings, gridMinTileWidth, showGridSelectAll]\n );\n\n return createElement(ListSettingsContext.Provider, { value }, children);\n};\n\n/** Without a provider, returns the Media Library page's settings. */\nexport const useListSettings = () => useContext(ListSettingsContext);\n"],"names":["DEFAULT_LIST_SETTINGS","showItemActions","showSectionHeadings","gridMinTileWidth","showGridSelectAll","ListSettingsContext","createContext","ListSettingsProvider","children","value","useMemo","createElement","Provider","useListSettings","useContext"],"mappings":";;;;AAwBA,MAAMA,qBAAAA,GAAsC;IAC1CC,eAAAA,EAAiB,IAAA;IACjBC,mBAAAA,EAAqB,KAAA;IACrBC,gBAAAA,EAAkB,OAAA;IAClBC,iBAAAA,EAAmB;AACrB,CAAA;AAEA,MAAMC,sBAAsBC,mBAAAA,CAA4BN,qBAAAA,CAAAA;AAMjD,MAAMO,uBAAuB,CAAC,EACnCC,QAAQ,EACRP,eAAAA,GAAkBD,sBAAsBC,eAAe,EACvDC,sBAAsBF,qBAAAA,CAAsBE,mBAAmB,EAC/DC,gBAAAA,GAAmBH,qBAAAA,CAAsBG,gBAAgB,EACzDC,iBAAAA,GAAoBJ,qBAAAA,CAAsBI,iBAAiB,EACjC,GAAA;IAC1B,MAAMK,KAAAA,GAAQC,aAAAA,CACZ,KAAO;AAAET,YAAAA,eAAAA;AAAiBC,YAAAA,mBAAAA;AAAqBC,YAAAA,gBAAAA;AAAkBC,YAAAA;AAAkB,SAAA,CAAA,EACnF;AAACH,QAAAA,eAAAA;AAAiBC,QAAAA,mBAAAA;AAAqBC,QAAAA,gBAAAA;AAAkBC,QAAAA;AAAkB,KAAA,CAAA;IAG7E,OAAOO,mBAAAA,CAAcN,mBAAAA,CAAoBO,QAAQ,EAAE;AAAEH,QAAAA;KAAM,EAAGD,QAAAA,CAAAA;AAChE;AAEA,sEACO,MAAMK,eAAAA,GAAkB,IAAMC,iBAAWT,mBAAAA;;;;;"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createContext, useMemo, createElement, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_LIST_SETTINGS = {
|
|
4
|
+
showItemActions: true,
|
|
5
|
+
showSectionHeadings: false,
|
|
6
|
+
gridMinTileWidth: '240px',
|
|
7
|
+
showGridSelectAll: false
|
|
8
|
+
};
|
|
9
|
+
const ListSettingsContext = createContext(DEFAULT_LIST_SETTINGS);
|
|
10
|
+
const ListSettingsProvider = ({ children, showItemActions = DEFAULT_LIST_SETTINGS.showItemActions, showSectionHeadings = DEFAULT_LIST_SETTINGS.showSectionHeadings, gridMinTileWidth = DEFAULT_LIST_SETTINGS.gridMinTileWidth, showGridSelectAll = DEFAULT_LIST_SETTINGS.showGridSelectAll })=>{
|
|
11
|
+
const value = useMemo(()=>({
|
|
12
|
+
showItemActions,
|
|
13
|
+
showSectionHeadings,
|
|
14
|
+
gridMinTileWidth,
|
|
15
|
+
showGridSelectAll
|
|
16
|
+
}), [
|
|
17
|
+
showItemActions,
|
|
18
|
+
showSectionHeadings,
|
|
19
|
+
gridMinTileWidth,
|
|
20
|
+
showGridSelectAll
|
|
21
|
+
]);
|
|
22
|
+
return createElement(ListSettingsContext.Provider, {
|
|
23
|
+
value
|
|
24
|
+
}, children);
|
|
25
|
+
};
|
|
26
|
+
/** Without a provider, returns the Media Library page's settings. */ const useListSettings = ()=>useContext(ListSettingsContext);
|
|
27
|
+
|
|
28
|
+
export { ListSettingsProvider, useListSettings };
|
|
29
|
+
//# sourceMappingURL=useListSettings.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useListSettings.mjs","sources":["../../../../../admin/src/pages/Assets/hooks/useListSettings.ts"],"sourcesContent":["import { createContext, createElement, useContext, useMemo, type ReactNode } from 'react';\n\ninterface ListSettings {\n /**\n * Management affordances on list items: the right-click menu and the bulk\n * actions bar. A picker hosted elsewhere (the Content Manager asset picker)\n * turns them off — choosing an asset is not the place to delete or move it.\n */\n showItemActions: boolean;\n /**\n * \"Folders (n)\" / \"Assets (n)\" headings above each grid section, with a\n * divider between them. The page carries the count in its own header instead.\n */\n showSectionHeadings: boolean;\n /** Narrowest a grid tile may get before the grid drops a column. */\n gridMinTileWidth: string;\n /**\n * A \"Select all\" checkbox beside the grid's \"Assets (n)\" heading, the grid's\n * counterpart of the table's header checkbox. The page reaches select-all\n * from its bulk actions bar instead.\n */\n showGridSelectAll: boolean;\n}\n\nconst DEFAULT_LIST_SETTINGS: ListSettings = {\n showItemActions: true,\n showSectionHeadings: false,\n gridMinTileWidth: '240px',\n showGridSelectAll: false,\n};\n\nconst ListSettingsContext = createContext<ListSettings>(DEFAULT_LIST_SETTINGS);\n\ninterface ListSettingsProviderProps extends Partial<ListSettings> {\n children: ReactNode;\n}\n\nexport const ListSettingsProvider = ({\n children,\n showItemActions = DEFAULT_LIST_SETTINGS.showItemActions,\n showSectionHeadings = DEFAULT_LIST_SETTINGS.showSectionHeadings,\n gridMinTileWidth = DEFAULT_LIST_SETTINGS.gridMinTileWidth,\n showGridSelectAll = DEFAULT_LIST_SETTINGS.showGridSelectAll,\n}: ListSettingsProviderProps) => {\n const value = useMemo(\n () => ({ showItemActions, showSectionHeadings, gridMinTileWidth, showGridSelectAll }),\n [showItemActions, showSectionHeadings, gridMinTileWidth, showGridSelectAll]\n );\n\n return createElement(ListSettingsContext.Provider, { value }, children);\n};\n\n/** Without a provider, returns the Media Library page's settings. */\nexport const useListSettings = () => useContext(ListSettingsContext);\n"],"names":["DEFAULT_LIST_SETTINGS","showItemActions","showSectionHeadings","gridMinTileWidth","showGridSelectAll","ListSettingsContext","createContext","ListSettingsProvider","children","value","useMemo","createElement","Provider","useListSettings","useContext"],"mappings":";;AAwBA,MAAMA,qBAAAA,GAAsC;IAC1CC,eAAAA,EAAiB,IAAA;IACjBC,mBAAAA,EAAqB,KAAA;IACrBC,gBAAAA,EAAkB,OAAA;IAClBC,iBAAAA,EAAmB;AACrB,CAAA;AAEA,MAAMC,sBAAsBC,aAAAA,CAA4BN,qBAAAA,CAAAA;AAMjD,MAAMO,uBAAuB,CAAC,EACnCC,QAAQ,EACRP,eAAAA,GAAkBD,sBAAsBC,eAAe,EACvDC,sBAAsBF,qBAAAA,CAAsBE,mBAAmB,EAC/DC,gBAAAA,GAAmBH,qBAAAA,CAAsBG,gBAAgB,EACzDC,iBAAAA,GAAoBJ,qBAAAA,CAAsBI,iBAAiB,EACjC,GAAA;IAC1B,MAAMK,KAAAA,GAAQC,OAAAA,CACZ,KAAO;AAAET,YAAAA,eAAAA;AAAiBC,YAAAA,mBAAAA;AAAqBC,YAAAA,gBAAAA;AAAkBC,YAAAA;AAAkB,SAAA,CAAA,EACnF;AAACH,QAAAA,eAAAA;AAAiBC,QAAAA,mBAAAA;AAAqBC,QAAAA,gBAAAA;AAAkBC,QAAAA;AAAkB,KAAA,CAAA;IAG7E,OAAOO,aAAAA,CAAcN,mBAAAA,CAAoBO,QAAQ,EAAE;AAAEH,QAAAA;KAAM,EAAGD,QAAAA,CAAAA;AAChE;AAEA,sEACO,MAAMK,eAAAA,GAAkB,IAAMC,WAAWT,mBAAAA;;;;"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
|
4
|
+
var useListQueryParams = require('./useListQueryParams.js');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Sort state for the assets list, backed by URL query params so the current
|
|
@@ -64,7 +65,7 @@ const serializeSort = (sortBy, direction)=>{
|
|
|
64
65
|
return rules.filter((rule)=>Boolean(rule)).join(',');
|
|
65
66
|
};
|
|
66
67
|
const useListSort = ()=>{
|
|
67
|
-
const [{ query }, setQuery] =
|
|
68
|
+
const [{ query }, setQuery] = useListQueryParams.useListQueryParams();
|
|
68
69
|
const { sortBy, direction, isExplicit } = parseSortParam(query?.sort);
|
|
69
70
|
const foldersPosition = query?.folders === 'mixed' ? 'mixed' : 'top';
|
|
70
71
|
const writeSort = (nextSortBy, nextDirection)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListSort.js","sources":["../../../../../admin/src/pages/Assets/hooks/useListSort.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"useListSort.js","sources":["../../../../../admin/src/pages/Assets/hooks/useListSort.ts"],"sourcesContent":["import { withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\nimport { useListQueryParams } from './useListQueryParams';\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] = useListQueryParams<{\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","useListQueryParams","sort","foldersPosition","folders","writeSort","nextSortBy","nextDirection","serialized","withEncodedUserParams","undefined","setSortBy","setDirection","setFoldersPosition","position","assetsSort","folderRuleCandidates","startsWith","folderRules","foldersSort","length"],"mappings":";;;;;AAIA;;;;;;;;;;;;UAcaA,aAAAA,GAAgB;IAC3BC,aAAAA,EAAe,eAAA;IACfC,iBAAAA,EAAmB;AACrB;MAEaC,oBAAAA,GAAuB;IAClCC,OAAAA,EAAS,UAAA;IACTC,QAAAA,EAAU,WAAA;IACVC,OAAAA,EAAS,UAAA;IACTC,QAAAA,EAAU;AACZ;AAMA,MAAMC,eAAAA,GAA6B,mBAAA;AAEnC,MAAMC,eAAAA,GAAkBC,MAAAA,CAAOC,WAAW,CACxCD,OAAOE,OAAO,CAACZ,aAAAA,CAAAA,CAAea,GAAG,CAAC,CAAC,CAACC,GAAAA,EAAKC,KAAK,GAAK;AAACA,QAAAA,IAAAA;AAAMD,QAAAA;AAAI,KAAA,CAAA,CAAA;AAGhE,MAAME,sBAAAA,GAAyBN,MAAAA,CAAOC,WAAW,CAC/CD,OAAOE,OAAO,CAACT,oBAAAA,CAAAA,CAAsBU,GAAG,CAAC,CAAC,CAACC,GAAAA,EAAKC,KAAK,GAAK;AAACA,QAAAA,IAAAA;AAAMD,QAAAA;AAAI,KAAA,CAAA,CAAA;AAUvE,oFACA,MAAMG,cAAAA,GAAiB,CAACC,GAAAA,GAAAA;IACtB,KAAK,MAAMH,QAAQ,CAACG,OAAO,EAAC,EAAGC,KAAK,CAAC,GAAA,CAAA,CAAM;AACzC,QAAA,IAAIJ,QAAQN,eAAAA,EAAiB;YAC3B,OAAO;gBAAEW,MAAAA,EAAQX,eAAe,CAACM,IAAAA,CAAK;gBAAEM,SAAAA,EAAW,IAAA;gBAAMC,UAAAA,EAAY;AAAK,aAAA;AAC5E,QAAA;AACA,QAAA,IAAIP,QAAQC,sBAAAA,EAAwB;YAClC,OAAO;gBAAEI,MAAAA,EAAQ,IAAA;gBAAMC,SAAAA,EAAWL,sBAAsB,CAACD,IAAAA,CAAK;gBAAEO,UAAAA,EAAY;AAAK,aAAA;AACnF,QAAA;AACF,IAAA;IAEA,OAAO;QAAEF,MAAAA,EAAQZ,eAAAA;QAAiBa,SAAAA,EAAW,IAAA;QAAMC,UAAAA,EAAY;AAAM,KAAA;AACvE,CAAA;AAEA,MAAMC,aAAAA,GAAgB,CAACH,MAAAA,EAA0BC,SAAAA,GAAAA;AAC/C,IAAA,MAAMG,KAAAA,GAA2B;QAC/BJ,MAAAA,IAAUpB,aAAa,CAACoB,MAAAA,CAAO;QAC/BC,SAAAA,IAAalB,oBAAoB,CAACkB,SAAAA;AACnC,KAAA;IAED,OAAOG,KAAAA,CAAMC,MAAM,CAAC,CAACV,OAAyBW,OAAAA,CAAQX,IAAAA,CAAAA,CAAAA,CAAOY,IAAI,CAAC,GAAA,CAAA;AACpE,CAAA;MAuBaC,WAAAA,GAAc,IAAA;AACzB,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,qCAAAA,EAAAA;IAM9B,MAAM,EAAEX,MAAM,EAAEC,SAAS,EAAEC,UAAU,EAAE,GAAGL,cAAAA,CAAeY,KAAAA,EAAOG,IAAAA,CAAAA;AAChE,IAAA,MAAMC,eAAAA,GAAmCJ,KAAAA,EAAOK,OAAAA,KAAY,OAAA,GAAU,OAAA,GAAU,KAAA;IAEhF,MAAMC,SAAAA,GAAY,CAACC,UAAAA,EAA8BC,aAAAA,GAAAA;;QAE/C,IAAID,UAAAA,KAAe,IAAA,IAAQC,aAAAA,KAAkB,IAAA,EAAM;YACjDD,UAAAA,GAAa5B,eAAAA;AACf,QAAA;QAEA,MAAM8B,UAAAA,GAAaf,cAAca,UAAAA,EAAYC,aAAAA,CAAAA;QAE7C,IAAID,UAAAA,KAAe5B,eAAAA,IAAmB6B,aAAAA,KAAkB,IAAA,EAAM;AAC5DP,YAAAA,QAAAA,CAASS,kCAAsBV,KAAAA,EAAO;gBAAEG,IAAAA,EAAMQ;AAAU,aAAA,CAAA,CAAA;QAC1D,CAAA,MAAO;AACLV,YAAAA,QAAAA,CAASS,kCAAsBV,KAAAA,EAAO;gBAAEG,IAAAA,EAAMM;AAAW,aAAA,CAAA,CAAA;AAC3D,QAAA;AACF,IAAA,CAAA;;AAGA,IAAA,MAAMG,SAAAA,GAAY,CAAC3B,GAAAA,GAA0BqB,SAAAA,CAAUrB,GAAAA,EAAK,IAAA,CAAA;AAC5D,IAAA,MAAM4B,YAAAA,GAAe,CAAC5B,GAAAA,GAAiCqB,SAAAA,CAAU,IAAA,EAAMrB,GAAAA,CAAAA;AAEvE,IAAA,MAAM6B,qBAAqB,CAACC,QAAAA,GAAAA;AAC1B,QAAA,IAAIA,aAAa,OAAA,EAAS;AACxBd,YAAAA,QAAAA,CAASS,kCAAsBV,KAAAA,EAAO;gBAAEK,OAAAA,EAAS;AAAQ,aAAA,CAAA,CAAA;QAC3D,CAAA,MAAO;AACLJ,YAAAA,QAAAA,CAASS,kCAAsBV,KAAAA,EAAO;gBAAEK,OAAAA,EAASM;AAAU,aAAA,CAAA,CAAA;AAC7D,QAAA;AACF,IAAA,CAAA;IAEA,MAAMK,UAAAA,GAAatB,cAAcH,MAAAA,EAAQC,SAAAA,CAAAA;AACzC,IAAA,MAAMyB,oBAAAA,GAA0C;QAC9C1B,MAAAA,IAAUpB,aAAa,CAACoB,MAAAA,CAAO;QAC/BC,SAAAA,IAAa,CAACA,UAAU0B,UAAU,CAAC,UAAU5C,oBAAoB,CAACkB,UAAU,GAAG;AAChF,KAAA;AACD,IAAA,MAAM2B,cAAcF,oBAAAA,CAAqBrB,MAAM,CAAC,CAACV,OAAyBW,OAAAA,CAAQX,IAAAA,CAAAA,CAAAA;;;IAGlF,MAAMkC,WAAAA,GAAc3B,cAAc0B,WAAAA,CAAYE,MAAM,GAAG,CAAA,GAAIF,WAAAA,CAAYrB,IAAI,CAAC,GAAA,CAAA,GAAO,UAAA;IAEnF,OAAO;AACLP,QAAAA,MAAAA;AACAC,QAAAA,SAAAA;AACAY,QAAAA,eAAAA;AACAY,QAAAA,UAAAA;AACAI,QAAAA,WAAAA;AACAR,QAAAA,SAAAA;AACAC,QAAAA,YAAAA;AACAC,QAAAA;AACF,KAAA;AACF;;;;;;"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { withEncodedUserParams } from '@strapi/admin/strapi-admin';
|
|
2
|
+
import { useListQueryParams } from './useListQueryParams.mjs';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Sort state for the assets list, backed by URL query params so the current
|
|
@@ -62,7 +63,7 @@ const serializeSort = (sortBy, direction)=>{
|
|
|
62
63
|
return rules.filter((rule)=>Boolean(rule)).join(',');
|
|
63
64
|
};
|
|
64
65
|
const useListSort = ()=>{
|
|
65
|
-
const [{ query }, setQuery] =
|
|
66
|
+
const [{ query }, setQuery] = useListQueryParams();
|
|
66
67
|
const { sortBy, direction, isExplicit } = parseSortParam(query?.sort);
|
|
67
68
|
const foldersPosition = query?.folders === 'mixed' ? 'mixed' : 'top';
|
|
68
69
|
const writeSort = (nextSortBy, nextDirection)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListSort.mjs","sources":["../../../../../admin/src/pages/Assets/hooks/useListSort.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"useListSort.mjs","sources":["../../../../../admin/src/pages/Assets/hooks/useListSort.ts"],"sourcesContent":["import { withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\nimport { useListQueryParams } from './useListQueryParams';\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] = useListQueryParams<{\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","useListQueryParams","sort","foldersPosition","folders","writeSort","nextSortBy","nextDirection","serialized","withEncodedUserParams","undefined","setSortBy","setDirection","setFoldersPosition","position","assetsSort","folderRuleCandidates","startsWith","folderRules","foldersSort","length"],"mappings":"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"}
|