@atlaskit/editor-plugin-media 9.1.1 → 9.3.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 (29) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cjs/mediaPlugin.js +7 -5
  3. package/dist/cjs/nodeviews/mediaSingleNext.js +46 -0
  4. package/dist/cjs/pm-plugins/alt-text/ui/AltTextEdit.js +1 -1
  5. package/dist/cjs/pm-plugins/commands.js +7 -1
  6. package/dist/cjs/pm-plugins/utils/media-single.js +103 -4
  7. package/dist/cjs/ui/ResizableMediaSingle/ResizableMediaSingleNext.js +15 -5
  8. package/dist/cjs/ui/ResizableMediaSingle/index.js +6 -0
  9. package/dist/es2019/mediaPlugin.js +8 -6
  10. package/dist/es2019/nodeviews/mediaSingleNext.js +46 -0
  11. package/dist/es2019/pm-plugins/alt-text/ui/AltTextEdit.js +1 -1
  12. package/dist/es2019/pm-plugins/commands.js +3 -1
  13. package/dist/es2019/pm-plugins/utils/media-single.js +105 -1
  14. package/dist/es2019/ui/ResizableMediaSingle/ResizableMediaSingleNext.js +15 -5
  15. package/dist/es2019/ui/ResizableMediaSingle/index.js +6 -0
  16. package/dist/esm/mediaPlugin.js +8 -6
  17. package/dist/esm/nodeviews/mediaSingleNext.js +46 -0
  18. package/dist/esm/pm-plugins/alt-text/ui/AltTextEdit.js +1 -1
  19. package/dist/esm/pm-plugins/commands.js +6 -0
  20. package/dist/esm/pm-plugins/utils/media-single.js +103 -4
  21. package/dist/esm/ui/ResizableMediaSingle/ResizableMediaSingleNext.js +15 -5
  22. package/dist/esm/ui/ResizableMediaSingle/index.js +6 -0
  23. package/dist/types/mediaPluginType.d.ts +15 -0
  24. package/dist/types/pm-plugins/commands.d.ts +2 -0
  25. package/dist/types/pm-plugins/utils/media-single.d.ts +3 -1
  26. package/dist/types-ts4.5/mediaPluginType.d.ts +15 -0
  27. package/dist/types-ts4.5/pm-plugins/commands.d.ts +2 -0
  28. package/dist/types-ts4.5/pm-plugins/utils/media-single.d.ts +3 -1
  29. package/package.json +9 -6
@@ -2,10 +2,11 @@ import memoizeOne from 'memoize-one';
2
2
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
3
3
  import { safeInsert, shouldSplitSelectedNodeOnNodeInsertion } from '@atlaskit/editor-common/insert';
4
4
  import { DEFAULT_IMAGE_WIDTH, getMaxWidthForNestedNodeNext, getMediaSingleInitialWidth, MEDIA_SINGLE_DEFAULT_MIN_PIXEL_WIDTH, MEDIA_SINGLE_VIDEO_MIN_PIXEL_WIDTH } from '@atlaskit/editor-common/media-single';
5
- import { atTheBeginningOfBlock } from '@atlaskit/editor-common/selection';
5
+ import { atTheBeginningOfBlock, selectionIsAtTheBeginningOfBlock } from '@atlaskit/editor-common/selection';
6
6
  import { checkNodeDown, isEmptyParagraph } from '@atlaskit/editor-common/utils';
7
7
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
8
8
  import { safeInsert as pmSafeInsert, removeSelectedNode } from '@atlaskit/editor-prosemirror/utils';
9
+ import { fg } from '@atlaskit/platform-feature-flags';
9
10
  import { copyOptionalAttrsFromMediaState } from '../utils/media-common';
10
11
  import { findChangeFromLocation, getChangeMediaAnalytics } from './analytics';
11
12
  import { isImage } from './is-type';
@@ -34,6 +35,21 @@ function insertNodesWithOptionalParagraph({
34
35
  const {
35
36
  tr
36
37
  } = state;
38
+ if (fg('platform_editor_introduce_insert_media_command')) {
39
+ const updatedTr = insertNodesWithOptionalParagraphCommand({
40
+ nodes,
41
+ analyticsAttributes,
42
+ editorAnalyticsAPI,
43
+ insertMediaVia
44
+ })({
45
+ tr
46
+ });
47
+ if (updatedTr && dispatch) {
48
+ dispatch === null || dispatch === void 0 ? void 0 : dispatch(updatedTr);
49
+ return true;
50
+ }
51
+ return false;
52
+ }
37
53
  const {
38
54
  inputMethod,
39
55
  fileExtension,
@@ -68,6 +84,46 @@ function insertNodesWithOptionalParagraph({
68
84
  return true;
69
85
  };
70
86
  }
87
+ function insertNodesWithOptionalParagraphCommand({
88
+ nodes,
89
+ analyticsAttributes = {},
90
+ editorAnalyticsAPI,
91
+ insertMediaVia
92
+ }) {
93
+ return ({
94
+ tr
95
+ }) => {
96
+ const {
97
+ inputMethod,
98
+ fileExtension,
99
+ newType,
100
+ previousType
101
+ } = analyticsAttributes;
102
+ let updatedTr = tr;
103
+ const openEnd = 0;
104
+ if (tr.selection.empty) {
105
+ const insertFrom = selectionIsAtTheBeginningOfBlock(tr.selection) ? tr.selection.$from.before() : tr.selection.from;
106
+
107
+ // the use of pmSafeInsert causes the node selection to media single node.
108
+ // It leads to discrepancy between the full-page and comment editor - not sure why :shrug:
109
+ // When multiple images are uploaded, the node selection is set to the previous node
110
+ // and got overridden by the next node inserted.
111
+ // It also causes the images position shifted when the images are uploaded.
112
+ // E.g the images are uploaded after a table, the images will be inserted inside the table.
113
+ // so we revert to use tr.insert instead. No extra paragraph is added.
114
+ updatedTr = updatedTr.insert(insertFrom, nodes);
115
+ } else {
116
+ updatedTr.replaceSelection(new Slice(Fragment.from(nodes), 0, openEnd));
117
+ }
118
+ if (inputMethod) {
119
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent(getInsertMediaAnalytics(inputMethod, fileExtension, insertMediaVia))(updatedTr);
120
+ }
121
+ if (newType && previousType) {
122
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent(getChangeMediaAnalytics(previousType, newType, findChangeFromLocation(tr.selection)))(updatedTr);
123
+ }
124
+ return updatedTr;
125
+ };
126
+ }
71
127
  export const isMediaSingle = (schema, fileMimeType) => !!schema.nodes.mediaSingle && isImage(fileMimeType);
72
128
  export const insertMediaAsMediaSingle = (view, node, inputMethod, editorAnalyticsAPI, insertMediaVia, allowPixelResizing) => {
73
129
  var _node$attrs$width;
@@ -87,6 +143,16 @@ export const insertMediaAsMediaSingle = (view, node, inputMethod, editorAnalytic
87
143
  if (node.type !== media || !isImage(node.attrs.__fileMimeType) && node.attrs.type !== 'external') {
88
144
  return false;
89
145
  }
146
+ if (fg('platform_editor_introduce_insert_media_command')) {
147
+ const updatedTr = createInsertMediaAsMediaSingleCommand(node.attrs, inputMethod, editorAnalyticsAPI, insertMediaVia, allowPixelResizing)({
148
+ tr: state.tr
149
+ });
150
+ if (updatedTr && dispatch) {
151
+ dispatch === null || dispatch === void 0 ? void 0 : dispatch(updatedTr);
152
+ return true;
153
+ }
154
+ return false;
155
+ }
90
156
  const mediaSingleAttrs = allowPixelResizing ? {
91
157
  widthType: 'pixel',
92
158
  width: getMediaSingleInitialWidth((_node$attrs$width = node.attrs.width) !== null && _node$attrs$width !== void 0 ? _node$attrs$width : DEFAULT_IMAGE_WIDTH),
@@ -105,6 +171,44 @@ export const insertMediaAsMediaSingle = (view, node, inputMethod, editorAnalytic
105
171
  insertMediaVia
106
172
  })(state, dispatch);
107
173
  };
174
+ export const createInsertMediaAsMediaSingleCommand = (mediaAttrs, inputMethod, editorAnalyticsAPI, insertMediaVia, allowPixelResizing) => {
175
+ return ({
176
+ tr
177
+ }) => {
178
+ var _mediaAttrs$__fileMim, _mediaAttrs$width, _mediaAttrs$__fileMim2;
179
+ const {
180
+ mediaSingle,
181
+ media
182
+ } = tr.doc.type.schema.nodes;
183
+ if (!mediaSingle || !media) {
184
+ return null;
185
+ }
186
+ if (mediaAttrs.type !== 'external' && !isImage((_mediaAttrs$__fileMim = mediaAttrs.__fileMimeType) !== null && _mediaAttrs$__fileMim !== void 0 ? _mediaAttrs$__fileMim : undefined)) {
187
+ return null;
188
+ }
189
+ const mediaSingleAttrs = allowPixelResizing ? {
190
+ widthType: 'pixel',
191
+ width: getMediaSingleInitialWidth((_mediaAttrs$width = mediaAttrs.width) !== null && _mediaAttrs$width !== void 0 ? _mediaAttrs$width : DEFAULT_IMAGE_WIDTH),
192
+ layout: 'center'
193
+ } : {};
194
+ const mediaNode = media.create(mediaAttrs);
195
+ const mediaSingleNode = mediaSingle.create(mediaSingleAttrs, mediaNode);
196
+ const nodes = [mediaSingleNode];
197
+ const analyticsAttributes = {
198
+ inputMethod,
199
+ // External images have no file extension
200
+ fileExtension: mediaAttrs.type !== 'external' && mediaAttrs.__fileMimeType ? (_mediaAttrs$__fileMim2 = mediaAttrs.__fileMimeType) !== null && _mediaAttrs$__fileMim2 !== void 0 ? _mediaAttrs$__fileMim2 : undefined : undefined
201
+ };
202
+ return insertNodesWithOptionalParagraphCommand({
203
+ nodes,
204
+ analyticsAttributes,
205
+ editorAnalyticsAPI,
206
+ insertMediaVia
207
+ })({
208
+ tr
209
+ });
210
+ };
211
+ };
108
212
  const getFileExtension = fileName => {
109
213
  if (fileName) {
110
214
  const extensionIdx = fileName.lastIndexOf('.');
@@ -161,7 +161,8 @@ export const ResizableMediaSingleNextFunctional = props => {
161
161
  updateSize,
162
162
  view,
163
163
  viewMediaClientConfig,
164
- forceHandlePositioning
164
+ forceHandlePositioning,
165
+ disableHandles
165
166
  } = props;
166
167
  const initialWidth = useMemo(() => {
167
168
  return mediaSingleWidth || DEFAULT_IMAGE_WIDTH;
@@ -224,6 +225,9 @@ export const ResizableMediaSingleNextFunctional = props => {
224
225
  if (isAdjacentMode || fullWidthMode) {
225
226
  return lineLength;
226
227
  }
228
+ if (!isResizing && expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
229
+ return `var(--ak-editor-max-container-width)`;
230
+ }
227
231
  return calcMaxWidth({
228
232
  containerWidth,
229
233
  editorAppearance
@@ -239,7 +243,7 @@ export const ResizableMediaSingleNextFunctional = props => {
239
243
  const resizerNextClassName = useMemo(() => {
240
244
  if (expValEquals('platform_editor_resizer_styles_cleanup', 'isEnabled', true)) {
241
245
  const classNameNext = classnames(richMediaClassName, `image-${layout}`, isResizing ? 'is-resizing' : 'not-resizing', className, resizerItemClassName, {
242
- 'display-handle': selected,
246
+ 'display-handle': expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true) ? selected && !disableHandles : selected,
243
247
  'richMedia-selected': selected,
244
248
  'rich-media-wrapped': layout === 'wrap-left' || layout === 'wrap-right'
245
249
  });
@@ -251,7 +255,7 @@ export const ResizableMediaSingleNextFunctional = props => {
251
255
  'rich-media-wrapped': layout === 'wrap-left' || layout === 'wrap-right'
252
256
  });
253
257
  return classnames(classNameNext, resizerStyles);
254
- }, [className, isResizing, layout, selected]);
258
+ }, [className, disableHandles, isResizing, layout, selected]);
255
259
  const isInsideInlineLike = useMemo(() => {
256
260
  if (nodePosition === null) {
257
261
  return false;
@@ -263,6 +267,12 @@ export const ResizableMediaSingleNextFunctional = props => {
263
267
  return !!findParentNodeOfTypeClosestToPos($pos, [listItem]);
264
268
  }, [nodePosition, view]);
265
269
  const enable = useMemo(() => {
270
+ if (disableHandles && expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
271
+ return {
272
+ left: false,
273
+ right: false
274
+ };
275
+ }
266
276
  return handleSides.reduce((acc, side) => {
267
277
  const oppositeSide = side === 'left' ? 'right' : 'left';
268
278
  acc[side] = nonWrappedLayouts.concat(`wrap-${oppositeSide}`).concat(`align-${imageAlignmentMap[oppositeSide]}`).indexOf(layout) > -1;
@@ -271,7 +281,7 @@ export const ResizableMediaSingleNextFunctional = props => {
271
281
  }
272
282
  return acc;
273
283
  }, {});
274
- }, [layout, isInsideInlineLike]);
284
+ }, [disableHandles, layout, isInsideInlineLike]);
275
285
  const defaultGuidelines = useMemo(() => {
276
286
  if (isAdjacentMode) {
277
287
  return [];
@@ -499,7 +509,7 @@ export const ResizableMediaSingleNextFunctional = props => {
499
509
  snap: snaps,
500
510
  resizeRatio: nonWrappedLayouts.includes(layout) ? 2 : 1,
501
511
  "data-testid": resizerNextTestId,
502
- isHandleVisible: selected,
512
+ isHandleVisible: expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true) ? selected && !disableHandles : selected,
503
513
  handlePositioning: handlePositioning,
504
514
  handleHighlight: "full-height"
505
515
  }, children, showLegacyNotification && jsx(ResizableMediaMigrationNotification, null)));
@@ -296,6 +296,12 @@ export default class ResizableMediaSingle extends React.Component {
296
296
  const enable = {};
297
297
  handleSides.forEach(side => {
298
298
  const oppositeSide = side === 'left' ? 'right' : 'left';
299
+ if (expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
300
+ if (this.props.disableHandles) {
301
+ enable[side] = false;
302
+ return;
303
+ }
304
+ }
299
305
  enable[side] = ['full-width', 'wide', 'center'].concat(`wrap-${oppositeSide}`).concat(`align-${imageAlignmentMap[oppositeSide]}`).indexOf(layout) > -1;
300
306
  if (side === 'left' && this.insideInlineLike) {
301
307
  enable[side] = false;
@@ -16,7 +16,7 @@ import { mediaInlineSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/mediaInli
16
16
  import { mediaSingleSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/mediaSingle';
17
17
  import { createPlugin as createMediaAltTextPlugin } from './pm-plugins/alt-text';
18
18
  import keymapMediaAltTextPlugin from './pm-plugins/alt-text/keymap';
19
- import { hideMediaViewer, showMediaViewer, trackMediaPaste } from './pm-plugins/commands';
19
+ import { hideMediaViewer, insertMediaAsMediaSingleCommand, showMediaViewer, trackMediaPaste } from './pm-plugins/commands';
20
20
  import keymapPlugin from './pm-plugins/keymap';
21
21
  import keymapMediaSinglePlugin from './pm-plugins/keymap-media';
22
22
  import linkingPlugin from './pm-plugins/linking';
@@ -96,6 +96,7 @@ var MediaViewerFunctionalComponent = function MediaViewerFunctionalComponent(_re
96
96
  });
97
97
  };
98
98
  export var mediaPlugin = function mediaPlugin(_ref3) {
99
+ var _api$analytics3;
99
100
  var _ref3$config = _ref3.config,
100
101
  options = _ref3$config === void 0 ? {} : _ref3$config,
101
102
  api = _ref3.api;
@@ -150,7 +151,8 @@ export var mediaPlugin = function mediaPlugin(_ref3) {
150
151
  commands: {
151
152
  showMediaViewer: showMediaViewer,
152
153
  hideMediaViewer: hideMediaViewer,
153
- trackMediaPaste: trackMediaPaste
154
+ trackMediaPaste: trackMediaPaste,
155
+ insertMediaSingle: insertMediaAsMediaSingleCommand(api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions, options.allowPixelResizing)
154
156
  },
155
157
  nodes: function nodes() {
156
158
  var _ref5 = options || {},
@@ -225,9 +227,9 @@ export var mediaPlugin = function mediaPlugin(_ref3) {
225
227
  }, {
226
228
  name: 'mediaKeymap',
227
229
  plugin: function plugin(_ref7) {
228
- var _api$analytics3, _api$selection;
230
+ var _api$analytics4, _api$selection;
229
231
  var getIntl = _ref7.getIntl;
230
- return keymapPlugin(options, api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions, api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 ? void 0 : _api$selection.actions, api === null || api === void 0 ? void 0 : api.width, getIntl);
232
+ return keymapPlugin(options, api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions, api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 ? void 0 : _api$selection.actions, api === null || api === void 0 ? void 0 : api.width, getIntl);
231
233
  }
232
234
  }];
233
235
  if (options && options.allowMediaSingle) {
@@ -247,9 +249,9 @@ export var mediaPlugin = function mediaPlugin(_ref3) {
247
249
  pmPlugins.push({
248
250
  name: 'mediaAltTextKeymap',
249
251
  plugin: function plugin(_ref9) {
250
- var _api$analytics4;
252
+ var _api$analytics5;
251
253
  var schema = _ref9.schema;
252
- return keymapMediaAltTextPlugin(schema, api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions);
254
+ return keymapMediaAltTextPlugin(schema, api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions);
253
255
  }
254
256
  });
255
257
  }
@@ -492,6 +492,52 @@ export var MediaSingleNodeNext = function MediaSingleNodeNext(mediaSingleNodeNex
492
492
  onClick: clickPlaceholder,
493
493
  placeholderMessage: mediaOptions.allowImagePreview ? captionMessages.placeholderWithDoubleClickPrompt : captionMessages.placeholder
494
494
  })));
495
+ if (expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
496
+ return mediaOptions.allowPixelResizing ? jsx(ResizableMediaSingleNext, {
497
+ view: view,
498
+ getPos: getPos,
499
+ updateSize: updateSize,
500
+ gridSize: 12,
501
+ viewMediaClientConfig: viewMediaClientConfig,
502
+ allowBreakoutSnapPoints: mediaOptions && mediaOptions.allowBreakoutSnapPoints,
503
+ selected: isSelected,
504
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
505
+ pluginInjectionApi: pluginInjectionApi,
506
+ layout: layout,
507
+ width: width,
508
+ height: height,
509
+ containerWidth: containerWidth,
510
+ lineLength: contentWidth || FALLBACK_MOST_COMMON_WIDTH,
511
+ fullWidthMode: fullWidthMode,
512
+ hasFallbackContainer: false,
513
+ mediaSingleWidth: mediaSingleWidth,
514
+ editorAppearance: editorAppearance,
515
+ showLegacyNotification: widthType !== 'pixel',
516
+ forceHandlePositioning: mediaOptions === null || mediaOptions === void 0 ? void 0 : mediaOptions.forceHandlePositioning,
517
+ disableHandles: !canResize
518
+ }, MediaChildren) : jsx(ResizableMediaSingle, {
519
+ view: view,
520
+ getPos: getPos,
521
+ updateSize: updateSize,
522
+ gridSize: 12,
523
+ viewMediaClientConfig: viewMediaClientConfig,
524
+ allowBreakoutSnapPoints: mediaOptions && mediaOptions.allowBreakoutSnapPoints,
525
+ selected: isSelected,
526
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
527
+ pluginInjectionApi: pluginInjectionApi,
528
+ layout: layout,
529
+ width: width,
530
+ height: height,
531
+ containerWidth: containerWidth,
532
+ fullWidthMode: fullWidthMode,
533
+ hasFallbackContainer: false,
534
+ mediaSingleWidth: mediaSingleWidth,
535
+ editorAppearance: editorAppearance,
536
+ lineLength: contentWidthForLegacyExperience || FALLBACK_MOST_COMMON_WIDTH,
537
+ pctWidth: mediaSingleWidthAttribute,
538
+ disableHandles: !canResize
539
+ }, MediaChildren);
540
+ }
495
541
  return jsx(Fragment, null, canResize ? mediaOptions.allowPixelResizing ? jsx(ResizableMediaSingleNext, {
496
542
  view: view,
497
543
  getPos: getPos,
@@ -24,7 +24,7 @@ import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-che
24
24
  import { RECENT_SEARCH_WIDTH_IN_PX as CONTAINER_WIDTH_IN_PX, FloatingToolbarButton as Button, ErrorMessage, PanelTextInput } from '@atlaskit/editor-common/ui';
25
25
  import { relativeFontSizeToBase16 } from '@atlaskit/editor-shared-styles';
26
26
  import ChevronLeftLargeIcon from '@atlaskit/icon/core/chevron-left';
27
- import CrossCircleIcon from '@atlaskit/icon/core/migration/cross-circle';
27
+ import CrossCircleIcon from '@atlaskit/icon/core/cross-circle';
28
28
  import { N200, N30, N80, R400 } from '@atlaskit/theme/colors';
29
29
  import { closeMediaAltTextMenu, closeMediaAltTextMenuAndSave } from '../commands';
30
30
  export var MAX_ALT_TEXT_LENGTH = 510; // double tweet length
@@ -1,6 +1,7 @@
1
1
  import { ACTIONS } from '../pm-plugins/actions';
2
2
  import { stateKey } from '../pm-plugins/plugin-key';
3
3
  import { getIdentifier } from '../pm-plugins/utils/media-common';
4
+ import { createInsertMediaAsMediaSingleCommand } from './utils/media-single';
4
5
  export var showMediaViewer = function showMediaViewer(media) {
5
6
  return function (_ref) {
6
7
  var tr = _ref.tr;
@@ -31,4 +32,9 @@ export var trackMediaPaste = function trackMediaPaste(attrs) {
31
32
  });
32
33
  return tr;
33
34
  };
35
+ };
36
+ export var insertMediaAsMediaSingleCommand = function insertMediaAsMediaSingleCommand(editorAnalyticsAPI, allowPixelResizing) {
37
+ return function (mediaAttrs, inputMethod, insertMediaVia) {
38
+ return createInsertMediaAsMediaSingleCommand(mediaAttrs, inputMethod, editorAnalyticsAPI, insertMediaVia, allowPixelResizing);
39
+ };
34
40
  };
@@ -5,10 +5,11 @@ import memoizeOne from 'memoize-one';
5
5
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
6
6
  import { safeInsert, shouldSplitSelectedNodeOnNodeInsertion } from '@atlaskit/editor-common/insert';
7
7
  import { DEFAULT_IMAGE_WIDTH, getMaxWidthForNestedNodeNext, getMediaSingleInitialWidth, MEDIA_SINGLE_DEFAULT_MIN_PIXEL_WIDTH, MEDIA_SINGLE_VIDEO_MIN_PIXEL_WIDTH } from '@atlaskit/editor-common/media-single';
8
- import { atTheBeginningOfBlock } from '@atlaskit/editor-common/selection';
8
+ import { atTheBeginningOfBlock, selectionIsAtTheBeginningOfBlock } from '@atlaskit/editor-common/selection';
9
9
  import { checkNodeDown, isEmptyParagraph } from '@atlaskit/editor-common/utils';
10
10
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
11
11
  import { safeInsert as pmSafeInsert, removeSelectedNode } from '@atlaskit/editor-prosemirror/utils';
12
+ import { fg } from '@atlaskit/platform-feature-flags';
12
13
  import { copyOptionalAttrsFromMediaState } from '../utils/media-common';
13
14
  import { findChangeFromLocation, getChangeMediaAnalytics } from './analytics';
14
15
  import { isImage } from './is-type';
@@ -37,6 +38,21 @@ function insertNodesWithOptionalParagraph(_ref) {
37
38
  insertMediaVia = _ref.insertMediaVia;
38
39
  return function (state, dispatch) {
39
40
  var tr = state.tr;
41
+ if (fg('platform_editor_introduce_insert_media_command')) {
42
+ var _updatedTr = insertNodesWithOptionalParagraphCommand({
43
+ nodes: nodes,
44
+ analyticsAttributes: analyticsAttributes,
45
+ editorAnalyticsAPI: editorAnalyticsAPI,
46
+ insertMediaVia: insertMediaVia
47
+ })({
48
+ tr: tr
49
+ });
50
+ if (_updatedTr && dispatch) {
51
+ dispatch === null || dispatch === void 0 || dispatch(_updatedTr);
52
+ return true;
53
+ }
54
+ return false;
55
+ }
40
56
  var inputMethod = analyticsAttributes.inputMethod,
41
57
  fileExtension = analyticsAttributes.fileExtension,
42
58
  newType = analyticsAttributes.newType,
@@ -69,6 +85,43 @@ function insertNodesWithOptionalParagraph(_ref) {
69
85
  return true;
70
86
  };
71
87
  }
88
+ function insertNodesWithOptionalParagraphCommand(_ref2) {
89
+ var nodes = _ref2.nodes,
90
+ _ref2$analyticsAttrib = _ref2.analyticsAttributes,
91
+ analyticsAttributes = _ref2$analyticsAttrib === void 0 ? {} : _ref2$analyticsAttrib,
92
+ editorAnalyticsAPI = _ref2.editorAnalyticsAPI,
93
+ insertMediaVia = _ref2.insertMediaVia;
94
+ return function (_ref3) {
95
+ var tr = _ref3.tr;
96
+ var inputMethod = analyticsAttributes.inputMethod,
97
+ fileExtension = analyticsAttributes.fileExtension,
98
+ newType = analyticsAttributes.newType,
99
+ previousType = analyticsAttributes.previousType;
100
+ var updatedTr = tr;
101
+ var openEnd = 0;
102
+ if (tr.selection.empty) {
103
+ var insertFrom = selectionIsAtTheBeginningOfBlock(tr.selection) ? tr.selection.$from.before() : tr.selection.from;
104
+
105
+ // the use of pmSafeInsert causes the node selection to media single node.
106
+ // It leads to discrepancy between the full-page and comment editor - not sure why :shrug:
107
+ // When multiple images are uploaded, the node selection is set to the previous node
108
+ // and got overridden by the next node inserted.
109
+ // It also causes the images position shifted when the images are uploaded.
110
+ // E.g the images are uploaded after a table, the images will be inserted inside the table.
111
+ // so we revert to use tr.insert instead. No extra paragraph is added.
112
+ updatedTr = updatedTr.insert(insertFrom, nodes);
113
+ } else {
114
+ updatedTr.replaceSelection(new Slice(Fragment.from(nodes), 0, openEnd));
115
+ }
116
+ if (inputMethod) {
117
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(getInsertMediaAnalytics(inputMethod, fileExtension, insertMediaVia))(updatedTr);
118
+ }
119
+ if (newType && previousType) {
120
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(getChangeMediaAnalytics(previousType, newType, findChangeFromLocation(tr.selection)))(updatedTr);
121
+ }
122
+ return updatedTr;
123
+ };
124
+ }
72
125
  export var isMediaSingle = function isMediaSingle(schema, fileMimeType) {
73
126
  return !!schema.nodes.mediaSingle && isImage(fileMimeType);
74
127
  };
@@ -87,6 +140,16 @@ export var insertMediaAsMediaSingle = function insertMediaAsMediaSingle(view, no
87
140
  if (node.type !== media || !isImage(node.attrs.__fileMimeType) && node.attrs.type !== 'external') {
88
141
  return false;
89
142
  }
143
+ if (fg('platform_editor_introduce_insert_media_command')) {
144
+ var updatedTr = createInsertMediaAsMediaSingleCommand(node.attrs, inputMethod, editorAnalyticsAPI, insertMediaVia, allowPixelResizing)({
145
+ tr: state.tr
146
+ });
147
+ if (updatedTr && dispatch) {
148
+ dispatch === null || dispatch === void 0 || dispatch(updatedTr);
149
+ return true;
150
+ }
151
+ return false;
152
+ }
90
153
  var mediaSingleAttrs = allowPixelResizing ? {
91
154
  widthType: 'pixel',
92
155
  width: getMediaSingleInitialWidth((_node$attrs$width = node.attrs.width) !== null && _node$attrs$width !== void 0 ? _node$attrs$width : DEFAULT_IMAGE_WIDTH),
@@ -105,6 +168,42 @@ export var insertMediaAsMediaSingle = function insertMediaAsMediaSingle(view, no
105
168
  insertMediaVia: insertMediaVia
106
169
  })(state, dispatch);
107
170
  };
171
+ export var createInsertMediaAsMediaSingleCommand = function createInsertMediaAsMediaSingleCommand(mediaAttrs, inputMethod, editorAnalyticsAPI, insertMediaVia, allowPixelResizing) {
172
+ return function (_ref4) {
173
+ var _mediaAttrs$__fileMim, _mediaAttrs$width, _mediaAttrs$__fileMim2;
174
+ var tr = _ref4.tr;
175
+ var _tr$doc$type$schema$n = tr.doc.type.schema.nodes,
176
+ mediaSingle = _tr$doc$type$schema$n.mediaSingle,
177
+ media = _tr$doc$type$schema$n.media;
178
+ if (!mediaSingle || !media) {
179
+ return null;
180
+ }
181
+ if (mediaAttrs.type !== 'external' && !isImage((_mediaAttrs$__fileMim = mediaAttrs.__fileMimeType) !== null && _mediaAttrs$__fileMim !== void 0 ? _mediaAttrs$__fileMim : undefined)) {
182
+ return null;
183
+ }
184
+ var mediaSingleAttrs = allowPixelResizing ? {
185
+ widthType: 'pixel',
186
+ width: getMediaSingleInitialWidth((_mediaAttrs$width = mediaAttrs.width) !== null && _mediaAttrs$width !== void 0 ? _mediaAttrs$width : DEFAULT_IMAGE_WIDTH),
187
+ layout: 'center'
188
+ } : {};
189
+ var mediaNode = media.create(mediaAttrs);
190
+ var mediaSingleNode = mediaSingle.create(mediaSingleAttrs, mediaNode);
191
+ var nodes = [mediaSingleNode];
192
+ var analyticsAttributes = {
193
+ inputMethod: inputMethod,
194
+ // External images have no file extension
195
+ fileExtension: mediaAttrs.type !== 'external' && mediaAttrs.__fileMimeType ? (_mediaAttrs$__fileMim2 = mediaAttrs.__fileMimeType) !== null && _mediaAttrs$__fileMim2 !== void 0 ? _mediaAttrs$__fileMim2 : undefined : undefined
196
+ };
197
+ return insertNodesWithOptionalParagraphCommand({
198
+ nodes: nodes,
199
+ analyticsAttributes: analyticsAttributes,
200
+ editorAnalyticsAPI: editorAnalyticsAPI,
201
+ insertMediaVia: insertMediaVia
202
+ })({
203
+ tr: tr
204
+ });
205
+ };
206
+ };
108
207
  var getFileExtension = function getFileExtension(fileName) {
109
208
  if (fileName) {
110
209
  var extensionIdx = fileName.lastIndexOf('.');
@@ -218,12 +317,12 @@ var createMediaSingleNode = function createMediaSingleNode(schema, collection, m
218
317
  _mediaState$scaleFact = mediaState.scaleFactor,
219
318
  scaleFactor = _mediaState$scaleFact === void 0 ? 1 : _mediaState$scaleFact,
220
319
  fileName = mediaState.fileName;
221
- var _ref2 = dimensions || {
320
+ var _ref5 = dimensions || {
222
321
  height: undefined,
223
322
  width: undefined
224
323
  },
225
- width = _ref2.width,
226
- height = _ref2.height;
324
+ width = _ref5.width,
325
+ height = _ref5.height;
227
326
  var _schema$nodes = schema.nodes,
228
327
  media = _schema$nodes.media,
229
328
  mediaSingle = _schema$nodes.mediaSingle;
@@ -160,7 +160,8 @@ export var ResizableMediaSingleNextFunctional = function ResizableMediaSingleNex
160
160
  updateSize = props.updateSize,
161
161
  view = props.view,
162
162
  viewMediaClientConfig = props.viewMediaClientConfig,
163
- forceHandlePositioning = props.forceHandlePositioning;
163
+ forceHandlePositioning = props.forceHandlePositioning,
164
+ disableHandles = props.disableHandles;
164
165
  var initialWidth = useMemo(function () {
165
166
  return mediaSingleWidth || DEFAULT_IMAGE_WIDTH;
166
167
  }, [mediaSingleWidth]);
@@ -237,6 +238,9 @@ export var ResizableMediaSingleNextFunctional = function ResizableMediaSingleNex
237
238
  if (isAdjacentMode || fullWidthMode) {
238
239
  return lineLength;
239
240
  }
241
+ if (!isResizing && expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
242
+ return "var(--ak-editor-max-container-width)";
243
+ }
240
244
  return calcMaxWidth({
241
245
  containerWidth: containerWidth,
242
246
  editorAppearance: editorAppearance
@@ -252,7 +256,7 @@ export var ResizableMediaSingleNextFunctional = function ResizableMediaSingleNex
252
256
  var resizerNextClassName = useMemo(function () {
253
257
  if (expValEquals('platform_editor_resizer_styles_cleanup', 'isEnabled', true)) {
254
258
  var _classNameNext = classnames(richMediaClassName, "image-".concat(layout), isResizing ? 'is-resizing' : 'not-resizing', className, resizerItemClassName, {
255
- 'display-handle': selected,
259
+ 'display-handle': expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true) ? selected && !disableHandles : selected,
256
260
  'richMedia-selected': selected,
257
261
  'rich-media-wrapped': layout === 'wrap-left' || layout === 'wrap-right'
258
262
  });
@@ -264,7 +268,7 @@ export var ResizableMediaSingleNextFunctional = function ResizableMediaSingleNex
264
268
  'rich-media-wrapped': layout === 'wrap-left' || layout === 'wrap-right'
265
269
  });
266
270
  return classnames(classNameNext, resizerStyles);
267
- }, [className, isResizing, layout, selected]);
271
+ }, [className, disableHandles, isResizing, layout, selected]);
268
272
  var isInsideInlineLike = useMemo(function () {
269
273
  if (nodePosition === null) {
270
274
  return false;
@@ -274,6 +278,12 @@ export var ResizableMediaSingleNextFunctional = function ResizableMediaSingleNex
274
278
  return !!findParentNodeOfTypeClosestToPos($pos, [listItem]);
275
279
  }, [nodePosition, view]);
276
280
  var enable = useMemo(function () {
281
+ if (disableHandles && expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
282
+ return {
283
+ left: false,
284
+ right: false
285
+ };
286
+ }
277
287
  return handleSides.reduce(function (acc, side) {
278
288
  var oppositeSide = side === 'left' ? 'right' : 'left';
279
289
  acc[side] = nonWrappedLayouts.concat("wrap-".concat(oppositeSide)).concat("align-".concat(imageAlignmentMap[oppositeSide])).indexOf(layout) > -1;
@@ -282,7 +292,7 @@ export var ResizableMediaSingleNextFunctional = function ResizableMediaSingleNex
282
292
  }
283
293
  return acc;
284
294
  }, {});
285
- }, [layout, isInsideInlineLike]);
295
+ }, [disableHandles, layout, isInsideInlineLike]);
286
296
  var defaultGuidelines = useMemo(function () {
287
297
  if (isAdjacentMode) {
288
298
  return [];
@@ -510,7 +520,7 @@ export var ResizableMediaSingleNextFunctional = function ResizableMediaSingleNex
510
520
  snap: snaps,
511
521
  resizeRatio: nonWrappedLayouts.includes(layout) ? 2 : 1,
512
522
  "data-testid": resizerNextTestId,
513
- isHandleVisible: selected,
523
+ isHandleVisible: expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true) ? selected && !disableHandles : selected,
514
524
  handlePositioning: handlePositioning,
515
525
  handleHighlight: "full-height"
516
526
  }, children, showLegacyNotification && jsx(ResizableMediaMigrationNotification, null)));
@@ -362,6 +362,12 @@ var ResizableMediaSingle = /*#__PURE__*/function (_React$Component) {
362
362
  var enable = {};
363
363
  handleSides.forEach(function (side) {
364
364
  var oppositeSide = side === 'left' ? 'right' : 'left';
365
+ if (expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
366
+ if (_this2.props.disableHandles) {
367
+ enable[side] = false;
368
+ return;
369
+ }
370
+ }
365
371
  enable[side] = ['full-width', 'wide', 'center'].concat("wrap-".concat(oppositeSide)).concat("align-".concat(imageAlignmentMap[oppositeSide])).indexOf(layout) > -1;
366
372
  if (side === 'left' && _this2.insideInlineLike) {
367
373
  enable[side] = false;
@@ -1,4 +1,5 @@
1
1
  import type { MediaADFAttrs } from '@atlaskit/adf-schema';
2
+ import type { InputMethodInsertMedia, InsertMediaVia } from '@atlaskit/editor-common/analytics';
2
3
  import type { MediaProvider } from '@atlaskit/editor-common/provider-factory';
3
4
  import type { EditorCommand, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
4
5
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
@@ -48,6 +49,11 @@ export type MediaNextEditorPluginType = NextEditorPlugin<'media', {
48
49
  * Callback to be called when there is an error rendering a media node.
49
50
  */
50
51
  handleMediaNodeRenderError: (node: PMNode, reason: string) => void;
52
+ /**
53
+ * @private
54
+ * @deprecated Use the command `insertMediaSingle` instead which is decoupled from EditorView
55
+ * and easier to use.
56
+ */
51
57
  insertMediaAsMediaSingle: InsertMediaAsMediaSingle;
52
58
  /**
53
59
  * Used to update the initial provider passed to the media plugin.
@@ -62,6 +68,15 @@ export type MediaNextEditorPluginType = NextEditorPlugin<'media', {
62
68
  };
63
69
  commands: {
64
70
  hideMediaViewer: EditorCommand;
71
+ /**
72
+ * Inserts a media node as a media single.
73
+ * This command creates a media single node from a set of attributes
74
+ *
75
+ * @param attrs - The media node attributes of the node to insert
76
+ * @param inputMethod - The method used to input the media
77
+ * @param insertMediaVia - Optional parameter indicating how the media was inserted
78
+ */
79
+ insertMediaSingle: (attrs: MediaADFAttrs, inputMethod: InputMethodInsertMedia, insertMediaVia?: InsertMediaVia) => EditorCommand;
65
80
  showMediaViewer: (media: MediaADFAttrs) => EditorCommand;
66
81
  trackMediaPaste: (attrs: MediaADFAttrs) => EditorCommand;
67
82
  };
@@ -1,5 +1,7 @@
1
1
  import type { MediaADFAttrs } from '@atlaskit/adf-schema';
2
+ import type { EditorAnalyticsAPI, InputMethodInsertMedia, InsertMediaVia } from '@atlaskit/editor-common/analytics';
2
3
  import type { EditorCommand } from '@atlaskit/editor-common/types';
3
4
  export declare const showMediaViewer: (media: MediaADFAttrs) => EditorCommand;
4
5
  export declare const hideMediaViewer: EditorCommand;
5
6
  export declare const trackMediaPaste: (attrs: MediaADFAttrs) => EditorCommand;
7
+ export declare const insertMediaAsMediaSingleCommand: (editorAnalyticsAPI?: EditorAnalyticsAPI, allowPixelResizing?: boolean) => (mediaAttrs: MediaADFAttrs, inputMethod: InputMethodInsertMedia, insertMediaVia?: InsertMediaVia) => EditorCommand;
@@ -1,12 +1,14 @@
1
+ import type { MediaADFAttrs } from '@atlaskit/adf-schema';
1
2
  import type { EditorAnalyticsAPI, InputMethodInsertMedia } from '@atlaskit/editor-common/analytics';
2
3
  import { type InsertMediaVia } from '@atlaskit/editor-common/analytics';
3
- import type { EditorContainerWidth as WidthPluginState } from '@atlaskit/editor-common/types';
4
+ import type { EditorCommand, EditorContainerWidth as WidthPluginState } from '@atlaskit/editor-common/types';
4
5
  import type { Node as PMNode, Schema } from '@atlaskit/editor-prosemirror/model';
5
6
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
6
7
  import type { MediaState } from '../../types';
7
8
  export declare const isMediaSingle: (schema: Schema, fileMimeType?: string) => boolean;
8
9
  export type InsertMediaAsMediaSingle = (view: EditorView, node: PMNode, inputMethod: InputMethodInsertMedia, insertMediaVia?: InsertMediaVia, allowPixelResizing?: boolean) => boolean;
9
10
  export declare const insertMediaAsMediaSingle: (view: EditorView, node: PMNode, inputMethod: InputMethodInsertMedia, editorAnalyticsAPI: EditorAnalyticsAPI | undefined, insertMediaVia?: InsertMediaVia, allowPixelResizing?: boolean) => boolean;
11
+ export declare const createInsertMediaAsMediaSingleCommand: (mediaAttrs: MediaADFAttrs, inputMethod: InputMethodInsertMedia, editorAnalyticsAPI: EditorAnalyticsAPI | undefined, insertMediaVia?: InsertMediaVia, allowPixelResizing?: boolean) => EditorCommand;
10
12
  export declare const insertMediaSingleNode: (view: EditorView, mediaState: MediaState, inputMethod?: InputMethodInsertMedia, collection?: string, alignLeftOnInsert?: boolean, widthPluginState?: WidthPluginState | undefined, editorAnalyticsAPI?: EditorAnalyticsAPI | undefined, onNodeInserted?: (id: string, selectionPosition: number) => void, insertMediaVia?: InsertMediaVia, allowPixelResizing?: boolean) => boolean;
11
13
  export declare const changeFromMediaInlineToMediaSingleNode: (view: EditorView, fromNode: PMNode, widthPluginState?: WidthPluginState | undefined, editorAnalyticsAPI?: EditorAnalyticsAPI | undefined, allowPixelResizing?: boolean) => boolean;
12
14
  export declare const isVideo: import("memoize-one").MemoizedFn<(fileType?: string) => boolean>;