@atlaskit/editor-plugin-media 1.35.0 → 1.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/nodeviews/mediaNodeUpdater.js +7 -1
  3. package/dist/cjs/nodeviews/mediaSingle.js +27 -0
  4. package/dist/cjs/nodeviews/mediaSingleNext.js +583 -0
  5. package/dist/cjs/plugin.js +70 -43
  6. package/dist/cjs/pm-plugins/actions.js +10 -0
  7. package/dist/cjs/pm-plugins/commands.js +28 -0
  8. package/dist/cjs/pm-plugins/main.js +14 -0
  9. package/dist/cjs/toolbar/filePreviewItem.js +3 -2
  10. package/dist/cjs/toolbar/index.js +46 -12
  11. package/dist/cjs/toolbar/mediaInline.js +14 -1
  12. package/dist/es2019/nodeviews/mediaNodeUpdater.js +7 -0
  13. package/dist/es2019/nodeviews/mediaSingle.js +27 -0
  14. package/dist/es2019/nodeviews/mediaSingleNext.js +543 -0
  15. package/dist/es2019/plugin.js +31 -2
  16. package/dist/es2019/pm-plugins/actions.js +4 -0
  17. package/dist/es2019/pm-plugins/commands.js +22 -0
  18. package/dist/es2019/pm-plugins/main.js +14 -0
  19. package/dist/es2019/toolbar/filePreviewItem.js +3 -2
  20. package/dist/es2019/toolbar/index.js +40 -5
  21. package/dist/es2019/toolbar/mediaInline.js +15 -2
  22. package/dist/esm/nodeviews/mediaNodeUpdater.js +6 -0
  23. package/dist/esm/nodeviews/mediaSingle.js +27 -0
  24. package/dist/esm/nodeviews/mediaSingleNext.js +576 -0
  25. package/dist/esm/plugin.js +70 -43
  26. package/dist/esm/pm-plugins/actions.js +4 -0
  27. package/dist/esm/pm-plugins/commands.js +22 -0
  28. package/dist/esm/pm-plugins/main.js +14 -0
  29. package/dist/esm/toolbar/filePreviewItem.js +3 -2
  30. package/dist/esm/toolbar/index.js +46 -12
  31. package/dist/esm/toolbar/mediaInline.js +15 -2
  32. package/dist/types/next-plugin-type.d.ts +6 -1
  33. package/dist/types/nodeviews/mediaNodeUpdater.d.ts +1 -0
  34. package/dist/types/nodeviews/mediaSingle.d.ts +1 -1
  35. package/dist/types/nodeviews/mediaSingleNext.d.ts +35 -0
  36. package/dist/types/pm-plugins/actions.d.ts +4 -0
  37. package/dist/types/pm-plugins/commands.d.ts +4 -0
  38. package/dist/types/pm-plugins/types.d.ts +3 -1
  39. package/dist/types/toolbar/index.d.ts +4 -0
  40. package/dist/types-ts4.5/next-plugin-type.d.ts +6 -1
  41. package/dist/types-ts4.5/nodeviews/mediaNodeUpdater.d.ts +1 -0
  42. package/dist/types-ts4.5/nodeviews/mediaSingle.d.ts +1 -1
  43. package/dist/types-ts4.5/nodeviews/mediaSingleNext.d.ts +35 -0
  44. package/dist/types-ts4.5/pm-plugins/actions.d.ts +4 -0
  45. package/dist/types-ts4.5/pm-plugins/commands.d.ts +4 -0
  46. package/dist/types-ts4.5/pm-plugins/types.d.ts +3 -1
  47. package/dist/types-ts4.5/toolbar/index.d.ts +4 -0
  48. package/package.json +13 -7
@@ -0,0 +1,576 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
3
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
5
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
+ /**
8
+ * @jsxRuntime classic
9
+ * @jsx jsx
10
+ * @jsxFrag
11
+ */
12
+ import React, { Fragment } from 'react';
13
+
14
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
15
+ import { css, jsx } from '@emotion/react';
16
+ import { usePreviousState } from '@atlaskit/editor-common/hooks';
17
+ import { calcMediaSinglePixelWidth, DEFAULT_IMAGE_HEIGHT, DEFAULT_IMAGE_WIDTH, ExternalImageBadge, getMaxWidthForNestedNode, MEDIA_SINGLE_GUTTER_SIZE, MediaBadges } from '@atlaskit/editor-common/media-single';
18
+ import { MediaSingle } from '@atlaskit/editor-common/ui';
19
+ import { browser } from '@atlaskit/editor-common/utils';
20
+ import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
21
+ import { findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
22
+ import { getAttrsFromUrl } from '@atlaskit/media-client';
23
+ import { fg } from '@atlaskit/platform-feature-flags';
24
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
25
+ import { insertAndSelectCaptionFromMediaSinglePos } from '../commands/captions';
26
+ import { CaptionPlaceholder, CaptionPlaceholderButton } from '../ui/CaptionPlaceholder';
27
+ import { CommentBadge, CommentBadgeNextWrapper } from '../ui/CommentBadge';
28
+ import ResizableMediaSingle from '../ui/ResizableMediaSingle';
29
+ import ResizableMediaSingleNext from '../ui/ResizableMediaSingle/ResizableMediaSingleNext';
30
+ import { isMediaBlobUrlFromAttrs } from '../utils/media-common';
31
+ import { hasPrivateAttrsChanged } from './helpers';
32
+ import { createMediaNodeUpdater } from './mediaNodeUpdater';
33
+ import { MediaSingleNodeSelector } from './styles';
34
+ var figureWrapperStyles = css({
35
+ margin: 0
36
+ });
37
+ var useMediaNodeUpdater = function useMediaNodeUpdater(_ref) {
38
+ var mediaProvider = _ref.mediaProvider,
39
+ mediaNode = _ref.mediaNode,
40
+ dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent,
41
+ mediaSingleNodeProps = _ref.mediaSingleNodeProps;
42
+ var previousMediaProvider = usePreviousState(mediaProvider);
43
+ var previousMediaNode = usePreviousState(mediaNode);
44
+ var mediaNodeUpdaterRef = React.useRef(null);
45
+ var createOrUpdateMediaNodeUpdater = React.useCallback(function (props) {
46
+ var mediaChildNode = mediaNode.firstChild;
47
+ var updaterProps = _objectSpread(_objectSpread({}, props), {}, {
48
+ isMediaSingle: true,
49
+ node: mediaChildNode ? mediaChildNode : mediaNode,
50
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent
51
+ });
52
+ if (!mediaNodeUpdaterRef.current) {
53
+ mediaNodeUpdaterRef.current = createMediaNodeUpdater(updaterProps);
54
+ } else {
55
+ mediaNodeUpdaterRef.current.setProps(updaterProps);
56
+ }
57
+ }, [mediaNode, dispatchAnalyticsEvent]);
58
+ React.useEffect(function () {
59
+ // Forced updates not required on mobile
60
+ if (mediaSingleNodeProps.isCopyPasteEnabled === false) {
61
+ return;
62
+ }
63
+ if (!mediaNodeUpdaterRef.current || previousMediaProvider !== mediaProvider) {
64
+ var _mediaNodeUpdaterRef$;
65
+ createOrUpdateMediaNodeUpdater(mediaSingleNodeProps);
66
+ (_mediaNodeUpdaterRef$ = mediaNodeUpdaterRef.current) === null || _mediaNodeUpdaterRef$ === void 0 || _mediaNodeUpdaterRef$.updateMediaSingleFileAttrs();
67
+ } else if (mediaNode.firstChild && previousMediaNode !== null && previousMediaNode !== void 0 && previousMediaNode.firstChild && mediaNode.firstChild !== (previousMediaNode === null || previousMediaNode === void 0 ? void 0 : previousMediaNode.firstChild)) {
68
+ var attrsChanged = hasPrivateAttrsChanged(previousMediaNode.firstChild.attrs, mediaNode.firstChild.attrs);
69
+ if (attrsChanged) {
70
+ var _mediaNodeUpdaterRef$2;
71
+ createOrUpdateMediaNodeUpdater(mediaSingleNodeProps);
72
+ // We need to call this method on any prop change since attrs can get removed with collab editing
73
+ (_mediaNodeUpdaterRef$2 = mediaNodeUpdaterRef.current) === null || _mediaNodeUpdaterRef$2 === void 0 || _mediaNodeUpdaterRef$2.updateMediaSingleFileAttrs();
74
+ }
75
+ }
76
+ }, [createOrUpdateMediaNodeUpdater, mediaNode, mediaProvider, mediaSingleNodeProps, previousMediaNode, previousMediaProvider]);
77
+ return mediaNodeUpdaterRef.current;
78
+ };
79
+ var mediaAsyncOperations = /*#__PURE__*/function () {
80
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(props) {
81
+ var updatedDimensions, currentAttrs, updatingNode, contextId, hasDifferentContextId, copyNode;
82
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
83
+ while (1) switch (_context.prev = _context.next) {
84
+ case 0:
85
+ _context.next = 2;
86
+ return props.updater.getRemoteDimensions();
87
+ case 2:
88
+ updatedDimensions = _context.sent;
89
+ currentAttrs = props.mediaChildNode.attrs;
90
+ if (updatedDimensions && ((currentAttrs === null || currentAttrs === void 0 ? void 0 : currentAttrs.width) !== updatedDimensions.width || (currentAttrs === null || currentAttrs === void 0 ? void 0 : currentAttrs.height) !== updatedDimensions.height)) {
91
+ props.updater.updateDimensions(updatedDimensions);
92
+ }
93
+ if (!(props.mediaChildNode.attrs.type === 'external' && props.mediaChildNode.attrs.__external)) {
94
+ _context.next = 11;
95
+ break;
96
+ }
97
+ updatingNode = props.updater.handleExternalMedia(props.getPos);
98
+ props.addPendingTask(updatingNode);
99
+ _context.next = 10;
100
+ return updatingNode;
101
+ case 10:
102
+ return _context.abrupt("return");
103
+ case 11:
104
+ contextId = props.updater.getNodeContextId();
105
+ if (contextId) {
106
+ _context.next = 15;
107
+ break;
108
+ }
109
+ _context.next = 15;
110
+ return props.updater.updateContextId();
111
+ case 15:
112
+ _context.next = 17;
113
+ return props.updater.hasDifferentContextId();
114
+ case 17:
115
+ hasDifferentContextId = _context.sent;
116
+ if (!hasDifferentContextId) {
117
+ _context.next = 28;
118
+ break;
119
+ }
120
+ _context.prev = 19;
121
+ copyNode = props.updater.copyNode({
122
+ traceId: props.mediaNode.attrs.__mediaTraceId
123
+ });
124
+ props.addPendingTask(copyNode);
125
+ _context.next = 24;
126
+ return copyNode;
127
+ case 24:
128
+ _context.next = 28;
129
+ break;
130
+ case 26:
131
+ _context.prev = 26;
132
+ _context.t0 = _context["catch"](19);
133
+ case 28:
134
+ case "end":
135
+ return _context.stop();
136
+ }
137
+ }, _callee, null, [[19, 26]]);
138
+ }));
139
+ return function mediaAsyncOperations(_x) {
140
+ return _ref2.apply(this, arguments);
141
+ };
142
+ }();
143
+ var useMediaAsyncOperations = function useMediaAsyncOperations(_ref3) {
144
+ var mediaNode = _ref3.mediaNode,
145
+ mediaNodeUpdater = _ref3.mediaNodeUpdater,
146
+ addPendingTask = _ref3.addPendingTask,
147
+ getPos = _ref3.getPos;
148
+ React.useEffect(function () {
149
+ if (!mediaNodeUpdater) {
150
+ return;
151
+ }
152
+ // we want the first child of MediaSingle (type "media")
153
+ var childNode = mediaNode.firstChild;
154
+ if (!childNode) {
155
+ return;
156
+ }
157
+ mediaAsyncOperations({
158
+ mediaChildNode: childNode,
159
+ updater: mediaNodeUpdater,
160
+ getPos: getPos,
161
+ mediaNode: mediaNode,
162
+ addPendingTask: addPendingTask
163
+ });
164
+ }, [mediaNode, addPendingTask, mediaNodeUpdater, getPos]);
165
+ };
166
+ var noop = function noop() {};
167
+
168
+ /**
169
+ * Keep returning the same ProseMirror Node, unless the node content changed.
170
+ *
171
+ * React uses shallow comparation with `Object.is`,
172
+ * but that can cause multiple re-renders when the same node is given in a different instance.
173
+ *
174
+ * To avoid unnecessary re-renders, this hook uses the `Node.eq` from ProseMirror API to compare
175
+ * previous and new values.
176
+ */
177
+ var useLatestMediaNode = function useLatestMediaNode(nextMediaNode) {
178
+ var previousMediaNode = usePreviousState(nextMediaNode);
179
+ var _React$useState = React.useState(nextMediaNode),
180
+ _React$useState2 = _slicedToArray(_React$useState, 2),
181
+ mediaNode = _React$useState2[0],
182
+ setMediaNode = _React$useState2[1];
183
+ React.useEffect(function () {
184
+ if (!previousMediaNode) {
185
+ return;
186
+ }
187
+ if (!previousMediaNode.eq(nextMediaNode)) {
188
+ setMediaNode(nextMediaNode);
189
+ }
190
+ }, [previousMediaNode, nextMediaNode]);
191
+ return mediaNode;
192
+ };
193
+ var useMediaDimensionsLogic = function useMediaDimensionsLogic(_ref4) {
194
+ var childMediaNodeAttrs = _ref4.childMediaNodeAttrs;
195
+ var originalWidth = childMediaNodeAttrs.width,
196
+ originalHeight = childMediaNodeAttrs.height;
197
+ var isExternalMedia = childMediaNodeAttrs.type === 'external';
198
+ var hasMediaUrlBlob = isExternalMedia && typeof childMediaNodeAttrs.url === 'string' && isMediaBlobUrlFromAttrs(childMediaNodeAttrs);
199
+ var urlBlobAttrs = React.useMemo(function () {
200
+ if (!hasMediaUrlBlob) {
201
+ return null;
202
+ }
203
+ return getAttrsFromUrl(childMediaNodeAttrs.url);
204
+ }, [hasMediaUrlBlob, childMediaNodeAttrs]);
205
+ var _React$useMemo = React.useMemo(function () {
206
+ // original width and height of child media node (scaled)
207
+ var width = originalWidth;
208
+ var height = originalHeight;
209
+ if (isExternalMedia) {
210
+ if (urlBlobAttrs) {
211
+ if (urlBlobAttrs) {
212
+ var urlWidth = urlBlobAttrs.width,
213
+ urlHeight = urlBlobAttrs.height;
214
+ width = width || urlWidth;
215
+ height = height || urlHeight;
216
+ }
217
+ }
218
+ if (width === null) {
219
+ width = DEFAULT_IMAGE_WIDTH;
220
+ }
221
+ if (height === null) {
222
+ height = DEFAULT_IMAGE_HEIGHT;
223
+ }
224
+ }
225
+ if (!width || !height) {
226
+ width = DEFAULT_IMAGE_WIDTH;
227
+ height = DEFAULT_IMAGE_HEIGHT;
228
+ }
229
+ return {
230
+ width: width,
231
+ height: height
232
+ };
233
+ }, [originalWidth, originalHeight, isExternalMedia, urlBlobAttrs]),
234
+ width = _React$useMemo.width,
235
+ height = _React$useMemo.height;
236
+ return {
237
+ width: width,
238
+ height: height
239
+ };
240
+ };
241
+ var useUpdateSizeCallback = function useUpdateSizeCallback(_ref5) {
242
+ var mediaNode = _ref5.mediaNode,
243
+ view = _ref5.view,
244
+ getPos = _ref5.getPos;
245
+ var updateSize = React.useCallback(function (width, layout) {
246
+ var state = view.state,
247
+ dispatch = view.dispatch;
248
+ var pos = getPos();
249
+ if (typeof pos === 'undefined') {
250
+ return;
251
+ }
252
+ var tr = state.tr.setNodeMarkup(pos, undefined, _objectSpread(_objectSpread({}, mediaNode.attrs), {}, {
253
+ layout: layout,
254
+ width: width,
255
+ widthType: 'pixel'
256
+ }));
257
+ tr.setMeta('scrollIntoView', false);
258
+ /**
259
+ * Any changes to attributes of a node count the node as "recreated" in Prosemirror[1]
260
+ * This makes it so Prosemirror resets the selection to the child i.e. "media" instead of "media-single"
261
+ * The recommended fix is to reset the selection.[2]
262
+ *
263
+ * [1] https://discuss.prosemirror.net/t/setnodemarkup-loses-current-nodeselection/976
264
+ * [2] https://discuss.prosemirror.net/t/setnodemarkup-and-deselect/3673
265
+ */
266
+ tr.setSelection(NodeSelection.create(tr.doc, pos));
267
+ return dispatch(tr);
268
+ }, [view, getPos, mediaNode]);
269
+ return updateSize;
270
+ };
271
+
272
+ /**
273
+ * This value is used to fallback when widthState is undefined.
274
+ *
275
+ * Previously, the old MediaSingle was ignoring the undefined situation:
276
+ *
277
+ * <MediaSingleNode
278
+ * width={widthState!.width}
279
+ * lineLength={widthState!.lineLength}
280
+ */
281
+ var FALLBACK_MOST_COMMON_WIDTH = 760;
282
+ export var MediaSingleNodeNext = function MediaSingleNodeNext(mediaSingleNodeNextProps) {
283
+ var _mediaOptions$getEdit, _pluginInjectionApi$m, _mediaNode$firstChild2;
284
+ var selected = mediaSingleNodeNextProps.selected,
285
+ getPos = mediaSingleNodeNextProps.getPos,
286
+ nextMediaNode = mediaSingleNodeNextProps.node,
287
+ mediaOptions = mediaSingleNodeNextProps.mediaOptions,
288
+ fullWidthMode = mediaSingleNodeNextProps.fullWidthMode,
289
+ view = mediaSingleNodeNextProps.view,
290
+ pluginInjectionApi = mediaSingleNodeNextProps.pluginInjectionApi,
291
+ containerWidth = mediaSingleNodeNextProps.width,
292
+ lineLength = mediaSingleNodeNextProps.lineLength,
293
+ dispatchAnalyticsEvent = mediaSingleNodeNextProps.dispatchAnalyticsEvent,
294
+ editorViewMode = mediaSingleNodeNextProps.editorViewMode,
295
+ editorDisabled = mediaSingleNodeNextProps.editorDisabled,
296
+ annotationPluginState = mediaSingleNodeNextProps.annotationPluginState,
297
+ editorAppearance = mediaSingleNodeNextProps.editorAppearance,
298
+ mediaProviderPromise = mediaSingleNodeNextProps.mediaProvider,
299
+ forwardRef = mediaSingleNodeNextProps.forwardRef,
300
+ contextIdentifierProviderPromise = mediaSingleNodeNextProps.contextIdentifierProvider,
301
+ mediaPluginState = mediaSingleNodeNextProps.mediaPluginState;
302
+ var _ref6 = (mediaOptions === null || mediaOptions === void 0 || (_mediaOptions$getEdit = mediaOptions.getEditorFeatureFlags) === null || _mediaOptions$getEdit === void 0 ? void 0 : _mediaOptions$getEdit.call(mediaOptions)) || {},
303
+ _ref6$commentsOnMedia = _ref6.commentsOnMedia,
304
+ commentsOnMedia = _ref6$commentsOnMedia === void 0 ? false : _ref6$commentsOnMedia;
305
+ var _React$useState3 = React.useState(null),
306
+ _React$useState4 = _slicedToArray(_React$useState3, 2),
307
+ mediaProvider = _React$useState4[0],
308
+ setMediaProvider = _React$useState4[1];
309
+ var _React$useState5 = React.useState(null),
310
+ _React$useState6 = _slicedToArray(_React$useState5, 2),
311
+ _contextIdentifierProvider = _React$useState6[0],
312
+ setContextIdentifierProvider = _React$useState6[1];
313
+ var _React$useState7 = React.useState(),
314
+ _React$useState8 = _slicedToArray(_React$useState7, 2),
315
+ viewMediaClientConfig = _React$useState8[0],
316
+ setViewMediaClientConfig = _React$useState8[1];
317
+ var mountedRef = React.useRef(true);
318
+ var pos = getPos();
319
+ var isSelected = selected();
320
+ var contentWidthForLegacyExperience = getMaxWidthForNestedNode(view, getPos()) || lineLength;
321
+ var mediaNode = useLatestMediaNode(nextMediaNode);
322
+ var mediaNodeUpdater = useMediaNodeUpdater({
323
+ mediaNode: mediaNode,
324
+ mediaSingleNodeProps: mediaSingleNodeNextProps,
325
+ mediaProvider: mediaProvider,
326
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent
327
+ });
328
+ useMediaAsyncOperations({
329
+ mediaNodeUpdater: mediaNodeUpdater,
330
+ getPos: getPos,
331
+ mediaNode: mediaNode,
332
+ addPendingTask: (mediaPluginState === null || mediaPluginState === void 0 ? void 0 : mediaPluginState.addPendingTask) || noop
333
+ });
334
+ React.useLayoutEffect(function () {
335
+ mountedRef.current = true;
336
+ return function () {
337
+ mountedRef.current = false;
338
+ };
339
+ }, []);
340
+ React.useLayoutEffect(function () {
341
+ if (!mediaProviderPromise) {
342
+ return;
343
+ }
344
+ mediaProviderPromise.then(function (resolvedProvider) {
345
+ var viewMediaClientConfig = resolvedProvider.viewMediaClientConfig;
346
+ if (mountedRef.current) {
347
+ setViewMediaClientConfig(viewMediaClientConfig);
348
+ setMediaProvider(resolvedProvider);
349
+ }
350
+ });
351
+ }, [mediaProviderPromise]);
352
+ React.useEffect(function () {
353
+ if (!contextIdentifierProviderPromise) {
354
+ return;
355
+ }
356
+ contextIdentifierProviderPromise.then(function (provider) {
357
+ if (mountedRef.current) {
358
+ setContextIdentifierProvider(provider);
359
+ }
360
+ });
361
+ }, [contextIdentifierProviderPromise]);
362
+ React.useEffect(function () {
363
+ var _mediaNode$firstChild;
364
+ // No-op but logging an exposure when an external image is rendered
365
+ // Remove this block when cleaning up platform_editor_add_media_from_url
366
+ if (((_mediaNode$firstChild = mediaNode.firstChild) === null || _mediaNode$firstChild === void 0 ? void 0 : _mediaNode$firstChild.attrs.type) === 'external') {
367
+ if (editorExperiment('add-media-from-url', true)) {
368
+ editorExperiment('add-media-from-url', true, {
369
+ exposure: true
370
+ });
371
+ } else {
372
+ editorExperiment('add-media-from-url', false, {
373
+ exposure: true
374
+ });
375
+ }
376
+ }
377
+ }, [mediaNode]);
378
+ var _ref7 = mediaNode.attrs,
379
+ layout = _ref7.layout,
380
+ widthType = _ref7.widthType,
381
+ mediaSingleWidthAttribute = _ref7.width;
382
+ var childNode = mediaNode.firstChild;
383
+ var childMediaNodeAttrs = React.useMemo(function () {
384
+ return (childNode === null || childNode === void 0 ? void 0 : childNode.attrs) || {};
385
+ }, [childNode]);
386
+ var _useMediaDimensionsLo = useMediaDimensionsLogic({
387
+ childMediaNodeAttrs: childMediaNodeAttrs
388
+ }),
389
+ width = _useMediaDimensionsLo.width,
390
+ height = _useMediaDimensionsLo.height;
391
+ var updateSize = useUpdateSizeCallback({
392
+ view: view,
393
+ getPos: getPos,
394
+ mediaNode: mediaNode
395
+ });
396
+ var canResize = React.useMemo(function () {
397
+ if (typeof pos !== 'number') {
398
+ return false;
399
+ }
400
+ var result = Boolean(!!mediaOptions.allowResizing && !editorDisabled && !editorViewMode);
401
+ if (mediaOptions.allowResizingInTables) {
402
+ return result;
403
+ }
404
+
405
+ // If resizing not allowed in tables, check parents for tables
406
+ var $pos = view.state.doc.resolve(pos);
407
+ var table = view.state.schema.nodes.table;
408
+ var disabledNode = !!findParentNodeOfTypeClosestToPos($pos, [table]);
409
+ return Boolean(result && !disabledNode);
410
+ }, [mediaOptions, pos, view, editorDisabled, editorViewMode]);
411
+ var badgeOffsetRight = React.useMemo(function () {
412
+ if (typeof pos !== 'number') {
413
+ return undefined;
414
+ }
415
+ var $pos = view.state.doc.resolve(pos);
416
+ var table = view.state.schema.nodes.table;
417
+ var foundTableNode = findParentNodeOfTypeClosestToPos($pos, [table]);
418
+ return foundTableNode ? '2px' : '14px';
419
+ }, [pos, view]);
420
+ var shouldShowPlaceholder = React.useMemo(function () {
421
+ var result = mediaOptions.allowCaptions && mediaNode.childCount !== 2 && isSelected && view.state.selection instanceof NodeSelection;
422
+ return !editorDisabled && result;
423
+ }, [editorDisabled, mediaOptions.allowCaptions, mediaNode, view, isSelected]);
424
+ var isInsideTable = React.useMemo(function () {
425
+ if (typeof pos !== 'number') {
426
+ return false;
427
+ }
428
+ return findParentNodeOfTypeClosestToPos(view.state.doc.resolve(pos), [view.state.schema.nodes.table]);
429
+ }, [pos, view]);
430
+ var currentMediaElement = React.useCallback(function () {
431
+ if (typeof pos !== 'number') {
432
+ return null;
433
+ }
434
+ var mediaNode = view.domAtPos(pos + 1).node;
435
+ return mediaNode instanceof HTMLElement ? mediaNode : null;
436
+ }, [view, pos]);
437
+ var mediaSingleWidth = React.useMemo(function () {
438
+ return calcMediaSinglePixelWidth({
439
+ width: mediaSingleWidthAttribute,
440
+ widthType: widthType,
441
+ origWidth: width,
442
+ layout: layout,
443
+ // This will only be used when calculating legacy media single width
444
+ // thus we use the legacy value (exclude table as container node)
445
+ contentWidth: contentWidthForLegacyExperience,
446
+ containerWidth: containerWidth,
447
+ gutterOffset: MEDIA_SINGLE_GUTTER_SIZE
448
+ });
449
+ }, [mediaSingleWidthAttribute, widthType, width, layout, contentWidthForLegacyExperience, containerWidth]);
450
+ var currentMaxWidth = isSelected ? pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$m = pluginInjectionApi.media) === null || _pluginInjectionApi$m === void 0 || (_pluginInjectionApi$m = _pluginInjectionApi$m.sharedState.currentState()) === null || _pluginInjectionApi$m === void 0 ? void 0 : _pluginInjectionApi$m.currentMaxWidth : undefined;
451
+ var contentWidth = currentMaxWidth || lineLength;
452
+ var isCurrentNodeDrafting = Boolean((annotationPluginState === null || annotationPluginState === void 0 ? void 0 : annotationPluginState.isDrafting) && (annotationPluginState === null || annotationPluginState === void 0 ? void 0 : annotationPluginState.targetNodeId) === (mediaNode === null || mediaNode === void 0 || (_mediaNode$firstChild2 = mediaNode.firstChild) === null || _mediaNode$firstChild2 === void 0 ? void 0 : _mediaNode$firstChild2.attrs.id));
453
+ var shouldShowExternalMediaBadge = childMediaNodeAttrs.type === 'external';
454
+ var mediaSingleWrapperRef = /*#__PURE__*/React.createRef();
455
+ var captionPlaceHolderRef = /*#__PURE__*/React.createRef();
456
+ var onMediaSingleClicked = React.useCallback(function (event) {
457
+ var _captionPlaceHolderRe;
458
+ // Workaround for iOS 16 Caption selection issue
459
+ // @see https://product-fabric.atlassian.net/browse/MEX-2012
460
+ if (!browser.ios) {
461
+ return;
462
+ }
463
+ if (mediaSingleWrapperRef.current !== event.target) {
464
+ return;
465
+ }
466
+ (_captionPlaceHolderRe = captionPlaceHolderRef.current) === null || _captionPlaceHolderRe === void 0 || _captionPlaceHolderRe.click();
467
+ }, [mediaSingleWrapperRef, captionPlaceHolderRef]);
468
+ var clickPlaceholder = React.useCallback(function () {
469
+ var _pluginInjectionApi$a;
470
+ if (typeof getPos === 'boolean') {
471
+ return;
472
+ }
473
+ insertAndSelectCaptionFromMediaSinglePos(pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 ? void 0 : _pluginInjectionApi$a.actions)(getPos(), mediaNode)(view.state, view.dispatch);
474
+ }, [view, getPos, mediaNode, pluginInjectionApi]);
475
+ var legacySize = React.useMemo(function () {
476
+ return {
477
+ width: mediaSingleWidthAttribute,
478
+ widthType: widthType
479
+ };
480
+ }, [widthType, mediaSingleWidthAttribute]);
481
+ var MediaChildren = jsx("figure", {
482
+ ref: mediaSingleWrapperRef,
483
+ css: figureWrapperStyles
484
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
485
+ ,
486
+ className: MediaSingleNodeSelector,
487
+ onClick: onMediaSingleClicked
488
+ }, editorExperiment('add-media-from-url', true) && jsx(MediaBadges, {
489
+ mediaElement: currentMediaElement(),
490
+ mediaHeight: height,
491
+ mediaWidth: width,
492
+ extendedResizeOffset: fg('platform.editor.media.extended-resize-experience') && !isInsideTable
493
+ }, function (_ref8) {
494
+ var badgeSize = _ref8.badgeSize;
495
+ return jsx(React.Fragment, null, shouldShowExternalMediaBadge && jsx(ExternalImageBadge, {
496
+ badgeSize: badgeSize
497
+ }), commentsOnMedia && jsx(CommentBadgeNextWrapper, {
498
+ view: view,
499
+ api: pluginInjectionApi,
500
+ mediaNode: mediaNode === null || mediaNode === void 0 ? void 0 : mediaNode.firstChild,
501
+ getPos: getPos,
502
+ isDrafting: isCurrentNodeDrafting,
503
+ badgeSize: badgeSize
504
+ }));
505
+ }), !editorExperiment('add-media-from-url', true) && commentsOnMedia && jsx(CommentBadge, {
506
+ view: view,
507
+ api: pluginInjectionApi,
508
+ mediaNode: mediaNode === null || mediaNode === void 0 ? void 0 : mediaNode.firstChild,
509
+ badgeOffsetRight: badgeOffsetRight,
510
+ getPos: getPos,
511
+ isDrafting: isCurrentNodeDrafting
512
+ }), jsx("div", {
513
+ ref: forwardRef
514
+ }), shouldShowPlaceholder && (editorExperiment('typography_migration_ugc', true) ? jsx(CaptionPlaceholderButton
515
+ // platform_editor_typography_migration_ugc clean up
516
+ // remove typecasting
517
+ , {
518
+ ref: captionPlaceHolderRef,
519
+ onClick: clickPlaceholder
520
+ }) : jsx(CaptionPlaceholder, {
521
+ ref: captionPlaceHolderRef,
522
+ onClick: clickPlaceholder
523
+ })));
524
+ return jsx(Fragment, null, canResize ? fg('platform.editor.media.extended-resize-experience') ? jsx(ResizableMediaSingleNext, {
525
+ view: view,
526
+ getPos: getPos,
527
+ updateSize: updateSize,
528
+ gridSize: 12,
529
+ viewMediaClientConfig: viewMediaClientConfig,
530
+ allowBreakoutSnapPoints: mediaOptions && mediaOptions.allowBreakoutSnapPoints,
531
+ selected: isSelected,
532
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
533
+ pluginInjectionApi: pluginInjectionApi,
534
+ layout: layout,
535
+ width: width,
536
+ height: height,
537
+ containerWidth: containerWidth,
538
+ lineLength: contentWidth || FALLBACK_MOST_COMMON_WIDTH,
539
+ fullWidthMode: fullWidthMode,
540
+ hasFallbackContainer: false,
541
+ mediaSingleWidth: mediaSingleWidth,
542
+ editorAppearance: editorAppearance,
543
+ showLegacyNotification: widthType !== 'pixel'
544
+ }, MediaChildren) : jsx(ResizableMediaSingle, {
545
+ view: view,
546
+ getPos: getPos,
547
+ updateSize: updateSize,
548
+ gridSize: 12,
549
+ viewMediaClientConfig: viewMediaClientConfig,
550
+ allowBreakoutSnapPoints: mediaOptions && mediaOptions.allowBreakoutSnapPoints,
551
+ selected: isSelected,
552
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
553
+ pluginInjectionApi: pluginInjectionApi,
554
+ layout: layout,
555
+ width: width,
556
+ height: height,
557
+ containerWidth: containerWidth,
558
+ fullWidthMode: fullWidthMode,
559
+ hasFallbackContainer: false,
560
+ mediaSingleWidth: mediaSingleWidth,
561
+ editorAppearance: editorAppearance,
562
+ lineLength: contentWidthForLegacyExperience || FALLBACK_MOST_COMMON_WIDTH,
563
+ pctWidth: mediaSingleWidthAttribute
564
+ }, MediaChildren) : jsx(MediaSingle, {
565
+ layout: layout,
566
+ width: width,
567
+ height: height,
568
+ containerWidth: containerWidth,
569
+ fullWidthMode: fullWidthMode,
570
+ hasFallbackContainer: false,
571
+ editorAppearance: editorAppearance,
572
+ pctWidth: mediaSingleWidthAttribute,
573
+ lineLength: lineLength || FALLBACK_MOST_COMMON_WIDTH,
574
+ size: legacySize
575
+ }, MediaChildren));
576
+ };