@strapi/upload 5.51.2 → 5.52.1
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/future/App.js +3 -3
- package/dist/admin/future/App.js.map +1 -1
- package/dist/admin/future/App.mjs +4 -4
- package/dist/admin/future/App.mjs.map +1 -1
- package/dist/admin/future/components/BetaNotice.js +72 -0
- package/dist/admin/future/components/BetaNotice.js.map +1 -0
- package/dist/admin/future/components/BetaNotice.mjs +70 -0
- package/dist/admin/future/components/BetaNotice.mjs.map +1 -0
- package/dist/admin/future/components/Drawer.js +16 -3
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +16 -3
- package/dist/admin/future/components/Drawer.mjs.map +1 -1
- package/dist/admin/future/components/TruncatedText.js +76 -0
- package/dist/admin/future/components/TruncatedText.js.map +1 -0
- package/dist/admin/future/components/TruncatedText.mjs +74 -0
- package/dist/admin/future/components/TruncatedText.mjs.map +1 -0
- package/dist/admin/future/hooks/useAIMetadataEnabled.js +39 -0
- package/dist/admin/future/hooks/useAIMetadataEnabled.js.map +1 -0
- package/dist/admin/future/hooks/useAIMetadataEnabled.mjs +37 -0
- package/dist/admin/future/hooks/useAIMetadataEnabled.mjs.map +1 -0
- package/dist/admin/future/hooks/useTracking.js +57 -0
- package/dist/admin/future/hooks/useTracking.js.map +1 -0
- package/dist/admin/future/hooks/useTracking.mjs +53 -0
- package/dist/admin/future/hooks/useTracking.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js +328 -139
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +332 -143
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js +46 -0
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs +44 -0
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +328 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +326 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +35 -14
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +35 -14
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +74 -53
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +74 -54
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +92 -28
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +94 -30
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +26 -4
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +27 -5
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +172 -60
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +175 -63
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +64 -132
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +64 -132
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +123 -76
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +126 -79
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BusyOverlay.js +36 -0
- package/dist/admin/future/pages/Assets/components/BusyOverlay.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs +34 -0
- package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js +131 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs +129 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +27 -17
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +28 -18
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +28 -31
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +28 -31
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +10 -15
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +10 -15
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterBadges.js +13 -4
- package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +13 -4
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterMenu.js +259 -63
- package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +262 -66
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +146 -0
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +144 -0
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.js → FolderFormDialog.js} +62 -16
- package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.mjs → FolderFormDialog.mjs} +64 -18
- package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +132 -229
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +135 -232
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/SortMenu.js +14 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs +14 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +5 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +6 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js +47 -0
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs +44 -0
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +203 -62
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +204 -63
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js +75 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs +73 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/selection.js +17 -0
- package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -1
- package/dist/admin/future/pages/Assets/utils/selection.mjs +17 -1
- package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -1
- package/dist/admin/future/services/api.js +31 -8
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +31 -8
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +22 -23
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +22 -23
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +37 -2
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +37 -3
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/utils/buildDragSetFromSelection.js +7 -5
- package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -1
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs +7 -5
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.js +12 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +12 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -1
- package/dist/admin/future/utils/getRelationId.js +24 -0
- package/dist/admin/future/utils/getRelationId.js.map +1 -0
- package/dist/admin/future/utils/getRelationId.mjs +22 -0
- package/dist/admin/future/utils/getRelationId.mjs.map +1 -0
- package/dist/admin/future/utils/isEventFromWithin.js +14 -0
- package/dist/admin/future/utils/isEventFromWithin.js.map +1 -0
- package/dist/admin/future/utils/isEventFromWithin.mjs +12 -0
- package/dist/admin/future/utils/isEventFromWithin.mjs.map +1 -0
- package/dist/admin/future/utils/itemLocations.js +40 -0
- package/dist/admin/future/utils/itemLocations.js.map +1 -0
- package/dist/admin/future/utils/itemLocations.mjs +36 -0
- package/dist/admin/future/utils/itemLocations.mjs.map +1 -0
- package/dist/admin/future/utils/itemSource.js +36 -0
- package/dist/admin/future/utils/itemSource.js.map +1 -0
- package/dist/admin/future/utils/itemSource.mjs +32 -0
- package/dist/admin/future/utils/itemSource.mjs.map +1 -0
- package/dist/admin/hooks/useAIMetadataJob.js +1 -1
- package/dist/admin/hooks/useAIMetadataJob.js.map +1 -1
- package/dist/admin/hooks/useAIMetadataJob.mjs +1 -1
- package/dist/admin/hooks/useAIMetadataJob.mjs.map +1 -1
- package/dist/admin/index.js +11 -17
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +12 -18
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.js +2 -2
- package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs +2 -2
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
- package/dist/admin/src/future/App.d.ts +1 -1
- package/dist/admin/src/future/components/BetaNotice.d.ts +11 -0
- package/dist/admin/src/future/components/Drawer.d.ts +5 -1
- package/dist/admin/src/future/components/TruncatedText.d.ts +32 -0
- package/dist/admin/src/future/hooks/useAIMetadataEnabled.d.ts +26 -0
- package/dist/admin/src/future/hooks/useTracking.d.ts +29 -0
- package/dist/admin/src/future/pages/Assets/components/ActionsMenuContent.d.ts +33 -0
- package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +26 -0
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +16 -0
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +17 -6
- package/dist/admin/src/future/pages/Assets/components/BusyOverlay.d.ts +9 -0
- package/dist/admin/src/future/pages/Assets/components/DeleteItemsDialog.d.ts +27 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +18 -3
- package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +2 -1
- package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +3 -1
- package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +4 -0
- package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +21 -0
- package/dist/admin/src/future/pages/Assets/components/FolderFormDialog.d.ts +16 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +2 -0
- package/dist/admin/src/future/pages/Assets/hooks/useBusyAssets.d.ts +38 -0
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +20 -6
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteScrollSentinel.d.ts +41 -0
- package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +10 -0
- package/dist/admin/src/future/services/api.d.ts +6 -7
- package/dist/admin/src/future/services/folders.d.ts +6 -1
- package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +4 -3
- package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +7 -1
- package/dist/admin/src/future/utils/getRelationId.d.ts +11 -0
- package/dist/admin/src/future/utils/isEventFromWithin.d.ts +11 -0
- package/dist/admin/src/future/utils/itemLocations.d.ts +31 -0
- package/dist/admin/src/future/utils/itemSource.d.ts +22 -0
- package/dist/admin/translations/en.json.js +25 -4
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +25 -4
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/controllers/admin-file.js +27 -9
- package/dist/server/controllers/admin-file.js.map +1 -1
- package/dist/server/controllers/admin-file.mjs +27 -9
- package/dist/server/controllers/admin-file.mjs.map +1 -1
- package/dist/server/controllers/admin-upload.js +24 -8
- package/dist/server/controllers/admin-upload.js.map +1 -1
- package/dist/server/controllers/admin-upload.mjs +24 -8
- package/dist/server/controllers/admin-upload.mjs.map +1 -1
- package/dist/server/routes/admin.js +41 -21
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/admin.mjs +41 -21
- package/dist/server/routes/admin.mjs.map +1 -1
- package/dist/server/services/ai-metadata.js.map +1 -1
- package/dist/server/services/ai-metadata.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/src/controllers/admin-file.d.ts +29 -8
- package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-upload.d.ts +20 -4
- package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +4 -4
- package/dist/server/src/index.d.ts +4 -4
- package/dist/server/src/routes/admin.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata.d.ts +2 -2
- package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
- package/dist/server/src/services/image-manipulation.d.ts +0 -5
- package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
- package/dist/shared/contracts/ai-metadata-jobs.d.ts +10 -32
- package/dist/shared/contracts/ai-metadata-jobs.d.ts.map +1 -1
- package/dist/shared/contracts/files.d.ts +10 -10
- package/dist/shared/contracts/files.d.ts.map +1 -1
- package/package.json +9 -9
- package/dist/admin/future/pages/Assets/components/CreateFolderDialog.js.map +0 -1
- package/dist/admin/future/pages/Assets/components/CreateFolderDialog.mjs.map +0 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +0 -33
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +0 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +0 -31
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +0 -1
- package/dist/admin/src/future/pages/Assets/components/CreateFolderDialog.d.ts +0 -8
- package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +0 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folders.js","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { encodeSearchQuery } from '../utils/searchQueryParam';\n\nimport { uploadApi } from './api';\n\nimport type {\n Folder,\n FolderNode,\n CreateFolders,\n GetFolder,\n GetFolders,\n GetFolderStructure,\n BulkMoveFolders,\n} from '../../../../shared/contracts/folders';\n\nexport type FolderWithCounts = Omit<Folder, 'children' | 'files'> & {\n children?: { count: number };\n files?: { count: number };\n};\n\ninterface GetFoldersParams {\n parentId?: number | null;\n /** Comma-separated rules, e.g. `updatedAt:DESC,name:ASC`. Defaults to alphabetical. */\n sort?: string;\n search?: string;\n /** Extra `filters[$and]` entries (list filters), AND-ed with the parent/search scope. */\n filters?: Record<string, unknown>[];\n}\n\ninterface BulkMoveParams {\n fileIds?: number[];\n folderIds?: number[];\n /** `null` moves the items to the root of the Media Library. */\n destinationFolderId: number | null;\n}\n\ntype DataEnvelope<T> = {\n data: T;\n};\n\nconst isDataEnvelope = <T>(response: T | DataEnvelope<T>): response is DataEnvelope<T> =>\n typeof response === 'object' && response !== null && 'data' in response;\n\nconst unwrapData = <T>(response: T | DataEnvelope<T>): T =>\n isDataEnvelope(response) ? response.data : response;\n\nconst foldersApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getFolders: builder.query<Folder[], GetFoldersParams | void>({\n query: (params = {}) => {\n const { parentId, sort, search, filters = [] } = params as GetFoldersParams;\n\n const queryParams: Record<string, unknown> = {\n // Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).\n sort: sort ?? 'name:ASC',\n };\n\n // List filters (dates) apply in BOTH modes — search composes with them,\n // only the parent scope is dropped while searching.\n if (search) {\n // Search is global: the parent filter is dropped so matching folders\n // anywhere in the library surface. The endpoint is unpaginated — it\n // returns every match — so callers can treat the array length as the\n // true total. Bounding it is a separate decision.\n queryParams['_q'] = encodeSearchQuery(search);\n\n if (filters.length > 0) {\n queryParams['filters'] = { $and: [...filters] };\n }\n } else {\n const parentScope =\n parentId != null ? { parent: { id: parentId } } : { parent: { id: { $null: true } } };\n\n queryParams['filters'] = {\n $and: [parentScope, ...filters],\n };\n }\n\n return {\n url: '/upload/folders',\n method: 'GET',\n config: { params: queryParams },\n };\n },\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder', id: 'LIST' },\n ];\n }\n return [{ type: 'Folder', id: 'LIST' }];\n },\n }),\n createFolder: builder.mutation<CreateFolders.Response['data'], CreateFolders.Request['body']>({\n query: (body) => ({\n url: '/upload/folders',\n method: 'POST',\n data: body,\n }),\n transformResponse: (response: CreateFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n /**\n * Hierarchical folder tree used by the FolderTree sidebar. Returned as a\n * single nested array — the server walks the folders table once and\n * assembles parent/child relationships.\n *\n * TODO: filter results like `admin-folder.find` for folder-scoped roles\n * (v1 returns all folders; relies on global `plugin::upload.read`).\n */\n getFolderStructure: builder.query<FolderNode[], void>({\n query: () => ({\n url: '/upload/folder-structure',\n method: 'GET',\n }),\n // TODO: align GetFolderStructure contract with the real /upload/folder-structure\n // envelope and drop this defensive unwrap (data[] vs { data: [] }).\n transformResponse: (response: GetFolderStructure.Response['data']) =>\n ((response as unknown as { data: FolderNode[] })?.data ??\n (response as unknown as FolderNode[]) ??\n []) as FolderNode[],\n providesTags: [{ type: 'Folder', id: 'STRUCTURE' }],\n }),\n /**\n * Flat list of every folder, used to populate the \"Location\" select in the\n * asset details drawer. No parent filter — we want the entire tree in one\n * query so the select can render any destination folder.\n */\n getAllFolders: builder.query<Folder[], void>({\n query: () => ({\n url: '/upload/folders',\n method: 'GET',\n }),\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response ?? []),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder' as const, id: 'LIST' },\n ];\n }\n return [{ type: 'Folder' as const, id: 'LIST' }];\n },\n }),\n getFolder: builder.query<FolderWithCounts, { id: number }>({\n query: ({ id }) => ({\n url: `/upload/folders/${id}`,\n method: 'GET',\n config: {\n params: {\n populate: {\n parent: {\n populate: {\n parent: '*',\n },\n },\n children: { count: true },\n files: { count: true },\n },\n },\n },\n }),\n transformResponse: (response: GetFolder.Response) =>\n response.data as unknown as FolderWithCounts,\n providesTags: (_result, _error, { id }) => [{ type: 'Folder', id }],\n }),\n bulkMove: builder.mutation<BulkMoveFolders.Response['data'], BulkMoveParams>({\n query: ({ fileIds = [], folderIds = [], destinationFolderId }) => ({\n url: '/upload/actions/bulk-move',\n method: 'POST',\n data: { fileIds, folderIds, destinationFolderId },\n }),\n transformResponse: (response: BulkMoveFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Asset', id: 'LIST' },\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n }),\n});\n\nexport const {\n useCreateFolderMutation,\n useGetFoldersQuery,\n useGetFolderQuery,\n useGetAllFoldersQuery,\n useGetFolderStructureQuery,\n useBulkMoveMutation,\n} = foldersApi;\n"],"names":["isDataEnvelope","response","unwrapData","data","foldersApi","uploadApi","injectEndpoints","endpoints","builder","getFolders","query","params","parentId","sort","search","filters","queryParams","encodeSearchQuery","length","$and","parentScope","parent","id","$null","url","method","config","transformResponse","providesTags","results","map","type","createFolder","mutation","body","invalidatesTags","getFolderStructure","getAllFolders","getFolder","populate","children","count","files","_result","_error","bulkMove","fileIds","folderIds","destinationFolderId","useCreateFolderMutation","useGetFoldersQuery","useGetFolderQuery","useGetAllFoldersQuery","useGetFolderStructureQuery","useBulkMoveMutation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"folders.js","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { encodeSearchQuery } from '../utils/searchQueryParam';\n\nimport { uploadApi } from './api';\n\nimport type {\n Folder,\n FolderNode,\n CreateFolders,\n GetFolder,\n GetFolders,\n GetFolderStructure,\n BulkMoveFolders,\n UpdateFolder,\n} from '../../../../shared/contracts/folders';\n\nexport type FolderWithCounts = Omit<Folder, 'children' | 'files'> & {\n children?: { count: number };\n files?: { count: number };\n};\n\ninterface GetFoldersParams {\n parentId?: number | null;\n /** Comma-separated rules, e.g. `updatedAt:DESC,name:ASC`. Defaults to alphabetical. */\n sort?: string;\n search?: string;\n /** Extra `filters[$and]` entries (list filters), AND-ed with the parent/search scope. */\n filters?: Record<string, unknown>[];\n}\n\ninterface BulkMoveParams {\n fileIds?: number[];\n folderIds?: number[];\n /** `null` moves the items to the root of the Media Library. */\n destinationFolderId: number | null;\n}\n\ntype DataEnvelope<T> = {\n data: T;\n};\n\nconst isDataEnvelope = <T>(response: T | DataEnvelope<T>): response is DataEnvelope<T> =>\n typeof response === 'object' && response !== null && 'data' in response;\n\nconst unwrapData = <T>(response: T | DataEnvelope<T>): T =>\n isDataEnvelope(response) ? response.data : response;\n\nconst foldersApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getFolders: builder.query<Folder[], GetFoldersParams | void>({\n query: (params = {}) => {\n const { parentId, sort, search, filters = [] } = params as GetFoldersParams;\n\n const queryParams: Record<string, unknown> = {\n // Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).\n sort: sort ?? 'name:ASC',\n populate: { parent: true },\n };\n\n // List filters (dates) apply in BOTH modes — search composes with them,\n // only the parent scope is dropped while searching.\n if (search) {\n // Search is global: the parent filter is dropped so matching folders\n // anywhere in the library surface. The endpoint is unpaginated — it\n // returns every match — so callers can treat the array length as the\n // true total. Bounding it is a separate decision.\n queryParams['_q'] = encodeSearchQuery(search);\n\n if (filters.length > 0) {\n queryParams['filters'] = { $and: [...filters] };\n }\n } else {\n const parentScope =\n parentId != null ? { parent: { id: parentId } } : { parent: { id: { $null: true } } };\n\n queryParams['filters'] = {\n $and: [parentScope, ...filters],\n };\n }\n\n return {\n url: '/upload/folders',\n method: 'GET',\n config: { params: queryParams },\n };\n },\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder', id: 'LIST' },\n ];\n }\n return [{ type: 'Folder', id: 'LIST' }];\n },\n }),\n createFolder: builder.mutation<CreateFolders.Response['data'], CreateFolders.Request['body']>({\n query: (body) => ({\n url: '/upload/folders',\n method: 'POST',\n data: body,\n }),\n transformResponse: (response: CreateFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n updateFolder: builder.mutation<\n UpdateFolder.Response['data'],\n { id: number } & UpdateFolder.Request['body']\n >({\n query: ({ id, ...body }) => ({\n url: `/upload/folders/${id}`,\n method: 'PUT',\n data: body,\n }),\n transformResponse: (response: UpdateFolder.Response) => response.data,\n invalidatesTags: (_result, _error, { id }) => [\n { type: 'Folder', id },\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n /**\n * Hierarchical folder tree used by the FolderTree sidebar. Returned as a\n * single nested array — the server walks the folders table once and\n * assembles parent/child relationships.\n *\n * TODO: filter results like `admin-folder.find` for folder-scoped roles\n * (v1 returns all folders; relies on global `plugin::upload.read`).\n */\n getFolderStructure: builder.query<FolderNode[], void>({\n query: () => ({\n url: '/upload/folder-structure',\n method: 'GET',\n }),\n // TODO: align GetFolderStructure contract with the real /upload/folder-structure\n // envelope and drop this defensive unwrap (data[] vs { data: [] }).\n transformResponse: (response: GetFolderStructure.Response['data']) =>\n ((response as unknown as { data: FolderNode[] })?.data ??\n (response as unknown as FolderNode[]) ??\n []) as FolderNode[],\n providesTags: [{ type: 'Folder', id: 'STRUCTURE' }],\n }),\n /**\n * Flat list of every folder, used to populate the \"Location\" select in the\n * asset details drawer. No parent filter — we want the entire tree in one\n * query so the select can render any destination folder.\n */\n getAllFolders: builder.query<Folder[], void>({\n query: () => ({\n url: '/upload/folders',\n method: 'GET',\n }),\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response ?? []),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder' as const, id: 'LIST' },\n ];\n }\n return [{ type: 'Folder' as const, id: 'LIST' }];\n },\n }),\n getFolder: builder.query<FolderWithCounts, { id: number }>({\n query: ({ id }) => ({\n url: `/upload/folders/${id}`,\n method: 'GET',\n config: {\n params: {\n populate: {\n parent: {\n populate: {\n parent: '*',\n },\n },\n children: { count: true },\n files: { count: true },\n },\n },\n },\n }),\n transformResponse: (response: GetFolder.Response) =>\n response.data as unknown as FolderWithCounts,\n // Also carries the LIST tag so a mutation that changes a folder's file\n // count (upload / delete into it) can refresh the header count by\n // invalidating `{ Folder, LIST }`, without needing the folder id at the\n // mutation site (it's buried in the upload FormData / not passed to the\n // delete).\n providesTags: (_result, _error, { id }) => [\n { type: 'Folder', id },\n { type: 'Folder', id: 'LIST' },\n ],\n }),\n bulkMove: builder.mutation<BulkMoveFolders.Response['data'], BulkMoveParams>({\n query: ({ fileIds = [], folderIds = [], destinationFolderId }) => ({\n url: '/upload/actions/bulk-move',\n method: 'POST',\n data: { fileIds, folderIds, destinationFolderId },\n }),\n transformResponse: (response: BulkMoveFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Asset', id: 'LIST' },\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n }),\n});\n\nexport const {\n useCreateFolderMutation,\n useUpdateFolderMutation,\n useGetFoldersQuery,\n useGetFolderQuery,\n useGetAllFoldersQuery,\n useGetFolderStructureQuery,\n useBulkMoveMutation,\n} = foldersApi;\n"],"names":["isDataEnvelope","response","unwrapData","data","foldersApi","uploadApi","injectEndpoints","endpoints","builder","getFolders","query","params","parentId","sort","search","filters","queryParams","populate","parent","encodeSearchQuery","length","$and","parentScope","id","$null","url","method","config","transformResponse","providesTags","results","map","type","createFolder","mutation","body","invalidatesTags","updateFolder","_result","_error","getFolderStructure","getAllFolders","getFolder","children","count","files","bulkMove","fileIds","folderIds","destinationFolderId","useCreateFolderMutation","useUpdateFolderMutation","useGetFoldersQuery","useGetFolderQuery","useGetAllFoldersQuery","useGetFolderStructureQuery","useBulkMoveMutation"],"mappings":"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"}
|
|
@@ -10,7 +10,10 @@ const foldersApi = uploadApi.injectEndpoints({
|
|
|
10
10
|
const { parentId, sort, search, filters = [] } = params;
|
|
11
11
|
const queryParams = {
|
|
12
12
|
// Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).
|
|
13
|
-
sort: sort ?? 'name:ASC'
|
|
13
|
+
sort: sort ?? 'name:ASC',
|
|
14
|
+
populate: {
|
|
15
|
+
parent: true
|
|
16
|
+
}
|
|
14
17
|
};
|
|
15
18
|
// List filters (dates) apply in BOTH modes — search composes with them,
|
|
16
19
|
// only the parent scope is dropped while searching.
|
|
@@ -94,6 +97,28 @@ const foldersApi = uploadApi.injectEndpoints({
|
|
|
94
97
|
}
|
|
95
98
|
]
|
|
96
99
|
}),
|
|
100
|
+
updateFolder: builder.mutation({
|
|
101
|
+
query: ({ id, ...body })=>({
|
|
102
|
+
url: `/upload/folders/${id}`,
|
|
103
|
+
method: 'PUT',
|
|
104
|
+
data: body
|
|
105
|
+
}),
|
|
106
|
+
transformResponse: (response)=>response.data,
|
|
107
|
+
invalidatesTags: (_result, _error, { id })=>[
|
|
108
|
+
{
|
|
109
|
+
type: 'Folder',
|
|
110
|
+
id
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
type: 'Folder',
|
|
114
|
+
id: 'LIST'
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
type: 'Folder',
|
|
118
|
+
id: 'STRUCTURE'
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}),
|
|
97
122
|
/**
|
|
98
123
|
* Hierarchical folder tree used by the FolderTree sidebar. Returned as a
|
|
99
124
|
* single nested array — the server walks the folders table once and
|
|
@@ -170,10 +195,19 @@ const foldersApi = uploadApi.injectEndpoints({
|
|
|
170
195
|
}
|
|
171
196
|
}),
|
|
172
197
|
transformResponse: (response)=>response.data,
|
|
198
|
+
// Also carries the LIST tag so a mutation that changes a folder's file
|
|
199
|
+
// count (upload / delete into it) can refresh the header count by
|
|
200
|
+
// invalidating `{ Folder, LIST }`, without needing the folder id at the
|
|
201
|
+
// mutation site (it's buried in the upload FormData / not passed to the
|
|
202
|
+
// delete).
|
|
173
203
|
providesTags: (_result, _error, { id })=>[
|
|
174
204
|
{
|
|
175
205
|
type: 'Folder',
|
|
176
206
|
id
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
type: 'Folder',
|
|
210
|
+
id: 'LIST'
|
|
177
211
|
}
|
|
178
212
|
]
|
|
179
213
|
}),
|
|
@@ -205,7 +239,7 @@ const foldersApi = uploadApi.injectEndpoints({
|
|
|
205
239
|
})
|
|
206
240
|
})
|
|
207
241
|
});
|
|
208
|
-
const { useCreateFolderMutation, useGetFoldersQuery, useGetFolderQuery, useGetAllFoldersQuery, useGetFolderStructureQuery, useBulkMoveMutation } = foldersApi;
|
|
242
|
+
const { useCreateFolderMutation, useUpdateFolderMutation, useGetFoldersQuery, useGetFolderQuery, useGetAllFoldersQuery, useGetFolderStructureQuery, useBulkMoveMutation } = foldersApi;
|
|
209
243
|
|
|
210
|
-
export { useBulkMoveMutation, useCreateFolderMutation, useGetAllFoldersQuery, useGetFolderQuery, useGetFolderStructureQuery, useGetFoldersQuery };
|
|
244
|
+
export { useBulkMoveMutation, useCreateFolderMutation, useGetAllFoldersQuery, useGetFolderQuery, useGetFolderStructureQuery, useGetFoldersQuery, useUpdateFolderMutation };
|
|
211
245
|
//# sourceMappingURL=folders.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folders.mjs","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { encodeSearchQuery } from '../utils/searchQueryParam';\n\nimport { uploadApi } from './api';\n\nimport type {\n Folder,\n FolderNode,\n CreateFolders,\n GetFolder,\n GetFolders,\n GetFolderStructure,\n BulkMoveFolders,\n} from '../../../../shared/contracts/folders';\n\nexport type FolderWithCounts = Omit<Folder, 'children' | 'files'> & {\n children?: { count: number };\n files?: { count: number };\n};\n\ninterface GetFoldersParams {\n parentId?: number | null;\n /** Comma-separated rules, e.g. `updatedAt:DESC,name:ASC`. Defaults to alphabetical. */\n sort?: string;\n search?: string;\n /** Extra `filters[$and]` entries (list filters), AND-ed with the parent/search scope. */\n filters?: Record<string, unknown>[];\n}\n\ninterface BulkMoveParams {\n fileIds?: number[];\n folderIds?: number[];\n /** `null` moves the items to the root of the Media Library. */\n destinationFolderId: number | null;\n}\n\ntype DataEnvelope<T> = {\n data: T;\n};\n\nconst isDataEnvelope = <T>(response: T | DataEnvelope<T>): response is DataEnvelope<T> =>\n typeof response === 'object' && response !== null && 'data' in response;\n\nconst unwrapData = <T>(response: T | DataEnvelope<T>): T =>\n isDataEnvelope(response) ? response.data : response;\n\nconst foldersApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getFolders: builder.query<Folder[], GetFoldersParams | void>({\n query: (params = {}) => {\n const { parentId, sort, search, filters = [] } = params as GetFoldersParams;\n\n const queryParams: Record<string, unknown> = {\n // Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).\n sort: sort ?? 'name:ASC',\n };\n\n // List filters (dates) apply in BOTH modes — search composes with them,\n // only the parent scope is dropped while searching.\n if (search) {\n // Search is global: the parent filter is dropped so matching folders\n // anywhere in the library surface. The endpoint is unpaginated — it\n // returns every match — so callers can treat the array length as the\n // true total. Bounding it is a separate decision.\n queryParams['_q'] = encodeSearchQuery(search);\n\n if (filters.length > 0) {\n queryParams['filters'] = { $and: [...filters] };\n }\n } else {\n const parentScope =\n parentId != null ? { parent: { id: parentId } } : { parent: { id: { $null: true } } };\n\n queryParams['filters'] = {\n $and: [parentScope, ...filters],\n };\n }\n\n return {\n url: '/upload/folders',\n method: 'GET',\n config: { params: queryParams },\n };\n },\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder', id: 'LIST' },\n ];\n }\n return [{ type: 'Folder', id: 'LIST' }];\n },\n }),\n createFolder: builder.mutation<CreateFolders.Response['data'], CreateFolders.Request['body']>({\n query: (body) => ({\n url: '/upload/folders',\n method: 'POST',\n data: body,\n }),\n transformResponse: (response: CreateFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n /**\n * Hierarchical folder tree used by the FolderTree sidebar. Returned as a\n * single nested array — the server walks the folders table once and\n * assembles parent/child relationships.\n *\n * TODO: filter results like `admin-folder.find` for folder-scoped roles\n * (v1 returns all folders; relies on global `plugin::upload.read`).\n */\n getFolderStructure: builder.query<FolderNode[], void>({\n query: () => ({\n url: '/upload/folder-structure',\n method: 'GET',\n }),\n // TODO: align GetFolderStructure contract with the real /upload/folder-structure\n // envelope and drop this defensive unwrap (data[] vs { data: [] }).\n transformResponse: (response: GetFolderStructure.Response['data']) =>\n ((response as unknown as { data: FolderNode[] })?.data ??\n (response as unknown as FolderNode[]) ??\n []) as FolderNode[],\n providesTags: [{ type: 'Folder', id: 'STRUCTURE' }],\n }),\n /**\n * Flat list of every folder, used to populate the \"Location\" select in the\n * asset details drawer. No parent filter — we want the entire tree in one\n * query so the select can render any destination folder.\n */\n getAllFolders: builder.query<Folder[], void>({\n query: () => ({\n url: '/upload/folders',\n method: 'GET',\n }),\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response ?? []),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder' as const, id: 'LIST' },\n ];\n }\n return [{ type: 'Folder' as const, id: 'LIST' }];\n },\n }),\n getFolder: builder.query<FolderWithCounts, { id: number }>({\n query: ({ id }) => ({\n url: `/upload/folders/${id}`,\n method: 'GET',\n config: {\n params: {\n populate: {\n parent: {\n populate: {\n parent: '*',\n },\n },\n children: { count: true },\n files: { count: true },\n },\n },\n },\n }),\n transformResponse: (response: GetFolder.Response) =>\n response.data as unknown as FolderWithCounts,\n providesTags: (_result, _error, { id }) => [{ type: 'Folder', id }],\n }),\n bulkMove: builder.mutation<BulkMoveFolders.Response['data'], BulkMoveParams>({\n query: ({ fileIds = [], folderIds = [], destinationFolderId }) => ({\n url: '/upload/actions/bulk-move',\n method: 'POST',\n data: { fileIds, folderIds, destinationFolderId },\n }),\n transformResponse: (response: BulkMoveFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Asset', id: 'LIST' },\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n }),\n});\n\nexport const {\n useCreateFolderMutation,\n useGetFoldersQuery,\n useGetFolderQuery,\n useGetAllFoldersQuery,\n useGetFolderStructureQuery,\n useBulkMoveMutation,\n} = foldersApi;\n"],"names":["isDataEnvelope","response","unwrapData","data","foldersApi","uploadApi","injectEndpoints","endpoints","builder","getFolders","query","params","parentId","sort","search","filters","queryParams","encodeSearchQuery","length","$and","parentScope","parent","id","$null","url","method","config","transformResponse","providesTags","results","map","type","createFolder","mutation","body","invalidatesTags","getFolderStructure","getAllFolders","getFolder","populate","children","count","files","_result","_error","bulkMove","fileIds","folderIds","destinationFolderId","useCreateFolderMutation","useGetFoldersQuery","useGetFolderQuery","useGetAllFoldersQuery","useGetFolderStructureQuery","useBulkMoveMutation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"folders.mjs","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { encodeSearchQuery } from '../utils/searchQueryParam';\n\nimport { uploadApi } from './api';\n\nimport type {\n Folder,\n FolderNode,\n CreateFolders,\n GetFolder,\n GetFolders,\n GetFolderStructure,\n BulkMoveFolders,\n UpdateFolder,\n} from '../../../../shared/contracts/folders';\n\nexport type FolderWithCounts = Omit<Folder, 'children' | 'files'> & {\n children?: { count: number };\n files?: { count: number };\n};\n\ninterface GetFoldersParams {\n parentId?: number | null;\n /** Comma-separated rules, e.g. `updatedAt:DESC,name:ASC`. Defaults to alphabetical. */\n sort?: string;\n search?: string;\n /** Extra `filters[$and]` entries (list filters), AND-ed with the parent/search scope. */\n filters?: Record<string, unknown>[];\n}\n\ninterface BulkMoveParams {\n fileIds?: number[];\n folderIds?: number[];\n /** `null` moves the items to the root of the Media Library. */\n destinationFolderId: number | null;\n}\n\ntype DataEnvelope<T> = {\n data: T;\n};\n\nconst isDataEnvelope = <T>(response: T | DataEnvelope<T>): response is DataEnvelope<T> =>\n typeof response === 'object' && response !== null && 'data' in response;\n\nconst unwrapData = <T>(response: T | DataEnvelope<T>): T =>\n isDataEnvelope(response) ? response.data : response;\n\nconst foldersApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getFolders: builder.query<Folder[], GetFoldersParams | void>({\n query: (params = {}) => {\n const { parentId, sort, search, filters = [] } = params as GetFoldersParams;\n\n const queryParams: Record<string, unknown> = {\n // Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).\n sort: sort ?? 'name:ASC',\n populate: { parent: true },\n };\n\n // List filters (dates) apply in BOTH modes — search composes with them,\n // only the parent scope is dropped while searching.\n if (search) {\n // Search is global: the parent filter is dropped so matching folders\n // anywhere in the library surface. The endpoint is unpaginated — it\n // returns every match — so callers can treat the array length as the\n // true total. Bounding it is a separate decision.\n queryParams['_q'] = encodeSearchQuery(search);\n\n if (filters.length > 0) {\n queryParams['filters'] = { $and: [...filters] };\n }\n } else {\n const parentScope =\n parentId != null ? { parent: { id: parentId } } : { parent: { id: { $null: true } } };\n\n queryParams['filters'] = {\n $and: [parentScope, ...filters],\n };\n }\n\n return {\n url: '/upload/folders',\n method: 'GET',\n config: { params: queryParams },\n };\n },\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder', id: 'LIST' },\n ];\n }\n return [{ type: 'Folder', id: 'LIST' }];\n },\n }),\n createFolder: builder.mutation<CreateFolders.Response['data'], CreateFolders.Request['body']>({\n query: (body) => ({\n url: '/upload/folders',\n method: 'POST',\n data: body,\n }),\n transformResponse: (response: CreateFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n updateFolder: builder.mutation<\n UpdateFolder.Response['data'],\n { id: number } & UpdateFolder.Request['body']\n >({\n query: ({ id, ...body }) => ({\n url: `/upload/folders/${id}`,\n method: 'PUT',\n data: body,\n }),\n transformResponse: (response: UpdateFolder.Response) => response.data,\n invalidatesTags: (_result, _error, { id }) => [\n { type: 'Folder', id },\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n /**\n * Hierarchical folder tree used by the FolderTree sidebar. Returned as a\n * single nested array — the server walks the folders table once and\n * assembles parent/child relationships.\n *\n * TODO: filter results like `admin-folder.find` for folder-scoped roles\n * (v1 returns all folders; relies on global `plugin::upload.read`).\n */\n getFolderStructure: builder.query<FolderNode[], void>({\n query: () => ({\n url: '/upload/folder-structure',\n method: 'GET',\n }),\n // TODO: align GetFolderStructure contract with the real /upload/folder-structure\n // envelope and drop this defensive unwrap (data[] vs { data: [] }).\n transformResponse: (response: GetFolderStructure.Response['data']) =>\n ((response as unknown as { data: FolderNode[] })?.data ??\n (response as unknown as FolderNode[]) ??\n []) as FolderNode[],\n providesTags: [{ type: 'Folder', id: 'STRUCTURE' }],\n }),\n /**\n * Flat list of every folder, used to populate the \"Location\" select in the\n * asset details drawer. No parent filter — we want the entire tree in one\n * query so the select can render any destination folder.\n */\n getAllFolders: builder.query<Folder[], void>({\n query: () => ({\n url: '/upload/folders',\n method: 'GET',\n }),\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response ?? []),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder' as const, id: 'LIST' },\n ];\n }\n return [{ type: 'Folder' as const, id: 'LIST' }];\n },\n }),\n getFolder: builder.query<FolderWithCounts, { id: number }>({\n query: ({ id }) => ({\n url: `/upload/folders/${id}`,\n method: 'GET',\n config: {\n params: {\n populate: {\n parent: {\n populate: {\n parent: '*',\n },\n },\n children: { count: true },\n files: { count: true },\n },\n },\n },\n }),\n transformResponse: (response: GetFolder.Response) =>\n response.data as unknown as FolderWithCounts,\n // Also carries the LIST tag so a mutation that changes a folder's file\n // count (upload / delete into it) can refresh the header count by\n // invalidating `{ Folder, LIST }`, without needing the folder id at the\n // mutation site (it's buried in the upload FormData / not passed to the\n // delete).\n providesTags: (_result, _error, { id }) => [\n { type: 'Folder', id },\n { type: 'Folder', id: 'LIST' },\n ],\n }),\n bulkMove: builder.mutation<BulkMoveFolders.Response['data'], BulkMoveParams>({\n query: ({ fileIds = [], folderIds = [], destinationFolderId }) => ({\n url: '/upload/actions/bulk-move',\n method: 'POST',\n data: { fileIds, folderIds, destinationFolderId },\n }),\n transformResponse: (response: BulkMoveFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Asset', id: 'LIST' },\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n }),\n});\n\nexport const {\n useCreateFolderMutation,\n useUpdateFolderMutation,\n useGetFoldersQuery,\n useGetFolderQuery,\n useGetAllFoldersQuery,\n useGetFolderStructureQuery,\n useBulkMoveMutation,\n} = foldersApi;\n"],"names":["isDataEnvelope","response","unwrapData","data","foldersApi","uploadApi","injectEndpoints","endpoints","builder","getFolders","query","params","parentId","sort","search","filters","queryParams","populate","parent","encodeSearchQuery","length","$and","parentScope","id","$null","url","method","config","transformResponse","providesTags","results","map","type","createFolder","mutation","body","invalidatesTags","updateFolder","_result","_error","getFolderStructure","getAllFolders","getFolder","children","count","files","bulkMove","fileIds","folderIds","destinationFolderId","useCreateFolderMutation","useUpdateFolderMutation","useGetFoldersQuery","useGetFolderQuery","useGetAllFoldersQuery","useGetFolderStructureQuery","useBulkMoveMutation"],"mappings":"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"}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var itemLocations = require('./itemLocations.js');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Reconstruct the drag set from a bulk-actions selection so the dialog can feed
|
|
5
7
|
* the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical
|
|
6
8
|
* `canDropItemOnFolder` predicate that pointer drag uses.
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
+
* Each item's location comes from the loaded row, not from the folder currently
|
|
11
|
+
* open: search results are global, so a selected item can live anywhere.
|
|
10
12
|
* Names are irrelevant to drop validation, so they are left empty.
|
|
11
|
-
*/ const buildDragSetFromSelection = (selectedIds, selectedFolderIds,
|
|
13
|
+
*/ const buildDragSetFromSelection = (selectedIds, selectedFolderIds, locations, fallbackFolderId)=>{
|
|
12
14
|
const items = [];
|
|
13
15
|
selectedIds.forEach((id)=>{
|
|
14
16
|
items.push({
|
|
15
17
|
kind: 'file',
|
|
16
18
|
id,
|
|
17
19
|
name: '',
|
|
18
|
-
folderId:
|
|
20
|
+
folderId: itemLocations.locateItem(locations, 'file', id, fallbackFolderId)
|
|
19
21
|
});
|
|
20
22
|
});
|
|
21
23
|
selectedFolderIds.forEach((id)=>{
|
|
@@ -23,7 +25,7 @@
|
|
|
23
25
|
kind: 'folder',
|
|
24
26
|
id,
|
|
25
27
|
name: '',
|
|
26
|
-
parentId:
|
|
28
|
+
parentId: itemLocations.locateItem(locations, 'folder', id, fallbackFolderId)
|
|
27
29
|
});
|
|
28
30
|
});
|
|
29
31
|
return items;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildDragSetFromSelection.js","sources":["../../../../admin/src/future/utils/buildDragSetFromSelection.ts"],"sourcesContent":["import type { DragItemData } from '../types/dnd';\n\n/**\n * Reconstruct the drag set from a bulk-actions selection so the dialog can feed\n * the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical\n * `canDropItemOnFolder` predicate that pointer drag uses.\n *\n *
|
|
1
|
+
{"version":3,"file":"buildDragSetFromSelection.js","sources":["../../../../admin/src/future/utils/buildDragSetFromSelection.ts"],"sourcesContent":["import { locateItem, type ItemLocations } from './itemLocations';\n\nimport type { DragItemData } from '../types/dnd';\n\n/**\n * Reconstruct the drag set from a bulk-actions selection so the dialog can feed\n * the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical\n * `canDropItemOnFolder` predicate that pointer drag uses.\n *\n * Each item's location comes from the loaded row, not from the folder currently\n * open: search results are global, so a selected item can live anywhere.\n * Names are irrelevant to drop validation, so they are left empty.\n */\nexport const buildDragSetFromSelection = (\n selectedIds: ReadonlySet<number>,\n selectedFolderIds: ReadonlySet<number>,\n locations: ItemLocations,\n fallbackFolderId: number | null\n): DragItemData[] => {\n const items: DragItemData[] = [];\n\n selectedIds.forEach((id) => {\n items.push({\n kind: 'file',\n id,\n name: '',\n folderId: locateItem(locations, 'file', id, fallbackFolderId),\n });\n });\n\n selectedFolderIds.forEach((id) => {\n items.push({\n kind: 'folder',\n id,\n name: '',\n parentId: locateItem(locations, 'folder', id, fallbackFolderId),\n });\n });\n\n return items;\n};\n"],"names":["buildDragSetFromSelection","selectedIds","selectedFolderIds","locations","fallbackFolderId","items","forEach","id","push","kind","name","folderId","locateItem","parentId"],"mappings":";;;;AAIA;;;;;;;;AAQC,IACM,MAAMA,yBAAAA,GAA4B,CACvCC,WAAAA,EACAC,mBACAC,SAAAA,EACAC,gBAAAA,GAAAA;AAEA,IAAA,MAAMC,QAAwB,EAAE;IAEhCJ,WAAAA,CAAYK,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACnBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YACTC,IAAAA,EAAM,MAAA;AACNF,YAAAA,EAAAA;YACAG,IAAAA,EAAM,EAAA;YACNC,QAAAA,EAAUC,wBAAAA,CAAWT,SAAAA,EAAW,MAAA,EAAQI,EAAAA,EAAIH,gBAAAA;AAC9C,SAAA,CAAA;AACF,IAAA,CAAA,CAAA;IAEAF,iBAAAA,CAAkBI,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACzBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YACTC,IAAAA,EAAM,QAAA;AACNF,YAAAA,EAAAA;YACAG,IAAAA,EAAM,EAAA;YACNG,QAAAA,EAAUD,wBAAAA,CAAWT,SAAAA,EAAW,QAAA,EAAUI,EAAAA,EAAIH,gBAAAA;AAChD,SAAA,CAAA;AACF,IAAA,CAAA,CAAA;IAEA,OAAOC,KAAAA;AACT;;;;"}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
+
import { locateItem } from './itemLocations.mjs';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Reconstruct the drag set from a bulk-actions selection so the dialog can feed
|
|
3
5
|
* the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical
|
|
4
6
|
* `canDropItemOnFolder` predicate that pointer drag uses.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
+
* Each item's location comes from the loaded row, not from the folder currently
|
|
9
|
+
* open: search results are global, so a selected item can live anywhere.
|
|
8
10
|
* Names are irrelevant to drop validation, so they are left empty.
|
|
9
|
-
*/ const buildDragSetFromSelection = (selectedIds, selectedFolderIds,
|
|
11
|
+
*/ const buildDragSetFromSelection = (selectedIds, selectedFolderIds, locations, fallbackFolderId)=>{
|
|
10
12
|
const items = [];
|
|
11
13
|
selectedIds.forEach((id)=>{
|
|
12
14
|
items.push({
|
|
13
15
|
kind: 'file',
|
|
14
16
|
id,
|
|
15
17
|
name: '',
|
|
16
|
-
folderId:
|
|
18
|
+
folderId: locateItem(locations, 'file', id, fallbackFolderId)
|
|
17
19
|
});
|
|
18
20
|
});
|
|
19
21
|
selectedFolderIds.forEach((id)=>{
|
|
@@ -21,7 +23,7 @@
|
|
|
21
23
|
kind: 'folder',
|
|
22
24
|
id,
|
|
23
25
|
name: '',
|
|
24
|
-
parentId:
|
|
26
|
+
parentId: locateItem(locations, 'folder', id, fallbackFolderId)
|
|
25
27
|
});
|
|
26
28
|
});
|
|
27
29
|
return items;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildDragSetFromSelection.mjs","sources":["../../../../admin/src/future/utils/buildDragSetFromSelection.ts"],"sourcesContent":["import type { DragItemData } from '../types/dnd';\n\n/**\n * Reconstruct the drag set from a bulk-actions selection so the dialog can feed\n * the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical\n * `canDropItemOnFolder` predicate that pointer drag uses.\n *\n *
|
|
1
|
+
{"version":3,"file":"buildDragSetFromSelection.mjs","sources":["../../../../admin/src/future/utils/buildDragSetFromSelection.ts"],"sourcesContent":["import { locateItem, type ItemLocations } from './itemLocations';\n\nimport type { DragItemData } from '../types/dnd';\n\n/**\n * Reconstruct the drag set from a bulk-actions selection so the dialog can feed\n * the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical\n * `canDropItemOnFolder` predicate that pointer drag uses.\n *\n * Each item's location comes from the loaded row, not from the folder currently\n * open: search results are global, so a selected item can live anywhere.\n * Names are irrelevant to drop validation, so they are left empty.\n */\nexport const buildDragSetFromSelection = (\n selectedIds: ReadonlySet<number>,\n selectedFolderIds: ReadonlySet<number>,\n locations: ItemLocations,\n fallbackFolderId: number | null\n): DragItemData[] => {\n const items: DragItemData[] = [];\n\n selectedIds.forEach((id) => {\n items.push({\n kind: 'file',\n id,\n name: '',\n folderId: locateItem(locations, 'file', id, fallbackFolderId),\n });\n });\n\n selectedFolderIds.forEach((id) => {\n items.push({\n kind: 'folder',\n id,\n name: '',\n parentId: locateItem(locations, 'folder', id, fallbackFolderId),\n });\n });\n\n return items;\n};\n"],"names":["buildDragSetFromSelection","selectedIds","selectedFolderIds","locations","fallbackFolderId","items","forEach","id","push","kind","name","folderId","locateItem","parentId"],"mappings":";;AAIA;;;;;;;;AAQC,IACM,MAAMA,yBAAAA,GAA4B,CACvCC,WAAAA,EACAC,mBACAC,SAAAA,EACAC,gBAAAA,GAAAA;AAEA,IAAA,MAAMC,QAAwB,EAAE;IAEhCJ,WAAAA,CAAYK,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACnBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YACTC,IAAAA,EAAM,MAAA;AACNF,YAAAA,EAAAA;YACAG,IAAAA,EAAM,EAAA;YACNC,QAAAA,EAAUC,UAAAA,CAAWT,SAAAA,EAAW,MAAA,EAAQI,EAAAA,EAAIH,gBAAAA;AAC9C,SAAA,CAAA;AACF,IAAA,CAAA,CAAA;IAEAF,iBAAAA,CAAkBI,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACzBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YACTC,IAAAA,EAAM,QAAA;AACNF,YAAAA,EAAAA;YACAG,IAAAA,EAAM,EAAA;YACNG,QAAAA,EAAUD,UAAAA,CAAWT,SAAAA,EAAW,QAAA,EAAUI,EAAAA,EAAIH,gBAAAA;AAChD,SAAA,CAAA;AACF,IAAA,CAAA,CAAA;IAEA,OAAOC,KAAAA;AACT;;;;"}
|
|
@@ -6,7 +6,17 @@ var translations = require('./translations.js');
|
|
|
6
6
|
* Single source of truth for the bulk-move success wording. Both the DnD drop
|
|
7
7
|
* and the BulkMoveDialog format the toast (and DnD live-region announcement)
|
|
8
8
|
* through here so the two paths stay identical for the same move.
|
|
9
|
-
*/ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>
|
|
9
|
+
*/ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>{
|
|
10
|
+
if (source === null) {
|
|
11
|
+
return formatMessage({
|
|
12
|
+
id: translations.getTranslationKey('list.bulk-actions.move.success-multiple-sources'),
|
|
13
|
+
defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved to {destination}'
|
|
14
|
+
}, {
|
|
15
|
+
count,
|
|
16
|
+
destination
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return formatMessage({
|
|
10
20
|
id: translations.getTranslationKey('list.bulk-actions.move.success'),
|
|
11
21
|
defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}'
|
|
12
22
|
}, {
|
|
@@ -14,6 +24,7 @@ var translations = require('./translations.js');
|
|
|
14
24
|
source,
|
|
15
25
|
destination
|
|
16
26
|
});
|
|
27
|
+
};
|
|
17
28
|
|
|
18
29
|
exports.formatMoveSuccessMessage = formatMoveSuccessMessage;
|
|
19
30
|
//# sourceMappingURL=formatMoveSuccessMessage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatMoveSuccessMessage.js","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n source: string;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string
|
|
1
|
+
{"version":3,"file":"formatMoveSuccessMessage.js","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n /**\n * Leaf name of the folder the items came from, or `null` when the move spans\n * several source folders and no single one can be named — a global-search\n * selection can hold items from anywhere. The wording then omits the source\n * rather than naming one folder and implying the rest came from it too.\n */\n source: string | null;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string => {\n if (source === null) {\n return formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success-multiple-sources'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved to {destination}',\n },\n { count, destination }\n );\n }\n\n return formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source, destination }\n );\n};\n"],"names":["formatMoveSuccessMessage","formatMessage","count","source","destination","id","getTranslationKey","defaultMessage"],"mappings":";;;;AAiBA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,WAAW,EACW,GAAA;AACtB,IAAA,IAAID,WAAW,IAAA,EAAM;AACnB,QAAA,OAAOF,aAAAA,CACL;AACEI,YAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,iDAAA,CAAA;YACtBC,cAAAA,EACE;SACJ,EACA;AAAEL,YAAAA,KAAAA;AAAOE,YAAAA;AAAY,SAAA,CAAA;AAEzB,IAAA;AAEA,IAAA,OAAOH,aAAAA,CACL;AACEI,QAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,gCAAA,CAAA;QACtBC,cAAAA,EACE;KACJ,EACA;AAAEL,QAAAA,KAAAA;AAAOC,QAAAA,MAAAA;AAAQC,QAAAA;AAAY,KAAA,CAAA;AAEjC;;;;"}
|
|
@@ -4,7 +4,17 @@ import { getTranslationKey } from './translations.mjs';
|
|
|
4
4
|
* Single source of truth for the bulk-move success wording. Both the DnD drop
|
|
5
5
|
* and the BulkMoveDialog format the toast (and DnD live-region announcement)
|
|
6
6
|
* through here so the two paths stay identical for the same move.
|
|
7
|
-
*/ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>
|
|
7
|
+
*/ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>{
|
|
8
|
+
if (source === null) {
|
|
9
|
+
return formatMessage({
|
|
10
|
+
id: getTranslationKey('list.bulk-actions.move.success-multiple-sources'),
|
|
11
|
+
defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved to {destination}'
|
|
12
|
+
}, {
|
|
13
|
+
count,
|
|
14
|
+
destination
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return formatMessage({
|
|
8
18
|
id: getTranslationKey('list.bulk-actions.move.success'),
|
|
9
19
|
defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}'
|
|
10
20
|
}, {
|
|
@@ -12,6 +22,7 @@ import { getTranslationKey } from './translations.mjs';
|
|
|
12
22
|
source,
|
|
13
23
|
destination
|
|
14
24
|
});
|
|
25
|
+
};
|
|
15
26
|
|
|
16
27
|
export { formatMoveSuccessMessage };
|
|
17
28
|
//# sourceMappingURL=formatMoveSuccessMessage.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatMoveSuccessMessage.mjs","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n source: string;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string
|
|
1
|
+
{"version":3,"file":"formatMoveSuccessMessage.mjs","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n /**\n * Leaf name of the folder the items came from, or `null` when the move spans\n * several source folders and no single one can be named — a global-search\n * selection can hold items from anywhere. The wording then omits the source\n * rather than naming one folder and implying the rest came from it too.\n */\n source: string | null;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string => {\n if (source === null) {\n return formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success-multiple-sources'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved to {destination}',\n },\n { count, destination }\n );\n }\n\n return formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source, destination }\n );\n};\n"],"names":["formatMoveSuccessMessage","formatMessage","count","source","destination","id","getTranslationKey","defaultMessage"],"mappings":";;AAiBA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,WAAW,EACW,GAAA;AACtB,IAAA,IAAID,WAAW,IAAA,EAAM;AACnB,QAAA,OAAOF,aAAAA,CACL;AACEI,YAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,iDAAA,CAAA;YACtBC,cAAAA,EACE;SACJ,EACA;AAAEL,YAAAA,KAAAA;AAAOE,YAAAA;AAAY,SAAA,CAAA;AAEzB,IAAA;AAEA,IAAA,OAAOH,aAAAA,CACL;AACEI,QAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,gCAAA,CAAA;QACtBC,cAAAA,EACE;KACJ,EACA;AAAEL,QAAAA,KAAAA;AAAOC,QAAAA,MAAAA;AAAQC,QAAAA;AAAY,KAAA,CAAA;AAEjC;;;;"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalise a to-one relation down to its id.
|
|
5
|
+
*
|
|
6
|
+
* The contracts type `File['folder']` and `Folder['parent']` as ids, but the
|
|
7
|
+
* admin populates both (`populate: { folder: true }` on `/upload/files`,
|
|
8
|
+
* `populate: { parent: true }` on `/upload/folders`), so the wire value is an
|
|
9
|
+
* object. Only the id is ever read — move validation compares locations by id.
|
|
10
|
+
*/ const getRelationId = (relation)=>{
|
|
11
|
+
if (relation == null) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (typeof relation === 'object') {
|
|
15
|
+
return relation.id ?? null;
|
|
16
|
+
}
|
|
17
|
+
if (typeof relation === 'number') {
|
|
18
|
+
return relation;
|
|
19
|
+
}
|
|
20
|
+
return Number(relation) || null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
exports.getRelationId = getRelationId;
|
|
24
|
+
//# sourceMappingURL=getRelationId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRelationId.js","sources":["../../../../admin/src/future/utils/getRelationId.ts"],"sourcesContent":["/**\n * Normalise a to-one relation down to its id.\n *\n * The contracts type `File['folder']` and `Folder['parent']` as ids, but the\n * admin populates both (`populate: { folder: true }` on `/upload/files`,\n * `populate: { parent: true }` on `/upload/folders`), so the wire value is an\n * object. Only the id is ever read — move validation compares locations by id.\n */\nexport const getRelationId = (\n relation: number | string | { id?: number } | null | undefined\n): number | null => {\n if (relation == null) {\n return null;\n }\n\n if (typeof relation === 'object') {\n return relation.id ?? null;\n }\n\n if (typeof relation === 'number') {\n return relation;\n }\n\n return Number(relation) || null;\n};\n"],"names":["getRelationId","relation","id","Number"],"mappings":";;AAAA;;;;;;;IAQO,MAAMA,aAAAA,GAAgB,CAC3BC,QAAAA,GAAAA;AAEA,IAAA,IAAIA,YAAY,IAAA,EAAM;QACpB,OAAO,IAAA;AACT,IAAA;IAEA,IAAI,OAAOA,aAAa,QAAA,EAAU;QAChC,OAAOA,QAAAA,CAASC,EAAE,IAAI,IAAA;AACxB,IAAA;IAEA,IAAI,OAAOD,aAAa,QAAA,EAAU;QAChC,OAAOA,QAAAA;AACT,IAAA;AAEA,IAAA,OAAOE,OAAOF,QAAAA,CAAAA,IAAa,IAAA;AAC7B;;;;"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalise a to-one relation down to its id.
|
|
3
|
+
*
|
|
4
|
+
* The contracts type `File['folder']` and `Folder['parent']` as ids, but the
|
|
5
|
+
* admin populates both (`populate: { folder: true }` on `/upload/files`,
|
|
6
|
+
* `populate: { parent: true }` on `/upload/folders`), so the wire value is an
|
|
7
|
+
* object. Only the id is ever read — move validation compares locations by id.
|
|
8
|
+
*/ const getRelationId = (relation)=>{
|
|
9
|
+
if (relation == null) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
if (typeof relation === 'object') {
|
|
13
|
+
return relation.id ?? null;
|
|
14
|
+
}
|
|
15
|
+
if (typeof relation === 'number') {
|
|
16
|
+
return relation;
|
|
17
|
+
}
|
|
18
|
+
return Number(relation) || null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export { getRelationId };
|
|
22
|
+
//# sourceMappingURL=getRelationId.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRelationId.mjs","sources":["../../../../admin/src/future/utils/getRelationId.ts"],"sourcesContent":["/**\n * Normalise a to-one relation down to its id.\n *\n * The contracts type `File['folder']` and `Folder['parent']` as ids, but the\n * admin populates both (`populate: { folder: true }` on `/upload/files`,\n * `populate: { parent: true }` on `/upload/folders`), so the wire value is an\n * object. Only the id is ever read — move validation compares locations by id.\n */\nexport const getRelationId = (\n relation: number | string | { id?: number } | null | undefined\n): number | null => {\n if (relation == null) {\n return null;\n }\n\n if (typeof relation === 'object') {\n return relation.id ?? null;\n }\n\n if (typeof relation === 'number') {\n return relation;\n }\n\n return Number(relation) || null;\n};\n"],"names":["getRelationId","relation","id","Number"],"mappings":"AAAA;;;;;;;IAQO,MAAMA,aAAAA,GAAgB,CAC3BC,QAAAA,GAAAA;AAEA,IAAA,IAAIA,YAAY,IAAA,EAAM;QACpB,OAAO,IAAA;AACT,IAAA;IAEA,IAAI,OAAOA,aAAa,QAAA,EAAU;QAChC,OAAOA,QAAAA,CAASC,EAAE,IAAI,IAAA;AACxB,IAAA;IAEA,IAAI,OAAOD,aAAa,QAAA,EAAU;QAChC,OAAOA,QAAAA;AACT,IAAA;AAEA,IAAA,OAAOE,OAAOF,QAAAA,CAAAA,IAAa,IAAA;AAC7B;;;;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* True when the event really originated inside `currentTarget`'s DOM subtree.
|
|
5
|
+
*
|
|
6
|
+
* Portaled content (Radix menus, modals, select popovers) bubbles through the
|
|
7
|
+
* React tree, so a row's handlers see events coming from dialogs rendered by
|
|
8
|
+
* its own children. Guarding on DOM containment keeps those out — without
|
|
9
|
+
* calling `stopPropagation`, which would also kill the native event before it
|
|
10
|
+
* reaches `document`, where Radix listens in order to dismiss its layers.
|
|
11
|
+
*/ const isEventFromWithin = (event)=>event.currentTarget instanceof Node && event.target instanceof Node && event.currentTarget.contains(event.target);
|
|
12
|
+
|
|
13
|
+
exports.isEventFromWithin = isEventFromWithin;
|
|
14
|
+
//# sourceMappingURL=isEventFromWithin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isEventFromWithin.js","sources":["../../../../admin/src/future/utils/isEventFromWithin.ts"],"sourcesContent":["import type { SyntheticEvent } from 'react';\n\n/**\n * True when the event really originated inside `currentTarget`'s DOM subtree.\n *\n * Portaled content (Radix menus, modals, select popovers) bubbles through the\n * React tree, so a row's handlers see events coming from dialogs rendered by\n * its own children. Guarding on DOM containment keeps those out — without\n * calling `stopPropagation`, which would also kill the native event before it\n * reaches `document`, where Radix listens in order to dismiss its layers.\n */\nexport const isEventFromWithin = (event: SyntheticEvent) =>\n event.currentTarget instanceof Node &&\n event.target instanceof Node &&\n event.currentTarget.contains(event.target);\n"],"names":["isEventFromWithin","event","currentTarget","Node","target","contains"],"mappings":";;AAEA;;;;;;;;UASaA,iBAAAA,GAAoB,CAACC,QAChCA,KAAAA,CAAMC,aAAa,YAAYC,IAAAA,IAC/BF,KAAAA,CAAMG,MAAM,YAAYD,IAAAA,IACxBF,MAAMC,aAAa,CAACG,QAAQ,CAACJ,KAAAA,CAAMG,MAAM;;;;"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when the event really originated inside `currentTarget`'s DOM subtree.
|
|
3
|
+
*
|
|
4
|
+
* Portaled content (Radix menus, modals, select popovers) bubbles through the
|
|
5
|
+
* React tree, so a row's handlers see events coming from dialogs rendered by
|
|
6
|
+
* its own children. Guarding on DOM containment keeps those out — without
|
|
7
|
+
* calling `stopPropagation`, which would also kill the native event before it
|
|
8
|
+
* reaches `document`, where Radix listens in order to dismiss its layers.
|
|
9
|
+
*/ const isEventFromWithin = (event)=>event.currentTarget instanceof Node && event.target instanceof Node && event.currentTarget.contains(event.target);
|
|
10
|
+
|
|
11
|
+
export { isEventFromWithin };
|
|
12
|
+
//# sourceMappingURL=isEventFromWithin.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isEventFromWithin.mjs","sources":["../../../../admin/src/future/utils/isEventFromWithin.ts"],"sourcesContent":["import type { SyntheticEvent } from 'react';\n\n/**\n * True when the event really originated inside `currentTarget`'s DOM subtree.\n *\n * Portaled content (Radix menus, modals, select popovers) bubbles through the\n * React tree, so a row's handlers see events coming from dialogs rendered by\n * its own children. Guarding on DOM containment keeps those out — without\n * calling `stopPropagation`, which would also kill the native event before it\n * reaches `document`, where Radix listens in order to dismiss its layers.\n */\nexport const isEventFromWithin = (event: SyntheticEvent) =>\n event.currentTarget instanceof Node &&\n event.target instanceof Node &&\n event.currentTarget.contains(event.target);\n"],"names":["isEventFromWithin","event","currentTarget","Node","target","contains"],"mappings":"AAEA;;;;;;;;UASaA,iBAAAA,GAAoB,CAACC,QAChCA,KAAAA,CAAMC,aAAa,YAAYC,IAAAA,IAC/BF,KAAAA,CAAMG,MAAM,YAAYD,IAAAA,IACxBF,MAAMC,aAAa,CAACG,QAAQ,CAACJ,KAAAA,CAAMG,MAAM;;;;"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var getRelationId = require('./getRelationId.js');
|
|
4
|
+
|
|
5
|
+
/** Every id misses — used where no list is loaded (e.g. an isolated provider). */ const emptyItemLocations = {
|
|
6
|
+
fileFolderId: ()=>undefined,
|
|
7
|
+
folderParentId: ()=>undefined
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Build the lookup once per list. Callers memoize on the rows so the identity
|
|
11
|
+
* stays stable: `BulkMoveDialog` memoizes its destination walk on the items
|
|
12
|
+
* derived from it.
|
|
13
|
+
*/ const buildItemLocations = (assets, folders)=>{
|
|
14
|
+
const fileFolderIds = new Map();
|
|
15
|
+
const folderParentIds = new Map();
|
|
16
|
+
assets.forEach((asset)=>{
|
|
17
|
+
fileFolderIds.set(asset.id, getRelationId.getRelationId(asset.folder));
|
|
18
|
+
});
|
|
19
|
+
folders.forEach((folder)=>{
|
|
20
|
+
folderParentIds.set(folder.id, getRelationId.getRelationId(folder.parent));
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
fileFolderId: (id)=>fileFolderIds.get(id),
|
|
24
|
+
folderParentId: (id)=>folderParentIds.get(id)
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Resolve an item's location, falling back to the folder currently open when it
|
|
29
|
+
* is not in the loaded list. The fallback is unreachable in practice — selection
|
|
30
|
+
* clears whenever the list identity changes and infinite scroll only appends —
|
|
31
|
+
* but a stale parent beats silently dropping the item from the move.
|
|
32
|
+
*/ const locateItem = (locations, kind, id, fallbackFolderId)=>{
|
|
33
|
+
const located = kind === 'file' ? locations.fileFolderId(id) : locations.folderParentId(id);
|
|
34
|
+
return located === undefined ? fallbackFolderId : located;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
exports.buildItemLocations = buildItemLocations;
|
|
38
|
+
exports.emptyItemLocations = emptyItemLocations;
|
|
39
|
+
exports.locateItem = locateItem;
|
|
40
|
+
//# sourceMappingURL=itemLocations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"itemLocations.js","sources":["../../../../admin/src/future/utils/itemLocations.ts"],"sourcesContent":["import { getRelationId } from './getRelationId';\n\nimport type { File } from '../../../../shared/contracts/files';\nimport type { Folder } from '../../../../shared/contracts/folders';\n\n/**\n * Where each loaded item actually lives, keyed by id.\n *\n * Search results are global, so the folder currently open (`?folder=`) says\n * nothing about a selected item's location. The loaded lists do: the server\n * populates `folder` on `/upload/files` and `parent` on `/upload/folders`.\n *\n * `undefined` means \"not in the loaded list\" and is distinct from `null`\n * (\"lives at the root\") — the two lead to different move validation.\n */\nexport interface ItemLocations {\n fileFolderId: (id: number) => number | null | undefined;\n folderParentId: (id: number) => number | null | undefined;\n}\n\n/** Every id misses — used where no list is loaded (e.g. an isolated provider). */\nexport const emptyItemLocations: ItemLocations = {\n fileFolderId: () => undefined,\n folderParentId: () => undefined,\n};\n\n/**\n * Build the lookup once per list. Callers memoize on the rows so the identity\n * stays stable: `BulkMoveDialog` memoizes its destination walk on the items\n * derived from it.\n */\nexport const buildItemLocations = (assets: File[], folders: Folder[]): ItemLocations => {\n const fileFolderIds = new Map<number, number | null>();\n const folderParentIds = new Map<number, number | null>();\n\n assets.forEach((asset) => {\n fileFolderIds.set(asset.id, getRelationId(asset.folder));\n });\n\n folders.forEach((folder) => {\n folderParentIds.set(folder.id, getRelationId(folder.parent));\n });\n\n return {\n fileFolderId: (id) => fileFolderIds.get(id),\n folderParentId: (id) => folderParentIds.get(id),\n };\n};\n\n/**\n * Resolve an item's location, falling back to the folder currently open when it\n * is not in the loaded list. The fallback is unreachable in practice — selection\n * clears whenever the list identity changes and infinite scroll only appends —\n * but a stale parent beats silently dropping the item from the move.\n */\nexport const locateItem = (\n locations: ItemLocations,\n kind: 'file' | 'folder',\n id: number,\n fallbackFolderId: number | null\n): number | null => {\n const located = kind === 'file' ? locations.fileFolderId(id) : locations.folderParentId(id);\n\n return located === undefined ? fallbackFolderId : located;\n};\n"],"names":["emptyItemLocations","fileFolderId","undefined","folderParentId","buildItemLocations","assets","folders","fileFolderIds","Map","folderParentIds","forEach","asset","set","id","getRelationId","folder","parent","get","locateItem","locations","kind","fallbackFolderId","located"],"mappings":";;;;AAoBA,mFACO,MAAMA,kBAAAA,GAAoC;AAC/CC,IAAAA,YAAAA,EAAc,IAAMC,SAAAA;AACpBC,IAAAA,cAAAA,EAAgB,IAAMD;AACxB;AAEA;;;;AAIC,IACM,MAAME,kBAAAA,GAAqB,CAACC,MAAAA,EAAgBC,OAAAA,GAAAA;AACjD,IAAA,MAAMC,gBAAgB,IAAIC,GAAAA,EAAAA;AAC1B,IAAA,MAAMC,kBAAkB,IAAID,GAAAA,EAAAA;IAE5BH,MAAAA,CAAOK,OAAO,CAAC,CAACC,KAAAA,GAAAA;AACdJ,QAAAA,aAAAA,CAAcK,GAAG,CAACD,KAAAA,CAAME,EAAE,EAAEC,2BAAAA,CAAcH,MAAMI,MAAM,CAAA,CAAA;AACxD,IAAA,CAAA,CAAA;IAEAT,OAAAA,CAAQI,OAAO,CAAC,CAACK,MAAAA,GAAAA;AACfN,QAAAA,eAAAA,CAAgBG,GAAG,CAACG,MAAAA,CAAOF,EAAE,EAAEC,2BAAAA,CAAcC,OAAOC,MAAM,CAAA,CAAA;AAC5D,IAAA,CAAA,CAAA;IAEA,OAAO;AACLf,QAAAA,YAAAA,EAAc,CAACY,EAAAA,GAAON,aAAAA,CAAcU,GAAG,CAACJ,EAAAA,CAAAA;AACxCV,QAAAA,cAAAA,EAAgB,CAACU,EAAAA,GAAOJ,eAAAA,CAAgBQ,GAAG,CAACJ,EAAAA;AAC9C,KAAA;AACF;AAEA;;;;;AAKC,IACM,MAAMK,UAAAA,GAAa,CACxBC,SAAAA,EACAC,MACAP,EAAAA,EACAQ,gBAAAA,GAAAA;IAEA,MAAMC,OAAAA,GAAUF,SAAS,MAAA,GAASD,SAAAA,CAAUlB,YAAY,CAACY,EAAAA,CAAAA,GAAMM,SAAAA,CAAUhB,cAAc,CAACU,EAAAA,CAAAA;IAExF,OAAOS,OAAAA,KAAYpB,YAAYmB,gBAAAA,GAAmBC,OAAAA;AACpD;;;;;;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getRelationId } from './getRelationId.mjs';
|
|
2
|
+
|
|
3
|
+
/** Every id misses — used where no list is loaded (e.g. an isolated provider). */ const emptyItemLocations = {
|
|
4
|
+
fileFolderId: ()=>undefined,
|
|
5
|
+
folderParentId: ()=>undefined
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Build the lookup once per list. Callers memoize on the rows so the identity
|
|
9
|
+
* stays stable: `BulkMoveDialog` memoizes its destination walk on the items
|
|
10
|
+
* derived from it.
|
|
11
|
+
*/ const buildItemLocations = (assets, folders)=>{
|
|
12
|
+
const fileFolderIds = new Map();
|
|
13
|
+
const folderParentIds = new Map();
|
|
14
|
+
assets.forEach((asset)=>{
|
|
15
|
+
fileFolderIds.set(asset.id, getRelationId(asset.folder));
|
|
16
|
+
});
|
|
17
|
+
folders.forEach((folder)=>{
|
|
18
|
+
folderParentIds.set(folder.id, getRelationId(folder.parent));
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
fileFolderId: (id)=>fileFolderIds.get(id),
|
|
22
|
+
folderParentId: (id)=>folderParentIds.get(id)
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Resolve an item's location, falling back to the folder currently open when it
|
|
27
|
+
* is not in the loaded list. The fallback is unreachable in practice — selection
|
|
28
|
+
* clears whenever the list identity changes and infinite scroll only appends —
|
|
29
|
+
* but a stale parent beats silently dropping the item from the move.
|
|
30
|
+
*/ const locateItem = (locations, kind, id, fallbackFolderId)=>{
|
|
31
|
+
const located = kind === 'file' ? locations.fileFolderId(id) : locations.folderParentId(id);
|
|
32
|
+
return located === undefined ? fallbackFolderId : located;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export { buildItemLocations, emptyItemLocations, locateItem };
|
|
36
|
+
//# sourceMappingURL=itemLocations.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"itemLocations.mjs","sources":["../../../../admin/src/future/utils/itemLocations.ts"],"sourcesContent":["import { getRelationId } from './getRelationId';\n\nimport type { File } from '../../../../shared/contracts/files';\nimport type { Folder } from '../../../../shared/contracts/folders';\n\n/**\n * Where each loaded item actually lives, keyed by id.\n *\n * Search results are global, so the folder currently open (`?folder=`) says\n * nothing about a selected item's location. The loaded lists do: the server\n * populates `folder` on `/upload/files` and `parent` on `/upload/folders`.\n *\n * `undefined` means \"not in the loaded list\" and is distinct from `null`\n * (\"lives at the root\") — the two lead to different move validation.\n */\nexport interface ItemLocations {\n fileFolderId: (id: number) => number | null | undefined;\n folderParentId: (id: number) => number | null | undefined;\n}\n\n/** Every id misses — used where no list is loaded (e.g. an isolated provider). */\nexport const emptyItemLocations: ItemLocations = {\n fileFolderId: () => undefined,\n folderParentId: () => undefined,\n};\n\n/**\n * Build the lookup once per list. Callers memoize on the rows so the identity\n * stays stable: `BulkMoveDialog` memoizes its destination walk on the items\n * derived from it.\n */\nexport const buildItemLocations = (assets: File[], folders: Folder[]): ItemLocations => {\n const fileFolderIds = new Map<number, number | null>();\n const folderParentIds = new Map<number, number | null>();\n\n assets.forEach((asset) => {\n fileFolderIds.set(asset.id, getRelationId(asset.folder));\n });\n\n folders.forEach((folder) => {\n folderParentIds.set(folder.id, getRelationId(folder.parent));\n });\n\n return {\n fileFolderId: (id) => fileFolderIds.get(id),\n folderParentId: (id) => folderParentIds.get(id),\n };\n};\n\n/**\n * Resolve an item's location, falling back to the folder currently open when it\n * is not in the loaded list. The fallback is unreachable in practice — selection\n * clears whenever the list identity changes and infinite scroll only appends —\n * but a stale parent beats silently dropping the item from the move.\n */\nexport const locateItem = (\n locations: ItemLocations,\n kind: 'file' | 'folder',\n id: number,\n fallbackFolderId: number | null\n): number | null => {\n const located = kind === 'file' ? locations.fileFolderId(id) : locations.folderParentId(id);\n\n return located === undefined ? fallbackFolderId : located;\n};\n"],"names":["emptyItemLocations","fileFolderId","undefined","folderParentId","buildItemLocations","assets","folders","fileFolderIds","Map","folderParentIds","forEach","asset","set","id","getRelationId","folder","parent","get","locateItem","locations","kind","fallbackFolderId","located"],"mappings":";;AAoBA,mFACO,MAAMA,kBAAAA,GAAoC;AAC/CC,IAAAA,YAAAA,EAAc,IAAMC,SAAAA;AACpBC,IAAAA,cAAAA,EAAgB,IAAMD;AACxB;AAEA;;;;AAIC,IACM,MAAME,kBAAAA,GAAqB,CAACC,MAAAA,EAAgBC,OAAAA,GAAAA;AACjD,IAAA,MAAMC,gBAAgB,IAAIC,GAAAA,EAAAA;AAC1B,IAAA,MAAMC,kBAAkB,IAAID,GAAAA,EAAAA;IAE5BH,MAAAA,CAAOK,OAAO,CAAC,CAACC,KAAAA,GAAAA;AACdJ,QAAAA,aAAAA,CAAcK,GAAG,CAACD,KAAAA,CAAME,EAAE,EAAEC,aAAAA,CAAcH,MAAMI,MAAM,CAAA,CAAA;AACxD,IAAA,CAAA,CAAA;IAEAT,OAAAA,CAAQI,OAAO,CAAC,CAACK,MAAAA,GAAAA;AACfN,QAAAA,eAAAA,CAAgBG,GAAG,CAACG,MAAAA,CAAOF,EAAE,EAAEC,aAAAA,CAAcC,OAAOC,MAAM,CAAA,CAAA;AAC5D,IAAA,CAAA,CAAA;IAEA,OAAO;AACLf,QAAAA,YAAAA,EAAc,CAACY,EAAAA,GAAON,aAAAA,CAAcU,GAAG,CAACJ,EAAAA,CAAAA;AACxCV,QAAAA,cAAAA,EAAgB,CAACU,EAAAA,GAAOJ,eAAAA,CAAgBQ,GAAG,CAACJ,EAAAA;AAC9C,KAAA;AACF;AAEA;;;;;AAKC,IACM,MAAMK,UAAAA,GAAa,CACxBC,SAAAA,EACAC,MACAP,EAAAA,EACAQ,gBAAAA,GAAAA;IAEA,MAAMC,OAAAA,GAAUF,SAAS,MAAA,GAASD,SAAAA,CAAUlB,YAAY,CAACY,EAAAA,CAAAA,GAAMM,SAAAA,CAAUhB,cAAc,CAACU,EAAAA,CAAAA;IAExF,OAAOS,OAAAA,KAAYpB,YAAYmB,gBAAAA,GAAmBC,OAAAA;AACpD;;;;"}
|