@strapi/upload 5.50.2 → 5.51.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/future/pages/Assets/AssetsPage.js +201 -137
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +202 -138
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +161 -30
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +163 -32
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +222 -59
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +223 -60
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +252 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +250 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +209 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +207 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +111 -36
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +113 -38
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js +120 -13
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs +120 -13
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +71 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +69 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js +34 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs +32 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js +15 -0
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs +13 -1
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +19 -25
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +19 -25
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js +35 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs +33 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/EmptyState.js +63 -0
- package/dist/admin/future/pages/Assets/components/EmptyState.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs +61 -0
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +120 -22
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +121 -23
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js +22 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs +19 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js +165 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs +163 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +59 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +55 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.js +124 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs +120 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js +29 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +27 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.js +84 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs +81 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/selection.js +104 -0
- package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/selection.mjs +94 -0
- package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -0
- package/dist/admin/future/services/assets.js +33 -1
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +33 -2
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +3 -3
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +3 -3
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/services/uploadFileViaXHR.js +23 -2
- package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -1
- package/dist/admin/future/services/uploadFileViaXHR.mjs +23 -2
- package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -1
- package/dist/admin/future/utils/buildDragSetFromSelection.js +33 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs +31 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -0
- package/dist/admin/future/utils/canDropItemOnFolder.js +4 -0
- package/dist/admin/future/utils/canDropItemOnFolder.js.map +1 -1
- package/dist/admin/future/utils/canDropItemOnFolder.mjs +4 -0
- package/dist/admin/future/utils/canDropItemOnFolder.mjs.map +1 -1
- package/dist/admin/future/utils/flattenFolderStructure.js +28 -0
- package/dist/admin/future/utils/flattenFolderStructure.js.map +1 -0
- package/dist/admin/future/utils/flattenFolderStructure.mjs +26 -0
- package/dist/admin/future/utils/flattenFolderStructure.mjs.map +1 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.js +19 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +17 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -0
- package/dist/admin/future/utils/getFolderLabel.js +31 -0
- package/dist/admin/future/utils/getFolderLabel.js.map +1 -0
- package/dist/admin/future/utils/getFolderLabel.mjs +29 -0
- package/dist/admin/future/utils/getFolderLabel.mjs.map +1 -0
- package/dist/admin/hooks/useUpload.js +15 -9
- package/dist/admin/hooks/useUpload.js.map +1 -1
- package/dist/admin/hooks/useUpload.mjs +17 -11
- package/dist/admin/hooks/useUpload.mjs.map +1 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsGrid.d.ts +1 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +7 -1
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +1 -0
- package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +13 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +2 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/DragOverlayChip.d.ts +2 -2
- package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +17 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/computeValidDropTargets.d.ts +7 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/dndIds.d.ts +3 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +3 -1
- package/dist/admin/src/future/pages/Assets/components/Dnd/useFolderTreeDroppable.d.ts +19 -0
- package/dist/admin/src/future/pages/Assets/components/EmptyState.d.ts +11 -0
- package/dist/admin/src/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +17 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +39 -0
- package/dist/admin/src/future/pages/Assets/hooks/useListSort.d.ts +47 -0
- package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +30 -0
- package/dist/admin/src/future/pages/Assets/utils/mergeMixedList.d.ts +40 -0
- package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +50 -0
- package/dist/admin/src/future/services/assets.d.ts +4 -1
- package/dist/admin/src/future/services/folders.d.ts +4 -1
- package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +1 -17
- package/dist/admin/src/future/types/dnd.d.ts +5 -0
- package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +11 -0
- package/dist/admin/src/future/utils/canDropItemOnFolder.d.ts +1 -1
- package/dist/admin/src/future/utils/flattenFolderStructure.d.ts +18 -0
- package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +14 -0
- package/dist/admin/src/future/utils/getFolderLabel.d.ts +11 -0
- package/dist/admin/translations/en.json.js +36 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +36 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/shared/contracts/folders.d.ts +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
|
6
|
+
var designSystem = require('@strapi/design-system');
|
|
7
|
+
var reactIntl = require('react-intl');
|
|
8
|
+
var styledComponents = require('styled-components');
|
|
9
|
+
var folders = require('../../../services/folders.js');
|
|
10
|
+
var buildDragSetFromSelection = require('../../../utils/buildDragSetFromSelection.js');
|
|
11
|
+
var canDropItemOnFolder = require('../../../utils/canDropItemOnFolder.js');
|
|
12
|
+
var flattenFolderStructure = require('../../../utils/flattenFolderStructure.js');
|
|
13
|
+
var getBulkMoveErrorMessage = require('../../../utils/getBulkMoveErrorMessage.js');
|
|
14
|
+
var translations = require('../../../utils/translations.js');
|
|
15
|
+
var useAssetSelection = require('../hooks/useAssetSelection.js');
|
|
16
|
+
var useFolderNavigation = require('../hooks/useFolderNavigation.js');
|
|
17
|
+
|
|
18
|
+
const StyledModalContent = styledComponents.styled(designSystem.Modal.Content)`
|
|
19
|
+
max-width: 51.6rem;
|
|
20
|
+
`;
|
|
21
|
+
/**
|
|
22
|
+
* "Move elements to" modal for the bulk actions bar. Moves the current
|
|
23
|
+
* selection (assets and/or folders) into the picked destination folder via
|
|
24
|
+
* `POST /upload/actions/bulk-move`. The mutation invalidates the asset list,
|
|
25
|
+
* folder list and the sidebar folder-structure, so both panes refresh on
|
|
26
|
+
* success. On failure the modal stays open so the user can retry or cancel.
|
|
27
|
+
*/ const BulkMoveDialog = ({ open, onClose })=>{
|
|
28
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
29
|
+
const { toggleNotification } = strapiAdmin.useNotification();
|
|
30
|
+
const { selectedIds, selectedFolderIds, clear } = useAssetSelection.useAssetSelection();
|
|
31
|
+
const { currentFolderId } = useFolderNavigation.useFolderNavigation();
|
|
32
|
+
const { data: folderStructure = [] } = folders.useGetFolderStructureQuery(undefined, {
|
|
33
|
+
skip: !open
|
|
34
|
+
});
|
|
35
|
+
const { data: currentFolder } = folders.useGetFolderQuery({
|
|
36
|
+
id: currentFolderId
|
|
37
|
+
}, {
|
|
38
|
+
skip: currentFolderId === null
|
|
39
|
+
});
|
|
40
|
+
const [bulkMove, { isLoading: isMoving }] = folders.useBulkMoveMutation();
|
|
41
|
+
// '' is the DOM-only sentinel for the Media Library root (null everywhere else).
|
|
42
|
+
const [destination, setDestination] = React.useState('');
|
|
43
|
+
React.useEffect(()=>{
|
|
44
|
+
if (open) {
|
|
45
|
+
setDestination('');
|
|
46
|
+
}
|
|
47
|
+
}, [
|
|
48
|
+
open
|
|
49
|
+
]);
|
|
50
|
+
const rootLabel = formatMessage({
|
|
51
|
+
id: translations.getTranslationKey('plugin.name'),
|
|
52
|
+
defaultMessage: 'Media Library'
|
|
53
|
+
});
|
|
54
|
+
// Reconstruct the selection as a drag set so the dialog validates destinations
|
|
55
|
+
// through the exact same canonical predicate as the DnD highlight.
|
|
56
|
+
const dragSet = React.useMemo(()=>buildDragSetFromSelection.buildDragSetFromSelection(selectedIds, selectedFolderIds, currentFolderId), [
|
|
57
|
+
selectedIds,
|
|
58
|
+
selectedFolderIds,
|
|
59
|
+
currentFolderId
|
|
60
|
+
]);
|
|
61
|
+
// Options carry the full ancestry ("About / Images" vs "Tech / Images") so
|
|
62
|
+
// same-named folders stay distinguishable, listed depth-first under their
|
|
63
|
+
// parent. Selected folders and their descendants are pruned during the same
|
|
64
|
+
// single walk — an item can't be moved into itself or below itself (the
|
|
65
|
+
// server would reject it). The current folder (where the items already live)
|
|
66
|
+
// is filtered out too via `canDropItemOnFolder`, so we never offer a no-op
|
|
67
|
+
// move. Memoized: large libraries shouldn't re-walk the tree on every render.
|
|
68
|
+
const destinationOptions = React.useMemo(()=>flattenFolderStructure.flattenFolderStructure(folderStructure, selectedFolderIds).filter((option)=>canDropItemOnFolder.canDropItemOnFolder({
|
|
69
|
+
items: dragSet,
|
|
70
|
+
targetFolderId: option.id,
|
|
71
|
+
folderStructure
|
|
72
|
+
})), [
|
|
73
|
+
folderStructure,
|
|
74
|
+
selectedFolderIds,
|
|
75
|
+
dragSet
|
|
76
|
+
]);
|
|
77
|
+
// Hide the root option when the items already live at root — dropping there
|
|
78
|
+
// would be a no-op. Uses the same predicate as the DnD highlight.
|
|
79
|
+
const canMoveToRoot = React.useMemo(()=>canDropItemOnFolder.canDropItemOnFolder({
|
|
80
|
+
items: dragSet,
|
|
81
|
+
targetFolderId: null,
|
|
82
|
+
folderStructure
|
|
83
|
+
}), [
|
|
84
|
+
dragSet,
|
|
85
|
+
folderStructure
|
|
86
|
+
]);
|
|
87
|
+
const count = selectedIds.size + selectedFolderIds.size;
|
|
88
|
+
const handleMove = async ()=>{
|
|
89
|
+
if (isMoving) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const destinationFolderId = destination === '' ? null : Number(destination);
|
|
93
|
+
try {
|
|
94
|
+
await bulkMove({
|
|
95
|
+
fileIds: Array.from(selectedIds),
|
|
96
|
+
folderIds: Array.from(selectedFolderIds),
|
|
97
|
+
destinationFolderId
|
|
98
|
+
}).unwrap();
|
|
99
|
+
} catch (error) {
|
|
100
|
+
// Keep the modal open and the selection intact so the user can retry
|
|
101
|
+
// (e.g. after picking a valid destination) or cancel. Surface the server
|
|
102
|
+
// message when there is one — it carries actionable causes like moving a
|
|
103
|
+
// folder into its own descendant or a name collision in the destination.
|
|
104
|
+
toggleNotification({
|
|
105
|
+
type: 'danger',
|
|
106
|
+
message: getBulkMoveErrorMessage.getBulkMoveErrorMessage(error, formatMessage({
|
|
107
|
+
id: translations.getTranslationKey('list.bulk-actions.move.error'),
|
|
108
|
+
defaultMessage: 'An error occurred while moving the items.'
|
|
109
|
+
}))
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const sourceName = currentFolderId === null ? rootLabel : currentFolder?.name ?? rootLabel;
|
|
114
|
+
const destinationName = destinationFolderId === null ? rootLabel : destinationOptions.find((option)=>option.id === destinationFolderId)?.label ?? rootLabel;
|
|
115
|
+
toggleNotification({
|
|
116
|
+
type: 'success',
|
|
117
|
+
message: formatMessage({
|
|
118
|
+
id: translations.getTranslationKey('list.bulk-actions.move.success'),
|
|
119
|
+
defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}'
|
|
120
|
+
}, {
|
|
121
|
+
count,
|
|
122
|
+
source: sourceName,
|
|
123
|
+
destination: destinationName
|
|
124
|
+
})
|
|
125
|
+
});
|
|
126
|
+
clear();
|
|
127
|
+
onClose();
|
|
128
|
+
};
|
|
129
|
+
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Root, {
|
|
130
|
+
open: open,
|
|
131
|
+
onOpenChange: (nextOpen)=>{
|
|
132
|
+
// The modal must stay open while the request runs — the loader on the
|
|
133
|
+
// Move button is the source of truth for the pending state.
|
|
134
|
+
if (!nextOpen && !isMoving) {
|
|
135
|
+
onClose();
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
children: /*#__PURE__*/ jsxRuntime.jsxs(StyledModalContent, {
|
|
139
|
+
children: [
|
|
140
|
+
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Header, {
|
|
141
|
+
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Title, {
|
|
142
|
+
children: formatMessage({
|
|
143
|
+
id: translations.getTranslationKey('list.bulk-actions.move.title'),
|
|
144
|
+
defaultMessage: 'Move elements to'
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
}),
|
|
148
|
+
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Body, {
|
|
149
|
+
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
|
|
150
|
+
name: "destination",
|
|
151
|
+
children: [
|
|
152
|
+
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
|
|
153
|
+
children: formatMessage({
|
|
154
|
+
id: translations.getTranslationKey('list.bulk-actions.move.location'),
|
|
155
|
+
defaultMessage: 'Location'
|
|
156
|
+
})
|
|
157
|
+
}),
|
|
158
|
+
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.SingleSelect, {
|
|
159
|
+
value: destination,
|
|
160
|
+
onChange: (value)=>setDestination(String(value)),
|
|
161
|
+
disabled: isMoving,
|
|
162
|
+
children: [
|
|
163
|
+
canMoveToRoot && /*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
|
|
164
|
+
value: "",
|
|
165
|
+
children: rootLabel
|
|
166
|
+
}),
|
|
167
|
+
destinationOptions.map((option)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
|
|
168
|
+
value: String(option.id),
|
|
169
|
+
children: option.label
|
|
170
|
+
}, option.id))
|
|
171
|
+
]
|
|
172
|
+
})
|
|
173
|
+
]
|
|
174
|
+
})
|
|
175
|
+
}),
|
|
176
|
+
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Footer, {
|
|
177
|
+
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
|
|
178
|
+
gap: 2,
|
|
179
|
+
justifyContent: "space-between",
|
|
180
|
+
width: "100%",
|
|
181
|
+
children: [
|
|
182
|
+
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
|
|
183
|
+
variant: "tertiary",
|
|
184
|
+
onClick: onClose,
|
|
185
|
+
disabled: isMoving,
|
|
186
|
+
type: "button",
|
|
187
|
+
children: formatMessage({
|
|
188
|
+
id: 'app.components.Button.cancel',
|
|
189
|
+
defaultMessage: 'Cancel'
|
|
190
|
+
})
|
|
191
|
+
}),
|
|
192
|
+
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
|
|
193
|
+
onClick: handleMove,
|
|
194
|
+
loading: isMoving,
|
|
195
|
+
children: formatMessage({
|
|
196
|
+
id: translations.getTranslationKey('list.bulk-actions.move.submit'),
|
|
197
|
+
defaultMessage: 'Move'
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
]
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
]
|
|
204
|
+
})
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
exports.BulkMoveDialog = BulkMoveDialog;
|
|
209
|
+
//# sourceMappingURL=BulkMoveDialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BulkMoveDialog.js","sources":["../../../../../../admin/src/future/pages/Assets/components/BulkMoveDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\n\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport {\n Button,\n Field,\n Flex,\n Modal,\n SingleSelect,\n SingleSelectOption,\n} from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport {\n useBulkMoveMutation,\n useGetFolderQuery,\n useGetFolderStructureQuery,\n} from '../../../services/folders';\nimport { buildDragSetFromSelection } from '../../../utils/buildDragSetFromSelection';\nimport { canDropItemOnFolder } from '../../../utils/canDropItemOnFolder';\nimport { flattenFolderStructure } from '../../../utils/flattenFolderStructure';\nimport { getBulkMoveErrorMessage } from '../../../utils/getBulkMoveErrorMessage';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useFolderNavigation } from '../hooks/useFolderNavigation';\n\nconst StyledModalContent = styled(Modal.Content)`\n max-width: 51.6rem;\n`;\n\ninterface BulkMoveDialogProps {\n open: boolean;\n onClose: () => void;\n}\n\n/**\n * \"Move elements to\" modal for the bulk actions bar. Moves the current\n * selection (assets and/or folders) into the picked destination folder via\n * `POST /upload/actions/bulk-move`. The mutation invalidates the asset list,\n * folder list and the sidebar folder-structure, so both panes refresh on\n * success. On failure the modal stays open so the user can retry or cancel.\n */\nexport const BulkMoveDialog = ({ open, onClose }: BulkMoveDialogProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n const { selectedIds, selectedFolderIds, clear } = useAssetSelection();\n const { currentFolderId } = useFolderNavigation();\n const { data: folderStructure = [] } = useGetFolderStructureQuery(undefined, { skip: !open });\n const { data: currentFolder } = useGetFolderQuery(\n { id: currentFolderId! },\n { skip: currentFolderId === null }\n );\n const [bulkMove, { isLoading: isMoving }] = useBulkMoveMutation();\n\n // '' is the DOM-only sentinel for the Media Library root (null everywhere else).\n const [destination, setDestination] = useState<string>('');\n\n useEffect(() => {\n if (open) {\n setDestination('');\n }\n }, [open]);\n\n const rootLabel = formatMessage({\n id: getTranslationKey('plugin.name'),\n defaultMessage: 'Media Library',\n });\n\n // Reconstruct the selection as a drag set so the dialog validates destinations\n // through the exact same canonical predicate as the DnD highlight.\n const dragSet = useMemo(\n () => buildDragSetFromSelection(selectedIds, selectedFolderIds, currentFolderId),\n [selectedIds, selectedFolderIds, currentFolderId]\n );\n\n // Options carry the full ancestry (\"About / Images\" vs \"Tech / Images\") so\n // same-named folders stay distinguishable, listed depth-first under their\n // parent. Selected folders and their descendants are pruned during the same\n // single walk — an item can't be moved into itself or below itself (the\n // server would reject it). The current folder (where the items already live)\n // is filtered out too via `canDropItemOnFolder`, so we never offer a no-op\n // move. Memoized: large libraries shouldn't re-walk the tree on every render.\n const destinationOptions = useMemo(\n () =>\n flattenFolderStructure(folderStructure, selectedFolderIds).filter((option) =>\n canDropItemOnFolder({ items: dragSet, targetFolderId: option.id, folderStructure })\n ),\n [folderStructure, selectedFolderIds, dragSet]\n );\n\n // Hide the root option when the items already live at root — dropping there\n // would be a no-op. Uses the same predicate as the DnD highlight.\n const canMoveToRoot = useMemo(\n () => canDropItemOnFolder({ items: dragSet, targetFolderId: null, folderStructure }),\n [dragSet, folderStructure]\n );\n\n const count = selectedIds.size + selectedFolderIds.size;\n\n const handleMove = async () => {\n if (isMoving) {\n return;\n }\n\n const destinationFolderId = destination === '' ? null : Number(destination);\n\n try {\n await bulkMove({\n fileIds: Array.from(selectedIds),\n folderIds: Array.from(selectedFolderIds),\n destinationFolderId,\n }).unwrap();\n } catch (error) {\n // Keep the modal open and the selection intact so the user can retry\n // (e.g. after picking a valid destination) or cancel. Surface the server\n // message when there is one — it carries actionable causes like moving a\n // folder into its own descendant or a name collision in the destination.\n toggleNotification({\n type: 'danger',\n message: getBulkMoveErrorMessage(\n error,\n formatMessage({\n id: getTranslationKey('list.bulk-actions.move.error'),\n defaultMessage: 'An error occurred while moving the items.',\n })\n ),\n });\n return;\n }\n\n const sourceName = currentFolderId === null ? rootLabel : (currentFolder?.name ?? rootLabel);\n const destinationName =\n destinationFolderId === null\n ? rootLabel\n : (destinationOptions.find((option) => option.id === destinationFolderId)?.label ??\n rootLabel);\n\n toggleNotification({\n type: 'success',\n message: formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source: sourceName, destination: destinationName }\n ),\n });\n clear();\n onClose();\n };\n\n return (\n <Modal.Root\n open={open}\n onOpenChange={(nextOpen: boolean) => {\n // The modal must stay open while the request runs — the loader on the\n // Move button is the source of truth for the pending state.\n if (!nextOpen && !isMoving) {\n onClose();\n }\n }}\n >\n <StyledModalContent>\n <Modal.Header>\n <Modal.Title>\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.move.title'),\n defaultMessage: 'Move elements to',\n })}\n </Modal.Title>\n </Modal.Header>\n <Modal.Body>\n <Field.Root name=\"destination\">\n <Field.Label>\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.move.location'),\n defaultMessage: 'Location',\n })}\n </Field.Label>\n <SingleSelect\n value={destination}\n onChange={(value) => setDestination(String(value))}\n disabled={isMoving}\n >\n {canMoveToRoot && <SingleSelectOption value=\"\">{rootLabel}</SingleSelectOption>}\n {destinationOptions.map((option) => (\n <SingleSelectOption key={option.id} value={String(option.id)}>\n {option.label}\n </SingleSelectOption>\n ))}\n </SingleSelect>\n </Field.Root>\n </Modal.Body>\n <Modal.Footer>\n <Flex gap={2} justifyContent=\"space-between\" width=\"100%\">\n <Button variant=\"tertiary\" onClick={onClose} disabled={isMoving} type=\"button\">\n {formatMessage({ id: 'app.components.Button.cancel', defaultMessage: 'Cancel' })}\n </Button>\n <Button onClick={handleMove} loading={isMoving}>\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.move.submit'),\n defaultMessage: 'Move',\n })}\n </Button>\n </Flex>\n </Modal.Footer>\n </StyledModalContent>\n </Modal.Root>\n );\n};\n"],"names":["StyledModalContent","styled","Modal","Content","BulkMoveDialog","open","onClose","formatMessage","useIntl","toggleNotification","useNotification","selectedIds","selectedFolderIds","clear","useAssetSelection","currentFolderId","useFolderNavigation","data","folderStructure","useGetFolderStructureQuery","undefined","skip","currentFolder","useGetFolderQuery","id","bulkMove","isLoading","isMoving","useBulkMoveMutation","destination","setDestination","useState","useEffect","rootLabel","getTranslationKey","defaultMessage","dragSet","useMemo","buildDragSetFromSelection","destinationOptions","flattenFolderStructure","filter","option","canDropItemOnFolder","items","targetFolderId","canMoveToRoot","count","size","handleMove","destinationFolderId","Number","fileIds","Array","from","folderIds","unwrap","error","type","message","getBulkMoveErrorMessage","sourceName","name","destinationName","find","label","source","_jsx","Root","onOpenChange","nextOpen","_jsxs","Header","Title","Body","Field","Label","SingleSelect","value","onChange","String","disabled","SingleSelectOption","map","Footer","Flex","gap","justifyContent","width","Button","variant","onClick","loading"],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
3
|
+
import { useNotification } from '@strapi/admin/strapi-admin';
|
|
4
|
+
import { Modal, Field, SingleSelect, SingleSelectOption, Flex, Button } from '@strapi/design-system';
|
|
5
|
+
import { useIntl } from 'react-intl';
|
|
6
|
+
import { styled } from 'styled-components';
|
|
7
|
+
import { useGetFolderStructureQuery, useGetFolderQuery, useBulkMoveMutation } from '../../../services/folders.mjs';
|
|
8
|
+
import { buildDragSetFromSelection } from '../../../utils/buildDragSetFromSelection.mjs';
|
|
9
|
+
import { canDropItemOnFolder } from '../../../utils/canDropItemOnFolder.mjs';
|
|
10
|
+
import { flattenFolderStructure } from '../../../utils/flattenFolderStructure.mjs';
|
|
11
|
+
import { getBulkMoveErrorMessage } from '../../../utils/getBulkMoveErrorMessage.mjs';
|
|
12
|
+
import { getTranslationKey } from '../../../utils/translations.mjs';
|
|
13
|
+
import { useAssetSelection } from '../hooks/useAssetSelection.mjs';
|
|
14
|
+
import { useFolderNavigation } from '../hooks/useFolderNavigation.mjs';
|
|
15
|
+
|
|
16
|
+
const StyledModalContent = styled(Modal.Content)`
|
|
17
|
+
max-width: 51.6rem;
|
|
18
|
+
`;
|
|
19
|
+
/**
|
|
20
|
+
* "Move elements to" modal for the bulk actions bar. Moves the current
|
|
21
|
+
* selection (assets and/or folders) into the picked destination folder via
|
|
22
|
+
* `POST /upload/actions/bulk-move`. The mutation invalidates the asset list,
|
|
23
|
+
* folder list and the sidebar folder-structure, so both panes refresh on
|
|
24
|
+
* success. On failure the modal stays open so the user can retry or cancel.
|
|
25
|
+
*/ const BulkMoveDialog = ({ open, onClose })=>{
|
|
26
|
+
const { formatMessage } = useIntl();
|
|
27
|
+
const { toggleNotification } = useNotification();
|
|
28
|
+
const { selectedIds, selectedFolderIds, clear } = useAssetSelection();
|
|
29
|
+
const { currentFolderId } = useFolderNavigation();
|
|
30
|
+
const { data: folderStructure = [] } = useGetFolderStructureQuery(undefined, {
|
|
31
|
+
skip: !open
|
|
32
|
+
});
|
|
33
|
+
const { data: currentFolder } = useGetFolderQuery({
|
|
34
|
+
id: currentFolderId
|
|
35
|
+
}, {
|
|
36
|
+
skip: currentFolderId === null
|
|
37
|
+
});
|
|
38
|
+
const [bulkMove, { isLoading: isMoving }] = useBulkMoveMutation();
|
|
39
|
+
// '' is the DOM-only sentinel for the Media Library root (null everywhere else).
|
|
40
|
+
const [destination, setDestination] = useState('');
|
|
41
|
+
useEffect(()=>{
|
|
42
|
+
if (open) {
|
|
43
|
+
setDestination('');
|
|
44
|
+
}
|
|
45
|
+
}, [
|
|
46
|
+
open
|
|
47
|
+
]);
|
|
48
|
+
const rootLabel = formatMessage({
|
|
49
|
+
id: getTranslationKey('plugin.name'),
|
|
50
|
+
defaultMessage: 'Media Library'
|
|
51
|
+
});
|
|
52
|
+
// Reconstruct the selection as a drag set so the dialog validates destinations
|
|
53
|
+
// through the exact same canonical predicate as the DnD highlight.
|
|
54
|
+
const dragSet = useMemo(()=>buildDragSetFromSelection(selectedIds, selectedFolderIds, currentFolderId), [
|
|
55
|
+
selectedIds,
|
|
56
|
+
selectedFolderIds,
|
|
57
|
+
currentFolderId
|
|
58
|
+
]);
|
|
59
|
+
// Options carry the full ancestry ("About / Images" vs "Tech / Images") so
|
|
60
|
+
// same-named folders stay distinguishable, listed depth-first under their
|
|
61
|
+
// parent. Selected folders and their descendants are pruned during the same
|
|
62
|
+
// single walk — an item can't be moved into itself or below itself (the
|
|
63
|
+
// server would reject it). The current folder (where the items already live)
|
|
64
|
+
// is filtered out too via `canDropItemOnFolder`, so we never offer a no-op
|
|
65
|
+
// move. Memoized: large libraries shouldn't re-walk the tree on every render.
|
|
66
|
+
const destinationOptions = useMemo(()=>flattenFolderStructure(folderStructure, selectedFolderIds).filter((option)=>canDropItemOnFolder({
|
|
67
|
+
items: dragSet,
|
|
68
|
+
targetFolderId: option.id,
|
|
69
|
+
folderStructure
|
|
70
|
+
})), [
|
|
71
|
+
folderStructure,
|
|
72
|
+
selectedFolderIds,
|
|
73
|
+
dragSet
|
|
74
|
+
]);
|
|
75
|
+
// Hide the root option when the items already live at root — dropping there
|
|
76
|
+
// would be a no-op. Uses the same predicate as the DnD highlight.
|
|
77
|
+
const canMoveToRoot = useMemo(()=>canDropItemOnFolder({
|
|
78
|
+
items: dragSet,
|
|
79
|
+
targetFolderId: null,
|
|
80
|
+
folderStructure
|
|
81
|
+
}), [
|
|
82
|
+
dragSet,
|
|
83
|
+
folderStructure
|
|
84
|
+
]);
|
|
85
|
+
const count = selectedIds.size + selectedFolderIds.size;
|
|
86
|
+
const handleMove = async ()=>{
|
|
87
|
+
if (isMoving) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const destinationFolderId = destination === '' ? null : Number(destination);
|
|
91
|
+
try {
|
|
92
|
+
await bulkMove({
|
|
93
|
+
fileIds: Array.from(selectedIds),
|
|
94
|
+
folderIds: Array.from(selectedFolderIds),
|
|
95
|
+
destinationFolderId
|
|
96
|
+
}).unwrap();
|
|
97
|
+
} catch (error) {
|
|
98
|
+
// Keep the modal open and the selection intact so the user can retry
|
|
99
|
+
// (e.g. after picking a valid destination) or cancel. Surface the server
|
|
100
|
+
// message when there is one — it carries actionable causes like moving a
|
|
101
|
+
// folder into its own descendant or a name collision in the destination.
|
|
102
|
+
toggleNotification({
|
|
103
|
+
type: 'danger',
|
|
104
|
+
message: getBulkMoveErrorMessage(error, formatMessage({
|
|
105
|
+
id: getTranslationKey('list.bulk-actions.move.error'),
|
|
106
|
+
defaultMessage: 'An error occurred while moving the items.'
|
|
107
|
+
}))
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const sourceName = currentFolderId === null ? rootLabel : currentFolder?.name ?? rootLabel;
|
|
112
|
+
const destinationName = destinationFolderId === null ? rootLabel : destinationOptions.find((option)=>option.id === destinationFolderId)?.label ?? rootLabel;
|
|
113
|
+
toggleNotification({
|
|
114
|
+
type: 'success',
|
|
115
|
+
message: formatMessage({
|
|
116
|
+
id: getTranslationKey('list.bulk-actions.move.success'),
|
|
117
|
+
defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}'
|
|
118
|
+
}, {
|
|
119
|
+
count,
|
|
120
|
+
source: sourceName,
|
|
121
|
+
destination: destinationName
|
|
122
|
+
})
|
|
123
|
+
});
|
|
124
|
+
clear();
|
|
125
|
+
onClose();
|
|
126
|
+
};
|
|
127
|
+
return /*#__PURE__*/ jsx(Modal.Root, {
|
|
128
|
+
open: open,
|
|
129
|
+
onOpenChange: (nextOpen)=>{
|
|
130
|
+
// The modal must stay open while the request runs — the loader on the
|
|
131
|
+
// Move button is the source of truth for the pending state.
|
|
132
|
+
if (!nextOpen && !isMoving) {
|
|
133
|
+
onClose();
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
children: /*#__PURE__*/ jsxs(StyledModalContent, {
|
|
137
|
+
children: [
|
|
138
|
+
/*#__PURE__*/ jsx(Modal.Header, {
|
|
139
|
+
children: /*#__PURE__*/ jsx(Modal.Title, {
|
|
140
|
+
children: formatMessage({
|
|
141
|
+
id: getTranslationKey('list.bulk-actions.move.title'),
|
|
142
|
+
defaultMessage: 'Move elements to'
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
}),
|
|
146
|
+
/*#__PURE__*/ jsx(Modal.Body, {
|
|
147
|
+
children: /*#__PURE__*/ jsxs(Field.Root, {
|
|
148
|
+
name: "destination",
|
|
149
|
+
children: [
|
|
150
|
+
/*#__PURE__*/ jsx(Field.Label, {
|
|
151
|
+
children: formatMessage({
|
|
152
|
+
id: getTranslationKey('list.bulk-actions.move.location'),
|
|
153
|
+
defaultMessage: 'Location'
|
|
154
|
+
})
|
|
155
|
+
}),
|
|
156
|
+
/*#__PURE__*/ jsxs(SingleSelect, {
|
|
157
|
+
value: destination,
|
|
158
|
+
onChange: (value)=>setDestination(String(value)),
|
|
159
|
+
disabled: isMoving,
|
|
160
|
+
children: [
|
|
161
|
+
canMoveToRoot && /*#__PURE__*/ jsx(SingleSelectOption, {
|
|
162
|
+
value: "",
|
|
163
|
+
children: rootLabel
|
|
164
|
+
}),
|
|
165
|
+
destinationOptions.map((option)=>/*#__PURE__*/ jsx(SingleSelectOption, {
|
|
166
|
+
value: String(option.id),
|
|
167
|
+
children: option.label
|
|
168
|
+
}, option.id))
|
|
169
|
+
]
|
|
170
|
+
})
|
|
171
|
+
]
|
|
172
|
+
})
|
|
173
|
+
}),
|
|
174
|
+
/*#__PURE__*/ jsx(Modal.Footer, {
|
|
175
|
+
children: /*#__PURE__*/ jsxs(Flex, {
|
|
176
|
+
gap: 2,
|
|
177
|
+
justifyContent: "space-between",
|
|
178
|
+
width: "100%",
|
|
179
|
+
children: [
|
|
180
|
+
/*#__PURE__*/ jsx(Button, {
|
|
181
|
+
variant: "tertiary",
|
|
182
|
+
onClick: onClose,
|
|
183
|
+
disabled: isMoving,
|
|
184
|
+
type: "button",
|
|
185
|
+
children: formatMessage({
|
|
186
|
+
id: 'app.components.Button.cancel',
|
|
187
|
+
defaultMessage: 'Cancel'
|
|
188
|
+
})
|
|
189
|
+
}),
|
|
190
|
+
/*#__PURE__*/ jsx(Button, {
|
|
191
|
+
onClick: handleMove,
|
|
192
|
+
loading: isMoving,
|
|
193
|
+
children: formatMessage({
|
|
194
|
+
id: getTranslationKey('list.bulk-actions.move.submit'),
|
|
195
|
+
defaultMessage: 'Move'
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
]
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
]
|
|
202
|
+
})
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export { BulkMoveDialog };
|
|
207
|
+
//# sourceMappingURL=BulkMoveDialog.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BulkMoveDialog.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/BulkMoveDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\n\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport {\n Button,\n Field,\n Flex,\n Modal,\n SingleSelect,\n SingleSelectOption,\n} from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport {\n useBulkMoveMutation,\n useGetFolderQuery,\n useGetFolderStructureQuery,\n} from '../../../services/folders';\nimport { buildDragSetFromSelection } from '../../../utils/buildDragSetFromSelection';\nimport { canDropItemOnFolder } from '../../../utils/canDropItemOnFolder';\nimport { flattenFolderStructure } from '../../../utils/flattenFolderStructure';\nimport { getBulkMoveErrorMessage } from '../../../utils/getBulkMoveErrorMessage';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useFolderNavigation } from '../hooks/useFolderNavigation';\n\nconst StyledModalContent = styled(Modal.Content)`\n max-width: 51.6rem;\n`;\n\ninterface BulkMoveDialogProps {\n open: boolean;\n onClose: () => void;\n}\n\n/**\n * \"Move elements to\" modal for the bulk actions bar. Moves the current\n * selection (assets and/or folders) into the picked destination folder via\n * `POST /upload/actions/bulk-move`. The mutation invalidates the asset list,\n * folder list and the sidebar folder-structure, so both panes refresh on\n * success. On failure the modal stays open so the user can retry or cancel.\n */\nexport const BulkMoveDialog = ({ open, onClose }: BulkMoveDialogProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n const { selectedIds, selectedFolderIds, clear } = useAssetSelection();\n const { currentFolderId } = useFolderNavigation();\n const { data: folderStructure = [] } = useGetFolderStructureQuery(undefined, { skip: !open });\n const { data: currentFolder } = useGetFolderQuery(\n { id: currentFolderId! },\n { skip: currentFolderId === null }\n );\n const [bulkMove, { isLoading: isMoving }] = useBulkMoveMutation();\n\n // '' is the DOM-only sentinel for the Media Library root (null everywhere else).\n const [destination, setDestination] = useState<string>('');\n\n useEffect(() => {\n if (open) {\n setDestination('');\n }\n }, [open]);\n\n const rootLabel = formatMessage({\n id: getTranslationKey('plugin.name'),\n defaultMessage: 'Media Library',\n });\n\n // Reconstruct the selection as a drag set so the dialog validates destinations\n // through the exact same canonical predicate as the DnD highlight.\n const dragSet = useMemo(\n () => buildDragSetFromSelection(selectedIds, selectedFolderIds, currentFolderId),\n [selectedIds, selectedFolderIds, currentFolderId]\n );\n\n // Options carry the full ancestry (\"About / Images\" vs \"Tech / Images\") so\n // same-named folders stay distinguishable, listed depth-first under their\n // parent. Selected folders and their descendants are pruned during the same\n // single walk — an item can't be moved into itself or below itself (the\n // server would reject it). The current folder (where the items already live)\n // is filtered out too via `canDropItemOnFolder`, so we never offer a no-op\n // move. Memoized: large libraries shouldn't re-walk the tree on every render.\n const destinationOptions = useMemo(\n () =>\n flattenFolderStructure(folderStructure, selectedFolderIds).filter((option) =>\n canDropItemOnFolder({ items: dragSet, targetFolderId: option.id, folderStructure })\n ),\n [folderStructure, selectedFolderIds, dragSet]\n );\n\n // Hide the root option when the items already live at root — dropping there\n // would be a no-op. Uses the same predicate as the DnD highlight.\n const canMoveToRoot = useMemo(\n () => canDropItemOnFolder({ items: dragSet, targetFolderId: null, folderStructure }),\n [dragSet, folderStructure]\n );\n\n const count = selectedIds.size + selectedFolderIds.size;\n\n const handleMove = async () => {\n if (isMoving) {\n return;\n }\n\n const destinationFolderId = destination === '' ? null : Number(destination);\n\n try {\n await bulkMove({\n fileIds: Array.from(selectedIds),\n folderIds: Array.from(selectedFolderIds),\n destinationFolderId,\n }).unwrap();\n } catch (error) {\n // Keep the modal open and the selection intact so the user can retry\n // (e.g. after picking a valid destination) or cancel. Surface the server\n // message when there is one — it carries actionable causes like moving a\n // folder into its own descendant or a name collision in the destination.\n toggleNotification({\n type: 'danger',\n message: getBulkMoveErrorMessage(\n error,\n formatMessage({\n id: getTranslationKey('list.bulk-actions.move.error'),\n defaultMessage: 'An error occurred while moving the items.',\n })\n ),\n });\n return;\n }\n\n const sourceName = currentFolderId === null ? rootLabel : (currentFolder?.name ?? rootLabel);\n const destinationName =\n destinationFolderId === null\n ? rootLabel\n : (destinationOptions.find((option) => option.id === destinationFolderId)?.label ??\n rootLabel);\n\n toggleNotification({\n type: 'success',\n message: formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source: sourceName, destination: destinationName }\n ),\n });\n clear();\n onClose();\n };\n\n return (\n <Modal.Root\n open={open}\n onOpenChange={(nextOpen: boolean) => {\n // The modal must stay open while the request runs — the loader on the\n // Move button is the source of truth for the pending state.\n if (!nextOpen && !isMoving) {\n onClose();\n }\n }}\n >\n <StyledModalContent>\n <Modal.Header>\n <Modal.Title>\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.move.title'),\n defaultMessage: 'Move elements to',\n })}\n </Modal.Title>\n </Modal.Header>\n <Modal.Body>\n <Field.Root name=\"destination\">\n <Field.Label>\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.move.location'),\n defaultMessage: 'Location',\n })}\n </Field.Label>\n <SingleSelect\n value={destination}\n onChange={(value) => setDestination(String(value))}\n disabled={isMoving}\n >\n {canMoveToRoot && <SingleSelectOption value=\"\">{rootLabel}</SingleSelectOption>}\n {destinationOptions.map((option) => (\n <SingleSelectOption key={option.id} value={String(option.id)}>\n {option.label}\n </SingleSelectOption>\n ))}\n </SingleSelect>\n </Field.Root>\n </Modal.Body>\n <Modal.Footer>\n <Flex gap={2} justifyContent=\"space-between\" width=\"100%\">\n <Button variant=\"tertiary\" onClick={onClose} disabled={isMoving} type=\"button\">\n {formatMessage({ id: 'app.components.Button.cancel', defaultMessage: 'Cancel' })}\n </Button>\n <Button onClick={handleMove} loading={isMoving}>\n {formatMessage({\n id: getTranslationKey('list.bulk-actions.move.submit'),\n defaultMessage: 'Move',\n })}\n </Button>\n </Flex>\n </Modal.Footer>\n </StyledModalContent>\n </Modal.Root>\n );\n};\n"],"names":["StyledModalContent","styled","Modal","Content","BulkMoveDialog","open","onClose","formatMessage","useIntl","toggleNotification","useNotification","selectedIds","selectedFolderIds","clear","useAssetSelection","currentFolderId","useFolderNavigation","data","folderStructure","useGetFolderStructureQuery","undefined","skip","currentFolder","useGetFolderQuery","id","bulkMove","isLoading","isMoving","useBulkMoveMutation","destination","setDestination","useState","useEffect","rootLabel","getTranslationKey","defaultMessage","dragSet","useMemo","buildDragSetFromSelection","destinationOptions","flattenFolderStructure","filter","option","canDropItemOnFolder","items","targetFolderId","canMoveToRoot","count","size","handleMove","destinationFolderId","Number","fileIds","Array","from","folderIds","unwrap","error","type","message","getBulkMoveErrorMessage","sourceName","name","destinationName","find","label","source","_jsx","Root","onOpenChange","nextOpen","_jsxs","Header","Title","Body","Field","Label","SingleSelect","value","onChange","String","disabled","SingleSelectOption","map","Footer","Flex","gap","justifyContent","width","Button","variant","onClick","loading"],"mappings":";;;;;;;;;;;;;;;AA2BA,MAAMA,kBAAAA,GAAqBC,MAAAA,CAAOC,KAAAA,CAAMC,OAAO,CAAC;;AAEhD,CAAC;AAOD;;;;;;UAOaC,cAAAA,GAAiB,CAAC,EAAEC,IAAI,EAAEC,OAAO,EAAuB,GAAA;IACnE,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;IAC1B,MAAM,EAAEC,kBAAkB,EAAE,GAAGC,eAAAA,EAAAA;AAC/B,IAAA,MAAM,EAAEC,WAAW,EAAEC,iBAAiB,EAAEC,KAAK,EAAE,GAAGC,iBAAAA,EAAAA;IAClD,MAAM,EAAEC,eAAe,EAAE,GAAGC,mBAAAA,EAAAA;AAC5B,IAAA,MAAM,EAAEC,IAAAA,EAAMC,eAAAA,GAAkB,EAAE,EAAE,GAAGC,2BAA2BC,SAAAA,EAAW;AAAEC,QAAAA,IAAAA,EAAM,CAAChB;AAAK,KAAA,CAAA;AAC3F,IAAA,MAAM,EAAEY,IAAAA,EAAMK,aAAa,EAAE,GAAGC,iBAAAA,CAC9B;QAAEC,EAAAA,EAAIT;KAAiB,EACvB;AAAEM,QAAAA,IAAAA,EAAMN,eAAAA,KAAoB;AAAK,KAAA,CAAA;AAEnC,IAAA,MAAM,CAACU,QAAAA,EAAU,EAAEC,WAAWC,QAAQ,EAAE,CAAC,GAAGC,mBAAAA,EAAAA;;AAG5C,IAAA,MAAM,CAACC,WAAAA,EAAaC,cAAAA,CAAe,GAAGC,QAAAA,CAAiB,EAAA,CAAA;IAEvDC,SAAAA,CAAU,IAAA;AACR,QAAA,IAAI3B,IAAAA,EAAM;YACRyB,cAAAA,CAAe,EAAA,CAAA;AACjB,QAAA;IACF,CAAA,EAAG;AAACzB,QAAAA;AAAK,KAAA,CAAA;AAET,IAAA,MAAM4B,YAAY1B,aAAAA,CAAc;AAC9BiB,QAAAA,EAAAA,EAAIU,iBAAAA,CAAkB,aAAA,CAAA;QACtBC,cAAAA,EAAgB;AAClB,KAAA,CAAA;;;AAIA,IAAA,MAAMC,UAAUC,OAAAA,CACd,IAAMC,yBAAAA,CAA0B3B,WAAAA,EAAaC,mBAAmBG,eAAAA,CAAAA,EAChE;AAACJ,QAAAA,WAAAA;AAAaC,QAAAA,iBAAAA;AAAmBG,QAAAA;AAAgB,KAAA,CAAA;;;;;;;;IAUnD,MAAMwB,kBAAAA,GAAqBF,OAAAA,CACzB,IACEG,sBAAAA,CAAuBtB,eAAAA,EAAiBN,mBAAmB6B,MAAM,CAAC,CAACC,MAAAA,GACjEC,mBAAAA,CAAoB;gBAAEC,KAAAA,EAAOR,OAAAA;AAASS,gBAAAA,cAAAA,EAAgBH,OAAOlB,EAAE;AAAEN,gBAAAA;aAAgB,CAAA,CAAA,EAErF;AAACA,QAAAA,eAAAA;AAAiBN,QAAAA,iBAAAA;AAAmBwB,QAAAA;AAAQ,KAAA,CAAA;;;IAK/C,MAAMU,aAAAA,GAAgBT,OAAAA,CACpB,IAAMM,mBAAAA,CAAoB;YAAEC,KAAAA,EAAOR,OAAAA;YAASS,cAAAA,EAAgB,IAAA;AAAM3B,YAAAA;SAAgB,CAAA,EAClF;AAACkB,QAAAA,OAAAA;AAASlB,QAAAA;AAAgB,KAAA,CAAA;AAG5B,IAAA,MAAM6B,KAAAA,GAAQpC,WAAAA,CAAYqC,IAAI,GAAGpC,kBAAkBoC,IAAI;AAEvD,IAAA,MAAMC,UAAAA,GAAa,UAAA;AACjB,QAAA,IAAItB,QAAAA,EAAU;AACZ,YAAA;AACF,QAAA;AAEA,QAAA,MAAMuB,mBAAAA,GAAsBrB,WAAAA,KAAgB,EAAA,GAAK,IAAA,GAAOsB,MAAAA,CAAOtB,WAAAA,CAAAA;QAE/D,IAAI;AACF,YAAA,MAAMJ,QAAAA,CAAS;gBACb2B,OAAAA,EAASC,KAAAA,CAAMC,IAAI,CAAC3C,WAAAA,CAAAA;gBACpB4C,SAAAA,EAAWF,KAAAA,CAAMC,IAAI,CAAC1C,iBAAAA,CAAAA;AACtBsC,gBAAAA;AACF,aAAA,CAAA,CAAGM,MAAM,EAAA;AACX,QAAA,CAAA,CAAE,OAAOC,KAAAA,EAAO;;;;;YAKdhD,kBAAAA,CAAmB;gBACjBiD,IAAAA,EAAM,QAAA;gBACNC,OAAAA,EAASC,uBAAAA,CACPH,OACAlD,aAAAA,CAAc;AACZiB,oBAAAA,EAAAA,EAAIU,iBAAAA,CAAkB,8BAAA,CAAA;oBACtBC,cAAAA,EAAgB;AAClB,iBAAA,CAAA;AAEJ,aAAA,CAAA;AACA,YAAA;AACF,QAAA;AAEA,QAAA,MAAM0B,UAAAA,GAAa9C,eAAAA,KAAoB,IAAA,GAAOkB,SAAAA,GAAaX,eAAewC,IAAAA,IAAQ7B,SAAAA;AAClF,QAAA,MAAM8B,eAAAA,GACJb,mBAAAA,KAAwB,IAAA,GACpBjB,SAAAA,GACCM,kBAAAA,CAAmByB,IAAI,CAAC,CAACtB,MAAAA,GAAWA,MAAAA,CAAOlB,EAAE,KAAK0B,sBAAsBe,KAAAA,IACzEhC,SAAAA;QAENxB,kBAAAA,CAAmB;YACjBiD,IAAAA,EAAM,SAAA;AACNC,YAAAA,OAAAA,EAASpD,aAAAA,CACP;AACEiB,gBAAAA,EAAAA,EAAIU,iBAAAA,CAAkB,gCAAA,CAAA;gBACtBC,cAAAA,EACE;aACJ,EACA;AAAEY,gBAAAA,KAAAA;gBAAOmB,MAAAA,EAAQL,UAAAA;gBAAYhC,WAAAA,EAAakC;AAAgB,aAAA;AAE9D,SAAA,CAAA;AACAlD,QAAAA,KAAAA,EAAAA;AACAP,QAAAA,OAAAA,EAAAA;AACF,IAAA,CAAA;IAEA,qBACE6D,GAAA,CAACjE,MAAMkE,IAAI,EAAA;QACT/D,IAAAA,EAAMA,IAAAA;AACNgE,QAAAA,YAAAA,EAAc,CAACC,QAAAA,GAAAA;;;YAGb,IAAI,CAACA,QAAAA,IAAY,CAAC3C,QAAAA,EAAU;AAC1BrB,gBAAAA,OAAAA,EAAAA;AACF,YAAA;AACF,QAAA,CAAA;AAEA,QAAA,QAAA,gBAAAiE,IAAA,CAACvE,kBAAAA,EAAAA;;AACC,8BAAAmE,GAAA,CAACjE,MAAMsE,MAAM,EAAA;4CACXL,GAAA,CAACjE,MAAMuE,KAAK,EAAA;kCACTlE,aAAAA,CAAc;AACbiB,4BAAAA,EAAAA,EAAIU,iBAAAA,CAAkB,8BAAA,CAAA;4BACtBC,cAAAA,EAAgB;AAClB,yBAAA;;;AAGJ,8BAAAgC,GAAA,CAACjE,MAAMwE,IAAI,EAAA;4CACTH,IAAA,CAACI,MAAMP,IAAI,EAAA;wBAACN,IAAAA,EAAK,aAAA;;AACf,0CAAAK,GAAA,CAACQ,MAAMC,KAAK,EAAA;0CACTrE,aAAAA,CAAc;AACbiB,oCAAAA,EAAAA,EAAIU,iBAAAA,CAAkB,iCAAA,CAAA;oCACtBC,cAAAA,EAAgB;AAClB,iCAAA;;0CAEFoC,IAAA,CAACM,YAAAA,EAAAA;gCACCC,KAAAA,EAAOjD,WAAAA;gCACPkD,QAAAA,EAAU,CAACD,KAAAA,GAAUhD,cAAAA,CAAekD,MAAAA,CAAOF,KAAAA,CAAAA,CAAAA;gCAC3CG,QAAAA,EAAUtD,QAAAA;;AAETmB,oCAAAA,aAAAA,kBAAiBqB,GAAA,CAACe,kBAAAA,EAAAA;wCAAmBJ,KAAAA,EAAM,EAAA;AAAI7C,wCAAAA,QAAAA,EAAAA;;AAC/CM,oCAAAA,kBAAAA,CAAmB4C,GAAG,CAAC,CAACzC,MAAAA,iBACvByB,GAAA,CAACe,kBAAAA,EAAAA;4CAAmCJ,KAAAA,EAAOE,MAAAA,CAAOtC,OAAOlB,EAAE,CAAA;AACxDkB,4CAAAA,QAAAA,EAAAA,MAAAA,CAAOuB;AADevB,yCAAAA,EAAAA,MAAAA,CAAOlB,EAAE,CAAA;;;;;;AAO1C,8BAAA2C,GAAA,CAACjE,MAAMkF,MAAM,EAAA;AACX,oBAAA,QAAA,gBAAAb,IAAA,CAACc,IAAAA,EAAAA;wBAAKC,GAAAA,EAAK,CAAA;wBAAGC,cAAAA,EAAe,eAAA;wBAAgBC,KAAAA,EAAM,MAAA;;0CACjDrB,GAAA,CAACsB,MAAAA,EAAAA;gCAAOC,OAAAA,EAAQ,UAAA;gCAAWC,OAAAA,EAASrF,OAAAA;gCAAS2E,QAAAA,EAAUtD,QAAAA;gCAAU+B,IAAAA,EAAK,QAAA;0CACnEnD,aAAAA,CAAc;oCAAEiB,EAAAA,EAAI,8BAAA;oCAAgCW,cAAAA,EAAgB;AAAS,iCAAA;;0CAEhFgC,GAAA,CAACsB,MAAAA,EAAAA;gCAAOE,OAAAA,EAAS1C,UAAAA;gCAAY2C,OAAAA,EAASjE,QAAAA;0CACnCpB,aAAAA,CAAc;AACbiB,oCAAAA,EAAAA,EAAIU,iBAAAA,CAAkB,+BAAA,CAAA;oCACtBC,cAAAA,EAAgB;AAClB,iCAAA;;;;;;;;AAOd;;;;"}
|