@atlaskit/editor-plugin-media 0.8.0 → 0.9.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 (36) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/pm-plugins/alt-text/commands.js +43 -22
  3. package/dist/cjs/pm-plugins/alt-text/ui/AltTextEdit.js +9 -16
  4. package/dist/cjs/pm-plugins/mediaResizeAnnouncerMess.js +1 -1
  5. package/dist/cjs/toolbar/alt-text.js +3 -2
  6. package/dist/cjs/toolbar/mediaInline.js +20 -9
  7. package/dist/cjs/utils/analytics.js +25 -2
  8. package/dist/cjs/utils/media-common.js +27 -1
  9. package/dist/cjs/utils/media-single.js +11 -3
  10. package/dist/es2019/pm-plugins/alt-text/commands.js +40 -19
  11. package/dist/es2019/pm-plugins/alt-text/ui/AltTextEdit.js +11 -25
  12. package/dist/es2019/pm-plugins/mediaResizeAnnouncerMess.js +1 -1
  13. package/dist/es2019/toolbar/alt-text.js +4 -3
  14. package/dist/es2019/toolbar/mediaInline.js +19 -9
  15. package/dist/es2019/utils/analytics.js +20 -1
  16. package/dist/es2019/utils/media-common.js +29 -0
  17. package/dist/es2019/utils/media-single.js +11 -3
  18. package/dist/esm/pm-plugins/alt-text/commands.js +43 -22
  19. package/dist/esm/pm-plugins/alt-text/ui/AltTextEdit.js +11 -21
  20. package/dist/esm/pm-plugins/mediaResizeAnnouncerMess.js +1 -1
  21. package/dist/esm/toolbar/alt-text.js +4 -3
  22. package/dist/esm/toolbar/mediaInline.js +20 -9
  23. package/dist/esm/utils/analytics.js +23 -1
  24. package/dist/esm/utils/media-common.js +26 -0
  25. package/dist/esm/utils/media-single.js +11 -3
  26. package/dist/types/pm-plugins/alt-text/commands.d.ts +3 -0
  27. package/dist/types/pm-plugins/alt-text/ui/AltTextEdit.d.ts +0 -1
  28. package/dist/types/toolbar/mediaInline.d.ts +1 -2
  29. package/dist/types/utils/analytics.d.ts +4 -1
  30. package/dist/types/utils/media-common.d.ts +4 -0
  31. package/dist/types-ts4.5/pm-plugins/alt-text/commands.d.ts +3 -0
  32. package/dist/types-ts4.5/pm-plugins/alt-text/ui/AltTextEdit.d.ts +0 -1
  33. package/dist/types-ts4.5/toolbar/mediaInline.d.ts +1 -2
  34. package/dist/types-ts4.5/utils/analytics.d.ts +4 -1
  35. package/dist/types-ts4.5/utils/media-common.d.ts +4 -0
  36. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @atlaskit/editor-plugin-media
2
2
 
3
+ ## 0.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#63809](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/63809) [`0a735e84c669`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/0a735e84c669) - Added alt text support for inline images
8
+
9
+ ## 0.8.1
10
+
11
+ ### Patch Changes
12
+
13
+ - [#63388](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/63388) [`999a8302f404`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/999a8302f404) - add analytics for changing media inline to media single
14
+ - [#63608](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/63608) [`bfb98fe84eae`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/bfb98fe84eae) - [ux] EDF-93: Report and resolve existing duplicate i18n message descriptor keys/IDs in editor, as precursor work to adding CI check
15
+
3
16
  ## 0.8.0
4
17
 
5
18
  ### Minor Changes
@@ -4,10 +4,11 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.updateAltText = exports.openMediaAltTextMenu = exports.closeMediaAltTextMenu = void 0;
7
+ exports.updateAltTextTransform = exports.updateAltText = exports.openMediaAltTextMenu = exports.closeMediaAltTextMenuAndSave = exports.closeMediaAltTextMenu = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _analytics = require("@atlaskit/editor-common/analytics");
10
10
  var _editorAnalytics = require("@atlaskit/editor-common/editor-analytics");
11
+ var _state = require("@atlaskit/editor-prosemirror/state");
11
12
  var _mediaCommon = require("../../utils/media-common");
12
13
  var _index = require("./index");
13
14
  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; }
@@ -22,16 +23,22 @@ var createCommandWithAnalytics = function createCommandWithAnalytics(actionType,
22
23
  })((0, _index.createCommand)(action, transform));
23
24
  };
24
25
  };
25
- var closeMediaAltTextMenu = exports.closeMediaAltTextMenu = (0, _index.createCommand)(function (state) {
26
- if ((0, _mediaCommon.isSelectionMediaSingleNode)(state)) {
27
- return {
28
- type: 'closeMediaAltTextMenu'
29
- };
30
- }
31
- return false;
32
- });
26
+
27
+ // pass in undefined to close the alt text menu without saving
28
+ var closeMediaAltTextMenuAndSave = exports.closeMediaAltTextMenuAndSave = function closeMediaAltTextMenuAndSave(altText) {
29
+ var commandTransform = typeof altText === 'string' ? updateAltTextTransform(altText) : undefined;
30
+ return (0, _index.createCommand)(function (state) {
31
+ if ((0, _mediaCommon.isMediaSingleOrInlineNodeSelected)(state)) {
32
+ return {
33
+ type: 'closeMediaAltTextMenu'
34
+ };
35
+ }
36
+ return false;
37
+ }, commandTransform);
38
+ };
39
+ var closeMediaAltTextMenu = exports.closeMediaAltTextMenu = closeMediaAltTextMenuAndSave();
33
40
  var openMediaAltTextMenu = exports.openMediaAltTextMenu = createCommandWithAnalytics(_analytics.ACTION.OPENED, function (state) {
34
- if ((0, _mediaCommon.isSelectionMediaSingleNode)(state)) {
41
+ if ((0, _mediaCommon.isMediaSingleOrInlineNodeSelected)(state)) {
35
42
  return {
36
43
  type: 'openMediaAltTextMenu'
37
44
  };
@@ -40,20 +47,34 @@ var openMediaAltTextMenu = exports.openMediaAltTextMenu = createCommandWithAnaly
40
47
  }, function (tr) {
41
48
  return tr.setMeta('scrollIntoView', false);
42
49
  });
43
- var updateAltText = exports.updateAltText = function updateAltText(newAltText) {
44
- return (0, _index.createCommand)(function (state) {
45
- return (0, _mediaCommon.isSelectionMediaSingleNode)(state) ? {
46
- type: 'updateAltText'
47
- } : false;
48
- }, function (tr, state) {
49
- var mediaNode = (0, _mediaCommon.getMediaNodeFromSelection)(state);
50
- var pos = tr.selection.from + 1;
50
+ var updateAltTextTransform = exports.updateAltTextTransform = function updateAltTextTransform(newAltText) {
51
+ return function (tr, state) {
52
+ var mediaNode = (0, _mediaCommon.getMediaSingleOrInlineNodeFromSelection)(state);
51
53
  if (mediaNode) {
52
- tr.setMeta('scrollIntoView', false);
53
- tr.setNodeMarkup(pos, undefined, _objectSpread(_objectSpread({}, mediaNode.attrs), {}, {
54
+ var pos = mediaNode.type === state.schema.nodes.media ? tr.selection.from + 1 : tr.selection.from;
55
+
56
+ /**
57
+ * Any changes to attributes of a node count the node as "recreated" in Prosemirror[1]
58
+ * This makes it so Prosemirror resets the selection to the child i.e. "media" instead of "media-single"
59
+ * The recommended fix is to reset the selection.[2]
60
+ *
61
+ * [1] https://discuss.prosemirror.net/t/setnodemarkup-loses-current-nodeselection/976
62
+ * [2] https://discuss.prosemirror.net/t/setnodemarkup-and-deselect/3673
63
+ */
64
+ tr.setMeta('scrollIntoView', false).setNodeMarkup(pos, undefined, _objectSpread(_objectSpread({}, mediaNode.attrs), {}, {
54
65
  alt: newAltText
55
- }));
66
+ })).setSelection(_state.NodeSelection.create(tr.doc, pos));
56
67
  }
57
68
  return tr;
58
- });
69
+ };
70
+ };
71
+ var updateAltText = exports.updateAltText = function updateAltText(newAltText) {
72
+ return (0, _index.createCommand)(function (state) {
73
+ if ((0, _mediaCommon.isMediaSingleOrInlineNodeSelected)(state)) {
74
+ return {
75
+ type: 'updateAltText'
76
+ };
77
+ }
78
+ return false;
79
+ }, updateAltTextTransform(newAltText));
59
80
  };
@@ -54,7 +54,11 @@ var AltTextEditComponent = exports.AltTextEditComponent = /*#__PURE__*/function
54
54
  });
55
55
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "closeMediaAltTextMenu", function () {
56
56
  var view = _this.props.view;
57
- (0, _commands.closeMediaAltTextMenu)(view.state, view.dispatch);
57
+ if (_this.state.validationErrors.length === 0) {
58
+ (0, _commands.closeMediaAltTextMenuAndSave)(_this.state.lastValue || '')(view.state, view.dispatch);
59
+ } else {
60
+ (0, _commands.closeMediaAltTextMenu)(view.state, view.dispatch);
61
+ }
58
62
  });
59
63
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "dispatchCancelEvent", function (event) {
60
64
  var _this$props = _this.props,
@@ -69,11 +73,6 @@ var AltTextEditComponent = exports.AltTextEditComponent = /*#__PURE__*/function
69
73
  });
70
74
  onEscape === null || onEscape === void 0 || onEscape();
71
75
  });
72
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "updateAltText", function (newAltText) {
73
- var view = _this.props.view;
74
- var newValue = newAltText.length === 0 ? '' : newAltText;
75
- (0, _commands.updateAltText)(newValue)(view.state, view.dispatch);
76
- });
77
76
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleOnChange", function (newAltText) {
78
77
  var _this$state;
79
78
  var validationErrors = _this.getValidationErrors(newAltText);
@@ -91,18 +90,12 @@ var AltTextEditComponent = exports.AltTextEditComponent = /*#__PURE__*/function
91
90
  showClearTextButton: Boolean(newAltText),
92
91
  validationErrors: validationErrors,
93
92
  lastValue: newAltText
94
- }, function () {
95
- if (!validationErrors || !validationErrors.length) {
96
- _this.updateAltText(newAltText);
97
- }
98
93
  });
99
94
  });
100
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleOnBlur", function () {
101
- // Handling the trimming onBlur() because PanelTextInput doesn't sync
102
- // defaultValue properly during unmount
103
- var value = _this.props.value;
104
- var newValue = (_this.state.lastValue || value || '').trim();
105
- _this.handleOnChange(newValue);
95
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleOnBlur", function (e) {
96
+ // prevent other selection transaction gets triggered
97
+ e.stopPropagation();
98
+ _this.closeMediaAltTextMenu();
106
99
  });
107
100
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleClearText", function () {
108
101
  _this.handleOnChange('');
@@ -22,7 +22,7 @@ var mediaResizeAnnouncerMess = exports.mediaResizeAnnouncerMess = (0, _reactIntl
22
22
  description: 'Media width {action} to {newMediaWidth} pixels.'
23
23
  },
24
24
  IncreasedAction: {
25
- id: 'fabric.editor.media.decreased',
25
+ id: 'fabric.editor.media.increased',
26
26
  defaultMessage: 'increased',
27
27
  description: 'Increased action'
28
28
  },
@@ -20,7 +20,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
20
20
  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) { (0, _defineProperty2.default)(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; }
21
21
  var testId = 'alt-text-edit-button';
22
22
  var altTextButton = exports.altTextButton = function altTextButton(intl, state, editorAnalyticsAPI) {
23
- var mediaNode = (0, _mediaCommon.getMediaNodeFromSelection)(state);
23
+ var mediaNode = (0, _mediaCommon.getMediaSingleOrInlineNodeFromSelection)(state);
24
24
  var message = mediaNode && mediaNode.attrs.alt ? _messages.messages.editAltText : _messages.messages.altText;
25
25
  var title = intl.formatMessage(message);
26
26
  return {
@@ -45,7 +45,8 @@ var altTextEditComponent = exports.altTextEditComponent = function altTextEditCo
45
45
  if (!view) {
46
46
  return null;
47
47
  }
48
- var mediaNode = (0, _mediaCommon.getMediaNodeFromSelection)(view.state);
48
+ var state = view.state;
49
+ var mediaNode = (0, _mediaCommon.getMediaSingleOrInlineNodeFromSelection)(state);
49
50
  if (!mediaNode) {
50
51
  return null;
51
52
  }
@@ -17,6 +17,7 @@ var _mediaUi = require("@atlaskit/media-ui");
17
17
  var _linking = require("../commands/linking");
18
18
  var _linking2 = require("../pm-plugins/linking");
19
19
  var _isType = require("../utils/is-type");
20
+ var _altText = require("./alt-text");
20
21
  var _commands = require("./commands");
21
22
  var _filePreviewItem = require("./filePreviewItem");
22
23
  var _linkingToolbarAppearance = require("./linking-toolbar-appearance");
@@ -24,15 +25,14 @@ var _utils = require("./utils");
24
25
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
25
26
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
26
27
  var generateMediaInlineFloatingToolbar = exports.generateMediaInlineFloatingToolbar = function generateMediaInlineFloatingToolbar(state, intl, mediaPluginState, hoverDecoration, pluginInjectionApi) {
27
- var _pluginInjectionApi$a, _pluginInjectionApi$w;
28
+ var _pluginInjectionApi$a;
28
29
  var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
29
30
  var editorAnalyticsAPI = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 ? void 0 : _pluginInjectionApi$a.actions;
30
- var widthPluginState = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$w = pluginInjectionApi.width) === null || _pluginInjectionApi$w === void 0 ? void 0 : _pluginInjectionApi$w.sharedState.currentState();
31
31
  var mediaInline = state.schema.nodes.mediaInline;
32
32
  var mediaType = state.selection instanceof _state2.NodeSelection && state.selection.node.attrs.type;
33
33
  var mediaLinkingState = (0, _linking2.getMediaLinkingState)(state);
34
34
  if (mediaPluginState.allowInlineImages && (0, _isType.isImage)(mediaType)) {
35
- return getMediaInlineImageToolbar(state, intl, mediaPluginState, hoverDecoration, editorAnalyticsAPI, widthPluginState, pluginInjectionApi, mediaLinkingState);
35
+ return getMediaInlineImageToolbar(state, intl, mediaPluginState, hoverDecoration, editorAnalyticsAPI, pluginInjectionApi, mediaLinkingState, options);
36
36
  }
37
37
  var items = [{
38
38
  id: 'editor.media.view.switcher',
@@ -105,14 +105,18 @@ var generateMediaInlineFloatingToolbar = exports.generateMediaInlineFloatingTool
105
105
  }];
106
106
  return items;
107
107
  };
108
- var getMediaInlineImageToolbar = exports.getMediaInlineImageToolbar = function getMediaInlineImageToolbar(state, intl, mediaPluginState, hoverDecoration, editorAnalyticsAPI, widthPluginState, pluginInjectionApi, mediaLinkingState) {
108
+ var getMediaInlineImageToolbar = exports.getMediaInlineImageToolbar = function getMediaInlineImageToolbar(state, intl, mediaPluginState, hoverDecoration, editorAnalyticsAPI, pluginInjectionApi, mediaLinkingState) {
109
+ var _pluginInjectionApi$w;
110
+ var options = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : {};
109
111
  var mediaInline = state.schema.nodes.mediaInline;
110
112
  var mediaInlineImageTitle = intl.formatMessage(_messages.mediaAndEmbedToolbarMessages.changeToMediaInlineImage);
111
113
  var mediaSingleTitle = intl.formatMessage(_messages.mediaAndEmbedToolbarMessages.changeToMediaSingle);
112
114
 
115
+ // TODO: border marks, media single switcher, alt text, etc
116
+ var widthPluginState = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$w = pluginInjectionApi.width) === null || _pluginInjectionApi$w === void 0 ? void 0 : _pluginInjectionApi$w.sharedState.currentState();
113
117
  // if type is image, return inline image floating toolbar items
114
118
  var inlineImageItems = [
115
- // TODO: border marks, media single switcher, alt text, etc
119
+ // TODO: border marks, media single switcher, link, etc
116
120
  {
117
121
  id: 'editor.media.convert.mediainline',
118
122
  type: 'button',
@@ -140,7 +144,14 @@ var getMediaInlineImageToolbar = exports.getMediaInlineImageToolbar = function g
140
144
  onClick: (0, _commands.changeMediaInlineToMediaSingle)(editorAnalyticsAPI, widthPluginState)
141
145
  }, {
142
146
  type: 'separator'
143
- }, {
147
+ }];
148
+ if (options.allowAltTextOnImages) {
149
+ var _pluginInjectionApi$a2;
150
+ inlineImageItems.push((0, _altText.altTextButton)(intl, state, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a2 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a2 === void 0 ? void 0 : _pluginInjectionApi$a2.actions), {
151
+ type: 'separator'
152
+ });
153
+ }
154
+ inlineImageItems.push({
144
155
  type: 'custom',
145
156
  fallback: [],
146
157
  render: function render(editorView, idx) {
@@ -154,10 +165,10 @@ var getMediaInlineImageToolbar = exports.getMediaInlineImageToolbar = function g
154
165
  };
155
166
  var openLink = function openLink() {
156
167
  if (editorView) {
157
- var _pluginInjectionApi$a2;
168
+ var _pluginInjectionApi$a3;
158
169
  var tr = editorView.state.tr,
159
170
  dispatch = editorView.dispatch;
160
- pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a2 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a2 === void 0 || _pluginInjectionApi$a2.actions.attachAnalyticsEvent({
171
+ pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a3 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a3 === void 0 || _pluginInjectionApi$a3.actions.attachAnalyticsEvent({
161
172
  eventType: _analytics.EVENT_TYPE.TRACK,
162
173
  action: _analytics.ACTION.VISITED,
163
174
  actionSubject: _analytics.ACTION_SUBJECT.MEDIA,
@@ -202,6 +213,6 @@ var getMediaInlineImageToolbar = exports.getMediaInlineImageToolbar = function g
202
213
  title: intl.formatMessage(_messages.default.remove),
203
214
  onClick: _commands.removeInlineCard,
204
215
  testId: 'media-toolbar-remove-button'
205
- }];
216
+ });
206
217
  return inlineImageItems;
207
218
  };
@@ -3,8 +3,10 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.getMediaResizeAnalyticsEvent = exports.getMediaInputResizeAnalyticsEvent = void 0;
6
+ exports.findChangeFromLocation = findChangeFromLocation;
7
+ exports.getMediaResizeAnalyticsEvent = exports.getMediaInputResizeAnalyticsEvent = exports.getChangeMediaAnalytics = void 0;
7
8
  var _analytics = require("@atlaskit/editor-common/analytics");
9
+ var _utils = require("@atlaskit/editor-prosemirror/utils");
8
10
  var getMediaResizeAnalyticsEvent = exports.getMediaResizeAnalyticsEvent = function getMediaResizeAnalyticsEvent(type, attributes) {
9
11
  if (!attributes) {
10
12
  return;
@@ -54,4 +56,25 @@ var getMediaInputResizeAnalyticsEvent = exports.getMediaInputResizeAnalyticsEven
54
56
  },
55
57
  eventType: _analytics.EVENT_TYPE.UI
56
58
  };
57
- };
59
+ };
60
+ var getChangeMediaAnalytics = exports.getChangeMediaAnalytics = function getChangeMediaAnalytics(previousType, newType, changeFromLocation) {
61
+ return {
62
+ action: _analytics.ACTION.CHANGED_TYPE,
63
+ actionSubject: _analytics.ACTION_SUBJECT.MEDIA,
64
+ eventType: _analytics.EVENT_TYPE.TRACK,
65
+ attributes: {
66
+ newType: newType,
67
+ previousType: previousType,
68
+ changeFromLocation: changeFromLocation
69
+ }
70
+ };
71
+ };
72
+ function findChangeFromLocation(selection) {
73
+ var _selection$$from$doc$ = selection.$from.doc.type,
74
+ schema = _selection$$from$doc$.schema,
75
+ name = _selection$$from$doc$.name;
76
+ var parentNodeInfo = (0, _utils.findParentNode)(function (node) {
77
+ return node.type !== schema.nodes.paragraph;
78
+ })(selection);
79
+ return parentNodeInfo ? parentNodeInfo.node.type.name : name;
80
+ }
@@ -3,13 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.splitMediaGroup = exports.removeMediaNode = exports.posOfPrecedingMediaGroup = exports.posOfParentMediaGroup = exports.posOfMediaGroupNearby = exports.isSelectionNonMediaBlockNode = exports.isSelectionMediaSingleNode = exports.isMediaBlobUrlFromAttrs = exports.isInsidePotentialEmptyParagraph = exports.getMediaNodeFromSelection = exports.copyOptionalAttrsFromMediaState = void 0;
6
+ exports.splitMediaGroup = exports.removeMediaNode = exports.posOfPrecedingMediaGroup = exports.posOfParentMediaGroup = exports.posOfMediaGroupNearby = exports.isSelectionNonMediaBlockNode = exports.isSelectionMediaSingleNode = exports.isSelectionMediaInlineNode = exports.isMediaSingleOrInlineNodeSelected = exports.isMediaBlobUrlFromAttrs = exports.isInsidePotentialEmptyParagraph = exports.getMediaSingleOrInlineNodeFromSelection = exports.getMediaNodeFromSelection = exports.getMediaInlineNodeFromSelection = exports.copyOptionalAttrsFromMediaState = void 0;
7
7
  var _selection = require("@atlaskit/editor-common/selection");
8
8
  var _utils = require("@atlaskit/editor-common/utils");
9
9
  var _commands = require("@atlaskit/editor-prosemirror/commands");
10
10
  var _state = require("@atlaskit/editor-prosemirror/state");
11
11
  var _utils2 = require("@atlaskit/editor-prosemirror/utils");
12
12
  var _mediaClient = require("@atlaskit/media-client");
13
+ var _main = require("../pm-plugins/main");
13
14
  var isTemporary = function isTemporary(id) {
14
15
  return id.indexOf('temporary:') === 0;
15
16
  };
@@ -29,6 +30,11 @@ var isSelectionMediaSingleNode = exports.isSelectionMediaSingleNode = function i
29
30
  node = _ref2.node;
30
31
  return node && node.type === state.schema.nodes.mediaSingle;
31
32
  };
33
+ var isSelectionMediaInlineNode = exports.isSelectionMediaInlineNode = function isSelectionMediaInlineNode(state) {
34
+ var _ref3 = state.selection,
35
+ node = _ref3.node;
36
+ return node && node.type === state.schema.nodes.mediaInline;
37
+ };
32
38
  var posOfPrecedingMediaGroup = exports.posOfPrecedingMediaGroup = function posOfPrecedingMediaGroup(state) {
33
39
  if (!(0, _selection.atTheBeginningOfBlock)(state)) {
34
40
  return;
@@ -178,4 +184,24 @@ var getMediaNodeFromSelection = exports.getMediaNodeFromSelection = function get
178
184
  return mediaNode;
179
185
  }
180
186
  return null;
187
+ };
188
+ var getMediaInlineNodeFromSelection = exports.getMediaInlineNodeFromSelection = function getMediaInlineNodeFromSelection(state) {
189
+ if (!isSelectionMediaInlineNode(state)) {
190
+ return null;
191
+ }
192
+ var tr = state.tr;
193
+ var pos = tr.selection.from;
194
+ var mediaNode = tr.doc.nodeAt(pos);
195
+ return mediaNode;
196
+ };
197
+ var isMediaSingleOrInlineNodeSelected = exports.isMediaSingleOrInlineNodeSelected = function isMediaSingleOrInlineNodeSelected(state) {
198
+ var _getMediaPluginState = (0, _main.getMediaPluginState)(state),
199
+ allowInlineImages = _getMediaPluginState.allowInlineImages;
200
+ return isSelectionMediaSingleNode(state) || allowInlineImages && isSelectionMediaInlineNode(state);
201
+ };
202
+ var getMediaSingleOrInlineNodeFromSelection = exports.getMediaSingleOrInlineNodeFromSelection = function getMediaSingleOrInlineNodeFromSelection(state) {
203
+ var _getMediaPluginState2 = (0, _main.getMediaPluginState)(state),
204
+ allowInlineImages = _getMediaPluginState2.allowInlineImages;
205
+ var mediaNode = getMediaNodeFromSelection(state) || allowInlineImages && getMediaInlineNodeFromSelection(state);
206
+ return mediaNode || null;
181
207
  };
@@ -18,6 +18,7 @@ var _model = require("@atlaskit/editor-prosemirror/model");
18
18
  var _utils2 = require("@atlaskit/editor-prosemirror/utils");
19
19
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
20
20
  var _mediaCommon = require("../utils/media-common");
21
+ var _analytics2 = require("./analytics");
21
22
  var _isType = require("./is-type");
22
23
  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; }
23
24
  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) { (0, _defineProperty2.default)(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; }
@@ -45,7 +46,9 @@ function insertNodesWithOptionalParagraph(nodes) {
45
46
  schema = state.schema;
46
47
  var paragraph = schema.nodes.paragraph;
47
48
  var inputMethod = analyticsAttributes.inputMethod,
48
- fileExtension = analyticsAttributes.fileExtension;
49
+ fileExtension = analyticsAttributes.fileExtension,
50
+ newType = analyticsAttributes.newType,
51
+ previousType = analyticsAttributes.previousType;
49
52
  var openEnd = 0;
50
53
  if (shouldAddParagraph(state)) {
51
54
  nodes.push(paragraph.create());
@@ -55,6 +58,9 @@ function insertNodesWithOptionalParagraph(nodes) {
55
58
  if (inputMethod) {
56
59
  editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(getInsertMediaAnalytics(inputMethod, fileExtension))(tr);
57
60
  }
61
+ if (newType && previousType) {
62
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent((0, _analytics2.getChangeMediaAnalytics)(previousType, newType, (0, _analytics2.findChangeFromLocation)(state.selection)))(tr);
63
+ }
58
64
  if (dispatch) {
59
65
  dispatch(tr);
60
66
  }
@@ -163,7 +169,9 @@ var changeFromMediaInlineToMediaSingleNode = exports.changeFromMediaInlineToMedi
163
169
  content: node
164
170
  })) {
165
171
  return insertNodesWithOptionalParagraph([node], {
166
- fileExtension: fileExtension
172
+ fileExtension: fileExtension,
173
+ newType: _analytics.ACTION_SUBJECT_ID.MEDIA_SINGLE,
174
+ previousType: _analytics.ACTION_SUBJECT_ID.MEDIA_INLINE
167
175
  }, editorAnalyticsAPI)(state, dispatch);
168
176
  } else {
169
177
  var nodePos = state.tr.doc.resolve(state.selection.from).end();
@@ -174,7 +182,7 @@ var changeFromMediaInlineToMediaSingleNode = exports.changeFromMediaInlineToMedi
174
182
  var content = shouldAddParagraph(view.state) ? _model.Fragment.fromArray([node, state.schema.nodes.paragraph.create()]) : node;
175
183
  tr = (0, _utils2.safeInsert)(content, undefined, true)(state.tr);
176
184
  }
177
- // TODO: add analytics specifically for change mediaInline to mediaSingle later
185
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent((0, _analytics2.getChangeMediaAnalytics)(_analytics.ACTION_SUBJECT_ID.MEDIA_INLINE, _analytics.ACTION_SUBJECT_ID.MEDIA_SINGLE, (0, _analytics2.findChangeFromLocation)(state.selection)))(tr);
178
186
  dispatch(tr);
179
187
  }
180
188
  return true;
@@ -1,6 +1,7 @@
1
1
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
2
  import { withAnalytics } from '@atlaskit/editor-common/editor-analytics';
3
- import { getMediaNodeFromSelection, isSelectionMediaSingleNode } from '../../utils/media-common';
3
+ import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
4
+ import { getMediaSingleOrInlineNodeFromSelection, isMediaSingleOrInlineNodeSelected } from '../../utils/media-common';
4
5
  import { createCommand } from './index';
5
6
  const createCommandWithAnalytics = (actionType, action, transform) => {
6
7
  return editorAnalyticsAPI => withAnalytics(editorAnalyticsAPI, {
@@ -10,33 +11,53 @@ const createCommandWithAnalytics = (actionType, action, transform) => {
10
11
  eventType: EVENT_TYPE.TRACK
11
12
  })(createCommand(action, transform));
12
13
  };
13
- export const closeMediaAltTextMenu = createCommand(state => {
14
- if (isSelectionMediaSingleNode(state)) {
15
- return {
16
- type: 'closeMediaAltTextMenu'
17
- };
18
- }
19
- return false;
20
- });
14
+
15
+ // pass in undefined to close the alt text menu without saving
16
+ export const closeMediaAltTextMenuAndSave = altText => {
17
+ const commandTransform = typeof altText === 'string' ? updateAltTextTransform(altText) : undefined;
18
+ return createCommand(state => {
19
+ if (isMediaSingleOrInlineNodeSelected(state)) {
20
+ return {
21
+ type: 'closeMediaAltTextMenu'
22
+ };
23
+ }
24
+ return false;
25
+ }, commandTransform);
26
+ };
27
+ export const closeMediaAltTextMenu = closeMediaAltTextMenuAndSave();
21
28
  export const openMediaAltTextMenu = createCommandWithAnalytics(ACTION.OPENED, state => {
22
- if (isSelectionMediaSingleNode(state)) {
29
+ if (isMediaSingleOrInlineNodeSelected(state)) {
23
30
  return {
24
31
  type: 'openMediaAltTextMenu'
25
32
  };
26
33
  }
27
34
  return false;
28
35
  }, tr => tr.setMeta('scrollIntoView', false));
29
- export const updateAltText = newAltText => createCommand(state => isSelectionMediaSingleNode(state) ? {
30
- type: 'updateAltText'
31
- } : false, (tr, state) => {
32
- const mediaNode = getMediaNodeFromSelection(state);
33
- const pos = tr.selection.from + 1;
36
+ export const updateAltTextTransform = newAltText => (tr, state) => {
37
+ const mediaNode = getMediaSingleOrInlineNodeFromSelection(state);
34
38
  if (mediaNode) {
35
- tr.setMeta('scrollIntoView', false);
36
- tr.setNodeMarkup(pos, undefined, {
39
+ const pos = mediaNode.type === state.schema.nodes.media ? tr.selection.from + 1 : tr.selection.from;
40
+
41
+ /**
42
+ * Any changes to attributes of a node count the node as "recreated" in Prosemirror[1]
43
+ * This makes it so Prosemirror resets the selection to the child i.e. "media" instead of "media-single"
44
+ * The recommended fix is to reset the selection.[2]
45
+ *
46
+ * [1] https://discuss.prosemirror.net/t/setnodemarkup-loses-current-nodeselection/976
47
+ * [2] https://discuss.prosemirror.net/t/setnodemarkup-and-deselect/3673
48
+ */
49
+ tr.setMeta('scrollIntoView', false).setNodeMarkup(pos, undefined, {
37
50
  ...mediaNode.attrs,
38
51
  alt: newAltText
39
- });
52
+ }).setSelection(NodeSelection.create(tr.doc, pos));
40
53
  }
41
54
  return tr;
42
- });
55
+ };
56
+ export const updateAltText = newAltText => createCommand(state => {
57
+ if (isMediaSingleOrInlineNodeSelected(state)) {
58
+ return {
59
+ type: 'updateAltText'
60
+ };
61
+ }
62
+ return false;
63
+ }, updateAltTextTransform(newAltText));
@@ -7,15 +7,12 @@ import { injectIntl } from 'react-intl-next';
7
7
  import { withAnalyticsEvents } from '@atlaskit/analytics-next';
8
8
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, fireAnalyticsEvent } from '@atlaskit/editor-common/analytics';
9
9
  import { escape, ToolTipContent } from '@atlaskit/editor-common/keymaps';
10
- import { PanelTextInput } from '@atlaskit/editor-common/ui';
11
- import { FloatingToolbarButton as Button } from '@atlaskit/editor-common/ui';
12
- import { RECENT_SEARCH_WIDTH_IN_PX } from '@atlaskit/editor-common/ui';
13
- import { ErrorMessage } from '@atlaskit/editor-common/ui';
10
+ import { FloatingToolbarButton as Button, ErrorMessage, PanelTextInput, RECENT_SEARCH_WIDTH_IN_PX } from '@atlaskit/editor-common/ui';
14
11
  import { relativeFontSizeToBase16 } from '@atlaskit/editor-shared-styles';
15
12
  import ChevronLeftLargeIcon from '@atlaskit/icon/glyph/chevron-left-large';
16
13
  import CrossCircleIcon from '@atlaskit/icon/glyph/cross-circle';
17
14
  import { N100, N30, N80, R400 } from '@atlaskit/theme/colors';
18
- import { closeMediaAltTextMenu, updateAltText } from '../commands';
15
+ import { closeMediaAltTextMenu, closeMediaAltTextMenuAndSave } from '../commands';
19
16
  import { messages } from '../messages';
20
17
  export const CONTAINER_WIDTH_IN_PX = RECENT_SEARCH_WIDTH_IN_PX;
21
18
  export const MAX_ALT_TEXT_LENGTH = 510; // double tweet length
@@ -71,7 +68,11 @@ export class AltTextEditComponent extends React.Component {
71
68
  const {
72
69
  view
73
70
  } = this.props;
74
- closeMediaAltTextMenu(view.state, view.dispatch);
71
+ if (this.state.validationErrors.length === 0) {
72
+ closeMediaAltTextMenuAndSave(this.state.lastValue || '')(view.state, view.dispatch);
73
+ } else {
74
+ closeMediaAltTextMenu(view.state, view.dispatch);
75
+ }
75
76
  });
76
77
  _defineProperty(this, "dispatchCancelEvent", event => {
77
78
  const {
@@ -85,13 +86,6 @@ export class AltTextEditComponent extends React.Component {
85
86
  view.someProp('handleKeyDown', fn => fn(view, event));
86
87
  onEscape === null || onEscape === void 0 ? void 0 : onEscape();
87
88
  });
88
- _defineProperty(this, "updateAltText", newAltText => {
89
- const {
90
- view
91
- } = this.props;
92
- const newValue = newAltText.length === 0 ? '' : newAltText;
93
- updateAltText(newValue)(view.state, view.dispatch);
94
- });
95
89
  _defineProperty(this, "handleOnChange", newAltText => {
96
90
  var _this$state, _this$state$validatio;
97
91
  const validationErrors = this.getValidationErrors(newAltText);
@@ -109,20 +103,12 @@ export class AltTextEditComponent extends React.Component {
109
103
  showClearTextButton: Boolean(newAltText),
110
104
  validationErrors,
111
105
  lastValue: newAltText
112
- }, () => {
113
- if (!validationErrors || !validationErrors.length) {
114
- this.updateAltText(newAltText);
115
- }
116
106
  });
117
107
  });
118
- _defineProperty(this, "handleOnBlur", () => {
119
- // Handling the trimming onBlur() because PanelTextInput doesn't sync
120
- // defaultValue properly during unmount
121
- const {
122
- value
123
- } = this.props;
124
- const newValue = (this.state.lastValue || value || '').trim();
125
- this.handleOnChange(newValue);
108
+ _defineProperty(this, "handleOnBlur", e => {
109
+ // prevent other selection transaction gets triggered
110
+ e.stopPropagation();
111
+ this.closeMediaAltTextMenu();
126
112
  });
127
113
  _defineProperty(this, "handleClearText", () => {
128
114
  this.handleOnChange('');
@@ -16,7 +16,7 @@ export const mediaResizeAnnouncerMess = defineMessages({
16
16
  description: 'Media width {action} to {newMediaWidth} pixels.'
17
17
  },
18
18
  IncreasedAction: {
19
- id: 'fabric.editor.media.decreased',
19
+ id: 'fabric.editor.media.increased',
20
20
  defaultMessage: 'increased',
21
21
  description: 'Increased action'
22
22
  },
@@ -4,10 +4,10 @@ import { MediaSharedClassNames as ClassNames } from '@atlaskit/editor-common/sty
4
4
  import { openMediaAltTextMenu } from '../pm-plugins/alt-text/commands';
5
5
  import { messages } from '../pm-plugins/alt-text/messages';
6
6
  import AltTextEdit, { CONTAINER_WIDTH_IN_PX } from '../pm-plugins/alt-text/ui/AltTextEdit';
7
- import { getMediaNodeFromSelection } from '../utils/media-common';
7
+ import { getMediaSingleOrInlineNodeFromSelection } from '../utils/media-common';
8
8
  const testId = 'alt-text-edit-button';
9
9
  export const altTextButton = (intl, state, editorAnalyticsAPI) => {
10
- const mediaNode = getMediaNodeFromSelection(state);
10
+ const mediaNode = getMediaSingleOrInlineNodeFromSelection(state);
11
11
  const message = mediaNode && mediaNode.attrs.alt ? messages.editAltText : messages.altText;
12
12
  const title = intl.formatMessage(message);
13
13
  return {
@@ -32,7 +32,8 @@ export const altTextEditComponent = options => {
32
32
  if (!view) {
33
33
  return null;
34
34
  }
35
- const mediaNode = getMediaNodeFromSelection(view.state);
35
+ const state = view.state;
36
+ const mediaNode = getMediaSingleOrInlineNodeFromSelection(state);
36
37
  if (!mediaNode) {
37
38
  return null;
38
39
  }