@strapi/upload 5.51.2 → 5.52.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/future/App.js +3 -3
- package/dist/admin/future/App.js.map +1 -1
- package/dist/admin/future/App.mjs +4 -4
- package/dist/admin/future/App.mjs.map +1 -1
- package/dist/admin/future/components/BetaNotice.js +72 -0
- package/dist/admin/future/components/BetaNotice.js.map +1 -0
- package/dist/admin/future/components/BetaNotice.mjs +70 -0
- package/dist/admin/future/components/BetaNotice.mjs.map +1 -0
- package/dist/admin/future/components/Drawer.js +16 -3
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +16 -3
- package/dist/admin/future/components/Drawer.mjs.map +1 -1
- package/dist/admin/future/hooks/useAIMetadataEnabled.js +39 -0
- package/dist/admin/future/hooks/useAIMetadataEnabled.js.map +1 -0
- package/dist/admin/future/hooks/useAIMetadataEnabled.mjs +37 -0
- package/dist/admin/future/hooks/useAIMetadataEnabled.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js +297 -139
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +301 -143
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js +46 -0
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs +44 -0
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +325 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +323 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +35 -14
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +35 -14
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +54 -53
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +54 -54
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +89 -24
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +91 -26
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +15 -3
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +16 -4
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +168 -58
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +171 -61
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +64 -132
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +64 -132
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +123 -76
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +126 -79
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BusyOverlay.js +36 -0
- package/dist/admin/future/pages/Assets/components/BusyOverlay.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs +34 -0
- package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js +131 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs +129 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +27 -17
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +28 -18
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +28 -31
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +28 -31
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +10 -15
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +10 -15
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterBadges.js +13 -4
- package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +13 -4
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterMenu.js +241 -61
- package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +244 -64
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +144 -0
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +142 -0
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.js → FolderFormDialog.js} +50 -16
- package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.mjs → FolderFormDialog.mjs} +52 -18
- package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +111 -191
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +114 -194
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js +47 -0
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs +44 -0
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +203 -62
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +204 -63
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js +75 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs +73 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs.map +1 -0
- package/dist/admin/future/services/api.js +24 -0
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +24 -0
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +11 -0
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +11 -0
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +37 -2
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +37 -3
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/utils/buildDragSetFromSelection.js +7 -5
- package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -1
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs +7 -5
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.js +12 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +12 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -1
- package/dist/admin/future/utils/getRelationId.js +24 -0
- package/dist/admin/future/utils/getRelationId.js.map +1 -0
- package/dist/admin/future/utils/getRelationId.mjs +22 -0
- package/dist/admin/future/utils/getRelationId.mjs.map +1 -0
- package/dist/admin/future/utils/isEventFromWithin.js +14 -0
- package/dist/admin/future/utils/isEventFromWithin.js.map +1 -0
- package/dist/admin/future/utils/isEventFromWithin.mjs +12 -0
- package/dist/admin/future/utils/isEventFromWithin.mjs.map +1 -0
- package/dist/admin/future/utils/itemLocations.js +40 -0
- package/dist/admin/future/utils/itemLocations.js.map +1 -0
- package/dist/admin/future/utils/itemLocations.mjs +36 -0
- package/dist/admin/future/utils/itemLocations.mjs.map +1 -0
- package/dist/admin/future/utils/itemSource.js +36 -0
- package/dist/admin/future/utils/itemSource.js.map +1 -0
- package/dist/admin/future/utils/itemSource.mjs +32 -0
- package/dist/admin/future/utils/itemSource.mjs.map +1 -0
- package/dist/admin/index.js +11 -17
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +12 -18
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/src/future/App.d.ts +1 -1
- package/dist/admin/src/future/components/BetaNotice.d.ts +11 -0
- package/dist/admin/src/future/components/Drawer.d.ts +5 -1
- package/dist/admin/src/future/hooks/useAIMetadataEnabled.d.ts +26 -0
- package/dist/admin/src/future/pages/Assets/components/ActionsMenuContent.d.ts +33 -0
- package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +24 -0
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +16 -0
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +17 -6
- package/dist/admin/src/future/pages/Assets/components/BusyOverlay.d.ts +9 -0
- package/dist/admin/src/future/pages/Assets/components/DeleteItemsDialog.d.ts +27 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +18 -3
- package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +2 -1
- package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +3 -1
- package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +4 -0
- package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +20 -0
- package/dist/admin/src/future/pages/Assets/components/FolderFormDialog.d.ts +16 -0
- package/dist/admin/src/future/pages/Assets/hooks/useBusyAssets.d.ts +38 -0
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +20 -6
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteScrollSentinel.d.ts +41 -0
- package/dist/admin/src/future/services/folders.d.ts +6 -1
- package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +4 -3
- package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +7 -1
- package/dist/admin/src/future/utils/getRelationId.d.ts +11 -0
- package/dist/admin/src/future/utils/isEventFromWithin.d.ts +11 -0
- package/dist/admin/src/future/utils/itemLocations.d.ts +31 -0
- package/dist/admin/src/future/utils/itemSource.d.ts +22 -0
- package/dist/admin/translations/en.json.js +25 -4
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +25 -4
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/src/services/image-manipulation.d.ts +0 -5
- package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
- package/package.json +9 -9
- package/dist/admin/future/pages/Assets/components/CreateFolderDialog.js.map +0 -1
- package/dist/admin/future/pages/Assets/components/CreateFolderDialog.mjs.map +0 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +0 -33
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +0 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +0 -31
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +0 -1
- package/dist/admin/src/future/pages/Assets/components/CreateFolderDialog.d.ts +0 -8
- package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +0 -16
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { useNotification } from '@strapi/admin/strapi-admin';
|
|
4
|
+
import { Dialog, Typography, Button } from '@strapi/design-system';
|
|
5
|
+
import { WarningCircle } from '@strapi/icons';
|
|
6
|
+
import { useIntl } from 'react-intl';
|
|
7
|
+
import { useBulkDeleteItemsMutation } from '../../../services/assets.mjs';
|
|
8
|
+
import { getTranslationKey } from '../../../utils/translations.mjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Confirm dialog for `POST /upload/actions/bulk-delete`. Fully controlled: the
|
|
12
|
+
* caller owns `open` and renders its own trigger. Shared by the bulk actions bar
|
|
13
|
+
* (selection-scoped) and the per-folder actions menu (item-scoped), so the
|
|
14
|
+
* target is always passed in explicitly rather than read from the selection.
|
|
15
|
+
*
|
|
16
|
+
* Deleting a folder cascades server-side, which is why the copy warns about
|
|
17
|
+
* folder contents even for a single item.
|
|
18
|
+
*/ const DeleteItemsDialog = ({ open, onClose, target, onSuccess, onPendingChange })=>{
|
|
19
|
+
const { formatMessage } = useIntl();
|
|
20
|
+
const { toggleNotification } = useNotification();
|
|
21
|
+
const [bulkDeleteItems, { isLoading: isDeleting }] = useBulkDeleteItemsMutation();
|
|
22
|
+
const count = target.fileIds.length + target.folderIds.length;
|
|
23
|
+
useEffect(()=>{
|
|
24
|
+
onPendingChange?.(isDeleting);
|
|
25
|
+
}, [
|
|
26
|
+
isDeleting,
|
|
27
|
+
onPendingChange
|
|
28
|
+
]);
|
|
29
|
+
const handleConfirmDelete = async (e)=>{
|
|
30
|
+
// Radix AlertDialog.Action closes the dialog on click by default; prevent
|
|
31
|
+
// that so the dialog stays open showing the loader while the request runs
|
|
32
|
+
// (a bulk delete can take a while depending on the number of assets).
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
// Guard re-entry while pending.
|
|
35
|
+
if (isDeleting) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const res = await bulkDeleteItems(target);
|
|
39
|
+
if ('error' in res) {
|
|
40
|
+
// Keep the dialog open and the selection intact so the user can retry
|
|
41
|
+
// (Confirm again) or Cancel; only surface the error toast.
|
|
42
|
+
toggleNotification({
|
|
43
|
+
type: 'danger',
|
|
44
|
+
message: formatMessage({
|
|
45
|
+
id: getTranslationKey('list.bulk-actions.delete.error'),
|
|
46
|
+
defaultMessage: 'An error occurred while deleting the items.'
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
onClose();
|
|
52
|
+
toggleNotification({
|
|
53
|
+
type: 'success',
|
|
54
|
+
message: formatMessage({
|
|
55
|
+
id: getTranslationKey('list.bulk-actions.delete.success'),
|
|
56
|
+
defaultMessage: '{count, plural, =1 {# item has been deleted} other {# items have been deleted}}'
|
|
57
|
+
}, {
|
|
58
|
+
count
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
onSuccess?.();
|
|
62
|
+
};
|
|
63
|
+
return /*#__PURE__*/ jsx(Dialog.Root, {
|
|
64
|
+
open: open,
|
|
65
|
+
onOpenChange: (nextOpen)=>{
|
|
66
|
+
// The dialog must stay open while the request runs — a bulk delete
|
|
67
|
+
// can't be aborted halfway, so the loader is the source of truth.
|
|
68
|
+
if (!nextOpen && !isDeleting) {
|
|
69
|
+
onClose();
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
children: /*#__PURE__*/ jsxs(Dialog.Content, {
|
|
73
|
+
children: [
|
|
74
|
+
/*#__PURE__*/ jsx(Dialog.Header, {
|
|
75
|
+
children: formatMessage({
|
|
76
|
+
id: getTranslationKey('list.bulk-actions.delete.confirm.title'),
|
|
77
|
+
defaultMessage: 'Delete {count, plural, =1 {# item} other {# items}}?'
|
|
78
|
+
}, {
|
|
79
|
+
count
|
|
80
|
+
})
|
|
81
|
+
}),
|
|
82
|
+
/*#__PURE__*/ jsx(Dialog.Body, {
|
|
83
|
+
icon: /*#__PURE__*/ jsx(WarningCircle, {
|
|
84
|
+
width: "24px",
|
|
85
|
+
height: "24px",
|
|
86
|
+
fill: "danger600"
|
|
87
|
+
}),
|
|
88
|
+
textAlign: "center",
|
|
89
|
+
children: /*#__PURE__*/ jsx(Typography, {
|
|
90
|
+
children: formatMessage({
|
|
91
|
+
id: getTranslationKey('list.bulk-actions.delete.confirm.description.are-you-sure'),
|
|
92
|
+
defaultMessage: 'These items cannot be recovered once deleted, and deleting a folder also deletes everything inside it. If they are currently in use, linked content will break and image containers will be empty.'
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
}),
|
|
96
|
+
/*#__PURE__*/ jsxs(Dialog.Footer, {
|
|
97
|
+
children: [
|
|
98
|
+
/*#__PURE__*/ jsx(Dialog.Cancel, {
|
|
99
|
+
children: /*#__PURE__*/ jsx(Button, {
|
|
100
|
+
variant: "tertiary",
|
|
101
|
+
disabled: isDeleting,
|
|
102
|
+
fullWidth: true,
|
|
103
|
+
children: formatMessage({
|
|
104
|
+
id: 'app.components.Button.cancel',
|
|
105
|
+
defaultMessage: 'Cancel'
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
}),
|
|
109
|
+
/*#__PURE__*/ jsx(Dialog.Action, {
|
|
110
|
+
children: /*#__PURE__*/ jsx(Button, {
|
|
111
|
+
variant: "danger-light",
|
|
112
|
+
loading: isDeleting,
|
|
113
|
+
onClick: handleConfirmDelete,
|
|
114
|
+
fullWidth: true,
|
|
115
|
+
children: formatMessage({
|
|
116
|
+
id: 'app.components.Button.confirm',
|
|
117
|
+
defaultMessage: 'Confirm'
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
]
|
|
122
|
+
})
|
|
123
|
+
]
|
|
124
|
+
})
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export { DeleteItemsDialog };
|
|
129
|
+
//# sourceMappingURL=DeleteItemsDialog.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeleteItemsDialog.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/DeleteItemsDialog.tsx"],"sourcesContent":["import { useEffect, type MouseEvent } from 'react';\n\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport { Button, Dialog, Typography } from '@strapi/design-system';\nimport { WarningCircle } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { useBulkDeleteItemsMutation } from '../../../services/assets';\nimport { getTranslationKey } from '../../../utils/translations';\n\ninterface DeleteItemsDialogProps {\n open: boolean;\n onClose: () => void;\n /** Explicit delete target — assets and/or folders, by id. */\n target: { fileIds: number[]; folderIds: number[] };\n /** Called after a successful delete, before the toast. */\n onSuccess?: () => void;\n /**\n * Mirrors the in-flight state out to the caller, so a host that has its own\n * controls (the bulk actions bar) can disable them for the duration.\n */\n onPendingChange?: (pending: boolean) => void;\n}\n\n/**\n * Confirm dialog for `POST /upload/actions/bulk-delete`. Fully controlled: the\n * caller owns `open` and renders its own trigger. Shared by the bulk actions bar\n * (selection-scoped) and the per-folder actions menu (item-scoped), so the\n * target is always passed in explicitly rather than read from the selection.\n *\n * Deleting a folder cascades server-side, which is why the copy warns about\n * folder contents even for a single item.\n */\nexport const DeleteItemsDialog = ({\n open,\n onClose,\n target,\n onSuccess,\n onPendingChange,\n}: DeleteItemsDialogProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n const [bulkDeleteItems, { isLoading: isDeleting }] = useBulkDeleteItemsMutation();\n\n const count = target.fileIds.length + target.folderIds.length;\n\n useEffect(() => {\n onPendingChange?.(isDeleting);\n }, [isDeleting, onPendingChange]);\n\n const handleConfirmDelete = async (e: MouseEvent) => {\n // Radix AlertDialog.Action closes the dialog on click by default; prevent\n // that so the dialog stays open showing the loader while the request runs\n // (a bulk delete can take a while depending on the number of assets).\n e.preventDefault();\n\n // Guard re-entry while pending.\n if (isDeleting) {\n return;\n }\n\n const res = await bulkDeleteItems(target);\n\n if ('error' in res) {\n // Keep the dialog open and the selection intact so the user can retry\n // (Confirm again) or Cancel; only surface the error toast.\n toggleNotification({\n type: 'danger',\n message: formatMessage({\n id: getTranslationKey('list.bulk-actions.delete.error'),\n defaultMessage: 'An error occurred while deleting the items.',\n }),\n });\n return;\n }\n\n onClose();\n toggleNotification({\n type: 'success',\n message: formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.delete.success'),\n defaultMessage:\n '{count, plural, =1 {# item has been deleted} other {# items have been deleted}}',\n },\n { count }\n ),\n });\n onSuccess?.();\n };\n\n return (\n <Dialog.Root\n open={open}\n onOpenChange={(nextOpen: boolean) => {\n // The dialog must stay open while the request runs — a bulk delete\n // can't be aborted halfway, so the loader is the source of truth.\n if (!nextOpen && !isDeleting) {\n onClose();\n }\n }}\n >\n <Dialog.Content>\n <Dialog.Header>\n {formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.delete.confirm.title'),\n defaultMessage: 'Delete {count, plural, =1 {# item} other {# items}}?',\n },\n { count }\n )}\n </Dialog.Header>\n <Dialog.Body\n icon={<WarningCircle width=\"24px\" height=\"24px\" fill=\"danger600\" />}\n textAlign=\"center\"\n >\n <Typography>\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.delete.confirm.description.are-you-sure'),\n defaultMessage:\n 'These items cannot be recovered once deleted, and deleting a folder also deletes everything inside it. If they are currently in use, linked content will break and image containers will be empty.',\n })}\n </Typography>\n </Dialog.Body>\n <Dialog.Footer>\n <Dialog.Cancel>\n <Button variant=\"tertiary\" disabled={isDeleting} fullWidth>\n {formatMessage({ id: 'app.components.Button.cancel', defaultMessage: 'Cancel' })}\n </Button>\n </Dialog.Cancel>\n <Dialog.Action>\n <Button\n variant=\"danger-light\"\n loading={isDeleting}\n onClick={handleConfirmDelete}\n fullWidth\n >\n {formatMessage({\n id: 'app.components.Button.confirm',\n defaultMessage: 'Confirm',\n })}\n </Button>\n </Dialog.Action>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog.Root>\n );\n};\n"],"names":["DeleteItemsDialog","open","onClose","target","onSuccess","onPendingChange","formatMessage","useIntl","toggleNotification","useNotification","bulkDeleteItems","isLoading","isDeleting","useBulkDeleteItemsMutation","count","fileIds","length","folderIds","useEffect","handleConfirmDelete","e","preventDefault","res","type","message","id","getTranslationKey","defaultMessage","_jsx","Dialog","Root","onOpenChange","nextOpen","_jsxs","Content","Header","Body","icon","WarningCircle","width","height","fill","textAlign","Typography","Footer","Cancel","Button","variant","disabled","fullWidth","Action","loading","onClick"],"mappings":"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"}
|
|
@@ -13,6 +13,7 @@ var canDropItemOnFolder = require('../../../../utils/canDropItemOnFolder.js');
|
|
|
13
13
|
var formatMoveSuccessMessage = require('../../../../utils/formatMoveSuccessMessage.js');
|
|
14
14
|
var getBulkMoveErrorMessage = require('../../../../utils/getBulkMoveErrorMessage.js');
|
|
15
15
|
var getFolderLabel = require('../../../../utils/getFolderLabel.js');
|
|
16
|
+
var itemLocations = require('../../../../utils/itemLocations.js');
|
|
16
17
|
var translations = require('../../../../utils/translations.js');
|
|
17
18
|
var useAssetSelection = require('../../hooks/useAssetSelection.js');
|
|
18
19
|
var useFolderNavigation = require('../../hooks/useFolderNavigation.js');
|
|
@@ -47,7 +48,7 @@ const resolveDestination = (overId)=>{
|
|
|
47
48
|
// constant-length sensor array while disabling drag for users without the move
|
|
48
49
|
// permission (see the note in the provider).
|
|
49
50
|
const DRAG_DISABLED_DISTANCE = Number.MAX_SAFE_INTEGER;
|
|
50
|
-
const AssetsDndProvider = ({ children })=>{
|
|
51
|
+
const AssetsDndProvider = ({ children, locations = itemLocations.emptyItemLocations })=>{
|
|
51
52
|
const { formatMessage } = reactIntl.useIntl();
|
|
52
53
|
const { toggleNotification } = strapiAdmin.useNotification();
|
|
53
54
|
const selection = useAssetSelection.useAssetSelectionOptional();
|
|
@@ -63,7 +64,9 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
63
64
|
// Sync session for drag-end — state alone can lag one render behind dragStart.
|
|
64
65
|
const dragSessionRef = React.useRef({
|
|
65
66
|
items: [],
|
|
66
|
-
fromSelection: false
|
|
67
|
+
fromSelection: false,
|
|
68
|
+
activeSourceFolderId: null,
|
|
69
|
+
spansMultipleSources: false
|
|
67
70
|
});
|
|
68
71
|
const announceToLiveRegion = React.useCallback((message)=>{
|
|
69
72
|
setLiveAnnouncement('');
|
|
@@ -105,7 +108,9 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
105
108
|
const clearDragState = React.useCallback(()=>{
|
|
106
109
|
dragSessionRef.current = {
|
|
107
110
|
items: [],
|
|
108
|
-
fromSelection: false
|
|
111
|
+
fromSelection: false,
|
|
112
|
+
activeSourceFolderId: null,
|
|
113
|
+
spansMultipleSources: false
|
|
109
114
|
};
|
|
110
115
|
setDragItems([]);
|
|
111
116
|
}, []);
|
|
@@ -115,20 +120,18 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
115
120
|
clearDragState();
|
|
116
121
|
return;
|
|
117
122
|
}
|
|
118
|
-
const
|
|
119
|
-
dragSessionRef.current =
|
|
120
|
-
|
|
121
|
-
fromSelection
|
|
122
|
-
};
|
|
123
|
-
setDragItems(items);
|
|
123
|
+
const dragSet = buildDragSet.buildDragSet(data, selection?.selectedKeys, locations, currentFolderId);
|
|
124
|
+
dragSessionRef.current = dragSet;
|
|
125
|
+
setDragItems(dragSet.items);
|
|
124
126
|
}, [
|
|
125
127
|
clearDragState,
|
|
126
128
|
currentFolderId,
|
|
129
|
+
locations,
|
|
127
130
|
selection?.selectedKeys
|
|
128
131
|
]);
|
|
129
132
|
const handleDragEnd = React.useCallback(async (event)=>{
|
|
130
133
|
const { over } = event;
|
|
131
|
-
const { items, fromSelection } = dragSessionRef.current;
|
|
134
|
+
const { items, fromSelection, activeSourceFolderId, spansMultipleSources } = dragSessionRef.current;
|
|
132
135
|
clearDragState();
|
|
133
136
|
if (isMovePending || !over || items.length === 0) {
|
|
134
137
|
return;
|
|
@@ -146,13 +149,17 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
146
149
|
return;
|
|
147
150
|
}
|
|
148
151
|
const payload = buildBulkMovePayload.buildBulkMovePayload(items);
|
|
149
|
-
// Source is
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
+
// Source is the grabbed row's own location, not the folder currently open:
|
|
153
|
+
// a global search result can be dragged out of a folder the user isn't in.
|
|
154
|
+
// A search-built selection can also span folders, and then no single
|
|
155
|
+
// source is true for the whole set — `null` drops it from the wording
|
|
156
|
+
// instead of naming one item's folder for all of them. Destination is the
|
|
157
|
+
// drop target. Both use leaf names so the DnD toast reads identically to
|
|
158
|
+
// the dialog's.
|
|
152
159
|
const successMessage = formatMoveSuccessMessage.formatMoveSuccessMessage({
|
|
153
160
|
formatMessage,
|
|
154
161
|
count: items.length,
|
|
155
|
-
source: getFolderLabel.getFolderLabel(folderStructure,
|
|
162
|
+
source: spansMultipleSources ? null : getFolderLabel.getFolderLabel(folderStructure, activeSourceFolderId, rootLabel),
|
|
156
163
|
destination: getFolderLabel.getFolderLabel(folderStructure, targetFolderId, rootLabel)
|
|
157
164
|
});
|
|
158
165
|
const errorFallback = formatMessage({
|
|
@@ -189,7 +196,6 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
189
196
|
announceToLiveRegion,
|
|
190
197
|
bulkMove,
|
|
191
198
|
clearDragState,
|
|
192
|
-
currentFolderId,
|
|
193
199
|
folderStructure,
|
|
194
200
|
formatMessage,
|
|
195
201
|
isMovePending,
|
|
@@ -225,7 +231,7 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
225
231
|
if (!destination || !activeData) {
|
|
226
232
|
return '';
|
|
227
233
|
}
|
|
228
|
-
const { items } = buildDragSet.buildDragSet(activeData, selection?.selectedKeys, currentFolderId);
|
|
234
|
+
const { items } = buildDragSet.buildDragSet(activeData, selection?.selectedKeys, locations, currentFolderId);
|
|
229
235
|
if (!canDropItemOnFolder.canDropItemOnFolder({
|
|
230
236
|
items,
|
|
231
237
|
targetFolderId: destination.destinationFolderId,
|
|
@@ -246,6 +252,7 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
246
252
|
currentFolderId,
|
|
247
253
|
folderStructure,
|
|
248
254
|
formatMessage,
|
|
255
|
+
locations,
|
|
249
256
|
selection?.selectedKeys
|
|
250
257
|
]);
|
|
251
258
|
return /*#__PURE__*/ jsxRuntime.jsx(AssetsDndContext.Provider, {
|
|
@@ -265,8 +272,11 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
265
272
|
"aria-atomic": "true",
|
|
266
273
|
children: liveAnnouncement
|
|
267
274
|
}),
|
|
268
|
-
/*#__PURE__*/ jsxRuntime.jsx(designSystem.
|
|
275
|
+
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
|
|
269
276
|
position: "relative",
|
|
277
|
+
alignItems: "stretch",
|
|
278
|
+
direction: "column",
|
|
279
|
+
height: "100%",
|
|
270
280
|
children: children
|
|
271
281
|
}),
|
|
272
282
|
/*#__PURE__*/ jsxRuntime.jsx(core.DragOverlay, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetsDndProvider.js","sources":["../../../../../../../admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n DndContext,\n DragOverlay,\n PointerSensor,\n pointerWithin,\n useSensor,\n useSensors,\n type DragEndEvent,\n type DragStartEvent,\n} from '@dnd-kit/core';\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport { Box, VisuallyHidden } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nimport { useMediaLibraryPermissions } from '../../../../hooks/useMediaLibraryPermissions';\nimport { useBulkMoveMutation, useGetFolderStructureQuery } from '../../../../services/folders';\nimport { buildBulkMovePayload } from '../../../../utils/buildBulkMovePayload';\nimport { canDropItemOnFolder } from '../../../../utils/canDropItemOnFolder';\nimport { formatMoveSuccessMessage } from '../../../../utils/formatMoveSuccessMessage';\nimport { getBulkMoveErrorMessage } from '../../../../utils/getBulkMoveErrorMessage';\nimport { getFolderLabel } from '../../../../utils/getFolderLabel';\nimport { getTranslationKey } from '../../../../utils/translations';\nimport { useAssetSelectionOptional } from '../../hooks/useAssetSelection';\nimport { useFolderNavigation } from '../../hooks/useFolderNavigation';\n\nimport { buildDragSet } from './buildDragSet';\nimport { computeValidDropTargets } from './computeValidDropTargets';\nimport { parseFolderTargetId, parseFolderTreeTargetId } from './dndIds';\nimport { DragOverlayChip } from './DragOverlayChip';\n\nimport type { DragItemData } from '../../../../types/dnd';\n\n/* -------------------------------------------------------------------------------------------------\n * Context — shared with UploadDropZoneProvider for bidirectional drag guard.\n * See UploadDropZoneContext.tsx TODO linking both providers.\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsDndContextValue {\n isInternalDragActive: boolean;\n isMovePending: boolean;\n /** O(1) lookup of whether the current drag set may drop on this destination. */\n isValidDropTarget: (targetFolderId: number | null) => boolean;\n}\n\nconst AssetsDndContext = createContext<AssetsDndContextValue | null>(null);\n\nexport const useAssetsDnd = () => {\n const context = useContext(AssetsDndContext);\n\n if (!context) {\n throw new Error('useAssetsDnd must be used within AssetsDndProvider');\n }\n\n return context;\n};\n\nexport const useAssetsDndOptional = () => useContext(AssetsDndContext);\n\n/* -------------------------------------------------------------------------------------------------\n * Provider\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsDndProviderProps {\n children: ReactNode;\n}\n\ninterface DragSession {\n items: DragItemData[];\n fromSelection: boolean;\n}\n\nconst resolveDestination = (\n overId: string | number\n): { destinationFolderId: number | null } | null => {\n const inView = parseFolderTargetId(overId);\n\n if (inView != null) {\n return { destinationFolderId: inView };\n }\n\n const tree = parseFolderTreeTargetId(overId);\n\n if (tree === 'root') {\n return { destinationFolderId: null };\n }\n\n if (typeof tree === 'number') {\n return { destinationFolderId: tree };\n }\n\n return null;\n};\n\n// Activation distance no real pointer gesture will ever reach — used to keep a\n// constant-length sensor array while disabling drag for users without the move\n// permission (see the note in the provider).\nconst DRAG_DISABLED_DISTANCE = Number.MAX_SAFE_INTEGER;\n\nexport const AssetsDndProvider = ({ children }: AssetsDndProviderProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n const selection = useAssetSelectionOptional();\n const { currentFolderId } = useFolderNavigation();\n const { data: folderStructure = [] } = useGetFolderStructureQuery();\n const rootLabel = formatMessage({\n id: getTranslationKey('plugin.name'),\n defaultMessage: 'Media Library',\n });\n const [bulkMove, { isLoading: isMovePending }] = useBulkMoveMutation();\n const [dragItems, setDragItems] = useState<DragItemData[]>([]);\n const [liveAnnouncement, setLiveAnnouncement] = useState('');\n // Sync session for drag-end — state alone can lag one render behind dragStart.\n const dragSessionRef = useRef<DragSession>({ items: [], fromSelection: false });\n\n const announceToLiveRegion = useCallback((message: string) => {\n setLiveAnnouncement('');\n requestAnimationFrame(() => setLiveAnnouncement(message));\n }, []);\n\n // Moving is a mutation (bulk-move → `assets.update`); without the permission\n // pointer drag must never activate. Keyboard moves go through BulkMoveDialog,\n // which is itself gated.\n //\n // The gate has to keep BOTH the `useSensor`/`useSensors` calls AND the sensor\n // array's length constant across renders: `canUpdate` starts false and flips\n // true once the RBAC check resolves, and dnd-kit keys internal memos/effects\n // on the sensor list, erroring if its size changes (\"argument changed size\n // between renders\"). So adding/removing a sensor is out — instead keep one\n // PointerSensor always and push its activation distance out of reach when the\n // user can't move, which no real drag gesture will ever satisfy.\n const { canUpdate } = useMediaLibraryPermissions();\n const sensors = useSensors(\n useSensor(PointerSensor, {\n activationConstraint: { distance: canUpdate ? 8 : DRAG_DISABLED_DISTANCE },\n })\n );\n\n const validDropTargets = useMemo(\n () => computeValidDropTargets(dragItems, folderStructure),\n [dragItems, folderStructure]\n );\n\n const isValidDropTarget = useCallback(\n (targetFolderId: number | null) => validDropTargets.has(targetFolderId),\n [validDropTargets]\n );\n\n const contextValue = useMemo<AssetsDndContextValue>(\n () => ({\n isInternalDragActive: dragItems.length > 0,\n isMovePending,\n isValidDropTarget,\n }),\n [dragItems.length, isMovePending, isValidDropTarget]\n );\n\n const clearDragState = useCallback(() => {\n dragSessionRef.current = { items: [], fromSelection: false };\n setDragItems([]);\n }, []);\n\n const handleDragStart = useCallback(\n (event: DragStartEvent) => {\n const data = event.active.data.current as DragItemData | undefined;\n\n if (!data) {\n clearDragState();\n return;\n }\n\n const { items, fromSelection } = buildDragSet(data, selection?.selectedKeys, currentFolderId);\n dragSessionRef.current = { items, fromSelection };\n setDragItems(items);\n },\n [clearDragState, currentFolderId, selection?.selectedKeys]\n );\n\n const handleDragEnd = useCallback(\n async (event: DragEndEvent) => {\n const { over } = event;\n const { items, fromSelection } = dragSessionRef.current;\n clearDragState();\n\n if (isMovePending || !over || items.length === 0) {\n return;\n }\n\n const destination = resolveDestination(over.id);\n\n if (!destination) {\n return;\n }\n\n const { destinationFolderId: targetFolderId } = destination;\n\n if (\n !canDropItemOnFolder({\n items,\n targetFolderId,\n folderStructure,\n })\n ) {\n return;\n }\n\n const payload = buildBulkMovePayload(items);\n // Source is always the current folder — dragged items are, by definition,\n // visible in the current view. Destination is the drop target. Both use\n // leaf names so the DnD toast reads identically to the dialog's.\n const successMessage = formatMoveSuccessMessage({\n formatMessage,\n count: items.length,\n source: getFolderLabel(folderStructure, currentFolderId, rootLabel),\n destination: getFolderLabel(folderStructure, targetFolderId, rootLabel),\n });\n const errorFallback = formatMessage({\n id: getTranslationKey('list.bulk-actions.move.error'),\n defaultMessage: 'An error occurred while moving the items.',\n });\n\n try {\n await bulkMove({ ...payload, destinationFolderId: targetFolderId }).unwrap();\n\n if (fromSelection) {\n selection?.clear();\n }\n\n announceToLiveRegion(successMessage);\n\n toggleNotification({\n type: 'success',\n message: successMessage,\n });\n } catch (error) {\n const errorMessage = getBulkMoveErrorMessage(error, errorFallback);\n\n announceToLiveRegion(\n formatMessage(\n {\n id: getTranslationKey('dnd.announce.move-failure'),\n defaultMessage: 'Move failed. {message}',\n },\n { message: errorMessage }\n )\n );\n\n toggleNotification({\n type: 'danger',\n message: errorMessage,\n });\n }\n },\n [\n announceToLiveRegion,\n bulkMove,\n clearDragState,\n currentFolderId,\n folderStructure,\n formatMessage,\n isMovePending,\n rootLabel,\n selection,\n toggleNotification,\n ]\n );\n\n const handleDragCancel = useCallback(() => {\n clearDragState();\n }, [clearDragState]);\n\n const announcements = useMemo(\n () => ({\n onDragStart: ({ active }: DragStartEvent) => {\n const data = active.data.current as DragItemData | undefined;\n return data\n ? formatMessage(\n {\n id: getTranslationKey('dnd.announce.drag-start'),\n defaultMessage: 'Picked up {name}. Drop on a folder to move.',\n },\n { name: data.name }\n )\n : '';\n },\n onDragOver: () => '',\n onDragEnd: ({ active, over }: DragEndEvent) => {\n if (!over) {\n return formatMessage({\n id: getTranslationKey('dnd.announce.cancel'),\n defaultMessage: 'Drag cancelled.',\n });\n }\n\n const destination = resolveDestination(over.id);\n const activeData = active.data.current as DragItemData | undefined;\n\n if (!destination || !activeData) {\n return '';\n }\n\n const { items } = buildDragSet(activeData, selection?.selectedKeys, currentFolderId);\n\n if (\n !canDropItemOnFolder({\n items,\n targetFolderId: destination.destinationFolderId,\n folderStructure,\n })\n ) {\n return formatMessage({\n id: getTranslationKey('dnd.announce.invalid-drop'),\n defaultMessage: 'Cannot move item to this folder.',\n });\n }\n\n return '';\n },\n onDragCancel: () =>\n formatMessage({\n id: getTranslationKey('dnd.announce.cancel'),\n defaultMessage: 'Drag cancelled.',\n }),\n }),\n [currentFolderId, folderStructure, formatMessage, selection?.selectedKeys]\n );\n\n return (\n <AssetsDndContext.Provider value={contextValue}>\n <DndContext\n sensors={sensors}\n collisionDetection={pointerWithin}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n accessibility={{ announcements }}\n >\n <VisuallyHidden aria-live=\"polite\" aria-atomic=\"true\">\n {liveAnnouncement}\n </VisuallyHidden>\n <Box position=\"relative\">{children}</Box>\n <DragOverlay dropAnimation={null}>\n {dragItems.length > 0 ? <DragOverlayChip items={dragItems} /> : null}\n </DragOverlay>\n </DndContext>\n </AssetsDndContext.Provider>\n );\n};\n\n// Keyboard-accessible moves are handled by `BulkMoveDialog` (reachable from the\n// bulk-actions bar): pointer drag is a mouse-only enhancement, the dialog is the\n// a11y path. No `KeyboardSensor` by design.\n"],"names":["AssetsDndContext","createContext","useAssetsDndOptional","useContext","resolveDestination","overId","inView","parseFolderTargetId","destinationFolderId","tree","parseFolderTreeTargetId","DRAG_DISABLED_DISTANCE","Number","MAX_SAFE_INTEGER","AssetsDndProvider","children","formatMessage","useIntl","toggleNotification","useNotification","selection","useAssetSelectionOptional","currentFolderId","useFolderNavigation","data","folderStructure","useGetFolderStructureQuery","rootLabel","id","getTranslationKey","defaultMessage","bulkMove","isLoading","isMovePending","useBulkMoveMutation","dragItems","setDragItems","useState","liveAnnouncement","setLiveAnnouncement","dragSessionRef","useRef","items","fromSelection","announceToLiveRegion","useCallback","message","requestAnimationFrame","canUpdate","useMediaLibraryPermissions","sensors","useSensors","useSensor","PointerSensor","activationConstraint","distance","validDropTargets","useMemo","computeValidDropTargets","isValidDropTarget","targetFolderId","has","contextValue","isInternalDragActive","length","clearDragState","current","handleDragStart","event","active","buildDragSet","selectedKeys","handleDragEnd","over","destination","canDropItemOnFolder","payload","buildBulkMovePayload","successMessage","formatMoveSuccessMessage","count","source","getFolderLabel","errorFallback","unwrap","clear","type","error","errorMessage","getBulkMoveErrorMessage","handleDragCancel","announcements","onDragStart","name","onDragOver","onDragEnd","activeData","onDragCancel","_jsx","Provider","value","_jsxs","DndContext","collisionDetection","pointerWithin","accessibility","VisuallyHidden","aria-live","aria-atomic","Box","position","DragOverlay","dropAnimation","DragOverlayChip"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AssetsDndProvider.js","sources":["../../../../../../../admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n DndContext,\n DragOverlay,\n PointerSensor,\n pointerWithin,\n useSensor,\n useSensors,\n type DragEndEvent,\n type DragStartEvent,\n} from '@dnd-kit/core';\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport { Flex, VisuallyHidden } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nimport { useMediaLibraryPermissions } from '../../../../hooks/useMediaLibraryPermissions';\nimport { useBulkMoveMutation, useGetFolderStructureQuery } from '../../../../services/folders';\nimport { buildBulkMovePayload } from '../../../../utils/buildBulkMovePayload';\nimport { canDropItemOnFolder } from '../../../../utils/canDropItemOnFolder';\nimport { formatMoveSuccessMessage } from '../../../../utils/formatMoveSuccessMessage';\nimport { getBulkMoveErrorMessage } from '../../../../utils/getBulkMoveErrorMessage';\nimport { getFolderLabel } from '../../../../utils/getFolderLabel';\nimport { emptyItemLocations, type ItemLocations } from '../../../../utils/itemLocations';\nimport { getTranslationKey } from '../../../../utils/translations';\nimport { useAssetSelectionOptional } from '../../hooks/useAssetSelection';\nimport { useFolderNavigation } from '../../hooks/useFolderNavigation';\n\nimport { buildDragSet } from './buildDragSet';\nimport { computeValidDropTargets } from './computeValidDropTargets';\nimport { parseFolderTargetId, parseFolderTreeTargetId } from './dndIds';\nimport { DragOverlayChip } from './DragOverlayChip';\n\nimport type { DragItemData } from '../../../../types/dnd';\n\n/* -------------------------------------------------------------------------------------------------\n * Context — shared with UploadDropZoneProvider for bidirectional drag guard.\n * See UploadDropZoneContext.tsx TODO linking both providers.\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsDndContextValue {\n isInternalDragActive: boolean;\n isMovePending: boolean;\n /** O(1) lookup of whether the current drag set may drop on this destination. */\n isValidDropTarget: (targetFolderId: number | null) => boolean;\n}\n\nconst AssetsDndContext = createContext<AssetsDndContextValue | null>(null);\n\nexport const useAssetsDnd = () => {\n const context = useContext(AssetsDndContext);\n\n if (!context) {\n throw new Error('useAssetsDnd must be used within AssetsDndProvider');\n }\n\n return context;\n};\n\nexport const useAssetsDndOptional = () => useContext(AssetsDndContext);\n\n/* -------------------------------------------------------------------------------------------------\n * Provider\n * -----------------------------------------------------------------------------------------------*/\n\ninterface AssetsDndProviderProps {\n children: ReactNode;\n /**\n * Real location of every loaded row, so a multi-item drag validates against\n * each item's own parent. Defaults to a lookup that misses everything, which\n * falls back to the folder currently open.\n */\n locations?: ItemLocations;\n}\n\ninterface DragSession {\n items: DragItemData[];\n fromSelection: boolean;\n /** Location of the grabbed row — names the source in the success toast. */\n activeSourceFolderId: number | null;\n /** True when the set spans several source folders, so none can be named. */\n spansMultipleSources: boolean;\n}\n\nconst resolveDestination = (\n overId: string | number\n): { destinationFolderId: number | null } | null => {\n const inView = parseFolderTargetId(overId);\n\n if (inView != null) {\n return { destinationFolderId: inView };\n }\n\n const tree = parseFolderTreeTargetId(overId);\n\n if (tree === 'root') {\n return { destinationFolderId: null };\n }\n\n if (typeof tree === 'number') {\n return { destinationFolderId: tree };\n }\n\n return null;\n};\n\n// Activation distance no real pointer gesture will ever reach — used to keep a\n// constant-length sensor array while disabling drag for users without the move\n// permission (see the note in the provider).\nconst DRAG_DISABLED_DISTANCE = Number.MAX_SAFE_INTEGER;\n\nexport const AssetsDndProvider = ({\n children,\n locations = emptyItemLocations,\n}: AssetsDndProviderProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n const selection = useAssetSelectionOptional();\n const { currentFolderId } = useFolderNavigation();\n const { data: folderStructure = [] } = useGetFolderStructureQuery();\n const rootLabel = formatMessage({\n id: getTranslationKey('plugin.name'),\n defaultMessage: 'Media Library',\n });\n const [bulkMove, { isLoading: isMovePending }] = useBulkMoveMutation();\n const [dragItems, setDragItems] = useState<DragItemData[]>([]);\n const [liveAnnouncement, setLiveAnnouncement] = useState('');\n // Sync session for drag-end — state alone can lag one render behind dragStart.\n const dragSessionRef = useRef<DragSession>({\n items: [],\n fromSelection: false,\n activeSourceFolderId: null,\n spansMultipleSources: false,\n });\n\n const announceToLiveRegion = useCallback((message: string) => {\n setLiveAnnouncement('');\n requestAnimationFrame(() => setLiveAnnouncement(message));\n }, []);\n\n // Moving is a mutation (bulk-move → `assets.update`); without the permission\n // pointer drag must never activate. Keyboard moves go through BulkMoveDialog,\n // which is itself gated.\n //\n // The gate has to keep BOTH the `useSensor`/`useSensors` calls AND the sensor\n // array's length constant across renders: `canUpdate` starts false and flips\n // true once the RBAC check resolves, and dnd-kit keys internal memos/effects\n // on the sensor list, erroring if its size changes (\"argument changed size\n // between renders\"). So adding/removing a sensor is out — instead keep one\n // PointerSensor always and push its activation distance out of reach when the\n // user can't move, which no real drag gesture will ever satisfy.\n const { canUpdate } = useMediaLibraryPermissions();\n const sensors = useSensors(\n useSensor(PointerSensor, {\n activationConstraint: { distance: canUpdate ? 8 : DRAG_DISABLED_DISTANCE },\n })\n );\n\n const validDropTargets = useMemo(\n () => computeValidDropTargets(dragItems, folderStructure),\n [dragItems, folderStructure]\n );\n\n const isValidDropTarget = useCallback(\n (targetFolderId: number | null) => validDropTargets.has(targetFolderId),\n [validDropTargets]\n );\n\n const contextValue = useMemo<AssetsDndContextValue>(\n () => ({\n isInternalDragActive: dragItems.length > 0,\n isMovePending,\n isValidDropTarget,\n }),\n [dragItems.length, isMovePending, isValidDropTarget]\n );\n\n const clearDragState = useCallback(() => {\n dragSessionRef.current = {\n items: [],\n fromSelection: false,\n activeSourceFolderId: null,\n spansMultipleSources: false,\n };\n setDragItems([]);\n }, []);\n\n const handleDragStart = useCallback(\n (event: DragStartEvent) => {\n const data = event.active.data.current as DragItemData | undefined;\n\n if (!data) {\n clearDragState();\n return;\n }\n\n const dragSet = buildDragSet(data, selection?.selectedKeys, locations, currentFolderId);\n dragSessionRef.current = dragSet;\n setDragItems(dragSet.items);\n },\n [clearDragState, currentFolderId, locations, selection?.selectedKeys]\n );\n\n const handleDragEnd = useCallback(\n async (event: DragEndEvent) => {\n const { over } = event;\n const { items, fromSelection, activeSourceFolderId, spansMultipleSources } =\n dragSessionRef.current;\n clearDragState();\n\n if (isMovePending || !over || items.length === 0) {\n return;\n }\n\n const destination = resolveDestination(over.id);\n\n if (!destination) {\n return;\n }\n\n const { destinationFolderId: targetFolderId } = destination;\n\n if (\n !canDropItemOnFolder({\n items,\n targetFolderId,\n folderStructure,\n })\n ) {\n return;\n }\n\n const payload = buildBulkMovePayload(items);\n // Source is the grabbed row's own location, not the folder currently open:\n // a global search result can be dragged out of a folder the user isn't in.\n // A search-built selection can also span folders, and then no single\n // source is true for the whole set — `null` drops it from the wording\n // instead of naming one item's folder for all of them. Destination is the\n // drop target. Both use leaf names so the DnD toast reads identically to\n // the dialog's.\n const successMessage = formatMoveSuccessMessage({\n formatMessage,\n count: items.length,\n source: spansMultipleSources\n ? null\n : getFolderLabel(folderStructure, activeSourceFolderId, rootLabel),\n destination: getFolderLabel(folderStructure, targetFolderId, rootLabel),\n });\n const errorFallback = formatMessage({\n id: getTranslationKey('list.bulk-actions.move.error'),\n defaultMessage: 'An error occurred while moving the items.',\n });\n\n try {\n await bulkMove({ ...payload, destinationFolderId: targetFolderId }).unwrap();\n\n if (fromSelection) {\n selection?.clear();\n }\n\n announceToLiveRegion(successMessage);\n\n toggleNotification({\n type: 'success',\n message: successMessage,\n });\n } catch (error) {\n const errorMessage = getBulkMoveErrorMessage(error, errorFallback);\n\n announceToLiveRegion(\n formatMessage(\n {\n id: getTranslationKey('dnd.announce.move-failure'),\n defaultMessage: 'Move failed. {message}',\n },\n { message: errorMessage }\n )\n );\n\n toggleNotification({\n type: 'danger',\n message: errorMessage,\n });\n }\n },\n [\n announceToLiveRegion,\n bulkMove,\n clearDragState,\n folderStructure,\n formatMessage,\n isMovePending,\n rootLabel,\n selection,\n toggleNotification,\n ]\n );\n\n const handleDragCancel = useCallback(() => {\n clearDragState();\n }, [clearDragState]);\n\n const announcements = useMemo(\n () => ({\n onDragStart: ({ active }: DragStartEvent) => {\n const data = active.data.current as DragItemData | undefined;\n return data\n ? formatMessage(\n {\n id: getTranslationKey('dnd.announce.drag-start'),\n defaultMessage: 'Picked up {name}. Drop on a folder to move.',\n },\n { name: data.name }\n )\n : '';\n },\n onDragOver: () => '',\n onDragEnd: ({ active, over }: DragEndEvent) => {\n if (!over) {\n return formatMessage({\n id: getTranslationKey('dnd.announce.cancel'),\n defaultMessage: 'Drag cancelled.',\n });\n }\n\n const destination = resolveDestination(over.id);\n const activeData = active.data.current as DragItemData | undefined;\n\n if (!destination || !activeData) {\n return '';\n }\n\n const { items } = buildDragSet(\n activeData,\n selection?.selectedKeys,\n locations,\n currentFolderId\n );\n\n if (\n !canDropItemOnFolder({\n items,\n targetFolderId: destination.destinationFolderId,\n folderStructure,\n })\n ) {\n return formatMessage({\n id: getTranslationKey('dnd.announce.invalid-drop'),\n defaultMessage: 'Cannot move item to this folder.',\n });\n }\n\n return '';\n },\n onDragCancel: () =>\n formatMessage({\n id: getTranslationKey('dnd.announce.cancel'),\n defaultMessage: 'Drag cancelled.',\n }),\n }),\n [currentFolderId, folderStructure, formatMessage, locations, selection?.selectedKeys]\n );\n\n return (\n <AssetsDndContext.Provider value={contextValue}>\n <DndContext\n sensors={sensors}\n collisionDetection={pointerWithin}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n accessibility={{ announcements }}\n >\n <VisuallyHidden aria-live=\"polite\" aria-atomic=\"true\">\n {liveAnnouncement}\n </VisuallyHidden>\n <Flex position=\"relative\" alignItems=\"stretch\" direction=\"column\" height=\"100%\">\n {children}\n </Flex>\n <DragOverlay dropAnimation={null}>\n {dragItems.length > 0 ? <DragOverlayChip items={dragItems} /> : null}\n </DragOverlay>\n </DndContext>\n </AssetsDndContext.Provider>\n );\n};\n\n// Keyboard-accessible moves are handled by `BulkMoveDialog` (reachable from the\n// bulk-actions bar): pointer drag is a mouse-only enhancement, the dialog is the\n// a11y path. No `KeyboardSensor` by design.\n"],"names":["AssetsDndContext","createContext","useAssetsDndOptional","useContext","resolveDestination","overId","inView","parseFolderTargetId","destinationFolderId","tree","parseFolderTreeTargetId","DRAG_DISABLED_DISTANCE","Number","MAX_SAFE_INTEGER","AssetsDndProvider","children","locations","emptyItemLocations","formatMessage","useIntl","toggleNotification","useNotification","selection","useAssetSelectionOptional","currentFolderId","useFolderNavigation","data","folderStructure","useGetFolderStructureQuery","rootLabel","id","getTranslationKey","defaultMessage","bulkMove","isLoading","isMovePending","useBulkMoveMutation","dragItems","setDragItems","useState","liveAnnouncement","setLiveAnnouncement","dragSessionRef","useRef","items","fromSelection","activeSourceFolderId","spansMultipleSources","announceToLiveRegion","useCallback","message","requestAnimationFrame","canUpdate","useMediaLibraryPermissions","sensors","useSensors","useSensor","PointerSensor","activationConstraint","distance","validDropTargets","useMemo","computeValidDropTargets","isValidDropTarget","targetFolderId","has","contextValue","isInternalDragActive","length","clearDragState","current","handleDragStart","event","active","dragSet","buildDragSet","selectedKeys","handleDragEnd","over","destination","canDropItemOnFolder","payload","buildBulkMovePayload","successMessage","formatMoveSuccessMessage","count","source","getFolderLabel","errorFallback","unwrap","clear","type","error","errorMessage","getBulkMoveErrorMessage","handleDragCancel","announcements","onDragStart","name","onDragOver","onDragEnd","activeData","onDragCancel","_jsx","Provider","value","_jsxs","DndContext","collisionDetection","pointerWithin","accessibility","VisuallyHidden","aria-live","aria-atomic","Flex","position","alignItems","direction","height","DragOverlay","dropAnimation","DragOverlayChip"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
|
|
|
2
2
|
import { useState, useRef, useCallback, useMemo, createContext, useContext } from 'react';
|
|
3
3
|
import { useSensors, useSensor, PointerSensor, DndContext, pointerWithin, DragOverlay } from '@dnd-kit/core';
|
|
4
4
|
import { useNotification } from '@strapi/admin/strapi-admin';
|
|
5
|
-
import { VisuallyHidden,
|
|
5
|
+
import { VisuallyHidden, Flex } from '@strapi/design-system';
|
|
6
6
|
import { useIntl } from 'react-intl';
|
|
7
7
|
import { useMediaLibraryPermissions } from '../../../../hooks/useMediaLibraryPermissions.mjs';
|
|
8
8
|
import { useGetFolderStructureQuery, useBulkMoveMutation } from '../../../../services/folders.mjs';
|
|
@@ -11,6 +11,7 @@ import { canDropItemOnFolder } from '../../../../utils/canDropItemOnFolder.mjs';
|
|
|
11
11
|
import { formatMoveSuccessMessage } from '../../../../utils/formatMoveSuccessMessage.mjs';
|
|
12
12
|
import { getBulkMoveErrorMessage } from '../../../../utils/getBulkMoveErrorMessage.mjs';
|
|
13
13
|
import { getFolderLabel } from '../../../../utils/getFolderLabel.mjs';
|
|
14
|
+
import { emptyItemLocations } from '../../../../utils/itemLocations.mjs';
|
|
14
15
|
import { getTranslationKey } from '../../../../utils/translations.mjs';
|
|
15
16
|
import { useAssetSelectionOptional } from '../../hooks/useAssetSelection.mjs';
|
|
16
17
|
import { useFolderNavigation } from '../../hooks/useFolderNavigation.mjs';
|
|
@@ -45,7 +46,7 @@ const resolveDestination = (overId)=>{
|
|
|
45
46
|
// constant-length sensor array while disabling drag for users without the move
|
|
46
47
|
// permission (see the note in the provider).
|
|
47
48
|
const DRAG_DISABLED_DISTANCE = Number.MAX_SAFE_INTEGER;
|
|
48
|
-
const AssetsDndProvider = ({ children })=>{
|
|
49
|
+
const AssetsDndProvider = ({ children, locations = emptyItemLocations })=>{
|
|
49
50
|
const { formatMessage } = useIntl();
|
|
50
51
|
const { toggleNotification } = useNotification();
|
|
51
52
|
const selection = useAssetSelectionOptional();
|
|
@@ -61,7 +62,9 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
61
62
|
// Sync session for drag-end — state alone can lag one render behind dragStart.
|
|
62
63
|
const dragSessionRef = useRef({
|
|
63
64
|
items: [],
|
|
64
|
-
fromSelection: false
|
|
65
|
+
fromSelection: false,
|
|
66
|
+
activeSourceFolderId: null,
|
|
67
|
+
spansMultipleSources: false
|
|
65
68
|
});
|
|
66
69
|
const announceToLiveRegion = useCallback((message)=>{
|
|
67
70
|
setLiveAnnouncement('');
|
|
@@ -103,7 +106,9 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
103
106
|
const clearDragState = useCallback(()=>{
|
|
104
107
|
dragSessionRef.current = {
|
|
105
108
|
items: [],
|
|
106
|
-
fromSelection: false
|
|
109
|
+
fromSelection: false,
|
|
110
|
+
activeSourceFolderId: null,
|
|
111
|
+
spansMultipleSources: false
|
|
107
112
|
};
|
|
108
113
|
setDragItems([]);
|
|
109
114
|
}, []);
|
|
@@ -113,20 +118,18 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
113
118
|
clearDragState();
|
|
114
119
|
return;
|
|
115
120
|
}
|
|
116
|
-
const
|
|
117
|
-
dragSessionRef.current =
|
|
118
|
-
|
|
119
|
-
fromSelection
|
|
120
|
-
};
|
|
121
|
-
setDragItems(items);
|
|
121
|
+
const dragSet = buildDragSet(data, selection?.selectedKeys, locations, currentFolderId);
|
|
122
|
+
dragSessionRef.current = dragSet;
|
|
123
|
+
setDragItems(dragSet.items);
|
|
122
124
|
}, [
|
|
123
125
|
clearDragState,
|
|
124
126
|
currentFolderId,
|
|
127
|
+
locations,
|
|
125
128
|
selection?.selectedKeys
|
|
126
129
|
]);
|
|
127
130
|
const handleDragEnd = useCallback(async (event)=>{
|
|
128
131
|
const { over } = event;
|
|
129
|
-
const { items, fromSelection } = dragSessionRef.current;
|
|
132
|
+
const { items, fromSelection, activeSourceFolderId, spansMultipleSources } = dragSessionRef.current;
|
|
130
133
|
clearDragState();
|
|
131
134
|
if (isMovePending || !over || items.length === 0) {
|
|
132
135
|
return;
|
|
@@ -144,13 +147,17 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
144
147
|
return;
|
|
145
148
|
}
|
|
146
149
|
const payload = buildBulkMovePayload(items);
|
|
147
|
-
// Source is
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
+
// Source is the grabbed row's own location, not the folder currently open:
|
|
151
|
+
// a global search result can be dragged out of a folder the user isn't in.
|
|
152
|
+
// A search-built selection can also span folders, and then no single
|
|
153
|
+
// source is true for the whole set — `null` drops it from the wording
|
|
154
|
+
// instead of naming one item's folder for all of them. Destination is the
|
|
155
|
+
// drop target. Both use leaf names so the DnD toast reads identically to
|
|
156
|
+
// the dialog's.
|
|
150
157
|
const successMessage = formatMoveSuccessMessage({
|
|
151
158
|
formatMessage,
|
|
152
159
|
count: items.length,
|
|
153
|
-
source: getFolderLabel(folderStructure,
|
|
160
|
+
source: spansMultipleSources ? null : getFolderLabel(folderStructure, activeSourceFolderId, rootLabel),
|
|
154
161
|
destination: getFolderLabel(folderStructure, targetFolderId, rootLabel)
|
|
155
162
|
});
|
|
156
163
|
const errorFallback = formatMessage({
|
|
@@ -187,7 +194,6 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
187
194
|
announceToLiveRegion,
|
|
188
195
|
bulkMove,
|
|
189
196
|
clearDragState,
|
|
190
|
-
currentFolderId,
|
|
191
197
|
folderStructure,
|
|
192
198
|
formatMessage,
|
|
193
199
|
isMovePending,
|
|
@@ -223,7 +229,7 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
223
229
|
if (!destination || !activeData) {
|
|
224
230
|
return '';
|
|
225
231
|
}
|
|
226
|
-
const { items } = buildDragSet(activeData, selection?.selectedKeys, currentFolderId);
|
|
232
|
+
const { items } = buildDragSet(activeData, selection?.selectedKeys, locations, currentFolderId);
|
|
227
233
|
if (!canDropItemOnFolder({
|
|
228
234
|
items,
|
|
229
235
|
targetFolderId: destination.destinationFolderId,
|
|
@@ -244,6 +250,7 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
244
250
|
currentFolderId,
|
|
245
251
|
folderStructure,
|
|
246
252
|
formatMessage,
|
|
253
|
+
locations,
|
|
247
254
|
selection?.selectedKeys
|
|
248
255
|
]);
|
|
249
256
|
return /*#__PURE__*/ jsx(AssetsDndContext.Provider, {
|
|
@@ -263,8 +270,11 @@ const AssetsDndProvider = ({ children })=>{
|
|
|
263
270
|
"aria-atomic": "true",
|
|
264
271
|
children: liveAnnouncement
|
|
265
272
|
}),
|
|
266
|
-
/*#__PURE__*/ jsx(
|
|
273
|
+
/*#__PURE__*/ jsx(Flex, {
|
|
267
274
|
position: "relative",
|
|
275
|
+
alignItems: "stretch",
|
|
276
|
+
direction: "column",
|
|
277
|
+
height: "100%",
|
|
268
278
|
children: children
|
|
269
279
|
}),
|
|
270
280
|
/*#__PURE__*/ jsx(DragOverlay, {
|