@strapi/upload 5.60.0 → 5.61.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/components/AssetPickerDialog/MediaLibraryPickerDialog.js +29 -0
- package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.js.map +1 -0
- package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.mjs +27 -0
- package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.mjs.map +1 -0
- package/dist/admin/components/Drawer.js +12 -7
- package/dist/admin/components/Drawer.js.map +1 -1
- package/dist/admin/components/Drawer.mjs +12 -7
- package/dist/admin/components/Drawer.mjs.map +1 -1
- package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.js +188 -0
- package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.js.map +1 -0
- package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.mjs +186 -0
- package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.mjs.map +1 -0
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js +5 -1
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs +5 -1
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js +37 -27
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs +37 -27
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/openAssetDetails.js +46 -0
- package/dist/admin/components/MediaLibraryInput/openAssetDetails.js.map +1 -0
- package/dist/admin/components/MediaLibraryInput/openAssetDetails.mjs +43 -0
- package/dist/admin/components/MediaLibraryInput/openAssetDetails.mjs.map +1 -0
- package/dist/admin/index.js +10 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +10 -1
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/pages/Assets/AssetsPage.js +1 -1
- package/dist/admin/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/pages/Assets/AssetsPage.mjs +2 -2
- package/dist/admin/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +2 -2
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +181 -65
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +184 -69
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetPreview.mjs +1 -1
- package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.js +2 -6
- package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.js.map +1 -1
- package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.mjs +2 -6
- package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.mjs.map +1 -1
- package/dist/admin/src/components/AssetPickerDialog/MediaLibraryPickerDialog.d.ts +19 -0
- package/dist/admin/src/components/Drawer.d.ts +8 -0
- package/dist/admin/src/components/EditorFileDropZone/EditorFileDropZone.d.ts +21 -0
- package/dist/admin/src/components/MediaLibraryInput/AddAssetMenu.d.ts +7 -1
- package/dist/admin/src/components/MediaLibraryInput/openAssetDetails.d.ts +10 -0
- package/dist/admin/src/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +41 -5
- package/dist/admin/src/pages/Assets/hooks/useIsAssetDetailsOpen.d.ts +2 -6
- package/dist/admin/translations/en.json.js +4 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +4 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/audit-logs.js +264 -0
- package/dist/server/audit-logs.js.map +1 -0
- package/dist/server/audit-logs.mjs +255 -0
- package/dist/server/audit-logs.mjs.map +1 -0
- package/dist/server/bootstrap.js +4 -0
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/server/bootstrap.mjs +4 -0
- package/dist/server/bootstrap.mjs.map +1 -1
- package/dist/server/controllers/admin-folder-file.js +23 -1
- package/dist/server/controllers/admin-folder-file.js.map +1 -1
- package/dist/server/controllers/admin-folder-file.mjs +23 -1
- package/dist/server/controllers/admin-folder-file.mjs.map +1 -1
- package/dist/server/services/folder.js +48 -1
- package/dist/server/services/folder.js.map +1 -1
- package/dist/server/services/folder.mjs +48 -1
- package/dist/server/services/folder.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js +12 -5
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs +12 -5
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/services/upload.js +100 -16
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs +102 -18
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/src/audit-logs.d.ts +142 -0
- package/dist/server/src/audit-logs.d.ts.map +1 -0
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-folder-file.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +3 -3
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -1
- package/dist/server/src/services/folder.d.ts.map +1 -1
- package/dist/server/src/services/image-manipulation.d.ts +3 -3
- package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +3 -3
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/upload.d.ts.map +1 -1
- package/package.json +9 -16
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useRef } from 'react';
|
|
3
|
+
import { useNotification } from '@strapi/admin/strapi-admin';
|
|
4
|
+
import { Typography } from '@strapi/design-system';
|
|
5
|
+
import { useIntl } from 'react-intl';
|
|
6
|
+
import { styled } from 'styled-components';
|
|
7
|
+
import { useAIMetadataEnabled } from '../../hooks/useAIMetadataEnabled.mjs';
|
|
8
|
+
import { useMediaLibraryPermissions } from '../../hooks/useMediaLibraryPermissions.mjs';
|
|
9
|
+
import { useTracking } from '../../hooks/useTracking.mjs';
|
|
10
|
+
import { useUploadFilesMutation } from '../../services/api.mjs';
|
|
11
|
+
import { useGetUploadSettingsQuery } from '../../services/settings.mjs';
|
|
12
|
+
import { filterAllowedFiles } from '../../utils/allowedMediaTypes.mjs';
|
|
13
|
+
import { getTranslationKey } from '../../utils/translations.mjs';
|
|
14
|
+
import { typeFromMime } from '../../utils/typeFromMime.mjs';
|
|
15
|
+
|
|
16
|
+
const FRAME_COLORS = {
|
|
17
|
+
accept: {
|
|
18
|
+
border: 'primary600',
|
|
19
|
+
background: 'primary100',
|
|
20
|
+
text: 'primary600'
|
|
21
|
+
},
|
|
22
|
+
reject: {
|
|
23
|
+
border: 'neutral300',
|
|
24
|
+
background: 'neutral150',
|
|
25
|
+
text: 'neutral600'
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
// Takes the place of the editor's content in its layout, so it fills the same
|
|
29
|
+
// space: the editor stacks its parts against the start, sized to their content.
|
|
30
|
+
const Root = styled.div`
|
|
31
|
+
position: relative;
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
flex: 1;
|
|
35
|
+
width: 100%;
|
|
36
|
+
min-height: 0;
|
|
37
|
+
`;
|
|
38
|
+
const Frame = styled.div`
|
|
39
|
+
position: absolute;
|
|
40
|
+
inset: ${({ theme })=>theme.spaces[2]};
|
|
41
|
+
z-index: 2;
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
border: 1px solid ${({ theme, $dragState })=>theme.colors[FRAME_COLORS[$dragState].border]};
|
|
46
|
+
border-radius: ${({ theme })=>theme.borderRadius};
|
|
47
|
+
background: ${({ theme, $dragState })=>theme.colors[FRAME_COLORS[$dragState].background]};
|
|
48
|
+
pointer-events: none;
|
|
49
|
+
`;
|
|
50
|
+
const hasFiles = (event)=>event.dataTransfer.types.includes('Files');
|
|
51
|
+
/**
|
|
52
|
+
* Lets an editor take files dropped onto it: they upload to the root of the
|
|
53
|
+
* Media Library, through the global upload progress dialog, and are handed
|
|
54
|
+
* back to the editor one by one. Drags that carry no file, such as an editor
|
|
55
|
+
* reordering its own blocks, pass through untouched.
|
|
56
|
+
*/ const EditorFileDropZone = ({ children, disabled = false, allowedTypes, onDropFiles })=>{
|
|
57
|
+
const { formatMessage } = useIntl();
|
|
58
|
+
const { toggleNotification } = useNotification();
|
|
59
|
+
const { trackUsage } = useTracking();
|
|
60
|
+
const [uploadFiles] = useUploadFilesMutation();
|
|
61
|
+
const { canRead, canCreate, isLoading } = useMediaLibraryPermissions();
|
|
62
|
+
const { data: settings } = useGetUploadSettingsQuery(undefined, {
|
|
63
|
+
skip: !canRead
|
|
64
|
+
});
|
|
65
|
+
const isAiMetadataEnabled = useAIMetadataEnabled();
|
|
66
|
+
const [dragState, setDragState] = useState('idle');
|
|
67
|
+
// Drag events fire on every descendant crossed, so count enter/leave pairs.
|
|
68
|
+
const dragDepth = useRef(0);
|
|
69
|
+
const canUpload = !disabled && !isLoading && canCreate;
|
|
70
|
+
const handleDragEnter = (event)=>{
|
|
71
|
+
if (!hasFiles(event)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
dragDepth.current += 1;
|
|
76
|
+
setDragState(canUpload ? 'accept' : 'reject');
|
|
77
|
+
};
|
|
78
|
+
const handleDragLeave = (event)=>{
|
|
79
|
+
if (!hasFiles(event)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
dragDepth.current -= 1;
|
|
83
|
+
if (dragDepth.current <= 0) {
|
|
84
|
+
dragDepth.current = 0;
|
|
85
|
+
setDragState('idle');
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const handleDragOver = (event)=>{
|
|
89
|
+
if (!hasFiles(event)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Without it the browser opens the file in the tab.
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
// `none` shows the no-drop cursor, and the browser then fires no `drop`.
|
|
95
|
+
event.dataTransfer.dropEffect = canUpload ? 'copy' : 'none';
|
|
96
|
+
};
|
|
97
|
+
const handleDrop = async (event)=>{
|
|
98
|
+
if (!hasFiles(event)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
dragDepth.current = 0;
|
|
103
|
+
setDragState('idle');
|
|
104
|
+
if (!canUpload) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const files = Array.from(event.dataTransfer.files);
|
|
108
|
+
const allowedFiles = filterAllowedFiles(allowedTypes, files);
|
|
109
|
+
if (allowedFiles.length < files.length) {
|
|
110
|
+
toggleNotification({
|
|
111
|
+
type: 'danger',
|
|
112
|
+
timeout: 4000,
|
|
113
|
+
message: formatMessage({
|
|
114
|
+
id: getTranslationKey('input.notification.not-supported'),
|
|
115
|
+
defaultMessage: `You can't upload this type of file, only the following types are accepted – {fileTypes}`
|
|
116
|
+
}, {
|
|
117
|
+
fileTypes: (allowedTypes ?? []).join(',')
|
|
118
|
+
})
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (allowedFiles.length === 0) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const onFileUploaded = onDropFiles(event);
|
|
125
|
+
const assetsCountByType = allowedFiles.reduce((acc, file)=>{
|
|
126
|
+
const type = typeFromMime(file.type);
|
|
127
|
+
acc[type] = (acc[type] ?? 0) + 1;
|
|
128
|
+
return acc;
|
|
129
|
+
}, {});
|
|
130
|
+
trackUsage('didSelectFile', {
|
|
131
|
+
source: 'computer',
|
|
132
|
+
location: 'content-manager'
|
|
133
|
+
});
|
|
134
|
+
trackUsage('willAddMediaLibraryAssets', {
|
|
135
|
+
location: 'content-manager',
|
|
136
|
+
...assetsCountByType
|
|
137
|
+
});
|
|
138
|
+
const formData = new FormData();
|
|
139
|
+
allowedFiles.forEach((file)=>formData.append('files', file));
|
|
140
|
+
formData.append('fileInfo', JSON.stringify(allowedFiles.map((file)=>({
|
|
141
|
+
name: file.name,
|
|
142
|
+
caption: null,
|
|
143
|
+
alternativeText: null,
|
|
144
|
+
folder: null
|
|
145
|
+
}))));
|
|
146
|
+
try {
|
|
147
|
+
await uploadFiles({
|
|
148
|
+
formData,
|
|
149
|
+
totalFiles: allowedFiles.length,
|
|
150
|
+
concurrency: settings?.data?.concurrentUploadRequests ?? 1,
|
|
151
|
+
generateAiMetadata: Boolean(isAiMetadataEnabled),
|
|
152
|
+
onFileUploaded
|
|
153
|
+
}).unwrap();
|
|
154
|
+
} catch {
|
|
155
|
+
// Errors reach the user through the progress dialog, which the mutation
|
|
156
|
+
// populates itself.
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return /*#__PURE__*/ jsxs(Root, {
|
|
160
|
+
onDragEnter: handleDragEnter,
|
|
161
|
+
onDragLeave: handleDragLeave,
|
|
162
|
+
onDragOver: handleDragOver,
|
|
163
|
+
onDrop: handleDrop,
|
|
164
|
+
children: [
|
|
165
|
+
children,
|
|
166
|
+
dragState !== 'idle' && /*#__PURE__*/ jsx(Frame, {
|
|
167
|
+
$dragState: dragState,
|
|
168
|
+
"data-testid": "editor-file-drop-zone",
|
|
169
|
+
children: /*#__PURE__*/ jsx(Typography, {
|
|
170
|
+
fontWeight: "semiBold",
|
|
171
|
+
textColor: FRAME_COLORS[dragState].text,
|
|
172
|
+
children: dragState === 'accept' ? formatMessage({
|
|
173
|
+
id: getTranslationKey('editor-drop-zone.accept'),
|
|
174
|
+
defaultMessage: 'Drop to upload and insert'
|
|
175
|
+
}) : formatMessage({
|
|
176
|
+
id: getTranslationKey('editor-drop-zone.reject'),
|
|
177
|
+
defaultMessage: "You don't have permission to upload files"
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
]
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
export { EditorFileDropZone };
|
|
186
|
+
//# sourceMappingURL=EditorFileDropZone.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditorFileDropZone.mjs","sources":["../../../../admin/src/components/EditorFileDropZone/EditorFileDropZone.tsx"],"sourcesContent":["import { useRef, useState, type DragEvent, type ReactNode } from 'react';\n\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport { Typography } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { useAIMetadataEnabled } from '../../hooks/useAIMetadataEnabled';\nimport { useMediaLibraryPermissions } from '../../hooks/useMediaLibraryPermissions';\nimport { useTracking } from '../../hooks/useTracking';\nimport { useUploadFilesMutation } from '../../services/api';\nimport { useGetUploadSettingsQuery } from '../../services/settings';\nimport { filterAllowedFiles, type AllowedMediaType } from '../../utils/allowedMediaTypes';\nimport { getTranslationKey } from '../../utils/translations';\nimport { typeFromMime } from '../../utils/typeFromMime';\n\nimport type { File } from '../../../../shared/contracts/files';\n\ntype DragState = 'idle' | 'accept' | 'reject';\n\nconst FRAME_COLORS = {\n accept: { border: 'primary600', background: 'primary100', text: 'primary600' },\n reject: { border: 'neutral300', background: 'neutral150', text: 'neutral600' },\n} as const;\n\n// Takes the place of the editor's content in its layout, so it fills the same\n// space: the editor stacks its parts against the start, sized to their content.\nconst Root = styled.div`\n position: relative;\n display: flex;\n flex-direction: column;\n flex: 1;\n width: 100%;\n min-height: 0;\n`;\n\nconst Frame = styled.div<{ $dragState: Exclude<DragState, 'idle'> }>`\n position: absolute;\n inset: ${({ theme }) => theme.spaces[2]};\n z-index: 2;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 1px solid ${({ theme, $dragState }) => theme.colors[FRAME_COLORS[$dragState].border]};\n border-radius: ${({ theme }) => theme.borderRadius};\n background: ${({ theme, $dragState }) => theme.colors[FRAME_COLORS[$dragState].background]};\n pointer-events: none;\n`;\n\nexport interface EditorFileDropZoneProps {\n children: ReactNode;\n disabled?: boolean;\n allowedTypes?: AllowedMediaType[];\n /**\n * Called once per accepted drop, while the drop event is still current. It\n * returns what receives each of that drop's files once uploaded, including\n * files a Retry in the progress dialog uploads later.\n */\n onDropFiles: (event: DragEvent<HTMLElement>) => (file: File) => void;\n}\n\nconst hasFiles = (event: DragEvent<HTMLElement>) => event.dataTransfer.types.includes('Files');\n\n/**\n * Lets an editor take files dropped onto it: they upload to the root of the\n * Media Library, through the global upload progress dialog, and are handed\n * back to the editor one by one. Drags that carry no file, such as an editor\n * reordering its own blocks, pass through untouched.\n */\nexport const EditorFileDropZone = ({\n children,\n disabled = false,\n allowedTypes,\n onDropFiles,\n}: EditorFileDropZoneProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n const { trackUsage } = useTracking();\n const [uploadFiles] = useUploadFilesMutation();\n const { canRead, canCreate, isLoading } = useMediaLibraryPermissions();\n const { data: settings } = useGetUploadSettingsQuery(undefined, { skip: !canRead });\n const isAiMetadataEnabled = useAIMetadataEnabled();\n const [dragState, setDragState] = useState<DragState>('idle');\n // Drag events fire on every descendant crossed, so count enter/leave pairs.\n const dragDepth = useRef(0);\n\n const canUpload = !disabled && !isLoading && canCreate;\n\n const handleDragEnter = (event: DragEvent<HTMLElement>) => {\n if (!hasFiles(event)) {\n return;\n }\n\n event.preventDefault();\n dragDepth.current += 1;\n setDragState(canUpload ? 'accept' : 'reject');\n };\n\n const handleDragLeave = (event: DragEvent<HTMLElement>) => {\n if (!hasFiles(event)) {\n return;\n }\n\n dragDepth.current -= 1;\n\n if (dragDepth.current <= 0) {\n dragDepth.current = 0;\n setDragState('idle');\n }\n };\n\n const handleDragOver = (event: DragEvent<HTMLElement>) => {\n if (!hasFiles(event)) {\n return;\n }\n\n // Without it the browser opens the file in the tab.\n event.preventDefault();\n // `none` shows the no-drop cursor, and the browser then fires no `drop`.\n event.dataTransfer.dropEffect = canUpload ? 'copy' : 'none';\n };\n\n const handleDrop = async (event: DragEvent<HTMLElement>) => {\n if (!hasFiles(event)) {\n return;\n }\n\n event.preventDefault();\n dragDepth.current = 0;\n setDragState('idle');\n\n if (!canUpload) {\n return;\n }\n\n const files = Array.from(event.dataTransfer.files);\n const allowedFiles = filterAllowedFiles(allowedTypes, files);\n\n if (allowedFiles.length < files.length) {\n toggleNotification({\n type: 'danger',\n timeout: 4000,\n message: formatMessage(\n {\n id: getTranslationKey('input.notification.not-supported'),\n defaultMessage: `You can't upload this type of file, only the following types are accepted – {fileTypes}`,\n },\n { fileTypes: (allowedTypes ?? []).join(',') }\n ),\n });\n }\n\n if (allowedFiles.length === 0) {\n return;\n }\n\n const onFileUploaded = onDropFiles(event);\n\n const assetsCountByType = allowedFiles.reduce<Record<string, number>>((acc, file) => {\n const type = typeFromMime(file.type);\n acc[type] = (acc[type] ?? 0) + 1;\n return acc;\n }, {});\n trackUsage('didSelectFile', { source: 'computer', location: 'content-manager' });\n trackUsage('willAddMediaLibraryAssets', { location: 'content-manager', ...assetsCountByType });\n\n const formData = new FormData();\n allowedFiles.forEach((file) => formData.append('files', file));\n formData.append(\n 'fileInfo',\n JSON.stringify(\n allowedFiles.map((file) => ({\n name: file.name,\n caption: null,\n alternativeText: null,\n folder: null,\n }))\n )\n );\n\n try {\n await uploadFiles({\n formData,\n totalFiles: allowedFiles.length,\n concurrency: settings?.data?.concurrentUploadRequests ?? 1,\n generateAiMetadata: Boolean(isAiMetadataEnabled),\n onFileUploaded,\n }).unwrap();\n } catch {\n // Errors reach the user through the progress dialog, which the mutation\n // populates itself.\n }\n };\n\n return (\n <Root\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n >\n {children}\n {dragState !== 'idle' && (\n <Frame $dragState={dragState} data-testid=\"editor-file-drop-zone\">\n <Typography fontWeight=\"semiBold\" textColor={FRAME_COLORS[dragState].text}>\n {dragState === 'accept'\n ? formatMessage({\n id: getTranslationKey('editor-drop-zone.accept'),\n defaultMessage: 'Drop to upload and insert',\n })\n : formatMessage({\n id: getTranslationKey('editor-drop-zone.reject'),\n defaultMessage: \"You don't have permission to upload files\",\n })}\n </Typography>\n </Frame>\n )}\n </Root>\n );\n};\n"],"names":["FRAME_COLORS","accept","border","background","text","reject","Root","styled","div","Frame","theme","spaces","$dragState","colors","borderRadius","hasFiles","event","dataTransfer","types","includes","EditorFileDropZone","children","disabled","allowedTypes","onDropFiles","formatMessage","useIntl","toggleNotification","useNotification","trackUsage","useTracking","uploadFiles","useUploadFilesMutation","canRead","canCreate","isLoading","useMediaLibraryPermissions","data","settings","useGetUploadSettingsQuery","undefined","skip","isAiMetadataEnabled","useAIMetadataEnabled","dragState","setDragState","useState","dragDepth","useRef","canUpload","handleDragEnter","preventDefault","current","handleDragLeave","handleDragOver","dropEffect","handleDrop","files","Array","from","allowedFiles","filterAllowedFiles","length","type","timeout","message","id","getTranslationKey","defaultMessage","fileTypes","join","onFileUploaded","assetsCountByType","reduce","acc","file","typeFromMime","source","location","formData","FormData","forEach","append","JSON","stringify","map","name","caption","alternativeText","folder","totalFiles","concurrency","concurrentUploadRequests","generateAiMetadata","Boolean","unwrap","_jsxs","onDragEnter","onDragLeave","onDragOver","onDrop","_jsx","data-testid","Typography","fontWeight","textColor"],"mappings":";;;;;;;;;;;;;;;AAoBA,MAAMA,YAAAA,GAAe;IACnBC,MAAAA,EAAQ;QAAEC,MAAAA,EAAQ,YAAA;QAAcC,UAAAA,EAAY,YAAA;QAAcC,IAAAA,EAAM;AAAa,KAAA;IAC7EC,MAAAA,EAAQ;QAAEH,MAAAA,EAAQ,YAAA;QAAcC,UAAAA,EAAY,YAAA;QAAcC,IAAAA,EAAM;AAAa;AAC/E,CAAA;AAEA;AACA;AACA,MAAME,IAAAA,GAAOC,MAAAA,CAAOC,GAAG;;;;;;;AAOvB,CAAC;AAED,MAAMC,KAAAA,GAAQF,MAAAA,CAAOC,GAA+C;;SAE3D,EAAE,CAAC,EAAEE,KAAK,EAAE,GAAKA,KAAAA,CAAMC,MAAM,CAAC,CAAA,CAAE,CAAC;;;;;AAKtB,oBAAA,EAAE,CAAC,EAAED,KAAK,EAAEE,UAAU,EAAE,GAAKF,KAAAA,CAAMG,MAAM,CAACb,YAAY,CAACY,UAAAA,CAAW,CAACV,MAAM,CAAC,CAAC;AAC9E,iBAAA,EAAE,CAAC,EAAEQ,KAAK,EAAE,GAAKA,KAAAA,CAAMI,YAAY,CAAC;AACvC,cAAA,EAAE,CAAC,EAAEJ,KAAK,EAAEE,UAAU,EAAE,GAAKF,KAAAA,CAAMG,MAAM,CAACb,YAAY,CAACY,UAAAA,CAAW,CAACT,UAAU,CAAC,CAAC;;AAE7F,CAAC;AAcD,MAAMY,QAAAA,GAAW,CAACC,KAAAA,GAAkCA,KAAAA,CAAMC,YAAY,CAACC,KAAK,CAACC,QAAQ,CAAC,OAAA,CAAA;AAEtF;;;;;AAKC,IACM,MAAMC,kBAAAA,GAAqB,CAAC,EACjCC,QAAQ,EACRC,QAAAA,GAAW,KAAK,EAChBC,YAAY,EACZC,WAAW,EACa,GAAA;IACxB,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;IAC1B,MAAM,EAAEC,kBAAkB,EAAE,GAAGC,eAAAA,EAAAA;IAC/B,MAAM,EAAEC,UAAU,EAAE,GAAGC,WAAAA,EAAAA;IACvB,MAAM,CAACC,YAAY,GAAGC,sBAAAA,EAAAA;AACtB,IAAA,MAAM,EAAEC,OAAO,EAAEC,SAAS,EAAEC,SAAS,EAAE,GAAGC,0BAAAA,EAAAA;AAC1C,IAAA,MAAM,EAAEC,IAAAA,EAAMC,QAAQ,EAAE,GAAGC,0BAA0BC,SAAAA,EAAW;AAAEC,QAAAA,IAAAA,EAAM,CAACR;AAAQ,KAAA,CAAA;AACjF,IAAA,MAAMS,mBAAAA,GAAsBC,oBAAAA,EAAAA;AAC5B,IAAA,MAAM,CAACC,SAAAA,EAAWC,YAAAA,CAAa,GAAGC,QAAAA,CAAoB,MAAA,CAAA;;AAEtD,IAAA,MAAMC,YAAYC,MAAAA,CAAO,CAAA,CAAA;AAEzB,IAAA,MAAMC,SAAAA,GAAY,CAAC3B,QAAAA,IAAY,CAACa,SAAAA,IAAaD,SAAAA;AAE7C,IAAA,MAAMgB,kBAAkB,CAAClC,KAAAA,GAAAA;QACvB,IAAI,CAACD,SAASC,KAAAA,CAAAA,EAAQ;AACpB,YAAA;AACF,QAAA;AAEAA,QAAAA,KAAAA,CAAMmC,cAAc,EAAA;AACpBJ,QAAAA,SAAAA,CAAUK,OAAO,IAAI,CAAA;AACrBP,QAAAA,YAAAA,CAAaI,YAAY,QAAA,GAAW,QAAA,CAAA;AACtC,IAAA,CAAA;AAEA,IAAA,MAAMI,kBAAkB,CAACrC,KAAAA,GAAAA;QACvB,IAAI,CAACD,SAASC,KAAAA,CAAAA,EAAQ;AACpB,YAAA;AACF,QAAA;AAEA+B,QAAAA,SAAAA,CAAUK,OAAO,IAAI,CAAA;QAErB,IAAIL,SAAAA,CAAUK,OAAO,IAAI,CAAA,EAAG;AAC1BL,YAAAA,SAAAA,CAAUK,OAAO,GAAG,CAAA;YACpBP,YAAAA,CAAa,MAAA,CAAA;AACf,QAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAMS,iBAAiB,CAACtC,KAAAA,GAAAA;QACtB,IAAI,CAACD,SAASC,KAAAA,CAAAA,EAAQ;AACpB,YAAA;AACF,QAAA;;AAGAA,QAAAA,KAAAA,CAAMmC,cAAc,EAAA;;AAEpBnC,QAAAA,KAAAA,CAAMC,YAAY,CAACsC,UAAU,GAAGN,YAAY,MAAA,GAAS,MAAA;AACvD,IAAA,CAAA;AAEA,IAAA,MAAMO,aAAa,OAAOxC,KAAAA,GAAAA;QACxB,IAAI,CAACD,SAASC,KAAAA,CAAAA,EAAQ;AACpB,YAAA;AACF,QAAA;AAEAA,QAAAA,KAAAA,CAAMmC,cAAc,EAAA;AACpBJ,QAAAA,SAAAA,CAAUK,OAAO,GAAG,CAAA;QACpBP,YAAAA,CAAa,MAAA,CAAA;AAEb,QAAA,IAAI,CAACI,SAAAA,EAAW;AACd,YAAA;AACF,QAAA;AAEA,QAAA,MAAMQ,QAAQC,KAAAA,CAAMC,IAAI,CAAC3C,KAAAA,CAAMC,YAAY,CAACwC,KAAK,CAAA;QACjD,MAAMG,YAAAA,GAAeC,mBAAmBtC,YAAAA,EAAckC,KAAAA,CAAAA;AAEtD,QAAA,IAAIG,YAAAA,CAAaE,MAAM,GAAGL,KAAAA,CAAMK,MAAM,EAAE;YACtCnC,kBAAAA,CAAmB;gBACjBoC,IAAAA,EAAM,QAAA;gBACNC,OAAAA,EAAS,IAAA;AACTC,gBAAAA,OAAAA,EAASxC,aAAAA,CACP;AACEyC,oBAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,kCAAA,CAAA;oBACtBC,cAAAA,EAAgB,CAAC,uFAAuF;iBAC1G,EACA;AAAEC,oBAAAA,SAAAA,EAAW,CAAC9C,YAAAA,IAAgB,EAAE,EAAE+C,IAAI,CAAC,GAAA;AAAK,iBAAA;AAEhD,aAAA,CAAA;AACF,QAAA;QAEA,IAAIV,YAAAA,CAAaE,MAAM,KAAK,CAAA,EAAG;AAC7B,YAAA;AACF,QAAA;AAEA,QAAA,MAAMS,iBAAiB/C,WAAAA,CAAYR,KAAAA,CAAAA;AAEnC,QAAA,MAAMwD,iBAAAA,GAAoBZ,YAAAA,CAAaa,MAAM,CAAyB,CAACC,GAAAA,EAAKC,IAAAA,GAAAA;YAC1E,MAAMZ,IAAAA,GAAOa,YAAAA,CAAaD,IAAAA,CAAKZ,IAAI,CAAA;YACnCW,GAAG,CAACX,IAAAA,CAAK,GAAG,CAACW,GAAG,CAACX,IAAAA,CAAK,IAAI,CAAA,IAAK,CAAA;YAC/B,OAAOW,GAAAA;AACT,QAAA,CAAA,EAAG,EAAC,CAAA;AACJ7C,QAAAA,UAAAA,CAAW,eAAA,EAAiB;YAAEgD,MAAAA,EAAQ,UAAA;YAAYC,QAAAA,EAAU;AAAkB,SAAA,CAAA;AAC9EjD,QAAAA,UAAAA,CAAW,2BAAA,EAA6B;YAAEiD,QAAAA,EAAU,iBAAA;AAAmB,YAAA,GAAGN;AAAkB,SAAA,CAAA;AAE5F,QAAA,MAAMO,WAAW,IAAIC,QAAAA,EAAAA;AACrBpB,QAAAA,YAAAA,CAAaqB,OAAO,CAAC,CAACN,OAASI,QAAAA,CAASG,MAAM,CAAC,OAAA,EAASP,IAAAA,CAAAA,CAAAA;QACxDI,QAAAA,CAASG,MAAM,CACb,UAAA,EACAC,IAAAA,CAAKC,SAAS,CACZxB,YAAAA,CAAayB,GAAG,CAAC,CAACV,IAAAA,IAAU;AAC1BW,gBAAAA,IAAAA,EAAMX,KAAKW,IAAI;gBACfC,OAAAA,EAAS,IAAA;gBACTC,eAAAA,EAAiB,IAAA;gBACjBC,MAAAA,EAAQ;aACV,CAAA,CAAA,CAAA,CAAA;QAIJ,IAAI;AACF,YAAA,MAAM1D,WAAAA,CAAY;AAChBgD,gBAAAA,QAAAA;AACAW,gBAAAA,UAAAA,EAAY9B,aAAaE,MAAM;gBAC/B6B,WAAAA,EAAarD,QAAAA,EAAUD,MAAMuD,wBAAAA,IAA4B,CAAA;AACzDC,gBAAAA,kBAAAA,EAAoBC,OAAAA,CAAQpD,mBAAAA,CAAAA;AAC5B6B,gBAAAA;AACF,aAAA,CAAA,CAAGwB,MAAM,EAAA;AACX,QAAA,CAAA,CAAE,OAAM;;;AAGR,QAAA;AACF,IAAA,CAAA;AAEA,IAAA,qBACEC,IAAA,CAAC1F,IAAAA,EAAAA;QACC2F,WAAAA,EAAa/C,eAAAA;QACbgD,WAAAA,EAAa7C,eAAAA;QACb8C,UAAAA,EAAY7C,cAAAA;QACZ8C,MAAAA,EAAQ5C,UAAAA;;AAEPnC,YAAAA,QAAAA;AACAuB,YAAAA,SAAAA,KAAc,wBACbyD,GAAA,CAAC5F,KAAAA,EAAAA;gBAAMG,UAAAA,EAAYgC,SAAAA;gBAAW0D,aAAAA,EAAY,uBAAA;AACxC,gBAAA,QAAA,gBAAAD,GAAA,CAACE,UAAAA,EAAAA;oBAAWC,UAAAA,EAAW,UAAA;AAAWC,oBAAAA,SAAAA,EAAWzG,YAAY,CAAC4C,SAAAA,CAAU,CAACxC,IAAI;AACtEwC,oBAAAA,QAAAA,EAAAA,SAAAA,KAAc,WACXnB,aAAAA,CAAc;AACZyC,wBAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,yBAAA,CAAA;wBACtBC,cAAAA,EAAgB;AAClB,qBAAA,CAAA,GACA3C,aAAAA,CAAc;AACZyC,wBAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,yBAAA,CAAA;wBACtBC,cAAAA,EAAgB;AAClB,qBAAA;;;;;AAMhB;;;;"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var React = require('react');
|
|
4
5
|
var designSystem = require('@strapi/design-system');
|
|
5
6
|
var icons = require('@strapi/icons');
|
|
6
7
|
var reactIntl = require('react-intl');
|
|
@@ -72,9 +73,10 @@ const AddAssetTrigger = styledComponents.styled(designSystem.Menu.Trigger)`
|
|
|
72
73
|
*
|
|
73
74
|
* The drop zone below keeps working whatever this shows — it is a second way
|
|
74
75
|
* in, not the only one.
|
|
75
|
-
*/ const AddAssetMenu = ({ disabled = false, onBrowseLibrary, onUploadFromDevice, onUploadFromUrl })=>{
|
|
76
|
+
*/ const AddAssetMenu = ({ disabled = false, preventOpen = false, onBrowseLibrary, onUploadFromDevice, onUploadFromUrl })=>{
|
|
76
77
|
const { formatMessage } = reactIntl.useIntl();
|
|
77
78
|
const { canRead, canCreate, isLoading } = useMediaLibraryPermissions.useMediaLibraryPermissions();
|
|
79
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
78
80
|
// `useRBAC` starts every instance at `isLoading: true` with the flags false.
|
|
79
81
|
// Until it settles, every entry shows but none can be picked: hiding them
|
|
80
82
|
// would flash a smaller menu, enabling them would lead to a 403.
|
|
@@ -84,6 +86,8 @@ const AddAssetTrigger = styledComponents.styled(designSystem.Menu.Trigger)`
|
|
|
84
86
|
return null;
|
|
85
87
|
}
|
|
86
88
|
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Menu.Root, {
|
|
89
|
+
open: isOpen,
|
|
90
|
+
onOpenChange: (open)=>setIsOpen(open && !preventOpen),
|
|
87
91
|
children: [
|
|
88
92
|
/*#__PURE__*/ jsxRuntime.jsx(AddAssetTrigger, {
|
|
89
93
|
variant: "ghost",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddAssetMenu.js","sources":["../../../../admin/src/components/MediaLibraryInput/AddAssetMenu.tsx"],"sourcesContent":["import { Menu } from '@strapi/design-system';\nimport { ChevronDown, Images, Link, Upload } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { useMediaLibraryPermissions } from '../../hooks/useMediaLibraryPermissions';\nimport { ActionsMenuContent } from '../../pages/Assets/components/ActionsMenuContent';\nimport { getTranslationKey } from '../../utils/translations';\n\nimport { FieldMenuItem } from './FieldControls';\n\n// The trigger reads as a link on the label row, not as a button: no padding, no\n// ghost background, primary text. `Menu.Trigger` takes no `textColor`, so the\n// colour is set here and forced past the design system's own span rule.\nconst AddAssetTrigger = styled(Menu.Trigger)`\n padding: 0;\n\n // No hover treatment at all: this reads as a link on the label row, and the\n // design system's ghost button paints a primary100 plate behind it (also while\n // the menu is open, via data-state).\n &,\n &:hover,\n &[data-state='open'] {\n background: transparent;\n background-color: transparent;\n }\n\n &,\n & span,\n &:hover span {\n color: ${({ theme }) => theme.colors.primary600};\n }\n\n & span {\n font-weight: ${({ theme }) => theme.fontWeights.regular};\n }\n\n // The chevron needs the fill, not the colour: the design system paints button\n // icons with its own \"svg path { fill }\" rule per variant, and repaints them\n // again on hover — both beat an inherited color and the icon's own\n // fill=\"currentColor\". Doubled specificity so these land after those rules\n // whatever order they are injected in.\n && svg path,\n &&:hover svg path {\n fill: ${({ theme }) => theme.colors.primary600};\n }\n\n // The design system's disabled button is a bordered grey plate; this stays a\n // greyed-out link instead.\n &&[aria-disabled='true'] {\n border-color: transparent;\n background: transparent;\n }\n\n &[aria-disabled='true'],\n &[aria-disabled='true'] span {\n color: ${({ theme }) => theme.colors.neutral500};\n }\n\n &&[aria-disabled='true'] svg path {\n fill: ${({ theme }) => theme.colors.neutral500};\n }\n`;\n\ninterface AddAssetMenuProps {\n disabled?: boolean;\n onBrowseLibrary: () => void;\n onUploadFromDevice: () => void;\n onUploadFromUrl: () => void;\n}\n\n/**\n * The field's \"Add asset\" menu, on the label row.\n *\n * Browsing needs the Media Library's read permission, the two upload entries\n * `assets.create`; each entry disappears without its permission, and the\n * trigger itself goes when nothing is left to offer, rather than opening an\n * empty menu.\n *\n * The drop zone below keeps working whatever this shows — it is a second way\n * in, not the only one.\n */\nexport const AddAssetMenu = ({\n disabled = false,\n onBrowseLibrary,\n onUploadFromDevice,\n onUploadFromUrl,\n}: AddAssetMenuProps) => {\n const { formatMessage } = useIntl();\n const { canRead, canCreate, isLoading } = useMediaLibraryPermissions();\n\n // `useRBAC` starts every instance at `isLoading: true` with the flags false.\n // Until it settles, every entry shows but none can be picked: hiding them\n // would flash a smaller menu, enabling them would lead to a 403.\n const canBrowse = isLoading || canRead;\n const canUpload = isLoading || canCreate;\n\n if (!canBrowse && !canUpload) {\n return null;\n }\n\n return (\n <Menu.Root>\n <AddAssetTrigger variant=\"ghost\" disabled={disabled} endIcon={<ChevronDown aria-hidden />}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset'),\n defaultMessage: 'Add asset',\n })}\n </AddAssetTrigger>\n\n <ActionsMenuContent popoverPlacement=\"bottom-end\" zIndex={2} minWidth=\"22rem\">\n {canBrowse && (\n <FieldMenuItem startIcon={<Images />} disabled={isLoading} onSelect={onBrowseLibrary}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.browse'),\n defaultMessage: 'Browse library',\n })}\n </FieldMenuItem>\n )}\n\n {canUpload && (\n <>\n <FieldMenuItem\n startIcon={<Upload />}\n disabled={isLoading}\n onSelect={onUploadFromDevice}\n >\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.device'),\n defaultMessage: 'Upload from device',\n })}\n </FieldMenuItem>\n\n <FieldMenuItem startIcon={<Link />} disabled={isLoading} onSelect={onUploadFromUrl}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.url'),\n defaultMessage: 'Upload from URL',\n })}\n </FieldMenuItem>\n </>\n )}\n </ActionsMenuContent>\n </Menu.Root>\n );\n};\n"],"names":["AddAssetTrigger","styled","Menu","Trigger","theme","colors","primary600","fontWeights","regular","neutral500","AddAssetMenu","disabled","onBrowseLibrary","onUploadFromDevice","onUploadFromUrl","formatMessage","useIntl","canRead","canCreate","isLoading","useMediaLibraryPermissions","canBrowse","canUpload","_jsxs","Root","_jsx","variant","endIcon","ChevronDown","aria-hidden","id","getTranslationKey","defaultMessage","ActionsMenuContent","popoverPlacement","zIndex","minWidth","FieldMenuItem","startIcon","Images","onSelect","_Fragment","Upload","Link"],"mappings":"
|
|
1
|
+
{"version":3,"file":"AddAssetMenu.js","sources":["../../../../admin/src/components/MediaLibraryInput/AddAssetMenu.tsx"],"sourcesContent":["import { useState } from 'react';\n\nimport { Menu } from '@strapi/design-system';\nimport { ChevronDown, Images, Link, Upload } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { useMediaLibraryPermissions } from '../../hooks/useMediaLibraryPermissions';\nimport { ActionsMenuContent } from '../../pages/Assets/components/ActionsMenuContent';\nimport { getTranslationKey } from '../../utils/translations';\n\nimport { FieldMenuItem } from './FieldControls';\n\n// The trigger reads as a link on the label row, not as a button: no padding, no\n// ghost background, primary text. `Menu.Trigger` takes no `textColor`, so the\n// colour is set here and forced past the design system's own span rule.\nconst AddAssetTrigger = styled(Menu.Trigger)`\n padding: 0;\n\n // No hover treatment at all: this reads as a link on the label row, and the\n // design system's ghost button paints a primary100 plate behind it (also while\n // the menu is open, via data-state).\n &,\n &:hover,\n &[data-state='open'] {\n background: transparent;\n background-color: transparent;\n }\n\n &,\n & span,\n &:hover span {\n color: ${({ theme }) => theme.colors.primary600};\n }\n\n & span {\n font-weight: ${({ theme }) => theme.fontWeights.regular};\n }\n\n // The chevron needs the fill, not the colour: the design system paints button\n // icons with its own \"svg path { fill }\" rule per variant, and repaints them\n // again on hover — both beat an inherited color and the icon's own\n // fill=\"currentColor\". Doubled specificity so these land after those rules\n // whatever order they are injected in.\n && svg path,\n &&:hover svg path {\n fill: ${({ theme }) => theme.colors.primary600};\n }\n\n // The design system's disabled button is a bordered grey plate; this stays a\n // greyed-out link instead.\n &&[aria-disabled='true'] {\n border-color: transparent;\n background: transparent;\n }\n\n &[aria-disabled='true'],\n &[aria-disabled='true'] span {\n color: ${({ theme }) => theme.colors.neutral500};\n }\n\n &&[aria-disabled='true'] svg path {\n fill: ${({ theme }) => theme.colors.neutral500};\n }\n`;\n\ninterface AddAssetMenuProps {\n disabled?: boolean;\n /**\n * Ignore requests to open, without looking disabled. The press still reaches\n * whatever listens for presses outside it, so an open drawer closes — or asks\n * about unsaved edits — instead of having the menu stacked over it.\n */\n preventOpen?: boolean;\n onBrowseLibrary: () => void;\n onUploadFromDevice: () => void;\n onUploadFromUrl: () => void;\n}\n\n/**\n * The field's \"Add asset\" menu, on the label row.\n *\n * Browsing needs the Media Library's read permission, the two upload entries\n * `assets.create`; each entry disappears without its permission, and the\n * trigger itself goes when nothing is left to offer, rather than opening an\n * empty menu.\n *\n * The drop zone below keeps working whatever this shows — it is a second way\n * in, not the only one.\n */\nexport const AddAssetMenu = ({\n disabled = false,\n preventOpen = false,\n onBrowseLibrary,\n onUploadFromDevice,\n onUploadFromUrl,\n}: AddAssetMenuProps) => {\n const { formatMessage } = useIntl();\n const { canRead, canCreate, isLoading } = useMediaLibraryPermissions();\n const [isOpen, setIsOpen] = useState(false);\n\n // `useRBAC` starts every instance at `isLoading: true` with the flags false.\n // Until it settles, every entry shows but none can be picked: hiding them\n // would flash a smaller menu, enabling them would lead to a 403.\n const canBrowse = isLoading || canRead;\n const canUpload = isLoading || canCreate;\n\n if (!canBrowse && !canUpload) {\n return null;\n }\n\n return (\n <Menu.Root open={isOpen} onOpenChange={(open) => setIsOpen(open && !preventOpen)}>\n <AddAssetTrigger variant=\"ghost\" disabled={disabled} endIcon={<ChevronDown aria-hidden />}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset'),\n defaultMessage: 'Add asset',\n })}\n </AddAssetTrigger>\n\n <ActionsMenuContent popoverPlacement=\"bottom-end\" zIndex={2} minWidth=\"22rem\">\n {canBrowse && (\n <FieldMenuItem startIcon={<Images />} disabled={isLoading} onSelect={onBrowseLibrary}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.browse'),\n defaultMessage: 'Browse library',\n })}\n </FieldMenuItem>\n )}\n\n {canUpload && (\n <>\n <FieldMenuItem\n startIcon={<Upload />}\n disabled={isLoading}\n onSelect={onUploadFromDevice}\n >\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.device'),\n defaultMessage: 'Upload from device',\n })}\n </FieldMenuItem>\n\n <FieldMenuItem startIcon={<Link />} disabled={isLoading} onSelect={onUploadFromUrl}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.url'),\n defaultMessage: 'Upload from URL',\n })}\n </FieldMenuItem>\n </>\n )}\n </ActionsMenuContent>\n </Menu.Root>\n );\n};\n"],"names":["AddAssetTrigger","styled","Menu","Trigger","theme","colors","primary600","fontWeights","regular","neutral500","AddAssetMenu","disabled","preventOpen","onBrowseLibrary","onUploadFromDevice","onUploadFromUrl","formatMessage","useIntl","canRead","canCreate","isLoading","useMediaLibraryPermissions","isOpen","setIsOpen","useState","canBrowse","canUpload","_jsxs","Root","open","onOpenChange","_jsx","variant","endIcon","ChevronDown","aria-hidden","id","getTranslationKey","defaultMessage","ActionsMenuContent","popoverPlacement","zIndex","minWidth","FieldMenuItem","startIcon","Images","onSelect","_Fragment","Upload","Link"],"mappings":";;;;;;;;;;;;;AAaA;AACA;AACA;AACA,MAAMA,eAAAA,GAAkBC,uBAAAA,CAAOC,iBAAAA,CAAKC,OAAO,CAAC;;;;;;;;;;;;;;;;WAgBjC,EAAE,CAAC,EAAEC,KAAK,EAAE,GAAKA,KAAAA,CAAMC,MAAM,CAACC,UAAU,CAAC;;;;iBAInC,EAAE,CAAC,EAAEF,KAAK,EAAE,GAAKA,KAAAA,CAAMG,WAAW,CAACC,OAAO,CAAC;;;;;;;;;;UAUlD,EAAE,CAAC,EAAEJ,KAAK,EAAE,GAAKA,KAAAA,CAAMC,MAAM,CAACC,UAAU,CAAC;;;;;;;;;;;;WAYxC,EAAE,CAAC,EAAEF,KAAK,EAAE,GAAKA,KAAAA,CAAMC,MAAM,CAACI,UAAU,CAAC;;;;UAI1C,EAAE,CAAC,EAAEL,KAAK,EAAE,GAAKA,KAAAA,CAAMC,MAAM,CAACI,UAAU,CAAC;;AAEnD,CAAC;AAeD;;;;;;;;;;AAUC,IACM,MAAMC,YAAAA,GAAe,CAAC,EAC3BC,WAAW,KAAK,EAChBC,WAAAA,GAAc,KAAK,EACnBC,eAAe,EACfC,kBAAkB,EAClBC,eAAe,EACG,GAAA;IAClB,MAAM,EAAEC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;AAC1B,IAAA,MAAM,EAAEC,OAAO,EAAEC,SAAS,EAAEC,SAAS,EAAE,GAAGC,qDAAAA,EAAAA;AAC1C,IAAA,MAAM,CAACC,MAAAA,EAAQC,SAAAA,CAAU,GAAGC,cAAAA,CAAS,KAAA,CAAA;;;;AAKrC,IAAA,MAAMC,YAAYL,SAAAA,IAAaF,OAAAA;AAC/B,IAAA,MAAMQ,YAAYN,SAAAA,IAAaD,SAAAA;IAE/B,IAAI,CAACM,SAAAA,IAAa,CAACC,SAAAA,EAAW;QAC5B,OAAO,IAAA;AACT,IAAA;IAEA,qBACEC,eAAA,CAACzB,kBAAK0B,IAAI,EAAA;QAACC,IAAAA,EAAMP,MAAAA;AAAQQ,QAAAA,YAAAA,EAAc,CAACD,IAAAA,GAASN,SAAAA,CAAUM,IAAAA,IAAQ,CAACjB,WAAAA,CAAAA;;0BAClEmB,cAAA,CAAC/B,eAAAA,EAAAA;gBAAgBgC,OAAAA,EAAQ,OAAA;gBAAQrB,QAAAA,EAAUA,QAAAA;AAAUsB,gBAAAA,OAAAA,gBAASF,cAAA,CAACG,iBAAAA,EAAAA;oBAAYC,aAAW,EAAA;;0BACnFnB,aAAAA,CAAc;AACboB,oBAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,iCAAA,CAAA;oBACtBC,cAAAA,EAAgB;AAClB,iBAAA;;0BAGFX,eAAA,CAACY,qCAAAA,EAAAA;gBAAmBC,gBAAAA,EAAiB,YAAA;gBAAaC,MAAAA,EAAQ,CAAA;gBAAGC,QAAAA,EAAS,OAAA;;AACnEjB,oBAAAA,SAAAA,kBACCM,cAAA,CAACY,2BAAAA,EAAAA;AAAcC,wBAAAA,SAAAA,gBAAWb,cAAA,CAACc,YAAAA,EAAAA,EAAAA,CAAAA;wBAAWlC,QAAAA,EAAUS,SAAAA;wBAAW0B,QAAAA,EAAUjC,eAAAA;kCAClEG,aAAAA,CAAc;AACboB,4BAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,wCAAA,CAAA;4BACtBC,cAAAA,EAAgB;AAClB,yBAAA;;oBAIHZ,SAAAA,kBACCC,eAAA,CAAAoB,mBAAA,EAAA;;0CACEhB,cAAA,CAACY,2BAAAA,EAAAA;AACCC,gCAAAA,SAAAA,gBAAWb,cAAA,CAACiB,YAAAA,EAAAA,EAAAA,CAAAA;gCACZrC,QAAAA,EAAUS,SAAAA;gCACV0B,QAAAA,EAAUhC,kBAAAA;0CAETE,aAAAA,CAAc;AACboB,oCAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,wCAAA,CAAA;oCACtBC,cAAAA,EAAgB;AAClB,iCAAA;;0CAGFP,cAAA,CAACY,2BAAAA,EAAAA;AAAcC,gCAAAA,SAAAA,gBAAWb,cAAA,CAACkB,UAAAA,EAAAA,EAAAA,CAAAA;gCAAStC,QAAAA,EAAUS,SAAAA;gCAAW0B,QAAAA,EAAU/B,eAAAA;0CAChEC,aAAAA,CAAc;AACboB,oCAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,qCAAA,CAAA;oCACtBC,cAAAA,EAAgB;AAClB,iCAAA;;;;;;;;AAOd;;;;"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { Menu } from '@strapi/design-system';
|
|
3
4
|
import { ChevronDown, Images, Upload, Link } from '@strapi/icons';
|
|
4
5
|
import { useIntl } from 'react-intl';
|
|
@@ -70,9 +71,10 @@ const AddAssetTrigger = styled(Menu.Trigger)`
|
|
|
70
71
|
*
|
|
71
72
|
* The drop zone below keeps working whatever this shows — it is a second way
|
|
72
73
|
* in, not the only one.
|
|
73
|
-
*/ const AddAssetMenu = ({ disabled = false, onBrowseLibrary, onUploadFromDevice, onUploadFromUrl })=>{
|
|
74
|
+
*/ const AddAssetMenu = ({ disabled = false, preventOpen = false, onBrowseLibrary, onUploadFromDevice, onUploadFromUrl })=>{
|
|
74
75
|
const { formatMessage } = useIntl();
|
|
75
76
|
const { canRead, canCreate, isLoading } = useMediaLibraryPermissions();
|
|
77
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
76
78
|
// `useRBAC` starts every instance at `isLoading: true` with the flags false.
|
|
77
79
|
// Until it settles, every entry shows but none can be picked: hiding them
|
|
78
80
|
// would flash a smaller menu, enabling them would lead to a 403.
|
|
@@ -82,6 +84,8 @@ const AddAssetTrigger = styled(Menu.Trigger)`
|
|
|
82
84
|
return null;
|
|
83
85
|
}
|
|
84
86
|
return /*#__PURE__*/ jsxs(Menu.Root, {
|
|
87
|
+
open: isOpen,
|
|
88
|
+
onOpenChange: (open)=>setIsOpen(open && !preventOpen),
|
|
85
89
|
children: [
|
|
86
90
|
/*#__PURE__*/ jsx(AddAssetTrigger, {
|
|
87
91
|
variant: "ghost",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddAssetMenu.mjs","sources":["../../../../admin/src/components/MediaLibraryInput/AddAssetMenu.tsx"],"sourcesContent":["import { Menu } from '@strapi/design-system';\nimport { ChevronDown, Images, Link, Upload } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { useMediaLibraryPermissions } from '../../hooks/useMediaLibraryPermissions';\nimport { ActionsMenuContent } from '../../pages/Assets/components/ActionsMenuContent';\nimport { getTranslationKey } from '../../utils/translations';\n\nimport { FieldMenuItem } from './FieldControls';\n\n// The trigger reads as a link on the label row, not as a button: no padding, no\n// ghost background, primary text. `Menu.Trigger` takes no `textColor`, so the\n// colour is set here and forced past the design system's own span rule.\nconst AddAssetTrigger = styled(Menu.Trigger)`\n padding: 0;\n\n // No hover treatment at all: this reads as a link on the label row, and the\n // design system's ghost button paints a primary100 plate behind it (also while\n // the menu is open, via data-state).\n &,\n &:hover,\n &[data-state='open'] {\n background: transparent;\n background-color: transparent;\n }\n\n &,\n & span,\n &:hover span {\n color: ${({ theme }) => theme.colors.primary600};\n }\n\n & span {\n font-weight: ${({ theme }) => theme.fontWeights.regular};\n }\n\n // The chevron needs the fill, not the colour: the design system paints button\n // icons with its own \"svg path { fill }\" rule per variant, and repaints them\n // again on hover — both beat an inherited color and the icon's own\n // fill=\"currentColor\". Doubled specificity so these land after those rules\n // whatever order they are injected in.\n && svg path,\n &&:hover svg path {\n fill: ${({ theme }) => theme.colors.primary600};\n }\n\n // The design system's disabled button is a bordered grey plate; this stays a\n // greyed-out link instead.\n &&[aria-disabled='true'] {\n border-color: transparent;\n background: transparent;\n }\n\n &[aria-disabled='true'],\n &[aria-disabled='true'] span {\n color: ${({ theme }) => theme.colors.neutral500};\n }\n\n &&[aria-disabled='true'] svg path {\n fill: ${({ theme }) => theme.colors.neutral500};\n }\n`;\n\ninterface AddAssetMenuProps {\n disabled?: boolean;\n onBrowseLibrary: () => void;\n onUploadFromDevice: () => void;\n onUploadFromUrl: () => void;\n}\n\n/**\n * The field's \"Add asset\" menu, on the label row.\n *\n * Browsing needs the Media Library's read permission, the two upload entries\n * `assets.create`; each entry disappears without its permission, and the\n * trigger itself goes when nothing is left to offer, rather than opening an\n * empty menu.\n *\n * The drop zone below keeps working whatever this shows — it is a second way\n * in, not the only one.\n */\nexport const AddAssetMenu = ({\n disabled = false,\n onBrowseLibrary,\n onUploadFromDevice,\n onUploadFromUrl,\n}: AddAssetMenuProps) => {\n const { formatMessage } = useIntl();\n const { canRead, canCreate, isLoading } = useMediaLibraryPermissions();\n\n // `useRBAC` starts every instance at `isLoading: true` with the flags false.\n // Until it settles, every entry shows but none can be picked: hiding them\n // would flash a smaller menu, enabling them would lead to a 403.\n const canBrowse = isLoading || canRead;\n const canUpload = isLoading || canCreate;\n\n if (!canBrowse && !canUpload) {\n return null;\n }\n\n return (\n <Menu.Root>\n <AddAssetTrigger variant=\"ghost\" disabled={disabled} endIcon={<ChevronDown aria-hidden />}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset'),\n defaultMessage: 'Add asset',\n })}\n </AddAssetTrigger>\n\n <ActionsMenuContent popoverPlacement=\"bottom-end\" zIndex={2} minWidth=\"22rem\">\n {canBrowse && (\n <FieldMenuItem startIcon={<Images />} disabled={isLoading} onSelect={onBrowseLibrary}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.browse'),\n defaultMessage: 'Browse library',\n })}\n </FieldMenuItem>\n )}\n\n {canUpload && (\n <>\n <FieldMenuItem\n startIcon={<Upload />}\n disabled={isLoading}\n onSelect={onUploadFromDevice}\n >\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.device'),\n defaultMessage: 'Upload from device',\n })}\n </FieldMenuItem>\n\n <FieldMenuItem startIcon={<Link />} disabled={isLoading} onSelect={onUploadFromUrl}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.url'),\n defaultMessage: 'Upload from URL',\n })}\n </FieldMenuItem>\n </>\n )}\n </ActionsMenuContent>\n </Menu.Root>\n );\n};\n"],"names":["AddAssetTrigger","styled","Menu","Trigger","theme","colors","primary600","fontWeights","regular","neutral500","AddAssetMenu","disabled","onBrowseLibrary","onUploadFromDevice","onUploadFromUrl","formatMessage","useIntl","canRead","canCreate","isLoading","useMediaLibraryPermissions","canBrowse","canUpload","_jsxs","Root","_jsx","variant","endIcon","ChevronDown","aria-hidden","id","getTranslationKey","defaultMessage","ActionsMenuContent","popoverPlacement","zIndex","minWidth","FieldMenuItem","startIcon","Images","onSelect","_Fragment","Upload","Link"],"mappings":"
|
|
1
|
+
{"version":3,"file":"AddAssetMenu.mjs","sources":["../../../../admin/src/components/MediaLibraryInput/AddAssetMenu.tsx"],"sourcesContent":["import { useState } from 'react';\n\nimport { Menu } from '@strapi/design-system';\nimport { ChevronDown, Images, Link, Upload } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { useMediaLibraryPermissions } from '../../hooks/useMediaLibraryPermissions';\nimport { ActionsMenuContent } from '../../pages/Assets/components/ActionsMenuContent';\nimport { getTranslationKey } from '../../utils/translations';\n\nimport { FieldMenuItem } from './FieldControls';\n\n// The trigger reads as a link on the label row, not as a button: no padding, no\n// ghost background, primary text. `Menu.Trigger` takes no `textColor`, so the\n// colour is set here and forced past the design system's own span rule.\nconst AddAssetTrigger = styled(Menu.Trigger)`\n padding: 0;\n\n // No hover treatment at all: this reads as a link on the label row, and the\n // design system's ghost button paints a primary100 plate behind it (also while\n // the menu is open, via data-state).\n &,\n &:hover,\n &[data-state='open'] {\n background: transparent;\n background-color: transparent;\n }\n\n &,\n & span,\n &:hover span {\n color: ${({ theme }) => theme.colors.primary600};\n }\n\n & span {\n font-weight: ${({ theme }) => theme.fontWeights.regular};\n }\n\n // The chevron needs the fill, not the colour: the design system paints button\n // icons with its own \"svg path { fill }\" rule per variant, and repaints them\n // again on hover — both beat an inherited color and the icon's own\n // fill=\"currentColor\". Doubled specificity so these land after those rules\n // whatever order they are injected in.\n && svg path,\n &&:hover svg path {\n fill: ${({ theme }) => theme.colors.primary600};\n }\n\n // The design system's disabled button is a bordered grey plate; this stays a\n // greyed-out link instead.\n &&[aria-disabled='true'] {\n border-color: transparent;\n background: transparent;\n }\n\n &[aria-disabled='true'],\n &[aria-disabled='true'] span {\n color: ${({ theme }) => theme.colors.neutral500};\n }\n\n &&[aria-disabled='true'] svg path {\n fill: ${({ theme }) => theme.colors.neutral500};\n }\n`;\n\ninterface AddAssetMenuProps {\n disabled?: boolean;\n /**\n * Ignore requests to open, without looking disabled. The press still reaches\n * whatever listens for presses outside it, so an open drawer closes — or asks\n * about unsaved edits — instead of having the menu stacked over it.\n */\n preventOpen?: boolean;\n onBrowseLibrary: () => void;\n onUploadFromDevice: () => void;\n onUploadFromUrl: () => void;\n}\n\n/**\n * The field's \"Add asset\" menu, on the label row.\n *\n * Browsing needs the Media Library's read permission, the two upload entries\n * `assets.create`; each entry disappears without its permission, and the\n * trigger itself goes when nothing is left to offer, rather than opening an\n * empty menu.\n *\n * The drop zone below keeps working whatever this shows — it is a second way\n * in, not the only one.\n */\nexport const AddAssetMenu = ({\n disabled = false,\n preventOpen = false,\n onBrowseLibrary,\n onUploadFromDevice,\n onUploadFromUrl,\n}: AddAssetMenuProps) => {\n const { formatMessage } = useIntl();\n const { canRead, canCreate, isLoading } = useMediaLibraryPermissions();\n const [isOpen, setIsOpen] = useState(false);\n\n // `useRBAC` starts every instance at `isLoading: true` with the flags false.\n // Until it settles, every entry shows but none can be picked: hiding them\n // would flash a smaller menu, enabling them would lead to a 403.\n const canBrowse = isLoading || canRead;\n const canUpload = isLoading || canCreate;\n\n if (!canBrowse && !canUpload) {\n return null;\n }\n\n return (\n <Menu.Root open={isOpen} onOpenChange={(open) => setIsOpen(open && !preventOpen)}>\n <AddAssetTrigger variant=\"ghost\" disabled={disabled} endIcon={<ChevronDown aria-hidden />}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset'),\n defaultMessage: 'Add asset',\n })}\n </AddAssetTrigger>\n\n <ActionsMenuContent popoverPlacement=\"bottom-end\" zIndex={2} minWidth=\"22rem\">\n {canBrowse && (\n <FieldMenuItem startIcon={<Images />} disabled={isLoading} onSelect={onBrowseLibrary}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.browse'),\n defaultMessage: 'Browse library',\n })}\n </FieldMenuItem>\n )}\n\n {canUpload && (\n <>\n <FieldMenuItem\n startIcon={<Upload />}\n disabled={isLoading}\n onSelect={onUploadFromDevice}\n >\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.device'),\n defaultMessage: 'Upload from device',\n })}\n </FieldMenuItem>\n\n <FieldMenuItem startIcon={<Link />} disabled={isLoading} onSelect={onUploadFromUrl}>\n {formatMessage({\n id: getTranslationKey('content-manager.input.add-asset.url'),\n defaultMessage: 'Upload from URL',\n })}\n </FieldMenuItem>\n </>\n )}\n </ActionsMenuContent>\n </Menu.Root>\n );\n};\n"],"names":["AddAssetTrigger","styled","Menu","Trigger","theme","colors","primary600","fontWeights","regular","neutral500","AddAssetMenu","disabled","preventOpen","onBrowseLibrary","onUploadFromDevice","onUploadFromUrl","formatMessage","useIntl","canRead","canCreate","isLoading","useMediaLibraryPermissions","isOpen","setIsOpen","useState","canBrowse","canUpload","_jsxs","Root","open","onOpenChange","_jsx","variant","endIcon","ChevronDown","aria-hidden","id","getTranslationKey","defaultMessage","ActionsMenuContent","popoverPlacement","zIndex","minWidth","FieldMenuItem","startIcon","Images","onSelect","_Fragment","Upload","Link"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ var reactIntl = require('react-intl');
|
|
|
9
9
|
var useAIMetadataEnabled = require('../../hooks/useAIMetadataEnabled.js');
|
|
10
10
|
var useMediaLibraryPermissions = require('../../hooks/useMediaLibraryPermissions.js');
|
|
11
11
|
var useTracking = require('../../hooks/useTracking.js');
|
|
12
|
-
var
|
|
12
|
+
var AssetDetailsDrawer = require('../../pages/Assets/components/AssetDetails/AssetDetailsDrawer.js');
|
|
13
13
|
var ImportFromUrlDialog = require('../../pages/Assets/components/ImportFromUrlDialog.js');
|
|
14
14
|
var api = require('../../services/api.js');
|
|
15
15
|
var settings = require('../../services/settings.js');
|
|
@@ -23,6 +23,7 @@ var AssetCard = require('./AssetCard.js');
|
|
|
23
23
|
var AssetCarousel = require('./AssetCarousel.js');
|
|
24
24
|
var AssetDropZone = require('./AssetDropZone.js');
|
|
25
25
|
var AssetRow = require('./AssetRow.js');
|
|
26
|
+
var openAssetDetails = require('./openAssetDetails.js');
|
|
26
27
|
var useFieldUploads = require('./useFieldUploads.js');
|
|
27
28
|
|
|
28
29
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -67,14 +68,15 @@ const EMPTY_ATTRIBUTE = {};
|
|
|
67
68
|
const { copy } = strapiAdmin.useClipboard();
|
|
68
69
|
const getValues = strapiAdmin.useForm('MediaLibraryInput', (state)=>state.getValues);
|
|
69
70
|
const [step, setStep] = React.useState(undefined);
|
|
70
|
-
const [
|
|
71
|
+
const [editedAssetId, setEditedAssetId] = React.useState(null);
|
|
72
|
+
const isAnyDrawerOpen = openAssetDetails.useOpenAssetDetails(editedAssetId !== null);
|
|
71
73
|
const [isUrlDialogOpen, setIsUrlDialogOpen] = React.useState(false);
|
|
72
74
|
const fileInputRef = React.useRef(null);
|
|
73
75
|
const [uploadFiles] = api.useUploadFilesMutation();
|
|
74
76
|
const [uploadFromUrls] = api.useUploadFromUrlsMutation();
|
|
75
77
|
// Echoes the app config; a missing payload (still loading) falls back to
|
|
76
78
|
// sequential rather than outpacing what the server asked for.
|
|
77
|
-
const { isLoading, canRead, canCreate
|
|
79
|
+
const { isLoading, canRead, canCreate } = useMediaLibraryPermissions.useMediaLibraryPermissions();
|
|
78
80
|
// Not while permissions load: `useRBAC` starts with every flag false, and an
|
|
79
81
|
// upload sent before it settles can end in a 403.
|
|
80
82
|
const canUpload = !isLoading && canCreate;
|
|
@@ -140,21 +142,30 @@ const EMPTY_ATTRIBUTE = {};
|
|
|
140
142
|
})
|
|
141
143
|
});
|
|
142
144
|
};
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
if (result && typeof result !== 'boolean') {
|
|
155
|
-
setAssets(assetsRef.current.map((current)=>current.id === asset.id ? result : current));
|
|
145
|
+
/**
|
|
146
|
+
* Opens only when no drawer is open, in this field or another. Pressing a row's
|
|
147
|
+
* button is a press outside the open drawer, which closes it — or, with unsaved
|
|
148
|
+
* edits, asks first. Opening on the click that follows would put a second
|
|
149
|
+
* drawer, or a new asset, under that question and drop the edits it is asking
|
|
150
|
+
* about.
|
|
151
|
+
*/ const handleEdit = (asset)=>{
|
|
152
|
+
if (!openAssetDetails.isAnyAssetDetailsOpen()) {
|
|
153
|
+
setEditedAssetId(asset.id);
|
|
156
154
|
}
|
|
157
155
|
};
|
|
156
|
+
// The drawer saves to the library itself; the field holds its own copy of the
|
|
157
|
+
// asset, so it takes the server's after every change. Read from the ref: the
|
|
158
|
+
// drawer reports after a request, by which time this render's `assets` may
|
|
159
|
+
// be stale.
|
|
160
|
+
const handleAssetChange = (changedAsset)=>{
|
|
161
|
+
setAssets(assetsRef.current.map((current)=>// The populated `createdBy` is the one shape difference, and the entry
|
|
162
|
+
// only keeps the asset's id on save.
|
|
163
|
+
current.id === changedAsset.id ? changedAsset : current));
|
|
164
|
+
};
|
|
165
|
+
// Deleting from the library leaves nothing for the field to point at.
|
|
166
|
+
const handleAssetDelete = (id)=>{
|
|
167
|
+
setAssets(assetsRef.current.filter((current)=>current.id !== id));
|
|
168
|
+
};
|
|
158
169
|
const handleSelect = (selectedAssets)=>{
|
|
159
170
|
setAssets(selectedAssets);
|
|
160
171
|
setStep(undefined);
|
|
@@ -344,8 +355,8 @@ const EMPTY_ATTRIBUTE = {};
|
|
|
344
355
|
asset: assets[0],
|
|
345
356
|
disabled: disabled,
|
|
346
357
|
onCopyLink: handleCopyLink,
|
|
347
|
-
// The details
|
|
348
|
-
onEdit: canRead ?
|
|
358
|
+
// The details drawer fetches the asset from the library, which needs read access.
|
|
359
|
+
onEdit: canRead ? handleEdit : undefined,
|
|
349
360
|
onRemove: handleRemove
|
|
350
361
|
});
|
|
351
362
|
}
|
|
@@ -357,7 +368,7 @@ const EMPTY_ATTRIBUTE = {};
|
|
|
357
368
|
asset: asset,
|
|
358
369
|
disabled: disabled,
|
|
359
370
|
onCopyLink: handleCopyLink,
|
|
360
|
-
onEdit: canRead ?
|
|
371
|
+
onEdit: canRead ? handleEdit : undefined,
|
|
361
372
|
onRemove: handleRemove
|
|
362
373
|
}, asset.id)),
|
|
363
374
|
uploads.map((upload)=>/*#__PURE__*/ jsxRuntime.jsx(AssetCard.UploadCard, {
|
|
@@ -384,6 +395,7 @@ const EMPTY_ATTRIBUTE = {};
|
|
|
384
395
|
}),
|
|
385
396
|
/*#__PURE__*/ jsxRuntime.jsx(AddAssetMenu.AddAssetMenu, {
|
|
386
397
|
disabled: disabled,
|
|
398
|
+
preventOpen: isAnyDrawerOpen,
|
|
387
399
|
onBrowseLibrary: ()=>setStep('browse'),
|
|
388
400
|
onUploadFromDevice: ()=>fileInputRef.current?.click(),
|
|
389
401
|
onUploadFromUrl: ()=>setIsUrlDialogOpen(true)
|
|
@@ -417,14 +429,12 @@ const EMPTY_ATTRIBUTE = {};
|
|
|
417
429
|
onClose: ()=>setStep(undefined),
|
|
418
430
|
onConfirm: handleSelect
|
|
419
431
|
}),
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
trackedLocation: CONTENT_MANAGER_LOCATION,
|
|
427
|
-
onClose: handleEditClose
|
|
432
|
+
/*#__PURE__*/ jsxRuntime.jsx(AssetDetailsDrawer.AssetDetailsDrawer, {
|
|
433
|
+
assetId: editedAssetId,
|
|
434
|
+
onClose: ()=>setEditedAssetId(null),
|
|
435
|
+
onAssetChange: handleAssetChange,
|
|
436
|
+
onAssetDelete: handleAssetDelete,
|
|
437
|
+
trackedLocation: CONTENT_MANAGER_LOCATION
|
|
428
438
|
}),
|
|
429
439
|
/*#__PURE__*/ jsxRuntime.jsx(ImportFromUrlDialog.ImportFromUrlDialog, {
|
|
430
440
|
open: isUrlDialogOpen,
|