@strapi/upload 5.52.2 → 5.52.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/components/AssetCard/AssetCard.js +1 -0
- package/dist/admin/components/AssetCard/AssetCard.js.map +1 -1
- package/dist/admin/components/AssetCard/AssetCard.mjs +1 -0
- package/dist/admin/components/AssetCard/AssetCard.mjs.map +1 -1
- package/dist/admin/components/AssetCard/AssetCardBase.js +1 -0
- package/dist/admin/components/AssetCard/AssetCardBase.js.map +1 -1
- package/dist/admin/components/AssetCard/AssetCardBase.mjs +1 -0
- package/dist/admin/components/AssetCard/AssetCardBase.mjs.map +1 -1
- package/dist/admin/components/AssetCard/ImageAssetCard.js +1 -0
- package/dist/admin/components/AssetCard/ImageAssetCard.js.map +1 -1
- package/dist/admin/components/AssetCard/ImageAssetCard.mjs +1 -0
- package/dist/admin/components/AssetCard/ImageAssetCard.mjs.map +1 -1
- package/dist/admin/components/AssetCard/UploadingAssetCard.js +1 -0
- package/dist/admin/components/AssetCard/UploadingAssetCard.js.map +1 -1
- package/dist/admin/components/AssetCard/UploadingAssetCard.mjs +1 -0
- package/dist/admin/components/AssetCard/UploadingAssetCard.mjs.map +1 -1
- package/dist/admin/components/AssetCard/VideoAssetCard.js +1 -0
- package/dist/admin/components/AssetCard/VideoAssetCard.js.map +1 -1
- package/dist/admin/components/AssetCard/VideoAssetCard.mjs +1 -0
- package/dist/admin/components/AssetCard/VideoAssetCard.mjs.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.js +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.js.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.mjs +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/BrowseStep.mjs.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/Filters.js +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/Filters.js.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/Filters.mjs +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/Filters.mjs.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.js +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.js.map +1 -1
- package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.mjs +1 -0
- package/dist/admin/components/AssetDialog/BrowseStep/SearchAsset/SearchAsset.mjs.map +1 -1
- package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.js +1 -0
- package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.js.map +1 -1
- package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.mjs +1 -0
- package/dist/admin/components/AssetDialog/SelectedStep/SelectedStep.mjs.map +1 -1
- package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.js +1 -0
- package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.js.map +1 -1
- package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.mjs +1 -0
- package/dist/admin/components/BulkMoveDialog/BulkMoveDialog.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/EditAssetContent.js +1 -0
- package/dist/admin/components/EditAssetDialog/EditAssetContent.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.js +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.js +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/CroppingActions.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.js +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.js +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.js +1 -0
- package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/ReplaceMediaButton.mjs.map +1 -1
- package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.js +1 -0
- package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.js.map +1 -1
- package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.mjs +1 -0
- package/dist/admin/components/EditFolderDialog/ModalHeader/ModalHeader.mjs.map +1 -1
- package/dist/admin/components/FilterList/FilterList.js +2 -2
- package/dist/admin/components/FilterList/FilterList.js.map +1 -1
- package/dist/admin/components/FilterList/FilterList.mjs +2 -2
- package/dist/admin/components/FilterList/FilterList.mjs.map +1 -1
- package/dist/admin/components/FilterPopover/FilterPopover.js +4 -4
- package/dist/admin/components/FilterPopover/FilterPopover.js.map +1 -1
- package/dist/admin/components/FilterPopover/FilterPopover.mjs +4 -4
- package/dist/admin/components/FilterPopover/FilterPopover.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.js +1 -0
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.mjs +1 -0
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAsset.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.js +1 -0
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.mjs +1 -0
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssetActions.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.js +4 -10
- package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.mjs +4 -10
- package/dist/admin/components/MediaLibraryInput/Carousel/EmptyStateAsset.mjs.map +1 -1
- package/dist/admin/components/SortPicker/SortPicker.js +1 -0
- package/dist/admin/components/SortPicker/SortPicker.js.map +1 -1
- package/dist/admin/components/SortPicker/SortPicker.mjs +1 -0
- package/dist/admin/components/SortPicker/SortPicker.mjs.map +1 -1
- package/dist/admin/components/TableList/CellContent.js +1 -0
- package/dist/admin/components/TableList/CellContent.js.map +1 -1
- package/dist/admin/components/TableList/CellContent.mjs +1 -0
- package/dist/admin/components/TableList/CellContent.mjs.map +1 -1
- package/dist/admin/components/TableList/PreviewCell.js +1 -0
- package/dist/admin/components/TableList/PreviewCell.js.map +1 -1
- package/dist/admin/components/TableList/PreviewCell.mjs +1 -0
- package/dist/admin/components/TableList/PreviewCell.mjs.map +1 -1
- package/dist/admin/components/TableList/TableList.js +1 -0
- package/dist/admin/components/TableList/TableList.js.map +1 -1
- package/dist/admin/components/TableList/TableList.mjs +1 -0
- package/dist/admin/components/TableList/TableList.mjs.map +1 -1
- package/dist/admin/components/TableList/TableRows.js +1 -0
- package/dist/admin/components/TableList/TableRows.js.map +1 -1
- package/dist/admin/components/TableList/TableRows.mjs +1 -0
- package/dist/admin/components/TableList/TableRows.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.js +1 -0
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.mjs +1 -0
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/AddAssetStep.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.js +9 -21
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.mjs +9 -21
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromComputerForm.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js +1 -0
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs +1 -0
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.js +1 -0
- package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.mjs +1 -0
- package/dist/admin/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.mjs.map +1 -1
- package/dist/admin/constants.js +1 -0
- package/dist/admin/constants.js.map +1 -1
- package/dist/admin/constants.mjs +1 -0
- package/dist/admin/constants.mjs.map +1 -1
- package/dist/admin/future/components/Drawer.js +11 -0
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +11 -0
- package/dist/admin/future/components/Drawer.mjs.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.js +46 -12
- package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.mjs +47 -13
- package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.js +11 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +11 -0
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +4 -1
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +4 -1
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +29 -9
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +31 -11
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +7 -5
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +7 -5
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +47 -28
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +48 -29
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +85 -9
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +87 -11
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +21 -3
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +21 -3
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +4 -3
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +5 -4
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +40 -2
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +40 -2
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js +5 -5
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs +6 -6
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListSort.js +10 -10
- package/dist/admin/future/pages/Assets/hooks/useListSort.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs +11 -11
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.js +86 -0
- package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.mjs +83 -0
- package/dist/admin/future/pages/Assets/utils/mergeUploadedAssets.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/renderedKeys.js +19 -0
- package/dist/admin/future/pages/Assets/utils/renderedKeys.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/renderedKeys.mjs +17 -0
- package/dist/admin/future/pages/Assets/utils/renderedKeys.mjs.map +1 -0
- package/dist/admin/future/services/api.js +19 -6
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +19 -6
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/store/uploadProgress.js +55 -2
- package/dist/admin/future/store/uploadProgress.js.map +1 -1
- package/dist/admin/future/store/uploadProgress.mjs +53 -3
- package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
- package/dist/admin/index.js +1 -0
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +1 -0
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/pages/App/ConfigureTheView/components/Settings.js +1 -0
- package/dist/admin/pages/App/ConfigureTheView/components/Settings.js.map +1 -1
- package/dist/admin/pages/App/ConfigureTheView/components/Settings.mjs +1 -0
- package/dist/admin/pages/App/ConfigureTheView/components/Settings.mjs.map +1 -1
- package/dist/admin/pages/App/MediaLibrary.js +7 -9
- package/dist/admin/pages/App/MediaLibrary.js.map +1 -1
- package/dist/admin/pages/App/MediaLibrary.mjs +8 -10
- package/dist/admin/pages/App/MediaLibrary.mjs.map +1 -1
- package/dist/admin/pages/App/components/BulkActions.js +1 -0
- package/dist/admin/pages/App/components/BulkActions.js.map +1 -1
- package/dist/admin/pages/App/components/BulkActions.mjs +1 -0
- package/dist/admin/pages/App/components/BulkActions.mjs.map +1 -1
- package/dist/admin/pages/App/components/EmptyOrNoPermissions.js +1 -0
- package/dist/admin/pages/App/components/EmptyOrNoPermissions.js.map +1 -1
- package/dist/admin/pages/App/components/EmptyOrNoPermissions.mjs +1 -0
- package/dist/admin/pages/App/components/EmptyOrNoPermissions.mjs.map +1 -1
- package/dist/admin/pages/App/components/Filters.js +8 -8
- package/dist/admin/pages/App/components/Filters.js.map +1 -1
- package/dist/admin/pages/App/components/Filters.mjs +9 -9
- package/dist/admin/pages/App/components/Filters.mjs.map +1 -1
- package/dist/admin/pages/App/components/Header.js +1 -1
- package/dist/admin/pages/App/components/Header.js.map +1 -1
- package/dist/admin/pages/App/components/Header.mjs +2 -2
- package/dist/admin/pages/App/components/Header.mjs.map +1 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsGrid.d.ts +4 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +4 -1
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/utils/mergeUploadedAssets.d.ts +35 -0
- package/dist/admin/src/future/pages/Assets/utils/renderedKeys.d.ts +19 -0
- package/dist/admin/src/future/store/uploadProgress.d.ts +51 -0
- package/dist/admin/src/utils/index.d.ts +1 -0
- package/dist/admin/src/utils/rawFileToAsset.d.ts +2 -2
- package/dist/admin/src/utils/resolveFileMime.d.ts +13 -0
- package/dist/admin/translations/en.json.js +4 -1
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +4 -1
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/getFolderURL.js +2 -1
- package/dist/admin/utils/getFolderURL.js.map +1 -1
- package/dist/admin/utils/getFolderURL.mjs +2 -1
- package/dist/admin/utils/getFolderURL.mjs.map +1 -1
- package/dist/admin/utils/rawFileToAsset.js +5 -3
- package/dist/admin/utils/rawFileToAsset.js.map +1 -1
- package/dist/admin/utils/rawFileToAsset.mjs +5 -3
- package/dist/admin/utils/rawFileToAsset.mjs.map +1 -1
- package/dist/admin/utils/resolveFileMime.js +69 -0
- package/dist/admin/utils/resolveFileMime.js.map +1 -0
- package/dist/admin/utils/resolveFileMime.mjs +64 -0
- package/dist/admin/utils/resolveFileMime.mjs.map +1 -0
- package/dist/admin/utils/urlsToAssets.js +8 -4
- package/dist/admin/utils/urlsToAssets.js.map +1 -1
- package/dist/admin/utils/urlsToAssets.mjs +8 -4
- package/dist/admin/utils/urlsToAssets.mjs.map +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FolderTree.mjs","sources":["../../../../../../../admin/src/future/pages/Assets/components/FolderTree/FolderTree.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\nimport { SubNav } from '@strapi/admin/strapi-admin';\nimport { Box, Flex, IconButton, Loader, Typography } from '@strapi/design-system';\nimport { ChevronDown, Folder as FolderIcon, House } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { css, styled } from 'styled-components';\n\nimport { TruncatedText } from '../../../../components/TruncatedText';\nimport { useGetFolderStructureQuery } from '../../../../services/folders';\nimport { getTranslationKey } from '../../../../utils/translations';\nimport { useAssetsDndOptional } from '../Dnd/AssetsDndProvider';\nimport { useFolderTreeDroppable } from '../Dnd/useFolderTreeDroppable';\n\nimport { useSpringLoadedExpand } from './useSpringLoadedExpand';\n\nimport type { FolderNode } from '../../../../../../../shared/contracts/folders';\n\n/* -------------------------------------------------------------------------------------------------\n * RowButton — shared row styling aligned with admin SubNav.Link\n * -----------------------------------------------------------------------------------------------*/\n\nconst RowButton = styled.button<{\n $isActive: boolean;\n $isValidDropTarget?: boolean;\n $isInvalidDropCursor?: boolean;\n $isMovePending?: boolean;\n}>`\n display: flex;\n align-items: center;\n gap: ${({ theme }) => theme.spaces[2]};\n width: 100%;\n min-height: 3.2rem;\n padding: ${({ theme }) => `${theme.spaces[1]} ${theme.spaces[2]}`};\n border: 0;\n background: ${({ $isActive, $isValidDropTarget, theme }) => {\n if ($isValidDropTarget) {\n return theme.colors.primary100;\n }\n\n return $isActive ? theme.colors.primary100 : 'transparent';\n }};\n color: ${({ $isActive, theme }) =>\n $isActive ? theme.colors.primary700 : theme.colors.neutral800};\n border-radius: ${({ theme }) => theme.borderRadius};\n cursor: ${({ $isMovePending, $isInvalidDropCursor }) => {\n if ($isMovePending) {\n return 'wait';\n }\n\n return $isInvalidDropCursor ? 'not-allowed' : 'pointer';\n }};\n text-align: left;\n font: inherit;\n pointer-events: ${({ $isMovePending }) => ($isMovePending ? 'none' : 'auto')};\n\n ${({ $isValidDropTarget, theme }) =>\n $isValidDropTarget &&\n css`\n outline: 1px dashed ${theme.colors.primary600};\n outline-offset: -1px;\n `}\n\n &:hover {\n background: ${({ $isActive, $isValidDropTarget, theme }) => {\n if ($isValidDropTarget) {\n return theme.colors.primary100;\n }\n\n return $isActive ? theme.colors.primary100 : theme.colors.neutral100;\n }};\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: -2px;\n }\n`;\n\nconst TreeRow = styled(Flex)<{\n $isValidDropTarget?: boolean;\n $isInvalidDropCursor?: boolean;\n $isMovePending?: boolean;\n}>`\n cursor: ${({ $isMovePending, $isInvalidDropCursor }) => {\n if ($isMovePending) {\n return 'wait';\n }\n\n return $isInvalidDropCursor ? 'not-allowed' : 'default';\n }};\n pointer-events: ${({ $isMovePending }) => ($isMovePending ? 'none' : 'auto')};\n border-radius: ${({ theme }) => theme.borderRadius};\n\n ${({ $isValidDropTarget, theme }) =>\n $isValidDropTarget &&\n css`\n background: ${theme.colors.primary100};\n outline: 1px dashed ${theme.colors.primary600};\n outline-offset: -1px;\n `}\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * useExpandedFolders — local expand/collapse state\n * -----------------------------------------------------------------------------------------------*/\n\nconst findAncestorIds = (\n nodes: FolderNode[],\n targetId: number,\n trail: number[] = []\n): number[] | null => {\n for (const node of nodes) {\n if (node.id === targetId) {\n return trail;\n }\n\n if (node.children?.length) {\n const nextTrail = node.id != null ? [...trail, node.id] : trail;\n const found = findAncestorIds(node.children, targetId, nextTrail);\n if (found !== null) {\n return found;\n }\n }\n }\n\n return null;\n};\n\nconst useExpandedFolders = (folderStructure: FolderNode[], currentFolderId: number | null) => {\n const [expandedIds, setExpandedIds] = useState<Set<number>>(() => new Set());\n\n useEffect(() => {\n if (currentFolderId == null) {\n return;\n }\n\n const ancestors = findAncestorIds(folderStructure, currentFolderId);\n if (!ancestors || ancestors.length === 0) {\n return;\n }\n\n setExpandedIds((prev) => {\n const next = new Set(prev);\n let changed = false;\n for (const id of ancestors) {\n if (!next.has(id)) {\n next.add(id);\n changed = true;\n }\n }\n return changed ? next : prev;\n });\n }, [folderStructure, currentFolderId]);\n\n const toggleExpanded = useCallback((id: number) => {\n setExpandedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n });\n }, []);\n\n const expandFolder = useCallback((id: number) => {\n setExpandedIds((prev) => {\n if (prev.has(id)) {\n return prev;\n }\n\n const next = new Set(prev);\n next.add(id);\n return next;\n });\n }, []);\n\n const isExpanded = useCallback((id: number) => expandedIds.has(id), [expandedIds]);\n\n return { isExpanded, toggleExpanded, expandFolder };\n};\n\n/* -------------------------------------------------------------------------------------------------\n * NavList — unstyled list for tree rows\n * -----------------------------------------------------------------------------------------------*/\n\nconst NavList = styled.ul`\n list-style: none;\n margin: 0;\n padding: 0;\n\n /* Grid rather than block, and load-bearing despite rendering a single column:\n a minmax(0, 1fr) track contributes a minimum of 0, which is what stops each\n row propagating the min-content width of its own label.\n\n Folder names ellipsize, and text-overflow needs white-space: nowrap — so a\n label's min-content width is the entire name, and no box lays out narrower\n than its min-content. In block flow that floor travels up to the SubNav\n ScrollArea, which widens the rail and shows a horizontal scrollbar instead of\n truncating the name. Nesting makes it worse: the indent is spent before the\n label is measured, so shorter names trigger it the deeper you go.\n\n Measured in Chromium — dropping either declaration brings the scrollbar\n back, and neither min-width nor overflow on the row is a substitute. */\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * FolderTreeItem — single tree row (internal)\n * -----------------------------------------------------------------------------------------------*/\n\nconst INDENT_PER_LEVEL_REM = 1.6;\n\nconst RotatingChevron = styled(ChevronDown)<{ $expanded: boolean }>`\n transform: rotate(${({ $expanded }) => ($expanded ? '0deg' : '-90deg')});\n transition: transform 0.2s ease;\n`;\n\ninterface FolderTreeItemProps {\n node: FolderNode;\n level: number;\n currentFolderId: number | null;\n showActiveFolder: boolean;\n isExpanded: (id: number) => boolean;\n onToggle: (id: number) => void;\n onExpand: (id: number) => void;\n onSelect: (folderId: number) => void;\n isMovePending: boolean;\n}\n\ninterface FolderTreeItemInnerProps extends Omit<FolderTreeItemProps, 'node'> {\n id: number;\n name: string;\n folderChildren: FolderNode[];\n}\n\nconst FolderTreeItemInner = ({\n id,\n name,\n folderChildren,\n level,\n currentFolderId,\n showActiveFolder,\n isExpanded,\n onToggle,\n onExpand,\n onSelect,\n isMovePending,\n}: FolderTreeItemInnerProps) => {\n const { formatMessage } = useIntl();\n const hasChildren = folderChildren.length > 0;\n const isFolderExpanded = isExpanded(id);\n const isActive = showActiveFolder && currentFolderId === id;\n\n const {\n droppable: { setNodeRef },\n isOver,\n showValidDropHighlight,\n showInvalidDropCursor,\n } = useFolderTreeDroppable({ id, name });\n\n const handleExpand = useCallback(() => onExpand(id), [id, onExpand]);\n\n useSpringLoadedExpand({\n isOver,\n canExpand: hasChildren && !isFolderExpanded,\n onExpand: handleExpand,\n });\n\n // TODO: full `role=\"tree\"` + arrow-key treeview navigation before revamp GA\n // if an accessibility audit requires it (v1 is button rows only).\n return (\n <li>\n <TreeRow\n ref={setNodeRef}\n alignItems=\"center\"\n paddingLeft={`${level * INDENT_PER_LEVEL_REM}rem`}\n gap={1}\n $isValidDropTarget={showValidDropHighlight}\n $isInvalidDropCursor={showInvalidDropCursor}\n $isMovePending={isMovePending}\n >\n <IconButton\n label={formatMessage(\n {\n id: getTranslationKey(\n isFolderExpanded ? 'sidebar.tree.collapse' : 'sidebar.tree.expand'\n ),\n defaultMessage: isFolderExpanded ? 'Collapse {name}' : 'Expand {name}',\n },\n { name }\n )}\n onClick={(event: React.MouseEvent) => {\n event.stopPropagation();\n onToggle(id);\n }}\n variant=\"ghost\"\n withTooltip={false}\n aria-expanded={isFolderExpanded}\n >\n <RotatingChevron $expanded={isFolderExpanded} fill=\"neutral500\" />\n </IconButton>\n\n <Box flex=\"1\" minWidth={0}>\n <RowButton\n type=\"button\"\n $isActive={isActive}\n $isValidDropTarget={showValidDropHighlight}\n $isInvalidDropCursor={showInvalidDropCursor}\n $isMovePending={isMovePending}\n aria-current={isActive ? 'page' : undefined}\n onClick={() => onSelect(id)}\n data-testid={`folder-tree-node-${id}`}\n data-folder-id={id}\n >\n <TruncatedText variant=\"omega\" fontWeight={isActive ? 'semiBold' : 'regular'}>\n {name}\n </TruncatedText>\n </RowButton>\n </Box>\n </TreeRow>\n\n {hasChildren && isFolderExpanded && (\n <NavList>\n {folderChildren.map((child) => (\n <FolderTreeItem\n key={child.id ?? child.name}\n node={child}\n level={level + 1}\n currentFolderId={currentFolderId}\n showActiveFolder={showActiveFolder}\n isExpanded={isExpanded}\n onToggle={onToggle}\n onExpand={onExpand}\n onSelect={onSelect}\n isMovePending={isMovePending}\n />\n ))}\n </NavList>\n )}\n </li>\n );\n};\n\nconst FolderTreeItem = ({ node, ...props }: FolderTreeItemProps) => {\n if (node.id == null) {\n return null;\n }\n\n return (\n <FolderTreeItemInner\n {...props}\n id={node.id}\n name={node.name ?? ''}\n folderChildren={node.children ?? []}\n />\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * FolderTree — public sidebar component\n * -----------------------------------------------------------------------------------------------*/\n\ninterface FolderTreeProps {\n currentFolderId: number | null;\n /**\n * Set to `false` while a global asset search is active: the results span the\n * whole library, so highlighting one folder would misrepresent them.\n * */\n showActiveFolder?: boolean;\n onSelectFolder: (folderId: number | null) => void;\n}\n\n/**\n * Left-rail navigation for the Media Library. Fetches folder structure internally\n * and renders:\n *\n * 1. A \"Media library\" title\n * 2. A filter input that prunes the tree to matching destinations\n * 3. A \"Home\" entry that clears the folder query param\n * 4. A \"FOLDERS\" section header\n * 5. The folder tree itself\n *\n * The filter is purely local: it never touches the URL and never affects the\n * asset list, which has its own independent search in the content toolbar.\n *\n * Presentational with respect to routing — navigation is delegated to the parent\n * via `onSelectFolder` so the URL stays the single source of truth (see\n * `useFolderNavigation`).\n */\nexport const FolderTree = ({\n currentFolderId,\n showActiveFolder = true,\n onSelectFolder,\n}: FolderTreeProps) => {\n const { formatMessage } = useIntl();\n const { data: folderStructure = [], isLoading, isError } = useGetFolderStructureQuery();\n const { isExpanded, toggleExpanded, expandFolder } = useExpandedFolders(\n folderStructure,\n currentFolderId\n );\n const { isMovePending } = useAssetsDndOptional() ?? { isMovePending: false };\n\n const isHomeActive = showActiveFolder && currentFolderId == null;\n const homeLabel = formatMessage({\n id: getTranslationKey('sidebar.home'),\n defaultMessage: 'Home',\n });\n\n const {\n droppable: { setNodeRef: setHomeDropRef },\n showValidDropHighlight: showHomeValidDropHighlight,\n showInvalidDropCursor: showHomeInvalidDropCursor,\n } = useFolderTreeDroppable({ id: null, name: homeLabel });\n\n return (\n <SubNav.Main\n aria-label={formatMessage({\n id: getTranslationKey('sidebar.tree.aria-label'),\n defaultMessage: 'Media library folders',\n })}\n >\n <SubNav.Header\n label={formatMessage({\n id: getTranslationKey('sidebar.title'),\n defaultMessage: 'Media library',\n })}\n />\n\n <SubNav.Content>\n <Flex direction=\"column\" alignItems=\"stretch\" gap={1} padding={3}>\n <RowButton\n ref={setHomeDropRef}\n type=\"button\"\n $isActive={isHomeActive}\n $isValidDropTarget={showHomeValidDropHighlight}\n $isInvalidDropCursor={showHomeInvalidDropCursor}\n $isMovePending={isMovePending}\n aria-current={isHomeActive ? 'page' : undefined}\n onClick={() => onSelectFolder(null)}\n data-testid=\"folder-tree-home\"\n >\n <House aria-hidden width=\"1.6rem\" height=\"1.6rem\" />\n <Typography variant=\"omega\" fontWeight={isHomeActive ? 'semiBold' : 'regular'}>\n {homeLabel}\n </Typography>\n </RowButton>\n\n <Box marginTop={4}>\n <Flex\n alignItems=\"center\"\n gap={1}\n paddingTop={1}\n paddingBottom={1}\n paddingLeft={2}\n paddingRight={2}\n marginBottom={2}\n >\n <FolderIcon aria-hidden width=\"1.6rem\" height=\"1.6rem\" fill=\"neutral500\" />\n <Typography\n variant=\"sigma\"\n textColor=\"neutral600\"\n style={{ textTransform: 'uppercase' }}\n >\n {formatMessage({\n id: getTranslationKey('sidebar.folders'),\n defaultMessage: 'Folders',\n })}\n </Typography>\n </Flex>\n\n {isLoading ? (\n // TODO: revisit loading state before revamp GA\n <Flex justifyContent=\"center\" padding={1} paddingTop={2}>\n <Loader>\n {formatMessage({\n id: getTranslationKey('sidebar.tree.loading'),\n defaultMessage: 'Loading folders...',\n })}\n </Loader>\n </Flex>\n ) : isError ? (\n // TODO: revisit error state before revamp GA\n <Box padding={1} paddingTop={2}>\n <Typography variant=\"pi\" textColor=\"danger600\">\n {formatMessage({\n id: getTranslationKey('sidebar.tree.error'),\n defaultMessage: 'Could not load folders.',\n })}\n </Typography>\n </Box>\n ) : folderStructure.length === 0 ? (\n // TODO: revisit empty state before revamp GA\n <Box padding={1} paddingTop={2}>\n <Typography variant=\"pi\" textColor=\"neutral500\">\n {formatMessage({\n id: getTranslationKey('sidebar.tree.empty'),\n defaultMessage: 'No folders yet',\n })}\n </Typography>\n </Box>\n ) : (\n <NavList>\n {folderStructure.map((node) => (\n <FolderTreeItem\n key={node.id ?? node.name}\n node={node}\n level={0}\n currentFolderId={currentFolderId}\n showActiveFolder={showActiveFolder}\n isExpanded={isExpanded}\n onToggle={toggleExpanded}\n onExpand={expandFolder}\n onSelect={onSelectFolder}\n isMovePending={isMovePending}\n />\n ))}\n </NavList>\n )}\n </Box>\n </Flex>\n </SubNav.Content>\n </SubNav.Main>\n );\n};\n"],"names":["RowButton","styled","button","theme","spaces","$isActive","$isValidDropTarget","colors","primary100","primary700","neutral800","borderRadius","$isMovePending","$isInvalidDropCursor","css","primary600","neutral100","TreeRow","Flex","findAncestorIds","nodes","targetId","trail","node","id","children","length","nextTrail","found","useExpandedFolders","folderStructure","currentFolderId","expandedIds","setExpandedIds","useState","Set","useEffect","ancestors","prev","next","changed","has","add","toggleExpanded","useCallback","delete","expandFolder","isExpanded","NavList","ul","INDENT_PER_LEVEL_REM","RotatingChevron","ChevronDown","$expanded","FolderTreeItemInner","name","folderChildren","level","showActiveFolder","onToggle","onExpand","onSelect","isMovePending","formatMessage","useIntl","hasChildren","isFolderExpanded","isActive","droppable","setNodeRef","isOver","showValidDropHighlight","showInvalidDropCursor","useFolderTreeDroppable","handleExpand","useSpringLoadedExpand","canExpand","_jsxs","li","ref","alignItems","paddingLeft","gap","_jsx","IconButton","label","getTranslationKey","defaultMessage","onClick","event","stopPropagation","variant","withTooltip","aria-expanded","fill","Box","flex","minWidth","type","aria-current","undefined","data-testid","data-folder-id","TruncatedText","fontWeight","map","child","FolderTreeItem","props","FolderTree","onSelectFolder","data","isLoading","isError","useGetFolderStructureQuery","useAssetsDndOptional","isHomeActive","homeLabel","setHomeDropRef","showHomeValidDropHighlight","showHomeInvalidDropCursor","SubNav","Main","aria-label","Header","Content","direction","padding","House","aria-hidden","width","height","Typography","marginTop","paddingTop","paddingBottom","paddingRight","marginBottom","FolderIcon","textColor","style","textTransform","justifyContent","Loader"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FolderTree.mjs","sources":["../../../../../../../admin/src/future/pages/Assets/components/FolderTree/FolderTree.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\nimport { SubNav } from '@strapi/admin/strapi-admin';\nimport { Box, Flex, IconButton, Loader, Typography } from '@strapi/design-system';\nimport { ChevronDown, Folder as FolderIcon, House } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { css, styled } from 'styled-components';\n\nimport { TruncatedText } from '../../../../components/TruncatedText';\nimport { useGetFolderStructureQuery } from '../../../../services/folders';\nimport { getTranslationKey } from '../../../../utils/translations';\nimport { useAssetsDndOptional } from '../Dnd/AssetsDndProvider';\nimport { useFolderTreeDroppable } from '../Dnd/useFolderTreeDroppable';\n\nimport { useSpringLoadedExpand } from './useSpringLoadedExpand';\n\nimport type { FolderNode } from '../../../../../../../shared/contracts/folders';\n\n/* -------------------------------------------------------------------------------------------------\n * RowButton — shared row styling aligned with admin SubNav.Link\n * -----------------------------------------------------------------------------------------------*/\n\nconst RowButton = styled.button<{\n $isActive: boolean;\n $isValidDropTarget?: boolean;\n $isInvalidDropCursor?: boolean;\n $isMovePending?: boolean;\n}>`\n display: flex;\n align-items: center;\n gap: ${({ theme }) => theme.spaces[2]};\n width: 100%;\n min-height: 3.2rem;\n padding: ${({ theme }) => `${theme.spaces[1]} ${theme.spaces[2]}`};\n border: 0;\n background: ${({ $isActive, $isValidDropTarget, theme }) => {\n if ($isValidDropTarget) {\n return theme.colors.primary100;\n }\n\n return $isActive ? theme.colors.primary100 : 'transparent';\n }};\n color: ${({ $isActive, theme }) =>\n $isActive ? theme.colors.primary700 : theme.colors.neutral800};\n border-radius: ${({ theme }) => theme.borderRadius};\n cursor: ${({ $isMovePending, $isInvalidDropCursor }) => {\n if ($isMovePending) {\n return 'wait';\n }\n\n return $isInvalidDropCursor ? 'not-allowed' : 'pointer';\n }};\n text-align: left;\n font: inherit;\n pointer-events: ${({ $isMovePending }) => ($isMovePending ? 'none' : 'auto')};\n\n ${({ $isValidDropTarget, theme }) =>\n $isValidDropTarget &&\n css`\n outline: 1px dashed ${theme.colors.primary600};\n outline-offset: -1px;\n `}\n\n &:hover {\n background: ${({ $isActive, $isValidDropTarget, theme }) => {\n if ($isValidDropTarget) {\n return theme.colors.primary100;\n }\n\n return $isActive ? theme.colors.primary100 : theme.colors.neutral100;\n }};\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: -2px;\n }\n`;\n\nconst TreeRow = styled(Flex)<{\n $isValidDropTarget?: boolean;\n $isInvalidDropCursor?: boolean;\n $isMovePending?: boolean;\n}>`\n cursor: ${({ $isMovePending, $isInvalidDropCursor }) => {\n if ($isMovePending) {\n return 'wait';\n }\n\n return $isInvalidDropCursor ? 'not-allowed' : 'default';\n }};\n pointer-events: ${({ $isMovePending }) => ($isMovePending ? 'none' : 'auto')};\n border-radius: ${({ theme }) => theme.borderRadius};\n\n ${({ $isValidDropTarget, theme }) =>\n $isValidDropTarget &&\n css`\n background: ${theme.colors.primary100};\n outline: 1px dashed ${theme.colors.primary600};\n outline-offset: -1px;\n `}\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * useExpandedFolders — local expand/collapse state\n * -----------------------------------------------------------------------------------------------*/\n\nconst findAncestorIds = (\n nodes: FolderNode[],\n targetId: number,\n trail: number[] = []\n): number[] | null => {\n for (const node of nodes) {\n if (node.id === targetId) {\n return trail;\n }\n\n if (node.children?.length) {\n const nextTrail = node.id != null ? [...trail, node.id] : trail;\n const found = findAncestorIds(node.children, targetId, nextTrail);\n if (found !== null) {\n return found;\n }\n }\n }\n\n return null;\n};\n\nconst useExpandedFolders = (folderStructure: FolderNode[], currentFolderId: number | null) => {\n const [expandedIds, setExpandedIds] = useState<Set<number>>(() => new Set());\n\n useEffect(() => {\n if (currentFolderId == null) {\n return;\n }\n\n const ancestors = findAncestorIds(folderStructure, currentFolderId);\n if (!ancestors || ancestors.length === 0) {\n return;\n }\n\n setExpandedIds((prev) => {\n const next = new Set(prev);\n let changed = false;\n for (const id of ancestors) {\n if (!next.has(id)) {\n next.add(id);\n changed = true;\n }\n }\n return changed ? next : prev;\n });\n }, [folderStructure, currentFolderId]);\n\n const toggleExpanded = useCallback((id: number) => {\n setExpandedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n });\n }, []);\n\n const expandFolder = useCallback((id: number) => {\n setExpandedIds((prev) => {\n if (prev.has(id)) {\n return prev;\n }\n\n const next = new Set(prev);\n next.add(id);\n return next;\n });\n }, []);\n\n const isExpanded = useCallback((id: number) => expandedIds.has(id), [expandedIds]);\n\n return { isExpanded, toggleExpanded, expandFolder };\n};\n\n/* -------------------------------------------------------------------------------------------------\n * NavList — unstyled list for tree rows\n * -----------------------------------------------------------------------------------------------*/\n\nconst NavList = styled.ul`\n list-style: none;\n margin: 0;\n padding: 0;\n\n /* Grid rather than block, and load-bearing despite rendering a single column:\n a minmax(0, 1fr) track contributes a minimum of 0, which is what stops each\n row propagating the min-content width of its own label.\n\n Folder names ellipsize, and text-overflow needs white-space: nowrap — so a\n label's min-content width is the entire name, and no box lays out narrower\n than its min-content. In block flow that floor travels up to the SubNav\n ScrollArea, which widens the rail and shows a horizontal scrollbar instead of\n truncating the name. Nesting makes it worse: the indent is spent before the\n label is measured, so shorter names trigger it the deeper you go.\n\n Measured in Chromium — dropping either declaration brings the scrollbar\n back, and neither min-width nor overflow on the row is a substitute. */\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * FolderTreeItem — single tree row (internal)\n * -----------------------------------------------------------------------------------------------*/\n\nconst INDENT_PER_LEVEL_REM = 1.6;\n\n/**\n * A leaf's chevron is present only to keep the folder names aligned, so it\n * should read as absent. The design system's disabled state is built for real\n * controls — a filled, bordered pill — which draws more attention than the\n * enabled chevron next to it. Doubled selector to outrank that rule.\n */\nconst ChevronButton = styled(IconButton)`\n &&[aria-disabled='true'] {\n background: transparent;\n border-color: transparent;\n opacity: 0.3;\n }\n`;\n\nconst RotatingChevron = styled(ChevronDown)<{ $expanded: boolean }>`\n transform: rotate(${({ $expanded }) => ($expanded ? '0deg' : '-90deg')});\n transition: transform 0.2s ease;\n`;\n\ninterface FolderTreeItemProps {\n node: FolderNode;\n level: number;\n currentFolderId: number | null;\n showActiveFolder: boolean;\n isExpanded: (id: number) => boolean;\n onToggle: (id: number) => void;\n onExpand: (id: number) => void;\n onSelect: (folderId: number) => void;\n isMovePending: boolean;\n}\n\ninterface FolderTreeItemInnerProps extends Omit<FolderTreeItemProps, 'node'> {\n id: number;\n name: string;\n folderChildren: FolderNode[];\n}\n\nconst FolderTreeItemInner = ({\n id,\n name,\n folderChildren,\n level,\n currentFolderId,\n showActiveFolder,\n isExpanded,\n onToggle,\n onExpand,\n onSelect,\n isMovePending,\n}: FolderTreeItemInnerProps) => {\n const { formatMessage } = useIntl();\n const hasChildren = folderChildren.length > 0;\n const isFolderExpanded = isExpanded(id);\n const isActive = showActiveFolder && currentFolderId === id;\n\n const {\n droppable: { setNodeRef },\n isOver,\n showValidDropHighlight,\n showInvalidDropCursor,\n } = useFolderTreeDroppable({ id, name });\n\n const handleExpand = useCallback(() => onExpand(id), [id, onExpand]);\n\n useSpringLoadedExpand({\n isOver,\n canExpand: hasChildren && !isFolderExpanded,\n onExpand: handleExpand,\n });\n\n // TODO: full `role=\"tree\"` + arrow-key treeview navigation before revamp GA\n // if an accessibility audit requires it (v1 is button rows only).\n return (\n <li>\n <TreeRow\n ref={setNodeRef}\n alignItems=\"center\"\n paddingLeft={`${level * INDENT_PER_LEVEL_REM}rem`}\n gap={1}\n $isValidDropTarget={showValidDropHighlight}\n $isInvalidDropCursor={showInvalidDropCursor}\n $isMovePending={isMovePending}\n >\n <ChevronButton\n label={\n hasChildren\n ? formatMessage(\n {\n id: getTranslationKey(\n isFolderExpanded ? 'sidebar.tree.collapse' : 'sidebar.tree.expand'\n ),\n defaultMessage: isFolderExpanded ? 'Collapse {name}' : 'Expand {name}',\n },\n { name }\n )\n : formatMessage(\n {\n id: getTranslationKey('sidebar.tree.no-subfolders'),\n defaultMessage: 'The folder {name} has no subfolders',\n },\n { name }\n )\n }\n disabled={!hasChildren}\n onClick={(event: React.MouseEvent) => {\n event.stopPropagation();\n onToggle(id);\n }}\n variant=\"ghost\"\n withTooltip={false}\n aria-expanded={hasChildren ? isFolderExpanded : undefined}\n >\n <RotatingChevron $expanded={isFolderExpanded} fill=\"neutral500\" />\n </ChevronButton>\n\n <Box flex=\"1\" minWidth={0}>\n <RowButton\n type=\"button\"\n $isActive={isActive}\n $isValidDropTarget={showValidDropHighlight}\n $isInvalidDropCursor={showInvalidDropCursor}\n $isMovePending={isMovePending}\n aria-current={isActive ? 'page' : undefined}\n onClick={() => onSelect(id)}\n data-testid={`folder-tree-node-${id}`}\n data-folder-id={id}\n >\n <TruncatedText variant=\"omega\" fontWeight={isActive ? 'semiBold' : 'regular'}>\n {name}\n </TruncatedText>\n </RowButton>\n </Box>\n </TreeRow>\n\n {hasChildren && isFolderExpanded && (\n <NavList>\n {folderChildren.map((child) => (\n <FolderTreeItem\n key={child.id ?? child.name}\n node={child}\n level={level + 1}\n currentFolderId={currentFolderId}\n showActiveFolder={showActiveFolder}\n isExpanded={isExpanded}\n onToggle={onToggle}\n onExpand={onExpand}\n onSelect={onSelect}\n isMovePending={isMovePending}\n />\n ))}\n </NavList>\n )}\n </li>\n );\n};\n\nconst FolderTreeItem = ({ node, ...props }: FolderTreeItemProps) => {\n if (node.id == null) {\n return null;\n }\n\n return (\n <FolderTreeItemInner\n {...props}\n id={node.id}\n name={node.name ?? ''}\n folderChildren={node.children ?? []}\n />\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * FolderTree — public sidebar component\n * -----------------------------------------------------------------------------------------------*/\n\ninterface FolderTreeProps {\n currentFolderId: number | null;\n /**\n * Set to `false` while a global asset search is active: the results span the\n * whole library, so highlighting one folder would misrepresent them.\n * */\n showActiveFolder?: boolean;\n onSelectFolder: (folderId: number | null) => void;\n}\n\n/**\n * Left-rail navigation for the Media Library. Fetches folder structure internally\n * and renders:\n *\n * 1. A \"Media library\" title\n * 2. A filter input that prunes the tree to matching destinations\n * 3. A \"Home\" entry that clears the folder query param\n * 4. A \"FOLDERS\" section header\n * 5. The folder tree itself\n *\n * The filter is purely local: it never touches the URL and never affects the\n * asset list, which has its own independent search in the content toolbar.\n *\n * Presentational with respect to routing — navigation is delegated to the parent\n * via `onSelectFolder` so the URL stays the single source of truth (see\n * `useFolderNavigation`).\n */\nexport const FolderTree = ({\n currentFolderId,\n showActiveFolder = true,\n onSelectFolder,\n}: FolderTreeProps) => {\n const { formatMessage } = useIntl();\n const { data: folderStructure = [], isLoading, isError } = useGetFolderStructureQuery();\n const { isExpanded, toggleExpanded, expandFolder } = useExpandedFolders(\n folderStructure,\n currentFolderId\n );\n const { isMovePending } = useAssetsDndOptional() ?? { isMovePending: false };\n\n const isHomeActive = showActiveFolder && currentFolderId == null;\n const homeLabel = formatMessage({\n id: getTranslationKey('sidebar.home'),\n defaultMessage: 'Home',\n });\n\n const {\n droppable: { setNodeRef: setHomeDropRef },\n showValidDropHighlight: showHomeValidDropHighlight,\n showInvalidDropCursor: showHomeInvalidDropCursor,\n } = useFolderTreeDroppable({ id: null, name: homeLabel });\n\n return (\n <SubNav.Main\n aria-label={formatMessage({\n id: getTranslationKey('sidebar.tree.aria-label'),\n defaultMessage: 'Media library folders',\n })}\n >\n <SubNav.Header\n label={formatMessage({\n id: getTranslationKey('sidebar.title'),\n defaultMessage: 'Media library',\n })}\n />\n\n <SubNav.Content>\n <Flex direction=\"column\" alignItems=\"stretch\" gap={1} padding={3}>\n <RowButton\n ref={setHomeDropRef}\n type=\"button\"\n $isActive={isHomeActive}\n $isValidDropTarget={showHomeValidDropHighlight}\n $isInvalidDropCursor={showHomeInvalidDropCursor}\n $isMovePending={isMovePending}\n aria-current={isHomeActive ? 'page' : undefined}\n onClick={() => onSelectFolder(null)}\n data-testid=\"folder-tree-home\"\n >\n <House aria-hidden width=\"1.6rem\" height=\"1.6rem\" />\n <Typography variant=\"omega\" fontWeight={isHomeActive ? 'semiBold' : 'regular'}>\n {homeLabel}\n </Typography>\n </RowButton>\n\n <Box marginTop={4}>\n <Flex\n alignItems=\"center\"\n gap={1}\n paddingTop={1}\n paddingBottom={1}\n paddingLeft={2}\n paddingRight={2}\n marginBottom={2}\n >\n <FolderIcon aria-hidden width=\"1.6rem\" height=\"1.6rem\" fill=\"neutral500\" />\n <Typography\n variant=\"sigma\"\n textColor=\"neutral600\"\n style={{ textTransform: 'uppercase' }}\n >\n {formatMessage({\n id: getTranslationKey('sidebar.folders'),\n defaultMessage: 'Folders',\n })}\n </Typography>\n </Flex>\n\n {isLoading ? (\n // TODO: revisit loading state before revamp GA\n <Flex justifyContent=\"center\" padding={1} paddingTop={2}>\n <Loader>\n {formatMessage({\n id: getTranslationKey('sidebar.tree.loading'),\n defaultMessage: 'Loading folders...',\n })}\n </Loader>\n </Flex>\n ) : isError ? (\n // TODO: revisit error state before revamp GA\n <Box padding={1} paddingTop={2}>\n <Typography variant=\"pi\" textColor=\"danger600\">\n {formatMessage({\n id: getTranslationKey('sidebar.tree.error'),\n defaultMessage: 'Could not load folders.',\n })}\n </Typography>\n </Box>\n ) : folderStructure.length === 0 ? (\n // TODO: revisit empty state before revamp GA\n <Box padding={1} paddingTop={2}>\n <Typography variant=\"pi\" textColor=\"neutral500\">\n {formatMessage({\n id: getTranslationKey('sidebar.tree.empty'),\n defaultMessage: 'No folders yet',\n })}\n </Typography>\n </Box>\n ) : (\n <NavList>\n {folderStructure.map((node) => (\n <FolderTreeItem\n key={node.id ?? node.name}\n node={node}\n level={0}\n currentFolderId={currentFolderId}\n showActiveFolder={showActiveFolder}\n isExpanded={isExpanded}\n onToggle={toggleExpanded}\n onExpand={expandFolder}\n onSelect={onSelectFolder}\n isMovePending={isMovePending}\n />\n ))}\n </NavList>\n )}\n </Box>\n </Flex>\n </SubNav.Content>\n </SubNav.Main>\n );\n};\n"],"names":["RowButton","styled","button","theme","spaces","$isActive","$isValidDropTarget","colors","primary100","primary700","neutral800","borderRadius","$isMovePending","$isInvalidDropCursor","css","primary600","neutral100","TreeRow","Flex","findAncestorIds","nodes","targetId","trail","node","id","children","length","nextTrail","found","useExpandedFolders","folderStructure","currentFolderId","expandedIds","setExpandedIds","useState","Set","useEffect","ancestors","prev","next","changed","has","add","toggleExpanded","useCallback","delete","expandFolder","isExpanded","NavList","ul","INDENT_PER_LEVEL_REM","ChevronButton","IconButton","RotatingChevron","ChevronDown","$expanded","FolderTreeItemInner","name","folderChildren","level","showActiveFolder","onToggle","onExpand","onSelect","isMovePending","formatMessage","useIntl","hasChildren","isFolderExpanded","isActive","droppable","setNodeRef","isOver","showValidDropHighlight","showInvalidDropCursor","useFolderTreeDroppable","handleExpand","useSpringLoadedExpand","canExpand","_jsxs","li","ref","alignItems","paddingLeft","gap","_jsx","label","getTranslationKey","defaultMessage","disabled","onClick","event","stopPropagation","variant","withTooltip","aria-expanded","undefined","fill","Box","flex","minWidth","type","aria-current","data-testid","data-folder-id","TruncatedText","fontWeight","map","child","FolderTreeItem","props","FolderTree","onSelectFolder","data","isLoading","isError","useGetFolderStructureQuery","useAssetsDndOptional","isHomeActive","homeLabel","setHomeDropRef","showHomeValidDropHighlight","showHomeInvalidDropCursor","SubNav","Main","aria-label","Header","Content","direction","padding","House","aria-hidden","width","height","Typography","marginTop","paddingTop","paddingBottom","paddingRight","marginBottom","FolderIcon","textColor","style","textTransform","justifyContent","Loader"],"mappings":";;;;;;;;;;;;;;AAkBA;;AAEkG,qGAElG,MAAMA,SAAAA,GAAYC,MAAAA,CAAOC,MAAM;;;OAQxB,EAAE,CAAC,EAAEC,KAAK,EAAE,GAAKA,KAAAA,CAAMC,MAAM,CAAC,CAAA,CAAE,CAAC;;;AAG7B,WAAA,EAAE,CAAC,EAAED,KAAK,EAAE,GAAK,CAAA,EAAGA,MAAMC,MAAM,CAAC,CAAA,CAAE,CAAC,CAAC,EAAED,KAAAA,CAAMC,MAAM,CAAC,CAAA,CAAE,EAAE,CAAC;;AAEtD,cAAA,EAAE,CAAC,EAAEC,SAAS,EAAEC,kBAAkB,EAAEH,KAAK,EAAE,GAAA;AACrD,IAAA,IAAIG,kBAAAA,EAAoB;QACtB,OAAOH,KAAAA,CAAMI,MAAM,CAACC,UAAU;AAChC,IAAA;AAEA,IAAA,OAAOH,SAAAA,GAAYF,KAAAA,CAAMI,MAAM,CAACC,UAAU,GAAG,aAAA;AAC/C,CAAA,CAAE;AACK,SAAA,EAAE,CAAC,EAAEH,SAAS,EAAEF,KAAK,EAAE,GAC5BE,SAAAA,GAAYF,KAAAA,CAAMI,MAAM,CAACE,UAAU,GAAGN,MAAMI,MAAM,CAACG,UAAU,CAAC;AACjD,iBAAA,EAAE,CAAC,EAAEP,KAAK,EAAE,GAAKA,KAAAA,CAAMQ,YAAY,CAAC;AAC3C,UAAA,EAAE,CAAC,EAAEC,cAAc,EAAEC,oBAAoB,EAAE,GAAA;AACjD,IAAA,IAAID,cAAAA,EAAgB;QAClB,OAAO,MAAA;AACT,IAAA;AAEA,IAAA,OAAOC,uBAAuB,aAAA,GAAgB,SAAA;AAChD,CAAA,CAAE;;;AAGc,kBAAA,EAAE,CAAC,EAAED,cAAc,EAAE,GAAMA,cAAAA,GAAiB,SAAS,MAAA,CAAQ;;EAE7E,EAAE,CAAC,EAAEN,kBAAkB,EAAEH,KAAK,EAAE,GAC9BG,kBAAAA,IACAQ,GAAG;AACmB,0BAAA,EAAEX,KAAAA,CAAMI,MAAM,CAACQ,UAAU,CAAC;;AAEhD,IAAA,CAAC;;;AAGW,gBAAA,EAAE,CAAC,EAAEV,SAAS,EAAEC,kBAAkB,EAAEH,KAAK,EAAE,GAAA;AACrD,IAAA,IAAIG,kBAAAA,EAAoB;QACtB,OAAOH,KAAAA,CAAMI,MAAM,CAACC,UAAU;AAChC,IAAA;IAEA,OAAOH,SAAAA,GAAYF,MAAMI,MAAM,CAACC,UAAU,GAAGL,KAAAA,CAAMI,MAAM,CAACS,UAAU;AACtE,CAAA,CAAE;;;;uBAIiB,EAAE,CAAC,EAAEb,KAAK,EAAE,GAAKA,KAAAA,CAAMI,MAAM,CAACQ,UAAU,CAAC;;;AAGhE,CAAC;AAED,MAAME,OAAAA,GAAUhB,MAAAA,CAAOiB,IAAAA,CAIrB;AACQ,UAAA,EAAE,CAAC,EAAEN,cAAc,EAAEC,oBAAoB,EAAE,GAAA;AACjD,IAAA,IAAID,cAAAA,EAAgB;QAClB,OAAO,MAAA;AACT,IAAA;AAEA,IAAA,OAAOC,uBAAuB,aAAA,GAAgB,SAAA;AAChD,CAAA,CAAE;AACc,kBAAA,EAAE,CAAC,EAAED,cAAc,EAAE,GAAMA,cAAAA,GAAiB,SAAS,MAAA,CAAQ;AAC9D,iBAAA,EAAE,CAAC,EAAET,KAAK,EAAE,GAAKA,KAAAA,CAAMQ,YAAY,CAAC;;EAEnD,EAAE,CAAC,EAAEL,kBAAkB,EAAEH,KAAK,EAAE,GAC9BG,kBAAAA,IACAQ,GAAG;AACW,kBAAA,EAAEX,KAAAA,CAAMI,MAAM,CAACC,UAAU,CAAC;AAClB,0BAAA,EAAEL,KAAAA,CAAMI,MAAM,CAACQ,UAAU,CAAC;;AAEhD,IAAA,CAAC;AACL,CAAC;AAED;;AAEkG,qGAElG,MAAMI,eAAAA,GAAkB,CACtBC,KAAAA,EACAC,QAAAA,EACAC,QAAkB,EAAE,GAAA;IAEpB,KAAK,MAAMC,QAAQH,KAAAA,CAAO;QACxB,IAAIG,IAAAA,CAAKC,EAAE,KAAKH,QAAAA,EAAU;YACxB,OAAOC,KAAAA;AACT,QAAA;QAEA,IAAIC,IAAAA,CAAKE,QAAQ,EAAEC,MAAAA,EAAQ;AACzB,YAAA,MAAMC,SAAAA,GAAYJ,IAAAA,CAAKC,EAAE,IAAI,IAAA,GAAO;AAAIF,gBAAAA,GAAAA,KAAAA;AAAOC,gBAAAA,IAAAA,CAAKC;aAAG,GAAGF,KAAAA;AAC1D,YAAA,MAAMM,KAAAA,GAAQT,eAAAA,CAAgBI,IAAAA,CAAKE,QAAQ,EAAEJ,QAAAA,EAAUM,SAAAA,CAAAA;AACvD,YAAA,IAAIC,UAAU,IAAA,EAAM;gBAClB,OAAOA,KAAAA;AACT,YAAA;AACF,QAAA;AACF,IAAA;IAEA,OAAO,IAAA;AACT,CAAA;AAEA,MAAMC,kBAAAA,GAAqB,CAACC,eAAAA,EAA+BC,eAAAA,GAAAA;AACzD,IAAA,MAAM,CAACC,WAAAA,EAAaC,cAAAA,CAAe,GAAGC,QAAAA,CAAsB,IAAM,IAAIC,GAAAA,EAAAA,CAAAA;IAEtEC,SAAAA,CAAU,IAAA;AACR,QAAA,IAAIL,mBAAmB,IAAA,EAAM;AAC3B,YAAA;AACF,QAAA;QAEA,MAAMM,SAAAA,GAAYlB,gBAAgBW,eAAAA,EAAiBC,eAAAA,CAAAA;AACnD,QAAA,IAAI,CAACM,SAAAA,IAAaA,SAAAA,CAAUX,MAAM,KAAK,CAAA,EAAG;AACxC,YAAA;AACF,QAAA;AAEAO,QAAAA,cAAAA,CAAe,CAACK,IAAAA,GAAAA;YACd,MAAMC,IAAAA,GAAO,IAAIJ,GAAAA,CAAIG,IAAAA,CAAAA;AACrB,YAAA,IAAIE,OAAAA,GAAU,KAAA;YACd,KAAK,MAAMhB,MAAMa,SAAAA,CAAW;AAC1B,gBAAA,IAAI,CAACE,IAAAA,CAAKE,GAAG,CAACjB,EAAAA,CAAAA,EAAK;AACjBe,oBAAAA,IAAAA,CAAKG,GAAG,CAAClB,EAAAA,CAAAA;oBACTgB,OAAAA,GAAU,IAAA;AACZ,gBAAA;AACF,YAAA;AACA,YAAA,OAAOA,UAAUD,IAAAA,GAAOD,IAAAA;AAC1B,QAAA,CAAA,CAAA;IACF,CAAA,EAAG;AAACR,QAAAA,eAAAA;AAAiBC,QAAAA;AAAgB,KAAA,CAAA;IAErC,MAAMY,cAAAA,GAAiBC,YAAY,CAACpB,EAAAA,GAAAA;AAClCS,QAAAA,cAAAA,CAAe,CAACK,IAAAA,GAAAA;YACd,MAAMC,IAAAA,GAAO,IAAIJ,GAAAA,CAAIG,IAAAA,CAAAA;YACrB,IAAIC,IAAAA,CAAKE,GAAG,CAACjB,EAAAA,CAAAA,EAAK;AAChBe,gBAAAA,IAAAA,CAAKM,MAAM,CAACrB,EAAAA,CAAAA;YACd,CAAA,MAAO;AACLe,gBAAAA,IAAAA,CAAKG,GAAG,CAAClB,EAAAA,CAAAA;AACX,YAAA;YACA,OAAOe,IAAAA;AACT,QAAA,CAAA,CAAA;AACF,IAAA,CAAA,EAAG,EAAE,CAAA;IAEL,MAAMO,YAAAA,GAAeF,YAAY,CAACpB,EAAAA,GAAAA;AAChCS,QAAAA,cAAAA,CAAe,CAACK,IAAAA,GAAAA;YACd,IAAIA,IAAAA,CAAKG,GAAG,CAACjB,EAAAA,CAAAA,EAAK;gBAChB,OAAOc,IAAAA;AACT,YAAA;YAEA,MAAMC,IAAAA,GAAO,IAAIJ,GAAAA,CAAIG,IAAAA,CAAAA;AACrBC,YAAAA,IAAAA,CAAKG,GAAG,CAAClB,EAAAA,CAAAA;YACT,OAAOe,IAAAA;AACT,QAAA,CAAA,CAAA;AACF,IAAA,CAAA,EAAG,EAAE,CAAA;AAEL,IAAA,MAAMQ,aAAaH,WAAAA,CAAY,CAACpB,KAAeQ,WAAAA,CAAYS,GAAG,CAACjB,EAAAA,CAAAA,EAAK;AAACQ,QAAAA;AAAY,KAAA,CAAA;IAEjF,OAAO;AAAEe,QAAAA,UAAAA;AAAYJ,QAAAA,cAAAA;AAAgBG,QAAAA;AAAa,KAAA;AACpD,CAAA;AAEA;;AAEkG,qGAElG,MAAME,OAAAA,GAAU/C,MAAAA,CAAOgD,EAAE;;;;;;;;;;;;;;;;;;;;AAoBzB,CAAC;AAED;;AAEkG,qGAElG,MAAMC,oBAAAA,GAAuB,GAAA;AAE7B;;;;;AAKC,IACD,MAAMC,aAAAA,GAAgBlD,MAAAA,CAAOmD,UAAAA,CAAW;;;;;;AAMxC,CAAC;AAED,MAAMC,eAAAA,GAAkBpD,MAAAA,CAAOqD,WAAAA,CAAoC;AAC/C,oBAAA,EAAE,CAAC,EAAEC,SAAS,EAAE,GAAMA,SAAAA,GAAY,SAAS,QAAA,CAAU;;AAEzE,CAAC;AAoBD,MAAMC,mBAAAA,GAAsB,CAAC,EAC3BhC,EAAE,EACFiC,IAAI,EACJC,cAAc,EACdC,KAAK,EACL5B,eAAe,EACf6B,gBAAgB,EAChBb,UAAU,EACVc,QAAQ,EACRC,QAAQ,EACRC,QAAQ,EACRC,aAAa,EACY,GAAA;IACzB,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;IAC1B,MAAMC,WAAAA,GAAcT,cAAAA,CAAehC,MAAM,GAAG,CAAA;AAC5C,IAAA,MAAM0C,mBAAmBrB,UAAAA,CAAWvB,EAAAA,CAAAA;IACpC,MAAM6C,QAAAA,GAAWT,oBAAoB7B,eAAAA,KAAoBP,EAAAA;AAEzD,IAAA,MAAM,EACJ8C,SAAAA,EAAW,EAAEC,UAAU,EAAE,EACzBC,MAAM,EACNC,sBAAsB,EACtBC,qBAAqB,EACtB,GAAGC,sBAAAA,CAAuB;AAAEnD,QAAAA,EAAAA;AAAIiC,QAAAA;AAAK,KAAA,CAAA;AAEtC,IAAA,MAAMmB,YAAAA,GAAehC,WAAAA,CAAY,IAAMkB,QAAAA,CAAStC,EAAAA,CAAAA,EAAK;AAACA,QAAAA,EAAAA;AAAIsC,QAAAA;AAAS,KAAA,CAAA;IAEnEe,qBAAAA,CAAsB;AACpBL,QAAAA,MAAAA;AACAM,QAAAA,SAAAA,EAAWX,eAAe,CAACC,gBAAAA;QAC3BN,QAAAA,EAAUc;AACZ,KAAA,CAAA;;;AAIA,IAAA,qBACEG,IAAA,CAACC,IAAAA,EAAAA;;0BACCD,IAAA,CAAC9D,OAAAA,EAAAA;gBACCgE,GAAAA,EAAKV,UAAAA;gBACLW,UAAAA,EAAW,QAAA;AACXC,gBAAAA,WAAAA,EAAa,CAAA,EAAGxB,KAAAA,GAAQT,oBAAAA,CAAqB,GAAG,CAAC;gBACjDkC,GAAAA,EAAK,CAAA;gBACL9E,kBAAAA,EAAoBmE,sBAAAA;gBACpB5D,oBAAAA,EAAsB6D,qBAAAA;gBACtB9D,cAAAA,EAAgBoD,aAAAA;;kCAEhBqB,GAAA,CAAClC,aAAAA,EAAAA;AACCmC,wBAAAA,KAAAA,EACEnB,cACIF,aAAAA,CACE;4BACEzC,EAAAA,EAAI+D,iBAAAA,CACFnB,mBAAmB,uBAAA,GAA0B,qBAAA,CAAA;AAE/CoB,4BAAAA,cAAAA,EAAgBpB,mBAAmB,iBAAA,GAAoB;yBACzD,EACA;AAAEX,4BAAAA;AAAK,yBAAA,CAAA,GAETQ,aAAAA,CACE;AACEzC,4BAAAA,EAAAA,EAAI+D,iBAAAA,CAAkB,4BAAA,CAAA;4BACtBC,cAAAA,EAAgB;yBAClB,EACA;AAAE/B,4BAAAA;AAAK,yBAAA,CAAA;AAGfgC,wBAAAA,QAAAA,EAAU,CAACtB,WAAAA;AACXuB,wBAAAA,OAAAA,EAAS,CAACC,KAAAA,GAAAA;AACRA,4BAAAA,KAAAA,CAAMC,eAAe,EAAA;4BACrB/B,QAAAA,CAASrC,EAAAA,CAAAA;AACX,wBAAA,CAAA;wBACAqE,OAAAA,EAAQ,OAAA;wBACRC,WAAAA,EAAa,KAAA;AACbC,wBAAAA,eAAAA,EAAe5B,cAAcC,gBAAAA,GAAmB4B,SAAAA;AAEhD,wBAAA,QAAA,gBAAAX,GAAA,CAAChC,eAAAA,EAAAA;4BAAgBE,SAAAA,EAAWa,gBAAAA;4BAAkB6B,IAAAA,EAAK;;;kCAGrDZ,GAAA,CAACa,GAAAA,EAAAA;wBAAIC,IAAAA,EAAK,GAAA;wBAAIC,QAAAA,EAAU,CAAA;AACtB,wBAAA,QAAA,gBAAAf,GAAA,CAACrF,SAAAA,EAAAA;4BACCqG,IAAAA,EAAK,QAAA;4BACLhG,SAAAA,EAAWgE,QAAAA;4BACX/D,kBAAAA,EAAoBmE,sBAAAA;4BACpB5D,oBAAAA,EAAsB6D,qBAAAA;4BACtB9D,cAAAA,EAAgBoD,aAAAA;AAChBsC,4BAAAA,cAAAA,EAAcjC,WAAW,MAAA,GAAS2B,SAAAA;AAClCN,4BAAAA,OAAAA,EAAS,IAAM3B,QAAAA,CAASvC,EAAAA,CAAAA;4BACxB+E,aAAAA,EAAa,CAAC,iBAAiB,EAAE/E,EAAAA,CAAAA,CAAI;4BACrCgF,gBAAAA,EAAgBhF,EAAAA;AAEhB,4BAAA,QAAA,gBAAA6D,GAAA,CAACoB,aAAAA,EAAAA;gCAAcZ,OAAAA,EAAQ,OAAA;AAAQa,gCAAAA,UAAAA,EAAYrC,WAAW,UAAA,GAAa,SAAA;AAChEZ,gCAAAA,QAAAA,EAAAA;;;;;;AAMRU,YAAAA,WAAAA,IAAeC,kCACdiB,GAAA,CAACrC,OAAAA,EAAAA;AACEU,gBAAAA,QAAAA,EAAAA,cAAAA,CAAeiD,GAAG,CAAC,CAACC,KAAAA,iBACnBvB,GAAA,CAACwB,cAAAA,EAAAA;wBAECtF,IAAAA,EAAMqF,KAAAA;AACNjD,wBAAAA,KAAAA,EAAOA,KAAAA,GAAQ,CAAA;wBACf5B,eAAAA,EAAiBA,eAAAA;wBACjB6B,gBAAAA,EAAkBA,gBAAAA;wBAClBb,UAAAA,EAAYA,UAAAA;wBACZc,QAAAA,EAAUA,QAAAA;wBACVC,QAAAA,EAAUA,QAAAA;wBACVC,QAAAA,EAAUA,QAAAA;wBACVC,aAAAA,EAAeA;uBATV4C,KAAAA,CAAMpF,EAAE,IAAIoF,KAAAA,CAAMnD,IAAI,CAAA;;;;AAgBzC,CAAA;AAEA,MAAMoD,iBAAiB,CAAC,EAAEtF,IAAI,EAAE,GAAGuF,KAAAA,EAA4B,GAAA;IAC7D,IAAIvF,IAAAA,CAAKC,EAAE,IAAI,IAAA,EAAM;QACnB,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,qBACE6D,GAAA,CAAC7B,mBAAAA,EAAAA;AACE,QAAA,GAAGsD,KAAK;AACTtF,QAAAA,EAAAA,EAAID,KAAKC,EAAE;QACXiC,IAAAA,EAAMlC,IAAAA,CAAKkC,IAAI,IAAI,EAAA;QACnBC,cAAAA,EAAgBnC,IAAAA,CAAKE,QAAQ,IAAI;;AAGvC,CAAA;AAgBA;;;;;;;;;;;;;;;;IAiBO,MAAMsF,UAAAA,GAAa,CAAC,EACzBhF,eAAe,EACf6B,gBAAAA,GAAmB,IAAI,EACvBoD,cAAc,EACE,GAAA;IAChB,MAAM,EAAE/C,aAAa,EAAE,GAAGC,OAAAA,EAAAA;IAC1B,MAAM,EAAE+C,MAAMnF,eAAAA,GAAkB,EAAE,EAAEoF,SAAS,EAAEC,OAAO,EAAE,GAAGC,0BAAAA,EAAAA;IAC3D,MAAM,EAAErE,UAAU,EAAEJ,cAAc,EAAEG,YAAY,EAAE,GAAGjB,kBAAAA,CACnDC,eAAAA,EACAC,eAAAA,CAAAA;AAEF,IAAA,MAAM,EAAEiC,aAAa,EAAE,GAAGqD,oBAAAA,EAAAA,IAA0B;QAAErD,aAAAA,EAAe;AAAM,KAAA;IAE3E,MAAMsD,YAAAA,GAAe1D,oBAAoB7B,eAAAA,IAAmB,IAAA;AAC5D,IAAA,MAAMwF,YAAYtD,aAAAA,CAAc;AAC9BzC,QAAAA,EAAAA,EAAI+D,iBAAAA,CAAkB,cAAA,CAAA;QACtBC,cAAAA,EAAgB;AAClB,KAAA,CAAA;AAEA,IAAA,MAAM,EACJlB,SAAAA,EAAW,EAAEC,UAAAA,EAAYiD,cAAc,EAAE,EACzC/C,sBAAAA,EAAwBgD,0BAA0B,EAClD/C,qBAAAA,EAAuBgD,yBAAyB,EACjD,GAAG/C,sBAAAA,CAAuB;QAAEnD,EAAAA,EAAI,IAAA;QAAMiC,IAAAA,EAAM8D;AAAU,KAAA,CAAA;IAEvD,qBACExC,IAAA,CAAC4C,OAAOC,IAAI,EAAA;AACVC,QAAAA,YAAAA,EAAY5D,aAAAA,CAAc;AACxBzC,YAAAA,EAAAA,EAAI+D,iBAAAA,CAAkB,yBAAA,CAAA;YACtBC,cAAAA,EAAgB;AAClB,SAAA,CAAA;;AAEA,0BAAAH,GAAA,CAACsC,OAAOG,MAAM,EAAA;AACZxC,gBAAAA,KAAAA,EAAOrB,aAAAA,CAAc;AACnBzC,oBAAAA,EAAAA,EAAI+D,iBAAAA,CAAkB,eAAA,CAAA;oBACtBC,cAAAA,EAAgB;AAClB,iBAAA;;AAGF,0BAAAH,GAAA,CAACsC,OAAOI,OAAO,EAAA;AACb,gBAAA,QAAA,gBAAAhD,IAAA,CAAC7D,IAAAA,EAAAA;oBAAK8G,SAAAA,EAAU,QAAA;oBAAS9C,UAAAA,EAAW,SAAA;oBAAUE,GAAAA,EAAK,CAAA;oBAAG6C,OAAAA,EAAS,CAAA;;sCAC7DlD,IAAA,CAAC/E,SAAAA,EAAAA;4BACCiF,GAAAA,EAAKuC,cAAAA;4BACLnB,IAAAA,EAAK,QAAA;4BACLhG,SAAAA,EAAWiH,YAAAA;4BACXhH,kBAAAA,EAAoBmH,0BAAAA;4BACpB5G,oBAAAA,EAAsB6G,yBAAAA;4BACtB9G,cAAAA,EAAgBoD,aAAAA;AAChBsC,4BAAAA,cAAAA,EAAcgB,eAAe,MAAA,GAAStB,SAAAA;AACtCN,4BAAAA,OAAAA,EAAS,IAAMsB,cAAAA,CAAe,IAAA,CAAA;4BAC9BT,aAAAA,EAAY,kBAAA;;8CAEZlB,GAAA,CAAC6C,KAAAA,EAAAA;oCAAMC,aAAW,EAAA,IAAA;oCAACC,KAAAA,EAAM,QAAA;oCAASC,MAAAA,EAAO;;8CACzChD,GAAA,CAACiD,UAAAA,EAAAA;oCAAWzC,OAAAA,EAAQ,OAAA;AAAQa,oCAAAA,UAAAA,EAAYY,eAAe,UAAA,GAAa,SAAA;AACjEC,oCAAAA,QAAAA,EAAAA;;;;sCAILxC,IAAA,CAACmB,GAAAA,EAAAA;4BAAIqC,SAAAA,EAAW,CAAA;;8CACdxD,IAAA,CAAC7D,IAAAA,EAAAA;oCACCgE,UAAAA,EAAW,QAAA;oCACXE,GAAAA,EAAK,CAAA;oCACLoD,UAAAA,EAAY,CAAA;oCACZC,aAAAA,EAAe,CAAA;oCACftD,WAAAA,EAAa,CAAA;oCACbuD,YAAAA,EAAc,CAAA;oCACdC,YAAAA,EAAc,CAAA;;sDAEdtD,GAAA,CAACuD,MAAAA,EAAAA;4CAAWT,aAAW,EAAA,IAAA;4CAACC,KAAAA,EAAM,QAAA;4CAASC,MAAAA,EAAO,QAAA;4CAASpC,IAAAA,EAAK;;sDAC5DZ,GAAA,CAACiD,UAAAA,EAAAA;4CACCzC,OAAAA,EAAQ,OAAA;4CACRgD,SAAAA,EAAU,YAAA;4CACVC,KAAAA,EAAO;gDAAEC,aAAAA,EAAe;AAAY,6CAAA;sDAEnC9E,aAAAA,CAAc;AACbzC,gDAAAA,EAAAA,EAAI+D,iBAAAA,CAAkB,iBAAA,CAAA;gDACtBC,cAAAA,EAAgB;AAClB,6CAAA;;;;AAIH0B,gCAAAA,SAAAA;8CAEC7B,GAAA,CAACnE,IAAAA,EAAAA;oCAAK8H,cAAAA,EAAe,QAAA;oCAASf,OAAAA,EAAS,CAAA;oCAAGO,UAAAA,EAAY,CAAA;AACpD,oCAAA,QAAA,gBAAAnD,GAAA,CAAC4D,MAAAA,EAAAA;kDACEhF,aAAAA,CAAc;AACbzC,4CAAAA,EAAAA,EAAI+D,iBAAAA,CAAkB,sBAAA,CAAA;4CACtBC,cAAAA,EAAgB;AAClB,yCAAA;;AAGF2B,iCAAAA,CAAAA,GAAAA,OAAAA;8CAEF9B,GAAA,CAACa,GAAAA,EAAAA;oCAAI+B,OAAAA,EAAS,CAAA;oCAAGO,UAAAA,EAAY,CAAA;AAC3B,oCAAA,QAAA,gBAAAnD,GAAA,CAACiD,UAAAA,EAAAA;wCAAWzC,OAAAA,EAAQ,IAAA;wCAAKgD,SAAAA,EAAU,WAAA;kDAChC5E,aAAAA,CAAc;AACbzC,4CAAAA,EAAAA,EAAI+D,iBAAAA,CAAkB,oBAAA,CAAA;4CACtBC,cAAAA,EAAgB;AAClB,yCAAA;;qCAGF1D,eAAAA,CAAgBJ,MAAM,KAAK,CAAA;8CAE7B2D,GAAA,CAACa,GAAAA,EAAAA;oCAAI+B,OAAAA,EAAS,CAAA;oCAAGO,UAAAA,EAAY,CAAA;AAC3B,oCAAA,QAAA,gBAAAnD,GAAA,CAACiD,UAAAA,EAAAA;wCAAWzC,OAAAA,EAAQ,IAAA;wCAAKgD,SAAAA,EAAU,YAAA;kDAChC5E,aAAAA,CAAc;AACbzC,4CAAAA,EAAAA,EAAI+D,iBAAAA,CAAkB,oBAAA,CAAA;4CACtBC,cAAAA,EAAgB;AAClB,yCAAA;;mDAIJH,GAAA,CAACrC,OAAAA,EAAAA;AACElB,oCAAAA,QAAAA,EAAAA,eAAAA,CAAgB6E,GAAG,CAAC,CAACpF,IAAAA,iBACpB8D,GAAA,CAACwB,cAAAA,EAAAA;4CAECtF,IAAAA,EAAMA,IAAAA;4CACNoC,KAAAA,EAAO,CAAA;4CACP5B,eAAAA,EAAiBA,eAAAA;4CACjB6B,gBAAAA,EAAkBA,gBAAAA;4CAClBb,UAAAA,EAAYA,UAAAA;4CACZc,QAAAA,EAAUlB,cAAAA;4CACVmB,QAAAA,EAAUhB,YAAAA;4CACViB,QAAAA,EAAUiD,cAAAA;4CACVhD,aAAAA,EAAeA;2CATVzC,IAAAA,CAAKC,EAAE,IAAID,IAAAA,CAAKkC,IAAI,CAAA;;;;;;;;;AAmB7C;;;;"}
|
|
@@ -41,10 +41,11 @@ const useFolderNavigation = ()=>{
|
|
|
41
41
|
/**
|
|
42
42
|
* Strips a `?folder=` value we can't parse.
|
|
43
43
|
*/ const stripFolderParam = React.useCallback(()=>{
|
|
44
|
-
setQuery({
|
|
45
|
-
folder:
|
|
46
|
-
}
|
|
44
|
+
setQuery(strapiAdmin.withEncodedUserParams(query, {
|
|
45
|
+
folder: undefined
|
|
46
|
+
}));
|
|
47
47
|
}, [
|
|
48
|
+
query,
|
|
48
49
|
setQuery
|
|
49
50
|
]);
|
|
50
51
|
// Malformed ?folder= values (e.g. abc) parse as null; strip the param from the URL.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFolderNavigation.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useFolderNavigation.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\n\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\nconst parseFolderIdFromQuery = (folder: string | undefined): number | null => {\n if (!folder) {\n return null;\n }\n\n const id = Number(folder);\n\n return Number.isFinite(id) ? id : null;\n};\n\ninterface FolderNavigationQuery {\n folder?: string;\n _q?: string;\n}\n\nexport const useFolderNavigation = () => {\n const [{ query }, setQuery] = useQueryParams<FolderNavigationQuery>();\n\n const currentFolderId = parseFolderIdFromQuery(query?.folder);\n\n /**\n * Asset search is global, so keeping `_q` after a folder navigation would show\n * byte-identical results and make the navigation look broken. Clearing it here\n * — rather than at each call site — means no caller can forget.\n */\n const navigateToFolder = useCallback(\n (folder: Folder) => {\n setQuery({ folder: String(folder.id), _q: undefined });\n },\n [setQuery]\n );\n\n /**\n * Move to the Media Library root by removing the `folder` query param, and the\n * search term with it. `setQuery(_, 'remove')` deletes the keys present on the\n * first argument (values are ignored) in one navigation, so any other query\n * state (pagination, the details drawer, etc.) is preserved.\n */\n const navigateToRoot = useCallback(() => {\n setQuery({ folder: '', _q: '' }, 'remove');\n }, [setQuery]);\n\n /**\n * Strips a `?folder=` value we can't parse.\n */\n const stripFolderParam = useCallback(() => {\n setQuery({ folder:
|
|
1
|
+
{"version":3,"file":"useFolderNavigation.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useFolderNavigation.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\n\nimport { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\nconst parseFolderIdFromQuery = (folder: string | undefined): number | null => {\n if (!folder) {\n return null;\n }\n\n const id = Number(folder);\n\n return Number.isFinite(id) ? id : null;\n};\n\ninterface FolderNavigationQuery {\n folder?: string;\n _q?: string;\n}\n\nexport const useFolderNavigation = () => {\n const [{ query }, setQuery] = useQueryParams<FolderNavigationQuery>();\n\n const currentFolderId = parseFolderIdFromQuery(query?.folder);\n\n /**\n * Asset search is global, so keeping `_q` after a folder navigation would show\n * byte-identical results and make the navigation look broken. Clearing it here\n * — rather than at each call site — means no caller can forget.\n */\n const navigateToFolder = useCallback(\n (folder: Folder) => {\n setQuery({ folder: String(folder.id), _q: undefined });\n },\n [setQuery]\n );\n\n /**\n * Move to the Media Library root by removing the `folder` query param, and the\n * search term with it. `setQuery(_, 'remove')` deletes the keys present on the\n * first argument (values are ignored) in one navigation, so any other query\n * state (pagination, the details drawer, etc.) is preserved.\n */\n const navigateToRoot = useCallback(() => {\n setQuery({ folder: '', _q: '' }, 'remove');\n }, [setQuery]);\n\n /**\n * Strips a `?folder=` value we can't parse.\n */\n const stripFolderParam = useCallback(() => {\n setQuery(withEncodedUserParams(query, { folder: undefined }));\n }, [query, setQuery]);\n\n // Malformed ?folder= values (e.g. abc) parse as null; strip the param from the URL.\n // Deleted/missing folders (404) are handled in AssetsPage — that needs a fetch.\n useEffect(() => {\n if (query?.folder && currentFolderId === null) {\n stripFolderParam();\n }\n }, [query?.folder, currentFolderId, stripFolderParam]);\n\n /**\n * Convenience for the FolderTree sidebar: a single entry point that handles\n * both \"go home\" and \"go to folder X\" without forcing the caller to know\n * whether to call navigateToFolder or navigateToRoot.\n */\n const navigateToFolderId = useCallback(\n (folderId: number | null) => {\n if (folderId == null) {\n navigateToRoot();\n } else {\n setQuery({ folder: String(folderId), _q: undefined });\n }\n },\n [navigateToRoot, setQuery]\n );\n\n return {\n currentFolderId,\n navigateToFolder,\n navigateToRoot,\n navigateToFolderId,\n };\n};\n"],"names":["parseFolderIdFromQuery","folder","id","Number","isFinite","useFolderNavigation","query","setQuery","useQueryParams","currentFolderId","navigateToFolder","useCallback","String","_q","undefined","navigateToRoot","stripFolderParam","withEncodedUserParams","useEffect","navigateToFolderId","folderId"],"mappings":";;;;;AAMA,MAAMA,yBAAyB,CAACC,MAAAA,GAAAA;AAC9B,IAAA,IAAI,CAACA,MAAAA,EAAQ;QACX,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,MAAMC,KAAKC,MAAAA,CAAOF,MAAAA,CAAAA;AAElB,IAAA,OAAOE,MAAAA,CAAOC,QAAQ,CAACF,EAAAA,CAAAA,GAAMA,EAAAA,GAAK,IAAA;AACpC,CAAA;MAOaG,mBAAAA,GAAsB,IAAA;AACjC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,0BAAAA,EAAAA;IAE9B,MAAMC,eAAAA,GAAkBT,uBAAuBM,KAAAA,EAAOL,MAAAA,CAAAA;AAEtD;;;;MAKA,MAAMS,gBAAAA,GAAmBC,iBAAAA,CACvB,CAACV,MAAAA,GAAAA;QACCM,QAAAA,CAAS;YAAEN,MAAAA,EAAQW,MAAAA,CAAOX,OAAOC,EAAE,CAAA;YAAGW,EAAAA,EAAIC;AAAU,SAAA,CAAA;IACtD,CAAA,EACA;AAACP,QAAAA;AAAS,KAAA,CAAA;AAGZ;;;;;MAMA,MAAMQ,iBAAiBJ,iBAAAA,CAAY,IAAA;QACjCJ,QAAAA,CAAS;YAAEN,MAAAA,EAAQ,EAAA;YAAIY,EAAAA,EAAI;SAAG,EAAG,QAAA,CAAA;IACnC,CAAA,EAAG;AAACN,QAAAA;AAAS,KAAA,CAAA;AAEb;;MAGA,MAAMS,mBAAmBL,iBAAAA,CAAY,IAAA;AACnCJ,QAAAA,QAAAA,CAASU,kCAAsBX,KAAAA,EAAO;YAAEL,MAAAA,EAAQa;AAAU,SAAA,CAAA,CAAA;IAC5D,CAAA,EAAG;AAACR,QAAAA,KAAAA;AAAOC,QAAAA;AAAS,KAAA,CAAA;;;IAIpBW,eAAAA,CAAU,IAAA;QACR,IAAIZ,KAAAA,EAAOL,MAAAA,IAAUQ,eAAAA,KAAoB,IAAA,EAAM;AAC7CO,YAAAA,gBAAAA,EAAAA;AACF,QAAA;IACF,CAAA,EAAG;QAACV,KAAAA,EAAOL,MAAAA;AAAQQ,QAAAA,eAAAA;AAAiBO,QAAAA;AAAiB,KAAA,CAAA;AAErD;;;;MAKA,MAAMG,kBAAAA,GAAqBR,iBAAAA,CACzB,CAACS,QAAAA,GAAAA;AACC,QAAA,IAAIA,YAAY,IAAA,EAAM;AACpBL,YAAAA,cAAAA,EAAAA;QACF,CAAA,MAAO;YACLR,QAAAA,CAAS;AAAEN,gBAAAA,MAAAA,EAAQW,MAAAA,CAAOQ,QAAAA,CAAAA;gBAAWP,EAAAA,EAAIC;AAAU,aAAA,CAAA;AACrD,QAAA;IACF,CAAA,EACA;AAACC,QAAAA,cAAAA;AAAgBR,QAAAA;AAAS,KAAA,CAAA;IAG5B,OAAO;AACLE,QAAAA,eAAAA;AACAC,QAAAA,gBAAAA;AACAK,QAAAA,cAAAA;AACAI,QAAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect } from 'react';
|
|
2
|
-
import { useQueryParams } from '@strapi/admin/strapi-admin';
|
|
2
|
+
import { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';
|
|
3
3
|
|
|
4
4
|
const parseFolderIdFromQuery = (folder)=>{
|
|
5
5
|
if (!folder) {
|
|
@@ -39,10 +39,11 @@ const useFolderNavigation = ()=>{
|
|
|
39
39
|
/**
|
|
40
40
|
* Strips a `?folder=` value we can't parse.
|
|
41
41
|
*/ const stripFolderParam = useCallback(()=>{
|
|
42
|
-
setQuery({
|
|
43
|
-
folder:
|
|
44
|
-
}
|
|
42
|
+
setQuery(withEncodedUserParams(query, {
|
|
43
|
+
folder: undefined
|
|
44
|
+
}));
|
|
45
45
|
}, [
|
|
46
|
+
query,
|
|
46
47
|
setQuery
|
|
47
48
|
]);
|
|
48
49
|
// Malformed ?folder= values (e.g. abc) parse as null; strip the param from the URL.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFolderNavigation.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useFolderNavigation.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\n\nimport { useQueryParams } from '@strapi/admin/strapi-admin';\n\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\nconst parseFolderIdFromQuery = (folder: string | undefined): number | null => {\n if (!folder) {\n return null;\n }\n\n const id = Number(folder);\n\n return Number.isFinite(id) ? id : null;\n};\n\ninterface FolderNavigationQuery {\n folder?: string;\n _q?: string;\n}\n\nexport const useFolderNavigation = () => {\n const [{ query }, setQuery] = useQueryParams<FolderNavigationQuery>();\n\n const currentFolderId = parseFolderIdFromQuery(query?.folder);\n\n /**\n * Asset search is global, so keeping `_q` after a folder navigation would show\n * byte-identical results and make the navigation look broken. Clearing it here\n * — rather than at each call site — means no caller can forget.\n */\n const navigateToFolder = useCallback(\n (folder: Folder) => {\n setQuery({ folder: String(folder.id), _q: undefined });\n },\n [setQuery]\n );\n\n /**\n * Move to the Media Library root by removing the `folder` query param, and the\n * search term with it. `setQuery(_, 'remove')` deletes the keys present on the\n * first argument (values are ignored) in one navigation, so any other query\n * state (pagination, the details drawer, etc.) is preserved.\n */\n const navigateToRoot = useCallback(() => {\n setQuery({ folder: '', _q: '' }, 'remove');\n }, [setQuery]);\n\n /**\n * Strips a `?folder=` value we can't parse.\n */\n const stripFolderParam = useCallback(() => {\n setQuery({ folder:
|
|
1
|
+
{"version":3,"file":"useFolderNavigation.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useFolderNavigation.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\n\nimport { useQueryParams, withEncodedUserParams } from '@strapi/admin/strapi-admin';\n\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\nconst parseFolderIdFromQuery = (folder: string | undefined): number | null => {\n if (!folder) {\n return null;\n }\n\n const id = Number(folder);\n\n return Number.isFinite(id) ? id : null;\n};\n\ninterface FolderNavigationQuery {\n folder?: string;\n _q?: string;\n}\n\nexport const useFolderNavigation = () => {\n const [{ query }, setQuery] = useQueryParams<FolderNavigationQuery>();\n\n const currentFolderId = parseFolderIdFromQuery(query?.folder);\n\n /**\n * Asset search is global, so keeping `_q` after a folder navigation would show\n * byte-identical results and make the navigation look broken. Clearing it here\n * — rather than at each call site — means no caller can forget.\n */\n const navigateToFolder = useCallback(\n (folder: Folder) => {\n setQuery({ folder: String(folder.id), _q: undefined });\n },\n [setQuery]\n );\n\n /**\n * Move to the Media Library root by removing the `folder` query param, and the\n * search term with it. `setQuery(_, 'remove')` deletes the keys present on the\n * first argument (values are ignored) in one navigation, so any other query\n * state (pagination, the details drawer, etc.) is preserved.\n */\n const navigateToRoot = useCallback(() => {\n setQuery({ folder: '', _q: '' }, 'remove');\n }, [setQuery]);\n\n /**\n * Strips a `?folder=` value we can't parse.\n */\n const stripFolderParam = useCallback(() => {\n setQuery(withEncodedUserParams(query, { folder: undefined }));\n }, [query, setQuery]);\n\n // Malformed ?folder= values (e.g. abc) parse as null; strip the param from the URL.\n // Deleted/missing folders (404) are handled in AssetsPage — that needs a fetch.\n useEffect(() => {\n if (query?.folder && currentFolderId === null) {\n stripFolderParam();\n }\n }, [query?.folder, currentFolderId, stripFolderParam]);\n\n /**\n * Convenience for the FolderTree sidebar: a single entry point that handles\n * both \"go home\" and \"go to folder X\" without forcing the caller to know\n * whether to call navigateToFolder or navigateToRoot.\n */\n const navigateToFolderId = useCallback(\n (folderId: number | null) => {\n if (folderId == null) {\n navigateToRoot();\n } else {\n setQuery({ folder: String(folderId), _q: undefined });\n }\n },\n [navigateToRoot, setQuery]\n );\n\n return {\n currentFolderId,\n navigateToFolder,\n navigateToRoot,\n navigateToFolderId,\n };\n};\n"],"names":["parseFolderIdFromQuery","folder","id","Number","isFinite","useFolderNavigation","query","setQuery","useQueryParams","currentFolderId","navigateToFolder","useCallback","String","_q","undefined","navigateToRoot","stripFolderParam","withEncodedUserParams","useEffect","navigateToFolderId","folderId"],"mappings":";;;AAMA,MAAMA,yBAAyB,CAACC,MAAAA,GAAAA;AAC9B,IAAA,IAAI,CAACA,MAAAA,EAAQ;QACX,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,MAAMC,KAAKC,MAAAA,CAAOF,MAAAA,CAAAA;AAElB,IAAA,OAAOE,MAAAA,CAAOC,QAAQ,CAACF,EAAAA,CAAAA,GAAMA,EAAAA,GAAK,IAAA;AACpC,CAAA;MAOaG,mBAAAA,GAAsB,IAAA;AACjC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,EAAEC,SAAS,GAAGC,cAAAA,EAAAA;IAE9B,MAAMC,eAAAA,GAAkBT,uBAAuBM,KAAAA,EAAOL,MAAAA,CAAAA;AAEtD;;;;MAKA,MAAMS,gBAAAA,GAAmBC,WAAAA,CACvB,CAACV,MAAAA,GAAAA;QACCM,QAAAA,CAAS;YAAEN,MAAAA,EAAQW,MAAAA,CAAOX,OAAOC,EAAE,CAAA;YAAGW,EAAAA,EAAIC;AAAU,SAAA,CAAA;IACtD,CAAA,EACA;AAACP,QAAAA;AAAS,KAAA,CAAA;AAGZ;;;;;MAMA,MAAMQ,iBAAiBJ,WAAAA,CAAY,IAAA;QACjCJ,QAAAA,CAAS;YAAEN,MAAAA,EAAQ,EAAA;YAAIY,EAAAA,EAAI;SAAG,EAAG,QAAA,CAAA;IACnC,CAAA,EAAG;AAACN,QAAAA;AAAS,KAAA,CAAA;AAEb;;MAGA,MAAMS,mBAAmBL,WAAAA,CAAY,IAAA;AACnCJ,QAAAA,QAAAA,CAASU,sBAAsBX,KAAAA,EAAO;YAAEL,MAAAA,EAAQa;AAAU,SAAA,CAAA,CAAA;IAC5D,CAAA,EAAG;AAACR,QAAAA,KAAAA;AAAOC,QAAAA;AAAS,KAAA,CAAA;;;IAIpBW,SAAAA,CAAU,IAAA;QACR,IAAIZ,KAAAA,EAAOL,MAAAA,IAAUQ,eAAAA,KAAoB,IAAA,EAAM;AAC7CO,YAAAA,gBAAAA,EAAAA;AACF,QAAA;IACF,CAAA,EAAG;QAACV,KAAAA,EAAOL,MAAAA;AAAQQ,QAAAA,eAAAA;AAAiBO,QAAAA;AAAiB,KAAA,CAAA;AAErD;;;;MAKA,MAAMG,kBAAAA,GAAqBR,WAAAA,CACzB,CAACS,QAAAA,GAAAA;AACC,QAAA,IAAIA,YAAY,IAAA,EAAM;AACpBL,YAAAA,cAAAA,EAAAA;QACF,CAAA,MAAO;YACLR,QAAAA,CAAS;AAAEN,gBAAAA,MAAAA,EAAQW,MAAAA,CAAOQ,QAAAA,CAAAA;gBAAWP,EAAAA,EAAIC;AAAU,aAAA,CAAA;AACrD,QAAA;IACF,CAAA,EACA;AAACC,QAAAA,cAAAA;AAAgBR,QAAAA;AAAS,KAAA,CAAA;IAG5B,OAAO;AACLE,QAAAA,eAAAA;AACAC,QAAAA,gBAAAA;AACAK,QAAAA,cAAAA;AACAI,QAAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -4,6 +4,10 @@ var React = require('react');
|
|
|
4
4
|
var reactRedux = require('react-redux');
|
|
5
5
|
var api = require('../../../services/api.js');
|
|
6
6
|
var assets = require('../../../services/assets.js');
|
|
7
|
+
var hooks = require('../../../store/hooks.js');
|
|
8
|
+
var uploadProgress = require('../../../store/uploadProgress.js');
|
|
9
|
+
var getRelationId = require('../../../utils/getRelationId.js');
|
|
10
|
+
var mergeUploadedAssets = require('../utils/mergeUploadedAssets.js');
|
|
7
11
|
|
|
8
12
|
const PAGE_SIZE = 20;
|
|
9
13
|
/**
|
|
@@ -100,7 +104,7 @@ const PAGE_SIZE = 20;
|
|
|
100
104
|
page: 1
|
|
101
105
|
});
|
|
102
106
|
}
|
|
103
|
-
const { currentData, isLoading, isFetching, error } = assets.useGetAssetsQuery({
|
|
107
|
+
const { currentData, isLoading, isFetching, error, startedTimeStamp, fulfilledTimeStamp } = assets.useGetAssetsQuery({
|
|
104
108
|
...queryArgs,
|
|
105
109
|
page,
|
|
106
110
|
pageSize: PAGE_SIZE
|
|
@@ -206,13 +210,47 @@ const PAGE_SIZE = 20;
|
|
|
206
210
|
// previous folder. Reported as loading so the page shows a spinner instead
|
|
207
211
|
// of the outgoing folder's assets under the incoming folder's header.
|
|
208
212
|
const isChangingList = accumulated.listKey !== listKey;
|
|
209
|
-
const
|
|
213
|
+
const loadedAssets = React.useMemo(()=>isChangingList ? [] : flattenPages(accumulated.pages), [
|
|
210
214
|
isChangingList,
|
|
211
215
|
accumulated.pages
|
|
212
216
|
]);
|
|
213
217
|
// Deliberately the live value, not the stale-tolerant one below: paging must
|
|
214
218
|
// never be driven by a total that belongs to the previous query.
|
|
215
219
|
const hasNextPage = currentData ? page < currentData.pagination.pageCount : false;
|
|
220
|
+
const completedUploads = hooks.useTypedSelector(uploadProgress.selectCompletedUploads);
|
|
221
|
+
// Only an unfiltered, unsearched list can place an upload locally: deciding
|
|
222
|
+
// whether an asset satisfies a filter means reimplementing the server's
|
|
223
|
+
// filter semantics client-side. Filtered views still refresh on the
|
|
224
|
+
// end-of-batch invalidation, as they did before.
|
|
225
|
+
const canPlaceUploads = !search && (filters?.length ?? 0) === 0;
|
|
226
|
+
// When the loaded data came back from a request that started after a given
|
|
227
|
+
// upload — i.e. the server had already stored it when asked. Past that point
|
|
228
|
+
// the list is the authority on whether the asset is there at all, so bridging
|
|
229
|
+
// must stop: otherwise a later delete or move, which refetches without it,
|
|
230
|
+
// would have it re-inserted.
|
|
231
|
+
const listAnsweredAfter = fulfilledTimeStamp !== undefined && startedTimeStamp !== undefined && // A refetch in flight leaves `fulfilledTimeStamp` on the previous response,
|
|
232
|
+
// which predates this request — so only settled data counts.
|
|
233
|
+
fulfilledTimeStamp > startedTimeStamp ? startedTimeStamp : undefined;
|
|
234
|
+
const assets$1 = React.useMemo(()=>{
|
|
235
|
+
if (!canPlaceUploads || completedUploads.length === 0) {
|
|
236
|
+
return loadedAssets;
|
|
237
|
+
}
|
|
238
|
+
const bridging = completedUploads.filter(({ asset, completedAt })=>getRelationId.getRelationId(asset.folder) === folder && (listAnsweredAfter === undefined || completedAt > listAnsweredAfter));
|
|
239
|
+
return mergeUploadedAssets.mergeUploadedAssets({
|
|
240
|
+
assets: loadedAssets,
|
|
241
|
+
uploaded: bridging.map(({ asset })=>asset),
|
|
242
|
+
sort,
|
|
243
|
+
hasNextPage
|
|
244
|
+
});
|
|
245
|
+
}, [
|
|
246
|
+
canPlaceUploads,
|
|
247
|
+
completedUploads,
|
|
248
|
+
loadedAssets,
|
|
249
|
+
folder,
|
|
250
|
+
sort,
|
|
251
|
+
hasNextPage,
|
|
252
|
+
listAnsweredAfter
|
|
253
|
+
]);
|
|
216
254
|
const isFetchingMore = isFetching && page > 1;
|
|
217
255
|
const fetchNextPage = React.useCallback(()=>{
|
|
218
256
|
setPageState((prev)=>({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInfiniteAssets.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useInfiniteAssets.ts"],"sourcesContent":["import { createElement, Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useDispatch, useStore } from 'react-redux';\n\nimport { uploadApi } from '../../../services/api';\nimport { useGetAssetsQuery } from '../../../services/assets';\n\nimport type { File, Pagination } from '../../../../../../shared/contracts/files';\n\nconst PAGE_SIZE = 20;\n\ninterface UseInfiniteAssetsOptions {\n folder?: number | null;\n sort?: string;\n search?: string;\n /** Extra `filters[$and]` entries for the files query. */\n filters?: Record<string, unknown>[];\n /** Structural switch: an `is [folder]`-only type badge means no file can match. */\n enabled?: boolean;\n}\n\ninterface QueryArgs {\n folder: number | null;\n sort?: string;\n search?: string;\n filters?: Record<string, unknown>[];\n}\n\n/**\n * Everything loaded for one query identity.\n *\n * `queryKey` lags the live key while a new query is in flight — that lag is\n * what keeps the previous results on screen across a search change.\n */\ninterface Accumulated {\n queryKey: string;\n listKey: string;\n pages: Record<number, File[]>;\n pagination?: Pagination;\n}\n\ninterface PageState {\n queryKey: string;\n page: number;\n}\n\ntype PageRefreshedHandler = (page: number, results: File[]) => void;\n\n/**\n * Concatenates the pages in ascending order, one entry per asset id.\n *\n * `Map.set` keeps the first insertion position but takes the latest value, so\n * an asset that moved between pages after a mutation holds its place and shows\n * fresh data instead of rendering twice under a duplicate key.\n */\nconst flattenPages = (pages: Record<number, File[]>): File[] => {\n const byId = new Map<number, File>();\n\n for (const page of Object.keys(pages)\n .map(Number)\n .sort((a, b) => a - b)) {\n for (const asset of pages[page]) {\n byId.set(asset.id, asset);\n }\n }\n\n return [...byId.values()];\n};\n\n/**\n * Keeps one earlier page's `getAssets` query subscribed and reports its results\n * back up whenever they change.\n *\n * The hook itself only subscribes the current (latest) page. Without these, a\n * mutation invalidating `{Asset, LIST}` refetches that page alone and earlier\n * pages stay frozen — a rename or delete on an earlier page never reaches the\n * list until a folder/sort/search change. One subscriber per earlier\n * page keeps them all subscribed so the invalidation refetches the whole list.\n * Renders nothing; it exists only for the subscription + `onRefreshed` report.\n */\nconst PageSubscriber = ({\n queryArgs,\n page,\n onRefreshed,\n}: {\n queryArgs: QueryArgs;\n page: number;\n onRefreshed: PageRefreshedHandler;\n}) => {\n const { currentData } = useGetAssetsQuery({ ...queryArgs, page, pageSize: PAGE_SIZE });\n const results = currentData?.results;\n\n useEffect(() => {\n if (results) {\n onRefreshed(page, results);\n }\n // `results` is a stable reference per cache entry, so this only re-reports\n // when a refetch actually replaces the page's data. Reporting the same\n // reference the accumulator already holds is a no-op (see `onRefreshed`).\n }, [results, page, onRefreshed]);\n\n return null;\n};\n\n/**\n * Infinite scroll over `/upload/files`, keeping one accumulated slot per page.\n *\n * The caller MUST render the returned `subscribers` node — that is what keeps\n * every loaded page subscribed, so `{Asset, LIST}` invalidations keep the whole\n * list consistent rather than only the current page.\n */\nconst useInfiniteAssets = ({\n folder = null,\n sort,\n search,\n filters,\n enabled = true,\n}: UseInfiniteAssetsOptions = {}) => {\n // Derived from the request args themselves, so a new filter can't reach the\n // API without also invalidating what was accumulated under the old one.\n const queryArgs: QueryArgs = { folder, sort, search, filters };\n const queryKey = JSON.stringify(queryArgs);\n // Identifies the list being viewed, ignoring the search term. Folder, sort\n // and filter changes are single committed actions, so one clean reset per\n // change is honest. Search is committed per keystroke on a 300ms debounce,\n // and blanking the list on each one would strobe as the user types.\n // The cost: through the search window the previous rows stay rendered and\n // `hasNextPage` is false, so the list reads as complete until page 1 lands.\n const listKey = JSON.stringify({ folder, sort, filters });\n\n const [pageState, setPageState] = useState<PageState>({ queryKey, page: 1 });\n const [accumulated, setAccumulated] = useState<Accumulated>({ queryKey, listKey, pages: {} });\n\n // Derived rather than read straight from state: the render that discovers\n // the key change still reaches the query below, and a list with no page 1\n // must never be requested at page 2.\n const page = pageState.queryKey === queryKey ? pageState.page : 1;\n\n if (pageState.queryKey !== queryKey) {\n setPageState({ queryKey, page: 1 });\n }\n\n const { currentData, isLoading, isFetching, error } = useGetAssetsQuery(\n {\n ...queryArgs,\n page,\n pageSize: PAGE_SIZE,\n },\n { skip: !enabled }\n );\n\n const isSameQuery = accumulated.queryKey === queryKey;\n\n // `currentData` is only ever the payload for the args just passed, so it\n // belongs to this key at this page — nothing to infer. Its reference is\n // stable per cache entry, so this settles after one extra render.\n if (currentData && (!isSameQuery || accumulated.pages[page] !== currentData.results)) {\n setAccumulated(\n isSameQuery\n ? {\n ...accumulated,\n pages: { ...accumulated.pages, [page]: currentData.results },\n pagination: currentData.pagination,\n }\n : {\n queryKey,\n listKey,\n pages: { [page]: currentData.results },\n pagination: currentData.pagination,\n }\n );\n }\n\n // Merges a refetched earlier page back into the accumulator. Ignores reports\n // for a superseded query, and no-ops when the reference is unchanged so a\n // subscriber re-reporting its already-stored page can't loop.\n const handlePageRefreshed = useCallback<PageRefreshedHandler>(\n (refreshedPage, results) => {\n setAccumulated((prev) => {\n if (prev.queryKey !== queryKey || prev.pages[refreshedPage] === results) {\n return prev;\n }\n\n return { ...prev, pages: { ...prev.pages, [refreshedPage]: results } };\n });\n },\n [queryKey]\n );\n\n // One subscriber per earlier page (the current page is already subscribed by\n // the query above). Keyed by queryKey so a folder/sort/search change remounts\n // them against the new args instead of reusing a stale cache entry.\n // `createElement` rather than JSX so this stays a plain `.ts` hook file.\n const subscribers = createElement(\n Fragment,\n null,\n Array.from({ length: Math.max(0, page - 1) }, (_, index) => index + 1).map((earlierPage) =>\n createElement(PageSubscriber, {\n key: `${queryKey}:${earlierPage}`,\n queryArgs,\n page: earlierPage,\n onRefreshed: handlePageRefreshed,\n })\n )\n );\n\n // Drop the left folder's cached `getAssets` pages when the folder changes.\n //\n // RTK Query 1.9.7 leaves a query's store subscription in place when its last\n // subscriber unmounts while a refetch is still in flight — e.g. a delete\n // invalidates `{Asset, LIST}`, every loaded page starts refetching, and the\n // user navigates away before those settle. Those stale subscriptions make the\n // next `{Asset, LIST}` invalidation (an upload in the new folder) refetch the\n // *previous* folder's pages: one bogus `/upload/files` request per loaded\n // page. There is no public per-entry unsubscribe, so evict the left folder's\n // entries outright — returning reloads from page 1, which the accumulator\n // already does on a list change.\n const dispatch = useDispatch();\n const store = useStore();\n const prevFolderRef = useRef(folder);\n useEffect(() => {\n const prevFolder = prevFolderRef.current;\n prevFolderRef.current = folder;\n if (prevFolder === folder) {\n return;\n }\n\n const apiState = (store.getState() as Record<string, { queries?: Record<string, unknown> }>)[\n uploadApi.reducerPath\n ];\n const removeQueryResult = (\n uploadApi as unknown as {\n internalActions: {\n removeQueryResult: (payload: { queryCacheKey: string }) => { type: string };\n };\n }\n ).internalActions.removeQueryResult;\n\n Object.keys(apiState?.queries ?? {}).forEach((cacheKey) => {\n if (!cacheKey.startsWith('getAssets(')) {\n return;\n }\n let args: { folder?: number | null };\n try {\n args = JSON.parse(cacheKey.slice('getAssets('.length, -1));\n } catch {\n return;\n }\n if (args.folder === prevFolder) {\n dispatch(removeQueryResult({ queryCacheKey: cacheKey }));\n }\n });\n }, [folder, dispatch, store]);\n\n // Until the new list's first page lands the accumulator still holds the\n // previous folder. Reported as loading so the page shows a spinner instead\n // of the outgoing folder's assets under the incoming folder's header.\n const isChangingList = accumulated.listKey !== listKey;\n\n const assets = useMemo(\n () => (isChangingList ? [] : flattenPages(accumulated.pages)),\n [isChangingList, accumulated.pages]\n );\n\n // Deliberately the live value, not the stale-tolerant one below: paging must\n // never be driven by a total that belongs to the previous query.\n const hasNextPage = currentData ? page < currentData.pagination.pageCount : false;\n const isFetchingMore = isFetching && page > 1;\n\n const fetchNextPage = useCallback(() => {\n setPageState((prev) => ({\n queryKey,\n page: (prev.queryKey === queryKey ? prev.page : 1) + 1,\n }));\n }, [queryKey]);\n\n if (!enabled) {\n return {\n assets: [] as File[],\n // No list to keep fresh while disabled, so nothing to subscribe.\n subscribers: null,\n pagination: undefined,\n isLoading: false,\n isFetchingMore: false,\n hasNextPage: false,\n fetchNextPage,\n error: undefined,\n };\n }\n\n return {\n assets,\n subscribers,\n // Falls back to the accumulated total so a consumer showing the count\n // doesn't flash zero mid-transition, matching the possibly stale `assets`.\n pagination: currentData?.pagination ?? accumulated.pagination,\n isLoading: isLoading || isChangingList,\n isFetchingMore,\n hasNextPage,\n fetchNextPage,\n error,\n };\n};\n\nexport { useInfiniteAssets };\nexport { PAGE_SIZE };\n"],"names":["PAGE_SIZE","flattenPages","pages","byId","Map","page","Object","keys","map","Number","sort","a","b","asset","set","id","values","PageSubscriber","queryArgs","onRefreshed","currentData","useGetAssetsQuery","pageSize","results","useEffect","useInfiniteAssets","folder","search","filters","enabled","queryKey","JSON","stringify","listKey","pageState","setPageState","useState","accumulated","setAccumulated","isLoading","isFetching","error","skip","isSameQuery","pagination","handlePageRefreshed","useCallback","refreshedPage","prev","subscribers","createElement","Fragment","Array","from","length","Math","max","_","index","earlierPage","key","dispatch","useDispatch","store","useStore","prevFolderRef","useRef","prevFolder","current","apiState","getState","uploadApi","reducerPath","removeQueryResult","internalActions","queries","forEach","cacheKey","startsWith","args","parse","slice","queryCacheKey","isChangingList","assets","useMemo","hasNextPage","pageCount","isFetchingMore","fetchNextPage","undefined"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useInfiniteAssets.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useInfiniteAssets.ts"],"sourcesContent":["import { createElement, Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useDispatch, useStore } from 'react-redux';\n\nimport { uploadApi } from '../../../services/api';\nimport { useGetAssetsQuery } from '../../../services/assets';\nimport { useTypedSelector } from '../../../store/hooks';\nimport { selectCompletedUploads } from '../../../store/uploadProgress';\nimport { getRelationId } from '../../../utils/getRelationId';\nimport { mergeUploadedAssets } from '../utils/mergeUploadedAssets';\n\nimport type { File, Pagination } from '../../../../../../shared/contracts/files';\n\nconst PAGE_SIZE = 20;\n\ninterface UseInfiniteAssetsOptions {\n folder?: number | null;\n sort?: string;\n search?: string;\n /** Extra `filters[$and]` entries for the files query. */\n filters?: Record<string, unknown>[];\n /** Structural switch: an `is [folder]`-only type badge means no file can match. */\n enabled?: boolean;\n}\n\ninterface QueryArgs {\n folder: number | null;\n sort?: string;\n search?: string;\n filters?: Record<string, unknown>[];\n}\n\n/**\n * Everything loaded for one query identity.\n *\n * `queryKey` lags the live key while a new query is in flight — that lag is\n * what keeps the previous results on screen across a search change.\n */\ninterface Accumulated {\n queryKey: string;\n listKey: string;\n pages: Record<number, File[]>;\n pagination?: Pagination;\n}\n\ninterface PageState {\n queryKey: string;\n page: number;\n}\n\ntype PageRefreshedHandler = (page: number, results: File[]) => void;\n\n/**\n * Concatenates the pages in ascending order, one entry per asset id.\n *\n * `Map.set` keeps the first insertion position but takes the latest value, so\n * an asset that moved between pages after a mutation holds its place and shows\n * fresh data instead of rendering twice under a duplicate key.\n */\nconst flattenPages = (pages: Record<number, File[]>): File[] => {\n const byId = new Map<number, File>();\n\n for (const page of Object.keys(pages)\n .map(Number)\n .sort((a, b) => a - b)) {\n for (const asset of pages[page]) {\n byId.set(asset.id, asset);\n }\n }\n\n return [...byId.values()];\n};\n\n/**\n * Keeps one earlier page's `getAssets` query subscribed and reports its results\n * back up whenever they change.\n *\n * The hook itself only subscribes the current (latest) page. Without these, a\n * mutation invalidating `{Asset, LIST}` refetches that page alone and earlier\n * pages stay frozen — a rename or delete on an earlier page never reaches the\n * list until a folder/sort/search change. One subscriber per earlier\n * page keeps them all subscribed so the invalidation refetches the whole list.\n * Renders nothing; it exists only for the subscription + `onRefreshed` report.\n */\nconst PageSubscriber = ({\n queryArgs,\n page,\n onRefreshed,\n}: {\n queryArgs: QueryArgs;\n page: number;\n onRefreshed: PageRefreshedHandler;\n}) => {\n const { currentData } = useGetAssetsQuery({ ...queryArgs, page, pageSize: PAGE_SIZE });\n const results = currentData?.results;\n\n useEffect(() => {\n if (results) {\n onRefreshed(page, results);\n }\n // `results` is a stable reference per cache entry, so this only re-reports\n // when a refetch actually replaces the page's data. Reporting the same\n // reference the accumulator already holds is a no-op (see `onRefreshed`).\n }, [results, page, onRefreshed]);\n\n return null;\n};\n\n/**\n * Infinite scroll over `/upload/files`, keeping one accumulated slot per page.\n *\n * The caller MUST render the returned `subscribers` node — that is what keeps\n * every loaded page subscribed, so `{Asset, LIST}` invalidations keep the whole\n * list consistent rather than only the current page.\n */\nconst useInfiniteAssets = ({\n folder = null,\n sort,\n search,\n filters,\n enabled = true,\n}: UseInfiniteAssetsOptions = {}) => {\n // Derived from the request args themselves, so a new filter can't reach the\n // API without also invalidating what was accumulated under the old one.\n const queryArgs: QueryArgs = { folder, sort, search, filters };\n const queryKey = JSON.stringify(queryArgs);\n // Identifies the list being viewed, ignoring the search term. Folder, sort\n // and filter changes are single committed actions, so one clean reset per\n // change is honest. Search is committed per keystroke on a 300ms debounce,\n // and blanking the list on each one would strobe as the user types.\n // The cost: through the search window the previous rows stay rendered and\n // `hasNextPage` is false, so the list reads as complete until page 1 lands.\n const listKey = JSON.stringify({ folder, sort, filters });\n\n const [pageState, setPageState] = useState<PageState>({ queryKey, page: 1 });\n const [accumulated, setAccumulated] = useState<Accumulated>({ queryKey, listKey, pages: {} });\n\n // Derived rather than read straight from state: the render that discovers\n // the key change still reaches the query below, and a list with no page 1\n // must never be requested at page 2.\n const page = pageState.queryKey === queryKey ? pageState.page : 1;\n\n if (pageState.queryKey !== queryKey) {\n setPageState({ queryKey, page: 1 });\n }\n\n const { currentData, isLoading, isFetching, error, startedTimeStamp, fulfilledTimeStamp } =\n useGetAssetsQuery(\n {\n ...queryArgs,\n page,\n pageSize: PAGE_SIZE,\n },\n { skip: !enabled }\n );\n\n const isSameQuery = accumulated.queryKey === queryKey;\n\n // `currentData` is only ever the payload for the args just passed, so it\n // belongs to this key at this page — nothing to infer. Its reference is\n // stable per cache entry, so this settles after one extra render.\n if (currentData && (!isSameQuery || accumulated.pages[page] !== currentData.results)) {\n setAccumulated(\n isSameQuery\n ? {\n ...accumulated,\n pages: { ...accumulated.pages, [page]: currentData.results },\n pagination: currentData.pagination,\n }\n : {\n queryKey,\n listKey,\n pages: { [page]: currentData.results },\n pagination: currentData.pagination,\n }\n );\n }\n\n // Merges a refetched earlier page back into the accumulator. Ignores reports\n // for a superseded query, and no-ops when the reference is unchanged so a\n // subscriber re-reporting its already-stored page can't loop.\n const handlePageRefreshed = useCallback<PageRefreshedHandler>(\n (refreshedPage, results) => {\n setAccumulated((prev) => {\n if (prev.queryKey !== queryKey || prev.pages[refreshedPage] === results) {\n return prev;\n }\n\n return { ...prev, pages: { ...prev.pages, [refreshedPage]: results } };\n });\n },\n [queryKey]\n );\n\n // One subscriber per earlier page (the current page is already subscribed by\n // the query above). Keyed by queryKey so a folder/sort/search change remounts\n // them against the new args instead of reusing a stale cache entry.\n // `createElement` rather than JSX so this stays a plain `.ts` hook file.\n const subscribers = createElement(\n Fragment,\n null,\n Array.from({ length: Math.max(0, page - 1) }, (_, index) => index + 1).map((earlierPage) =>\n createElement(PageSubscriber, {\n key: `${queryKey}:${earlierPage}`,\n queryArgs,\n page: earlierPage,\n onRefreshed: handlePageRefreshed,\n })\n )\n );\n\n // Drop the left folder's cached `getAssets` pages when the folder changes.\n //\n // RTK Query 1.9.7 leaves a query's store subscription in place when its last\n // subscriber unmounts while a refetch is still in flight — e.g. a delete\n // invalidates `{Asset, LIST}`, every loaded page starts refetching, and the\n // user navigates away before those settle. Those stale subscriptions make the\n // next `{Asset, LIST}` invalidation (an upload in the new folder) refetch the\n // *previous* folder's pages: one bogus `/upload/files` request per loaded\n // page. There is no public per-entry unsubscribe, so evict the left folder's\n // entries outright — returning reloads from page 1, which the accumulator\n // already does on a list change.\n const dispatch = useDispatch();\n const store = useStore();\n const prevFolderRef = useRef(folder);\n useEffect(() => {\n const prevFolder = prevFolderRef.current;\n prevFolderRef.current = folder;\n if (prevFolder === folder) {\n return;\n }\n\n const apiState = (store.getState() as Record<string, { queries?: Record<string, unknown> }>)[\n uploadApi.reducerPath\n ];\n const removeQueryResult = (\n uploadApi as unknown as {\n internalActions: {\n removeQueryResult: (payload: { queryCacheKey: string }) => { type: string };\n };\n }\n ).internalActions.removeQueryResult;\n\n Object.keys(apiState?.queries ?? {}).forEach((cacheKey) => {\n if (!cacheKey.startsWith('getAssets(')) {\n return;\n }\n let args: { folder?: number | null };\n try {\n args = JSON.parse(cacheKey.slice('getAssets('.length, -1));\n } catch {\n return;\n }\n if (args.folder === prevFolder) {\n dispatch(removeQueryResult({ queryCacheKey: cacheKey }));\n }\n });\n }, [folder, dispatch, store]);\n\n // Until the new list's first page lands the accumulator still holds the\n // previous folder. Reported as loading so the page shows a spinner instead\n // of the outgoing folder's assets under the incoming folder's header.\n const isChangingList = accumulated.listKey !== listKey;\n\n const loadedAssets = useMemo(\n () => (isChangingList ? [] : flattenPages(accumulated.pages)),\n [isChangingList, accumulated.pages]\n );\n\n // Deliberately the live value, not the stale-tolerant one below: paging must\n // never be driven by a total that belongs to the previous query.\n const hasNextPage = currentData ? page < currentData.pagination.pageCount : false;\n\n const completedUploads = useTypedSelector(selectCompletedUploads);\n\n // Only an unfiltered, unsearched list can place an upload locally: deciding\n // whether an asset satisfies a filter means reimplementing the server's\n // filter semantics client-side. Filtered views still refresh on the\n // end-of-batch invalidation, as they did before.\n const canPlaceUploads = !search && (filters?.length ?? 0) === 0;\n\n // When the loaded data came back from a request that started after a given\n // upload — i.e. the server had already stored it when asked. Past that point\n // the list is the authority on whether the asset is there at all, so bridging\n // must stop: otherwise a later delete or move, which refetches without it,\n // would have it re-inserted.\n const listAnsweredAfter =\n fulfilledTimeStamp !== undefined &&\n startedTimeStamp !== undefined &&\n // A refetch in flight leaves `fulfilledTimeStamp` on the previous response,\n // which predates this request — so only settled data counts.\n fulfilledTimeStamp > startedTimeStamp\n ? startedTimeStamp\n : undefined;\n\n const assets = useMemo(() => {\n if (!canPlaceUploads || completedUploads.length === 0) {\n return loadedAssets;\n }\n\n const bridging = completedUploads.filter(\n ({ asset, completedAt }) =>\n getRelationId(asset.folder) === folder &&\n (listAnsweredAfter === undefined || completedAt > listAnsweredAfter)\n );\n\n return mergeUploadedAssets({\n assets: loadedAssets,\n uploaded: bridging.map(({ asset }) => asset),\n sort,\n hasNextPage,\n });\n }, [\n canPlaceUploads,\n completedUploads,\n loadedAssets,\n folder,\n sort,\n hasNextPage,\n listAnsweredAfter,\n ]);\n const isFetchingMore = isFetching && page > 1;\n\n const fetchNextPage = useCallback(() => {\n setPageState((prev) => ({\n queryKey,\n page: (prev.queryKey === queryKey ? prev.page : 1) + 1,\n }));\n }, [queryKey]);\n\n if (!enabled) {\n return {\n assets: [] as File[],\n // No list to keep fresh while disabled, so nothing to subscribe.\n subscribers: null,\n pagination: undefined,\n isLoading: false,\n isFetchingMore: false,\n hasNextPage: false,\n fetchNextPage,\n error: undefined,\n };\n }\n\n return {\n assets,\n subscribers,\n // Falls back to the accumulated total so a consumer showing the count\n // doesn't flash zero mid-transition, matching the possibly stale `assets`.\n pagination: currentData?.pagination ?? accumulated.pagination,\n isLoading: isLoading || isChangingList,\n isFetchingMore,\n hasNextPage,\n fetchNextPage,\n error,\n };\n};\n\nexport { useInfiniteAssets };\nexport { PAGE_SIZE };\n"],"names":["PAGE_SIZE","flattenPages","pages","byId","Map","page","Object","keys","map","Number","sort","a","b","asset","set","id","values","PageSubscriber","queryArgs","onRefreshed","currentData","useGetAssetsQuery","pageSize","results","useEffect","useInfiniteAssets","folder","search","filters","enabled","queryKey","JSON","stringify","listKey","pageState","setPageState","useState","accumulated","setAccumulated","isLoading","isFetching","error","startedTimeStamp","fulfilledTimeStamp","skip","isSameQuery","pagination","handlePageRefreshed","useCallback","refreshedPage","prev","subscribers","createElement","Fragment","Array","from","length","Math","max","_","index","earlierPage","key","dispatch","useDispatch","store","useStore","prevFolderRef","useRef","prevFolder","current","apiState","getState","uploadApi","reducerPath","removeQueryResult","internalActions","queries","forEach","cacheKey","startsWith","args","parse","slice","queryCacheKey","isChangingList","loadedAssets","useMemo","hasNextPage","pageCount","completedUploads","useTypedSelector","selectCompletedUploads","canPlaceUploads","listAnsweredAfter","undefined","assets","bridging","filter","completedAt","getRelationId","mergeUploadedAssets","uploaded","isFetchingMore","fetchNextPage"],"mappings":"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"}
|
|
@@ -2,6 +2,10 @@ import { useState, useCallback, createElement, Fragment, useRef, useEffect, useM
|
|
|
2
2
|
import { useDispatch, useStore } from 'react-redux';
|
|
3
3
|
import { uploadApi } from '../../../services/api.mjs';
|
|
4
4
|
import { useGetAssetsQuery } from '../../../services/assets.mjs';
|
|
5
|
+
import { useTypedSelector } from '../../../store/hooks.mjs';
|
|
6
|
+
import { selectCompletedUploads } from '../../../store/uploadProgress.mjs';
|
|
7
|
+
import { getRelationId } from '../../../utils/getRelationId.mjs';
|
|
8
|
+
import { mergeUploadedAssets } from '../utils/mergeUploadedAssets.mjs';
|
|
5
9
|
|
|
6
10
|
const PAGE_SIZE = 20;
|
|
7
11
|
/**
|
|
@@ -98,7 +102,7 @@ const PAGE_SIZE = 20;
|
|
|
98
102
|
page: 1
|
|
99
103
|
});
|
|
100
104
|
}
|
|
101
|
-
const { currentData, isLoading, isFetching, error } = useGetAssetsQuery({
|
|
105
|
+
const { currentData, isLoading, isFetching, error, startedTimeStamp, fulfilledTimeStamp } = useGetAssetsQuery({
|
|
102
106
|
...queryArgs,
|
|
103
107
|
page,
|
|
104
108
|
pageSize: PAGE_SIZE
|
|
@@ -204,13 +208,47 @@ const PAGE_SIZE = 20;
|
|
|
204
208
|
// previous folder. Reported as loading so the page shows a spinner instead
|
|
205
209
|
// of the outgoing folder's assets under the incoming folder's header.
|
|
206
210
|
const isChangingList = accumulated.listKey !== listKey;
|
|
207
|
-
const
|
|
211
|
+
const loadedAssets = useMemo(()=>isChangingList ? [] : flattenPages(accumulated.pages), [
|
|
208
212
|
isChangingList,
|
|
209
213
|
accumulated.pages
|
|
210
214
|
]);
|
|
211
215
|
// Deliberately the live value, not the stale-tolerant one below: paging must
|
|
212
216
|
// never be driven by a total that belongs to the previous query.
|
|
213
217
|
const hasNextPage = currentData ? page < currentData.pagination.pageCount : false;
|
|
218
|
+
const completedUploads = useTypedSelector(selectCompletedUploads);
|
|
219
|
+
// Only an unfiltered, unsearched list can place an upload locally: deciding
|
|
220
|
+
// whether an asset satisfies a filter means reimplementing the server's
|
|
221
|
+
// filter semantics client-side. Filtered views still refresh on the
|
|
222
|
+
// end-of-batch invalidation, as they did before.
|
|
223
|
+
const canPlaceUploads = !search && (filters?.length ?? 0) === 0;
|
|
224
|
+
// When the loaded data came back from a request that started after a given
|
|
225
|
+
// upload — i.e. the server had already stored it when asked. Past that point
|
|
226
|
+
// the list is the authority on whether the asset is there at all, so bridging
|
|
227
|
+
// must stop: otherwise a later delete or move, which refetches without it,
|
|
228
|
+
// would have it re-inserted.
|
|
229
|
+
const listAnsweredAfter = fulfilledTimeStamp !== undefined && startedTimeStamp !== undefined && // A refetch in flight leaves `fulfilledTimeStamp` on the previous response,
|
|
230
|
+
// which predates this request — so only settled data counts.
|
|
231
|
+
fulfilledTimeStamp > startedTimeStamp ? startedTimeStamp : undefined;
|
|
232
|
+
const assets = useMemo(()=>{
|
|
233
|
+
if (!canPlaceUploads || completedUploads.length === 0) {
|
|
234
|
+
return loadedAssets;
|
|
235
|
+
}
|
|
236
|
+
const bridging = completedUploads.filter(({ asset, completedAt })=>getRelationId(asset.folder) === folder && (listAnsweredAfter === undefined || completedAt > listAnsweredAfter));
|
|
237
|
+
return mergeUploadedAssets({
|
|
238
|
+
assets: loadedAssets,
|
|
239
|
+
uploaded: bridging.map(({ asset })=>asset),
|
|
240
|
+
sort,
|
|
241
|
+
hasNextPage
|
|
242
|
+
});
|
|
243
|
+
}, [
|
|
244
|
+
canPlaceUploads,
|
|
245
|
+
completedUploads,
|
|
246
|
+
loadedAssets,
|
|
247
|
+
folder,
|
|
248
|
+
sort,
|
|
249
|
+
hasNextPage,
|
|
250
|
+
listAnsweredAfter
|
|
251
|
+
]);
|
|
214
252
|
const isFetchingMore = isFetching && page > 1;
|
|
215
253
|
const fetchNextPage = useCallback(()=>{
|
|
216
254
|
setPageState((prev)=>({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInfiniteAssets.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useInfiniteAssets.ts"],"sourcesContent":["import { createElement, Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useDispatch, useStore } from 'react-redux';\n\nimport { uploadApi } from '../../../services/api';\nimport { useGetAssetsQuery } from '../../../services/assets';\n\nimport type { File, Pagination } from '../../../../../../shared/contracts/files';\n\nconst PAGE_SIZE = 20;\n\ninterface UseInfiniteAssetsOptions {\n folder?: number | null;\n sort?: string;\n search?: string;\n /** Extra `filters[$and]` entries for the files query. */\n filters?: Record<string, unknown>[];\n /** Structural switch: an `is [folder]`-only type badge means no file can match. */\n enabled?: boolean;\n}\n\ninterface QueryArgs {\n folder: number | null;\n sort?: string;\n search?: string;\n filters?: Record<string, unknown>[];\n}\n\n/**\n * Everything loaded for one query identity.\n *\n * `queryKey` lags the live key while a new query is in flight — that lag is\n * what keeps the previous results on screen across a search change.\n */\ninterface Accumulated {\n queryKey: string;\n listKey: string;\n pages: Record<number, File[]>;\n pagination?: Pagination;\n}\n\ninterface PageState {\n queryKey: string;\n page: number;\n}\n\ntype PageRefreshedHandler = (page: number, results: File[]) => void;\n\n/**\n * Concatenates the pages in ascending order, one entry per asset id.\n *\n * `Map.set` keeps the first insertion position but takes the latest value, so\n * an asset that moved between pages after a mutation holds its place and shows\n * fresh data instead of rendering twice under a duplicate key.\n */\nconst flattenPages = (pages: Record<number, File[]>): File[] => {\n const byId = new Map<number, File>();\n\n for (const page of Object.keys(pages)\n .map(Number)\n .sort((a, b) => a - b)) {\n for (const asset of pages[page]) {\n byId.set(asset.id, asset);\n }\n }\n\n return [...byId.values()];\n};\n\n/**\n * Keeps one earlier page's `getAssets` query subscribed and reports its results\n * back up whenever they change.\n *\n * The hook itself only subscribes the current (latest) page. Without these, a\n * mutation invalidating `{Asset, LIST}` refetches that page alone and earlier\n * pages stay frozen — a rename or delete on an earlier page never reaches the\n * list until a folder/sort/search change. One subscriber per earlier\n * page keeps them all subscribed so the invalidation refetches the whole list.\n * Renders nothing; it exists only for the subscription + `onRefreshed` report.\n */\nconst PageSubscriber = ({\n queryArgs,\n page,\n onRefreshed,\n}: {\n queryArgs: QueryArgs;\n page: number;\n onRefreshed: PageRefreshedHandler;\n}) => {\n const { currentData } = useGetAssetsQuery({ ...queryArgs, page, pageSize: PAGE_SIZE });\n const results = currentData?.results;\n\n useEffect(() => {\n if (results) {\n onRefreshed(page, results);\n }\n // `results` is a stable reference per cache entry, so this only re-reports\n // when a refetch actually replaces the page's data. Reporting the same\n // reference the accumulator already holds is a no-op (see `onRefreshed`).\n }, [results, page, onRefreshed]);\n\n return null;\n};\n\n/**\n * Infinite scroll over `/upload/files`, keeping one accumulated slot per page.\n *\n * The caller MUST render the returned `subscribers` node — that is what keeps\n * every loaded page subscribed, so `{Asset, LIST}` invalidations keep the whole\n * list consistent rather than only the current page.\n */\nconst useInfiniteAssets = ({\n folder = null,\n sort,\n search,\n filters,\n enabled = true,\n}: UseInfiniteAssetsOptions = {}) => {\n // Derived from the request args themselves, so a new filter can't reach the\n // API without also invalidating what was accumulated under the old one.\n const queryArgs: QueryArgs = { folder, sort, search, filters };\n const queryKey = JSON.stringify(queryArgs);\n // Identifies the list being viewed, ignoring the search term. Folder, sort\n // and filter changes are single committed actions, so one clean reset per\n // change is honest. Search is committed per keystroke on a 300ms debounce,\n // and blanking the list on each one would strobe as the user types.\n // The cost: through the search window the previous rows stay rendered and\n // `hasNextPage` is false, so the list reads as complete until page 1 lands.\n const listKey = JSON.stringify({ folder, sort, filters });\n\n const [pageState, setPageState] = useState<PageState>({ queryKey, page: 1 });\n const [accumulated, setAccumulated] = useState<Accumulated>({ queryKey, listKey, pages: {} });\n\n // Derived rather than read straight from state: the render that discovers\n // the key change still reaches the query below, and a list with no page 1\n // must never be requested at page 2.\n const page = pageState.queryKey === queryKey ? pageState.page : 1;\n\n if (pageState.queryKey !== queryKey) {\n setPageState({ queryKey, page: 1 });\n }\n\n const { currentData, isLoading, isFetching, error } = useGetAssetsQuery(\n {\n ...queryArgs,\n page,\n pageSize: PAGE_SIZE,\n },\n { skip: !enabled }\n );\n\n const isSameQuery = accumulated.queryKey === queryKey;\n\n // `currentData` is only ever the payload for the args just passed, so it\n // belongs to this key at this page — nothing to infer. Its reference is\n // stable per cache entry, so this settles after one extra render.\n if (currentData && (!isSameQuery || accumulated.pages[page] !== currentData.results)) {\n setAccumulated(\n isSameQuery\n ? {\n ...accumulated,\n pages: { ...accumulated.pages, [page]: currentData.results },\n pagination: currentData.pagination,\n }\n : {\n queryKey,\n listKey,\n pages: { [page]: currentData.results },\n pagination: currentData.pagination,\n }\n );\n }\n\n // Merges a refetched earlier page back into the accumulator. Ignores reports\n // for a superseded query, and no-ops when the reference is unchanged so a\n // subscriber re-reporting its already-stored page can't loop.\n const handlePageRefreshed = useCallback<PageRefreshedHandler>(\n (refreshedPage, results) => {\n setAccumulated((prev) => {\n if (prev.queryKey !== queryKey || prev.pages[refreshedPage] === results) {\n return prev;\n }\n\n return { ...prev, pages: { ...prev.pages, [refreshedPage]: results } };\n });\n },\n [queryKey]\n );\n\n // One subscriber per earlier page (the current page is already subscribed by\n // the query above). Keyed by queryKey so a folder/sort/search change remounts\n // them against the new args instead of reusing a stale cache entry.\n // `createElement` rather than JSX so this stays a plain `.ts` hook file.\n const subscribers = createElement(\n Fragment,\n null,\n Array.from({ length: Math.max(0, page - 1) }, (_, index) => index + 1).map((earlierPage) =>\n createElement(PageSubscriber, {\n key: `${queryKey}:${earlierPage}`,\n queryArgs,\n page: earlierPage,\n onRefreshed: handlePageRefreshed,\n })\n )\n );\n\n // Drop the left folder's cached `getAssets` pages when the folder changes.\n //\n // RTK Query 1.9.7 leaves a query's store subscription in place when its last\n // subscriber unmounts while a refetch is still in flight — e.g. a delete\n // invalidates `{Asset, LIST}`, every loaded page starts refetching, and the\n // user navigates away before those settle. Those stale subscriptions make the\n // next `{Asset, LIST}` invalidation (an upload in the new folder) refetch the\n // *previous* folder's pages: one bogus `/upload/files` request per loaded\n // page. There is no public per-entry unsubscribe, so evict the left folder's\n // entries outright — returning reloads from page 1, which the accumulator\n // already does on a list change.\n const dispatch = useDispatch();\n const store = useStore();\n const prevFolderRef = useRef(folder);\n useEffect(() => {\n const prevFolder = prevFolderRef.current;\n prevFolderRef.current = folder;\n if (prevFolder === folder) {\n return;\n }\n\n const apiState = (store.getState() as Record<string, { queries?: Record<string, unknown> }>)[\n uploadApi.reducerPath\n ];\n const removeQueryResult = (\n uploadApi as unknown as {\n internalActions: {\n removeQueryResult: (payload: { queryCacheKey: string }) => { type: string };\n };\n }\n ).internalActions.removeQueryResult;\n\n Object.keys(apiState?.queries ?? {}).forEach((cacheKey) => {\n if (!cacheKey.startsWith('getAssets(')) {\n return;\n }\n let args: { folder?: number | null };\n try {\n args = JSON.parse(cacheKey.slice('getAssets('.length, -1));\n } catch {\n return;\n }\n if (args.folder === prevFolder) {\n dispatch(removeQueryResult({ queryCacheKey: cacheKey }));\n }\n });\n }, [folder, dispatch, store]);\n\n // Until the new list's first page lands the accumulator still holds the\n // previous folder. Reported as loading so the page shows a spinner instead\n // of the outgoing folder's assets under the incoming folder's header.\n const isChangingList = accumulated.listKey !== listKey;\n\n const assets = useMemo(\n () => (isChangingList ? [] : flattenPages(accumulated.pages)),\n [isChangingList, accumulated.pages]\n );\n\n // Deliberately the live value, not the stale-tolerant one below: paging must\n // never be driven by a total that belongs to the previous query.\n const hasNextPage = currentData ? page < currentData.pagination.pageCount : false;\n const isFetchingMore = isFetching && page > 1;\n\n const fetchNextPage = useCallback(() => {\n setPageState((prev) => ({\n queryKey,\n page: (prev.queryKey === queryKey ? prev.page : 1) + 1,\n }));\n }, [queryKey]);\n\n if (!enabled) {\n return {\n assets: [] as File[],\n // No list to keep fresh while disabled, so nothing to subscribe.\n subscribers: null,\n pagination: undefined,\n isLoading: false,\n isFetchingMore: false,\n hasNextPage: false,\n fetchNextPage,\n error: undefined,\n };\n }\n\n return {\n assets,\n subscribers,\n // Falls back to the accumulated total so a consumer showing the count\n // doesn't flash zero mid-transition, matching the possibly stale `assets`.\n pagination: currentData?.pagination ?? accumulated.pagination,\n isLoading: isLoading || isChangingList,\n isFetchingMore,\n hasNextPage,\n fetchNextPage,\n error,\n };\n};\n\nexport { useInfiniteAssets };\nexport { PAGE_SIZE };\n"],"names":["PAGE_SIZE","flattenPages","pages","byId","Map","page","Object","keys","map","Number","sort","a","b","asset","set","id","values","PageSubscriber","queryArgs","onRefreshed","currentData","useGetAssetsQuery","pageSize","results","useEffect","useInfiniteAssets","folder","search","filters","enabled","queryKey","JSON","stringify","listKey","pageState","setPageState","useState","accumulated","setAccumulated","isLoading","isFetching","error","skip","isSameQuery","pagination","handlePageRefreshed","useCallback","refreshedPage","prev","subscribers","createElement","Fragment","Array","from","length","Math","max","_","index","earlierPage","key","dispatch","useDispatch","store","useStore","prevFolderRef","useRef","prevFolder","current","apiState","getState","uploadApi","reducerPath","removeQueryResult","internalActions","queries","forEach","cacheKey","startsWith","args","parse","slice","queryCacheKey","isChangingList","assets","useMemo","hasNextPage","pageCount","isFetchingMore","fetchNextPage","undefined"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useInfiniteAssets.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useInfiniteAssets.ts"],"sourcesContent":["import { createElement, Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useDispatch, useStore } from 'react-redux';\n\nimport { uploadApi } from '../../../services/api';\nimport { useGetAssetsQuery } from '../../../services/assets';\nimport { useTypedSelector } from '../../../store/hooks';\nimport { selectCompletedUploads } from '../../../store/uploadProgress';\nimport { getRelationId } from '../../../utils/getRelationId';\nimport { mergeUploadedAssets } from '../utils/mergeUploadedAssets';\n\nimport type { File, Pagination } from '../../../../../../shared/contracts/files';\n\nconst PAGE_SIZE = 20;\n\ninterface UseInfiniteAssetsOptions {\n folder?: number | null;\n sort?: string;\n search?: string;\n /** Extra `filters[$and]` entries for the files query. */\n filters?: Record<string, unknown>[];\n /** Structural switch: an `is [folder]`-only type badge means no file can match. */\n enabled?: boolean;\n}\n\ninterface QueryArgs {\n folder: number | null;\n sort?: string;\n search?: string;\n filters?: Record<string, unknown>[];\n}\n\n/**\n * Everything loaded for one query identity.\n *\n * `queryKey` lags the live key while a new query is in flight — that lag is\n * what keeps the previous results on screen across a search change.\n */\ninterface Accumulated {\n queryKey: string;\n listKey: string;\n pages: Record<number, File[]>;\n pagination?: Pagination;\n}\n\ninterface PageState {\n queryKey: string;\n page: number;\n}\n\ntype PageRefreshedHandler = (page: number, results: File[]) => void;\n\n/**\n * Concatenates the pages in ascending order, one entry per asset id.\n *\n * `Map.set` keeps the first insertion position but takes the latest value, so\n * an asset that moved between pages after a mutation holds its place and shows\n * fresh data instead of rendering twice under a duplicate key.\n */\nconst flattenPages = (pages: Record<number, File[]>): File[] => {\n const byId = new Map<number, File>();\n\n for (const page of Object.keys(pages)\n .map(Number)\n .sort((a, b) => a - b)) {\n for (const asset of pages[page]) {\n byId.set(asset.id, asset);\n }\n }\n\n return [...byId.values()];\n};\n\n/**\n * Keeps one earlier page's `getAssets` query subscribed and reports its results\n * back up whenever they change.\n *\n * The hook itself only subscribes the current (latest) page. Without these, a\n * mutation invalidating `{Asset, LIST}` refetches that page alone and earlier\n * pages stay frozen — a rename or delete on an earlier page never reaches the\n * list until a folder/sort/search change. One subscriber per earlier\n * page keeps them all subscribed so the invalidation refetches the whole list.\n * Renders nothing; it exists only for the subscription + `onRefreshed` report.\n */\nconst PageSubscriber = ({\n queryArgs,\n page,\n onRefreshed,\n}: {\n queryArgs: QueryArgs;\n page: number;\n onRefreshed: PageRefreshedHandler;\n}) => {\n const { currentData } = useGetAssetsQuery({ ...queryArgs, page, pageSize: PAGE_SIZE });\n const results = currentData?.results;\n\n useEffect(() => {\n if (results) {\n onRefreshed(page, results);\n }\n // `results` is a stable reference per cache entry, so this only re-reports\n // when a refetch actually replaces the page's data. Reporting the same\n // reference the accumulator already holds is a no-op (see `onRefreshed`).\n }, [results, page, onRefreshed]);\n\n return null;\n};\n\n/**\n * Infinite scroll over `/upload/files`, keeping one accumulated slot per page.\n *\n * The caller MUST render the returned `subscribers` node — that is what keeps\n * every loaded page subscribed, so `{Asset, LIST}` invalidations keep the whole\n * list consistent rather than only the current page.\n */\nconst useInfiniteAssets = ({\n folder = null,\n sort,\n search,\n filters,\n enabled = true,\n}: UseInfiniteAssetsOptions = {}) => {\n // Derived from the request args themselves, so a new filter can't reach the\n // API without also invalidating what was accumulated under the old one.\n const queryArgs: QueryArgs = { folder, sort, search, filters };\n const queryKey = JSON.stringify(queryArgs);\n // Identifies the list being viewed, ignoring the search term. Folder, sort\n // and filter changes are single committed actions, so one clean reset per\n // change is honest. Search is committed per keystroke on a 300ms debounce,\n // and blanking the list on each one would strobe as the user types.\n // The cost: through the search window the previous rows stay rendered and\n // `hasNextPage` is false, so the list reads as complete until page 1 lands.\n const listKey = JSON.stringify({ folder, sort, filters });\n\n const [pageState, setPageState] = useState<PageState>({ queryKey, page: 1 });\n const [accumulated, setAccumulated] = useState<Accumulated>({ queryKey, listKey, pages: {} });\n\n // Derived rather than read straight from state: the render that discovers\n // the key change still reaches the query below, and a list with no page 1\n // must never be requested at page 2.\n const page = pageState.queryKey === queryKey ? pageState.page : 1;\n\n if (pageState.queryKey !== queryKey) {\n setPageState({ queryKey, page: 1 });\n }\n\n const { currentData, isLoading, isFetching, error, startedTimeStamp, fulfilledTimeStamp } =\n useGetAssetsQuery(\n {\n ...queryArgs,\n page,\n pageSize: PAGE_SIZE,\n },\n { skip: !enabled }\n );\n\n const isSameQuery = accumulated.queryKey === queryKey;\n\n // `currentData` is only ever the payload for the args just passed, so it\n // belongs to this key at this page — nothing to infer. Its reference is\n // stable per cache entry, so this settles after one extra render.\n if (currentData && (!isSameQuery || accumulated.pages[page] !== currentData.results)) {\n setAccumulated(\n isSameQuery\n ? {\n ...accumulated,\n pages: { ...accumulated.pages, [page]: currentData.results },\n pagination: currentData.pagination,\n }\n : {\n queryKey,\n listKey,\n pages: { [page]: currentData.results },\n pagination: currentData.pagination,\n }\n );\n }\n\n // Merges a refetched earlier page back into the accumulator. Ignores reports\n // for a superseded query, and no-ops when the reference is unchanged so a\n // subscriber re-reporting its already-stored page can't loop.\n const handlePageRefreshed = useCallback<PageRefreshedHandler>(\n (refreshedPage, results) => {\n setAccumulated((prev) => {\n if (prev.queryKey !== queryKey || prev.pages[refreshedPage] === results) {\n return prev;\n }\n\n return { ...prev, pages: { ...prev.pages, [refreshedPage]: results } };\n });\n },\n [queryKey]\n );\n\n // One subscriber per earlier page (the current page is already subscribed by\n // the query above). Keyed by queryKey so a folder/sort/search change remounts\n // them against the new args instead of reusing a stale cache entry.\n // `createElement` rather than JSX so this stays a plain `.ts` hook file.\n const subscribers = createElement(\n Fragment,\n null,\n Array.from({ length: Math.max(0, page - 1) }, (_, index) => index + 1).map((earlierPage) =>\n createElement(PageSubscriber, {\n key: `${queryKey}:${earlierPage}`,\n queryArgs,\n page: earlierPage,\n onRefreshed: handlePageRefreshed,\n })\n )\n );\n\n // Drop the left folder's cached `getAssets` pages when the folder changes.\n //\n // RTK Query 1.9.7 leaves a query's store subscription in place when its last\n // subscriber unmounts while a refetch is still in flight — e.g. a delete\n // invalidates `{Asset, LIST}`, every loaded page starts refetching, and the\n // user navigates away before those settle. Those stale subscriptions make the\n // next `{Asset, LIST}` invalidation (an upload in the new folder) refetch the\n // *previous* folder's pages: one bogus `/upload/files` request per loaded\n // page. There is no public per-entry unsubscribe, so evict the left folder's\n // entries outright — returning reloads from page 1, which the accumulator\n // already does on a list change.\n const dispatch = useDispatch();\n const store = useStore();\n const prevFolderRef = useRef(folder);\n useEffect(() => {\n const prevFolder = prevFolderRef.current;\n prevFolderRef.current = folder;\n if (prevFolder === folder) {\n return;\n }\n\n const apiState = (store.getState() as Record<string, { queries?: Record<string, unknown> }>)[\n uploadApi.reducerPath\n ];\n const removeQueryResult = (\n uploadApi as unknown as {\n internalActions: {\n removeQueryResult: (payload: { queryCacheKey: string }) => { type: string };\n };\n }\n ).internalActions.removeQueryResult;\n\n Object.keys(apiState?.queries ?? {}).forEach((cacheKey) => {\n if (!cacheKey.startsWith('getAssets(')) {\n return;\n }\n let args: { folder?: number | null };\n try {\n args = JSON.parse(cacheKey.slice('getAssets('.length, -1));\n } catch {\n return;\n }\n if (args.folder === prevFolder) {\n dispatch(removeQueryResult({ queryCacheKey: cacheKey }));\n }\n });\n }, [folder, dispatch, store]);\n\n // Until the new list's first page lands the accumulator still holds the\n // previous folder. Reported as loading so the page shows a spinner instead\n // of the outgoing folder's assets under the incoming folder's header.\n const isChangingList = accumulated.listKey !== listKey;\n\n const loadedAssets = useMemo(\n () => (isChangingList ? [] : flattenPages(accumulated.pages)),\n [isChangingList, accumulated.pages]\n );\n\n // Deliberately the live value, not the stale-tolerant one below: paging must\n // never be driven by a total that belongs to the previous query.\n const hasNextPage = currentData ? page < currentData.pagination.pageCount : false;\n\n const completedUploads = useTypedSelector(selectCompletedUploads);\n\n // Only an unfiltered, unsearched list can place an upload locally: deciding\n // whether an asset satisfies a filter means reimplementing the server's\n // filter semantics client-side. Filtered views still refresh on the\n // end-of-batch invalidation, as they did before.\n const canPlaceUploads = !search && (filters?.length ?? 0) === 0;\n\n // When the loaded data came back from a request that started after a given\n // upload — i.e. the server had already stored it when asked. Past that point\n // the list is the authority on whether the asset is there at all, so bridging\n // must stop: otherwise a later delete or move, which refetches without it,\n // would have it re-inserted.\n const listAnsweredAfter =\n fulfilledTimeStamp !== undefined &&\n startedTimeStamp !== undefined &&\n // A refetch in flight leaves `fulfilledTimeStamp` on the previous response,\n // which predates this request — so only settled data counts.\n fulfilledTimeStamp > startedTimeStamp\n ? startedTimeStamp\n : undefined;\n\n const assets = useMemo(() => {\n if (!canPlaceUploads || completedUploads.length === 0) {\n return loadedAssets;\n }\n\n const bridging = completedUploads.filter(\n ({ asset, completedAt }) =>\n getRelationId(asset.folder) === folder &&\n (listAnsweredAfter === undefined || completedAt > listAnsweredAfter)\n );\n\n return mergeUploadedAssets({\n assets: loadedAssets,\n uploaded: bridging.map(({ asset }) => asset),\n sort,\n hasNextPage,\n });\n }, [\n canPlaceUploads,\n completedUploads,\n loadedAssets,\n folder,\n sort,\n hasNextPage,\n listAnsweredAfter,\n ]);\n const isFetchingMore = isFetching && page > 1;\n\n const fetchNextPage = useCallback(() => {\n setPageState((prev) => ({\n queryKey,\n page: (prev.queryKey === queryKey ? prev.page : 1) + 1,\n }));\n }, [queryKey]);\n\n if (!enabled) {\n return {\n assets: [] as File[],\n // No list to keep fresh while disabled, so nothing to subscribe.\n subscribers: null,\n pagination: undefined,\n isLoading: false,\n isFetchingMore: false,\n hasNextPage: false,\n fetchNextPage,\n error: undefined,\n };\n }\n\n return {\n assets,\n subscribers,\n // Falls back to the accumulated total so a consumer showing the count\n // doesn't flash zero mid-transition, matching the possibly stale `assets`.\n pagination: currentData?.pagination ?? accumulated.pagination,\n isLoading: isLoading || isChangingList,\n isFetchingMore,\n hasNextPage,\n fetchNextPage,\n error,\n };\n};\n\nexport { useInfiniteAssets };\nexport { PAGE_SIZE };\n"],"names":["PAGE_SIZE","flattenPages","pages","byId","Map","page","Object","keys","map","Number","sort","a","b","asset","set","id","values","PageSubscriber","queryArgs","onRefreshed","currentData","useGetAssetsQuery","pageSize","results","useEffect","useInfiniteAssets","folder","search","filters","enabled","queryKey","JSON","stringify","listKey","pageState","setPageState","useState","accumulated","setAccumulated","isLoading","isFetching","error","startedTimeStamp","fulfilledTimeStamp","skip","isSameQuery","pagination","handlePageRefreshed","useCallback","refreshedPage","prev","subscribers","createElement","Fragment","Array","from","length","Math","max","_","index","earlierPage","key","dispatch","useDispatch","store","useStore","prevFolderRef","useRef","prevFolder","current","apiState","getState","uploadApi","reducerPath","removeQueryResult","internalActions","queries","forEach","cacheKey","startsWith","args","parse","slice","queryCacheKey","isChangingList","loadedAssets","useMemo","hasNextPage","pageCount","completedUploads","useTypedSelector","selectCompletedUploads","canPlaceUploads","listAnsweredAfter","undefined","assets","bridging","filter","completedAt","getRelationId","mergeUploadedAssets","uploaded","isFetchingMore","fetchNextPage"],"mappings":"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"}
|