@strapi/upload 5.55.1 → 5.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/hooks/useAIMetadataAvailability.js +14 -0
- package/dist/admin/hooks/useAIMetadataAvailability.js.map +1 -0
- package/dist/admin/hooks/useAIMetadataAvailability.mjs +12 -0
- package/dist/admin/hooks/useAIMetadataAvailability.mjs.map +1 -0
- package/dist/admin/hooks/useAIMetadataEnabled.js +7 -5
- package/dist/admin/hooks/useAIMetadataEnabled.js.map +1 -1
- package/dist/admin/hooks/useAIMetadataEnabled.mjs +7 -5
- package/dist/admin/hooks/useAIMetadataEnabled.mjs.map +1 -1
- package/dist/admin/hooks/useTracking.js +4 -10
- package/dist/admin/hooks/useTracking.js.map +1 -1
- package/dist/admin/hooks/useTracking.mjs +5 -10
- package/dist/admin/hooks/useTracking.mjs.map +1 -1
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.js +14 -0
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.js.map +1 -0
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.mjs +12 -0
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.mjs.map +1 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.js +23 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.js.map +1 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.mjs +21 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.mjs.map +1 -0
- package/dist/admin/legacy/hooks/useTracking.js +5 -3
- package/dist/admin/legacy/hooks/useTracking.js.map +1 -1
- package/dist/admin/legacy/hooks/useTracking.mjs +5 -3
- package/dist/admin/legacy/hooks/useTracking.mjs.map +1 -1
- package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.js +2 -1
- package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.js.map +1 -1
- package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.mjs +2 -1
- package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.mjs.map +1 -1
- package/dist/admin/legacy/pages/App/MediaLibrary.js +6 -5
- package/dist/admin/legacy/pages/App/MediaLibrary.js.map +1 -1
- package/dist/admin/legacy/pages/App/MediaLibrary.mjs +6 -5
- package/dist/admin/legacy/pages/App/MediaLibrary.mjs.map +1 -1
- package/dist/admin/pages/Assets/AssetsPage.js +24 -12
- package/dist/admin/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/pages/Assets/AssetsPage.mjs +25 -13
- package/dist/admin/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetActions.js +326 -0
- package/dist/admin/pages/Assets/components/AssetActions.js.map +1 -0
- package/dist/admin/pages/Assets/components/AssetActions.mjs +324 -0
- package/dist/admin/pages/Assets/components/AssetActions.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/AssetActionsMenu.js +28 -309
- package/dist/admin/pages/Assets/components/AssetActionsMenu.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetActionsMenu.mjs +31 -312
- package/dist/admin/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetContextMenu.js +79 -0
- package/dist/admin/pages/Assets/components/AssetContextMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/AssetContextMenu.mjs +77 -0
- package/dist/admin/pages/Assets/components/AssetContextMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.js +87 -5
- package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +89 -7
- package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +3 -3
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +3 -3
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.js +62 -5
- package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.mjs +60 -6
- package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsGrid.js +27 -4
- package/dist/admin/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsGrid.mjs +27 -4
- package/dist/admin/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsTable.js +27 -4
- package/dist/admin/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsTable.mjs +27 -4
- package/dist/admin/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/CursorAnchoredMenu.js +77 -0
- package/dist/admin/pages/Assets/components/CursorAnchoredMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/CursorAnchoredMenu.mjs +75 -0
- package/dist/admin/pages/Assets/components/CursorAnchoredMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.js +4 -2
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.js.map +1 -1
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.mjs +4 -2
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/FolderActions.js +139 -0
- package/dist/admin/pages/Assets/components/FolderActions.js.map +1 -0
- package/dist/admin/pages/Assets/components/FolderActions.mjs +137 -0
- package/dist/admin/pages/Assets/components/FolderActions.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/FolderActionsMenu.js +25 -119
- package/dist/admin/pages/Assets/components/FolderActionsMenu.js.map +1 -1
- package/dist/admin/pages/Assets/components/FolderActionsMenu.mjs +27 -121
- package/dist/admin/pages/Assets/components/FolderActionsMenu.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/FolderContextMenu.js +67 -0
- package/dist/admin/pages/Assets/components/FolderContextMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/FolderContextMenu.mjs +65 -0
- package/dist/admin/pages/Assets/components/FolderContextMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.js +139 -45
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.mjs +140 -47
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.js +115 -0
- package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.mjs +113 -0
- package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/ItemContextMenu.js +125 -0
- package/dist/admin/pages/Assets/components/ItemContextMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/ItemContextMenu.mjs +122 -0
- package/dist/admin/pages/Assets/components/ItemContextMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/MainAreaContextMenu.js +88 -63
- package/dist/admin/pages/Assets/components/MainAreaContextMenu.js.map +1 -1
- package/dist/admin/pages/Assets/components/MainAreaContextMenu.mjs +88 -63
- package/dist/admin/pages/Assets/components/MainAreaContextMenu.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/SelectionActions.js +111 -0
- package/dist/admin/pages/Assets/components/SelectionActions.js.map +1 -0
- package/dist/admin/pages/Assets/components/SelectionActions.mjs +109 -0
- package/dist/admin/pages/Assets/components/SelectionActions.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/SelectionContextMenu.js +82 -0
- package/dist/admin/pages/Assets/components/SelectionContextMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/SelectionContextMenu.mjs +80 -0
- package/dist/admin/pages/Assets/components/SelectionContextMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/hooks/useAssetSelection.js +10 -0
- package/dist/admin/pages/Assets/hooks/useAssetSelection.js.map +1 -1
- package/dist/admin/pages/Assets/hooks/useAssetSelection.mjs +10 -0
- package/dist/admin/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.js +10 -3
- package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs +10 -3
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
- package/dist/admin/services/api.js.map +1 -1
- package/dist/admin/services/api.mjs.map +1 -1
- package/dist/admin/src/hooks/useAIMetadataAvailability.d.ts +5 -0
- package/dist/admin/src/hooks/useAIMetadataEnabled.d.ts +5 -3
- package/dist/admin/src/hooks/useTracking.d.ts +0 -7
- package/dist/admin/src/legacy/components/EditAssetDialog/PreviewBox/PreviewComponents.d.ts +2 -2
- package/dist/admin/src/legacy/hooks/useAIMetadataAvailability.d.ts +5 -0
- package/dist/admin/src/legacy/hooks/useAIMetadataEnabled.d.ts +12 -0
- package/dist/admin/src/legacy/hooks/useSettings.d.ts +1 -0
- package/dist/admin/src/pages/Assets/components/ActionsMenuContent.d.ts +2 -2
- package/dist/admin/src/pages/Assets/components/AssetActions.d.ts +70 -0
- package/dist/admin/src/pages/Assets/components/AssetActionsMenu.d.ts +5 -14
- package/dist/admin/src/pages/Assets/components/AssetContextMenu.d.ts +27 -0
- package/dist/admin/src/pages/Assets/components/AssetDetails/useCropImg.d.ts +16 -0
- package/dist/admin/src/pages/Assets/components/CursorAnchoredMenu.d.ts +32 -0
- package/dist/admin/src/pages/Assets/components/FolderActions.d.ts +48 -0
- package/dist/admin/src/pages/Assets/components/FolderActionsMenu.d.ts +4 -8
- package/dist/admin/src/pages/Assets/components/FolderContextMenu.d.ts +21 -0
- package/dist/admin/src/pages/Assets/components/FolderTree/FolderTree.d.ts +5 -1
- package/dist/admin/src/pages/Assets/components/FolderTree/FolderTreeRowMenu.d.ts +25 -0
- package/dist/admin/src/pages/Assets/components/ItemContextMenu.d.ts +53 -0
- package/dist/admin/src/pages/Assets/components/MainAreaContextMenu.d.ts +15 -1
- package/dist/admin/src/pages/Assets/components/SelectionActions.d.ts +40 -0
- package/dist/admin/src/pages/Assets/components/SelectionContextMenu.d.ts +25 -0
- package/dist/admin/src/pages/Assets/hooks/useAssetSelection.d.ts +5 -0
- package/dist/admin/src/utils/mediaLibraryVersion.d.ts +18 -0
- package/dist/admin/translations/en.json.js +9 -3
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +9 -3
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/mediaLibraryVersion.js +24 -0
- package/dist/admin/utils/mediaLibraryVersion.js.map +1 -0
- package/dist/admin/utils/mediaLibraryVersion.mjs +20 -0
- package/dist/admin/utils/mediaLibraryVersion.mjs.map +1 -0
- package/dist/server/bootstrap.js +7 -0
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/server/bootstrap.mjs +7 -0
- package/dist/server/bootstrap.mjs.map +1 -1
- package/dist/server/controllers/admin-file.js +2 -2
- package/dist/server/controllers/admin-file.js.map +1 -1
- package/dist/server/controllers/admin-file.mjs +2 -2
- package/dist/server/controllers/admin-file.mjs.map +1 -1
- package/dist/server/controllers/admin-folder-file.js +51 -4
- package/dist/server/controllers/admin-folder-file.js.map +1 -1
- package/dist/server/controllers/admin-folder-file.mjs +52 -5
- package/dist/server/controllers/admin-folder-file.mjs.map +1 -1
- package/dist/server/controllers/admin-folder.js +5 -5
- package/dist/server/controllers/admin-folder.js.map +1 -1
- package/dist/server/controllers/admin-folder.mjs +5 -5
- package/dist/server/controllers/admin-folder.mjs.map +1 -1
- package/dist/server/controllers/admin-settings.js +2 -1
- package/dist/server/controllers/admin-settings.js.map +1 -1
- package/dist/server/controllers/admin-settings.mjs +2 -1
- package/dist/server/controllers/admin-settings.mjs.map +1 -1
- package/dist/server/controllers/content-api.js +30 -7
- package/dist/server/controllers/content-api.js.map +1 -1
- package/dist/server/controllers/content-api.mjs +30 -7
- package/dist/server/controllers/content-api.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/folder-file.js +4 -4
- package/dist/server/controllers/validation/admin/folder-file.js.map +1 -1
- package/dist/server/controllers/validation/admin/folder-file.mjs +2 -2
- package/dist/server/controllers/validation/admin/folder-file.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/folder.js +3 -4
- package/dist/server/controllers/validation/admin/folder.js.map +1 -1
- package/dist/server/controllers/validation/admin/folder.mjs +3 -4
- package/dist/server/controllers/validation/admin/folder.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/upload.js +1 -2
- package/dist/server/controllers/validation/admin/upload.js.map +1 -1
- package/dist/server/controllers/validation/admin/upload.mjs +1 -2
- package/dist/server/controllers/validation/admin/upload.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/utils.js +1 -2
- package/dist/server/controllers/validation/admin/utils.js.map +1 -1
- package/dist/server/controllers/validation/admin/utils.mjs +1 -2
- package/dist/server/controllers/validation/admin/utils.mjs.map +1 -1
- package/dist/server/errors.js +12 -0
- package/dist/server/errors.js.map +1 -0
- package/dist/server/errors.mjs +10 -0
- package/dist/server/errors.mjs.map +1 -0
- package/dist/server/migrations/unsign-richtext-and-blocks-urls.js +2 -2
- package/dist/server/migrations/unsign-richtext-and-blocks-urls.js.map +1 -1
- package/dist/server/migrations/unsign-richtext-and-blocks-urls.mjs +1 -1
- package/dist/server/migrations/unsign-richtext-and-blocks-urls.mjs.map +1 -1
- package/dist/server/routes/content-api.js +3 -3
- package/dist/server/routes/content-api.js.map +1 -1
- package/dist/server/routes/content-api.mjs +3 -3
- package/dist/server/routes/content-api.mjs.map +1 -1
- package/dist/server/routes/validation/upload.js +8 -0
- package/dist/server/routes/validation/upload.js.map +1 -1
- package/dist/server/routes/validation/upload.mjs +8 -0
- package/dist/server/routes/validation/upload.mjs.map +1 -1
- package/dist/server/services/ai-metadata-provider.js +83 -0
- package/dist/server/services/ai-metadata-provider.js.map +1 -0
- package/dist/server/services/ai-metadata-provider.mjs +61 -0
- package/dist/server/services/ai-metadata-provider.mjs.map +1 -0
- package/dist/server/services/ai-metadata-strapi-managed.js +48 -0
- package/dist/server/services/ai-metadata-strapi-managed.js.map +1 -0
- package/dist/server/services/ai-metadata-strapi-managed.mjs +46 -0
- package/dist/server/services/ai-metadata-strapi-managed.mjs.map +1 -0
- package/dist/server/services/ai-metadata.js +5 -58
- package/dist/server/services/ai-metadata.js.map +1 -1
- package/dist/server/services/ai-metadata.mjs +6 -39
- package/dist/server/services/ai-metadata.mjs.map +1 -1
- package/dist/server/services/api-upload-folder.js +3 -3
- package/dist/server/services/api-upload-folder.js.map +1 -1
- package/dist/server/services/api-upload-folder.mjs +3 -3
- package/dist/server/services/api-upload-folder.mjs.map +1 -1
- package/dist/server/services/extensions/utils.js +19 -15
- package/dist/server/services/extensions/utils.js.map +1 -1
- package/dist/server/services/extensions/utils.mjs +19 -15
- package/dist/server/services/extensions/utils.mjs.map +1 -1
- package/dist/server/services/file.js +2 -2
- package/dist/server/services/file.js.map +1 -1
- package/dist/server/services/file.mjs +1 -1
- package/dist/server/services/file.mjs.map +1 -1
- package/dist/server/services/folder.js +9 -6
- package/dist/server/services/folder.js.map +1 -1
- package/dist/server/services/folder.mjs +9 -6
- package/dist/server/services/folder.mjs.map +1 -1
- package/dist/server/services/index.js +3 -1
- package/dist/server/services/index.js.map +1 -1
- package/dist/server/services/index.mjs +3 -1
- package/dist/server/services/index.mjs.map +1 -1
- package/dist/server/services/metrics.js +1 -1
- package/dist/server/services/metrics.js.map +1 -1
- package/dist/server/services/metrics.mjs +1 -1
- package/dist/server/services/metrics.mjs.map +1 -1
- package/dist/server/services/provider.js +3 -4
- package/dist/server/services/provider.js.map +1 -1
- package/dist/server/services/provider.mjs +3 -4
- package/dist/server/services/provider.mjs.map +1 -1
- package/dist/server/services/upload.js +5 -6
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs +4 -5
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/services/weekly-metrics.js +2 -2
- package/dist/server/services/weekly-metrics.js.map +1 -1
- package/dist/server/services/weekly-metrics.mjs +2 -2
- package/dist/server/services/weekly-metrics.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-folder-file.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-folder.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
- package/dist/server/src/controllers/content-api.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/folder.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/upload.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/utils.d.ts.map +1 -1
- package/dist/server/src/errors.d.ts +6 -0
- package/dist/server/src/errors.d.ts.map +1 -0
- package/dist/server/src/index.d.ts +6 -1
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/mcp/ambient-instance.d.ts +3 -1
- package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -1
- package/dist/server/src/routes/validation/upload.d.ts +4 -0
- package/dist/server/src/routes/validation/upload.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata-provider.d.ts +26 -0
- package/dist/server/src/services/ai-metadata-provider.d.ts.map +1 -0
- package/dist/server/src/services/ai-metadata-strapi-managed.d.ts +7 -0
- package/dist/server/src/services/ai-metadata-strapi-managed.d.ts.map +1 -0
- package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
- package/dist/server/src/services/extensions/utils.d.ts +33 -1
- package/dist/server/src/services/extensions/utils.d.ts.map +1 -1
- package/dist/server/src/services/folder.d.ts +5 -2
- package/dist/server/src/services/folder.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +6 -1
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/provider.d.ts.map +1 -1
- package/dist/server/src/services/upload.d.ts.map +1 -1
- package/dist/server/src/utils/images.d.ts +2 -2
- package/dist/server/src/utils/images.d.ts.map +1 -1
- package/dist/server/src/utils/index.d.ts +2 -0
- package/dist/server/src/utils/index.d.ts.map +1 -1
- package/dist/server/utils/images.js +6 -7
- package/dist/server/utils/images.js.map +1 -1
- package/dist/server/utils/images.mjs +6 -7
- package/dist/server/utils/images.mjs.map +1 -1
- package/dist/server/utils/index.js.map +1 -1
- package/dist/server/utils/index.mjs.map +1 -1
- package/dist/shared/contracts/settings.d.ts +6 -0
- package/package.json +11 -11
- package/dist/admin/legacy/hooks/useAiAvailability.js +0 -22
- package/dist/admin/legacy/hooks/useAiAvailability.js.map +0 -1
- package/dist/admin/legacy/hooks/useAiAvailability.mjs +0 -20
- package/dist/admin/legacy/hooks/useAiAvailability.mjs.map +0 -1
- package/dist/admin/src/legacy/hooks/useAiAvailability.d.ts +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCropImg.js","sources":["../../../../../../admin/src/pages/Assets/components/AssetDetails/useCropImg.ts"],"sourcesContent":["import * as React from 'react';\n\nconst QUALITY = 1;\n\ninterface NaturalSize {\n width: number;\n height: number;\n}\n\nexport interface CropRect {\n /** Origin in natural-px from the image top-left. */\n x: number;\n y: number;\n /** Crop area dimensions in natural px. */\n width: number;\n height: number;\n}\n\n/**\n * Resolve a corner-resize drag into a crop rect, keeping the opposite corner\n * (`anchor`) fixed. When `aspectRatio` is set, one dimension is snapped to the\n * ratio first (driven by whichever axis the pointer pushed furthest), then the\n * position is derived from the anchor — so a locked resize never drifts the\n * anchored corner. Pure (no clamping to image bounds; the caller clamps).\n */\nexport const resolveCornerResize = ({\n anchorX,\n anchorY,\n point,\n aspectRatio,\n}: {\n anchorX: number;\n anchorY: number;\n point: { x: number; y: number };\n aspectRatio: number | null;\n}): CropRect => {\n let width = Math.abs(point.x - anchorX);\n let height = Math.abs(point.y - anchorY);\n\n if (aspectRatio) {\n if (width / aspectRatio >= height) {\n height = width / aspectRatio;\n } else {\n width = height * aspectRatio;\n }\n }\n\n const x = point.x < anchorX ? anchorX - width : anchorX;\n const y = point.y < anchorY ? anchorY - height : anchorY;\n\n return { x, y, width, height };\n};\n\n/**\n * Custom crop state + file producer. Replaces a cropperjs-based version: the\n * crop rectangle is stored in natural-image pixels (so it stays locked across\n * window resizes), and `AssetCropEditor` overlays a div sized by\n * (cropNatural × displayRatio). produceFile draws the natural-px crop region\n * straight to a canvas.\n */\nexport const useCropImg = () => {\n const [naturalSize, setNaturalSize] = React.useState<NaturalSize>({ width: 0, height: 0 });\n const [crop, setCrop] = React.useState<CropRect>({ x: 0, y: 0, width: 0, height: 0 });\n const [aspectRatio, setAspectRatioState] = React.useState<number | null>(null);\n const imageRef = React.useRef<HTMLImageElement | null>(null);\n\n /**\n * Call from the <img> onLoad handler. Captures the natural dimensions and\n * seeds the crop to the full image (auto-crop area = 1).\n */\n const init = React.useCallback((image: HTMLImageElement) => {\n imageRef.current = image;\n const next: NaturalSize = { width: image.naturalWidth, height: image.naturalHeight };\n setNaturalSize(next);\n setCrop({ x: 0, y: 0, width: next.width, height: next.height });\n }, []);\n\n const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\n /** Update crop dimensions, clamping to remain inside the image bounds. */\n const setCropSize = React.useCallback(\n (next: Partial<Pick<CropRect, 'width' | 'height'>>) => {\n setCrop((prev) => {\n const maxW = naturalSize.width - prev.x;\n const maxH = naturalSize.height - prev.y;\n let width = next.width !== undefined ? clamp(next.width, 1, maxW) : prev.width;\n let height = next.height !== undefined ? clamp(next.height, 1, maxH) : prev.height;\n\n if (aspectRatio) {\n // When aspect locked, the axis the user is editing wins; the other\n // axis follows. If both changed in one call, prefer width.\n if (next.width !== undefined) {\n height = clamp(width / aspectRatio, 1, maxH);\n } else if (next.height !== undefined) {\n width = clamp(height * aspectRatio, 1, maxW);\n }\n }\n\n return { ...prev, width, height };\n });\n },\n [naturalSize.width, naturalSize.height, aspectRatio]\n );\n\n /** Update crop origin, clamping so the crop stays inside the image. */\n const setCropPosition = React.useCallback(\n (next: Partial<Pick<CropRect, 'x' | 'y'>>) => {\n setCrop((prev) => {\n const x = next.x !== undefined ? clamp(next.x, 0, naturalSize.width - prev.width) : prev.x;\n const y =\n next.y !== undefined ? clamp(next.y, 0, naturalSize.height - prev.height) : prev.y;\n return { ...prev, x, y };\n });\n },\n [naturalSize.width, naturalSize.height]\n );\n\n /** Lock or release the crop aspect ratio. Pass `null` to free it. */\n const setAspectRatio = React.useCallback(\n (ratio: number | null) => {\n setAspectRatioState(ratio);\n if (ratio) {\n setCrop((prev) => {\n // Bring the current rect into the requested ratio without escaping\n // the image bounds.\n const maxW = naturalSize.width - prev.x;\n const maxH = naturalSize.height - prev.y;\n let width = prev.width;\n let height = width / ratio;\n if (height > maxH) {\n height = maxH;\n width = height * ratio;\n }\n if (width > maxW) {\n width = maxW;\n height = width / ratio;\n }\n return { ...prev, width: Math.round(width), height: Math.round(height) };\n });\n }\n },\n [naturalSize.width, naturalSize.height]\n );\n\n /**\n * Draw the crop region from the loaded image element straight to a canvas\n * and produce a File. The image must be CORS-clean (set crossOrigin on the\n * <img>); otherwise canvas.toBlob throws on tainted canvas.\n */\n const produceFile = React.useCallback(\n (name: string, mimeType: string, lastModifiedDate?: string): Promise<globalThis.File> =>\n new Promise((resolve, reject) => {\n const image = imageRef.current;\n if (!image) {\n reject(new Error('Image not ready: call init() before produceFile().'));\n return;\n }\n const canvas = document.createElement('canvas');\n canvas.width = Math.max(1, Math.round(crop.width));\n canvas.height = Math.max(1, Math.round(crop.height));\n const context = canvas.getContext('2d');\n if (!context) {\n reject(new Error('Could not get a 2D canvas context to crop the image.'));\n return;\n }\n context.drawImage(\n image,\n crop.x,\n crop.y,\n crop.width,\n crop.height,\n 0,\n 0,\n canvas.width,\n canvas.height\n );\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error('Could not export the cropped image to a blob.'));\n return;\n }\n resolve(\n new File([blob], name, {\n type: mimeType,\n lastModified: lastModifiedDate ? new Date(lastModifiedDate).getTime() : Date.now(),\n })\n );\n },\n mimeType,\n QUALITY\n );\n }),\n [crop.x, crop.y, crop.width, crop.height]\n );\n\n return {\n init,\n crop,\n naturalSize,\n aspectRatio,\n setCropSize,\n setCropPosition,\n setAspectRatio,\n produceFile,\n // Round-aware getters for the px inputs.\n width: Math.round(crop.width),\n height: Math.round(crop.height),\n };\n};\n"],"names":["QUALITY","resolveCornerResize","anchorX","anchorY","point","aspectRatio","width","Math","abs","x","height","y","useCropImg","naturalSize","setNaturalSize","React","useState","crop","setCrop","setAspectRatioState","imageRef","useRef","init","useCallback","image","current","next","naturalWidth","naturalHeight","clamp","value","min","max","setCropSize","prev","maxW","maxH","undefined","setCropPosition","setAspectRatio","ratio","round","produceFile","name","mimeType","lastModifiedDate","Promise","resolve","reject","Error","canvas","document","createElement","context","getContext","drawImage","toBlob","blob","File","type","lastModified","Date","getTime","now"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useCropImg.js","sources":["../../../../../../admin/src/pages/Assets/components/AssetDetails/useCropImg.ts"],"sourcesContent":["import * as React from 'react';\n\nconst QUALITY = 1;\n\ninterface NaturalSize {\n width: number;\n height: number;\n}\n\nexport interface CropRect {\n /** Origin in natural-px from the image top-left. */\n x: number;\n y: number;\n /** Crop area dimensions in natural px. */\n width: number;\n height: number;\n}\n\n/**\n * Resolve a corner-resize drag into a crop rect, keeping the opposite corner\n * (`anchor`) fixed. When `aspectRatio` is set, one dimension is snapped to the\n * ratio first (driven by whichever axis the pointer pushed furthest), then the\n * position is derived from the anchor — so a locked resize never drifts the\n * anchored corner. Pure (no clamping to image bounds; the caller clamps).\n */\nexport const resolveCornerResize = ({\n anchorX,\n anchorY,\n point,\n aspectRatio,\n}: {\n anchorX: number;\n anchorY: number;\n point: { x: number; y: number };\n aspectRatio: number | null;\n}): CropRect => {\n let width = Math.abs(point.x - anchorX);\n let height = Math.abs(point.y - anchorY);\n\n if (aspectRatio) {\n if (width / aspectRatio >= height) {\n height = width / aspectRatio;\n } else {\n width = height * aspectRatio;\n }\n }\n\n const x = point.x < anchorX ? anchorX - width : anchorX;\n const y = point.y < anchorY ? anchorY - height : anchorY;\n\n return { x, y, width, height };\n};\n\nexport type Rotation = 0 | 90 | 180 | 270;\n\n/**\n * Remap a crop rect through a quarter-turn. `size` is the image size *before* the\n * turn, so the returned rect is expressed against the swapped size for 90/270.\n * Pure: the caller swaps `naturalSize` to match.\n */\nexport const rotateCropRect = (\n crop: CropRect,\n direction: 'left' | 'right',\n size: NaturalSize\n): CropRect =>\n direction === 'right'\n ? {\n x: size.height - (crop.y + crop.height),\n y: crop.x,\n width: crop.height,\n height: crop.width,\n }\n : {\n x: crop.y,\n y: size.width - (crop.x + crop.width),\n width: crop.height,\n height: crop.width,\n };\n\n/**\n * The crop rect expressed against the *unrotated* source, by undoing each quarter-turn\n * already applied. `produceFile` needs it to know which source pixels to sample.\n */\nexport const sourceCropRect = (\n crop: CropRect,\n rotation: Rotation,\n rotatedSize: NaturalSize\n): CropRect => {\n let rect = crop;\n let size = rotatedSize;\n\n for (let turn = 0; turn < rotation / 90; turn += 1) {\n rect = rotateCropRect(rect, 'left', size);\n size = { width: size.height, height: size.width };\n }\n\n return rect;\n};\n\n/** Advance a rotation by one quarter-turn, wrapping at 360. */\nexport const nextRotation = (current: Rotation, direction: 'left' | 'right'): Rotation =>\n ((current + (direction === 'right' ? 90 : 270)) % 360) as Rotation;\n\n/**\n * Custom crop state + file producer. Replaces a cropperjs-based version: the\n * crop rectangle is stored in natural-image pixels (so it stays locked across\n * window resizes), and `AssetCropEditor` overlays a div sized by\n * (cropNatural × displayRatio). produceFile draws the natural-px crop region\n * straight to a canvas.\n */\nexport const useCropImg = () => {\n const [naturalSize, setNaturalSize] = React.useState<NaturalSize>({ width: 0, height: 0 });\n const [crop, setCrop] = React.useState<CropRect>({ x: 0, y: 0, width: 0, height: 0 });\n const [aspectRatio, setAspectRatioState] = React.useState<number | null>(null);\n const [rotation, setRotation] = React.useState<Rotation>(0);\n const imageRef = React.useRef<HTMLImageElement | null>(null);\n\n /**\n * Call from the <img> onLoad handler. Captures the natural dimensions and\n * seeds the crop to the full image (auto-crop area = 1).\n */\n const init = React.useCallback((image: HTMLImageElement) => {\n imageRef.current = image;\n const next: NaturalSize = { width: image.naturalWidth, height: image.naturalHeight };\n setNaturalSize(next);\n setCrop({ x: 0, y: 0, width: next.width, height: next.height });\n setRotation(0);\n }, []);\n\n /**\n * Turn the image a quarter-turn. `naturalSize` swaps and the crop rect is remapped\n * so the same pixels stay selected; nothing is reset.\n */\n const rotate = React.useCallback(\n (direction: 'left' | 'right') => {\n setCrop((prev) => rotateCropRect(prev, direction, naturalSize));\n setNaturalSize((prev) => ({ width: prev.height, height: prev.width }));\n setRotation((prev) => nextRotation(prev, direction));\n // A locked ratio describes the orientation it was locked in, so a quarter\n // turn inverts it. Without this it keeps enforcing the pre-rotation shape\n // and fights every later resize.\n setAspectRatioState((prev) => (prev ? 1 / prev : prev));\n },\n [naturalSize]\n );\n\n const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\n /** Update crop dimensions, clamping to remain inside the image bounds. */\n const setCropSize = React.useCallback(\n (next: Partial<Pick<CropRect, 'width' | 'height'>>) => {\n setCrop((prev) => {\n const maxW = naturalSize.width - prev.x;\n const maxH = naturalSize.height - prev.y;\n let width = next.width !== undefined ? clamp(next.width, 1, maxW) : prev.width;\n let height = next.height !== undefined ? clamp(next.height, 1, maxH) : prev.height;\n\n if (aspectRatio) {\n // When aspect locked, the axis the user is editing wins; the other\n // axis follows. If both changed in one call, prefer width.\n if (next.width !== undefined) {\n height = clamp(width / aspectRatio, 1, maxH);\n } else if (next.height !== undefined) {\n width = clamp(height * aspectRatio, 1, maxW);\n }\n }\n\n return { ...prev, width, height };\n });\n },\n [naturalSize.width, naturalSize.height, aspectRatio]\n );\n\n /** Update crop origin, clamping so the crop stays inside the image. */\n const setCropPosition = React.useCallback(\n (next: Partial<Pick<CropRect, 'x' | 'y'>>) => {\n setCrop((prev) => {\n const x = next.x !== undefined ? clamp(next.x, 0, naturalSize.width - prev.width) : prev.x;\n const y =\n next.y !== undefined ? clamp(next.y, 0, naturalSize.height - prev.height) : prev.y;\n return { ...prev, x, y };\n });\n },\n [naturalSize.width, naturalSize.height]\n );\n\n /** Lock or release the crop aspect ratio. Pass `null` to free it. */\n const setAspectRatio = React.useCallback(\n (ratio: number | null) => {\n setAspectRatioState(ratio);\n if (ratio) {\n setCrop((prev) => {\n // Bring the current rect into the requested ratio without escaping\n // the image bounds.\n const maxW = naturalSize.width - prev.x;\n const maxH = naturalSize.height - prev.y;\n let width = prev.width;\n let height = width / ratio;\n if (height > maxH) {\n height = maxH;\n width = height * ratio;\n }\n if (width > maxW) {\n width = maxW;\n height = width / ratio;\n }\n return { ...prev, width: Math.round(width), height: Math.round(height) };\n });\n }\n },\n [naturalSize.width, naturalSize.height]\n );\n\n /**\n * Draw the crop region from the loaded image element straight to a canvas\n * and produce a File. The image must be CORS-clean (set crossOrigin on the\n * <img>); otherwise canvas.toBlob throws on tainted canvas.\n */\n const produceFile = React.useCallback(\n (name: string, mimeType: string, lastModifiedDate?: string): Promise<globalThis.File> =>\n new Promise((resolve, reject) => {\n const image = imageRef.current;\n if (!image) {\n reject(new Error('Image not ready: call init() before produceFile().'));\n return;\n }\n const canvas = document.createElement('canvas');\n canvas.width = Math.max(1, Math.round(crop.width));\n canvas.height = Math.max(1, Math.round(crop.height));\n const context = canvas.getContext('2d');\n if (!context) {\n reject(new Error('Could not get a 2D canvas context to crop the image.'));\n return;\n }\n const source = sourceCropRect(crop, rotation, naturalSize);\n\n context.translate(canvas.width / 2, canvas.height / 2);\n context.rotate((rotation * Math.PI) / 180);\n context.drawImage(\n image,\n source.x,\n source.y,\n source.width,\n source.height,\n -source.width / 2,\n -source.height / 2,\n source.width,\n source.height\n );\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error('Could not export the cropped image to a blob.'));\n return;\n }\n resolve(\n new File([blob], name, {\n type: mimeType,\n lastModified: lastModifiedDate ? new Date(lastModifiedDate).getTime() : Date.now(),\n })\n );\n },\n mimeType,\n QUALITY\n );\n }),\n [crop, rotation, naturalSize]\n );\n\n return {\n init,\n crop,\n naturalSize,\n rotation,\n rotate,\n aspectRatio,\n setCropSize,\n setCropPosition,\n setAspectRatio,\n produceFile,\n // Round-aware getters for the px inputs.\n width: Math.round(crop.width),\n height: Math.round(crop.height),\n };\n};\n"],"names":["QUALITY","resolveCornerResize","anchorX","anchorY","point","aspectRatio","width","Math","abs","x","height","y","rotateCropRect","crop","direction","size","sourceCropRect","rotation","rotatedSize","rect","turn","nextRotation","current","useCropImg","naturalSize","setNaturalSize","React","useState","setCrop","setAspectRatioState","setRotation","imageRef","useRef","init","useCallback","image","next","naturalWidth","naturalHeight","rotate","prev","clamp","value","min","max","setCropSize","maxW","maxH","undefined","setCropPosition","setAspectRatio","ratio","round","produceFile","name","mimeType","lastModifiedDate","Promise","resolve","reject","Error","canvas","document","createElement","context","getContext","source","translate","PI","drawImage","toBlob","blob","File","type","lastModified","Date","getTime","now"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAEA,MAAMA,OAAAA,GAAU,CAAA;AAgBhB;;;;;;AAMC,IACM,MAAMC,mBAAAA,GAAsB,CAAC,EAClCC,OAAO,EACPC,OAAO,EACPC,KAAK,EACLC,WAAW,EAMZ,GAAA;AACC,IAAA,IAAIC,QAAQC,IAAAA,CAAKC,GAAG,CAACJ,KAAAA,CAAMK,CAAC,GAAGP,OAAAA,CAAAA;AAC/B,IAAA,IAAIQ,SAASH,IAAAA,CAAKC,GAAG,CAACJ,KAAAA,CAAMO,CAAC,GAAGR,OAAAA,CAAAA;AAEhC,IAAA,IAAIE,WAAAA,EAAa;QACf,IAAIC,KAAAA,GAAQD,eAAeK,MAAAA,EAAQ;AACjCA,YAAAA,MAAAA,GAASJ,KAAAA,GAAQD,WAAAA;QACnB,CAAA,MAAO;AACLC,YAAAA,KAAAA,GAAQI,MAAAA,GAASL,WAAAA;AACnB,QAAA;AACF,IAAA;AAEA,IAAA,MAAMI,IAAIL,KAAAA,CAAMK,CAAC,GAAGP,OAAAA,GAAUA,UAAUI,KAAAA,GAAQJ,OAAAA;AAChD,IAAA,MAAMS,IAAIP,KAAAA,CAAMO,CAAC,GAAGR,OAAAA,GAAUA,UAAUO,MAAAA,GAASP,OAAAA;IAEjD,OAAO;AAAEM,QAAAA,CAAAA;AAAGE,QAAAA,CAAAA;AAAGL,QAAAA,KAAAA;AAAOI,QAAAA;AAAO,KAAA;AAC/B;AAIA;;;;UAKaE,cAAAA,GAAiB,CAC5BC,MACAC,SAAAA,EACAC,IAAAA,GAEAD,cAAc,OAAA,GACV;QACEL,CAAAA,EAAGM,IAAAA,CAAKL,MAAM,IAAIG,KAAKF,CAAC,GAAGE,IAAAA,CAAKH,MAAM,CAAD;AACrCC,QAAAA,CAAAA,EAAGE,KAAKJ,CAAC;AACTH,QAAAA,KAAAA,EAAOO,KAAKH,MAAM;AAClBA,QAAAA,MAAAA,EAAQG,KAAKP;KACf,GACA;AACEG,QAAAA,CAAAA,EAAGI,KAAKF,CAAC;QACTA,CAAAA,EAAGI,IAAAA,CAAKT,KAAK,IAAIO,KAAKJ,CAAC,GAAGI,IAAAA,CAAKP,KAAK,CAAD;AACnCA,QAAAA,KAAAA,EAAOO,KAAKH,MAAM;AAClBA,QAAAA,MAAAA,EAAQG,KAAKP;;AAGrB;;;AAGC,IACM,MAAMU,cAAAA,GAAiB,CAC5BH,MACAI,QAAAA,EACAC,WAAAA,GAAAA;AAEA,IAAA,IAAIC,IAAAA,GAAON,IAAAA;AACX,IAAA,IAAIE,IAAAA,GAAOG,WAAAA;AAEX,IAAA,IAAK,IAAIE,IAAAA,GAAO,CAAA,EAAGA,OAAOH,QAAAA,GAAW,EAAA,EAAIG,QAAQ,CAAA,CAAG;QAClDD,IAAAA,GAAOP,cAAAA,CAAeO,MAAM,MAAA,EAAQJ,IAAAA,CAAAA;QACpCA,IAAAA,GAAO;AAAET,YAAAA,KAAAA,EAAOS,KAAKL,MAAM;AAAEA,YAAAA,MAAAA,EAAQK,KAAKT;AAAM,SAAA;AAClD,IAAA;IAEA,OAAOa,IAAAA;AACT;AAEA,gEACO,MAAME,YAAAA,GAAe,CAACC,SAAmBR,SAAAA,GAC5CQ,CAAAA,OAAAA,IAAWR,SAAAA,KAAc,OAAA,GAAU,KAAK,GAAE,CAAC,IAAK;AAEpD;;;;;;UAOaS,UAAAA,GAAa,IAAA;AACxB,IAAA,MAAM,CAACC,WAAAA,EAAaC,cAAAA,CAAe,GAAGC,gBAAAA,CAAMC,QAAQ,CAAc;QAAErB,KAAAA,EAAO,CAAA;QAAGI,MAAAA,EAAQ;AAAE,KAAA,CAAA;AACxF,IAAA,MAAM,CAACG,IAAAA,EAAMe,OAAAA,CAAQ,GAAGF,gBAAAA,CAAMC,QAAQ,CAAW;QAAElB,CAAAA,EAAG,CAAA;QAAGE,CAAAA,EAAG,CAAA;QAAGL,KAAAA,EAAO,CAAA;QAAGI,MAAAA,EAAQ;AAAE,KAAA,CAAA;AACnF,IAAA,MAAM,CAACL,WAAAA,EAAawB,mBAAAA,CAAoB,GAAGH,gBAAAA,CAAMC,QAAQ,CAAgB,IAAA,CAAA;AACzE,IAAA,MAAM,CAACV,QAAAA,EAAUa,WAAAA,CAAY,GAAGJ,gBAAAA,CAAMC,QAAQ,CAAW,CAAA,CAAA;IACzD,MAAMI,QAAAA,GAAWL,gBAAAA,CAAMM,MAAM,CAA0B,IAAA,CAAA;AAEvD;;;AAGC,MACD,MAAMC,IAAAA,GAAOP,gBAAAA,CAAMQ,WAAW,CAAC,CAACC,KAAAA,GAAAA;AAC9BJ,QAAAA,QAAAA,CAAST,OAAO,GAAGa,KAAAA;AACnB,QAAA,MAAMC,IAAAA,GAAoB;AAAE9B,YAAAA,KAAAA,EAAO6B,MAAME,YAAY;AAAE3B,YAAAA,MAAAA,EAAQyB,MAAMG;AAAc,SAAA;QACnFb,cAAAA,CAAeW,IAAAA,CAAAA;QACfR,OAAAA,CAAQ;YAAEnB,CAAAA,EAAG,CAAA;YAAGE,CAAAA,EAAG,CAAA;AAAGL,YAAAA,KAAAA,EAAO8B,KAAK9B,KAAK;AAAEI,YAAAA,MAAAA,EAAQ0B,KAAK1B;AAAO,SAAA,CAAA;QAC7DoB,WAAAA,CAAY,CAAA,CAAA;AACd,IAAA,CAAA,EAAG,EAAE,CAAA;AAEL;;;AAGC,MACD,MAAMS,MAAAA,GAASb,gBAAAA,CAAMQ,WAAW,CAC9B,CAACpB,SAAAA,GAAAA;AACCc,QAAAA,OAAAA,CAAQ,CAACY,IAAAA,GAAS5B,cAAAA,CAAe4B,IAAAA,EAAM1B,SAAAA,EAAWU,WAAAA,CAAAA,CAAAA;QAClDC,cAAAA,CAAe,CAACe,QAAU;AAAElC,gBAAAA,KAAAA,EAAOkC,KAAK9B,MAAM;AAAEA,gBAAAA,MAAAA,EAAQ8B,KAAKlC;aAAM,CAAA,CAAA;QACnEwB,WAAAA,CAAY,CAACU,IAAAA,GAASnB,YAAAA,CAAamB,IAAAA,EAAM1B,SAAAA,CAAAA,CAAAA;;;;AAIzCe,QAAAA,mBAAAA,CAAoB,CAACW,IAAAA,GAAUA,IAAAA,GAAO,CAAA,GAAIA,IAAAA,GAAOA,IAAAA,CAAAA;IACnD,CAAA,EACA;AAAChB,QAAAA;AAAY,KAAA,CAAA;AAGf,IAAA,MAAMiB,KAAAA,GAAQ,CAACC,KAAAA,EAAeC,GAAAA,EAAaC,GAAAA,GAAgBrC,IAAAA,CAAKoC,GAAG,CAACC,GAAAA,EAAKrC,IAAAA,CAAKqC,GAAG,CAACD,GAAAA,EAAKD,KAAAA,CAAAA,CAAAA;AAEvF,+EACA,MAAMG,WAAAA,GAAcnB,gBAAAA,CAAMQ,WAAW,CACnC,CAACE,IAAAA,GAAAA;AACCR,QAAAA,OAAAA,CAAQ,CAACY,IAAAA,GAAAA;AACP,YAAA,MAAMM,IAAAA,GAAOtB,WAAAA,CAAYlB,KAAK,GAAGkC,KAAK/B,CAAC;AACvC,YAAA,MAAMsC,IAAAA,GAAOvB,WAAAA,CAAYd,MAAM,GAAG8B,KAAK7B,CAAC;AACxC,YAAA,IAAIL,KAAAA,GAAQ8B,IAAAA,CAAK9B,KAAK,KAAK0C,SAAAA,GAAYP,KAAAA,CAAML,IAAAA,CAAK9B,KAAK,EAAE,CAAA,EAAGwC,IAAAA,CAAAA,GAAQN,IAAAA,CAAKlC,KAAK;AAC9E,YAAA,IAAII,MAAAA,GAAS0B,IAAAA,CAAK1B,MAAM,KAAKsC,SAAAA,GAAYP,KAAAA,CAAML,IAAAA,CAAK1B,MAAM,EAAE,CAAA,EAAGqC,IAAAA,CAAAA,GAAQP,IAAAA,CAAK9B,MAAM;AAElF,YAAA,IAAIL,WAAAA,EAAa;;;gBAGf,IAAI+B,IAAAA,CAAK9B,KAAK,KAAK0C,SAAAA,EAAW;oBAC5BtC,MAAAA,GAAS+B,KAAAA,CAAMnC,KAAAA,GAAQD,WAAAA,EAAa,CAAA,EAAG0C,IAAAA,CAAAA;AACzC,gBAAA,CAAA,MAAO,IAAIX,IAAAA,CAAK1B,MAAM,KAAKsC,SAAAA,EAAW;oBACpC1C,KAAAA,GAAQmC,KAAAA,CAAM/B,MAAAA,GAASL,WAAAA,EAAa,CAAA,EAAGyC,IAAAA,CAAAA;AACzC,gBAAA;AACF,YAAA;YAEA,OAAO;AAAE,gBAAA,GAAGN,IAAI;AAAElC,gBAAAA,KAAAA;AAAOI,gBAAAA;AAAO,aAAA;AAClC,QAAA,CAAA,CAAA;IACF,CAAA,EACA;AAACc,QAAAA,WAAAA,CAAYlB,KAAK;AAAEkB,QAAAA,WAAAA,CAAYd,MAAM;AAAEL,QAAAA;AAAY,KAAA,CAAA;AAGtD,4EACA,MAAM4C,eAAAA,GAAkBvB,gBAAAA,CAAMQ,WAAW,CACvC,CAACE,IAAAA,GAAAA;AACCR,QAAAA,OAAAA,CAAQ,CAACY,IAAAA,GAAAA;AACP,YAAA,MAAM/B,IAAI2B,IAAAA,CAAK3B,CAAC,KAAKuC,SAAAA,GAAYP,MAAML,IAAAA,CAAK3B,CAAC,EAAE,CAAA,EAAGe,YAAYlB,KAAK,GAAGkC,KAAKlC,KAAK,CAAA,GAAIkC,KAAK/B,CAAC;AAC1F,YAAA,MAAME,IACJyB,IAAAA,CAAKzB,CAAC,KAAKqC,SAAAA,GAAYP,MAAML,IAAAA,CAAKzB,CAAC,EAAE,CAAA,EAAGa,YAAYd,MAAM,GAAG8B,KAAK9B,MAAM,CAAA,GAAI8B,KAAK7B,CAAC;YACpF,OAAO;AAAE,gBAAA,GAAG6B,IAAI;AAAE/B,gBAAAA,CAAAA;AAAGE,gBAAAA;AAAE,aAAA;AACzB,QAAA,CAAA,CAAA;IACF,CAAA,EACA;AAACa,QAAAA,WAAAA,CAAYlB,KAAK;AAAEkB,QAAAA,WAAAA,CAAYd;AAAO,KAAA,CAAA;AAGzC,0EACA,MAAMwC,cAAAA,GAAiBxB,gBAAAA,CAAMQ,WAAW,CACtC,CAACiB,KAAAA,GAAAA;QACCtB,mBAAAA,CAAoBsB,KAAAA,CAAAA;AACpB,QAAA,IAAIA,KAAAA,EAAO;AACTvB,YAAAA,OAAAA,CAAQ,CAACY,IAAAA,GAAAA;;;AAGP,gBAAA,MAAMM,IAAAA,GAAOtB,WAAAA,CAAYlB,KAAK,GAAGkC,KAAK/B,CAAC;AACvC,gBAAA,MAAMsC,IAAAA,GAAOvB,WAAAA,CAAYd,MAAM,GAAG8B,KAAK7B,CAAC;gBACxC,IAAIL,KAAAA,GAAQkC,KAAKlC,KAAK;AACtB,gBAAA,IAAII,SAASJ,KAAAA,GAAQ6C,KAAAA;AACrB,gBAAA,IAAIzC,SAASqC,IAAAA,EAAM;oBACjBrC,MAAAA,GAASqC,IAAAA;AACTzC,oBAAAA,KAAAA,GAAQI,MAAAA,GAASyC,KAAAA;AACnB,gBAAA;AACA,gBAAA,IAAI7C,QAAQwC,IAAAA,EAAM;oBAChBxC,KAAAA,GAAQwC,IAAAA;AACRpC,oBAAAA,MAAAA,GAASJ,KAAAA,GAAQ6C,KAAAA;AACnB,gBAAA;gBACA,OAAO;AAAE,oBAAA,GAAGX,IAAI;oBAAElC,KAAAA,EAAOC,IAAAA,CAAK6C,KAAK,CAAC9C,KAAAA,CAAAA;oBAAQI,MAAAA,EAAQH,IAAAA,CAAK6C,KAAK,CAAC1C,MAAAA;AAAQ,iBAAA;AACzE,YAAA,CAAA,CAAA;AACF,QAAA;IACF,CAAA,EACA;AAACc,QAAAA,WAAAA,CAAYlB,KAAK;AAAEkB,QAAAA,WAAAA,CAAYd;AAAO,KAAA,CAAA;AAGzC;;;;AAIC,MACD,MAAM2C,WAAAA,GAAc3B,gBAAAA,CAAMQ,WAAW,CACnC,CAACoB,IAAAA,EAAcC,QAAAA,EAAkBC,gBAAAA,GAC/B,IAAIC,OAAAA,CAAQ,CAACC,OAAAA,EAASC,MAAAA,GAAAA;YACpB,MAAMxB,KAAAA,GAAQJ,SAAST,OAAO;AAC9B,YAAA,IAAI,CAACa,KAAAA,EAAO;AACVwB,gBAAAA,MAAAA,CAAO,IAAIC,KAAAA,CAAM,oDAAA,CAAA,CAAA;AACjB,gBAAA;AACF,YAAA;YACA,MAAMC,MAAAA,GAASC,QAAAA,CAASC,aAAa,CAAC,QAAA,CAAA;YACtCF,MAAAA,CAAOvD,KAAK,GAAGC,IAAAA,CAAKqC,GAAG,CAAC,GAAGrC,IAAAA,CAAK6C,KAAK,CAACvC,IAAAA,CAAKP,KAAK,CAAA,CAAA;YAChDuD,MAAAA,CAAOnD,MAAM,GAAGH,IAAAA,CAAKqC,GAAG,CAAC,GAAGrC,IAAAA,CAAK6C,KAAK,CAACvC,IAAAA,CAAKH,MAAM,CAAA,CAAA;YAClD,MAAMsD,OAAAA,GAAUH,MAAAA,CAAOI,UAAU,CAAC,IAAA,CAAA;AAClC,YAAA,IAAI,CAACD,OAAAA,EAAS;AACZL,gBAAAA,MAAAA,CAAO,IAAIC,KAAAA,CAAM,sDAAA,CAAA,CAAA;AACjB,gBAAA;AACF,YAAA;YACA,MAAMM,MAAAA,GAASlD,cAAAA,CAAeH,IAAAA,EAAMI,QAAAA,EAAUO,WAAAA,CAAAA;YAE9CwC,OAAAA,CAAQG,SAAS,CAACN,MAAAA,CAAOvD,KAAK,GAAG,CAAA,EAAGuD,MAAAA,CAAOnD,MAAM,GAAG,CAAA,CAAA;AACpDsD,YAAAA,OAAAA,CAAQzB,MAAM,CAAEtB,QAAAA,GAAWV,IAAAA,CAAK6D,EAAE,GAAI,GAAA,CAAA;AACtCJ,YAAAA,OAAAA,CAAQK,SAAS,CACflC,KAAAA,EACA+B,MAAAA,CAAOzD,CAAC,EACRyD,MAAAA,CAAOvD,CAAC,EACRuD,MAAAA,CAAO5D,KAAK,EACZ4D,MAAAA,CAAOxD,MAAM,EACb,CAACwD,MAAAA,CAAO5D,KAAK,GAAG,CAAA,EAChB,CAAC4D,MAAAA,CAAOxD,MAAM,GAAG,CAAA,EACjBwD,MAAAA,CAAO5D,KAAK,EACZ4D,OAAOxD,MAAM,CAAA;YAEfmD,MAAAA,CAAOS,MAAM,CACX,CAACC,IAAAA,GAAAA;AACC,gBAAA,IAAI,CAACA,IAAAA,EAAM;AACTZ,oBAAAA,MAAAA,CAAO,IAAIC,KAAAA,CAAM,+CAAA,CAAA,CAAA;AACjB,oBAAA;AACF,gBAAA;AACAF,gBAAAA,OAAAA,CACE,IAAIc,IAAAA,CAAK;AAACD,oBAAAA;AAAK,iBAAA,EAAEjB,IAAAA,EAAM;oBACrBmB,IAAAA,EAAMlB,QAAAA;AACNmB,oBAAAA,YAAAA,EAAclB,mBAAmB,IAAImB,IAAAA,CAAKnB,kBAAkBoB,OAAO,EAAA,GAAKD,KAAKE,GAAG;AAClF,iBAAA,CAAA,CAAA;AAEJ,YAAA,CAAA,EACAtB,QAAAA,EACAvD,OAAAA,CAAAA;QAEJ,CAAA,CAAA,EACF;AAACa,QAAAA,IAAAA;AAAMI,QAAAA,QAAAA;AAAUO,QAAAA;AAAY,KAAA,CAAA;IAG/B,OAAO;AACLS,QAAAA,IAAAA;AACApB,QAAAA,IAAAA;AACAW,QAAAA,WAAAA;AACAP,QAAAA,QAAAA;AACAsB,QAAAA,MAAAA;AACAlC,QAAAA,WAAAA;AACAwC,QAAAA,WAAAA;AACAI,QAAAA,eAAAA;AACAC,QAAAA,cAAAA;AACAG,QAAAA,WAAAA;;AAEA/C,QAAAA,KAAAA,EAAOC,IAAAA,CAAK6C,KAAK,CAACvC,IAAAA,CAAKP,KAAK,CAAA;AAC5BI,QAAAA,MAAAA,EAAQH,IAAAA,CAAK6C,KAAK,CAACvC,IAAAA,CAAKH,MAAM;AAChC,KAAA;AACF;;;;;;;;"}
|
|
@@ -26,6 +26,37 @@ const QUALITY = 1;
|
|
|
26
26
|
height
|
|
27
27
|
};
|
|
28
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* Remap a crop rect through a quarter-turn. `size` is the image size *before* the
|
|
31
|
+
* turn, so the returned rect is expressed against the swapped size for 90/270.
|
|
32
|
+
* Pure: the caller swaps `naturalSize` to match.
|
|
33
|
+
*/ const rotateCropRect = (crop, direction, size)=>direction === 'right' ? {
|
|
34
|
+
x: size.height - (crop.y + crop.height),
|
|
35
|
+
y: crop.x,
|
|
36
|
+
width: crop.height,
|
|
37
|
+
height: crop.width
|
|
38
|
+
} : {
|
|
39
|
+
x: crop.y,
|
|
40
|
+
y: size.width - (crop.x + crop.width),
|
|
41
|
+
width: crop.height,
|
|
42
|
+
height: crop.width
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* The crop rect expressed against the *unrotated* source, by undoing each quarter-turn
|
|
46
|
+
* already applied. `produceFile` needs it to know which source pixels to sample.
|
|
47
|
+
*/ const sourceCropRect = (crop, rotation, rotatedSize)=>{
|
|
48
|
+
let rect = crop;
|
|
49
|
+
let size = rotatedSize;
|
|
50
|
+
for(let turn = 0; turn < rotation / 90; turn += 1){
|
|
51
|
+
rect = rotateCropRect(rect, 'left', size);
|
|
52
|
+
size = {
|
|
53
|
+
width: size.height,
|
|
54
|
+
height: size.width
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return rect;
|
|
58
|
+
};
|
|
59
|
+
/** Advance a rotation by one quarter-turn, wrapping at 360. */ const nextRotation = (current, direction)=>(current + (direction === 'right' ? 90 : 270)) % 360;
|
|
29
60
|
/**
|
|
30
61
|
* Custom crop state + file producer. Replaces a cropperjs-based version: the
|
|
31
62
|
* crop rectangle is stored in natural-image pixels (so it stays locked across
|
|
@@ -44,6 +75,7 @@ const QUALITY = 1;
|
|
|
44
75
|
height: 0
|
|
45
76
|
});
|
|
46
77
|
const [aspectRatio, setAspectRatioState] = React.useState(null);
|
|
78
|
+
const [rotation, setRotation] = React.useState(0);
|
|
47
79
|
const imageRef = React.useRef(null);
|
|
48
80
|
/**
|
|
49
81
|
* Call from the <img> onLoad handler. Captures the natural dimensions and
|
|
@@ -61,7 +93,25 @@ const QUALITY = 1;
|
|
|
61
93
|
width: next.width,
|
|
62
94
|
height: next.height
|
|
63
95
|
});
|
|
96
|
+
setRotation(0);
|
|
64
97
|
}, []);
|
|
98
|
+
/**
|
|
99
|
+
* Turn the image a quarter-turn. `naturalSize` swaps and the crop rect is remapped
|
|
100
|
+
* so the same pixels stay selected; nothing is reset.
|
|
101
|
+
*/ const rotate = React.useCallback((direction)=>{
|
|
102
|
+
setCrop((prev)=>rotateCropRect(prev, direction, naturalSize));
|
|
103
|
+
setNaturalSize((prev)=>({
|
|
104
|
+
width: prev.height,
|
|
105
|
+
height: prev.width
|
|
106
|
+
}));
|
|
107
|
+
setRotation((prev)=>nextRotation(prev, direction));
|
|
108
|
+
// A locked ratio describes the orientation it was locked in, so a quarter
|
|
109
|
+
// turn inverts it. Without this it keeps enforcing the pre-rotation shape
|
|
110
|
+
// and fights every later resize.
|
|
111
|
+
setAspectRatioState((prev)=>prev ? 1 / prev : prev);
|
|
112
|
+
}, [
|
|
113
|
+
naturalSize
|
|
114
|
+
]);
|
|
65
115
|
const clamp = (value, min, max)=>Math.min(max, Math.max(min, value));
|
|
66
116
|
/** Update crop dimensions, clamping to remain inside the image bounds. */ const setCropSize = React.useCallback((next)=>{
|
|
67
117
|
setCrop((prev)=>{
|
|
@@ -150,7 +200,10 @@ const QUALITY = 1;
|
|
|
150
200
|
reject(new Error('Could not get a 2D canvas context to crop the image.'));
|
|
151
201
|
return;
|
|
152
202
|
}
|
|
153
|
-
|
|
203
|
+
const source = sourceCropRect(crop, rotation, naturalSize);
|
|
204
|
+
context.translate(canvas.width / 2, canvas.height / 2);
|
|
205
|
+
context.rotate(rotation * Math.PI / 180);
|
|
206
|
+
context.drawImage(image, source.x, source.y, source.width, source.height, -source.width / 2, -source.height / 2, source.width, source.height);
|
|
154
207
|
canvas.toBlob((blob)=>{
|
|
155
208
|
if (!blob) {
|
|
156
209
|
reject(new Error('Could not export the cropped image to a blob.'));
|
|
@@ -164,15 +217,16 @@ const QUALITY = 1;
|
|
|
164
217
|
}));
|
|
165
218
|
}, mimeType, QUALITY);
|
|
166
219
|
}), [
|
|
167
|
-
crop
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
crop.height
|
|
220
|
+
crop,
|
|
221
|
+
rotation,
|
|
222
|
+
naturalSize
|
|
171
223
|
]);
|
|
172
224
|
return {
|
|
173
225
|
init,
|
|
174
226
|
crop,
|
|
175
227
|
naturalSize,
|
|
228
|
+
rotation,
|
|
229
|
+
rotate,
|
|
176
230
|
aspectRatio,
|
|
177
231
|
setCropSize,
|
|
178
232
|
setCropPosition,
|
|
@@ -184,5 +238,5 @@ const QUALITY = 1;
|
|
|
184
238
|
};
|
|
185
239
|
};
|
|
186
240
|
|
|
187
|
-
export { resolveCornerResize, useCropImg };
|
|
241
|
+
export { nextRotation, resolveCornerResize, rotateCropRect, sourceCropRect, useCropImg };
|
|
188
242
|
//# sourceMappingURL=useCropImg.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCropImg.mjs","sources":["../../../../../../admin/src/pages/Assets/components/AssetDetails/useCropImg.ts"],"sourcesContent":["import * as React from 'react';\n\nconst QUALITY = 1;\n\ninterface NaturalSize {\n width: number;\n height: number;\n}\n\nexport interface CropRect {\n /** Origin in natural-px from the image top-left. */\n x: number;\n y: number;\n /** Crop area dimensions in natural px. */\n width: number;\n height: number;\n}\n\n/**\n * Resolve a corner-resize drag into a crop rect, keeping the opposite corner\n * (`anchor`) fixed. When `aspectRatio` is set, one dimension is snapped to the\n * ratio first (driven by whichever axis the pointer pushed furthest), then the\n * position is derived from the anchor — so a locked resize never drifts the\n * anchored corner. Pure (no clamping to image bounds; the caller clamps).\n */\nexport const resolveCornerResize = ({\n anchorX,\n anchorY,\n point,\n aspectRatio,\n}: {\n anchorX: number;\n anchorY: number;\n point: { x: number; y: number };\n aspectRatio: number | null;\n}): CropRect => {\n let width = Math.abs(point.x - anchorX);\n let height = Math.abs(point.y - anchorY);\n\n if (aspectRatio) {\n if (width / aspectRatio >= height) {\n height = width / aspectRatio;\n } else {\n width = height * aspectRatio;\n }\n }\n\n const x = point.x < anchorX ? anchorX - width : anchorX;\n const y = point.y < anchorY ? anchorY - height : anchorY;\n\n return { x, y, width, height };\n};\n\n/**\n * Custom crop state + file producer. Replaces a cropperjs-based version: the\n * crop rectangle is stored in natural-image pixels (so it stays locked across\n * window resizes), and `AssetCropEditor` overlays a div sized by\n * (cropNatural × displayRatio). produceFile draws the natural-px crop region\n * straight to a canvas.\n */\nexport const useCropImg = () => {\n const [naturalSize, setNaturalSize] = React.useState<NaturalSize>({ width: 0, height: 0 });\n const [crop, setCrop] = React.useState<CropRect>({ x: 0, y: 0, width: 0, height: 0 });\n const [aspectRatio, setAspectRatioState] = React.useState<number | null>(null);\n const imageRef = React.useRef<HTMLImageElement | null>(null);\n\n /**\n * Call from the <img> onLoad handler. Captures the natural dimensions and\n * seeds the crop to the full image (auto-crop area = 1).\n */\n const init = React.useCallback((image: HTMLImageElement) => {\n imageRef.current = image;\n const next: NaturalSize = { width: image.naturalWidth, height: image.naturalHeight };\n setNaturalSize(next);\n setCrop({ x: 0, y: 0, width: next.width, height: next.height });\n }, []);\n\n const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\n /** Update crop dimensions, clamping to remain inside the image bounds. */\n const setCropSize = React.useCallback(\n (next: Partial<Pick<CropRect, 'width' | 'height'>>) => {\n setCrop((prev) => {\n const maxW = naturalSize.width - prev.x;\n const maxH = naturalSize.height - prev.y;\n let width = next.width !== undefined ? clamp(next.width, 1, maxW) : prev.width;\n let height = next.height !== undefined ? clamp(next.height, 1, maxH) : prev.height;\n\n if (aspectRatio) {\n // When aspect locked, the axis the user is editing wins; the other\n // axis follows. If both changed in one call, prefer width.\n if (next.width !== undefined) {\n height = clamp(width / aspectRatio, 1, maxH);\n } else if (next.height !== undefined) {\n width = clamp(height * aspectRatio, 1, maxW);\n }\n }\n\n return { ...prev, width, height };\n });\n },\n [naturalSize.width, naturalSize.height, aspectRatio]\n );\n\n /** Update crop origin, clamping so the crop stays inside the image. */\n const setCropPosition = React.useCallback(\n (next: Partial<Pick<CropRect, 'x' | 'y'>>) => {\n setCrop((prev) => {\n const x = next.x !== undefined ? clamp(next.x, 0, naturalSize.width - prev.width) : prev.x;\n const y =\n next.y !== undefined ? clamp(next.y, 0, naturalSize.height - prev.height) : prev.y;\n return { ...prev, x, y };\n });\n },\n [naturalSize.width, naturalSize.height]\n );\n\n /** Lock or release the crop aspect ratio. Pass `null` to free it. */\n const setAspectRatio = React.useCallback(\n (ratio: number | null) => {\n setAspectRatioState(ratio);\n if (ratio) {\n setCrop((prev) => {\n // Bring the current rect into the requested ratio without escaping\n // the image bounds.\n const maxW = naturalSize.width - prev.x;\n const maxH = naturalSize.height - prev.y;\n let width = prev.width;\n let height = width / ratio;\n if (height > maxH) {\n height = maxH;\n width = height * ratio;\n }\n if (width > maxW) {\n width = maxW;\n height = width / ratio;\n }\n return { ...prev, width: Math.round(width), height: Math.round(height) };\n });\n }\n },\n [naturalSize.width, naturalSize.height]\n );\n\n /**\n * Draw the crop region from the loaded image element straight to a canvas\n * and produce a File. The image must be CORS-clean (set crossOrigin on the\n * <img>); otherwise canvas.toBlob throws on tainted canvas.\n */\n const produceFile = React.useCallback(\n (name: string, mimeType: string, lastModifiedDate?: string): Promise<globalThis.File> =>\n new Promise((resolve, reject) => {\n const image = imageRef.current;\n if (!image) {\n reject(new Error('Image not ready: call init() before produceFile().'));\n return;\n }\n const canvas = document.createElement('canvas');\n canvas.width = Math.max(1, Math.round(crop.width));\n canvas.height = Math.max(1, Math.round(crop.height));\n const context = canvas.getContext('2d');\n if (!context) {\n reject(new Error('Could not get a 2D canvas context to crop the image.'));\n return;\n }\n context.drawImage(\n image,\n crop.x,\n crop.y,\n crop.width,\n crop.height,\n 0,\n 0,\n canvas.width,\n canvas.height\n );\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error('Could not export the cropped image to a blob.'));\n return;\n }\n resolve(\n new File([blob], name, {\n type: mimeType,\n lastModified: lastModifiedDate ? new Date(lastModifiedDate).getTime() : Date.now(),\n })\n );\n },\n mimeType,\n QUALITY\n );\n }),\n [crop.x, crop.y, crop.width, crop.height]\n );\n\n return {\n init,\n crop,\n naturalSize,\n aspectRatio,\n setCropSize,\n setCropPosition,\n setAspectRatio,\n produceFile,\n // Round-aware getters for the px inputs.\n width: Math.round(crop.width),\n height: Math.round(crop.height),\n };\n};\n"],"names":["QUALITY","resolveCornerResize","anchorX","anchorY","point","aspectRatio","width","Math","abs","x","height","y","useCropImg","naturalSize","setNaturalSize","React","useState","crop","setCrop","setAspectRatioState","imageRef","useRef","init","useCallback","image","current","next","naturalWidth","naturalHeight","clamp","value","min","max","setCropSize","prev","maxW","maxH","undefined","setCropPosition","setAspectRatio","ratio","round","produceFile","name","mimeType","lastModifiedDate","Promise","resolve","reject","Error","canvas","document","createElement","context","getContext","drawImage","toBlob","blob","File","type","lastModified","Date","getTime","now"],"mappings":";;AAEA,MAAMA,OAAAA,GAAU,CAAA;AAgBhB;;;;;;AAMC,IACM,MAAMC,mBAAAA,GAAsB,CAAC,EAClCC,OAAO,EACPC,OAAO,EACPC,KAAK,EACLC,WAAW,EAMZ,GAAA;AACC,IAAA,IAAIC,QAAQC,IAAAA,CAAKC,GAAG,CAACJ,KAAAA,CAAMK,CAAC,GAAGP,OAAAA,CAAAA;AAC/B,IAAA,IAAIQ,SAASH,IAAAA,CAAKC,GAAG,CAACJ,KAAAA,CAAMO,CAAC,GAAGR,OAAAA,CAAAA;AAEhC,IAAA,IAAIE,WAAAA,EAAa;QACf,IAAIC,KAAAA,GAAQD,eAAeK,MAAAA,EAAQ;AACjCA,YAAAA,MAAAA,GAASJ,KAAAA,GAAQD,WAAAA;QACnB,CAAA,MAAO;AACLC,YAAAA,KAAAA,GAAQI,MAAAA,GAASL,WAAAA;AACnB,QAAA;AACF,IAAA;AAEA,IAAA,MAAMI,IAAIL,KAAAA,CAAMK,CAAC,GAAGP,OAAAA,GAAUA,UAAUI,KAAAA,GAAQJ,OAAAA;AAChD,IAAA,MAAMS,IAAIP,KAAAA,CAAMO,CAAC,GAAGR,OAAAA,GAAUA,UAAUO,MAAAA,GAASP,OAAAA;IAEjD,OAAO;AAAEM,QAAAA,CAAAA;AAAGE,QAAAA,CAAAA;AAAGL,QAAAA,KAAAA;AAAOI,QAAAA;AAAO,KAAA;AAC/B;AAEA;;;;;;UAOaE,UAAAA,GAAa,IAAA;AACxB,IAAA,MAAM,CAACC,WAAAA,EAAaC,cAAAA,CAAe,GAAGC,KAAAA,CAAMC,QAAQ,CAAc;QAAEV,KAAAA,EAAO,CAAA;QAAGI,MAAAA,EAAQ;AAAE,KAAA,CAAA;AACxF,IAAA,MAAM,CAACO,IAAAA,EAAMC,OAAAA,CAAQ,GAAGH,KAAAA,CAAMC,QAAQ,CAAW;QAAEP,CAAAA,EAAG,CAAA;QAAGE,CAAAA,EAAG,CAAA;QAAGL,KAAAA,EAAO,CAAA;QAAGI,MAAAA,EAAQ;AAAE,KAAA,CAAA;AACnF,IAAA,MAAM,CAACL,WAAAA,EAAac,mBAAAA,CAAoB,GAAGJ,KAAAA,CAAMC,QAAQ,CAAgB,IAAA,CAAA;IACzE,MAAMI,QAAAA,GAAWL,KAAAA,CAAMM,MAAM,CAA0B,IAAA,CAAA;AAEvD;;;AAGC,MACD,MAAMC,IAAAA,GAAOP,KAAAA,CAAMQ,WAAW,CAAC,CAACC,KAAAA,GAAAA;AAC9BJ,QAAAA,QAAAA,CAASK,OAAO,GAAGD,KAAAA;AACnB,QAAA,MAAME,IAAAA,GAAoB;AAAEpB,YAAAA,KAAAA,EAAOkB,MAAMG,YAAY;AAAEjB,YAAAA,MAAAA,EAAQc,MAAMI;AAAc,SAAA;QACnFd,cAAAA,CAAeY,IAAAA,CAAAA;QACfR,OAAAA,CAAQ;YAAET,CAAAA,EAAG,CAAA;YAAGE,CAAAA,EAAG,CAAA;AAAGL,YAAAA,KAAAA,EAAOoB,KAAKpB,KAAK;AAAEI,YAAAA,MAAAA,EAAQgB,KAAKhB;AAAO,SAAA,CAAA;AAC/D,IAAA,CAAA,EAAG,EAAE,CAAA;AAEL,IAAA,MAAMmB,KAAAA,GAAQ,CAACC,KAAAA,EAAeC,GAAAA,EAAaC,GAAAA,GAAgBzB,IAAAA,CAAKwB,GAAG,CAACC,GAAAA,EAAKzB,IAAAA,CAAKyB,GAAG,CAACD,GAAAA,EAAKD,KAAAA,CAAAA,CAAAA;AAEvF,+EACA,MAAMG,WAAAA,GAAclB,KAAAA,CAAMQ,WAAW,CACnC,CAACG,IAAAA,GAAAA;AACCR,QAAAA,OAAAA,CAAQ,CAACgB,IAAAA,GAAAA;AACP,YAAA,MAAMC,IAAAA,GAAOtB,WAAAA,CAAYP,KAAK,GAAG4B,KAAKzB,CAAC;AACvC,YAAA,MAAM2B,IAAAA,GAAOvB,WAAAA,CAAYH,MAAM,GAAGwB,KAAKvB,CAAC;AACxC,YAAA,IAAIL,KAAAA,GAAQoB,IAAAA,CAAKpB,KAAK,KAAK+B,SAAAA,GAAYR,KAAAA,CAAMH,IAAAA,CAAKpB,KAAK,EAAE,CAAA,EAAG6B,IAAAA,CAAAA,GAAQD,IAAAA,CAAK5B,KAAK;AAC9E,YAAA,IAAII,MAAAA,GAASgB,IAAAA,CAAKhB,MAAM,KAAK2B,SAAAA,GAAYR,KAAAA,CAAMH,IAAAA,CAAKhB,MAAM,EAAE,CAAA,EAAG0B,IAAAA,CAAAA,GAAQF,IAAAA,CAAKxB,MAAM;AAElF,YAAA,IAAIL,WAAAA,EAAa;;;gBAGf,IAAIqB,IAAAA,CAAKpB,KAAK,KAAK+B,SAAAA,EAAW;oBAC5B3B,MAAAA,GAASmB,KAAAA,CAAMvB,KAAAA,GAAQD,WAAAA,EAAa,CAAA,EAAG+B,IAAAA,CAAAA;AACzC,gBAAA,CAAA,MAAO,IAAIV,IAAAA,CAAKhB,MAAM,KAAK2B,SAAAA,EAAW;oBACpC/B,KAAAA,GAAQuB,KAAAA,CAAMnB,MAAAA,GAASL,WAAAA,EAAa,CAAA,EAAG8B,IAAAA,CAAAA;AACzC,gBAAA;AACF,YAAA;YAEA,OAAO;AAAE,gBAAA,GAAGD,IAAI;AAAE5B,gBAAAA,KAAAA;AAAOI,gBAAAA;AAAO,aAAA;AAClC,QAAA,CAAA,CAAA;IACF,CAAA,EACA;AAACG,QAAAA,WAAAA,CAAYP,KAAK;AAAEO,QAAAA,WAAAA,CAAYH,MAAM;AAAEL,QAAAA;AAAY,KAAA,CAAA;AAGtD,4EACA,MAAMiC,eAAAA,GAAkBvB,KAAAA,CAAMQ,WAAW,CACvC,CAACG,IAAAA,GAAAA;AACCR,QAAAA,OAAAA,CAAQ,CAACgB,IAAAA,GAAAA;AACP,YAAA,MAAMzB,IAAIiB,IAAAA,CAAKjB,CAAC,KAAK4B,SAAAA,GAAYR,MAAMH,IAAAA,CAAKjB,CAAC,EAAE,CAAA,EAAGI,YAAYP,KAAK,GAAG4B,KAAK5B,KAAK,CAAA,GAAI4B,KAAKzB,CAAC;AAC1F,YAAA,MAAME,IACJe,IAAAA,CAAKf,CAAC,KAAK0B,SAAAA,GAAYR,MAAMH,IAAAA,CAAKf,CAAC,EAAE,CAAA,EAAGE,YAAYH,MAAM,GAAGwB,KAAKxB,MAAM,CAAA,GAAIwB,KAAKvB,CAAC;YACpF,OAAO;AAAE,gBAAA,GAAGuB,IAAI;AAAEzB,gBAAAA,CAAAA;AAAGE,gBAAAA;AAAE,aAAA;AACzB,QAAA,CAAA,CAAA;IACF,CAAA,EACA;AAACE,QAAAA,WAAAA,CAAYP,KAAK;AAAEO,QAAAA,WAAAA,CAAYH;AAAO,KAAA,CAAA;AAGzC,0EACA,MAAM6B,cAAAA,GAAiBxB,KAAAA,CAAMQ,WAAW,CACtC,CAACiB,KAAAA,GAAAA;QACCrB,mBAAAA,CAAoBqB,KAAAA,CAAAA;AACpB,QAAA,IAAIA,KAAAA,EAAO;AACTtB,YAAAA,OAAAA,CAAQ,CAACgB,IAAAA,GAAAA;;;AAGP,gBAAA,MAAMC,IAAAA,GAAOtB,WAAAA,CAAYP,KAAK,GAAG4B,KAAKzB,CAAC;AACvC,gBAAA,MAAM2B,IAAAA,GAAOvB,WAAAA,CAAYH,MAAM,GAAGwB,KAAKvB,CAAC;gBACxC,IAAIL,KAAAA,GAAQ4B,KAAK5B,KAAK;AACtB,gBAAA,IAAII,SAASJ,KAAAA,GAAQkC,KAAAA;AACrB,gBAAA,IAAI9B,SAAS0B,IAAAA,EAAM;oBACjB1B,MAAAA,GAAS0B,IAAAA;AACT9B,oBAAAA,KAAAA,GAAQI,MAAAA,GAAS8B,KAAAA;AACnB,gBAAA;AACA,gBAAA,IAAIlC,QAAQ6B,IAAAA,EAAM;oBAChB7B,KAAAA,GAAQ6B,IAAAA;AACRzB,oBAAAA,MAAAA,GAASJ,KAAAA,GAAQkC,KAAAA;AACnB,gBAAA;gBACA,OAAO;AAAE,oBAAA,GAAGN,IAAI;oBAAE5B,KAAAA,EAAOC,IAAAA,CAAKkC,KAAK,CAACnC,KAAAA,CAAAA;oBAAQI,MAAAA,EAAQH,IAAAA,CAAKkC,KAAK,CAAC/B,MAAAA;AAAQ,iBAAA;AACzE,YAAA,CAAA,CAAA;AACF,QAAA;IACF,CAAA,EACA;AAACG,QAAAA,WAAAA,CAAYP,KAAK;AAAEO,QAAAA,WAAAA,CAAYH;AAAO,KAAA,CAAA;AAGzC;;;;AAIC,MACD,MAAMgC,WAAAA,GAAc3B,KAAAA,CAAMQ,WAAW,CACnC,CAACoB,IAAAA,EAAcC,QAAAA,EAAkBC,gBAAAA,GAC/B,IAAIC,OAAAA,CAAQ,CAACC,OAAAA,EAASC,MAAAA,GAAAA;YACpB,MAAMxB,KAAAA,GAAQJ,SAASK,OAAO;AAC9B,YAAA,IAAI,CAACD,KAAAA,EAAO;AACVwB,gBAAAA,MAAAA,CAAO,IAAIC,KAAAA,CAAM,oDAAA,CAAA,CAAA;AACjB,gBAAA;AACF,YAAA;YACA,MAAMC,MAAAA,GAASC,QAAAA,CAASC,aAAa,CAAC,QAAA,CAAA;YACtCF,MAAAA,CAAO5C,KAAK,GAAGC,IAAAA,CAAKyB,GAAG,CAAC,GAAGzB,IAAAA,CAAKkC,KAAK,CAACxB,IAAAA,CAAKX,KAAK,CAAA,CAAA;YAChD4C,MAAAA,CAAOxC,MAAM,GAAGH,IAAAA,CAAKyB,GAAG,CAAC,GAAGzB,IAAAA,CAAKkC,KAAK,CAACxB,IAAAA,CAAKP,MAAM,CAAA,CAAA;YAClD,MAAM2C,OAAAA,GAAUH,MAAAA,CAAOI,UAAU,CAAC,IAAA,CAAA;AAClC,YAAA,IAAI,CAACD,OAAAA,EAAS;AACZL,gBAAAA,MAAAA,CAAO,IAAIC,KAAAA,CAAM,sDAAA,CAAA,CAAA;AACjB,gBAAA;AACF,YAAA;YACAI,OAAAA,CAAQE,SAAS,CACf/B,KAAAA,EACAP,IAAAA,CAAKR,CAAC,EACNQ,IAAAA,CAAKN,CAAC,EACNM,IAAAA,CAAKX,KAAK,EACVW,IAAAA,CAAKP,MAAM,EACX,CAAA,EACA,GACAwC,MAAAA,CAAO5C,KAAK,EACZ4C,MAAAA,CAAOxC,MAAM,CAAA;YAEfwC,MAAAA,CAAOM,MAAM,CACX,CAACC,IAAAA,GAAAA;AACC,gBAAA,IAAI,CAACA,IAAAA,EAAM;AACTT,oBAAAA,MAAAA,CAAO,IAAIC,KAAAA,CAAM,+CAAA,CAAA,CAAA;AACjB,oBAAA;AACF,gBAAA;AACAF,gBAAAA,OAAAA,CACE,IAAIW,IAAAA,CAAK;AAACD,oBAAAA;AAAK,iBAAA,EAAEd,IAAAA,EAAM;oBACrBgB,IAAAA,EAAMf,QAAAA;AACNgB,oBAAAA,YAAAA,EAAcf,mBAAmB,IAAIgB,IAAAA,CAAKhB,kBAAkBiB,OAAO,EAAA,GAAKD,KAAKE,GAAG;AAClF,iBAAA,CAAA,CAAA;AAEJ,YAAA,CAAA,EACAnB,QAAAA,EACA5C,OAAAA,CAAAA;QAEJ,CAAA,CAAA,EACF;AAACiB,QAAAA,IAAAA,CAAKR,CAAC;AAAEQ,QAAAA,IAAAA,CAAKN,CAAC;AAAEM,QAAAA,IAAAA,CAAKX,KAAK;AAAEW,QAAAA,IAAAA,CAAKP;AAAO,KAAA,CAAA;IAG3C,OAAO;AACLY,QAAAA,IAAAA;AACAL,QAAAA,IAAAA;AACAJ,QAAAA,WAAAA;AACAR,QAAAA,WAAAA;AACA4B,QAAAA,WAAAA;AACAK,QAAAA,eAAAA;AACAC,QAAAA,cAAAA;AACAG,QAAAA,WAAAA;;AAEApC,QAAAA,KAAAA,EAAOC,IAAAA,CAAKkC,KAAK,CAACxB,IAAAA,CAAKX,KAAK,CAAA;AAC5BI,QAAAA,MAAAA,EAAQH,IAAAA,CAAKkC,KAAK,CAACxB,IAAAA,CAAKP,MAAM;AAChC,KAAA;AACF;;;;"}
|
|
1
|
+
{"version":3,"file":"useCropImg.mjs","sources":["../../../../../../admin/src/pages/Assets/components/AssetDetails/useCropImg.ts"],"sourcesContent":["import * as React from 'react';\n\nconst QUALITY = 1;\n\ninterface NaturalSize {\n width: number;\n height: number;\n}\n\nexport interface CropRect {\n /** Origin in natural-px from the image top-left. */\n x: number;\n y: number;\n /** Crop area dimensions in natural px. */\n width: number;\n height: number;\n}\n\n/**\n * Resolve a corner-resize drag into a crop rect, keeping the opposite corner\n * (`anchor`) fixed. When `aspectRatio` is set, one dimension is snapped to the\n * ratio first (driven by whichever axis the pointer pushed furthest), then the\n * position is derived from the anchor — so a locked resize never drifts the\n * anchored corner. Pure (no clamping to image bounds; the caller clamps).\n */\nexport const resolveCornerResize = ({\n anchorX,\n anchorY,\n point,\n aspectRatio,\n}: {\n anchorX: number;\n anchorY: number;\n point: { x: number; y: number };\n aspectRatio: number | null;\n}): CropRect => {\n let width = Math.abs(point.x - anchorX);\n let height = Math.abs(point.y - anchorY);\n\n if (aspectRatio) {\n if (width / aspectRatio >= height) {\n height = width / aspectRatio;\n } else {\n width = height * aspectRatio;\n }\n }\n\n const x = point.x < anchorX ? anchorX - width : anchorX;\n const y = point.y < anchorY ? anchorY - height : anchorY;\n\n return { x, y, width, height };\n};\n\nexport type Rotation = 0 | 90 | 180 | 270;\n\n/**\n * Remap a crop rect through a quarter-turn. `size` is the image size *before* the\n * turn, so the returned rect is expressed against the swapped size for 90/270.\n * Pure: the caller swaps `naturalSize` to match.\n */\nexport const rotateCropRect = (\n crop: CropRect,\n direction: 'left' | 'right',\n size: NaturalSize\n): CropRect =>\n direction === 'right'\n ? {\n x: size.height - (crop.y + crop.height),\n y: crop.x,\n width: crop.height,\n height: crop.width,\n }\n : {\n x: crop.y,\n y: size.width - (crop.x + crop.width),\n width: crop.height,\n height: crop.width,\n };\n\n/**\n * The crop rect expressed against the *unrotated* source, by undoing each quarter-turn\n * already applied. `produceFile` needs it to know which source pixels to sample.\n */\nexport const sourceCropRect = (\n crop: CropRect,\n rotation: Rotation,\n rotatedSize: NaturalSize\n): CropRect => {\n let rect = crop;\n let size = rotatedSize;\n\n for (let turn = 0; turn < rotation / 90; turn += 1) {\n rect = rotateCropRect(rect, 'left', size);\n size = { width: size.height, height: size.width };\n }\n\n return rect;\n};\n\n/** Advance a rotation by one quarter-turn, wrapping at 360. */\nexport const nextRotation = (current: Rotation, direction: 'left' | 'right'): Rotation =>\n ((current + (direction === 'right' ? 90 : 270)) % 360) as Rotation;\n\n/**\n * Custom crop state + file producer. Replaces a cropperjs-based version: the\n * crop rectangle is stored in natural-image pixels (so it stays locked across\n * window resizes), and `AssetCropEditor` overlays a div sized by\n * (cropNatural × displayRatio). produceFile draws the natural-px crop region\n * straight to a canvas.\n */\nexport const useCropImg = () => {\n const [naturalSize, setNaturalSize] = React.useState<NaturalSize>({ width: 0, height: 0 });\n const [crop, setCrop] = React.useState<CropRect>({ x: 0, y: 0, width: 0, height: 0 });\n const [aspectRatio, setAspectRatioState] = React.useState<number | null>(null);\n const [rotation, setRotation] = React.useState<Rotation>(0);\n const imageRef = React.useRef<HTMLImageElement | null>(null);\n\n /**\n * Call from the <img> onLoad handler. Captures the natural dimensions and\n * seeds the crop to the full image (auto-crop area = 1).\n */\n const init = React.useCallback((image: HTMLImageElement) => {\n imageRef.current = image;\n const next: NaturalSize = { width: image.naturalWidth, height: image.naturalHeight };\n setNaturalSize(next);\n setCrop({ x: 0, y: 0, width: next.width, height: next.height });\n setRotation(0);\n }, []);\n\n /**\n * Turn the image a quarter-turn. `naturalSize` swaps and the crop rect is remapped\n * so the same pixels stay selected; nothing is reset.\n */\n const rotate = React.useCallback(\n (direction: 'left' | 'right') => {\n setCrop((prev) => rotateCropRect(prev, direction, naturalSize));\n setNaturalSize((prev) => ({ width: prev.height, height: prev.width }));\n setRotation((prev) => nextRotation(prev, direction));\n // A locked ratio describes the orientation it was locked in, so a quarter\n // turn inverts it. Without this it keeps enforcing the pre-rotation shape\n // and fights every later resize.\n setAspectRatioState((prev) => (prev ? 1 / prev : prev));\n },\n [naturalSize]\n );\n\n const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\n /** Update crop dimensions, clamping to remain inside the image bounds. */\n const setCropSize = React.useCallback(\n (next: Partial<Pick<CropRect, 'width' | 'height'>>) => {\n setCrop((prev) => {\n const maxW = naturalSize.width - prev.x;\n const maxH = naturalSize.height - prev.y;\n let width = next.width !== undefined ? clamp(next.width, 1, maxW) : prev.width;\n let height = next.height !== undefined ? clamp(next.height, 1, maxH) : prev.height;\n\n if (aspectRatio) {\n // When aspect locked, the axis the user is editing wins; the other\n // axis follows. If both changed in one call, prefer width.\n if (next.width !== undefined) {\n height = clamp(width / aspectRatio, 1, maxH);\n } else if (next.height !== undefined) {\n width = clamp(height * aspectRatio, 1, maxW);\n }\n }\n\n return { ...prev, width, height };\n });\n },\n [naturalSize.width, naturalSize.height, aspectRatio]\n );\n\n /** Update crop origin, clamping so the crop stays inside the image. */\n const setCropPosition = React.useCallback(\n (next: Partial<Pick<CropRect, 'x' | 'y'>>) => {\n setCrop((prev) => {\n const x = next.x !== undefined ? clamp(next.x, 0, naturalSize.width - prev.width) : prev.x;\n const y =\n next.y !== undefined ? clamp(next.y, 0, naturalSize.height - prev.height) : prev.y;\n return { ...prev, x, y };\n });\n },\n [naturalSize.width, naturalSize.height]\n );\n\n /** Lock or release the crop aspect ratio. Pass `null` to free it. */\n const setAspectRatio = React.useCallback(\n (ratio: number | null) => {\n setAspectRatioState(ratio);\n if (ratio) {\n setCrop((prev) => {\n // Bring the current rect into the requested ratio without escaping\n // the image bounds.\n const maxW = naturalSize.width - prev.x;\n const maxH = naturalSize.height - prev.y;\n let width = prev.width;\n let height = width / ratio;\n if (height > maxH) {\n height = maxH;\n width = height * ratio;\n }\n if (width > maxW) {\n width = maxW;\n height = width / ratio;\n }\n return { ...prev, width: Math.round(width), height: Math.round(height) };\n });\n }\n },\n [naturalSize.width, naturalSize.height]\n );\n\n /**\n * Draw the crop region from the loaded image element straight to a canvas\n * and produce a File. The image must be CORS-clean (set crossOrigin on the\n * <img>); otherwise canvas.toBlob throws on tainted canvas.\n */\n const produceFile = React.useCallback(\n (name: string, mimeType: string, lastModifiedDate?: string): Promise<globalThis.File> =>\n new Promise((resolve, reject) => {\n const image = imageRef.current;\n if (!image) {\n reject(new Error('Image not ready: call init() before produceFile().'));\n return;\n }\n const canvas = document.createElement('canvas');\n canvas.width = Math.max(1, Math.round(crop.width));\n canvas.height = Math.max(1, Math.round(crop.height));\n const context = canvas.getContext('2d');\n if (!context) {\n reject(new Error('Could not get a 2D canvas context to crop the image.'));\n return;\n }\n const source = sourceCropRect(crop, rotation, naturalSize);\n\n context.translate(canvas.width / 2, canvas.height / 2);\n context.rotate((rotation * Math.PI) / 180);\n context.drawImage(\n image,\n source.x,\n source.y,\n source.width,\n source.height,\n -source.width / 2,\n -source.height / 2,\n source.width,\n source.height\n );\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error('Could not export the cropped image to a blob.'));\n return;\n }\n resolve(\n new File([blob], name, {\n type: mimeType,\n lastModified: lastModifiedDate ? new Date(lastModifiedDate).getTime() : Date.now(),\n })\n );\n },\n mimeType,\n QUALITY\n );\n }),\n [crop, rotation, naturalSize]\n );\n\n return {\n init,\n crop,\n naturalSize,\n rotation,\n rotate,\n aspectRatio,\n setCropSize,\n setCropPosition,\n setAspectRatio,\n produceFile,\n // Round-aware getters for the px inputs.\n width: Math.round(crop.width),\n height: Math.round(crop.height),\n };\n};\n"],"names":["QUALITY","resolveCornerResize","anchorX","anchorY","point","aspectRatio","width","Math","abs","x","height","y","rotateCropRect","crop","direction","size","sourceCropRect","rotation","rotatedSize","rect","turn","nextRotation","current","useCropImg","naturalSize","setNaturalSize","React","useState","setCrop","setAspectRatioState","setRotation","imageRef","useRef","init","useCallback","image","next","naturalWidth","naturalHeight","rotate","prev","clamp","value","min","max","setCropSize","maxW","maxH","undefined","setCropPosition","setAspectRatio","ratio","round","produceFile","name","mimeType","lastModifiedDate","Promise","resolve","reject","Error","canvas","document","createElement","context","getContext","source","translate","PI","drawImage","toBlob","blob","File","type","lastModified","Date","getTime","now"],"mappings":"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"}
|
|
@@ -23,6 +23,7 @@ var BusyOverlay = require('./BusyOverlay.js');
|
|
|
23
23
|
var AssetsDndProvider = require('./Dnd/AssetsDndProvider.js');
|
|
24
24
|
var useAssetDnd = require('./Dnd/useAssetDnd.js');
|
|
25
25
|
var FolderActionsMenu = require('./FolderActionsMenu.js');
|
|
26
|
+
var ItemContextMenu = require('./ItemContextMenu.js');
|
|
26
27
|
|
|
27
28
|
/* -------------------------------------------------------------------------------------------------
|
|
28
29
|
* AssetsGrid
|
|
@@ -129,6 +130,7 @@ const FolderCard = ({ folder, orderedItemKeys })=>{
|
|
|
129
130
|
};
|
|
130
131
|
const { isSelected, toggle, selectRange } = useAssetSelection.useAssetSelection();
|
|
131
132
|
const { canUpdate } = useMediaLibraryPermissions.useMediaLibraryPermissions();
|
|
133
|
+
const openContextMenu = ItemContextMenu.useItemContextMenuTrigger();
|
|
132
134
|
const { dragData, draggable: { attributes, listeners, setNodeRef: setDragRef, isDragging }, droppable: { setNodeRef: setDropRef }, showValidDropHighlight, showInvalidDropCursor } = useAssetDnd.useFolderDraggableDroppable(folder);
|
|
133
135
|
const key = selection.folderKey(folder.id);
|
|
134
136
|
const setNodeRef = (node)=>{
|
|
@@ -193,9 +195,19 @@ const FolderCard = ({ folder, orderedItemKeys })=>{
|
|
|
193
195
|
},
|
|
194
196
|
role: "listitem",
|
|
195
197
|
tabIndex: 0,
|
|
196
|
-
// Right-
|
|
197
|
-
//
|
|
198
|
+
// Right-click opens this folder's own actions, or the selection's when it
|
|
199
|
+
// is part of one. Still marked as "not the background gesture" so
|
|
200
|
+
// MainAreaContextMenu's create menu stays out of it.
|
|
198
201
|
"data-native-context-menu": true,
|
|
202
|
+
onContextMenu: (e)=>{
|
|
203
|
+
if (isEventFromWithin.isEventFromWithin(e)) {
|
|
204
|
+
openContextMenu?.(e, key, {
|
|
205
|
+
kind: 'folder',
|
|
206
|
+
folder,
|
|
207
|
+
dragData
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
},
|
|
199
211
|
children: [
|
|
200
212
|
canUpdate && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
|
|
201
213
|
onKeyDown: (e)=>e.stopPropagation(),
|
|
@@ -355,6 +367,7 @@ const AssetCard = ({ asset, orderedItemKeys, onAssetItemClick })=>{
|
|
|
355
367
|
const { isSelected, toggle, selectRange } = useAssetSelection.useAssetSelection();
|
|
356
368
|
const { canUpdate } = useMediaLibraryPermissions.useMediaLibraryPermissions();
|
|
357
369
|
const busyMessage = useBusyAssets.useBusyAssetsOptional()?.getBusyMessage(asset.id) ?? null;
|
|
370
|
+
const openContextMenu = ItemContextMenu.useItemContextMenuTrigger();
|
|
358
371
|
const key = selection.assetKey(asset.id);
|
|
359
372
|
const selected = isSelected(key);
|
|
360
373
|
// Plain click opens the asset details; pointer selection lives on the
|
|
@@ -414,9 +427,19 @@ const AssetCard = ({ asset, orderedItemKeys, onAssetItemClick })=>{
|
|
|
414
427
|
$isSelected: selected,
|
|
415
428
|
tabIndex: 0,
|
|
416
429
|
role: "listitem",
|
|
417
|
-
// Right-
|
|
418
|
-
//
|
|
430
|
+
// Right-click opens this asset's own actions, or the selection's when it
|
|
431
|
+
// is part of one. Still marked as "not the background gesture" so
|
|
432
|
+
// MainAreaContextMenu's create menu stays out of it.
|
|
419
433
|
"data-native-context-menu": true,
|
|
434
|
+
onContextMenu: (e)=>{
|
|
435
|
+
if (isEventFromWithin.isEventFromWithin(e)) {
|
|
436
|
+
openContextMenu?.(e, key, {
|
|
437
|
+
kind: 'asset',
|
|
438
|
+
asset,
|
|
439
|
+
dragData
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
},
|
|
420
443
|
onDragStart: (e)=>e.preventDefault(),
|
|
421
444
|
onClick: handleCardClick,
|
|
422
445
|
onKeyDown: handleKeyDown,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetsGrid.js","sources":["../../../../../admin/src/pages/Assets/components/AssetsGrid.tsx"],"sourcesContent":["import { Box, Card, CardBody, CardHeader, Checkbox, Flex } from '@strapi/design-system';\nimport { Folder as FolderIcon } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled, css } from 'styled-components';\n\nimport { TruncatedText } from '../../../components/TruncatedText';\nimport { ASSET_TYPES } from '../../../enums';\nimport { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions';\nimport { prefixFileUrlWithBackendUrl } from '../../../utils/files';\nimport { getAssetIcon } from '../../../utils/getAssetIcon';\nimport { isEventFromWithin } from '../../../utils/isEventFromWithin';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { ASSET_DETAILS_TRIGGER_PROPS, ASSET_ITEM_CONTROL_PROPS } from '../constants';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useBusyAssetsOptional } from '../hooks/useBusyAssets';\nimport { useFolderNavigation } from '../hooks/useFolderNavigation';\nimport { buildRenderedKeys } from '../utils/renderedKeys';\nimport { assetKey, folderKey, type ItemKey } from '../utils/selection';\n\nimport { AssetActionsMenu } from './AssetActionsMenu';\nimport { BusyOverlay } from './BusyOverlay';\nimport { useAssetsDndOptional } from './Dnd/AssetsDndProvider';\nimport { useFileDraggable, useFolderDraggableDroppable } from './Dnd/useAssetDnd';\nimport { FolderActionsMenu } from './FolderActionsMenu';\n\nimport type { File } from '../../../../../shared/contracts/files';\nimport type { Folder } from '../../../../../shared/contracts/folders';\n\n/* -------------------------------------------------------------------------------------------------\n * AssetsGrid\n * -----------------------------------------------------------------------------------------------*/\n\n// Cards own click, Enter and Space; the controls sitting on them (checkbox, \"...\"\n// menu) must not let those reach the card. Scoped to the wrapper's own DOM\n// subtree so the menu's portaled content — a React child of the wrapper, but\n// never a DOM descendant — still reaches `document`, where Radix listens in\n// order to dismiss its layers.\nconst stopCardEvent = (e: React.SyntheticEvent) => {\n if (isEventFromWithin(e)) {\n e.stopPropagation();\n }\n};\n\n// Top-left selection checkbox overlaid on the asset preview, always visible.\nconst CheckboxOverlay = styled(Flex)`\n position: absolute;\n top: ${({ theme }) => theme.spaces[3]};\n left: ${({ theme }) => theme.spaces[3]};\n z-index: 1;\n box-shadow: ${({ theme }) => theme.shadows.filterShadow};\n`;\n\nconst StyledCard = styled(Card)<{\n $isDragging?: boolean;\n $isMovePending?: boolean;\n $isBusy?: boolean;\n $isSelected?: boolean;\n}>`\n border: 1px solid\n ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary600 : theme.colors.neutral200)};\n border-radius: 8px;\n overflow: hidden;\n isolation: isolate;\n cursor: ${({ $isMovePending, $isBusy }) => ($isMovePending || $isBusy ? 'wait' : 'pointer')};\n opacity: ${({ $isDragging }) => ($isDragging ? 0.4 : 1)};\n /* No opacity change while busy — the overlay does the dimming, and stacking\n one on the other would wash the card out. */\n pointer-events: ${({ $isMovePending, $isBusy }) => ($isMovePending || $isBusy ? 'none' : 'auto')};\n background: ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary100 : undefined)};\n /* Shift+click range selection must not highlight card text. */\n user-select: none;\n\n &:hover {\n background: ${({ theme }) => theme.colors.primary100};\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * FolderCard\n * -----------------------------------------------------------------------------------------------*/\n\nconst FoldersRow = styled(Box)`\n grid-column: 1 / -1;\n`;\n\nconst MIN_TILE_WIDTH = '240px';\n\nconst ItemsGrid = styled(Box)`\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(min(${MIN_TILE_WIDTH}, 100%), 1fr));\n gap: ${({ theme }) => theme.spaces[4]};\n`;\n\nconst StyledFolderCard = styled(Flex)<{\n $isDragging?: boolean;\n $isMovePending?: boolean;\n $isValidDropTarget?: boolean;\n $isInvalidDropTarget?: boolean;\n $isSelected?: boolean;\n}>`\n width: 100%;\n user-select: none;\n padding: ${({ theme }) => `${theme.spaces[2]} ${theme.spaces[3]}`}; // 8px 12px\n align-items: center;\n gap: ${({ theme }) => theme.spaces[2]}; // 8px\n border: 1px solid\n ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary600 : theme.colors.neutral200)};\n border-radius: ${({ theme }) => theme.borderRadius};\n background: ${({ theme, $isSelected }) =>\n $isSelected ? theme.colors.primary100 : theme.colors.neutral0};\n cursor: ${({ $isMovePending, $isInvalidDropTarget }) => {\n if ($isMovePending) {\n return 'wait';\n }\n\n return $isInvalidDropTarget ? 'not-allowed' : 'pointer';\n }};\n opacity: ${({ $isDragging }) => ($isDragging ? 0.4 : 1)};\n pointer-events: ${({ $isMovePending }) => ($isMovePending ? 'none' : 'auto')};\n transition: background 0.2s;\n\n ${({ $isValidDropTarget, theme }) =>\n $isValidDropTarget &&\n css`\n background: ${theme.colors.primary100};\n border: 1px dashed ${theme.colors.primary600};\n `}\n\n &:hover {\n background: ${({ theme }) => theme.colors.primary100};\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n }\n`;\n\nconst FolderIconContainer = styled(Flex)`\n flex-shrink: 0;\n color: ${({ theme }) => theme.colors.neutral600};\n`;\n\nconst FolderName = styled(TruncatedText)`\n flex: 1;\n min-width: 0;\n`;\n\ninterface FolderCardProps {\n folder: Folder;\n orderedItemKeys: ItemKey[];\n}\n\nconst FolderCard = ({ folder, orderedItemKeys }: FolderCardProps) => {\n const { formatMessage } = useIntl();\n const { navigateToFolder } = useFolderNavigation();\n const { isMovePending } = useAssetsDndOptional() ?? { isMovePending: false };\n const { isSelected, toggle, selectRange } = useAssetSelection();\n const { canUpdate } = useMediaLibraryPermissions();\n const {\n dragData,\n draggable: { attributes, listeners, setNodeRef: setDragRef, isDragging },\n droppable: { setNodeRef: setDropRef },\n showValidDropHighlight,\n showInvalidDropCursor,\n } = useFolderDraggableDroppable(folder);\n\n const key = folderKey(folder.id);\n\n const setNodeRef = (node: HTMLElement | null) => {\n setDragRef(node);\n setDropRef(node);\n };\n\n // Folders share the selection mechanism with assets (toggle, range,\n // select-all). Only the plain-click semantic differs: it navigates into the\n // folder instead of selecting it.\n //\n // The containment guard on this and the handlers below is what keeps the\n // actions menu's portaled dialogs — React children of this card — from\n // navigating into the folder or starting a drag.\n const handleClick = (e: React.MouseEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else if (e.metaKey || e.ctrlKey) {\n toggle(key);\n } else {\n navigateToFolder(folder);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.key === 'Enter') {\n e.preventDefault();\n navigateToFolder(folder);\n } else if (e.key === ' ') {\n e.preventDefault();\n toggle(key);\n }\n };\n\n // Checkbox is the pointer path to selection; Shift extends the range.\n const handleCheckboxClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else {\n toggle(key);\n }\n };\n\n return (\n <StyledFolderCard\n ref={setNodeRef}\n {...attributes}\n {...listeners}\n $isDragging={isDragging}\n $isMovePending={isMovePending}\n $isValidDropTarget={showValidDropHighlight}\n $isInvalidDropTarget={showInvalidDropCursor}\n $isSelected={isSelected(key)}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n onPointerDown={(e: React.PointerEvent) => {\n if (isEventFromWithin(e)) {\n listeners?.onPointerDown?.(e);\n }\n }}\n role=\"listitem\"\n tabIndex={0}\n // Right-clicking an item is not the background gesture: the folder keeps\n // the browser's own menu. See MainAreaContextMenu.\n data-native-context-menu\n >\n {canUpdate && (\n <Flex onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()}>\n <Checkbox\n checked={isSelected(key)}\n onClick={handleCheckboxClick}\n aria-label={formatMessage(\n {\n id: getTranslationKey('list.table.row.select'),\n defaultMessage: 'Select {name}',\n },\n { name: folder.name }\n )}\n />\n </Flex>\n )}\n <FolderIconContainer>\n <FolderIcon width={20} height={20} />\n </FolderIconContainer>\n <FolderName textColor=\"neutral800\">{folder.name}</FolderName>\n <Flex onClick={stopCardEvent} onKeyDown={stopCardEvent} onPointerDown={stopCardEvent}>\n <FolderActionsMenu folder={folder} dragData={dragData} />\n </Flex>\n </StyledFolderCard>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetPreview\n * -----------------------------------------------------------------------------------------------*/\n\nconst PreviewContainer = styled(Box)`\n position: relative;\n width: 100%;\n padding-bottom: 62.5%;\n height: 0;\n overflow: hidden;\n background: repeating-conic-gradient(\n ${({ theme }) => theme.colors.neutral100} 0% 25%,\n transparent 0% 50%\n )\n 50% / 20px 20px;\n`;\n\nconst StyledImage = styled.img`\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n`;\n\nconst IconPreview = styled(Flex)`\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n color: ${({ theme }) => theme.colors.neutral500};\n background: ${({ theme }) => theme.colors.neutral100};\n`;\n\ninterface AssetPreviewProps {\n asset: File;\n}\n\nconst AssetPreview = ({ asset }: AssetPreviewProps) => {\n const { alternativeText, ext, formats, mime, url, updatedAt, isLocal, isUrlSigned } = asset;\n\n if (mime?.includes(ASSET_TYPES.Image)) {\n // Replace keeps the same hash/URL, so without a cache-buster the browser\n // serves the stale thumbnail after a Replace Media. `updatedAt`\n // changes on replace, so it re-fetches exactly once per replacement.\n // Skipped for signed URLs — an extra query param invalidates the signature.\n const cacheKey = updatedAt && !isUrlSigned ? new Date(updatedAt).getTime() : undefined;\n const appendCacheBuster = (raw: string) => {\n if (cacheKey === undefined) {\n return raw;\n }\n return raw.includes('?') ? `${raw}&v=${cacheKey}` : `${raw}?v=${cacheKey}`;\n };\n\n const rawMediaURL =\n prefixFileUrlWithBackendUrl(formats?.thumbnail?.url) ?? prefixFileUrlWithBackendUrl(url);\n const mediaURL = rawMediaURL ? appendCacheBuster(rawMediaURL) : rawMediaURL;\n\n if (mediaURL) {\n return (\n <PreviewContainer>\n <StyledImage\n src={mediaURL}\n alt={alternativeText || ''}\n // Only signed remote URLs need crossOrigin (cache collision with\n // the preview). Public/unsigned remote thumbnails must render\n // without a bucket CORS rule. See #26581.\n crossOrigin={!isLocal && isUrlSigned ? 'anonymous' : undefined}\n draggable={false}\n onDragStart={(e) => e.preventDefault()}\n />\n </PreviewContainer>\n );\n }\n }\n\n const DocIcon = getAssetIcon(mime, ext);\n\n return (\n <PreviewContainer>\n <IconPreview justifyContent=\"center\" alignItems=\"center\">\n <DocIcon width={48} height={48} />\n </IconPreview>\n </PreviewContainer>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetCard\n * -----------------------------------------------------------------------------------------------*/\n\nconst StyledCardHeader = styled(CardHeader)`\n position: relative;\n border-bottom: 1px solid ${({ theme }) => theme.colors.neutral200};\n`;\n\nconst CardFooter = styled(Flex)`\n min-width: 0;\n width: 100%;\n`;\n\nconst FileTypeIcon = styled(Flex)`\n color: ${({ theme }) => theme.colors.neutral600};\n flex-shrink: 0;\n`;\n\nconst FileName = styled(TruncatedText)`\n flex: 1;\n min-width: 0;\n`;\n\n// The asset filename is its own interactive element: clicking it opens the\n// details drawer instead of selecting the card (mirrors the table view).\nconst NameButton = styled.button`\n display: inline-flex;\n flex: 1;\n min-width: 0;\n border: none;\n background: transparent;\n padding: 0;\n margin: 0;\n cursor: pointer;\n text-align: left;\n color: inherit;\n font: inherit;\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n border-radius: 2px;\n }\n`;\n\ninterface AssetCardProps {\n asset: File;\n orderedItemKeys: ItemKey[];\n onAssetItemClick: (assetId: number) => void;\n}\n\nconst AssetCard = ({ asset, orderedItemKeys, onAssetItemClick }: AssetCardProps) => {\n const { formatMessage } = useIntl();\n const TypeIcon = getAssetIcon(asset.mime, asset.ext);\n const { isMovePending } = useAssetsDndOptional() ?? { isMovePending: false };\n const { attributes, listeners, setNodeRef, isDragging, dragData } = useFileDraggable(asset);\n const { isSelected, toggle, selectRange } = useAssetSelection();\n const { canUpdate } = useMediaLibraryPermissions();\n const busyMessage = useBusyAssetsOptional()?.getBusyMessage(asset.id) ?? null;\n\n const key = assetKey(asset.id);\n const selected = isSelected(key);\n\n // Plain click opens the asset details; pointer selection lives on the\n // checkbox only. Modifier clicks keep the selection semantics: shift selects\n // a range, cmd/ctrl toggles.\n //\n // The containment guard on this and the handler below is what keeps the\n // actions menu's portaled dialogs — React children of this card — from\n // opening the details drawer or toggling the selection.\n const handleCardClick = (e: React.MouseEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else if (e.metaKey || e.ctrlKey) {\n toggle(key);\n } else {\n onAssetItemClick(asset.id);\n }\n };\n\n // Space toggles selection (additive), Enter opens the details drawer.\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.key === 'Enter') {\n e.preventDefault();\n onAssetItemClick(asset.id);\n } else if (e.key === ' ') {\n e.preventDefault();\n toggle(key);\n }\n };\n\n const handleNameClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n onAssetItemClick(asset.id);\n };\n\n // Same semantics as the table's row checkbox: Shift extends the range,\n // otherwise a plain additive toggle. Never bubbles into the card click.\n const handleCheckboxClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else {\n toggle(key);\n }\n };\n\n return (\n <StyledCard\n ref={setNodeRef}\n {...attributes}\n {...listeners}\n {...ASSET_DETAILS_TRIGGER_PROPS}\n $isDragging={isDragging}\n $isMovePending={isMovePending}\n $isBusy={busyMessage !== null}\n $isSelected={selected}\n tabIndex={0}\n role=\"listitem\"\n // Right-clicking an item is not the background gesture: the card keeps\n // the browser's own menu. See MainAreaContextMenu.\n data-native-context-menu\n onDragStart={(e) => e.preventDefault()}\n onClick={handleCardClick}\n onKeyDown={handleKeyDown}\n onPointerDown={(e: React.PointerEvent) => {\n if (isEventFromWithin(e)) {\n listeners?.onPointerDown?.(e);\n }\n }}\n >\n <StyledCardHeader>\n {canUpdate && (\n <CheckboxOverlay\n {...ASSET_ITEM_CONTROL_PROPS}\n onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()}\n >\n <Checkbox\n checked={selected}\n onClick={handleCheckboxClick}\n aria-label={formatMessage(\n {\n id: getTranslationKey('list.table.row.select'),\n defaultMessage: 'Select {name}',\n },\n { name: asset.name }\n )}\n />\n </CheckboxOverlay>\n )}\n <AssetPreview asset={asset} />\n {/* The header is the card's only positioned ancestor, and it's the part\n worth covering — the footer keeps the name legible while the preview\n is mid-replace. Below the checkbox overlay (z-index 1) would hide\n the spinner, so it sits above it. */}\n {busyMessage !== null ? <BusyOverlay zIndex={2}>{busyMessage}</BusyOverlay> : null}\n </StyledCardHeader>\n <CardBody>\n <CardFooter alignItems=\"center\" gap={2}>\n <FileTypeIcon>\n <TypeIcon width={20} height={20} />\n </FileTypeIcon>\n <NameButton type=\"button\" onClick={handleNameClick}>\n <FileName textColor=\"primary800\">{asset.name}</FileName>\n </NameButton>\n <Flex\n {...ASSET_ITEM_CONTROL_PROPS}\n onClick={stopCardEvent}\n onKeyDown={stopCardEvent}\n onPointerDown={stopCardEvent}\n >\n <AssetActionsMenu asset={asset} dragData={dragData} />\n </Flex>\n </CardFooter>\n </CardBody>\n </StyledCard>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetsGrid\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsGridProps {\n assets: File[];\n folders?: Folder[];\n /** Keys of the rendered items, in render order. Owned by the view. */\n renderedKeys?: ItemKey[];\n onAssetItemClick: (assetId: number) => void;\n}\n\nexport const AssetsGrid = ({\n assets,\n folders = [],\n renderedKeys,\n onAssetItemClick,\n}: AssetsGridProps) => {\n const totalItems = folders.length + assets.length;\n\n // Render order: folders always on top in the grid (mixing is table-only) —\n // range selection follows it.\n const orderedItemKeys: ItemKey[] = renderedKeys ?? buildRenderedKeys({ folders, assets });\n\n // The empty state is owned by the page (`AssetsView` renders `EmptyState`) — an\n // empty grid renders nothing at all.\n if (totalItems === 0) {\n return null;\n }\n\n return (\n <ItemsGrid role=\"list\" data-testid=\"assets-grid\">\n {folders.length > 0 && (\n <FoldersRow>\n <ItemsGrid>\n {folders.map((folder) => (\n <FolderCard\n folder={folder}\n orderedItemKeys={orderedItemKeys}\n key={`folder-${folder.id}`}\n />\n ))}\n </ItemsGrid>\n </FoldersRow>\n )}\n {assets.map((asset) => (\n <Flex key={asset.id} direction=\"column\" alignItems=\"stretch\">\n <AssetCard\n asset={asset}\n orderedItemKeys={orderedItemKeys}\n onAssetItemClick={onAssetItemClick}\n />\n </Flex>\n ))}\n </ItemsGrid>\n );\n};\n"],"names":["stopCardEvent","e","isEventFromWithin","stopPropagation","CheckboxOverlay","styled","Flex","theme","spaces","shadows","filterShadow","StyledCard","Card","$isSelected","colors","primary600","neutral200","$isMovePending","$isBusy","$isDragging","primary100","undefined","FoldersRow","Box","MIN_TILE_WIDTH","ItemsGrid","StyledFolderCard","borderRadius","neutral0","$isInvalidDropTarget","$isValidDropTarget","css","FolderIconContainer","neutral600","FolderName","TruncatedText","FolderCard","folder","orderedItemKeys","formatMessage","useIntl","navigateToFolder","useFolderNavigation","isMovePending","useAssetsDndOptional","isSelected","toggle","selectRange","useAssetSelection","canUpdate","useMediaLibraryPermissions","dragData","draggable","attributes","listeners","setNodeRef","setDragRef","isDragging","droppable","setDropRef","showValidDropHighlight","showInvalidDropCursor","useFolderDraggableDroppable","key","folderKey","id","node","handleClick","shiftKey","metaKey","ctrlKey","handleKeyDown","preventDefault","handleCheckboxClick","_jsxs","ref","onClick","onKeyDown","onPointerDown","role","tabIndex","data-native-context-menu","_jsx","Checkbox","checked","aria-label","getTranslationKey","defaultMessage","name","FolderIcon","width","height","textColor","FolderActionsMenu","PreviewContainer","neutral100","StyledImage","img","IconPreview","neutral500","AssetPreview","asset","alternativeText","ext","formats","mime","url","updatedAt","isLocal","isUrlSigned","includes","ASSET_TYPES","Image","cacheKey","Date","getTime","appendCacheBuster","raw","rawMediaURL","prefixFileUrlWithBackendUrl","thumbnail","mediaURL","src","alt","crossOrigin","onDragStart","DocIcon","getAssetIcon","justifyContent","alignItems","StyledCardHeader","CardHeader","CardFooter","FileTypeIcon","FileName","NameButton","button","AssetCard","onAssetItemClick","TypeIcon","useFileDraggable","busyMessage","useBusyAssetsOptional","getBusyMessage","assetKey","selected","handleCardClick","handleNameClick","ASSET_DETAILS_TRIGGER_PROPS","ASSET_ITEM_CONTROL_PROPS","BusyOverlay","zIndex","CardBody","gap","type","AssetActionsMenu","AssetsGrid","assets","folders","renderedKeys","totalItems","length","buildRenderedKeys","data-testid","map","direction"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AssetsGrid.js","sources":["../../../../../admin/src/pages/Assets/components/AssetsGrid.tsx"],"sourcesContent":["import { Box, Card, CardBody, CardHeader, Checkbox, Flex } from '@strapi/design-system';\nimport { Folder as FolderIcon } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled, css } from 'styled-components';\n\nimport { TruncatedText } from '../../../components/TruncatedText';\nimport { ASSET_TYPES } from '../../../enums';\nimport { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions';\nimport { prefixFileUrlWithBackendUrl } from '../../../utils/files';\nimport { getAssetIcon } from '../../../utils/getAssetIcon';\nimport { isEventFromWithin } from '../../../utils/isEventFromWithin';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { ASSET_DETAILS_TRIGGER_PROPS, ASSET_ITEM_CONTROL_PROPS } from '../constants';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useBusyAssetsOptional } from '../hooks/useBusyAssets';\nimport { useFolderNavigation } from '../hooks/useFolderNavigation';\nimport { buildRenderedKeys } from '../utils/renderedKeys';\nimport { assetKey, folderKey, type ItemKey } from '../utils/selection';\n\nimport { AssetActionsMenu } from './AssetActionsMenu';\nimport { BusyOverlay } from './BusyOverlay';\nimport { useAssetsDndOptional } from './Dnd/AssetsDndProvider';\nimport { useFileDraggable, useFolderDraggableDroppable } from './Dnd/useAssetDnd';\nimport { FolderActionsMenu } from './FolderActionsMenu';\nimport { useItemContextMenuTrigger } from './ItemContextMenu';\n\nimport type { File } from '../../../../../shared/contracts/files';\nimport type { Folder } from '../../../../../shared/contracts/folders';\n\n/* -------------------------------------------------------------------------------------------------\n * AssetsGrid\n * -----------------------------------------------------------------------------------------------*/\n\n// Cards own click, Enter and Space; the controls sitting on them (checkbox, \"...\"\n// menu) must not let those reach the card. Scoped to the wrapper's own DOM\n// subtree so the menu's portaled content — a React child of the wrapper, but\n// never a DOM descendant — still reaches `document`, where Radix listens in\n// order to dismiss its layers.\nconst stopCardEvent = (e: React.SyntheticEvent) => {\n if (isEventFromWithin(e)) {\n e.stopPropagation();\n }\n};\n\n// Top-left selection checkbox overlaid on the asset preview, always visible.\nconst CheckboxOverlay = styled(Flex)`\n position: absolute;\n top: ${({ theme }) => theme.spaces[3]};\n left: ${({ theme }) => theme.spaces[3]};\n z-index: 1;\n box-shadow: ${({ theme }) => theme.shadows.filterShadow};\n`;\n\nconst StyledCard = styled(Card)<{\n $isDragging?: boolean;\n $isMovePending?: boolean;\n $isBusy?: boolean;\n $isSelected?: boolean;\n}>`\n border: 1px solid\n ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary600 : theme.colors.neutral200)};\n border-radius: 8px;\n overflow: hidden;\n isolation: isolate;\n cursor: ${({ $isMovePending, $isBusy }) => ($isMovePending || $isBusy ? 'wait' : 'pointer')};\n opacity: ${({ $isDragging }) => ($isDragging ? 0.4 : 1)};\n /* No opacity change while busy — the overlay does the dimming, and stacking\n one on the other would wash the card out. */\n pointer-events: ${({ $isMovePending, $isBusy }) => ($isMovePending || $isBusy ? 'none' : 'auto')};\n background: ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary100 : undefined)};\n /* Shift+click range selection must not highlight card text. */\n user-select: none;\n\n &:hover {\n background: ${({ theme }) => theme.colors.primary100};\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * FolderCard\n * -----------------------------------------------------------------------------------------------*/\n\nconst FoldersRow = styled(Box)`\n grid-column: 1 / -1;\n`;\n\nconst MIN_TILE_WIDTH = '240px';\n\nconst ItemsGrid = styled(Box)`\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(min(${MIN_TILE_WIDTH}, 100%), 1fr));\n gap: ${({ theme }) => theme.spaces[4]};\n`;\n\nconst StyledFolderCard = styled(Flex)<{\n $isDragging?: boolean;\n $isMovePending?: boolean;\n $isValidDropTarget?: boolean;\n $isInvalidDropTarget?: boolean;\n $isSelected?: boolean;\n}>`\n width: 100%;\n user-select: none;\n padding: ${({ theme }) => `${theme.spaces[2]} ${theme.spaces[3]}`}; // 8px 12px\n align-items: center;\n gap: ${({ theme }) => theme.spaces[2]}; // 8px\n border: 1px solid\n ${({ theme, $isSelected }) => ($isSelected ? theme.colors.primary600 : theme.colors.neutral200)};\n border-radius: ${({ theme }) => theme.borderRadius};\n background: ${({ theme, $isSelected }) =>\n $isSelected ? theme.colors.primary100 : theme.colors.neutral0};\n cursor: ${({ $isMovePending, $isInvalidDropTarget }) => {\n if ($isMovePending) {\n return 'wait';\n }\n\n return $isInvalidDropTarget ? 'not-allowed' : 'pointer';\n }};\n opacity: ${({ $isDragging }) => ($isDragging ? 0.4 : 1)};\n pointer-events: ${({ $isMovePending }) => ($isMovePending ? 'none' : 'auto')};\n transition: background 0.2s;\n\n ${({ $isValidDropTarget, theme }) =>\n $isValidDropTarget &&\n css`\n background: ${theme.colors.primary100};\n border: 1px dashed ${theme.colors.primary600};\n `}\n\n &:hover {\n background: ${({ theme }) => theme.colors.primary100};\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n }\n`;\n\nconst FolderIconContainer = styled(Flex)`\n flex-shrink: 0;\n color: ${({ theme }) => theme.colors.neutral600};\n`;\n\nconst FolderName = styled(TruncatedText)`\n flex: 1;\n min-width: 0;\n`;\n\ninterface FolderCardProps {\n folder: Folder;\n orderedItemKeys: ItemKey[];\n}\n\nconst FolderCard = ({ folder, orderedItemKeys }: FolderCardProps) => {\n const { formatMessage } = useIntl();\n const { navigateToFolder } = useFolderNavigation();\n const { isMovePending } = useAssetsDndOptional() ?? { isMovePending: false };\n const { isSelected, toggle, selectRange } = useAssetSelection();\n const { canUpdate } = useMediaLibraryPermissions();\n const openContextMenu = useItemContextMenuTrigger();\n const {\n dragData,\n draggable: { attributes, listeners, setNodeRef: setDragRef, isDragging },\n droppable: { setNodeRef: setDropRef },\n showValidDropHighlight,\n showInvalidDropCursor,\n } = useFolderDraggableDroppable(folder);\n\n const key = folderKey(folder.id);\n\n const setNodeRef = (node: HTMLElement | null) => {\n setDragRef(node);\n setDropRef(node);\n };\n\n // Folders share the selection mechanism with assets (toggle, range,\n // select-all). Only the plain-click semantic differs: it navigates into the\n // folder instead of selecting it.\n //\n // The containment guard on this and the handlers below is what keeps the\n // actions menu's portaled dialogs — React children of this card — from\n // navigating into the folder or starting a drag.\n const handleClick = (e: React.MouseEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else if (e.metaKey || e.ctrlKey) {\n toggle(key);\n } else {\n navigateToFolder(folder);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.key === 'Enter') {\n e.preventDefault();\n navigateToFolder(folder);\n } else if (e.key === ' ') {\n e.preventDefault();\n toggle(key);\n }\n };\n\n // Checkbox is the pointer path to selection; Shift extends the range.\n const handleCheckboxClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else {\n toggle(key);\n }\n };\n\n return (\n <StyledFolderCard\n ref={setNodeRef}\n {...attributes}\n {...listeners}\n $isDragging={isDragging}\n $isMovePending={isMovePending}\n $isValidDropTarget={showValidDropHighlight}\n $isInvalidDropTarget={showInvalidDropCursor}\n $isSelected={isSelected(key)}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n onPointerDown={(e: React.PointerEvent) => {\n if (isEventFromWithin(e)) {\n listeners?.onPointerDown?.(e);\n }\n }}\n role=\"listitem\"\n tabIndex={0}\n // Right-click opens this folder's own actions, or the selection's when it\n // is part of one. Still marked as \"not the background gesture\" so\n // MainAreaContextMenu's create menu stays out of it.\n data-native-context-menu\n onContextMenu={(e: React.MouseEvent) => {\n if (isEventFromWithin(e)) {\n openContextMenu?.(e, key, { kind: 'folder', folder, dragData });\n }\n }}\n >\n {canUpdate && (\n <Flex onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()}>\n <Checkbox\n checked={isSelected(key)}\n onClick={handleCheckboxClick}\n aria-label={formatMessage(\n {\n id: getTranslationKey('list.table.row.select'),\n defaultMessage: 'Select {name}',\n },\n { name: folder.name }\n )}\n />\n </Flex>\n )}\n <FolderIconContainer>\n <FolderIcon width={20} height={20} />\n </FolderIconContainer>\n <FolderName textColor=\"neutral800\">{folder.name}</FolderName>\n <Flex onClick={stopCardEvent} onKeyDown={stopCardEvent} onPointerDown={stopCardEvent}>\n <FolderActionsMenu folder={folder} dragData={dragData} />\n </Flex>\n </StyledFolderCard>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetPreview\n * -----------------------------------------------------------------------------------------------*/\n\nconst PreviewContainer = styled(Box)`\n position: relative;\n width: 100%;\n padding-bottom: 62.5%;\n height: 0;\n overflow: hidden;\n background: repeating-conic-gradient(\n ${({ theme }) => theme.colors.neutral100} 0% 25%,\n transparent 0% 50%\n )\n 50% / 20px 20px;\n`;\n\nconst StyledImage = styled.img`\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n`;\n\nconst IconPreview = styled(Flex)`\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n color: ${({ theme }) => theme.colors.neutral500};\n background: ${({ theme }) => theme.colors.neutral100};\n`;\n\ninterface AssetPreviewProps {\n asset: File;\n}\n\nconst AssetPreview = ({ asset }: AssetPreviewProps) => {\n const { alternativeText, ext, formats, mime, url, updatedAt, isLocal, isUrlSigned } = asset;\n\n if (mime?.includes(ASSET_TYPES.Image)) {\n // Replace keeps the same hash/URL, so without a cache-buster the browser\n // serves the stale thumbnail after a Replace Media. `updatedAt`\n // changes on replace, so it re-fetches exactly once per replacement.\n // Skipped for signed URLs — an extra query param invalidates the signature.\n const cacheKey = updatedAt && !isUrlSigned ? new Date(updatedAt).getTime() : undefined;\n const appendCacheBuster = (raw: string) => {\n if (cacheKey === undefined) {\n return raw;\n }\n return raw.includes('?') ? `${raw}&v=${cacheKey}` : `${raw}?v=${cacheKey}`;\n };\n\n const rawMediaURL =\n prefixFileUrlWithBackendUrl(formats?.thumbnail?.url) ?? prefixFileUrlWithBackendUrl(url);\n const mediaURL = rawMediaURL ? appendCacheBuster(rawMediaURL) : rawMediaURL;\n\n if (mediaURL) {\n return (\n <PreviewContainer>\n <StyledImage\n src={mediaURL}\n alt={alternativeText || ''}\n // Only signed remote URLs need crossOrigin (cache collision with\n // the preview). Public/unsigned remote thumbnails must render\n // without a bucket CORS rule. See #26581.\n crossOrigin={!isLocal && isUrlSigned ? 'anonymous' : undefined}\n draggable={false}\n onDragStart={(e) => e.preventDefault()}\n />\n </PreviewContainer>\n );\n }\n }\n\n const DocIcon = getAssetIcon(mime, ext);\n\n return (\n <PreviewContainer>\n <IconPreview justifyContent=\"center\" alignItems=\"center\">\n <DocIcon width={48} height={48} />\n </IconPreview>\n </PreviewContainer>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetCard\n * -----------------------------------------------------------------------------------------------*/\n\nconst StyledCardHeader = styled(CardHeader)`\n position: relative;\n border-bottom: 1px solid ${({ theme }) => theme.colors.neutral200};\n`;\n\nconst CardFooter = styled(Flex)`\n min-width: 0;\n width: 100%;\n`;\n\nconst FileTypeIcon = styled(Flex)`\n color: ${({ theme }) => theme.colors.neutral600};\n flex-shrink: 0;\n`;\n\nconst FileName = styled(TruncatedText)`\n flex: 1;\n min-width: 0;\n`;\n\n// The asset filename is its own interactive element: clicking it opens the\n// details drawer instead of selecting the card (mirrors the table view).\nconst NameButton = styled.button`\n display: inline-flex;\n flex: 1;\n min-width: 0;\n border: none;\n background: transparent;\n padding: 0;\n margin: 0;\n cursor: pointer;\n text-align: left;\n color: inherit;\n font: inherit;\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.colors.primary600};\n outline-offset: 2px;\n border-radius: 2px;\n }\n`;\n\ninterface AssetCardProps {\n asset: File;\n orderedItemKeys: ItemKey[];\n onAssetItemClick: (assetId: number) => void;\n}\n\nconst AssetCard = ({ asset, orderedItemKeys, onAssetItemClick }: AssetCardProps) => {\n const { formatMessage } = useIntl();\n const TypeIcon = getAssetIcon(asset.mime, asset.ext);\n const { isMovePending } = useAssetsDndOptional() ?? { isMovePending: false };\n const { attributes, listeners, setNodeRef, isDragging, dragData } = useFileDraggable(asset);\n const { isSelected, toggle, selectRange } = useAssetSelection();\n const { canUpdate } = useMediaLibraryPermissions();\n const busyMessage = useBusyAssetsOptional()?.getBusyMessage(asset.id) ?? null;\n const openContextMenu = useItemContextMenuTrigger();\n\n const key = assetKey(asset.id);\n const selected = isSelected(key);\n\n // Plain click opens the asset details; pointer selection lives on the\n // checkbox only. Modifier clicks keep the selection semantics: shift selects\n // a range, cmd/ctrl toggles.\n //\n // The containment guard on this and the handler below is what keeps the\n // actions menu's portaled dialogs — React children of this card — from\n // opening the details drawer or toggling the selection.\n const handleCardClick = (e: React.MouseEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else if (e.metaKey || e.ctrlKey) {\n toggle(key);\n } else {\n onAssetItemClick(asset.id);\n }\n };\n\n // Space toggles selection (additive), Enter opens the details drawer.\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (!isEventFromWithin(e)) {\n return;\n }\n\n if (e.key === 'Enter') {\n e.preventDefault();\n onAssetItemClick(asset.id);\n } else if (e.key === ' ') {\n e.preventDefault();\n toggle(key);\n }\n };\n\n const handleNameClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n onAssetItemClick(asset.id);\n };\n\n // Same semantics as the table's row checkbox: Shift extends the range,\n // otherwise a plain additive toggle. Never bubbles into the card click.\n const handleCheckboxClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (e.shiftKey) {\n selectRange(orderedItemKeys, key);\n } else {\n toggle(key);\n }\n };\n\n return (\n <StyledCard\n ref={setNodeRef}\n {...attributes}\n {...listeners}\n {...ASSET_DETAILS_TRIGGER_PROPS}\n $isDragging={isDragging}\n $isMovePending={isMovePending}\n $isBusy={busyMessage !== null}\n $isSelected={selected}\n tabIndex={0}\n role=\"listitem\"\n // Right-click opens this asset's own actions, or the selection's when it\n // is part of one. Still marked as \"not the background gesture\" so\n // MainAreaContextMenu's create menu stays out of it.\n data-native-context-menu\n onContextMenu={(e: React.MouseEvent) => {\n if (isEventFromWithin(e)) {\n openContextMenu?.(e, key, { kind: 'asset', asset, dragData });\n }\n }}\n onDragStart={(e) => e.preventDefault()}\n onClick={handleCardClick}\n onKeyDown={handleKeyDown}\n onPointerDown={(e: React.PointerEvent) => {\n if (isEventFromWithin(e)) {\n listeners?.onPointerDown?.(e);\n }\n }}\n >\n <StyledCardHeader>\n {canUpdate && (\n <CheckboxOverlay\n {...ASSET_ITEM_CONTROL_PROPS}\n onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()}\n >\n <Checkbox\n checked={selected}\n onClick={handleCheckboxClick}\n aria-label={formatMessage(\n {\n id: getTranslationKey('list.table.row.select'),\n defaultMessage: 'Select {name}',\n },\n { name: asset.name }\n )}\n />\n </CheckboxOverlay>\n )}\n <AssetPreview asset={asset} />\n {/* The header is the card's only positioned ancestor, and it's the part\n worth covering — the footer keeps the name legible while the preview\n is mid-replace. Below the checkbox overlay (z-index 1) would hide\n the spinner, so it sits above it. */}\n {busyMessage !== null ? <BusyOverlay zIndex={2}>{busyMessage}</BusyOverlay> : null}\n </StyledCardHeader>\n <CardBody>\n <CardFooter alignItems=\"center\" gap={2}>\n <FileTypeIcon>\n <TypeIcon width={20} height={20} />\n </FileTypeIcon>\n <NameButton type=\"button\" onClick={handleNameClick}>\n <FileName textColor=\"primary800\">{asset.name}</FileName>\n </NameButton>\n <Flex\n {...ASSET_ITEM_CONTROL_PROPS}\n onClick={stopCardEvent}\n onKeyDown={stopCardEvent}\n onPointerDown={stopCardEvent}\n >\n <AssetActionsMenu asset={asset} dragData={dragData} />\n </Flex>\n </CardFooter>\n </CardBody>\n </StyledCard>\n );\n};\n\n/* -------------------------------------------------------------------------------------------------\n * AssetsGrid\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsGridProps {\n assets: File[];\n folders?: Folder[];\n /** Keys of the rendered items, in render order. Owned by the view. */\n renderedKeys?: ItemKey[];\n onAssetItemClick: (assetId: number) => void;\n}\n\nexport const AssetsGrid = ({\n assets,\n folders = [],\n renderedKeys,\n onAssetItemClick,\n}: AssetsGridProps) => {\n const totalItems = folders.length + assets.length;\n\n // Render order: folders always on top in the grid (mixing is table-only) —\n // range selection follows it.\n const orderedItemKeys: ItemKey[] = renderedKeys ?? buildRenderedKeys({ folders, assets });\n\n // The empty state is owned by the page (`AssetsView` renders `EmptyState`) — an\n // empty grid renders nothing at all.\n if (totalItems === 0) {\n return null;\n }\n\n return (\n <ItemsGrid role=\"list\" data-testid=\"assets-grid\">\n {folders.length > 0 && (\n <FoldersRow>\n <ItemsGrid>\n {folders.map((folder) => (\n <FolderCard\n folder={folder}\n orderedItemKeys={orderedItemKeys}\n key={`folder-${folder.id}`}\n />\n ))}\n </ItemsGrid>\n </FoldersRow>\n )}\n {assets.map((asset) => (\n <Flex key={asset.id} direction=\"column\" alignItems=\"stretch\">\n <AssetCard\n asset={asset}\n orderedItemKeys={orderedItemKeys}\n onAssetItemClick={onAssetItemClick}\n />\n </Flex>\n ))}\n </ItemsGrid>\n );\n};\n"],"names":["stopCardEvent","e","isEventFromWithin","stopPropagation","CheckboxOverlay","styled","Flex","theme","spaces","shadows","filterShadow","StyledCard","Card","$isSelected","colors","primary600","neutral200","$isMovePending","$isBusy","$isDragging","primary100","undefined","FoldersRow","Box","MIN_TILE_WIDTH","ItemsGrid","StyledFolderCard","borderRadius","neutral0","$isInvalidDropTarget","$isValidDropTarget","css","FolderIconContainer","neutral600","FolderName","TruncatedText","FolderCard","folder","orderedItemKeys","formatMessage","useIntl","navigateToFolder","useFolderNavigation","isMovePending","useAssetsDndOptional","isSelected","toggle","selectRange","useAssetSelection","canUpdate","useMediaLibraryPermissions","openContextMenu","useItemContextMenuTrigger","dragData","draggable","attributes","listeners","setNodeRef","setDragRef","isDragging","droppable","setDropRef","showValidDropHighlight","showInvalidDropCursor","useFolderDraggableDroppable","key","folderKey","id","node","handleClick","shiftKey","metaKey","ctrlKey","handleKeyDown","preventDefault","handleCheckboxClick","_jsxs","ref","onClick","onKeyDown","onPointerDown","role","tabIndex","data-native-context-menu","onContextMenu","kind","_jsx","Checkbox","checked","aria-label","getTranslationKey","defaultMessage","name","FolderIcon","width","height","textColor","FolderActionsMenu","PreviewContainer","neutral100","StyledImage","img","IconPreview","neutral500","AssetPreview","asset","alternativeText","ext","formats","mime","url","updatedAt","isLocal","isUrlSigned","includes","ASSET_TYPES","Image","cacheKey","Date","getTime","appendCacheBuster","raw","rawMediaURL","prefixFileUrlWithBackendUrl","thumbnail","mediaURL","src","alt","crossOrigin","onDragStart","DocIcon","getAssetIcon","justifyContent","alignItems","StyledCardHeader","CardHeader","CardFooter","FileTypeIcon","FileName","NameButton","button","AssetCard","onAssetItemClick","TypeIcon","useFileDraggable","busyMessage","useBusyAssetsOptional","getBusyMessage","assetKey","selected","handleCardClick","handleNameClick","ASSET_DETAILS_TRIGGER_PROPS","ASSET_ITEM_CONTROL_PROPS","BusyOverlay","zIndex","CardBody","gap","type","AssetActionsMenu","AssetsGrid","assets","folders","renderedKeys","totalItems","length","buildRenderedKeys","data-testid","map","direction"],"mappings":"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"}
|
|
@@ -21,6 +21,7 @@ import { BusyOverlay } from './BusyOverlay.mjs';
|
|
|
21
21
|
import { useAssetsDndOptional } from './Dnd/AssetsDndProvider.mjs';
|
|
22
22
|
import { useFolderDraggableDroppable, useFileDraggable } from './Dnd/useAssetDnd.mjs';
|
|
23
23
|
import { FolderActionsMenu } from './FolderActionsMenu.mjs';
|
|
24
|
+
import { useItemContextMenuTrigger } from './ItemContextMenu.mjs';
|
|
24
25
|
|
|
25
26
|
/* -------------------------------------------------------------------------------------------------
|
|
26
27
|
* AssetsGrid
|
|
@@ -127,6 +128,7 @@ const FolderCard = ({ folder, orderedItemKeys })=>{
|
|
|
127
128
|
};
|
|
128
129
|
const { isSelected, toggle, selectRange } = useAssetSelection();
|
|
129
130
|
const { canUpdate } = useMediaLibraryPermissions();
|
|
131
|
+
const openContextMenu = useItemContextMenuTrigger();
|
|
130
132
|
const { dragData, draggable: { attributes, listeners, setNodeRef: setDragRef, isDragging }, droppable: { setNodeRef: setDropRef }, showValidDropHighlight, showInvalidDropCursor } = useFolderDraggableDroppable(folder);
|
|
131
133
|
const key = folderKey(folder.id);
|
|
132
134
|
const setNodeRef = (node)=>{
|
|
@@ -191,9 +193,19 @@ const FolderCard = ({ folder, orderedItemKeys })=>{
|
|
|
191
193
|
},
|
|
192
194
|
role: "listitem",
|
|
193
195
|
tabIndex: 0,
|
|
194
|
-
// Right-
|
|
195
|
-
//
|
|
196
|
+
// Right-click opens this folder's own actions, or the selection's when it
|
|
197
|
+
// is part of one. Still marked as "not the background gesture" so
|
|
198
|
+
// MainAreaContextMenu's create menu stays out of it.
|
|
196
199
|
"data-native-context-menu": true,
|
|
200
|
+
onContextMenu: (e)=>{
|
|
201
|
+
if (isEventFromWithin(e)) {
|
|
202
|
+
openContextMenu?.(e, key, {
|
|
203
|
+
kind: 'folder',
|
|
204
|
+
folder,
|
|
205
|
+
dragData
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
},
|
|
197
209
|
children: [
|
|
198
210
|
canUpdate && /*#__PURE__*/ jsx(Flex, {
|
|
199
211
|
onKeyDown: (e)=>e.stopPropagation(),
|
|
@@ -353,6 +365,7 @@ const AssetCard = ({ asset, orderedItemKeys, onAssetItemClick })=>{
|
|
|
353
365
|
const { isSelected, toggle, selectRange } = useAssetSelection();
|
|
354
366
|
const { canUpdate } = useMediaLibraryPermissions();
|
|
355
367
|
const busyMessage = useBusyAssetsOptional()?.getBusyMessage(asset.id) ?? null;
|
|
368
|
+
const openContextMenu = useItemContextMenuTrigger();
|
|
356
369
|
const key = assetKey(asset.id);
|
|
357
370
|
const selected = isSelected(key);
|
|
358
371
|
// Plain click opens the asset details; pointer selection lives on the
|
|
@@ -412,9 +425,19 @@ const AssetCard = ({ asset, orderedItemKeys, onAssetItemClick })=>{
|
|
|
412
425
|
$isSelected: selected,
|
|
413
426
|
tabIndex: 0,
|
|
414
427
|
role: "listitem",
|
|
415
|
-
// Right-
|
|
416
|
-
//
|
|
428
|
+
// Right-click opens this asset's own actions, or the selection's when it
|
|
429
|
+
// is part of one. Still marked as "not the background gesture" so
|
|
430
|
+
// MainAreaContextMenu's create menu stays out of it.
|
|
417
431
|
"data-native-context-menu": true,
|
|
432
|
+
onContextMenu: (e)=>{
|
|
433
|
+
if (isEventFromWithin(e)) {
|
|
434
|
+
openContextMenu?.(e, key, {
|
|
435
|
+
kind: 'asset',
|
|
436
|
+
asset,
|
|
437
|
+
dragData
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
},
|
|
418
441
|
onDragStart: (e)=>e.preventDefault(),
|
|
419
442
|
onClick: handleCardClick,
|
|
420
443
|
onKeyDown: handleKeyDown,
|