@atlaskit/editor-plugin-card 35.1.4 → 35.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @atlaskit/editor-plugin-card
2
2
 
3
+ ## 35.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`8a910ddcb6d43`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8a910ddcb6d43) -
8
+ Add opt-in `trackPastedInlineLinks`. When enabled, the card plugin keeps a short, position-mapped
9
+ list of locally pasted inline links (pasted URLs converted to inline cards, and pasted inline
10
+ cards) in `pastedInlineLinks`. Existing links and other creation paths are never tracked, and a
11
+ candidate is dropped when its card is deleted, replaced or given a different URL.
12
+
13
+ The consumer decides which candidate to use. `markPastedInlineLinkShown(target)` confirms display:
14
+ it keeps only the displayed link and stops adding candidates. `clearPastedInlineLink(target)`
15
+ stops tracking one link. Existing first-resolved-inline-link changeboard targeting is unchanged.
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+
3
21
  ## 35.1.4
4
22
 
5
23
  ### Patch Changes
@@ -204,9 +204,21 @@ var cardPlugin = exports.cardPlugin = function cardPlugin(_ref2) {
204
204
  }));
205
205
  },
206
206
  commands: {
207
- queueCardsFromRange: function queueCardsFromRange(from, to, source, analyticsAction, normalizeLinkText, sourceEvent, appearance) {
207
+ markPastedInlineLinkShown: function markPastedInlineLinkShown(target) {
208
208
  return function (_ref6) {
209
209
  var tr = _ref6.tr;
210
+ return options.trackPastedInlineLinks ? (0, _actions.markPastedInlineLinkShown)(target)(tr) : null;
211
+ };
212
+ },
213
+ clearPastedInlineLink: function clearPastedInlineLink(target) {
214
+ return function (_ref7) {
215
+ var tr = _ref7.tr;
216
+ return options.trackPastedInlineLinks ? (0, _actions.clearPastedInlineLink)(target)(tr) : null;
217
+ };
218
+ },
219
+ queueCardsFromRange: function queueCardsFromRange(from, to, source, analyticsAction, normalizeLinkText, sourceEvent, appearance) {
220
+ return function (_ref8) {
221
+ var tr = _ref8.tr;
210
222
  return (0, _doc.queueCardsFromRange)(
211
223
  // Synthesise the minimal EditorState shape the impl actually reads.
212
224
  {
@@ -239,8 +251,8 @@ var cardPlugin = exports.cardPlugin = function cardPlugin(_ref2) {
239
251
  return _context.abrupt("return", false);
240
252
  case 2:
241
253
  previousCardProvider = provider;
242
- return _context.abrupt("return", (_api$core$actions$exe = api === null || api === void 0 ? void 0 : api.core.actions.execute(function (_ref7) {
243
- var tr = _ref7.tr;
254
+ return _context.abrupt("return", (_api$core$actions$exe = api === null || api === void 0 ? void 0 : api.core.actions.execute(function (_ref9) {
255
+ var tr = _ref9.tr;
244
256
  return (0, _actions.setProvider)(provider)(tr);
245
257
  })) !== null && _api$core$actions$exe !== void 0 ? _api$core$actions$exe : false);
246
258
  case 3:
@@ -349,8 +361,8 @@ var cardPlugin = exports.cardPlugin = function cardPlugin(_ref2) {
349
361
  pluginsOptions: _objectSpread({
350
362
  floatingToolbar: (0, _toolbar.floatingToolbar)(options, (_options$lpLinkPicker = options.lpLinkPicker) !== null && _options$lpLinkPicker !== void 0 ? _options$lpLinkPicker : false, options.linkPicker, api, options.disableFloatingToolbar)
351
363
  }, isRegisteredSlashCommandEnabled ? {} : {
352
- quickInsert: function quickInsert(_ref8) {
353
- var formatMessage = _ref8.formatMessage;
364
+ quickInsert: function quickInsert(_ref0) {
365
+ var formatMessage = _ref0.formatMessage;
354
366
  var quickInsertArray = [];
355
367
  if (!options.allowDatasource) {
356
368
  return quickInsertArray;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.showLinkToolbar = exports.showDatasourceModal = exports.setResolvedToolbarAttributes = exports.setProvider = exports.setDatasourceTableRef = exports.setDatasourceStash = exports.setCardLayoutAndDatasourceTableRef = exports.setCardLayout = exports.resolveCard = exports.removeDatasourceStash = exports.removeCard = exports.registerSmartCardEvents = exports.registerRemoveOverlay = exports.registerCard = exports.queueCards = exports.hideLinkToolbar = exports.hideDatasourceModal = exports.clearOverlayCandidate = exports.cardAction = void 0;
6
+ exports.showLinkToolbar = exports.showDatasourceModal = exports.setResolvedToolbarAttributes = exports.setProvider = exports.setDatasourceTableRef = exports.setDatasourceStash = exports.setCardLayoutAndDatasourceTableRef = exports.setCardLayout = exports.resolveCard = exports.removeDatasourceStash = exports.removeCard = exports.registerSmartCardEvents = exports.registerRemoveOverlay = exports.registerCard = exports.queueCards = exports.markPastedInlineLinkShown = exports.hideLinkToolbar = exports.hideDatasourceModal = exports.clearPastedInlineLink = exports.clearOverlayCandidate = exports.cardAction = void 0;
7
7
  var _pluginKey = require("./plugin-key");
8
8
  var cardAction = exports.cardAction = function cardAction(tr, action) {
9
9
  return tr.setMeta(_pluginKey.pluginKey, action);
@@ -140,4 +140,24 @@ var removeDatasourceStash = exports.removeDatasourceStash = function removeDatas
140
140
  type: 'REMOVE_DATASOURCE_STASH',
141
141
  url: url
142
142
  });
143
+ };
144
+
145
+ /** Confirm actual display, not merely target selection or a request to display. */
146
+ var markPastedInlineLinkShown = exports.markPastedInlineLinkShown = function markPastedInlineLinkShown(target) {
147
+ return function (tr) {
148
+ return cardAction(tr, {
149
+ type: 'PASTED_INLINE_LINK_SHOWN',
150
+ target: target
151
+ });
152
+ };
153
+ };
154
+
155
+ /** Discard only this candidate; a stale callback must not clear a newer paste. */
156
+ var clearPastedInlineLink = exports.clearPastedInlineLink = function clearPastedInlineLink(target) {
157
+ return function (tr) {
158
+ return cardAction(tr, {
159
+ type: 'CLEAR_PASTED_INLINE_LINK',
160
+ target: target
161
+ });
162
+ };
143
163
  };
@@ -5,9 +5,10 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.createPlugin = void 0;
8
- var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
8
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
10
  var _rafSchd = _interopRequireDefault(require("raf-schd"));
11
+ var _enums = require("@atlaskit/editor-common/analytics/types/enums");
11
12
  var _getInlineNodeViewProducer = require("@atlaskit/editor-common/getInlineNodeViewProducer");
12
13
  var _isSsr = require("@atlaskit/editor-common/is-ssr");
13
14
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
@@ -26,6 +27,7 @@ var _localStorage = require("../ui/local-storage");
26
27
  var _actions = require("./actions");
27
28
  var _pluginKey = require("./plugin-key");
28
29
  var _reducers = _interopRequireDefault(require("./reducers"));
30
+ var _pastedInlineLink = require("./util/pasted-inline-link");
29
31
  var _resolve = require("./util/resolve");
30
32
  var _state2 = require("./util/state");
31
33
  var _utils3 = require("./utils");
@@ -55,6 +57,7 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pluginI
55
57
  fullWidthMode = options.fullWidthMode,
56
58
  actionOptions = options.actionOptions,
57
59
  cardPluginEvents = options.cardPluginEvents,
60
+ trackPastedInlineLinks = options.trackPastedInlineLinks,
58
61
  showUpgradeDiscoverability = options.showUpgradeDiscoverability,
59
62
  allowEmbeds = options.allowEmbeds,
60
63
  allowBlockCards = options.allowBlockCards,
@@ -87,8 +90,11 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pluginI
87
90
  return new _safePlugin.SafePlugin({
88
91
  state: {
89
92
  init: function init() {
90
- return {
91
- requests: [],
93
+ return _objectSpread(_objectSpread({
94
+ requests: []
95
+ }, trackPastedInlineLinks ? {
96
+ pastedInlineLinks: []
97
+ } : {}), {}, {
92
98
  provider: null,
93
99
  cards: [],
94
100
  datasourceStash: {},
@@ -101,13 +107,15 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pluginI
101
107
  datasourceModalType: undefined,
102
108
  datasourceTableRef: undefined,
103
109
  layout: undefined
104
- };
110
+ });
105
111
  },
106
112
  apply: function apply(tr, pluginState, prevEditorState) {
107
113
  var _pluginState$requests, _pluginState$requests2;
108
114
  // Update all the positions of outstanding requests and
109
115
  // cards in the plugin state.
110
116
  var pluginStateWithUpdatedPos = (0, _state2.getPluginStateWithUpdatedPos)(pluginState, tr);
117
+ // Pasted inline cards that already exist as nodes (copy and paste of cards).
118
+ pluginStateWithUpdatedPos = (0, _pastedInlineLink.withPastedInlineLinks)(pluginStateWithUpdatedPos, trackPastedInlineLinks ? (0, _pastedInlineLink.getPastedInlineCards)(tr) : []);
111
119
 
112
120
  // apply any actions
113
121
  var meta = tr.getMeta(_pluginKey.pluginKey);
@@ -129,7 +137,6 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pluginI
129
137
  return pluginStateWithUpdatedPos;
130
138
  }
131
139
  var newState = (0, _reducers.default)(pluginStateWithUpdatedPos, meta);
132
-
133
140
  // Track the first resolved inline smart link for PO spotlight DOM targeting
134
141
  if (meta.type === 'RESOLVE' && pluginState !== null && pluginState !== void 0 && (_pluginState$requests = pluginState.requests) !== null && _pluginState$requests !== void 0 && _pluginState$requests.length) {
135
142
  var resolvedRequest = pluginState.requests.find(function (req) {
@@ -147,6 +154,11 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pluginI
147
154
  }
148
155
  }
149
156
  }
157
+
158
+ // Pasted URLs that were converted to inline cards.
159
+ if (trackPastedInlineLinks && meta.type === 'RESOLVE') {
160
+ newState = (0, _pastedInlineLink.withPastedInlineLinks)(newState, (0, _pastedInlineLink.getResolvedPastedInlineCards)(tr, pluginState.requests, meta.url, _enums.INPUT_METHOD.CLIPBOARD));
161
+ }
150
162
  if (!enableInlineUpgradeFeatures) {
151
163
  return newState;
152
164
  }
@@ -9,6 +9,7 @@ exports.default = void 0;
9
9
  var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
11
  var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
12
+ var _pastedInlineLink = require("./util/pasted-inline-link");
12
13
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
13
14
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
14
15
  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; }
@@ -141,6 +142,18 @@ var removeDatasourceStash = function removeDatasourceStash(state, action) {
141
142
  };
142
143
  var _default = exports.default = function _default(state, action) {
143
144
  switch (action.type) {
145
+ case 'PASTED_INLINE_LINK_SHOWN':
146
+ return state.pastedInlineLinks ? _objectSpread(_objectSpread({}, state), {}, {
147
+ pastedInlineLinkShown: true,
148
+ // Keep only the displayed link so it can still be dismissed if it changes.
149
+ pastedInlineLinks: state.pastedInlineLinks.filter(function (link) {
150
+ return link.pos === action.target.pos && link.url === action.target.url;
151
+ })
152
+ }) : state;
153
+ case 'CLEAR_PASTED_INLINE_LINK':
154
+ return state.pastedInlineLinks ? _objectSpread(_objectSpread({}, state), {}, {
155
+ pastedInlineLinks: (0, _pastedInlineLink.removePastedInlineLink)(state.pastedInlineLinks, action.target)
156
+ }) : state;
144
157
  case 'QUEUE':
145
158
  return queue(state, action);
146
159
  case 'SET_PROVIDER':
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.withPastedInlineLinks = exports.removePastedInlineLink = exports.mapPastedInlineLinks = exports.getResolvedPastedInlineCards = exports.getPastedInlineCards = exports.addPastedInlineLinks = exports.MAX_PASTED_INLINE_LINKS = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
+ 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; }
11
+ 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; }
12
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
13
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
14
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
15
+ /** Bounds the per-editor work; the first pasted links win. */
16
+ var MAX_PASTED_INLINE_LINKS = exports.MAX_PASTED_INLINE_LINKS = 10;
17
+ var isSameLink = function isSameLink(a, b) {
18
+ return a.pos === b.pos && a.url === b.url;
19
+ };
20
+
21
+ /**
22
+ * Map candidates through a transaction. Anything that was deleted, replaced by another node or
23
+ * given a different URL stops being a candidate. Returns the same array when nothing changed.
24
+ */
25
+ var mapPastedInlineLinks = exports.mapPastedInlineLinks = function mapPastedInlineLinks(links, tr) {
26
+ if (tr.getMeta('replaceDocument')) {
27
+ return links.length ? [] : links;
28
+ }
29
+ if (!tr.docChanged || !links.length) {
30
+ return links;
31
+ }
32
+ var changed = false;
33
+ var mapped = [];
34
+ var _iterator = _createForOfIteratorHelper(links),
35
+ _step;
36
+ try {
37
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
38
+ var link = _step.value;
39
+ var result = tr.mapping.mapResult(link.pos, 1);
40
+ var node = result.deleted ? null : tr.doc.nodeAt(result.pos);
41
+ if ((node === null || node === void 0 ? void 0 : node.type.name) !== 'inlineCard' || node.attrs.url !== link.url) {
42
+ changed = true;
43
+ continue;
44
+ }
45
+ if (result.pos !== link.pos) {
46
+ changed = true;
47
+ mapped.push({
48
+ pos: result.pos,
49
+ url: link.url
50
+ });
51
+ } else {
52
+ mapped.push(link);
53
+ }
54
+ }
55
+ } catch (err) {
56
+ _iterator.e(err);
57
+ } finally {
58
+ _iterator.f();
59
+ }
60
+ return changed ? mapped : links;
61
+ };
62
+
63
+ /** Append new candidates, skipping duplicates and anything beyond the cap. */
64
+ var addPastedInlineLinks = exports.addPastedInlineLinks = function addPastedInlineLinks(links, additions) {
65
+ var next = links;
66
+ var _iterator2 = _createForOfIteratorHelper(additions),
67
+ _step2;
68
+ try {
69
+ var _loop = function _loop() {
70
+ var addition = _step2.value;
71
+ if (next.length >= MAX_PASTED_INLINE_LINKS) {
72
+ return 1; // break
73
+ }
74
+ if (!next.some(function (link) {
75
+ return isSameLink(link, addition);
76
+ })) {
77
+ next = [].concat((0, _toConsumableArray2.default)(next), [addition]);
78
+ }
79
+ };
80
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
81
+ if (_loop()) break;
82
+ }
83
+ } catch (err) {
84
+ _iterator2.e(err);
85
+ } finally {
86
+ _iterator2.f();
87
+ }
88
+ return next;
89
+ };
90
+ var removePastedInlineLink = exports.removePastedInlineLink = function removePastedInlineLink(links, target) {
91
+ var next = links.filter(function (link) {
92
+ return !isSameLink(link, target);
93
+ });
94
+ return next.length === links.length ? links : next;
95
+ };
96
+
97
+ /** Inline cards inserted by a paste transaction (copy and paste of existing cards). */
98
+ var getPastedInlineCards = exports.getPastedInlineCards = function getPastedInlineCards(tr) {
99
+ if (!tr.docChanged || tr.getMeta('uiEvent') !== 'paste' || tr.getMeta('isRemote')) {
100
+ return [];
101
+ }
102
+ var found = [];
103
+ tr.mapping.maps.forEach(function (stepMap, index) {
104
+ var later = tr.mapping.slice(index + 1);
105
+ stepMap.forEach(function (_oldStart, _oldEnd, newStart, newEnd) {
106
+ tr.doc.nodesBetween(later.map(newStart, -1), later.map(newEnd, 1), function (node, pos) {
107
+ if (node.type.name === 'inlineCard' && typeof node.attrs.url === 'string') {
108
+ found.push({
109
+ pos: pos,
110
+ url: node.attrs.url
111
+ });
112
+ }
113
+ });
114
+ });
115
+ });
116
+ return found;
117
+ };
118
+
119
+ /** Inline cards created from clipboard URLs that just resolved in this transaction. */
120
+ var getResolvedPastedInlineCards = exports.getResolvedPastedInlineCards = function getResolvedPastedInlineCards(tr, requests, resolvedUrl, clipboardSource) {
121
+ if (tr.getMeta('isRemote')) {
122
+ return [];
123
+ }
124
+ var found = [];
125
+ var _iterator3 = _createForOfIteratorHelper(requests),
126
+ _step3;
127
+ try {
128
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
129
+ var request = _step3.value;
130
+ if (request.url !== resolvedUrl || request.appearance !== 'inline' || request.source !== clipboardSource) {
131
+ continue;
132
+ }
133
+ var pos = tr.mapping.map(request.pos, -1);
134
+ if (pos < 0 || pos >= tr.doc.content.size) {
135
+ continue;
136
+ }
137
+ var node = tr.doc.nodeAt(pos);
138
+ if ((node === null || node === void 0 ? void 0 : node.type.name) === 'inlineCard' && node.attrs.url === request.url) {
139
+ found.push({
140
+ pos: pos,
141
+ url: request.url
142
+ });
143
+ }
144
+ }
145
+ } catch (err) {
146
+ _iterator3.e(err);
147
+ } finally {
148
+ _iterator3.f();
149
+ }
150
+ return found;
151
+ };
152
+ /** Add candidates unless tracking is off or a link has already been displayed. */
153
+ var withPastedInlineLinks = exports.withPastedInlineLinks = function withPastedInlineLinks(state, additions) {
154
+ if (!state.pastedInlineLinks || state.pastedInlineLinkShown || !additions.length) {
155
+ return state;
156
+ }
157
+ var next = addPastedInlineLinks(state.pastedInlineLinks, additions);
158
+ return next === state.pastedInlineLinks ? state : _objectSpread(_objectSpread({}, state), {}, {
159
+ pastedInlineLinks: next
160
+ });
161
+ };
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.getPluginStateWithUpdatedPos = exports.getPluginState = exports.getNewRequests = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _pluginKey = require("../plugin-key");
10
+ var _pastedInlineLink = require("./pasted-inline-link");
10
11
  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; }
11
12
  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; }
12
13
  // ============================================================================ //
@@ -19,7 +20,9 @@ var getPluginState = exports.getPluginState = function getPluginState(editorStat
19
20
  };
20
21
  var getPluginStateWithUpdatedPos = exports.getPluginStateWithUpdatedPos = function getPluginStateWithUpdatedPos(pluginState, tr) {
21
22
  var _pluginState$resolved;
22
- return _objectSpread(_objectSpread({}, pluginState), {}, {
23
+ return _objectSpread(_objectSpread(_objectSpread({}, pluginState), pluginState.pastedInlineLinks ? {
24
+ pastedInlineLinks: (0, _pastedInlineLink.mapPastedInlineLinks)(pluginState.pastedInlineLinks, tr)
25
+ } : {}), {}, {
23
26
  requests: pluginState.requests.map(function (request) {
24
27
  return _objectSpread(_objectSpread({}, request), {}, {
25
28
  pos: tr.mapping.map(request.pos)
@@ -12,7 +12,7 @@ import { createCardRegistry } from './actions/cardRegistry';
12
12
  import { blockCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/blockCard';
13
13
  import { embedCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/embedCard';
14
14
  import { inlineCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/inlineCard';
15
- import { hideLinkToolbar, setProvider, showDatasourceModal } from './pm-plugins/actions';
15
+ import { clearPastedInlineLink, markPastedInlineLinkShown, hideLinkToolbar, setProvider, showDatasourceModal } from './pm-plugins/actions';
16
16
  import { queueCardsFromChangedTr, queueCardsFromRange } from './pm-plugins/doc';
17
17
  import { cardKeymap } from './pm-plugins/keymap';
18
18
  import { createPlugin } from './pm-plugins/main';
@@ -199,6 +199,12 @@ export const cardPlugin = ({
199
199
  }));
200
200
  },
201
201
  commands: {
202
+ markPastedInlineLinkShown: target => ({
203
+ tr
204
+ }) => options.trackPastedInlineLinks ? markPastedInlineLinkShown(target)(tr) : null,
205
+ clearPastedInlineLink: target => ({
206
+ tr
207
+ }) => options.trackPastedInlineLinks ? clearPastedInlineLink(target)(tr) : null,
202
208
  queueCardsFromRange: (from, to, source, analyticsAction, normalizeLinkText, sourceEvent, appearance) => ({
203
209
  tr
204
210
  }) => queueCardsFromRange(
@@ -75,4 +75,16 @@ export const setDatasourceStash = (tr, datasourceStash) => cardAction(tr, {
75
75
  export const removeDatasourceStash = (tr, url) => cardAction(tr, {
76
76
  type: 'REMOVE_DATASOURCE_STASH',
77
77
  url
78
+ });
79
+
80
+ /** Confirm actual display, not merely target selection or a request to display. */
81
+ export const markPastedInlineLinkShown = target => tr => cardAction(tr, {
82
+ type: 'PASTED_INLINE_LINK_SHOWN',
83
+ target
84
+ });
85
+
86
+ /** Discard only this candidate; a stale callback must not clear a newer paste. */
87
+ export const clearPastedInlineLink = target => tr => cardAction(tr, {
88
+ type: 'CLEAR_PASTED_INLINE_LINK',
89
+ target
78
90
  });
@@ -1,4 +1,5 @@
1
1
  import rafSchedule from 'raf-schd';
2
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics/types/enums';
2
3
  import { getInlineNodeViewProducer } from '@atlaskit/editor-common/getInlineNodeViewProducer';
3
4
  import { isSSR } from '@atlaskit/editor-common/is-ssr';
4
5
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
@@ -17,6 +18,7 @@ import { isLocalStorageKeyDiscovered } from '../ui/local-storage';
17
18
  import { clearOverlayCandidate, setCardLayoutAndDatasourceTableRef, setDatasourceTableRef } from './actions';
18
19
  import { pluginKey } from './plugin-key';
19
20
  import reducer from './reducers';
21
+ import { getPastedInlineCards, getResolvedPastedInlineCards, withPastedInlineLinks } from './util/pasted-inline-link';
20
22
  import { handleProvider, resolveWithProvider } from './util/resolve';
21
23
  import { getNewRequests, getPluginState, getPluginStateWithUpdatedPos } from './util/state';
22
24
  import { isBlockSupportedAtPosition, isEmbedSupportedAtPosition } from './utils';
@@ -44,6 +46,7 @@ export const createPlugin = (options, pluginInjectionApi) => pmPluginFactoryPara
44
46
  fullWidthMode,
45
47
  actionOptions,
46
48
  cardPluginEvents,
49
+ trackPastedInlineLinks,
47
50
  showUpgradeDiscoverability,
48
51
  allowEmbeds,
49
52
  allowBlockCards,
@@ -79,6 +82,9 @@ export const createPlugin = (options, pluginInjectionApi) => pmPluginFactoryPara
79
82
  init() {
80
83
  return {
81
84
  requests: [],
85
+ ...(trackPastedInlineLinks ? {
86
+ pastedInlineLinks: []
87
+ } : {}),
82
88
  provider: null,
83
89
  cards: [],
84
90
  datasourceStash: {},
@@ -97,7 +103,9 @@ export const createPlugin = (options, pluginInjectionApi) => pmPluginFactoryPara
97
103
  var _pluginState$requests, _pluginState$requests2;
98
104
  // Update all the positions of outstanding requests and
99
105
  // cards in the plugin state.
100
- const pluginStateWithUpdatedPos = getPluginStateWithUpdatedPos(pluginState, tr);
106
+ let pluginStateWithUpdatedPos = getPluginStateWithUpdatedPos(pluginState, tr);
107
+ // Pasted inline cards that already exist as nodes (copy and paste of cards).
108
+ pluginStateWithUpdatedPos = withPastedInlineLinks(pluginStateWithUpdatedPos, trackPastedInlineLinks ? getPastedInlineCards(tr) : []);
101
109
 
102
110
  // apply any actions
103
111
  const meta = tr.getMeta(pluginKey);
@@ -119,8 +127,7 @@ export const createPlugin = (options, pluginInjectionApi) => pmPluginFactoryPara
119
127
  if (!meta) {
120
128
  return pluginStateWithUpdatedPos;
121
129
  }
122
- const newState = reducer(pluginStateWithUpdatedPos, meta);
123
-
130
+ let newState = reducer(pluginStateWithUpdatedPos, meta);
124
131
  // Track the first resolved inline smart link for PO spotlight DOM targeting
125
132
  if (meta.type === 'RESOLVE' && pluginState !== null && pluginState !== void 0 && (_pluginState$requests = pluginState.requests) !== null && _pluginState$requests !== void 0 && _pluginState$requests.length) {
126
133
  const resolvedRequest = pluginState.requests.find(req => req.url === meta.url);
@@ -136,6 +143,11 @@ export const createPlugin = (options, pluginInjectionApi) => pmPluginFactoryPara
136
143
  }
137
144
  }
138
145
  }
146
+
147
+ // Pasted URLs that were converted to inline cards.
148
+ if (trackPastedInlineLinks && meta.type === 'RESOLVE') {
149
+ newState = withPastedInlineLinks(newState, getResolvedPastedInlineCards(tr, pluginState.requests, meta.url, INPUT_METHOD.CLIPBOARD));
150
+ }
139
151
  if (!enableInlineUpgradeFeatures) {
140
152
  return newState;
141
153
  }
@@ -1,4 +1,5 @@
1
1
  import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
2
+ import { removePastedInlineLink } from './util/pasted-inline-link';
2
3
  const queue = (state, action) => {
3
4
  return {
4
5
  ...state,
@@ -149,6 +150,18 @@ const removeDatasourceStash = (state, action) => {
149
150
  };
150
151
  export default ((state, action) => {
151
152
  switch (action.type) {
153
+ case 'PASTED_INLINE_LINK_SHOWN':
154
+ return state.pastedInlineLinks ? {
155
+ ...state,
156
+ pastedInlineLinkShown: true,
157
+ // Keep only the displayed link so it can still be dismissed if it changes.
158
+ pastedInlineLinks: state.pastedInlineLinks.filter(link => link.pos === action.target.pos && link.url === action.target.url)
159
+ } : state;
160
+ case 'CLEAR_PASTED_INLINE_LINK':
161
+ return state.pastedInlineLinks ? {
162
+ ...state,
163
+ pastedInlineLinks: removePastedInlineLink(state.pastedInlineLinks, action.target)
164
+ } : state;
152
165
  case 'QUEUE':
153
166
  return queue(state, action);
154
167
  case 'SET_PROVIDER':
@@ -0,0 +1,112 @@
1
+ /** Bounds the per-editor work; the first pasted links win. */
2
+ export const MAX_PASTED_INLINE_LINKS = 10;
3
+ const isSameLink = (a, b) => a.pos === b.pos && a.url === b.url;
4
+
5
+ /**
6
+ * Map candidates through a transaction. Anything that was deleted, replaced by another node or
7
+ * given a different URL stops being a candidate. Returns the same array when nothing changed.
8
+ */
9
+ export const mapPastedInlineLinks = (links, tr) => {
10
+ if (tr.getMeta('replaceDocument')) {
11
+ return links.length ? [] : links;
12
+ }
13
+ if (!tr.docChanged || !links.length) {
14
+ return links;
15
+ }
16
+ let changed = false;
17
+ const mapped = [];
18
+ for (const link of links) {
19
+ const result = tr.mapping.mapResult(link.pos, 1);
20
+ const node = result.deleted ? null : tr.doc.nodeAt(result.pos);
21
+ if ((node === null || node === void 0 ? void 0 : node.type.name) !== 'inlineCard' || node.attrs.url !== link.url) {
22
+ changed = true;
23
+ continue;
24
+ }
25
+ if (result.pos !== link.pos) {
26
+ changed = true;
27
+ mapped.push({
28
+ pos: result.pos,
29
+ url: link.url
30
+ });
31
+ } else {
32
+ mapped.push(link);
33
+ }
34
+ }
35
+ return changed ? mapped : links;
36
+ };
37
+
38
+ /** Append new candidates, skipping duplicates and anything beyond the cap. */
39
+ export const addPastedInlineLinks = (links, additions) => {
40
+ let next = links;
41
+ for (const addition of additions) {
42
+ if (next.length >= MAX_PASTED_INLINE_LINKS) {
43
+ break;
44
+ }
45
+ if (!next.some(link => isSameLink(link, addition))) {
46
+ next = [...next, addition];
47
+ }
48
+ }
49
+ return next;
50
+ };
51
+ export const removePastedInlineLink = (links, target) => {
52
+ const next = links.filter(link => !isSameLink(link, target));
53
+ return next.length === links.length ? links : next;
54
+ };
55
+
56
+ /** Inline cards inserted by a paste transaction (copy and paste of existing cards). */
57
+ export const getPastedInlineCards = tr => {
58
+ if (!tr.docChanged || tr.getMeta('uiEvent') !== 'paste' || tr.getMeta('isRemote')) {
59
+ return [];
60
+ }
61
+ const found = [];
62
+ tr.mapping.maps.forEach((stepMap, index) => {
63
+ const later = tr.mapping.slice(index + 1);
64
+ stepMap.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
65
+ tr.doc.nodesBetween(later.map(newStart, -1), later.map(newEnd, 1), (node, pos) => {
66
+ if (node.type.name === 'inlineCard' && typeof node.attrs.url === 'string') {
67
+ found.push({
68
+ pos,
69
+ url: node.attrs.url
70
+ });
71
+ }
72
+ });
73
+ });
74
+ });
75
+ return found;
76
+ };
77
+
78
+ /** Inline cards created from clipboard URLs that just resolved in this transaction. */
79
+ export const getResolvedPastedInlineCards = (tr, requests, resolvedUrl, clipboardSource) => {
80
+ if (tr.getMeta('isRemote')) {
81
+ return [];
82
+ }
83
+ const found = [];
84
+ for (const request of requests) {
85
+ if (request.url !== resolvedUrl || request.appearance !== 'inline' || request.source !== clipboardSource) {
86
+ continue;
87
+ }
88
+ const pos = tr.mapping.map(request.pos, -1);
89
+ if (pos < 0 || pos >= tr.doc.content.size) {
90
+ continue;
91
+ }
92
+ const node = tr.doc.nodeAt(pos);
93
+ if ((node === null || node === void 0 ? void 0 : node.type.name) === 'inlineCard' && node.attrs.url === request.url) {
94
+ found.push({
95
+ pos,
96
+ url: request.url
97
+ });
98
+ }
99
+ }
100
+ return found;
101
+ };
102
+ /** Add candidates unless tracking is off or a link has already been displayed. */
103
+ export const withPastedInlineLinks = (state, additions) => {
104
+ if (!state.pastedInlineLinks || state.pastedInlineLinkShown || !additions.length) {
105
+ return state;
106
+ }
107
+ const next = addPastedInlineLinks(state.pastedInlineLinks, additions);
108
+ return next === state.pastedInlineLinks ? state : {
109
+ ...state,
110
+ pastedInlineLinks: next
111
+ };
112
+ };
@@ -1,4 +1,5 @@
1
1
  import { pluginKey } from '../plugin-key';
2
+ import { mapPastedInlineLinks } from './pasted-inline-link';
2
3
 
3
4
  // ============================================================================ //
4
5
  // ============================== PLUGIN STATE ================================ //
@@ -10,6 +11,9 @@ export const getPluginStateWithUpdatedPos = (pluginState, tr) => {
10
11
  var _pluginState$resolved;
11
12
  return {
12
13
  ...pluginState,
14
+ ...(pluginState.pastedInlineLinks ? {
15
+ pastedInlineLinks: mapPastedInlineLinks(pluginState.pastedInlineLinks, tr)
16
+ } : {}),
13
17
  requests: pluginState.requests.map(request => ({
14
18
  ...request,
15
19
  pos: tr.mapping.map(request.pos)
@@ -18,7 +18,7 @@ import { createCardRegistry } from './actions/cardRegistry';
18
18
  import { blockCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/blockCard';
19
19
  import { embedCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/embedCard';
20
20
  import { inlineCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/inlineCard';
21
- import { hideLinkToolbar, setProvider as _setProvider, showDatasourceModal } from './pm-plugins/actions';
21
+ import { clearPastedInlineLink as _clearPastedInlineLink, markPastedInlineLinkShown as _markPastedInlineLinkShown, hideLinkToolbar, setProvider as _setProvider, showDatasourceModal } from './pm-plugins/actions';
22
22
  import { queueCardsFromChangedTr, queueCardsFromRange as _queueCardsFromRange } from './pm-plugins/doc';
23
23
  import { cardKeymap } from './pm-plugins/keymap';
24
24
  import { createPlugin } from './pm-plugins/main';
@@ -196,9 +196,21 @@ export var cardPlugin = function cardPlugin(_ref2) {
196
196
  }));
197
197
  },
198
198
  commands: {
199
- queueCardsFromRange: function queueCardsFromRange(from, to, source, analyticsAction, normalizeLinkText, sourceEvent, appearance) {
199
+ markPastedInlineLinkShown: function markPastedInlineLinkShown(target) {
200
200
  return function (_ref6) {
201
201
  var tr = _ref6.tr;
202
+ return options.trackPastedInlineLinks ? _markPastedInlineLinkShown(target)(tr) : null;
203
+ };
204
+ },
205
+ clearPastedInlineLink: function clearPastedInlineLink(target) {
206
+ return function (_ref7) {
207
+ var tr = _ref7.tr;
208
+ return options.trackPastedInlineLinks ? _clearPastedInlineLink(target)(tr) : null;
209
+ };
210
+ },
211
+ queueCardsFromRange: function queueCardsFromRange(from, to, source, analyticsAction, normalizeLinkText, sourceEvent, appearance) {
212
+ return function (_ref8) {
213
+ var tr = _ref8.tr;
202
214
  return _queueCardsFromRange(
203
215
  // Synthesise the minimal EditorState shape the impl actually reads.
204
216
  {
@@ -231,8 +243,8 @@ export var cardPlugin = function cardPlugin(_ref2) {
231
243
  return _context.abrupt("return", false);
232
244
  case 2:
233
245
  previousCardProvider = provider;
234
- return _context.abrupt("return", (_api$core$actions$exe = api === null || api === void 0 ? void 0 : api.core.actions.execute(function (_ref7) {
235
- var tr = _ref7.tr;
246
+ return _context.abrupt("return", (_api$core$actions$exe = api === null || api === void 0 ? void 0 : api.core.actions.execute(function (_ref9) {
247
+ var tr = _ref9.tr;
236
248
  return _setProvider(provider)(tr);
237
249
  })) !== null && _api$core$actions$exe !== void 0 ? _api$core$actions$exe : false);
238
250
  case 3:
@@ -341,8 +353,8 @@ export var cardPlugin = function cardPlugin(_ref2) {
341
353
  pluginsOptions: _objectSpread({
342
354
  floatingToolbar: floatingToolbar(options, (_options$lpLinkPicker = options.lpLinkPicker) !== null && _options$lpLinkPicker !== void 0 ? _options$lpLinkPicker : false, options.linkPicker, api, options.disableFloatingToolbar)
343
355
  }, isRegisteredSlashCommandEnabled ? {} : {
344
- quickInsert: function quickInsert(_ref8) {
345
- var formatMessage = _ref8.formatMessage;
356
+ quickInsert: function quickInsert(_ref0) {
357
+ var formatMessage = _ref0.formatMessage;
346
358
  var quickInsertArray = [];
347
359
  if (!options.allowDatasource) {
348
360
  return quickInsertArray;
@@ -134,4 +134,24 @@ export var removeDatasourceStash = function removeDatasourceStash(tr, url) {
134
134
  type: 'REMOVE_DATASOURCE_STASH',
135
135
  url: url
136
136
  });
137
+ };
138
+
139
+ /** Confirm actual display, not merely target selection or a request to display. */
140
+ export var markPastedInlineLinkShown = function markPastedInlineLinkShown(target) {
141
+ return function (tr) {
142
+ return cardAction(tr, {
143
+ type: 'PASTED_INLINE_LINK_SHOWN',
144
+ target: target
145
+ });
146
+ };
147
+ };
148
+
149
+ /** Discard only this candidate; a stale callback must not clear a newer paste. */
150
+ export var clearPastedInlineLink = function clearPastedInlineLink(target) {
151
+ return function (tr) {
152
+ return cardAction(tr, {
153
+ type: 'CLEAR_PASTED_INLINE_LINK',
154
+ target: target
155
+ });
156
+ };
137
157
  };
@@ -1,8 +1,9 @@
1
- import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
3
  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; }
4
4
  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; }
5
5
  import rafSchedule from 'raf-schd';
6
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics/types/enums';
6
7
  import { getInlineNodeViewProducer } from '@atlaskit/editor-common/getInlineNodeViewProducer';
7
8
  import { isSSR } from '@atlaskit/editor-common/is-ssr';
8
9
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
@@ -21,6 +22,7 @@ import { isLocalStorageKeyDiscovered } from '../ui/local-storage';
21
22
  import { clearOverlayCandidate, setCardLayoutAndDatasourceTableRef, setDatasourceTableRef } from './actions';
22
23
  import { pluginKey } from './plugin-key';
23
24
  import reducer from './reducers';
25
+ import { getPastedInlineCards, getResolvedPastedInlineCards, withPastedInlineLinks } from './util/pasted-inline-link';
24
26
  import { handleProvider, resolveWithProvider } from './util/resolve';
25
27
  import { getNewRequests, getPluginState, getPluginStateWithUpdatedPos } from './util/state';
26
28
  import { isBlockSupportedAtPosition, isEmbedSupportedAtPosition } from './utils';
@@ -48,6 +50,7 @@ export var createPlugin = function createPlugin(options, pluginInjectionApi) {
48
50
  fullWidthMode = options.fullWidthMode,
49
51
  actionOptions = options.actionOptions,
50
52
  cardPluginEvents = options.cardPluginEvents,
53
+ trackPastedInlineLinks = options.trackPastedInlineLinks,
51
54
  showUpgradeDiscoverability = options.showUpgradeDiscoverability,
52
55
  allowEmbeds = options.allowEmbeds,
53
56
  allowBlockCards = options.allowBlockCards,
@@ -80,8 +83,11 @@ export var createPlugin = function createPlugin(options, pluginInjectionApi) {
80
83
  return new SafePlugin({
81
84
  state: {
82
85
  init: function init() {
83
- return {
84
- requests: [],
86
+ return _objectSpread(_objectSpread({
87
+ requests: []
88
+ }, trackPastedInlineLinks ? {
89
+ pastedInlineLinks: []
90
+ } : {}), {}, {
85
91
  provider: null,
86
92
  cards: [],
87
93
  datasourceStash: {},
@@ -94,13 +100,15 @@ export var createPlugin = function createPlugin(options, pluginInjectionApi) {
94
100
  datasourceModalType: undefined,
95
101
  datasourceTableRef: undefined,
96
102
  layout: undefined
97
- };
103
+ });
98
104
  },
99
105
  apply: function apply(tr, pluginState, prevEditorState) {
100
106
  var _pluginState$requests, _pluginState$requests2;
101
107
  // Update all the positions of outstanding requests and
102
108
  // cards in the plugin state.
103
109
  var pluginStateWithUpdatedPos = getPluginStateWithUpdatedPos(pluginState, tr);
110
+ // Pasted inline cards that already exist as nodes (copy and paste of cards).
111
+ pluginStateWithUpdatedPos = withPastedInlineLinks(pluginStateWithUpdatedPos, trackPastedInlineLinks ? getPastedInlineCards(tr) : []);
104
112
 
105
113
  // apply any actions
106
114
  var meta = tr.getMeta(pluginKey);
@@ -122,7 +130,6 @@ export var createPlugin = function createPlugin(options, pluginInjectionApi) {
122
130
  return pluginStateWithUpdatedPos;
123
131
  }
124
132
  var newState = reducer(pluginStateWithUpdatedPos, meta);
125
-
126
133
  // Track the first resolved inline smart link for PO spotlight DOM targeting
127
134
  if (meta.type === 'RESOLVE' && pluginState !== null && pluginState !== void 0 && (_pluginState$requests = pluginState.requests) !== null && _pluginState$requests !== void 0 && _pluginState$requests.length) {
128
135
  var resolvedRequest = pluginState.requests.find(function (req) {
@@ -140,6 +147,11 @@ export var createPlugin = function createPlugin(options, pluginInjectionApi) {
140
147
  }
141
148
  }
142
149
  }
150
+
151
+ // Pasted URLs that were converted to inline cards.
152
+ if (trackPastedInlineLinks && meta.type === 'RESOLVE') {
153
+ newState = withPastedInlineLinks(newState, getResolvedPastedInlineCards(tr, pluginState.requests, meta.url, INPUT_METHOD.CLIPBOARD));
154
+ }
143
155
  if (!enableInlineUpgradeFeatures) {
144
156
  return newState;
145
157
  }
@@ -6,6 +6,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
6
6
  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; }
7
7
  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; }
8
8
  import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
9
+ import { removePastedInlineLink } from './util/pasted-inline-link';
9
10
  var queue = function queue(state, action) {
10
11
  return _objectSpread(_objectSpread({}, state), {}, {
11
12
  requests: state.requests.concat(action.requests)
@@ -134,6 +135,18 @@ var removeDatasourceStash = function removeDatasourceStash(state, action) {
134
135
  };
135
136
  export default (function (state, action) {
136
137
  switch (action.type) {
138
+ case 'PASTED_INLINE_LINK_SHOWN':
139
+ return state.pastedInlineLinks ? _objectSpread(_objectSpread({}, state), {}, {
140
+ pastedInlineLinkShown: true,
141
+ // Keep only the displayed link so it can still be dismissed if it changes.
142
+ pastedInlineLinks: state.pastedInlineLinks.filter(function (link) {
143
+ return link.pos === action.target.pos && link.url === action.target.url;
144
+ })
145
+ }) : state;
146
+ case 'CLEAR_PASTED_INLINE_LINK':
147
+ return state.pastedInlineLinks ? _objectSpread(_objectSpread({}, state), {}, {
148
+ pastedInlineLinks: removePastedInlineLink(state.pastedInlineLinks, action.target)
149
+ }) : state;
137
150
  case 'QUEUE':
138
151
  return queue(state, action);
139
152
  case 'SET_PROVIDER':
@@ -0,0 +1,154 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
+ 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; }
4
+ 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; }
5
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
6
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
7
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
8
+ /** Bounds the per-editor work; the first pasted links win. */
9
+ export var MAX_PASTED_INLINE_LINKS = 10;
10
+ var isSameLink = function isSameLink(a, b) {
11
+ return a.pos === b.pos && a.url === b.url;
12
+ };
13
+
14
+ /**
15
+ * Map candidates through a transaction. Anything that was deleted, replaced by another node or
16
+ * given a different URL stops being a candidate. Returns the same array when nothing changed.
17
+ */
18
+ export var mapPastedInlineLinks = function mapPastedInlineLinks(links, tr) {
19
+ if (tr.getMeta('replaceDocument')) {
20
+ return links.length ? [] : links;
21
+ }
22
+ if (!tr.docChanged || !links.length) {
23
+ return links;
24
+ }
25
+ var changed = false;
26
+ var mapped = [];
27
+ var _iterator = _createForOfIteratorHelper(links),
28
+ _step;
29
+ try {
30
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
31
+ var link = _step.value;
32
+ var result = tr.mapping.mapResult(link.pos, 1);
33
+ var node = result.deleted ? null : tr.doc.nodeAt(result.pos);
34
+ if ((node === null || node === void 0 ? void 0 : node.type.name) !== 'inlineCard' || node.attrs.url !== link.url) {
35
+ changed = true;
36
+ continue;
37
+ }
38
+ if (result.pos !== link.pos) {
39
+ changed = true;
40
+ mapped.push({
41
+ pos: result.pos,
42
+ url: link.url
43
+ });
44
+ } else {
45
+ mapped.push(link);
46
+ }
47
+ }
48
+ } catch (err) {
49
+ _iterator.e(err);
50
+ } finally {
51
+ _iterator.f();
52
+ }
53
+ return changed ? mapped : links;
54
+ };
55
+
56
+ /** Append new candidates, skipping duplicates and anything beyond the cap. */
57
+ export var addPastedInlineLinks = function addPastedInlineLinks(links, additions) {
58
+ var next = links;
59
+ var _iterator2 = _createForOfIteratorHelper(additions),
60
+ _step2;
61
+ try {
62
+ var _loop = function _loop() {
63
+ var addition = _step2.value;
64
+ if (next.length >= MAX_PASTED_INLINE_LINKS) {
65
+ return 1; // break
66
+ }
67
+ if (!next.some(function (link) {
68
+ return isSameLink(link, addition);
69
+ })) {
70
+ next = [].concat(_toConsumableArray(next), [addition]);
71
+ }
72
+ };
73
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
74
+ if (_loop()) break;
75
+ }
76
+ } catch (err) {
77
+ _iterator2.e(err);
78
+ } finally {
79
+ _iterator2.f();
80
+ }
81
+ return next;
82
+ };
83
+ export var removePastedInlineLink = function removePastedInlineLink(links, target) {
84
+ var next = links.filter(function (link) {
85
+ return !isSameLink(link, target);
86
+ });
87
+ return next.length === links.length ? links : next;
88
+ };
89
+
90
+ /** Inline cards inserted by a paste transaction (copy and paste of existing cards). */
91
+ export var getPastedInlineCards = function getPastedInlineCards(tr) {
92
+ if (!tr.docChanged || tr.getMeta('uiEvent') !== 'paste' || tr.getMeta('isRemote')) {
93
+ return [];
94
+ }
95
+ var found = [];
96
+ tr.mapping.maps.forEach(function (stepMap, index) {
97
+ var later = tr.mapping.slice(index + 1);
98
+ stepMap.forEach(function (_oldStart, _oldEnd, newStart, newEnd) {
99
+ tr.doc.nodesBetween(later.map(newStart, -1), later.map(newEnd, 1), function (node, pos) {
100
+ if (node.type.name === 'inlineCard' && typeof node.attrs.url === 'string') {
101
+ found.push({
102
+ pos: pos,
103
+ url: node.attrs.url
104
+ });
105
+ }
106
+ });
107
+ });
108
+ });
109
+ return found;
110
+ };
111
+
112
+ /** Inline cards created from clipboard URLs that just resolved in this transaction. */
113
+ export var getResolvedPastedInlineCards = function getResolvedPastedInlineCards(tr, requests, resolvedUrl, clipboardSource) {
114
+ if (tr.getMeta('isRemote')) {
115
+ return [];
116
+ }
117
+ var found = [];
118
+ var _iterator3 = _createForOfIteratorHelper(requests),
119
+ _step3;
120
+ try {
121
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
122
+ var request = _step3.value;
123
+ if (request.url !== resolvedUrl || request.appearance !== 'inline' || request.source !== clipboardSource) {
124
+ continue;
125
+ }
126
+ var pos = tr.mapping.map(request.pos, -1);
127
+ if (pos < 0 || pos >= tr.doc.content.size) {
128
+ continue;
129
+ }
130
+ var node = tr.doc.nodeAt(pos);
131
+ if ((node === null || node === void 0 ? void 0 : node.type.name) === 'inlineCard' && node.attrs.url === request.url) {
132
+ found.push({
133
+ pos: pos,
134
+ url: request.url
135
+ });
136
+ }
137
+ }
138
+ } catch (err) {
139
+ _iterator3.e(err);
140
+ } finally {
141
+ _iterator3.f();
142
+ }
143
+ return found;
144
+ };
145
+ /** Add candidates unless tracking is off or a link has already been displayed. */
146
+ export var withPastedInlineLinks = function withPastedInlineLinks(state, additions) {
147
+ if (!state.pastedInlineLinks || state.pastedInlineLinkShown || !additions.length) {
148
+ return state;
149
+ }
150
+ var next = addPastedInlineLinks(state.pastedInlineLinks, additions);
151
+ return next === state.pastedInlineLinks ? state : _objectSpread(_objectSpread({}, state), {}, {
152
+ pastedInlineLinks: next
153
+ });
154
+ };
@@ -2,6 +2,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  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; }
3
3
  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; }
4
4
  import { pluginKey } from '../plugin-key';
5
+ import { mapPastedInlineLinks } from './pasted-inline-link';
5
6
 
6
7
  // ============================================================================ //
7
8
  // ============================== PLUGIN STATE ================================ //
@@ -13,7 +14,9 @@ export var getPluginState = function getPluginState(editorState) {
13
14
  };
14
15
  export var getPluginStateWithUpdatedPos = function getPluginStateWithUpdatedPos(pluginState, tr) {
15
16
  var _pluginState$resolved;
16
- return _objectSpread(_objectSpread({}, pluginState), {}, {
17
+ return _objectSpread(_objectSpread(_objectSpread({}, pluginState), pluginState.pastedInlineLinks ? {
18
+ pastedInlineLinks: mapPastedInlineLinks(pluginState.pastedInlineLinks, tr)
19
+ } : {}), {}, {
17
20
  requests: pluginState.requests.map(function (request) {
18
21
  return _objectSpread(_objectSpread({}, request), {}, {
19
22
  pos: tr.mapping.map(request.pos)
@@ -20,7 +20,7 @@ import type { ToolbarPlugin } from '@atlaskit/editor-plugin-toolbar/toolbar-plug
20
20
  import type { UiControlRegistryPlugin } from '@atlaskit/editor-plugin-ui-control-registry/ui-control-registry-plugin-type';
21
21
  import type { UserIntentPlugin } from '@atlaskit/editor-plugin-user-intent/user-intent-plugin-type';
22
22
  import type { WidthPlugin } from '@atlaskit/editor-plugin-width/width-plugin-type';
23
- import type { CardPluginOptions, CardPluginState } from './types';
23
+ import type { CardPluginOptions, CardPluginState, PastedInlineLink } from './types';
24
24
  type DummyAnnotationPlugin = NextEditorPlugin<'annotation', {
25
25
  actions: {
26
26
  setInlineCommentDraftState: (isDraft: boolean, inputMethod: INPUT_METHOD) => Command;
@@ -47,6 +47,10 @@ export type CardPluginDependencies = [
47
47
  export type CardPlugin = NextEditorPlugin<'card', {
48
48
  actions: CardPluginActions;
49
49
  commands: {
50
+ /** Stop tracking a pasted link, for example one the consumer found ineligible. */
51
+ clearPastedInlineLink: (target: PastedInlineLink) => EditorCommand;
52
+ /** Confirm that the consumer displayed the message for this pasted link. */
53
+ markPastedInlineLinkShown: (target: PastedInlineLink) => EditorCommand;
50
54
  /** EditorCommand form of `queueCardsFromRange`. Prefer over the action. */
51
55
  queueCardsFromRange: (from: number, to: number, source: CardReplacementInputMethod, analyticsAction?: ACTION, normalizeLinkText?: boolean, sourceEvent?: UIAnalyticsEvent | null, appearance?: CardAppearance) => EditorCommand;
52
56
  };
@@ -1 +1 @@
1
- export type { Request, CardPluginOptions, CardPluginState } from '../types/index';
1
+ export type { Request, CardPluginOptions, CardPluginState, PastedInlineLink } from '../types/index';
@@ -3,7 +3,7 @@ import type { DatasourceModalType } from '@atlaskit/editor-common/types/datasour
3
3
  import type { Transaction } from '@atlaskit/editor-prosemirror/state';
4
4
  import type { DatasourceAdfView } from '@atlaskit/linking-common/types';
5
5
  import type { SmartLinkEvents } from '@atlaskit/smart-card/smart-link-events';
6
- import type { CardInfo, CardPluginAction, Request, ToolbarResolvedAttributes } from '../types';
6
+ import type { CardInfo, CardPluginAction, PastedInlineLink, Request, ToolbarResolvedAttributes } from '../types';
7
7
  import type { DatasourceTableLayout } from '../ui/LayoutButton/types';
8
8
  export declare const cardAction: (tr: Transaction, action: CardPluginAction) => Transaction;
9
9
  export declare const resolveCard: (url: string) => (tr: Transaction) => Transaction;
@@ -30,3 +30,7 @@ export declare const setDatasourceStash: (tr: Transaction, datasourceStash: {
30
30
  views: DatasourceAdfView[];
31
31
  }) => Transaction;
32
32
  export declare const removeDatasourceStash: (tr: Transaction, url: string) => Transaction;
33
+ /** Confirm actual display, not merely target selection or a request to display. */
34
+ export declare const markPastedInlineLinkShown: (target: PastedInlineLink) => (tr: Transaction) => Transaction;
35
+ /** Discard only this candidate; a stale callback must not clear a newer paste. */
36
+ export declare const clearPastedInlineLink: (target: PastedInlineLink) => (tr: Transaction) => Transaction;
@@ -0,0 +1,28 @@
1
+ import type { ReadonlyTransaction, Transaction } from '@atlaskit/editor-prosemirror/state';
2
+ import type { PastedInlineLink } from '../../types';
3
+ /** Bounds the per-editor work; the first pasted links win. */
4
+ export declare const MAX_PASTED_INLINE_LINKS = 10;
5
+ /**
6
+ * Map candidates through a transaction. Anything that was deleted, replaced by another node or
7
+ * given a different URL stops being a candidate. Returns the same array when nothing changed.
8
+ */
9
+ export declare const mapPastedInlineLinks: (links: PastedInlineLink[], tr: ReadonlyTransaction | Transaction) => PastedInlineLink[];
10
+ /** Append new candidates, skipping duplicates and anything beyond the cap. */
11
+ export declare const addPastedInlineLinks: (links: PastedInlineLink[], additions: PastedInlineLink[]) => PastedInlineLink[];
12
+ export declare const removePastedInlineLink: (links: PastedInlineLink[], target: PastedInlineLink) => PastedInlineLink[];
13
+ /** Inline cards inserted by a paste transaction (copy and paste of existing cards). */
14
+ export declare const getPastedInlineCards: (tr: ReadonlyTransaction | Transaction) => PastedInlineLink[];
15
+ /** Inline cards created from clipboard URLs that just resolved in this transaction. */
16
+ export declare const getResolvedPastedInlineCards: (tr: ReadonlyTransaction | Transaction, requests: Array<{
17
+ appearance?: string;
18
+ pos: number;
19
+ source?: string;
20
+ url: string;
21
+ }>, resolvedUrl: string, clipboardSource: string) => PastedInlineLink[];
22
+ type PastedInlineLinkState = {
23
+ pastedInlineLinks?: PastedInlineLink[];
24
+ pastedInlineLinkShown?: boolean;
25
+ };
26
+ /** Add candidates unless tracking is off or a link has already been displayed. */
27
+ export declare const withPastedInlineLinks: <State extends PastedInlineLinkState>(state: State, additions: PastedInlineLink[]) => State;
28
+ export {};
@@ -4,13 +4,13 @@ import type { CardReplacementInputMethod } from '@atlaskit/editor-common/card/ty
4
4
  import type { CardAppearance, CardProvider } from '@atlaskit/editor-common/provider-factory/card-provider';
5
5
  import type { DatasourceModalType } from '@atlaskit/editor-common/types/datasource';
6
6
  import type { EditorAppearance } from '@atlaskit/editor-common/types/editor-appearance';
7
- import type { EditorState, ReadonlyTransaction } from '@atlaskit/editor-prosemirror/state';
7
+ import type { EditorState, ReadonlyTransaction, Transaction } from '@atlaskit/editor-prosemirror/state';
8
8
  import type { DatasourceAdfView } from '@atlaskit/linking-common/types';
9
9
  import type { SmartLinkEvents } from '@atlaskit/smart-card/smart-link-events';
10
- import type { CardPluginState, Request, ToolbarResolvedAttributes } from '../../types';
10
+ import type { CardPluginState, Request, ToolbarResolvedAttributes, PastedInlineLink } from '../../types';
11
11
  import type { DatasourceTableLayout } from '../../ui/LayoutButton/types';
12
12
  export declare const getPluginState: (editorState: EditorState) => CardPluginState | undefined;
13
- export declare const getPluginStateWithUpdatedPos: (pluginState: CardPluginState, tr: ReadonlyTransaction) => {
13
+ export declare const getPluginStateWithUpdatedPos: (pluginState: CardPluginState, tr: ReadonlyTransaction | Transaction) => {
14
14
  allowBlockCards?: boolean;
15
15
  allowEmbeds?: boolean;
16
16
  cards: {
@@ -30,6 +30,8 @@ export declare const getPluginStateWithUpdatedPos: (pluginState: CardPluginState
30
30
  inlineCardAwarenessCandidatePosition?: number;
31
31
  layout?: DatasourceTableLayout;
32
32
  overlayCandidatePosition?: number;
33
+ pastedInlineLinks?: PastedInlineLink[];
34
+ pastedInlineLinkShown?: boolean;
33
35
  provider: CardProvider | null;
34
36
  removeOverlay?: () => void;
35
37
  removeOverlayCardId?: string;
@@ -90,6 +90,11 @@ export type Request = {
90
90
  type DatasourceStash = Record<string, {
91
91
  views: DatasourceAdfView[];
92
92
  }>;
93
+ /** The single eligible locally pasted inline link selected for a product nudge. */
94
+ export type PastedInlineLink = {
95
+ pos: number;
96
+ url: string;
97
+ };
93
98
  export type CardPluginState = {
94
99
  allowBlockCards?: boolean;
95
100
  allowEmbeds?: boolean;
@@ -102,6 +107,13 @@ export type CardPluginState = {
102
107
  inlineCardAwarenessCandidatePosition?: number;
103
108
  layout?: DatasourceTableLayout;
104
109
  overlayCandidatePosition?: number;
110
+ /**
111
+ * Locally pasted inline links, mapped through edits. Defined only when `trackPastedInlineLinks`
112
+ * is on. Once the consumer confirms display this holds only the displayed link.
113
+ */
114
+ pastedInlineLinks?: PastedInlineLink[];
115
+ /** Permanently stop adding candidates after the consumer confirms actual display. */
116
+ pastedInlineLinkShown?: boolean;
105
117
  provider: CardProvider | null;
106
118
  removeOverlay?: () => void;
107
119
  removeOverlayCardId?: string;
@@ -133,6 +145,8 @@ export type CardPluginOptions = CardOptions & {
133
145
  lpLinkPicker?: boolean;
134
146
  onClickCallback?: OnClickCallback;
135
147
  smartCardContext?: CardContext;
148
+ /** Opt in to tracking locally pasted inline links in plugin state. */
149
+ trackPastedInlineLinks?: boolean;
136
150
  };
137
151
  export type SetProvider = {
138
152
  provider: CardProvider | null;
@@ -208,5 +222,11 @@ export type RemoveDatasourceStash = {
208
222
  type: 'REMOVE_DATASOURCE_STASH';
209
223
  url: string;
210
224
  };
211
- export type CardPluginAction = SetProvider | Queue | Resolve | Register | RemoveCard | ShowLinkToolbar | HideLinkToolbar | ShowDatasourceModal | HideDatasourceModal | RegisterSmartCardEvents | SetDatasourceTableRef | SetResolvedToolbarAttributes | SetCardLayout | SetCardLayoutAndDatasourceTableRef | ClearOverlayCandidate | RegisterRemoveOverlayOnInsertedLink | SetDatasourceStash | RemoveDatasourceStash;
225
+ export type CardPluginAction = {
226
+ target: PastedInlineLink;
227
+ type: 'PASTED_INLINE_LINK_SHOWN';
228
+ } | {
229
+ target: PastedInlineLink;
230
+ type: 'CLEAR_PASTED_INLINE_LINK';
231
+ } | SetProvider | Queue | Resolve | Register | RemoveCard | ShowLinkToolbar | HideLinkToolbar | ShowDatasourceModal | HideDatasourceModal | RegisterSmartCardEvents | SetDatasourceTableRef | SetResolvedToolbarAttributes | SetCardLayout | SetCardLayoutAndDatasourceTableRef | ClearOverlayCandidate | RegisterRemoveOverlayOnInsertedLink | SetDatasourceStash | RemoveDatasourceStash;
212
232
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-card",
3
- "version": "35.1.4",
3
+ "version": "35.2.0",
4
4
  "description": "Card plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -28,7 +28,7 @@
28
28
  "@atlaskit/custom-steps": "^1.1.0",
29
29
  "@atlaskit/editor-card-provider": "^8.2.0",
30
30
  "@atlaskit/editor-plugin-analytics": "^29.0.0",
31
- "@atlaskit/editor-plugin-annotation": "^29.0.0",
31
+ "@atlaskit/editor-plugin-annotation": "^29.1.0",
32
32
  "@atlaskit/editor-plugin-base": "^30.0.0",
33
33
  "@atlaskit/editor-plugin-connectivity": "^29.0.0",
34
34
  "@atlaskit/editor-plugin-decorations": "^29.0.0",