@strapi/upload 5.47.0 → 5.48.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/EditAssetDialog/EditAssetContent.js +12 -2
- package/dist/admin/components/EditAssetDialog/EditAssetContent.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs +12 -2
- package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.js +1 -0
- package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.mjs +1 -0
- package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.mjs.map +1 -1
- package/dist/admin/future/components/Drawer.js +7 -2
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +7 -2
- package/dist/admin/future/components/Drawer.mjs.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.js +33 -29
- package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.mjs +36 -32
- package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.js +2 -2
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +3 -3
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +733 -148
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +737 -155
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.js +25 -5
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.mjs +25 -5
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.mjs.map +1 -1
- package/dist/admin/future/services/api.js +124 -200
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +124 -200
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +88 -1
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +86 -2
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +33 -1
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +33 -2
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/services/settings.js +18 -0
- package/dist/admin/future/services/settings.js.map +1 -0
- package/dist/admin/future/services/settings.mjs +16 -0
- package/dist/admin/future/services/settings.mjs.map +1 -0
- package/dist/admin/future/services/uploadFileViaXHR.js +92 -0
- package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -0
- package/dist/admin/future/services/uploadFileViaXHR.mjs +88 -0
- package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -0
- package/dist/admin/future/store/uploadProgress.js +32 -26
- package/dist/admin/future/store/uploadProgress.js.map +1 -1
- package/dist/admin/future/store/uploadProgress.mjs +32 -27
- package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
- package/dist/admin/future/utils/createRafBatcher.js +42 -0
- package/dist/admin/future/utils/createRafBatcher.js.map +1 -0
- package/dist/admin/future/utils/createRafBatcher.mjs +40 -0
- package/dist/admin/future/utils/createRafBatcher.mjs.map +1 -0
- package/dist/admin/future/utils/downloadFile.js +19 -0
- package/dist/admin/future/utils/downloadFile.js.map +1 -0
- package/dist/admin/future/utils/downloadFile.mjs +17 -0
- package/dist/admin/future/utils/downloadFile.mjs.map +1 -0
- package/dist/admin/hooks/useAssets.js +5 -3
- package/dist/admin/hooks/useAssets.js.map +1 -1
- package/dist/admin/hooks/useAssets.mjs +5 -3
- package/dist/admin/hooks/useAssets.mjs.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/EditAssetDialog/EditAssetContent.d.ts +2 -1
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +22 -0
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetPreview.d.ts +4 -1
- package/dist/admin/src/future/services/api.d.ts +9 -8
- package/dist/admin/src/future/services/assets.d.ts +11 -2
- package/dist/admin/src/future/services/folders.d.ts +1 -1
- package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +34 -0
- package/dist/admin/src/future/store/uploadProgress.d.ts +17 -4
- package/dist/admin/src/future/utils/createRafBatcher.d.ts +23 -0
- package/dist/admin/src/future/utils/downloadFile.d.ts +6 -0
- package/dist/admin/translations/{dk.json.js → da.json.js} +3 -3
- package/dist/admin/translations/{dk.json.js.map → da.json.js.map} +1 -1
- package/dist/admin/translations/{dk.json.mjs → da.json.mjs} +3 -3
- package/dist/admin/translations/{dk.json.mjs.map → da.json.mjs.map} +1 -1
- package/dist/admin/translations/en.json.js +26 -1
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +26 -1
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/bootstrap.js +0 -3
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/server/bootstrap.mjs +0 -3
- package/dist/server/bootstrap.mjs.map +1 -1
- package/dist/server/controllers/admin-upload.js +69 -118
- package/dist/server/controllers/admin-upload.js.map +1 -1
- package/dist/server/controllers/admin-upload.mjs +69 -118
- package/dist/server/controllers/admin-upload.mjs.map +1 -1
- package/dist/server/routes/admin.js +2 -2
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/admin.mjs +2 -2
- package/dist/server/routes/admin.mjs.map +1 -1
- package/dist/server/services/ai-metadata-jobs.js +0 -23
- package/dist/server/services/ai-metadata-jobs.js.map +1 -1
- package/dist/server/services/ai-metadata-jobs.mjs +0 -23
- package/dist/server/services/ai-metadata-jobs.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js +16 -8
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs +16 -8
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/services/upload.js +1 -1
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs +1 -1
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-upload.d.ts +6 -8
- package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +1 -1
- package/dist/server/src/index.d.ts +1 -2
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata-jobs.d.ts +0 -1
- package/dist/server/src/services/ai-metadata-jobs.d.ts.map +1 -1
- package/dist/server/src/services/image-manipulation.d.ts +5 -0
- package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +0 -1
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/upload.d.ts.map +1 -1
- package/dist/server/src/types.d.ts +2 -2
- package/dist/server/src/types.d.ts.map +1 -1
- package/dist/shared/contracts/files.d.ts +19 -2
- package/dist/shared/contracts/files.d.ts.map +1 -1
- package/package.json +8 -8
|
@@ -1,20 +1,40 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { useQueryParams, getDisplayName } from '@strapi/admin/strapi-admin';
|
|
4
|
-
import { VisuallyHidden, Flex, Loader, Alert, Typography, Box, Field, TextInput } from '@strapi/design-system';
|
|
5
|
-
import { FileError, ArrowLineRight, WarningCircle } from '@strapi/icons';
|
|
3
|
+
import { useQueryParams, useNotification, Form, Blocker, getDisplayName, useField, useForm, useClipboard } from '@strapi/admin/strapi-admin';
|
|
4
|
+
import { VisuallyHidden, Flex, Loader, Alert, Typography, Box, Button, IconButton, Dialog, Field, TextInput, Tooltip, SingleSelect, SingleSelectOption } from '@strapi/design-system';
|
|
5
|
+
import { FileError, ArrowLineRight, ArrowsCounterClockwise, Trash, WarningCircle, Link, Download } from '@strapi/icons';
|
|
6
6
|
import { useIntl } from 'react-intl';
|
|
7
7
|
import { styled } from 'styled-components';
|
|
8
|
-
import {
|
|
8
|
+
import { Drawer } from '../../../../components/Drawer.mjs';
|
|
9
9
|
import { AssetType } from '../../../../enums.mjs';
|
|
10
|
-
import { useGetAssetQuery } from '../../../../services/assets.mjs';
|
|
11
|
-
import {
|
|
10
|
+
import { useGetAssetQuery, useUpdateAssetMutation, useReplaceAssetMutation, useDeleteAssetMutation } from '../../../../services/assets.mjs';
|
|
11
|
+
import { useGetAllFoldersQuery } from '../../../../services/folders.mjs';
|
|
12
|
+
import { useGetSettingsQuery } from '../../../../services/settings.mjs';
|
|
13
|
+
import { downloadFile } from '../../../../utils/downloadFile.mjs';
|
|
14
|
+
import { formatBytes, getFileExtension, prefixFileUrlWithBackendUrl } from '../../../../utils/files.mjs';
|
|
12
15
|
import { getAssetIcon } from '../../../../utils/getAssetIcon.mjs';
|
|
13
16
|
import { getTranslationKey } from '../../../../utils/translations.mjs';
|
|
17
|
+
import { useFolderInfo } from '../../hooks/useFolderInfo.mjs';
|
|
14
18
|
import { AssetPreview } from './AssetPreview.mjs';
|
|
15
19
|
|
|
16
20
|
// Name of the parameter to look for in the URL to open the drawer
|
|
17
21
|
const URL_PARAM = 'assetId';
|
|
22
|
+
const DrawerNotifyContext = /*#__PURE__*/ React.createContext(null);
|
|
23
|
+
const useDrawerNotify = ()=>{
|
|
24
|
+
const ctx = React.useContext(DrawerNotifyContext);
|
|
25
|
+
if (!ctx) {
|
|
26
|
+
throw new Error('useDrawerNotify must be used within AssetDetails');
|
|
27
|
+
}
|
|
28
|
+
return ctx;
|
|
29
|
+
};
|
|
30
|
+
const AssetOperationsContext = /*#__PURE__*/ React.createContext(null);
|
|
31
|
+
const useAssetOperation = ()=>{
|
|
32
|
+
const ctx = React.useContext(AssetOperationsContext);
|
|
33
|
+
if (!ctx) {
|
|
34
|
+
throw new Error('useAssetOperation must be used within AssetDetails');
|
|
35
|
+
}
|
|
36
|
+
return ctx;
|
|
37
|
+
};
|
|
18
38
|
/* -------------------------------------------------------------------------------------------------
|
|
19
39
|
* useAssetDetailsParam - sync drawer visibility with URL ?{URL_PARAM}={id}
|
|
20
40
|
* -----------------------------------------------------------------------------------------------*/ const useAssetDetailsParam = ()=>{
|
|
@@ -22,51 +42,49 @@ const URL_PARAM = 'assetId';
|
|
|
22
42
|
const detailsId = query?.[URL_PARAM];
|
|
23
43
|
const assetId = detailsId ? parseInt(detailsId, 10) : null;
|
|
24
44
|
const hasValidId = assetId !== null && !Number.isNaN(assetId);
|
|
25
|
-
|
|
45
|
+
// Closing is driven by removing the URL param (a navigation), so navigation
|
|
46
|
+
// guards like <Blocker> can intercept it. `isMounted` keeps the drawer in the
|
|
47
|
+
// tree through the slide-out: it stays true once opened and only flips false
|
|
48
|
+
// when the close animation actually ends (see onCloseAnimationEnd), so the
|
|
49
|
+
// close duration lives entirely in CSS — no JS timer.
|
|
50
|
+
const [isMounted, setIsMounted] = React.useState(hasValidId);
|
|
26
51
|
const displayAssetId = React.useRef(null);
|
|
27
|
-
const isVisible = hasValidId && !isClosing;
|
|
28
52
|
React.useEffect(()=>{
|
|
29
53
|
if (hasValidId) {
|
|
30
54
|
displayAssetId.current = assetId;
|
|
55
|
+
setIsMounted(true);
|
|
31
56
|
}
|
|
32
57
|
}, [
|
|
33
58
|
hasValidId,
|
|
34
59
|
assetId
|
|
35
60
|
]);
|
|
61
|
+
const onCloseAnimationEnd = React.useCallback((event)=>{
|
|
62
|
+
// Ignore animations bubbling up from descendants, and the slide-in.
|
|
63
|
+
if (event.target === event.currentTarget && !hasValidId) {
|
|
64
|
+
setIsMounted(false);
|
|
65
|
+
}
|
|
66
|
+
}, [
|
|
67
|
+
hasValidId
|
|
68
|
+
]);
|
|
36
69
|
const openDetails = React.useCallback((id)=>{
|
|
37
|
-
setIsClosing(false);
|
|
38
70
|
setQuery({
|
|
39
71
|
[URL_PARAM]: String(id)
|
|
40
|
-
});
|
|
72
|
+
}, 'push', true);
|
|
41
73
|
}, [
|
|
42
74
|
setQuery
|
|
43
75
|
]);
|
|
44
76
|
const closeDetails = React.useCallback(()=>{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
hasValidId
|
|
49
|
-
]);
|
|
50
|
-
React.useEffect(()=>{
|
|
51
|
-
if (!isClosing) return;
|
|
52
|
-
const timer = window.setTimeout(()=>{
|
|
53
|
-
setQuery({
|
|
54
|
-
[URL_PARAM]: undefined
|
|
55
|
-
}, 'remove');
|
|
56
|
-
setIsClosing(false);
|
|
57
|
-
displayAssetId.current = null;
|
|
58
|
-
}, DRAWER_CLOSE_ANIMATION_MS);
|
|
59
|
-
return ()=>window.clearTimeout(timer);
|
|
77
|
+
setQuery({
|
|
78
|
+
[URL_PARAM]: undefined
|
|
79
|
+
}, 'remove', true);
|
|
60
80
|
}, [
|
|
61
|
-
isClosing,
|
|
62
81
|
setQuery
|
|
63
82
|
]);
|
|
64
|
-
const shouldRenderDrawer = hasValidId || isClosing;
|
|
65
|
-
const drawerAssetId = isClosing ? displayAssetId.current ?? assetId : assetId;
|
|
66
83
|
return {
|
|
67
|
-
assetId:
|
|
68
|
-
isVisible,
|
|
69
|
-
shouldRenderDrawer,
|
|
84
|
+
assetId: hasValidId ? assetId : displayAssetId.current,
|
|
85
|
+
isVisible: hasValidId,
|
|
86
|
+
shouldRenderDrawer: isMounted,
|
|
87
|
+
onCloseAnimationEnd,
|
|
70
88
|
openDetails,
|
|
71
89
|
closeDetails
|
|
72
90
|
};
|
|
@@ -96,7 +114,50 @@ const DetailItem = ({ label, value })=>/*#__PURE__*/ jsxs(DetailItemContainer, {
|
|
|
96
114
|
});
|
|
97
115
|
/* -------------------------------------------------------------------------------------------------
|
|
98
116
|
* DetailField
|
|
99
|
-
* -----------------------------------------------------------------------------------------------*/
|
|
117
|
+
* -----------------------------------------------------------------------------------------------*/ /**
|
|
118
|
+
* Make the asset details Form behave as a flex column inside Drawer.Body so
|
|
119
|
+
* the scrollable area can grow while the footer stays pinned at the bottom.
|
|
120
|
+
* The Form component from `@strapi/admin/strapi-admin` only forwards `width`
|
|
121
|
+
* + `height` to its Box, so we target the rendered `<form>` element via a
|
|
122
|
+
* styled-components descendant rule.
|
|
123
|
+
*/ const FormShell = styled(Box)`
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
flex: 1;
|
|
127
|
+
min-height: 0;
|
|
128
|
+
|
|
129
|
+
> form {
|
|
130
|
+
display: flex;
|
|
131
|
+
flex-direction: column;
|
|
132
|
+
flex: 1;
|
|
133
|
+
min-height: 0;
|
|
134
|
+
position: relative;
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
/**
|
|
138
|
+
* In-drawer toast container
|
|
139
|
+
*/ const DrawerToastSlot = styled(Box)`
|
|
140
|
+
position: absolute;
|
|
141
|
+
top: ${({ theme })=>theme.spaces[2]};
|
|
142
|
+
left: 50%;
|
|
143
|
+
transform: translateX(-50%);
|
|
144
|
+
z-index: 10;
|
|
145
|
+
width: calc(100% - ${({ theme })=>theme.spaces[2]});
|
|
146
|
+
`;
|
|
147
|
+
/**
|
|
148
|
+
* Full-form overlay rendered during long-running drawer-scoped mutations
|
|
149
|
+
* (e.g. replacing the binary). Sits above the toast slot (z-index 10) and
|
|
150
|
+
* the in-drawer Alert so the user can't interact with the form mid-flight.
|
|
151
|
+
*/ const DrawerBusyOverlay = styled(Flex)`
|
|
152
|
+
position: absolute;
|
|
153
|
+
inset: 0;
|
|
154
|
+
z-index: 20;
|
|
155
|
+
align-items: center;
|
|
156
|
+
justify-content: center;
|
|
157
|
+
background: ${({ theme })=>theme.colors.neutral0};
|
|
158
|
+
opacity: 0.7;
|
|
159
|
+
`;
|
|
160
|
+
const StyledWarning = styled(WarningCircle)`
|
|
100
161
|
width: 1.6rem;
|
|
101
162
|
height: 1.6rem;
|
|
102
163
|
|
|
@@ -104,7 +165,18 @@ const DetailItem = ({ label, value })=>/*#__PURE__*/ jsxs(DetailItemContainer, {
|
|
|
104
165
|
fill: ${({ theme })=>theme.colors.warning500};
|
|
105
166
|
}
|
|
106
167
|
`;
|
|
107
|
-
const DetailField = ({ name, label,
|
|
168
|
+
const DetailField = ({ name, label, required })=>{
|
|
169
|
+
const { formatMessage } = useIntl();
|
|
170
|
+
const field = useField(name);
|
|
171
|
+
const isSubmitting = useForm('DetailField', (state)=>state.isSubmitting);
|
|
172
|
+
const value = field.value ?? '';
|
|
173
|
+
const emptyTooltipLabel = formatMessage({
|
|
174
|
+
id: getTranslationKey('asset-details.field.empty'),
|
|
175
|
+
defaultMessage: '{label} is currently empty.'
|
|
176
|
+
}, {
|
|
177
|
+
label
|
|
178
|
+
});
|
|
179
|
+
return /*#__PURE__*/ jsxs(Field.Root, {
|
|
108
180
|
name: name,
|
|
109
181
|
required: required,
|
|
110
182
|
children: [
|
|
@@ -112,139 +184,651 @@ const DetailField = ({ name, label, value, required })=>/*#__PURE__*/ jsxs(Field
|
|
|
112
184
|
children: label
|
|
113
185
|
}),
|
|
114
186
|
/*#__PURE__*/ jsx(TextInput, {
|
|
115
|
-
value: value
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
187
|
+
value: value,
|
|
188
|
+
onChange: (event)=>field.onChange(name, event.target.value),
|
|
189
|
+
endAction: !value ? /*#__PURE__*/ jsx(Tooltip, {
|
|
190
|
+
label: emptyTooltipLabel,
|
|
191
|
+
children: /*#__PURE__*/ jsx(StyledWarning, {
|
|
192
|
+
"aria-label": emptyTooltipLabel,
|
|
193
|
+
role: "img"
|
|
194
|
+
})
|
|
195
|
+
}) : undefined,
|
|
196
|
+
type: "text",
|
|
197
|
+
disabled: isSubmitting
|
|
120
198
|
})
|
|
121
199
|
]
|
|
122
200
|
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
paddingTop: 4,
|
|
131
|
-
paddingBottom: 4,
|
|
132
|
-
paddingLeft: 5,
|
|
133
|
-
paddingRight: 5,
|
|
201
|
+
};
|
|
202
|
+
const LocationField = ({ label, rootLabel, folders })=>{
|
|
203
|
+
const field = useField('folder');
|
|
204
|
+
const isSubmitting = useForm('LocationField', (state)=>state.isSubmitting);
|
|
205
|
+
return /*#__PURE__*/ jsxs(Field.Root, {
|
|
206
|
+
name: "folder",
|
|
207
|
+
required: true,
|
|
134
208
|
children: [
|
|
135
|
-
/*#__PURE__*/ jsx(
|
|
136
|
-
|
|
137
|
-
fontWeight: "semiBold",
|
|
138
|
-
tag: "h3",
|
|
139
|
-
children: formatMessage({
|
|
140
|
-
id: getTranslationKey('asset-details.fileInfo'),
|
|
141
|
-
defaultMessage: 'File info'
|
|
142
|
-
})
|
|
209
|
+
/*#__PURE__*/ jsx(Field.Label, {
|
|
210
|
+
children: label
|
|
143
211
|
}),
|
|
144
|
-
/*#__PURE__*/ jsxs(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
paddingRight: 6,
|
|
152
|
-
alignItems: "flex-start",
|
|
212
|
+
/*#__PURE__*/ jsxs(SingleSelect, {
|
|
213
|
+
value: field.value == null ? '' : String(field.value),
|
|
214
|
+
onChange: (value)=>{
|
|
215
|
+
const next = value === '' ? null : Number(value);
|
|
216
|
+
field.onChange('folder', next);
|
|
217
|
+
},
|
|
218
|
+
disabled: isSubmitting,
|
|
153
219
|
children: [
|
|
154
|
-
/*#__PURE__*/ jsx(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
defaultMessage: 'Creation date'
|
|
158
|
-
}),
|
|
159
|
-
value: asset.createdAt ? formatDate(new Date(asset.createdAt), {
|
|
160
|
-
dateStyle: 'long',
|
|
161
|
-
timeStyle: 'short'
|
|
162
|
-
}) : null
|
|
163
|
-
}),
|
|
164
|
-
/*#__PURE__*/ jsx(DetailItem, {
|
|
165
|
-
label: formatMessage({
|
|
166
|
-
id: getTranslationKey('asset-details.lastUpdated'),
|
|
167
|
-
defaultMessage: 'Last updated'
|
|
168
|
-
}),
|
|
169
|
-
value: asset.updatedAt ? formatDate(new Date(asset.updatedAt), {
|
|
170
|
-
dateStyle: 'long',
|
|
171
|
-
timeStyle: 'short'
|
|
172
|
-
}) : null
|
|
173
|
-
}),
|
|
174
|
-
/*#__PURE__*/ jsx(DetailItem, {
|
|
175
|
-
label: formatMessage({
|
|
176
|
-
id: getTranslationKey('asset-details.createdBy'),
|
|
177
|
-
defaultMessage: 'Created by'
|
|
178
|
-
}),
|
|
179
|
-
value: asset.createdBy ? getDisplayName({
|
|
180
|
-
firstname: asset.createdBy.firstname ?? undefined,
|
|
181
|
-
lastname: asset.createdBy.lastname ?? undefined,
|
|
182
|
-
username: asset.createdBy.username ?? undefined,
|
|
183
|
-
email: asset.createdBy.email ?? undefined
|
|
184
|
-
}) ?? '-' : null
|
|
220
|
+
/*#__PURE__*/ jsx(SingleSelectOption, {
|
|
221
|
+
value: "",
|
|
222
|
+
children: rootLabel
|
|
185
223
|
}),
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
224
|
+
folders.map((folder)=>/*#__PURE__*/ jsx(SingleSelectOption, {
|
|
225
|
+
value: String(folder.id),
|
|
226
|
+
children: folder.name
|
|
227
|
+
}, folder.id))
|
|
228
|
+
]
|
|
229
|
+
})
|
|
230
|
+
]
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
/* -------------------------------------------------------------------------------------------------
|
|
234
|
+
* DeleteAssetButton
|
|
235
|
+
* -----------------------------------------------------------------------------------------------*/ const DeleteAssetButton = ()=>{
|
|
236
|
+
const { formatMessage } = useIntl();
|
|
237
|
+
const { deleteAsset, isDeleting } = useAssetOperation();
|
|
238
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
239
|
+
const handleConfirm = async ()=>{
|
|
240
|
+
await deleteAsset();
|
|
241
|
+
setIsOpen(false);
|
|
242
|
+
};
|
|
243
|
+
const triggerLabel = formatMessage({
|
|
244
|
+
id: getTranslationKey('asset-details.delete.trigger'),
|
|
245
|
+
defaultMessage: 'Delete this file'
|
|
246
|
+
});
|
|
247
|
+
return /*#__PURE__*/ jsxs(Dialog.Root, {
|
|
248
|
+
open: isOpen,
|
|
249
|
+
onOpenChange: setIsOpen,
|
|
250
|
+
children: [
|
|
251
|
+
/*#__PURE__*/ jsx(Dialog.Trigger, {
|
|
252
|
+
children: /*#__PURE__*/ jsx(IconButton, {
|
|
253
|
+
withTooltip: false,
|
|
254
|
+
label: triggerLabel,
|
|
255
|
+
variant: "danger-light",
|
|
256
|
+
children: /*#__PURE__*/ jsx(Trash, {})
|
|
257
|
+
})
|
|
258
|
+
}),
|
|
259
|
+
/*#__PURE__*/ jsxs(Dialog.Content, {
|
|
260
|
+
children: [
|
|
261
|
+
/*#__PURE__*/ jsx(Dialog.Header, {
|
|
262
|
+
children: formatMessage({
|
|
263
|
+
id: getTranslationKey('asset-details.delete.title'),
|
|
264
|
+
defaultMessage: 'Delete this media file?'
|
|
265
|
+
})
|
|
199
266
|
}),
|
|
200
|
-
/*#__PURE__*/ jsx(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
267
|
+
/*#__PURE__*/ jsx(Dialog.Body, {
|
|
268
|
+
icon: /*#__PURE__*/ jsx(WarningCircle, {
|
|
269
|
+
width: "24px",
|
|
270
|
+
height: "24px",
|
|
271
|
+
fill: "danger600"
|
|
204
272
|
}),
|
|
205
|
-
|
|
273
|
+
textAlign: "center",
|
|
274
|
+
children: formatMessage({
|
|
275
|
+
id: getTranslationKey('asset-details.delete.description'),
|
|
276
|
+
defaultMessage: 'This file cannot be recovered once deleted. If it is currently in use, linked content will break and image containers will be empty.'
|
|
277
|
+
})
|
|
206
278
|
}),
|
|
207
|
-
/*#__PURE__*/
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
279
|
+
/*#__PURE__*/ jsxs(Dialog.Footer, {
|
|
280
|
+
children: [
|
|
281
|
+
/*#__PURE__*/ jsx(Dialog.Cancel, {
|
|
282
|
+
children: /*#__PURE__*/ jsx(Button, {
|
|
283
|
+
variant: "tertiary",
|
|
284
|
+
disabled: isDeleting,
|
|
285
|
+
fullWidth: true,
|
|
286
|
+
children: formatMessage({
|
|
287
|
+
id: 'app.components.Button.cancel',
|
|
288
|
+
defaultMessage: 'Cancel'
|
|
289
|
+
})
|
|
290
|
+
})
|
|
291
|
+
}),
|
|
292
|
+
/*#__PURE__*/ jsx(Dialog.Action, {
|
|
293
|
+
children: /*#__PURE__*/ jsx(Button, {
|
|
294
|
+
variant: "danger-light",
|
|
295
|
+
loading: isDeleting,
|
|
296
|
+
onClick: handleConfirm,
|
|
297
|
+
fullWidth: true,
|
|
298
|
+
children: formatMessage({
|
|
299
|
+
id: 'app.components.Button.confirm',
|
|
300
|
+
defaultMessage: 'Confirm'
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
]
|
|
213
305
|
})
|
|
214
306
|
]
|
|
307
|
+
})
|
|
308
|
+
]
|
|
309
|
+
});
|
|
310
|
+
};
|
|
311
|
+
const CopyLinkButton = ({ asset })=>{
|
|
312
|
+
const { formatMessage } = useIntl();
|
|
313
|
+
const { copy } = useClipboard();
|
|
314
|
+
const notify = useDrawerNotify();
|
|
315
|
+
const handleCopy = async ()=>{
|
|
316
|
+
const url = prefixFileUrlWithBackendUrl(asset.url);
|
|
317
|
+
if (!url) return;
|
|
318
|
+
const didCopy = await copy(url);
|
|
319
|
+
notify({
|
|
320
|
+
type: didCopy ? 'success' : 'danger',
|
|
321
|
+
message: didCopy ? formatMessage({
|
|
322
|
+
id: getTranslationKey('asset-details.copy-link.success'),
|
|
323
|
+
defaultMessage: 'Link copied.'
|
|
324
|
+
}) : formatMessage({
|
|
325
|
+
id: getTranslationKey('asset-details.copy-link.error'),
|
|
326
|
+
defaultMessage: 'Failed to copy the link.'
|
|
327
|
+
})
|
|
328
|
+
});
|
|
329
|
+
};
|
|
330
|
+
return /*#__PURE__*/ jsx(IconButton, {
|
|
331
|
+
withTooltip: false,
|
|
332
|
+
label: formatMessage({
|
|
333
|
+
id: getTranslationKey('asset-details.copy-link.trigger'),
|
|
334
|
+
defaultMessage: 'Copy link'
|
|
335
|
+
}),
|
|
336
|
+
variant: "tertiary",
|
|
337
|
+
onClick: handleCopy,
|
|
338
|
+
children: /*#__PURE__*/ jsx(Link, {})
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
const DownloadAssetButton = ({ asset })=>{
|
|
342
|
+
const { formatMessage } = useIntl();
|
|
343
|
+
const notify = useDrawerNotify();
|
|
344
|
+
const [isDownloading, setIsDownloading] = React.useState(false);
|
|
345
|
+
const handleDownload = async ()=>{
|
|
346
|
+
const url = prefixFileUrlWithBackendUrl(asset.url);
|
|
347
|
+
if (!url) return;
|
|
348
|
+
setIsDownloading(true);
|
|
349
|
+
try {
|
|
350
|
+
await downloadFile(url, asset.name);
|
|
351
|
+
} catch {
|
|
352
|
+
notify({
|
|
353
|
+
type: 'danger',
|
|
354
|
+
message: formatMessage({
|
|
355
|
+
id: getTranslationKey('asset-details.download.error'),
|
|
356
|
+
defaultMessage: 'Failed to download the file.'
|
|
357
|
+
})
|
|
358
|
+
});
|
|
359
|
+
} finally{
|
|
360
|
+
setIsDownloading(false);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
return /*#__PURE__*/ jsx(IconButton, {
|
|
364
|
+
withTooltip: false,
|
|
365
|
+
label: formatMessage({
|
|
366
|
+
id: getTranslationKey('asset-details.download.trigger'),
|
|
367
|
+
defaultMessage: 'Download'
|
|
368
|
+
}),
|
|
369
|
+
variant: "tertiary",
|
|
370
|
+
onClick: handleDownload,
|
|
371
|
+
disabled: isDownloading,
|
|
372
|
+
children: /*#__PURE__*/ jsx(Download, {})
|
|
373
|
+
});
|
|
374
|
+
};
|
|
375
|
+
/* -------------------------------------------------------------------------------------------------
|
|
376
|
+
* ReplaceAssetButton
|
|
377
|
+
* -----------------------------------------------------------------------------------------------*/ const ReplaceAssetButton = ()=>{
|
|
378
|
+
const { formatMessage } = useIntl();
|
|
379
|
+
const { replaceAsset, isReplacing } = useAssetOperation();
|
|
380
|
+
const fileInputRef = React.useRef(null);
|
|
381
|
+
const [isDialogOpen, setIsDialogOpen] = React.useState(false);
|
|
382
|
+
const { data: settings } = useGetSettingsQuery();
|
|
383
|
+
const aiEnabled = settings?.data?.aiMetadata ?? false;
|
|
384
|
+
const handleTriggerClick = ()=>{
|
|
385
|
+
setIsDialogOpen(true);
|
|
386
|
+
};
|
|
387
|
+
const handleContinue = ()=>{
|
|
388
|
+
// Confirm first, then open the native picker so the user only commits to
|
|
389
|
+
// replacing after acknowledging the warning. The actual POST is delegated
|
|
390
|
+
// to the parent (which owns the mutation + loading state).
|
|
391
|
+
setIsDialogOpen(false);
|
|
392
|
+
fileInputRef.current?.click();
|
|
393
|
+
};
|
|
394
|
+
const handleFileChange = async (event)=>{
|
|
395
|
+
const file = event.target.files?.[0];
|
|
396
|
+
// Reset the native input so the same file can be picked again later.
|
|
397
|
+
event.target.value = '';
|
|
398
|
+
if (!file) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
await replaceAsset(file);
|
|
402
|
+
};
|
|
403
|
+
return /*#__PURE__*/ jsxs(Fragment, {
|
|
404
|
+
children: [
|
|
405
|
+
/*#__PURE__*/ jsx(VisuallyHidden, {
|
|
406
|
+
children: /*#__PURE__*/ jsx("input", {
|
|
407
|
+
ref: fileInputRef,
|
|
408
|
+
type: "file",
|
|
409
|
+
multiple: false,
|
|
410
|
+
onChange: handleFileChange,
|
|
411
|
+
"aria-hidden": true,
|
|
412
|
+
tabIndex: -1
|
|
413
|
+
})
|
|
215
414
|
}),
|
|
216
|
-
/*#__PURE__*/ jsx(
|
|
217
|
-
|
|
415
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
416
|
+
withTooltip: false,
|
|
218
417
|
label: formatMessage({
|
|
219
|
-
id: getTranslationKey('asset-details.
|
|
220
|
-
defaultMessage: '
|
|
418
|
+
id: getTranslationKey('asset-details.replace.trigger'),
|
|
419
|
+
defaultMessage: 'Replace this file'
|
|
221
420
|
}),
|
|
222
|
-
|
|
223
|
-
|
|
421
|
+
variant: "tertiary",
|
|
422
|
+
onClick: handleTriggerClick,
|
|
423
|
+
disabled: isReplacing,
|
|
424
|
+
children: /*#__PURE__*/ jsx(ArrowsCounterClockwise, {})
|
|
224
425
|
}),
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
426
|
+
/*#__PURE__*/ jsx(Dialog.Root, {
|
|
427
|
+
open: isDialogOpen,
|
|
428
|
+
onOpenChange: setIsDialogOpen,
|
|
429
|
+
children: /*#__PURE__*/ jsxs(Dialog.Content, {
|
|
430
|
+
children: [
|
|
431
|
+
/*#__PURE__*/ jsx(Dialog.Header, {
|
|
432
|
+
children: formatMessage({
|
|
433
|
+
id: getTranslationKey('asset-details.replace.title'),
|
|
434
|
+
defaultMessage: 'Replace this media file?'
|
|
435
|
+
})
|
|
232
436
|
}),
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
437
|
+
/*#__PURE__*/ jsx(Dialog.Body, {
|
|
438
|
+
textAlign: "center",
|
|
439
|
+
children: /*#__PURE__*/ jsxs(Flex, {
|
|
440
|
+
direction: "column",
|
|
441
|
+
textAlign: "center",
|
|
442
|
+
children: [
|
|
443
|
+
/*#__PURE__*/ jsx(Typography, {
|
|
444
|
+
variant: "omega",
|
|
445
|
+
children: formatMessage({
|
|
446
|
+
id: getTranslationKey('asset-details.replace.description'),
|
|
447
|
+
defaultMessage: 'Current content will be permanently replaced.'
|
|
448
|
+
})
|
|
449
|
+
}),
|
|
450
|
+
aiEnabled ? /*#__PURE__*/ jsx(Typography, {
|
|
451
|
+
variant: "omega",
|
|
452
|
+
children: formatMessage({
|
|
453
|
+
id: getTranslationKey('asset-details.replace.description.ai'),
|
|
454
|
+
defaultMessage: 'AI will generate new metadata after upload.'
|
|
455
|
+
})
|
|
456
|
+
}) : null
|
|
457
|
+
]
|
|
458
|
+
})
|
|
240
459
|
}),
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
460
|
+
/*#__PURE__*/ jsxs(Dialog.Footer, {
|
|
461
|
+
children: [
|
|
462
|
+
/*#__PURE__*/ jsx(Dialog.Cancel, {
|
|
463
|
+
children: /*#__PURE__*/ jsx(Button, {
|
|
464
|
+
variant: "tertiary",
|
|
465
|
+
fullWidth: true,
|
|
466
|
+
children: formatMessage({
|
|
467
|
+
id: 'app.components.Button.cancel',
|
|
468
|
+
defaultMessage: 'Cancel'
|
|
469
|
+
})
|
|
470
|
+
})
|
|
471
|
+
}),
|
|
472
|
+
/*#__PURE__*/ jsx(Dialog.Action, {
|
|
473
|
+
children: /*#__PURE__*/ jsx(Button, {
|
|
474
|
+
variant: "secondary",
|
|
475
|
+
onClick: handleContinue,
|
|
476
|
+
fullWidth: true,
|
|
477
|
+
children: formatMessage({
|
|
478
|
+
id: getTranslationKey('asset-details.replace.continue'),
|
|
479
|
+
defaultMessage: 'Continue'
|
|
480
|
+
})
|
|
481
|
+
})
|
|
482
|
+
})
|
|
483
|
+
]
|
|
484
|
+
})
|
|
485
|
+
]
|
|
486
|
+
})
|
|
244
487
|
})
|
|
245
488
|
]
|
|
246
489
|
});
|
|
247
490
|
};
|
|
491
|
+
const AssetDetails = ({ asset, closeDetails })=>{
|
|
492
|
+
const { formatMessage, formatDate } = useIntl();
|
|
493
|
+
const { data: folders = [] } = useGetAllFoldersQuery();
|
|
494
|
+
const { toggleNotification } = useNotification();
|
|
495
|
+
const [updateAsset] = useUpdateAssetMutation();
|
|
496
|
+
const [replaceMutation, { isLoading: isReplacing }] = useReplaceAssetMutation();
|
|
497
|
+
const [deleteMutation, { isLoading: isDeleting }] = useDeleteAssetMutation();
|
|
498
|
+
// In-drawer toast slot
|
|
499
|
+
const [drawerToast, setDrawerToast] = React.useState(null);
|
|
500
|
+
React.useEffect(()=>{
|
|
501
|
+
if (!drawerToast) return;
|
|
502
|
+
const timer = window.setTimeout(()=>setDrawerToast(null), 5000);
|
|
503
|
+
return ()=>window.clearTimeout(timer);
|
|
504
|
+
}, [
|
|
505
|
+
drawerToast
|
|
506
|
+
]);
|
|
507
|
+
// Local alias matching the DrawerNotifyContext signature, so the drawer's
|
|
508
|
+
// own handlers (replace, update) read like the consumers do.
|
|
509
|
+
const notify = React.useCallback((toast)=>setDrawerToast(toast), []);
|
|
510
|
+
const isImage = asset.mime?.includes(AssetType.Image);
|
|
511
|
+
const initialValues = {
|
|
512
|
+
name: asset.name ?? '',
|
|
513
|
+
caption: asset.caption ?? '',
|
|
514
|
+
alternativeText: asset.alternativeText ?? '',
|
|
515
|
+
folder: typeof asset.folder === 'object' && asset.folder !== null ? asset.folder.id ?? null : asset.folder ?? null
|
|
516
|
+
};
|
|
517
|
+
const handleSubmit = async (values)=>{
|
|
518
|
+
const res = await updateAsset({
|
|
519
|
+
id: asset.id,
|
|
520
|
+
fileInfo: {
|
|
521
|
+
name: values.name,
|
|
522
|
+
caption: values.caption,
|
|
523
|
+
alternativeText: values.alternativeText,
|
|
524
|
+
folder: values.folder
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
if ('error' in res) {
|
|
528
|
+
notify({
|
|
529
|
+
type: 'danger',
|
|
530
|
+
message: formatMessage({
|
|
531
|
+
id: getTranslationKey('asset-details.update.error'),
|
|
532
|
+
defaultMessage: 'Failed to update the file.'
|
|
533
|
+
})
|
|
534
|
+
});
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
notify({
|
|
538
|
+
type: 'success',
|
|
539
|
+
message: formatMessage({
|
|
540
|
+
id: getTranslationKey('asset-details.update.success'),
|
|
541
|
+
defaultMessage: 'File updated'
|
|
542
|
+
})
|
|
543
|
+
});
|
|
544
|
+
};
|
|
545
|
+
const { title: folderName } = useFolderInfo(typeof asset.folder === 'object' && asset.folder !== null ? asset.folder.id ?? null : asset.folder ?? null);
|
|
546
|
+
// Owns the replace upload so isReplacing can drive the busy overlay.
|
|
547
|
+
const handleReplace = async (file)=>{
|
|
548
|
+
const res = await replaceMutation({
|
|
549
|
+
id: asset.id,
|
|
550
|
+
file
|
|
551
|
+
});
|
|
552
|
+
if ('error' in res) {
|
|
553
|
+
const error = res.error;
|
|
554
|
+
const message = error?.data?.error?.message ?? error?.data?.message ?? formatMessage({
|
|
555
|
+
id: getTranslationKey('asset-details.replace.error'),
|
|
556
|
+
defaultMessage: 'Failed to replace the file.'
|
|
557
|
+
});
|
|
558
|
+
notify({
|
|
559
|
+
type: 'danger',
|
|
560
|
+
message
|
|
561
|
+
});
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
notify({
|
|
565
|
+
type: 'success',
|
|
566
|
+
message: formatMessage({
|
|
567
|
+
id: getTranslationKey('asset-details.replace.success'),
|
|
568
|
+
defaultMessage: 'File replaced.'
|
|
569
|
+
})
|
|
570
|
+
});
|
|
571
|
+
};
|
|
572
|
+
// Owns the delete: on error notify in-drawer (drawer stays), on success fire
|
|
573
|
+
// a persistent global notification then close the drawer.
|
|
574
|
+
const handleDelete = async ()=>{
|
|
575
|
+
const res = await deleteMutation(asset.id);
|
|
576
|
+
if ('error' in res) {
|
|
577
|
+
const error = res.error;
|
|
578
|
+
const message = error?.data?.error?.message ?? error?.data?.message ?? formatMessage({
|
|
579
|
+
id: getTranslationKey('asset-details.delete.error'),
|
|
580
|
+
defaultMessage: 'Failed to delete the asset.'
|
|
581
|
+
});
|
|
582
|
+
notify({
|
|
583
|
+
type: 'danger',
|
|
584
|
+
message
|
|
585
|
+
});
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
toggleNotification({
|
|
589
|
+
type: 'success',
|
|
590
|
+
message: formatMessage({
|
|
591
|
+
id: getTranslationKey('asset-details.delete.success'),
|
|
592
|
+
defaultMessage: '1 element have been deleted from {folderName}'
|
|
593
|
+
}, {
|
|
594
|
+
folderName
|
|
595
|
+
})
|
|
596
|
+
});
|
|
597
|
+
closeDetails();
|
|
598
|
+
};
|
|
599
|
+
const operations = React.useMemo(()=>({
|
|
600
|
+
replaceAsset: handleReplace,
|
|
601
|
+
deleteAsset: handleDelete,
|
|
602
|
+
isReplacing,
|
|
603
|
+
isDeleting
|
|
604
|
+
}), // handleReplace / handleDelete close over asset+mutations and don't need a
|
|
605
|
+
// stable identity here; the consumers re-render with the new context value.
|
|
606
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
607
|
+
[
|
|
608
|
+
isReplacing,
|
|
609
|
+
isDeleting
|
|
610
|
+
]);
|
|
611
|
+
return(// `key={asset.id}` resets the form when the drawer switches to a different
|
|
612
|
+
// asset so cached values from the previous asset don't bleed in.
|
|
613
|
+
/*#__PURE__*/ jsx(DrawerNotifyContext.Provider, {
|
|
614
|
+
value: notify,
|
|
615
|
+
children: /*#__PURE__*/ jsx(AssetOperationsContext.Provider, {
|
|
616
|
+
value: operations,
|
|
617
|
+
children: /*#__PURE__*/ jsx(FormShell, {
|
|
618
|
+
children: /*#__PURE__*/ jsx(Form, {
|
|
619
|
+
method: "POST",
|
|
620
|
+
initialValues: initialValues,
|
|
621
|
+
onSubmit: handleSubmit,
|
|
622
|
+
children: ({ modified, isSubmitting, values, resetForm })=>{
|
|
623
|
+
const nameIsEmpty = (values.name ?? '').trim() === '';
|
|
624
|
+
return /*#__PURE__*/ jsxs(Fragment, {
|
|
625
|
+
children: [
|
|
626
|
+
/*#__PURE__*/ jsx(Blocker, {
|
|
627
|
+
onProceed: resetForm
|
|
628
|
+
}),
|
|
629
|
+
isReplacing || isDeleting ? /*#__PURE__*/ jsx(DrawerBusyOverlay, {
|
|
630
|
+
children: /*#__PURE__*/ jsx(Loader, {
|
|
631
|
+
children: formatMessage({
|
|
632
|
+
id: getTranslationKey(isDeleting ? 'asset-details.delete.loading' : 'asset-details.replace.loading'),
|
|
633
|
+
defaultMessage: isDeleting ? 'Deleting the file…' : 'Replacing the file…'
|
|
634
|
+
})
|
|
635
|
+
})
|
|
636
|
+
}) : null,
|
|
637
|
+
drawerToast ? /*#__PURE__*/ jsx(DrawerToastSlot, {
|
|
638
|
+
children: /*#__PURE__*/ jsx(Alert, {
|
|
639
|
+
variant: drawerToast.type === 'success' ? 'success' : 'danger',
|
|
640
|
+
closeLabel: formatMessage({
|
|
641
|
+
id: 'global.close',
|
|
642
|
+
defaultMessage: 'Close'
|
|
643
|
+
}),
|
|
644
|
+
onClose: ()=>setDrawerToast(null),
|
|
645
|
+
children: drawerToast.message
|
|
646
|
+
})
|
|
647
|
+
}) : null,
|
|
648
|
+
/*#__PURE__*/ jsxs(Drawer.ScrollableContent, {
|
|
649
|
+
children: [
|
|
650
|
+
/*#__PURE__*/ jsx(AssetPreview, {
|
|
651
|
+
asset: asset,
|
|
652
|
+
actions: isImage ? /*#__PURE__*/ jsx(Flex, {
|
|
653
|
+
direction: "column",
|
|
654
|
+
gap: 2,
|
|
655
|
+
children: /*#__PURE__*/ jsx(ReplaceAssetButton, {})
|
|
656
|
+
}) : null
|
|
657
|
+
}),
|
|
658
|
+
/*#__PURE__*/ jsxs(Flex, {
|
|
659
|
+
direction: "column",
|
|
660
|
+
alignItems: "stretch",
|
|
661
|
+
gap: 4,
|
|
662
|
+
paddingTop: 4,
|
|
663
|
+
paddingBottom: 4,
|
|
664
|
+
paddingLeft: 5,
|
|
665
|
+
paddingRight: 5,
|
|
666
|
+
children: [
|
|
667
|
+
/*#__PURE__*/ jsx(Typography, {
|
|
668
|
+
variant: "beta",
|
|
669
|
+
fontWeight: "semiBold",
|
|
670
|
+
tag: "h3",
|
|
671
|
+
children: formatMessage({
|
|
672
|
+
id: getTranslationKey('asset-details.fileInfo'),
|
|
673
|
+
defaultMessage: 'File info'
|
|
674
|
+
})
|
|
675
|
+
}),
|
|
676
|
+
/*#__PURE__*/ jsxs(Flex, {
|
|
677
|
+
wrap: "wrap",
|
|
678
|
+
gap: 4,
|
|
679
|
+
background: "neutral100",
|
|
680
|
+
paddingTop: 4,
|
|
681
|
+
paddingBottom: 4,
|
|
682
|
+
paddingLeft: 6,
|
|
683
|
+
paddingRight: 6,
|
|
684
|
+
alignItems: "flex-start",
|
|
685
|
+
children: [
|
|
686
|
+
/*#__PURE__*/ jsx(DetailItem, {
|
|
687
|
+
label: formatMessage({
|
|
688
|
+
id: getTranslationKey('asset-details.creationDate'),
|
|
689
|
+
defaultMessage: 'Creation date'
|
|
690
|
+
}),
|
|
691
|
+
value: asset.createdAt ? formatDate(new Date(asset.createdAt), {
|
|
692
|
+
dateStyle: 'long',
|
|
693
|
+
timeStyle: 'short'
|
|
694
|
+
}) : null
|
|
695
|
+
}),
|
|
696
|
+
/*#__PURE__*/ jsx(DetailItem, {
|
|
697
|
+
label: formatMessage({
|
|
698
|
+
id: getTranslationKey('asset-details.lastUpdated'),
|
|
699
|
+
defaultMessage: 'Last updated'
|
|
700
|
+
}),
|
|
701
|
+
value: asset.updatedAt ? formatDate(new Date(asset.updatedAt), {
|
|
702
|
+
dateStyle: 'long',
|
|
703
|
+
timeStyle: 'short'
|
|
704
|
+
}) : null
|
|
705
|
+
}),
|
|
706
|
+
/*#__PURE__*/ jsx(DetailItem, {
|
|
707
|
+
label: formatMessage({
|
|
708
|
+
id: getTranslationKey('asset-details.createdBy'),
|
|
709
|
+
defaultMessage: 'Created by'
|
|
710
|
+
}),
|
|
711
|
+
value: asset.createdBy ? getDisplayName({
|
|
712
|
+
firstname: asset.createdBy.firstname ?? undefined,
|
|
713
|
+
lastname: asset.createdBy.lastname ?? undefined,
|
|
714
|
+
username: asset.createdBy.username ?? undefined,
|
|
715
|
+
email: asset.createdBy.email ?? undefined
|
|
716
|
+
}) ?? '-' : null
|
|
717
|
+
}),
|
|
718
|
+
/*#__PURE__*/ jsx(DetailItem, {
|
|
719
|
+
label: formatMessage({
|
|
720
|
+
id: getTranslationKey('asset-details.size'),
|
|
721
|
+
defaultMessage: 'Size'
|
|
722
|
+
}),
|
|
723
|
+
value: asset.size ? formatBytes(asset.size, 1) : null
|
|
724
|
+
}),
|
|
725
|
+
isImage && (asset.width != null || asset.height != null) && /*#__PURE__*/ jsx(DetailItem, {
|
|
726
|
+
label: formatMessage({
|
|
727
|
+
id: getTranslationKey('asset-details.dimensions'),
|
|
728
|
+
defaultMessage: 'Dimensions'
|
|
729
|
+
}),
|
|
730
|
+
value: asset.width != null && asset.height != null ? `${asset.width} × ${asset.height}` : null
|
|
731
|
+
}),
|
|
732
|
+
/*#__PURE__*/ jsx(DetailItem, {
|
|
733
|
+
label: formatMessage({
|
|
734
|
+
id: getTranslationKey('asset-details.extension'),
|
|
735
|
+
defaultMessage: 'Extension'
|
|
736
|
+
}),
|
|
737
|
+
value: getFileExtension(asset.ext)
|
|
738
|
+
}),
|
|
739
|
+
/*#__PURE__*/ jsx(DetailItem, {
|
|
740
|
+
label: formatMessage({
|
|
741
|
+
id: getTranslationKey('asset-details.assetId'),
|
|
742
|
+
defaultMessage: 'Asset ID'
|
|
743
|
+
}),
|
|
744
|
+
value: String(asset.id)
|
|
745
|
+
})
|
|
746
|
+
]
|
|
747
|
+
}),
|
|
748
|
+
/*#__PURE__*/ jsx(DetailField, {
|
|
749
|
+
name: "name",
|
|
750
|
+
label: formatMessage({
|
|
751
|
+
id: getTranslationKey('asset-details.fileName'),
|
|
752
|
+
defaultMessage: 'File name'
|
|
753
|
+
}),
|
|
754
|
+
required: true
|
|
755
|
+
}),
|
|
756
|
+
/*#__PURE__*/ jsx(LocationField, {
|
|
757
|
+
label: formatMessage({
|
|
758
|
+
id: getTranslationKey('asset-details.location'),
|
|
759
|
+
defaultMessage: 'Location'
|
|
760
|
+
}),
|
|
761
|
+
rootLabel: formatMessage({
|
|
762
|
+
id: getTranslationKey('plugin.home'),
|
|
763
|
+
defaultMessage: 'Home'
|
|
764
|
+
}),
|
|
765
|
+
folders: folders
|
|
766
|
+
}),
|
|
767
|
+
isImage && /*#__PURE__*/ jsxs(Fragment, {
|
|
768
|
+
children: [
|
|
769
|
+
/*#__PURE__*/ jsx(DetailField, {
|
|
770
|
+
name: "caption",
|
|
771
|
+
label: formatMessage({
|
|
772
|
+
id: getTranslationKey('asset-details.caption'),
|
|
773
|
+
defaultMessage: 'Caption'
|
|
774
|
+
})
|
|
775
|
+
}),
|
|
776
|
+
/*#__PURE__*/ jsx(DetailField, {
|
|
777
|
+
name: "alternativeText",
|
|
778
|
+
label: formatMessage({
|
|
779
|
+
id: getTranslationKey('asset-details.alternativeText'),
|
|
780
|
+
defaultMessage: 'Alternative text'
|
|
781
|
+
})
|
|
782
|
+
})
|
|
783
|
+
]
|
|
784
|
+
})
|
|
785
|
+
]
|
|
786
|
+
})
|
|
787
|
+
]
|
|
788
|
+
}),
|
|
789
|
+
/*#__PURE__*/ jsxs(Flex, {
|
|
790
|
+
justifyContent: "space-between",
|
|
791
|
+
alignItems: "center",
|
|
792
|
+
gap: 2,
|
|
793
|
+
padding: 3,
|
|
794
|
+
borderColor: "neutral150",
|
|
795
|
+
borderStyle: "solid",
|
|
796
|
+
borderWidth: "1px 0 0 0",
|
|
797
|
+
background: "neutral0",
|
|
798
|
+
children: [
|
|
799
|
+
/*#__PURE__*/ jsxs(Flex, {
|
|
800
|
+
gap: 2,
|
|
801
|
+
children: [
|
|
802
|
+
/*#__PURE__*/ jsx(DeleteAssetButton, {}),
|
|
803
|
+
/*#__PURE__*/ jsx(CopyLinkButton, {
|
|
804
|
+
asset: asset
|
|
805
|
+
}),
|
|
806
|
+
/*#__PURE__*/ jsx(DownloadAssetButton, {
|
|
807
|
+
asset: asset
|
|
808
|
+
})
|
|
809
|
+
]
|
|
810
|
+
}),
|
|
811
|
+
/*#__PURE__*/ jsx(Button, {
|
|
812
|
+
type: "submit",
|
|
813
|
+
variant: "default",
|
|
814
|
+
loading: isSubmitting,
|
|
815
|
+
// File name is required; block submit when it's empty or whitespace so the API can't 400 on a blank value.
|
|
816
|
+
disabled: !modified || isSubmitting || nameIsEmpty,
|
|
817
|
+
children: formatMessage({
|
|
818
|
+
id: getTranslationKey('asset-details.save'),
|
|
819
|
+
defaultMessage: 'Save changes'
|
|
820
|
+
})
|
|
821
|
+
})
|
|
822
|
+
]
|
|
823
|
+
})
|
|
824
|
+
]
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
}, asset.id)
|
|
828
|
+
})
|
|
829
|
+
})
|
|
830
|
+
}));
|
|
831
|
+
};
|
|
248
832
|
const DrawerHeader = ({ asset, closeDetails })=>{
|
|
249
833
|
const DocIcon = asset ? getAssetIcon(asset.mime, asset.ext) : FileError;
|
|
250
834
|
return /*#__PURE__*/ jsxs(Flex, {
|
|
@@ -253,6 +837,9 @@ const DrawerHeader = ({ asset, closeDetails })=>{
|
|
|
253
837
|
paddingTop: 3,
|
|
254
838
|
paddingBottom: 3,
|
|
255
839
|
paddingRight: 3,
|
|
840
|
+
borderColor: "neutral150",
|
|
841
|
+
borderStyle: "solid",
|
|
842
|
+
borderWidth: "0 0 1px 0",
|
|
256
843
|
children: [
|
|
257
844
|
/*#__PURE__*/ jsx(DocIcon, {
|
|
258
845
|
width: 20,
|
|
@@ -324,15 +911,9 @@ const DrawerContent = ({ assetId, closeDetails })=>{
|
|
|
324
911
|
asset: asset,
|
|
325
912
|
closeDetails: closeDetails
|
|
326
913
|
}),
|
|
327
|
-
/*#__PURE__*/
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
asset: asset
|
|
331
|
-
}),
|
|
332
|
-
/*#__PURE__*/ jsx(AssetDetails, {
|
|
333
|
-
asset: asset
|
|
334
|
-
})
|
|
335
|
-
]
|
|
914
|
+
/*#__PURE__*/ jsx(AssetDetails, {
|
|
915
|
+
asset: asset,
|
|
916
|
+
closeDetails: closeDetails
|
|
336
917
|
})
|
|
337
918
|
]
|
|
338
919
|
});
|
|
@@ -341,7 +922,7 @@ const DrawerContent = ({ assetId, closeDetails })=>{
|
|
|
341
922
|
* AssetDetailsDrawer
|
|
342
923
|
* -----------------------------------------------------------------------------------------------*/ const AssetDetailsDrawer = ()=>{
|
|
343
924
|
const { formatMessage } = useIntl();
|
|
344
|
-
const { assetId, isVisible, shouldRenderDrawer, closeDetails } = useAssetDetailsParam();
|
|
925
|
+
const { assetId, isVisible, shouldRenderDrawer, onCloseAnimationEnd, closeDetails } = useAssetDetailsParam();
|
|
345
926
|
if (!shouldRenderDrawer || assetId === null) {
|
|
346
927
|
return null;
|
|
347
928
|
}
|
|
@@ -371,6 +952,7 @@ const DrawerContent = ({ assetId, closeDetails })=>{
|
|
|
371
952
|
animationDirection: "left",
|
|
372
953
|
width: "41.6rem",
|
|
373
954
|
height: "100vh",
|
|
955
|
+
onAnimationEnd: onCloseAnimationEnd,
|
|
374
956
|
children: /*#__PURE__*/ jsx(DrawerContent, {
|
|
375
957
|
assetId: assetId,
|
|
376
958
|
closeDetails: closeDetails
|
|
@@ -380,5 +962,5 @@ const DrawerContent = ({ assetId, closeDetails })=>{
|
|
|
380
962
|
});
|
|
381
963
|
};
|
|
382
964
|
|
|
383
|
-
export { AssetDetailsDrawer, useAssetDetailsParam };
|
|
965
|
+
export { AssetDetails, AssetDetailsDrawer, AssetOperationsContext, DrawerNotifyContext, useAssetDetailsParam };
|
|
384
966
|
//# sourceMappingURL=AssetDetailsDrawer.mjs.map
|