@strapi/upload 5.34.0 → 5.35.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 +32 -3
- package/dist/admin/components/EditAssetDialog/EditAssetContent.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs +32 -3
- package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.js +57 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.js.map +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.mjs +55 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/FocalPointActions.mjs.map +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.js +96 -20
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.mjs +98 -22
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewBox.mjs.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewComponents.js +47 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewComponents.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewComponents.mjs +44 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/PreviewComponents.mjs.map +1 -1
- package/dist/admin/future/App.js +2 -2
- package/dist/admin/future/App.js.map +1 -1
- package/dist/admin/future/App.mjs +2 -2
- package/dist/admin/future/App.mjs.map +1 -1
- package/dist/admin/future/pages/MediaLibraryPage.js +97 -33
- package/dist/admin/future/pages/MediaLibraryPage.js.map +1 -1
- package/dist/admin/future/pages/MediaLibraryPage.mjs +80 -35
- package/dist/admin/future/pages/MediaLibraryPage.mjs.map +1 -1
- package/dist/admin/future/services/api.js +28 -0
- package/dist/admin/future/services/api.js.map +1 -0
- package/dist/admin/future/services/api.mjs +25 -0
- package/dist/admin/future/services/api.mjs.map +1 -0
- package/dist/admin/future/utils/translations.js +8 -0
- package/dist/admin/future/utils/translations.js.map +1 -0
- package/dist/admin/future/utils/translations.mjs +6 -0
- package/dist/admin/future/utils/translations.mjs.map +1 -0
- package/dist/admin/hooks/useEditAsset.js +1 -0
- package/dist/admin/hooks/useEditAsset.js.map +1 -1
- package/dist/admin/hooks/useEditAsset.mjs +1 -0
- package/dist/admin/hooks/useEditAsset.mjs.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/package.json.js +5 -5
- package/dist/admin/package.json.mjs +5 -5
- package/dist/admin/src/components/EditAssetDialog/PreviewBox/AssetPreview.d.ts +1 -2
- package/dist/admin/src/components/EditAssetDialog/PreviewBox/FocalPointActions.d.ts +7 -0
- package/dist/admin/src/components/EditAssetDialog/PreviewBox/PreviewBox.d.ts +6 -2
- package/dist/admin/src/components/EditAssetDialog/PreviewBox/PreviewComponents.d.ts +13 -0
- package/dist/admin/src/future/App.d.ts +1 -1
- package/dist/admin/src/future/services/api.d.ts +5 -1
- package/dist/admin/src/future/utils/translations.d.ts +1 -0
- package/dist/admin/translations/en.json.js +8 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +8 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/content-types/file.js +4 -0
- package/dist/server/content-types/file.js.map +1 -1
- package/dist/server/content-types/file.mjs +4 -0
- package/dist/server/content-types/file.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/upload.js +5 -0
- package/dist/server/controllers/validation/admin/upload.js.map +1 -1
- package/dist/server/controllers/validation/admin/upload.mjs +5 -0
- package/dist/server/controllers/validation/admin/upload.mjs.map +1 -1
- package/dist/server/controllers/validation/content-api/upload.js +6 -1
- package/dist/server/controllers/validation/content-api/upload.js.map +1 -1
- package/dist/server/controllers/validation/content-api/upload.mjs +6 -1
- package/dist/server/controllers/validation/content-api/upload.mjs.map +1 -1
- package/dist/server/services/upload.js +3 -1
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs +3 -1
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/src/content-types/file.d.ts +4 -0
- package/dist/server/src/content-types/file.d.ts.map +1 -1
- package/dist/server/src/content-types/index.d.ts +4 -0
- package/dist/server/src/content-types/index.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/upload.d.ts +240 -0
- package/dist/server/src/controllers/validation/admin/upload.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/content-api/upload.d.ts +180 -0
- package/dist/server/src/controllers/validation/content-api/upload.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +5 -1
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +1 -1
- package/dist/server/src/services/upload.d.ts +1 -1
- package/dist/server/src/services/upload.d.ts.map +1 -1
- package/dist/server/src/types.d.ts +6 -0
- package/dist/server/src/types.d.ts.map +1 -1
- package/dist/shared/contracts/files.d.ts +6 -0
- package/package.json +5 -5
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { FocusTrap, Flex, IconButton } from '@strapi/design-system';
|
|
3
|
+
import { Cross, ArrowsCounterClockwise, Check } from '@strapi/icons';
|
|
4
|
+
import { useIntl } from 'react-intl';
|
|
5
|
+
import 'byte-size';
|
|
6
|
+
import 'date-fns';
|
|
7
|
+
import { getTrad } from '../../../utils/getTrad.mjs';
|
|
8
|
+
import 'qs';
|
|
9
|
+
import '../../../utils/typeFromMime.mjs';
|
|
10
|
+
import '../../../utils/urlYupSchema.mjs';
|
|
11
|
+
import { FocalPointActionRow } from './PreviewComponents.mjs';
|
|
12
|
+
|
|
13
|
+
const FocalPointActions = ({ onCancel, onValidate, onReset })=>{
|
|
14
|
+
const { formatMessage } = useIntl();
|
|
15
|
+
return /*#__PURE__*/ jsx(FocusTrap, {
|
|
16
|
+
onEscape: onCancel,
|
|
17
|
+
children: /*#__PURE__*/ jsx(FocalPointActionRow, {
|
|
18
|
+
justifyContent: "flex-end",
|
|
19
|
+
paddingLeft: 3,
|
|
20
|
+
paddingRight: 3,
|
|
21
|
+
children: /*#__PURE__*/ jsxs(Flex, {
|
|
22
|
+
gap: 1,
|
|
23
|
+
children: [
|
|
24
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
25
|
+
label: formatMessage({
|
|
26
|
+
id: getTrad('control-card.stop-focal-point'),
|
|
27
|
+
defaultMessage: 'Cancel focal point selection'
|
|
28
|
+
}),
|
|
29
|
+
onClick: onCancel,
|
|
30
|
+
children: /*#__PURE__*/ jsx(Cross, {})
|
|
31
|
+
}),
|
|
32
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
33
|
+
label: formatMessage({
|
|
34
|
+
id: getTrad('control-card.reset-focal-point'),
|
|
35
|
+
defaultMessage: 'Reset to center'
|
|
36
|
+
}),
|
|
37
|
+
onClick: onReset,
|
|
38
|
+
children: /*#__PURE__*/ jsx(ArrowsCounterClockwise, {})
|
|
39
|
+
}),
|
|
40
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
41
|
+
label: formatMessage({
|
|
42
|
+
id: getTrad('control-card.save-focal-point'),
|
|
43
|
+
defaultMessage: 'Save focal point'
|
|
44
|
+
}),
|
|
45
|
+
onClick: onValidate,
|
|
46
|
+
children: /*#__PURE__*/ jsx(Check, {})
|
|
47
|
+
})
|
|
48
|
+
]
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export { FocalPointActions };
|
|
55
|
+
//# sourceMappingURL=FocalPointActions.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FocalPointActions.mjs","sources":["../../../../../admin/src/components/EditAssetDialog/PreviewBox/FocalPointActions.tsx"],"sourcesContent":["import { Flex, FocusTrap, IconButton } from '@strapi/design-system';\nimport { Check, Cross, ArrowsCounterClockwise } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { getTrad } from '../../../utils';\n\nimport { FocalPointActionRow } from './PreviewComponents';\n\ninterface FocalPointActionsProps {\n onCancel: () => void;\n onValidate: () => void;\n onReset: () => void;\n}\n\nexport const FocalPointActions = ({ onCancel, onValidate, onReset }: FocalPointActionsProps) => {\n const { formatMessage } = useIntl();\n\n return (\n <FocusTrap onEscape={onCancel}>\n <FocalPointActionRow justifyContent=\"flex-end\" paddingLeft={3} paddingRight={3}>\n <Flex gap={1}>\n <IconButton\n label={formatMessage({\n id: getTrad('control-card.stop-focal-point'),\n defaultMessage: 'Cancel focal point selection',\n })}\n onClick={onCancel}\n >\n <Cross />\n </IconButton>\n\n <IconButton\n label={formatMessage({\n id: getTrad('control-card.reset-focal-point'),\n defaultMessage: 'Reset to center',\n })}\n onClick={onReset}\n >\n <ArrowsCounterClockwise />\n </IconButton>\n\n <IconButton\n label={formatMessage({\n id: getTrad('control-card.save-focal-point'),\n defaultMessage: 'Save focal point',\n })}\n onClick={onValidate}\n >\n <Check />\n </IconButton>\n </Flex>\n </FocalPointActionRow>\n </FocusTrap>\n );\n};\n"],"names":["FocalPointActions","onCancel","onValidate","onReset","formatMessage","useIntl","_jsx","FocusTrap","onEscape","FocalPointActionRow","justifyContent","paddingLeft","paddingRight","_jsxs","Flex","gap","IconButton","label","id","getTrad","defaultMessage","onClick","Cross","ArrowsCounterClockwise","Check"],"mappings":";;;;;;;;;;;;AAcO,MAAMA,oBAAoB,CAAC,EAAEC,QAAQ,EAAEC,UAAU,EAAEC,OAAO,EAA0B,GAAA;IACzF,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;AAE1B,IAAA,qBACEC,GAACC,CAAAA,SAAAA,EAAAA;QAAUC,QAAUP,EAAAA,QAAAA;AACnB,QAAA,QAAA,gBAAAK,GAACG,CAAAA,mBAAAA,EAAAA;YAAoBC,cAAe,EAAA,UAAA;YAAWC,WAAa,EAAA,CAAA;YAAGC,YAAc,EAAA,CAAA;AAC3E,YAAA,QAAA,gBAAAC,IAACC,CAAAA,IAAAA,EAAAA;gBAAKC,GAAK,EAAA,CAAA;;kCACTT,GAACU,CAAAA,UAAAA,EAAAA;AACCC,wBAAAA,KAAAA,EAAOb,aAAc,CAAA;AACnBc,4BAAAA,EAAAA,EAAIC,OAAQ,CAAA,+BAAA,CAAA;4BACZC,cAAgB,EAAA;AAClB,yBAAA,CAAA;wBACAC,OAASpB,EAAAA,QAAAA;AAET,wBAAA,QAAA,gBAAAK,GAACgB,CAAAA,KAAAA,EAAAA,EAAAA;;kCAGHhB,GAACU,CAAAA,UAAAA,EAAAA;AACCC,wBAAAA,KAAAA,EAAOb,aAAc,CAAA;AACnBc,4BAAAA,EAAAA,EAAIC,OAAQ,CAAA,gCAAA,CAAA;4BACZC,cAAgB,EAAA;AAClB,yBAAA,CAAA;wBACAC,OAASlB,EAAAA,OAAAA;AAET,wBAAA,QAAA,gBAAAG,GAACiB,CAAAA,sBAAAA,EAAAA,EAAAA;;kCAGHjB,GAACU,CAAAA,UAAAA,EAAAA;AACCC,wBAAAA,KAAAA,EAAOb,aAAc,CAAA;AACnBc,4BAAAA,EAAAA,EAAIC,OAAQ,CAAA,+BAAA,CAAA;4BACZC,cAAgB,EAAA;AAClB,yBAAA,CAAA;wBACAC,OAASnB,EAAAA,UAAAA;AAET,wBAAA,QAAA,gBAAAI,GAACkB,CAAAA,KAAAA,EAAAA,EAAAA;;;;;;AAMb;;;;"}
|
|
@@ -25,6 +25,7 @@ var UploadProgress = require('../../UploadProgress/UploadProgress.js');
|
|
|
25
25
|
var RemoveAssetDialog = require('../RemoveAssetDialog.js');
|
|
26
26
|
var AssetPreview = require('./AssetPreview.js');
|
|
27
27
|
var CroppingActions = require('./CroppingActions.js');
|
|
28
|
+
var FocalPointActions = require('./FocalPointActions.js');
|
|
28
29
|
var PreviewComponents = require('./PreviewComponents.js');
|
|
29
30
|
|
|
30
31
|
function _interopNamespaceDefault(e) {
|
|
@@ -47,7 +48,7 @@ function _interopNamespaceDefault(e) {
|
|
|
47
48
|
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
48
49
|
|
|
49
50
|
// TODO: find a better naming convention for the file that was an index file before
|
|
50
|
-
const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCropFinish, onCropStart, onCropCancel, replacementFile, trackedLocation })=>{
|
|
51
|
+
const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCropFinish, onCropStart, onCropCancel, replacementFile, trackedLocation, formFocalPoint, onFocalPointStart, onFocalPointFinish, onFocalPointCancel })=>{
|
|
51
52
|
const CropperjsStyle = styledComponents.createGlobalStyle`${cropperjscss}`;
|
|
52
53
|
const { trackUsage } = useTracking.useTracking();
|
|
53
54
|
const previewRef = React__namespace.useRef(null);
|
|
@@ -59,6 +60,11 @@ const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCr
|
|
|
59
60
|
const [showConfirmDialog, setShowConfirmDialog] = React__namespace.useState(false);
|
|
60
61
|
const { crop, produceFile, stopCropping, isCropping, isCropperReady, width, height } = useCropImg.useCropImg();
|
|
61
62
|
const { editAsset, error, isLoading, progress, cancel } = useEditAsset.useEditAsset();
|
|
63
|
+
const [isInFocalPointMode, setIsInFocalPointMode] = React__namespace.useState(false);
|
|
64
|
+
const [focalPoint, setFocalPoint] = React__namespace.useState(formFocalPoint ?? {
|
|
65
|
+
x: 50,
|
|
66
|
+
y: 50
|
|
67
|
+
});
|
|
62
68
|
const { upload, isLoading: isLoadingUpload, cancel: cancelUpload, error: uploadError, progress: progressUpload } = useUpload.useUpload();
|
|
63
69
|
React__namespace.useEffect(()=>{
|
|
64
70
|
// Whenever a replacementUrl is set, make sure to permutate the real asset.url by
|
|
@@ -156,6 +162,42 @@ const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCr
|
|
|
156
162
|
const handleCropStart = ()=>{
|
|
157
163
|
setHasCropIntent(true);
|
|
158
164
|
};
|
|
165
|
+
const calculateFocalPointFromEvent = (e)=>{
|
|
166
|
+
const { clientX, clientY } = e;
|
|
167
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
168
|
+
const posX = clientX - rect.left;
|
|
169
|
+
const posY = clientY - rect.top;
|
|
170
|
+
return {
|
|
171
|
+
x: Number((posX / rect.width * 100).toFixed(2)),
|
|
172
|
+
y: Number((posY / rect.height * 100).toFixed(2))
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
const handleFocalPointClick = (e)=>{
|
|
176
|
+
if (!isInFocalPointMode) return;
|
|
177
|
+
setFocalPoint(calculateFocalPointFromEvent(e));
|
|
178
|
+
};
|
|
179
|
+
const handleFocalPointCancel = ()=>{
|
|
180
|
+
setIsInFocalPointMode(false);
|
|
181
|
+
setFocalPoint(formFocalPoint ?? {
|
|
182
|
+
x: 50,
|
|
183
|
+
y: 50
|
|
184
|
+
});
|
|
185
|
+
onFocalPointCancel();
|
|
186
|
+
};
|
|
187
|
+
const handleFocalPointStart = ()=>{
|
|
188
|
+
onFocalPointStart();
|
|
189
|
+
setIsInFocalPointMode(true);
|
|
190
|
+
};
|
|
191
|
+
const handleFocalPointValidate = ()=>{
|
|
192
|
+
setIsInFocalPointMode(false);
|
|
193
|
+
onFocalPointFinish(focalPoint);
|
|
194
|
+
};
|
|
195
|
+
const handleFocalPointReset = ()=>{
|
|
196
|
+
setFocalPoint({
|
|
197
|
+
x: 50,
|
|
198
|
+
y: 50
|
|
199
|
+
});
|
|
200
|
+
};
|
|
159
201
|
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
160
202
|
children: [
|
|
161
203
|
/*#__PURE__*/ jsxRuntime.jsx(CropperjsStyle, {}),
|
|
@@ -169,6 +211,11 @@ const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCr
|
|
|
169
211
|
onDuplicate: asset.isLocal ? undefined : handleDuplication,
|
|
170
212
|
onCancel: handleCropCancel
|
|
171
213
|
}),
|
|
214
|
+
isInFocalPointMode && /*#__PURE__*/ jsxRuntime.jsx(FocalPointActions.FocalPointActions, {
|
|
215
|
+
onValidate: handleFocalPointValidate,
|
|
216
|
+
onCancel: handleFocalPointCancel,
|
|
217
|
+
onReset: handleFocalPointReset
|
|
218
|
+
}),
|
|
172
219
|
/*#__PURE__*/ jsxRuntime.jsx(PreviewComponents.ActionRow, {
|
|
173
220
|
paddingLeft: 3,
|
|
174
221
|
paddingRight: 3,
|
|
@@ -202,6 +249,14 @@ const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCr
|
|
|
202
249
|
}),
|
|
203
250
|
onClick: handleCropStart,
|
|
204
251
|
children: /*#__PURE__*/ jsxRuntime.jsx(icons.Crop, {})
|
|
252
|
+
}),
|
|
253
|
+
canUpdate && asset.mime?.includes(enums.AssetType.Image) && /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
|
|
254
|
+
label: formatMessage({
|
|
255
|
+
id: getTrad.getTrad('control-card.set-focal-point'),
|
|
256
|
+
defaultMessage: 'Set focal point'
|
|
257
|
+
}),
|
|
258
|
+
onClick: handleFocalPointStart,
|
|
259
|
+
children: /*#__PURE__*/ jsxRuntime.jsx(icons.PinMap, {})
|
|
205
260
|
})
|
|
206
261
|
]
|
|
207
262
|
})
|
|
@@ -222,37 +277,58 @@ const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCr
|
|
|
222
277
|
progress: progressUpload
|
|
223
278
|
})
|
|
224
279
|
}),
|
|
225
|
-
/*#__PURE__*/ jsxRuntime.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
280
|
+
/*#__PURE__*/ jsxRuntime.jsxs(PreviewComponents.FocalPointImageWrapper, {
|
|
281
|
+
children: [
|
|
282
|
+
/*#__PURE__*/ jsxRuntime.jsx(AssetPreview.AssetPreview, {
|
|
283
|
+
ref: previewRef,
|
|
284
|
+
mime: asset.mime,
|
|
285
|
+
name: asset.name,
|
|
286
|
+
url: hasCropIntent ? assetUrl : thumbnailUrl,
|
|
287
|
+
onLoad: ()=>{
|
|
288
|
+
if (asset.isLocal || hasCropIntent) {
|
|
289
|
+
setIsCropImageReady(true);
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
onClick: handleFocalPointClick,
|
|
293
|
+
style: {
|
|
294
|
+
cursor: isInFocalPointMode ? 'crosshair' : undefined
|
|
295
|
+
}
|
|
296
|
+
}),
|
|
297
|
+
isInFocalPointMode && /*#__PURE__*/ jsxRuntime.jsx(PreviewComponents.FocalPointAim, {
|
|
298
|
+
$focalPoint: focalPoint,
|
|
299
|
+
children: /*#__PURE__*/ jsxRuntime.jsx(PreviewComponents.FocalPointHalo, {})
|
|
300
|
+
})
|
|
301
|
+
]
|
|
235
302
|
})
|
|
236
303
|
]
|
|
237
304
|
}),
|
|
238
|
-
/*#__PURE__*/ jsxRuntime.
|
|
305
|
+
/*#__PURE__*/ jsxRuntime.jsxs(PreviewComponents.ActionRow, {
|
|
239
306
|
paddingLeft: 2,
|
|
240
307
|
paddingRight: 2,
|
|
241
308
|
justifyContent: "flex-end",
|
|
242
|
-
$blurry: isInCroppingMode,
|
|
243
|
-
children:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
309
|
+
$blurry: isInCroppingMode || isInFocalPointMode,
|
|
310
|
+
children: [
|
|
311
|
+
isInCroppingMode && width && height && /*#__PURE__*/ jsxRuntime.jsx(PreviewComponents.BadgeOverride, {
|
|
312
|
+
background: "neutral900",
|
|
313
|
+
color: "neutral0",
|
|
314
|
+
children: width && height ? `${height}✕${width}` : 'N/A'
|
|
315
|
+
}),
|
|
316
|
+
isInFocalPointMode && /*#__PURE__*/ jsxRuntime.jsx(PreviewComponents.BadgeOverride, {
|
|
317
|
+
background: "neutral900",
|
|
318
|
+
color: "neutral0",
|
|
319
|
+
children: `x: ${focalPoint.x}% | y: ${focalPoint.y}%`
|
|
320
|
+
})
|
|
321
|
+
]
|
|
248
322
|
})
|
|
249
323
|
]
|
|
250
324
|
}),
|
|
251
325
|
/*#__PURE__*/ jsxRuntime.jsx(RemoveAssetDialog.RemoveAssetDialog, {
|
|
252
326
|
open: showConfirmDialog,
|
|
253
|
-
onClose: ()=>{
|
|
327
|
+
onClose: (value)=>{
|
|
254
328
|
setShowConfirmDialog(false);
|
|
255
|
-
|
|
329
|
+
if (value === null) {
|
|
330
|
+
onDelete(null);
|
|
331
|
+
}
|
|
256
332
|
},
|
|
257
333
|
asset: asset
|
|
258
334
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PreviewBox.js","sources":["../../../../../admin/src/components/EditAssetDialog/PreviewBox/PreviewBox.tsx"],"sourcesContent":["// TODO: find a better naming convention for the file that was an index file before\nimport * as React from 'react';\n\nimport { Flex, IconButton } from '@strapi/design-system';\nimport { Crop as Resize, Download as DownloadIcon, Trash } from '@strapi/icons';\nimport cropperjscss from 'cropperjs/dist/cropper.css?raw';\nimport { useIntl } from 'react-intl';\nimport { createGlobalStyle } from 'styled-components';\n\nimport { AssetType } from '../../../enums';\nimport { useCropImg } from '../../../hooks/useCropImg';\nimport { useEditAsset } from '../../../hooks/useEditAsset';\nimport { useTracking } from '../../../hooks/useTracking';\nimport { useUpload } from '../../../hooks/useUpload';\nimport { createAssetUrl, getTrad, downloadFile } from '../../../utils';\nimport { CopyLinkButton } from '../../CopyLinkButton/CopyLinkButton';\nimport { UploadProgress } from '../../UploadProgress/UploadProgress';\nimport { RemoveAssetDialog } from '../RemoveAssetDialog';\n\nimport { AssetPreview } from './AssetPreview';\nimport { CroppingActions } from './CroppingActions';\nimport {\n ActionRow,\n BadgeOverride,\n RelativeBox,\n UploadProgressWrapper,\n Wrapper,\n} from './PreviewComponents';\n\nimport type { File as FileDefinition, RawFile } from '../../../../../shared/contracts/files';\n\ninterface Asset extends Omit<FileDefinition, 'folder'> {\n isLocal?: boolean;\n rawFile?: RawFile;\n folder?: FileDefinition['folder'] & { id: number };\n}\n\ninterface PreviewBoxProps {\n asset: Asset;\n canUpdate: boolean;\n canCopyLink: boolean;\n canDownload: boolean;\n replacementFile?: File;\n onDelete: (asset?: Asset | null) => void;\n onCropFinish: () => void;\n onCropStart: () => void;\n onCropCancel: () => void;\n trackedLocation?: string;\n}\n\nexport const PreviewBox = ({\n asset,\n canUpdate,\n canCopyLink,\n canDownload,\n onDelete,\n onCropFinish,\n onCropStart,\n onCropCancel,\n replacementFile,\n trackedLocation,\n}: PreviewBoxProps) => {\n const CropperjsStyle = createGlobalStyle`${cropperjscss}`;\n const { trackUsage } = useTracking();\n const previewRef = React.useRef(null);\n const [isCropImageReady, setIsCropImageReady] = React.useState(false);\n const [hasCropIntent, setHasCropIntent] = React.useState<boolean | null>(null);\n const [assetUrl, setAssetUrl] = React.useState(createAssetUrl(asset, false));\n const [thumbnailUrl, setThumbnailUrl] = React.useState(createAssetUrl(asset, true));\n const { formatMessage } = useIntl();\n const [showConfirmDialog, setShowConfirmDialog] = React.useState(false);\n const { crop, produceFile, stopCropping, isCropping, isCropperReady, width, height } =\n useCropImg();\n const { editAsset, error, isLoading, progress, cancel } = useEditAsset();\n\n const {\n upload,\n isLoading: isLoadingUpload,\n cancel: cancelUpload,\n error: uploadError,\n progress: progressUpload,\n } = useUpload();\n\n React.useEffect(() => {\n // Whenever a replacementUrl is set, make sure to permutate the real asset.url by\n // the locally generated one\n if (replacementFile) {\n const fileLocalUrl = URL.createObjectURL(replacementFile);\n\n if (asset.isLocal) {\n asset.url = fileLocalUrl;\n }\n\n setAssetUrl(fileLocalUrl);\n setThumbnailUrl(fileLocalUrl);\n }\n }, [replacementFile, asset]);\n\n React.useEffect(() => {\n if (hasCropIntent === false) {\n stopCropping();\n onCropCancel();\n }\n }, [hasCropIntent, stopCropping, onCropCancel, onCropFinish]);\n\n React.useEffect(() => {\n if (hasCropIntent && isCropImageReady) {\n crop(previewRef.current!);\n onCropStart();\n }\n }, [isCropImageReady, hasCropIntent, onCropStart, crop]);\n\n const handleCropping = async () => {\n const nextAsset = { ...asset, width, height, folder: asset.folder?.id };\n const file = (await produceFile(nextAsset.name, nextAsset.mime!, nextAsset.updatedAt!)) as File;\n\n // Making sure that when persisting the new asset, the URL changes with width and height\n // So that the browser makes a request and handle the image caching correctly at the good size\n let optimizedCachingImage;\n let optimizedCachingThumbnailImage;\n\n if (asset.isLocal) {\n optimizedCachingImage = URL.createObjectURL(file);\n optimizedCachingThumbnailImage = optimizedCachingImage;\n asset.url = optimizedCachingImage;\n asset.rawFile = file;\n\n trackUsage('didCropFile', { duplicatedFile: null, location: trackedLocation! });\n } else {\n const updatedAsset = await editAsset(nextAsset, file);\n optimizedCachingImage = createAssetUrl(updatedAsset, false);\n optimizedCachingThumbnailImage = createAssetUrl(updatedAsset, true);\n\n trackUsage('didCropFile', { duplicatedFile: false, location: trackedLocation! });\n }\n\n setAssetUrl(optimizedCachingImage);\n setThumbnailUrl(optimizedCachingThumbnailImage);\n setHasCropIntent(false);\n };\n\n const isInCroppingMode = isCropping && !isLoading;\n\n const handleDuplication = async () => {\n const nextAsset = { ...asset, width, height };\n const file = (await produceFile(\n nextAsset.name,\n nextAsset.mime!,\n nextAsset.updatedAt!\n )) as RawFile;\n\n await upload({ name: file.name, rawFile: file }, asset.folder?.id ? asset.folder.id : null);\n\n trackUsage('didCropFile', { duplicatedFile: true, location: trackedLocation! });\n\n setHasCropIntent(false);\n onCropFinish();\n };\n\n const handleCropCancel = () => {\n setHasCropIntent(false);\n };\n\n const handleCropStart = () => {\n setHasCropIntent(true);\n };\n\n return (\n <>\n <CropperjsStyle />\n <RelativeBox hasRadius background=\"neutral150\" borderColor=\"neutral200\">\n {isCropperReady && isInCroppingMode && (\n <CroppingActions\n onValidate={handleCropping}\n onDuplicate={asset.isLocal ? undefined : handleDuplication}\n onCancel={handleCropCancel}\n />\n )}\n\n <ActionRow paddingLeft={3} paddingRight={3} justifyContent=\"flex-end\">\n <Flex gap={1}>\n {canUpdate && !asset.isLocal && (\n <IconButton\n label={formatMessage({\n id: 'global.delete',\n defaultMessage: 'Delete',\n })}\n onClick={() => setShowConfirmDialog(true)}\n >\n <Trash />\n </IconButton>\n )}\n\n {canDownload && (\n <IconButton\n label={formatMessage({\n id: getTrad('control-card.download'),\n defaultMessage: 'Download',\n })}\n onClick={() => downloadFile(assetUrl!, asset.name)}\n >\n <DownloadIcon />\n </IconButton>\n )}\n\n {canCopyLink && <CopyLinkButton url={assetUrl!} />}\n\n {canUpdate && asset.mime?.includes(AssetType.Image) && (\n <IconButton\n label={formatMessage({ id: getTrad('control-card.crop'), defaultMessage: 'Crop' })}\n onClick={handleCropStart}\n >\n <Resize />\n </IconButton>\n )}\n </Flex>\n </ActionRow>\n\n <Wrapper>\n {/* This one is for editting an asset */}\n {isLoading && (\n <UploadProgressWrapper>\n <UploadProgress error={error} onCancel={cancel} progress={progress} />\n </UploadProgressWrapper>\n )}\n\n {/* This one is for duplicating an asset after cropping */}\n {isLoadingUpload && (\n <UploadProgressWrapper>\n <UploadProgress\n error={uploadError}\n onCancel={cancelUpload}\n progress={progressUpload}\n />\n </UploadProgressWrapper>\n )}\n\n <AssetPreview\n ref={previewRef}\n mime={asset.mime!}\n name={asset.name}\n url={hasCropIntent ? assetUrl! : thumbnailUrl!}\n onLoad={() => {\n if (asset.isLocal || hasCropIntent) {\n setIsCropImageReady(true);\n }\n }}\n />\n </Wrapper>\n\n <ActionRow\n paddingLeft={2}\n paddingRight={2}\n justifyContent=\"flex-end\"\n $blurry={isInCroppingMode}\n >\n {isInCroppingMode && width && height && (\n <BadgeOverride background=\"neutral900\" color=\"neutral0\">\n {width && height ? `${height}✕${width}` : 'N/A'}\n </BadgeOverride>\n )}\n </ActionRow>\n </RelativeBox>\n\n <RemoveAssetDialog\n open={showConfirmDialog}\n onClose={() => {\n setShowConfirmDialog(false);\n onDelete(null);\n }}\n asset={asset}\n />\n </>\n );\n};\n"],"names":["PreviewBox","asset","canUpdate","canCopyLink","canDownload","onDelete","onCropFinish","onCropStart","onCropCancel","replacementFile","trackedLocation","CropperjsStyle","createGlobalStyle","cropperjscss","trackUsage","useTracking","previewRef","React","useRef","isCropImageReady","setIsCropImageReady","useState","hasCropIntent","setHasCropIntent","assetUrl","setAssetUrl","createAssetUrl","thumbnailUrl","setThumbnailUrl","formatMessage","useIntl","showConfirmDialog","setShowConfirmDialog","crop","produceFile","stopCropping","isCropping","isCropperReady","width","height","useCropImg","editAsset","error","isLoading","progress","cancel","useEditAsset","upload","isLoadingUpload","cancelUpload","uploadError","progressUpload","useUpload","useEffect","fileLocalUrl","URL","createObjectURL","isLocal","url","current","handleCropping","nextAsset","folder","id","file","name","mime","updatedAt","optimizedCachingImage","optimizedCachingThumbnailImage","rawFile","duplicatedFile","location","updatedAsset","isInCroppingMode","handleDuplication","handleCropCancel","handleCropStart","_jsxs","_Fragment","_jsx","RelativeBox","hasRadius","background","borderColor","CroppingActions","onValidate","onDuplicate","undefined","onCancel","ActionRow","paddingLeft","paddingRight","justifyContent","Flex","gap","IconButton","label","defaultMessage","onClick","Trash","getTrad","downloadFile","DownloadIcon","CopyLinkButton","includes","AssetType","Image","Resize","Wrapper","UploadProgressWrapper","UploadProgress","AssetPreview","ref","onLoad","$blurry","BadgeOverride","color","RemoveAssetDialog","open","onClose"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PreviewBox.js","sources":["../../../../../admin/src/components/EditAssetDialog/PreviewBox/PreviewBox.tsx"],"sourcesContent":["// TODO: find a better naming convention for the file that was an index file before\nimport * as React from 'react';\n\nimport { Flex, IconButton } from '@strapi/design-system';\nimport { Crop as Resize, Download as DownloadIcon, Trash, PinMap } from '@strapi/icons';\nimport cropperjscss from 'cropperjs/dist/cropper.css?raw';\nimport { useIntl } from 'react-intl';\nimport { createGlobalStyle } from 'styled-components';\n\nimport { AssetType } from '../../../enums';\nimport { useCropImg } from '../../../hooks/useCropImg';\nimport { useEditAsset } from '../../../hooks/useEditAsset';\nimport { useTracking } from '../../../hooks/useTracking';\nimport { useUpload } from '../../../hooks/useUpload';\nimport { createAssetUrl, getTrad, downloadFile } from '../../../utils';\nimport { CopyLinkButton } from '../../CopyLinkButton/CopyLinkButton';\nimport { UploadProgress } from '../../UploadProgress/UploadProgress';\nimport { RemoveAssetDialog } from '../RemoveAssetDialog';\n\nimport { AssetPreview } from './AssetPreview';\nimport { CroppingActions } from './CroppingActions';\nimport { FocalPointActions } from './FocalPointActions';\nimport {\n ActionRow,\n BadgeOverride,\n RelativeBox,\n UploadProgressWrapper,\n Wrapper,\n FocalPointImageWrapper,\n FocalPointAim,\n FocalPointHalo,\n} from './PreviewComponents';\n\nimport type {\n File as FileDefinition,\n RawFile,\n FocalPoint,\n} from '../../../../../shared/contracts/files';\n\ninterface Asset extends Omit<FileDefinition, 'folder'> {\n isLocal?: boolean;\n rawFile?: RawFile;\n folder?: FileDefinition['folder'] & { id: number };\n}\n\ninterface PreviewBoxProps {\n asset: Asset;\n canUpdate: boolean;\n canCopyLink: boolean;\n canDownload: boolean;\n replacementFile?: File;\n onDelete: (asset?: Asset | null) => void;\n onCropFinish: () => void;\n onCropStart: () => void;\n onCropCancel: () => void;\n trackedLocation?: string;\n formFocalPoint?: FocalPoint | null;\n onFocalPointStart: () => void;\n onFocalPointFinish: (focalPoint: FocalPoint) => void;\n onFocalPointCancel: () => void;\n}\n\nexport const PreviewBox = ({\n asset,\n canUpdate,\n canCopyLink,\n canDownload,\n onDelete,\n onCropFinish,\n onCropStart,\n onCropCancel,\n replacementFile,\n trackedLocation,\n formFocalPoint,\n onFocalPointStart,\n onFocalPointFinish,\n onFocalPointCancel,\n}: PreviewBoxProps) => {\n const CropperjsStyle = createGlobalStyle`${cropperjscss}`;\n const { trackUsage } = useTracking();\n const previewRef = React.useRef(null);\n const [isCropImageReady, setIsCropImageReady] = React.useState(false);\n const [hasCropIntent, setHasCropIntent] = React.useState<boolean | null>(null);\n const [assetUrl, setAssetUrl] = React.useState(createAssetUrl(asset, false));\n const [thumbnailUrl, setThumbnailUrl] = React.useState(createAssetUrl(asset, true));\n const { formatMessage } = useIntl();\n const [showConfirmDialog, setShowConfirmDialog] = React.useState(false);\n const { crop, produceFile, stopCropping, isCropping, isCropperReady, width, height } =\n useCropImg();\n const { editAsset, error, isLoading, progress, cancel } = useEditAsset();\n const [isInFocalPointMode, setIsInFocalPointMode] = React.useState<boolean>(false);\n const [focalPoint, setFocalPoint] = React.useState<FocalPoint>(\n formFocalPoint ?? { x: 50, y: 50 }\n );\n\n const {\n upload,\n isLoading: isLoadingUpload,\n cancel: cancelUpload,\n error: uploadError,\n progress: progressUpload,\n } = useUpload();\n\n React.useEffect(() => {\n // Whenever a replacementUrl is set, make sure to permutate the real asset.url by\n // the locally generated one\n if (replacementFile) {\n const fileLocalUrl = URL.createObjectURL(replacementFile);\n\n if (asset.isLocal) {\n asset.url = fileLocalUrl;\n }\n\n setAssetUrl(fileLocalUrl);\n setThumbnailUrl(fileLocalUrl);\n }\n }, [replacementFile, asset]);\n\n React.useEffect(() => {\n if (hasCropIntent === false) {\n stopCropping();\n onCropCancel();\n }\n }, [hasCropIntent, stopCropping, onCropCancel, onCropFinish]);\n\n React.useEffect(() => {\n if (hasCropIntent && isCropImageReady) {\n crop(previewRef.current!);\n onCropStart();\n }\n }, [isCropImageReady, hasCropIntent, onCropStart, crop]);\n\n const handleCropping = async () => {\n const nextAsset = { ...asset, width, height, folder: asset.folder?.id };\n const file = (await produceFile(nextAsset.name, nextAsset.mime!, nextAsset.updatedAt!)) as File;\n\n // Making sure that when persisting the new asset, the URL changes with width and height\n // So that the browser makes a request and handle the image caching correctly at the good size\n let optimizedCachingImage;\n let optimizedCachingThumbnailImage;\n\n if (asset.isLocal) {\n optimizedCachingImage = URL.createObjectURL(file);\n optimizedCachingThumbnailImage = optimizedCachingImage;\n asset.url = optimizedCachingImage;\n asset.rawFile = file;\n\n trackUsage('didCropFile', { duplicatedFile: null, location: trackedLocation! });\n } else {\n const updatedAsset = await editAsset(nextAsset, file);\n optimizedCachingImage = createAssetUrl(updatedAsset, false);\n optimizedCachingThumbnailImage = createAssetUrl(updatedAsset, true);\n\n trackUsage('didCropFile', { duplicatedFile: false, location: trackedLocation! });\n }\n\n setAssetUrl(optimizedCachingImage);\n setThumbnailUrl(optimizedCachingThumbnailImage);\n setHasCropIntent(false);\n };\n\n const isInCroppingMode = isCropping && !isLoading;\n\n const handleDuplication = async () => {\n const nextAsset = { ...asset, width, height };\n const file = (await produceFile(\n nextAsset.name,\n nextAsset.mime!,\n nextAsset.updatedAt!\n )) as RawFile;\n\n await upload({ name: file.name, rawFile: file }, asset.folder?.id ? asset.folder.id : null);\n\n trackUsage('didCropFile', { duplicatedFile: true, location: trackedLocation! });\n\n setHasCropIntent(false);\n onCropFinish();\n };\n\n const handleCropCancel = () => {\n setHasCropIntent(false);\n };\n\n const handleCropStart = () => {\n setHasCropIntent(true);\n };\n\n const calculateFocalPointFromEvent = (e: React.MouseEvent<HTMLElement>): FocalPoint => {\n const { clientX, clientY } = e;\n const rect = e.currentTarget.getBoundingClientRect();\n const posX = clientX - rect.left;\n const posY = clientY - rect.top;\n\n return {\n x: Number(((posX / rect.width) * 100).toFixed(2)),\n y: Number(((posY / rect.height) * 100).toFixed(2)),\n };\n };\n\n const handleFocalPointClick = (e: React.MouseEvent<HTMLElement>) => {\n if (!isInFocalPointMode) return;\n setFocalPoint(calculateFocalPointFromEvent(e));\n };\n\n const handleFocalPointCancel = () => {\n setIsInFocalPointMode(false);\n setFocalPoint(formFocalPoint ?? { x: 50, y: 50 });\n onFocalPointCancel();\n };\n\n const handleFocalPointStart = () => {\n onFocalPointStart();\n setIsInFocalPointMode(true);\n };\n\n const handleFocalPointValidate = () => {\n setIsInFocalPointMode(false);\n onFocalPointFinish(focalPoint);\n };\n\n const handleFocalPointReset = () => {\n setFocalPoint({ x: 50, y: 50 });\n };\n\n return (\n <>\n <CropperjsStyle />\n <RelativeBox hasRadius background=\"neutral150\" borderColor=\"neutral200\">\n {isCropperReady && isInCroppingMode && (\n <CroppingActions\n onValidate={handleCropping}\n onDuplicate={asset.isLocal ? undefined : handleDuplication}\n onCancel={handleCropCancel}\n />\n )}\n\n {isInFocalPointMode && (\n <FocalPointActions\n onValidate={handleFocalPointValidate}\n onCancel={handleFocalPointCancel}\n onReset={handleFocalPointReset}\n />\n )}\n\n <ActionRow paddingLeft={3} paddingRight={3} justifyContent=\"flex-end\">\n <Flex gap={1}>\n {canUpdate && !asset.isLocal && (\n <IconButton\n label={formatMessage({\n id: 'global.delete',\n defaultMessage: 'Delete',\n })}\n onClick={() => setShowConfirmDialog(true)}\n >\n <Trash />\n </IconButton>\n )}\n\n {canDownload && (\n <IconButton\n label={formatMessage({\n id: getTrad('control-card.download'),\n defaultMessage: 'Download',\n })}\n onClick={() => downloadFile(assetUrl!, asset.name)}\n >\n <DownloadIcon />\n </IconButton>\n )}\n\n {canCopyLink && <CopyLinkButton url={assetUrl!} />}\n\n {canUpdate && asset.mime?.includes(AssetType.Image) && (\n <IconButton\n label={formatMessage({ id: getTrad('control-card.crop'), defaultMessage: 'Crop' })}\n onClick={handleCropStart}\n >\n <Resize />\n </IconButton>\n )}\n\n {canUpdate && asset.mime?.includes(AssetType.Image) && (\n <IconButton\n label={formatMessage({\n id: getTrad('control-card.set-focal-point'),\n defaultMessage: 'Set focal point',\n })}\n onClick={handleFocalPointStart}\n >\n <PinMap />\n </IconButton>\n )}\n </Flex>\n </ActionRow>\n\n <Wrapper>\n {/* This one is for editting an asset */}\n {isLoading && (\n <UploadProgressWrapper>\n <UploadProgress error={error} onCancel={cancel} progress={progress} />\n </UploadProgressWrapper>\n )}\n\n {/* This one is for duplicating an asset after cropping */}\n {isLoadingUpload && (\n <UploadProgressWrapper>\n <UploadProgress\n error={uploadError}\n onCancel={cancelUpload}\n progress={progressUpload}\n />\n </UploadProgressWrapper>\n )}\n\n <FocalPointImageWrapper>\n <AssetPreview\n ref={previewRef}\n mime={asset.mime!}\n name={asset.name}\n url={hasCropIntent ? assetUrl! : thumbnailUrl!}\n onLoad={() => {\n if (asset.isLocal || hasCropIntent) {\n setIsCropImageReady(true);\n }\n }}\n onClick={handleFocalPointClick}\n style={{ cursor: isInFocalPointMode ? 'crosshair' : undefined }}\n />\n\n {/* Show the set focal point marker */}\n {isInFocalPointMode && (\n <FocalPointAim $focalPoint={focalPoint}>\n <FocalPointHalo />\n </FocalPointAim>\n )}\n </FocalPointImageWrapper>\n </Wrapper>\n\n <ActionRow\n paddingLeft={2}\n paddingRight={2}\n justifyContent=\"flex-end\"\n $blurry={isInCroppingMode || isInFocalPointMode}\n >\n {isInCroppingMode && width && height && (\n <BadgeOverride background=\"neutral900\" color=\"neutral0\">\n {width && height ? `${height}✕${width}` : 'N/A'}\n </BadgeOverride>\n )}\n {isInFocalPointMode && (\n <BadgeOverride background=\"neutral900\" color=\"neutral0\">\n {`x: ${focalPoint.x}% | y: ${focalPoint.y}%`}\n </BadgeOverride>\n )}\n </ActionRow>\n </RelativeBox>\n\n <RemoveAssetDialog\n open={showConfirmDialog}\n onClose={(value) => {\n setShowConfirmDialog(false);\n if (value === null) {\n onDelete(null);\n }\n }}\n asset={asset}\n />\n </>\n );\n};\n"],"names":["PreviewBox","asset","canUpdate","canCopyLink","canDownload","onDelete","onCropFinish","onCropStart","onCropCancel","replacementFile","trackedLocation","formFocalPoint","onFocalPointStart","onFocalPointFinish","onFocalPointCancel","CropperjsStyle","createGlobalStyle","cropperjscss","trackUsage","useTracking","previewRef","React","useRef","isCropImageReady","setIsCropImageReady","useState","hasCropIntent","setHasCropIntent","assetUrl","setAssetUrl","createAssetUrl","thumbnailUrl","setThumbnailUrl","formatMessage","useIntl","showConfirmDialog","setShowConfirmDialog","crop","produceFile","stopCropping","isCropping","isCropperReady","width","height","useCropImg","editAsset","error","isLoading","progress","cancel","useEditAsset","isInFocalPointMode","setIsInFocalPointMode","focalPoint","setFocalPoint","x","y","upload","isLoadingUpload","cancelUpload","uploadError","progressUpload","useUpload","useEffect","fileLocalUrl","URL","createObjectURL","isLocal","url","current","handleCropping","nextAsset","folder","id","file","name","mime","updatedAt","optimizedCachingImage","optimizedCachingThumbnailImage","rawFile","duplicatedFile","location","updatedAsset","isInCroppingMode","handleDuplication","handleCropCancel","handleCropStart","calculateFocalPointFromEvent","e","clientX","clientY","rect","currentTarget","getBoundingClientRect","posX","left","posY","top","Number","toFixed","handleFocalPointClick","handleFocalPointCancel","handleFocalPointStart","handleFocalPointValidate","handleFocalPointReset","_jsxs","_Fragment","_jsx","RelativeBox","hasRadius","background","borderColor","CroppingActions","onValidate","onDuplicate","undefined","onCancel","FocalPointActions","onReset","ActionRow","paddingLeft","paddingRight","justifyContent","Flex","gap","IconButton","label","defaultMessage","onClick","Trash","getTrad","downloadFile","DownloadIcon","CopyLinkButton","includes","AssetType","Image","Resize","PinMap","Wrapper","UploadProgressWrapper","UploadProgress","FocalPointImageWrapper","AssetPreview","ref","onLoad","style","cursor","FocalPointAim","$focalPoint","FocalPointHalo","$blurry","BadgeOverride","color","RemoveAssetDialog","open","onClose","value"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AA8DaA,MAAAA,UAAAA,GAAa,CAAC,EACzBC,KAAK,EACLC,SAAS,EACTC,WAAW,EACXC,WAAW,EACXC,QAAQ,EACRC,YAAY,EACZC,WAAW,EACXC,YAAY,EACZC,eAAe,EACfC,eAAe,EACfC,cAAc,EACdC,iBAAiB,EACjBC,kBAAkB,EAClBC,kBAAkB,EACF,GAAA;AAChB,IAAA,MAAMC,cAAiBC,GAAAA,kCAAiB,CAAC,EAAEC,aAAa,CAAC;IACzD,MAAM,EAAEC,UAAU,EAAE,GAAGC,uBAAAA,EAAAA;IACvB,MAAMC,UAAAA,GAAaC,gBAAMC,CAAAA,MAAM,CAAC,IAAA,CAAA;AAChC,IAAA,MAAM,CAACC,gBAAkBC,EAAAA,mBAAAA,CAAoB,GAAGH,gBAAAA,CAAMI,QAAQ,CAAC,KAAA,CAAA;AAC/D,IAAA,MAAM,CAACC,aAAeC,EAAAA,gBAAAA,CAAiB,GAAGN,gBAAAA,CAAMI,QAAQ,CAAiB,IAAA,CAAA;IACzE,MAAM,CAACG,UAAUC,WAAY,CAAA,GAAGR,iBAAMI,QAAQ,CAACK,8BAAe7B,KAAO,EAAA,KAAA,CAAA,CAAA;IACrE,MAAM,CAAC8B,cAAcC,eAAgB,CAAA,GAAGX,iBAAMI,QAAQ,CAACK,8BAAe7B,KAAO,EAAA,IAAA,CAAA,CAAA;IAC7E,MAAM,EAAEgC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;AAC1B,IAAA,MAAM,CAACC,iBAAmBC,EAAAA,oBAAAA,CAAqB,GAAGf,gBAAAA,CAAMI,QAAQ,CAAC,KAAA,CAAA;AACjE,IAAA,MAAM,EAAEY,IAAI,EAAEC,WAAW,EAAEC,YAAY,EAAEC,UAAU,EAAEC,cAAc,EAAEC,KAAK,EAAEC,MAAM,EAAE,GAClFC,qBAAAA,EAAAA;IACF,MAAM,EAAEC,SAAS,EAAEC,KAAK,EAAEC,SAAS,EAAEC,QAAQ,EAAEC,MAAM,EAAE,GAAGC,yBAAAA,EAAAA;AAC1D,IAAA,MAAM,CAACC,kBAAoBC,EAAAA,qBAAAA,CAAsB,GAAG/B,gBAAAA,CAAMI,QAAQ,CAAU,KAAA,CAAA;AAC5E,IAAA,MAAM,CAAC4B,UAAYC,EAAAA,aAAAA,CAAc,GAAGjC,gBAAMI,CAAAA,QAAQ,CAChDd,cAAkB,IAAA;QAAE4C,CAAG,EAAA,EAAA;QAAIC,CAAG,EAAA;AAAG,KAAA,CAAA;AAGnC,IAAA,MAAM,EACJC,MAAM,EACNV,SAAAA,EAAWW,eAAe,EAC1BT,MAAAA,EAAQU,YAAY,EACpBb,OAAOc,WAAW,EAClBZ,QAAUa,EAAAA,cAAc,EACzB,GAAGC,mBAAAA,EAAAA;AAEJzC,IAAAA,gBAAAA,CAAM0C,SAAS,CAAC,IAAA;;;AAGd,QAAA,IAAItD,eAAiB,EAAA;YACnB,MAAMuD,YAAAA,GAAeC,GAAIC,CAAAA,eAAe,CAACzD,eAAAA,CAAAA;YAEzC,IAAIR,KAAAA,CAAMkE,OAAO,EAAE;AACjBlE,gBAAAA,KAAAA,CAAMmE,GAAG,GAAGJ,YAAAA;AACd;YAEAnC,WAAYmC,CAAAA,YAAAA,CAAAA;YACZhC,eAAgBgC,CAAAA,YAAAA,CAAAA;AAClB;KACC,EAAA;AAACvD,QAAAA,eAAAA;AAAiBR,QAAAA;AAAM,KAAA,CAAA;AAE3BoB,IAAAA,gBAAAA,CAAM0C,SAAS,CAAC,IAAA;AACd,QAAA,IAAIrC,kBAAkB,KAAO,EAAA;AAC3Ba,YAAAA,YAAAA,EAAAA;AACA/B,YAAAA,YAAAA,EAAAA;AACF;KACC,EAAA;AAACkB,QAAAA,aAAAA;AAAea,QAAAA,YAAAA;AAAc/B,QAAAA,YAAAA;AAAcF,QAAAA;AAAa,KAAA,CAAA;AAE5De,IAAAA,gBAAAA,CAAM0C,SAAS,CAAC,IAAA;AACd,QAAA,IAAIrC,iBAAiBH,gBAAkB,EAAA;AACrCc,YAAAA,IAAAA,CAAKjB,WAAWiD,OAAO,CAAA;AACvB9D,YAAAA,WAAAA,EAAAA;AACF;KACC,EAAA;AAACgB,QAAAA,gBAAAA;AAAkBG,QAAAA,aAAAA;AAAenB,QAAAA,WAAAA;AAAa8B,QAAAA;AAAK,KAAA,CAAA;AAEvD,IAAA,MAAMiC,cAAiB,GAAA,UAAA;AACrB,QAAA,MAAMC,SAAY,GAAA;AAAE,YAAA,GAAGtE,KAAK;AAAEyC,YAAAA,KAAAA;AAAOC,YAAAA,MAAAA;YAAQ6B,MAAQvE,EAAAA,KAAAA,CAAMuE,MAAM,EAAEC;AAAG,SAAA;QACtE,MAAMC,IAAAA,GAAQ,MAAMpC,WAAAA,CAAYiC,SAAUI,CAAAA,IAAI,EAAEJ,SAAUK,CAAAA,IAAI,EAAGL,SAAAA,CAAUM,SAAS,CAAA;;;QAIpF,IAAIC,qBAAAA;QACJ,IAAIC,8BAAAA;QAEJ,IAAI9E,KAAAA,CAAMkE,OAAO,EAAE;YACjBW,qBAAwBb,GAAAA,GAAAA,CAAIC,eAAe,CAACQ,IAAAA,CAAAA;YAC5CK,8BAAiCD,GAAAA,qBAAAA;AACjC7E,YAAAA,KAAAA,CAAMmE,GAAG,GAAGU,qBAAAA;AACZ7E,YAAAA,KAAAA,CAAM+E,OAAO,GAAGN,IAAAA;AAEhBxD,YAAAA,UAAAA,CAAW,aAAe,EAAA;gBAAE+D,cAAgB,EAAA,IAAA;gBAAMC,QAAUxE,EAAAA;AAAiB,aAAA,CAAA;SACxE,MAAA;YACL,MAAMyE,YAAAA,GAAe,MAAMtC,SAAAA,CAAU0B,SAAWG,EAAAA,IAAAA,CAAAA;AAChDI,YAAAA,qBAAAA,GAAwBhD,8BAAeqD,YAAc,EAAA,KAAA,CAAA;AACrDJ,YAAAA,8BAAAA,GAAiCjD,8BAAeqD,YAAc,EAAA,IAAA,CAAA;AAE9DjE,YAAAA,UAAAA,CAAW,aAAe,EAAA;gBAAE+D,cAAgB,EAAA,KAAA;gBAAOC,QAAUxE,EAAAA;AAAiB,aAAA,CAAA;AAChF;QAEAmB,WAAYiD,CAAAA,qBAAAA,CAAAA;QACZ9C,eAAgB+C,CAAAA,8BAAAA,CAAAA;QAChBpD,gBAAiB,CAAA,KAAA,CAAA;AACnB,KAAA;IAEA,MAAMyD,gBAAAA,GAAmB5C,cAAc,CAACO,SAAAA;AAExC,IAAA,MAAMsC,iBAAoB,GAAA,UAAA;AACxB,QAAA,MAAMd,SAAY,GAAA;AAAE,YAAA,GAAGtE,KAAK;AAAEyC,YAAAA,KAAAA;AAAOC,YAAAA;AAAO,SAAA;QAC5C,MAAM+B,IAAAA,GAAQ,MAAMpC,WAAAA,CAClBiC,SAAUI,CAAAA,IAAI,EACdJ,SAAUK,CAAAA,IAAI,EACdL,SAAAA,CAAUM,SAAS,CAAA;AAGrB,QAAA,MAAMpB,MAAO,CAAA;AAAEkB,YAAAA,IAAAA,EAAMD,KAAKC,IAAI;YAAEK,OAASN,EAAAA;SAAQzE,EAAAA,KAAAA,CAAMuE,MAAM,EAAEC,EAAAA,GAAKxE,MAAMuE,MAAM,CAACC,EAAE,GAAG,IAAA,CAAA;AAEtFvD,QAAAA,UAAAA,CAAW,aAAe,EAAA;YAAE+D,cAAgB,EAAA,IAAA;YAAMC,QAAUxE,EAAAA;AAAiB,SAAA,CAAA;QAE7EiB,gBAAiB,CAAA,KAAA,CAAA;AACjBrB,QAAAA,YAAAA,EAAAA;AACF,KAAA;AAEA,IAAA,MAAMgF,gBAAmB,GAAA,IAAA;QACvB3D,gBAAiB,CAAA,KAAA,CAAA;AACnB,KAAA;AAEA,IAAA,MAAM4D,eAAkB,GAAA,IAAA;QACtB5D,gBAAiB,CAAA,IAAA,CAAA;AACnB,KAAA;AAEA,IAAA,MAAM6D,+BAA+B,CAACC,CAAAA,GAAAA;AACpC,QAAA,MAAM,EAAEC,OAAO,EAAEC,OAAO,EAAE,GAAGF,CAAAA;AAC7B,QAAA,MAAMG,IAAOH,GAAAA,CAAAA,CAAEI,aAAa,CAACC,qBAAqB,EAAA;QAClD,MAAMC,IAAAA,GAAOL,OAAUE,GAAAA,IAAAA,CAAKI,IAAI;QAChC,MAAMC,IAAAA,GAAON,OAAUC,GAAAA,IAAAA,CAAKM,GAAG;QAE/B,OAAO;YACL3C,CAAG4C,EAAAA,MAAAA,CAAO,CAAC,IAACJ,GAAOH,IAAKlD,CAAAA,KAAK,GAAI,GAAE,EAAG0D,OAAO,CAAC,CAAA,CAAA,CAAA;YAC9C5C,CAAG2C,EAAAA,MAAAA,CAAO,CAAC,IAACF,GAAOL,IAAKjD,CAAAA,MAAM,GAAI,GAAE,EAAGyD,OAAO,CAAC,CAAA,CAAA;AACjD,SAAA;AACF,KAAA;AAEA,IAAA,MAAMC,wBAAwB,CAACZ,CAAAA,GAAAA;AAC7B,QAAA,IAAI,CAACtC,kBAAoB,EAAA;AACzBG,QAAAA,aAAAA,CAAckC,4BAA6BC,CAAAA,CAAAA,CAAAA,CAAAA;AAC7C,KAAA;AAEA,IAAA,MAAMa,sBAAyB,GAAA,IAAA;QAC7BlD,qBAAsB,CAAA,KAAA,CAAA;AACtBE,QAAAA,aAAAA,CAAc3C,cAAkB,IAAA;YAAE4C,CAAG,EAAA,EAAA;YAAIC,CAAG,EAAA;AAAG,SAAA,CAAA;AAC/C1C,QAAAA,kBAAAA,EAAAA;AACF,KAAA;AAEA,IAAA,MAAMyF,qBAAwB,GAAA,IAAA;AAC5B3F,QAAAA,iBAAAA,EAAAA;QACAwC,qBAAsB,CAAA,IAAA,CAAA;AACxB,KAAA;AAEA,IAAA,MAAMoD,wBAA2B,GAAA,IAAA;QAC/BpD,qBAAsB,CAAA,KAAA,CAAA;QACtBvC,kBAAmBwC,CAAAA,UAAAA,CAAAA;AACrB,KAAA;AAEA,IAAA,MAAMoD,qBAAwB,GAAA,IAAA;QAC5BnD,aAAc,CAAA;YAAEC,CAAG,EAAA,EAAA;YAAIC,CAAG,EAAA;AAAG,SAAA,CAAA;AAC/B,KAAA;IAEA,qBACEkD,eAAA,CAAAC,mBAAA,EAAA;;0BACEC,cAAC7F,CAAAA,cAAAA,EAAAA,EAAAA,CAAAA;0BACD2F,eAACG,CAAAA,6BAAAA,EAAAA;gBAAYC,SAAS,EAAA,IAAA;gBAACC,UAAW,EAAA,YAAA;gBAAaC,WAAY,EAAA,YAAA;;AACxDvE,oBAAAA,cAAAA,IAAkB2C,kCACjBwB,cAACK,CAAAA,+BAAAA,EAAAA;wBACCC,UAAY5C,EAAAA,cAAAA;wBACZ6C,WAAalH,EAAAA,KAAAA,CAAMkE,OAAO,GAAGiD,SAAY/B,GAAAA,iBAAAA;wBACzCgC,QAAU/B,EAAAA;;AAIbnC,oBAAAA,kBAAAA,kBACCyD,cAACU,CAAAA,mCAAAA,EAAAA;wBACCJ,UAAYV,EAAAA,wBAAAA;wBACZa,QAAUf,EAAAA,sBAAAA;wBACViB,OAASd,EAAAA;;kCAIbG,cAACY,CAAAA,2BAAAA,EAAAA;wBAAUC,WAAa,EAAA,CAAA;wBAAGC,YAAc,EAAA,CAAA;wBAAGC,cAAe,EAAA,UAAA;AACzD,wBAAA,QAAA,gBAAAjB,eAACkB,CAAAA,iBAAAA,EAAAA;4BAAKC,GAAK,EAAA,CAAA;;AACR3H,gCAAAA,SAAAA,IAAa,CAACD,KAAAA,CAAMkE,OAAO,kBAC1ByC,cAACkB,CAAAA,uBAAAA,EAAAA;AACCC,oCAAAA,KAAAA,EAAO9F,aAAc,CAAA;wCACnBwC,EAAI,EAAA,eAAA;wCACJuD,cAAgB,EAAA;AAClB,qCAAA,CAAA;AACAC,oCAAAA,OAAAA,EAAS,IAAM7F,oBAAqB,CAAA,IAAA,CAAA;AAEpC,oCAAA,QAAA,gBAAAwE,cAACsB,CAAAA,WAAAA,EAAAA,EAAAA;;AAIJ9H,gCAAAA,WAAAA,kBACCwG,cAACkB,CAAAA,uBAAAA,EAAAA;AACCC,oCAAAA,KAAAA,EAAO9F,aAAc,CAAA;AACnBwC,wCAAAA,EAAAA,EAAI0D,eAAQ,CAAA,uBAAA,CAAA;wCACZH,cAAgB,EAAA;AAClB,qCAAA,CAAA;AACAC,oCAAAA,OAAAA,EAAS,IAAMG,yBAAAA,CAAaxG,QAAW3B,EAAAA,KAAAA,CAAM0E,IAAI,CAAA;AAEjD,oCAAA,QAAA,gBAAAiC,cAACyB,CAAAA,cAAAA,EAAAA,EAAAA;;AAIJlI,gCAAAA,WAAAA,kBAAeyG,cAAC0B,CAAAA,6BAAAA,EAAAA;oCAAelE,GAAKxC,EAAAA;;AAEpC1B,gCAAAA,SAAAA,IAAaD,MAAM2E,IAAI,EAAE2D,SAASC,eAAUC,CAAAA,KAAK,mBAChD7B,cAACkB,CAAAA,uBAAAA,EAAAA;AACCC,oCAAAA,KAAAA,EAAO9F,aAAc,CAAA;AAAEwC,wCAAAA,EAAAA,EAAI0D,eAAQ,CAAA,mBAAA,CAAA;wCAAsBH,cAAgB,EAAA;AAAO,qCAAA,CAAA;oCAChFC,OAAS1C,EAAAA,eAAAA;AAET,oCAAA,QAAA,gBAAAqB,cAAC8B,CAAAA,UAAAA,EAAAA,EAAAA;;AAIJxI,gCAAAA,SAAAA,IAAaD,MAAM2E,IAAI,EAAE2D,SAASC,eAAUC,CAAAA,KAAK,mBAChD7B,cAACkB,CAAAA,uBAAAA,EAAAA;AACCC,oCAAAA,KAAAA,EAAO9F,aAAc,CAAA;AACnBwC,wCAAAA,EAAAA,EAAI0D,eAAQ,CAAA,8BAAA,CAAA;wCACZH,cAAgB,EAAA;AAClB,qCAAA,CAAA;oCACAC,OAAS1B,EAAAA,qBAAAA;AAET,oCAAA,QAAA,gBAAAK,cAAC+B,CAAAA,YAAAA,EAAAA,EAAAA;;;;;kCAMTjC,eAACkC,CAAAA,yBAAAA,EAAAA;;AAEE7F,4BAAAA,SAAAA,kBACC6D,cAACiC,CAAAA,uCAAAA,EAAAA;AACC,gCAAA,QAAA,gBAAAjC,cAACkC,CAAAA,6BAAAA,EAAAA;oCAAehG,KAAOA,EAAAA,KAAAA;oCAAOuE,QAAUpE,EAAAA,MAAAA;oCAAQD,QAAUA,EAAAA;;;AAK7DU,4BAAAA,eAAAA,kBACCkD,cAACiC,CAAAA,uCAAAA,EAAAA;AACC,gCAAA,QAAA,gBAAAjC,cAACkC,CAAAA,6BAAAA,EAAAA;oCACChG,KAAOc,EAAAA,WAAAA;oCACPyD,QAAU1D,EAAAA,YAAAA;oCACVX,QAAUa,EAAAA;;;0CAKhB6C,eAACqC,CAAAA,wCAAAA,EAAAA;;kDACCnC,cAACoC,CAAAA,yBAAAA,EAAAA;wCACCC,GAAK7H,EAAAA,UAAAA;AACLwD,wCAAAA,IAAAA,EAAM3E,MAAM2E,IAAI;AAChBD,wCAAAA,IAAAA,EAAM1E,MAAM0E,IAAI;AAChBP,wCAAAA,GAAAA,EAAK1C,gBAAgBE,QAAYG,GAAAA,YAAAA;wCACjCmH,MAAQ,EAAA,IAAA;4CACN,IAAIjJ,KAAAA,CAAMkE,OAAO,IAAIzC,aAAe,EAAA;gDAClCF,mBAAoB,CAAA,IAAA,CAAA;AACtB;AACF,yCAAA;wCACAyG,OAAS5B,EAAAA,qBAAAA;wCACT8C,KAAO,EAAA;AAAEC,4CAAAA,MAAAA,EAAQjG,qBAAqB,WAAciE,GAAAA;AAAU;;AAI/DjE,oCAAAA,kBAAAA,kBACCyD,cAACyC,CAAAA,+BAAAA,EAAAA;wCAAcC,WAAajG,EAAAA,UAAAA;AAC1B,wCAAA,QAAA,gBAAAuD,cAAC2C,CAAAA,gCAAAA,EAAAA,EAAAA;;;;;;kCAMT7C,eAACc,CAAAA,2BAAAA,EAAAA;wBACCC,WAAa,EAAA,CAAA;wBACbC,YAAc,EAAA,CAAA;wBACdC,cAAe,EAAA,UAAA;AACf6B,wBAAAA,OAAAA,EAASpE,gBAAoBjC,IAAAA,kBAAAA;;4BAE5BiC,gBAAoB1C,IAAAA,KAAAA,IAASC,wBAC5BiE,cAAC6C,CAAAA,+BAAAA,EAAAA;gCAAc1C,UAAW,EAAA,YAAA;gCAAa2C,KAAM,EAAA,UAAA;AAC1ChH,gCAAAA,QAAAA,EAAAA,KAAAA,IAASC,SAAS,CAAGA,EAAAA,MAAAA,CAAO,CAAC,EAAED,OAAO,GAAG;;AAG7CS,4BAAAA,kBAAAA,kBACCyD,cAAC6C,CAAAA,+BAAAA,EAAAA;gCAAc1C,UAAW,EAAA,YAAA;gCAAa2C,KAAM,EAAA,UAAA;0CAC1C,CAAC,GAAG,EAAErG,UAAAA,CAAWE,CAAC,CAAC,OAAO,EAAEF,UAAWG,CAAAA,CAAC,CAAC,CAAC;;;;;;0BAMnDoD,cAAC+C,CAAAA,mCAAAA,EAAAA;gBACCC,IAAMzH,EAAAA,iBAAAA;AACN0H,gBAAAA,OAAAA,EAAS,CAACC,KAAAA,GAAAA;oBACR1H,oBAAqB,CAAA,KAAA,CAAA;AACrB,oBAAA,IAAI0H,UAAU,IAAM,EAAA;wBAClBzJ,QAAS,CAAA,IAAA,CAAA;AACX;AACF,iBAAA;gBACAJ,KAAOA,EAAAA;;;;AAIf;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { Flex, IconButton } from '@strapi/design-system';
|
|
4
|
-
import { Trash, Download, Crop } from '@strapi/icons';
|
|
4
|
+
import { Trash, Download, Crop, PinMap } from '@strapi/icons';
|
|
5
5
|
import cropperjscss from 'cropperjs/dist/cropper.css?raw';
|
|
6
6
|
import { useIntl } from 'react-intl';
|
|
7
7
|
import { createGlobalStyle } from 'styled-components';
|
|
@@ -23,10 +23,11 @@ import { UploadProgress } from '../../UploadProgress/UploadProgress.mjs';
|
|
|
23
23
|
import { RemoveAssetDialog } from '../RemoveAssetDialog.mjs';
|
|
24
24
|
import { AssetPreview } from './AssetPreview.mjs';
|
|
25
25
|
import { CroppingActions } from './CroppingActions.mjs';
|
|
26
|
-
import {
|
|
26
|
+
import { FocalPointActions } from './FocalPointActions.mjs';
|
|
27
|
+
import { RelativeBox, ActionRow, Wrapper, UploadProgressWrapper, FocalPointImageWrapper, FocalPointAim, FocalPointHalo, BadgeOverride } from './PreviewComponents.mjs';
|
|
27
28
|
|
|
28
29
|
// TODO: find a better naming convention for the file that was an index file before
|
|
29
|
-
const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCropFinish, onCropStart, onCropCancel, replacementFile, trackedLocation })=>{
|
|
30
|
+
const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCropFinish, onCropStart, onCropCancel, replacementFile, trackedLocation, formFocalPoint, onFocalPointStart, onFocalPointFinish, onFocalPointCancel })=>{
|
|
30
31
|
const CropperjsStyle = createGlobalStyle`${cropperjscss}`;
|
|
31
32
|
const { trackUsage } = useTracking();
|
|
32
33
|
const previewRef = React.useRef(null);
|
|
@@ -38,6 +39,11 @@ const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCr
|
|
|
38
39
|
const [showConfirmDialog, setShowConfirmDialog] = React.useState(false);
|
|
39
40
|
const { crop, produceFile, stopCropping, isCropping, isCropperReady, width, height } = useCropImg();
|
|
40
41
|
const { editAsset, error, isLoading, progress, cancel } = useEditAsset();
|
|
42
|
+
const [isInFocalPointMode, setIsInFocalPointMode] = React.useState(false);
|
|
43
|
+
const [focalPoint, setFocalPoint] = React.useState(formFocalPoint ?? {
|
|
44
|
+
x: 50,
|
|
45
|
+
y: 50
|
|
46
|
+
});
|
|
41
47
|
const { upload, isLoading: isLoadingUpload, cancel: cancelUpload, error: uploadError, progress: progressUpload } = useUpload();
|
|
42
48
|
React.useEffect(()=>{
|
|
43
49
|
// Whenever a replacementUrl is set, make sure to permutate the real asset.url by
|
|
@@ -135,6 +141,42 @@ const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCr
|
|
|
135
141
|
const handleCropStart = ()=>{
|
|
136
142
|
setHasCropIntent(true);
|
|
137
143
|
};
|
|
144
|
+
const calculateFocalPointFromEvent = (e)=>{
|
|
145
|
+
const { clientX, clientY } = e;
|
|
146
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
147
|
+
const posX = clientX - rect.left;
|
|
148
|
+
const posY = clientY - rect.top;
|
|
149
|
+
return {
|
|
150
|
+
x: Number((posX / rect.width * 100).toFixed(2)),
|
|
151
|
+
y: Number((posY / rect.height * 100).toFixed(2))
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
const handleFocalPointClick = (e)=>{
|
|
155
|
+
if (!isInFocalPointMode) return;
|
|
156
|
+
setFocalPoint(calculateFocalPointFromEvent(e));
|
|
157
|
+
};
|
|
158
|
+
const handleFocalPointCancel = ()=>{
|
|
159
|
+
setIsInFocalPointMode(false);
|
|
160
|
+
setFocalPoint(formFocalPoint ?? {
|
|
161
|
+
x: 50,
|
|
162
|
+
y: 50
|
|
163
|
+
});
|
|
164
|
+
onFocalPointCancel();
|
|
165
|
+
};
|
|
166
|
+
const handleFocalPointStart = ()=>{
|
|
167
|
+
onFocalPointStart();
|
|
168
|
+
setIsInFocalPointMode(true);
|
|
169
|
+
};
|
|
170
|
+
const handleFocalPointValidate = ()=>{
|
|
171
|
+
setIsInFocalPointMode(false);
|
|
172
|
+
onFocalPointFinish(focalPoint);
|
|
173
|
+
};
|
|
174
|
+
const handleFocalPointReset = ()=>{
|
|
175
|
+
setFocalPoint({
|
|
176
|
+
x: 50,
|
|
177
|
+
y: 50
|
|
178
|
+
});
|
|
179
|
+
};
|
|
138
180
|
return /*#__PURE__*/ jsxs(Fragment, {
|
|
139
181
|
children: [
|
|
140
182
|
/*#__PURE__*/ jsx(CropperjsStyle, {}),
|
|
@@ -148,6 +190,11 @@ const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCr
|
|
|
148
190
|
onDuplicate: asset.isLocal ? undefined : handleDuplication,
|
|
149
191
|
onCancel: handleCropCancel
|
|
150
192
|
}),
|
|
193
|
+
isInFocalPointMode && /*#__PURE__*/ jsx(FocalPointActions, {
|
|
194
|
+
onValidate: handleFocalPointValidate,
|
|
195
|
+
onCancel: handleFocalPointCancel,
|
|
196
|
+
onReset: handleFocalPointReset
|
|
197
|
+
}),
|
|
151
198
|
/*#__PURE__*/ jsx(ActionRow, {
|
|
152
199
|
paddingLeft: 3,
|
|
153
200
|
paddingRight: 3,
|
|
@@ -181,6 +228,14 @@ const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCr
|
|
|
181
228
|
}),
|
|
182
229
|
onClick: handleCropStart,
|
|
183
230
|
children: /*#__PURE__*/ jsx(Crop, {})
|
|
231
|
+
}),
|
|
232
|
+
canUpdate && asset.mime?.includes(AssetType.Image) && /*#__PURE__*/ jsx(IconButton, {
|
|
233
|
+
label: formatMessage({
|
|
234
|
+
id: getTrad('control-card.set-focal-point'),
|
|
235
|
+
defaultMessage: 'Set focal point'
|
|
236
|
+
}),
|
|
237
|
+
onClick: handleFocalPointStart,
|
|
238
|
+
children: /*#__PURE__*/ jsx(PinMap, {})
|
|
184
239
|
})
|
|
185
240
|
]
|
|
186
241
|
})
|
|
@@ -201,37 +256,58 @@ const PreviewBox = ({ asset, canUpdate, canCopyLink, canDownload, onDelete, onCr
|
|
|
201
256
|
progress: progressUpload
|
|
202
257
|
})
|
|
203
258
|
}),
|
|
204
|
-
/*#__PURE__*/
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
259
|
+
/*#__PURE__*/ jsxs(FocalPointImageWrapper, {
|
|
260
|
+
children: [
|
|
261
|
+
/*#__PURE__*/ jsx(AssetPreview, {
|
|
262
|
+
ref: previewRef,
|
|
263
|
+
mime: asset.mime,
|
|
264
|
+
name: asset.name,
|
|
265
|
+
url: hasCropIntent ? assetUrl : thumbnailUrl,
|
|
266
|
+
onLoad: ()=>{
|
|
267
|
+
if (asset.isLocal || hasCropIntent) {
|
|
268
|
+
setIsCropImageReady(true);
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
onClick: handleFocalPointClick,
|
|
272
|
+
style: {
|
|
273
|
+
cursor: isInFocalPointMode ? 'crosshair' : undefined
|
|
274
|
+
}
|
|
275
|
+
}),
|
|
276
|
+
isInFocalPointMode && /*#__PURE__*/ jsx(FocalPointAim, {
|
|
277
|
+
$focalPoint: focalPoint,
|
|
278
|
+
children: /*#__PURE__*/ jsx(FocalPointHalo, {})
|
|
279
|
+
})
|
|
280
|
+
]
|
|
214
281
|
})
|
|
215
282
|
]
|
|
216
283
|
}),
|
|
217
|
-
/*#__PURE__*/
|
|
284
|
+
/*#__PURE__*/ jsxs(ActionRow, {
|
|
218
285
|
paddingLeft: 2,
|
|
219
286
|
paddingRight: 2,
|
|
220
287
|
justifyContent: "flex-end",
|
|
221
|
-
$blurry: isInCroppingMode,
|
|
222
|
-
children:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
288
|
+
$blurry: isInCroppingMode || isInFocalPointMode,
|
|
289
|
+
children: [
|
|
290
|
+
isInCroppingMode && width && height && /*#__PURE__*/ jsx(BadgeOverride, {
|
|
291
|
+
background: "neutral900",
|
|
292
|
+
color: "neutral0",
|
|
293
|
+
children: width && height ? `${height}✕${width}` : 'N/A'
|
|
294
|
+
}),
|
|
295
|
+
isInFocalPointMode && /*#__PURE__*/ jsx(BadgeOverride, {
|
|
296
|
+
background: "neutral900",
|
|
297
|
+
color: "neutral0",
|
|
298
|
+
children: `x: ${focalPoint.x}% | y: ${focalPoint.y}%`
|
|
299
|
+
})
|
|
300
|
+
]
|
|
227
301
|
})
|
|
228
302
|
]
|
|
229
303
|
}),
|
|
230
304
|
/*#__PURE__*/ jsx(RemoveAssetDialog, {
|
|
231
305
|
open: showConfirmDialog,
|
|
232
|
-
onClose: ()=>{
|
|
306
|
+
onClose: (value)=>{
|
|
233
307
|
setShowConfirmDialog(false);
|
|
234
|
-
|
|
308
|
+
if (value === null) {
|
|
309
|
+
onDelete(null);
|
|
310
|
+
}
|
|
235
311
|
},
|
|
236
312
|
asset: asset
|
|
237
313
|
})
|