@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.
Files changed (91) hide show
  1. package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.js +29 -0
  2. package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.js.map +1 -0
  3. package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.mjs +27 -0
  4. package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.mjs.map +1 -0
  5. package/dist/admin/components/Drawer.js +12 -7
  6. package/dist/admin/components/Drawer.js.map +1 -1
  7. package/dist/admin/components/Drawer.mjs +12 -7
  8. package/dist/admin/components/Drawer.mjs.map +1 -1
  9. package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.js +188 -0
  10. package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.js.map +1 -0
  11. package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.mjs +186 -0
  12. package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.mjs.map +1 -0
  13. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js +5 -1
  14. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js.map +1 -1
  15. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs +5 -1
  16. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs.map +1 -1
  17. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js +37 -27
  18. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js.map +1 -1
  19. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs +37 -27
  20. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs.map +1 -1
  21. package/dist/admin/components/MediaLibraryInput/openAssetDetails.js +46 -0
  22. package/dist/admin/components/MediaLibraryInput/openAssetDetails.js.map +1 -0
  23. package/dist/admin/components/MediaLibraryInput/openAssetDetails.mjs +43 -0
  24. package/dist/admin/components/MediaLibraryInput/openAssetDetails.mjs.map +1 -0
  25. package/dist/admin/index.js +10 -1
  26. package/dist/admin/index.js.map +1 -1
  27. package/dist/admin/index.mjs +10 -1
  28. package/dist/admin/index.mjs.map +1 -1
  29. package/dist/admin/pages/Assets/AssetsPage.js +1 -1
  30. package/dist/admin/pages/Assets/AssetsPage.js.map +1 -1
  31. package/dist/admin/pages/Assets/AssetsPage.mjs +2 -2
  32. package/dist/admin/pages/Assets/AssetsPage.mjs.map +1 -1
  33. package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +2 -2
  34. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +181 -65
  35. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  36. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +184 -69
  37. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  38. package/dist/admin/pages/Assets/components/AssetDetails/AssetPreview.mjs +1 -1
  39. package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.js +2 -6
  40. package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.js.map +1 -1
  41. package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.mjs +2 -6
  42. package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.mjs.map +1 -1
  43. package/dist/admin/src/components/AssetPickerDialog/MediaLibraryPickerDialog.d.ts +19 -0
  44. package/dist/admin/src/components/Drawer.d.ts +8 -0
  45. package/dist/admin/src/components/EditorFileDropZone/EditorFileDropZone.d.ts +21 -0
  46. package/dist/admin/src/components/MediaLibraryInput/AddAssetMenu.d.ts +7 -1
  47. package/dist/admin/src/components/MediaLibraryInput/openAssetDetails.d.ts +10 -0
  48. package/dist/admin/src/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +41 -5
  49. package/dist/admin/src/pages/Assets/hooks/useIsAssetDetailsOpen.d.ts +2 -6
  50. package/dist/admin/translations/en.json.js +4 -0
  51. package/dist/admin/translations/en.json.js.map +1 -1
  52. package/dist/admin/translations/en.json.mjs +4 -0
  53. package/dist/admin/translations/en.json.mjs.map +1 -1
  54. package/dist/server/audit-logs.js +264 -0
  55. package/dist/server/audit-logs.js.map +1 -0
  56. package/dist/server/audit-logs.mjs +255 -0
  57. package/dist/server/audit-logs.mjs.map +1 -0
  58. package/dist/server/bootstrap.js +4 -0
  59. package/dist/server/bootstrap.js.map +1 -1
  60. package/dist/server/bootstrap.mjs +4 -0
  61. package/dist/server/bootstrap.mjs.map +1 -1
  62. package/dist/server/controllers/admin-folder-file.js +23 -1
  63. package/dist/server/controllers/admin-folder-file.js.map +1 -1
  64. package/dist/server/controllers/admin-folder-file.mjs +23 -1
  65. package/dist/server/controllers/admin-folder-file.mjs.map +1 -1
  66. package/dist/server/services/folder.js +48 -1
  67. package/dist/server/services/folder.js.map +1 -1
  68. package/dist/server/services/folder.mjs +48 -1
  69. package/dist/server/services/folder.mjs.map +1 -1
  70. package/dist/server/services/image-manipulation.js +12 -5
  71. package/dist/server/services/image-manipulation.js.map +1 -1
  72. package/dist/server/services/image-manipulation.mjs +12 -5
  73. package/dist/server/services/image-manipulation.mjs.map +1 -1
  74. package/dist/server/services/upload.js +100 -16
  75. package/dist/server/services/upload.js.map +1 -1
  76. package/dist/server/services/upload.mjs +102 -18
  77. package/dist/server/services/upload.mjs.map +1 -1
  78. package/dist/server/src/audit-logs.d.ts +142 -0
  79. package/dist/server/src/audit-logs.d.ts.map +1 -0
  80. package/dist/server/src/bootstrap.d.ts.map +1 -1
  81. package/dist/server/src/controllers/admin-folder-file.d.ts.map +1 -1
  82. package/dist/server/src/index.d.ts +3 -3
  83. package/dist/server/src/index.d.ts.map +1 -1
  84. package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -1
  85. package/dist/server/src/services/folder.d.ts.map +1 -1
  86. package/dist/server/src/services/image-manipulation.d.ts +3 -3
  87. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  88. package/dist/server/src/services/index.d.ts +3 -3
  89. package/dist/server/src/services/index.d.ts.map +1 -1
  90. package/dist/server/src/services/upload.d.ts.map +1 -1
  91. 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":"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"}
@@ -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":"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"}
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":"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"}
@@ -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":"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"}
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 EditAssetContent = require('../../legacy/components/EditAssetDialog/EditAssetContent.js');
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 [editedAsset, setEditedAsset] = React.useState(null);
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, canUpdate, canCopyLink, canDownload } = useMediaLibraryPermissions.useMediaLibraryPermissions();
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
- const handleEditClose = (result)=>{
144
- const asset = editedAsset;
145
- setEditedAsset(null);
146
- if (!asset) {
147
- return;
148
- }
149
- // `null` means the asset was deleted from the library from inside the dialog.
150
- if (result === null) {
151
- setAssets(assetsRef.current.filter((current)=>current.id !== asset.id));
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 dialog needs the library's folder tree, which needs read access.
348
- onEdit: canRead ? setEditedAsset : undefined,
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 ? setEditedAsset : undefined,
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
- editedAsset && /*#__PURE__*/ jsxRuntime.jsx(EditAssetContent.EditAssetDialog, {
421
- open: true,
422
- asset: editedAsset,
423
- canUpdate: canUpdate && !disabled,
424
- canCopyLink: canCopyLink,
425
- canDownload: canDownload,
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,