@atlaskit/editor-plugin-selection-extension 3.0.1 → 3.1.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 (32) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cjs/pm-plugins/actions.js +69 -0
  3. package/dist/cjs/pm-plugins/main.js +5 -0
  4. package/dist/cjs/pm-plugins/utils/getOffsetByPath.js +83 -0
  5. package/dist/cjs/pm-plugins/utils/index.js +23 -6
  6. package/dist/cjs/selectionExtensionPlugin.js +29 -2
  7. package/dist/cjs/types/index.js +1 -0
  8. package/dist/es2019/pm-plugins/actions.js +67 -0
  9. package/dist/es2019/pm-plugins/main.js +6 -0
  10. package/dist/es2019/pm-plugins/utils/getOffsetByPath.js +76 -0
  11. package/dist/es2019/pm-plugins/utils/index.js +22 -5
  12. package/dist/es2019/selectionExtensionPlugin.js +31 -2
  13. package/dist/es2019/types/index.js +1 -0
  14. package/dist/esm/pm-plugins/actions.js +63 -0
  15. package/dist/esm/pm-plugins/main.js +5 -0
  16. package/dist/esm/pm-plugins/utils/getOffsetByPath.js +78 -0
  17. package/dist/esm/pm-plugins/utils/index.js +21 -5
  18. package/dist/esm/selectionExtensionPlugin.js +29 -2
  19. package/dist/esm/types/index.js +1 -0
  20. package/dist/types/pm-plugins/actions.d.ts +8 -0
  21. package/dist/types/pm-plugins/main.d.ts +10 -0
  22. package/dist/types/pm-plugins/utils/getOffsetByPath.d.ts +14 -0
  23. package/dist/types/pm-plugins/utils/index.d.ts +12 -2
  24. package/dist/types/selectionExtensionPluginType.d.ts +8 -1
  25. package/dist/types/types/index.d.ts +14 -1
  26. package/dist/types-ts4.5/pm-plugins/actions.d.ts +8 -0
  27. package/dist/types-ts4.5/pm-plugins/main.d.ts +10 -0
  28. package/dist/types-ts4.5/pm-plugins/utils/getOffsetByPath.d.ts +14 -0
  29. package/dist/types-ts4.5/pm-plugins/utils/index.d.ts +12 -2
  30. package/dist/types-ts4.5/selectionExtensionPluginType.d.ts +8 -1
  31. package/dist/types-ts4.5/types/index.d.ts +14 -1
  32. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @atlaskit/editor-plugin-selection-extension
2
2
 
3
+ ## 3.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#181784](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/181784)
8
+ [`e3cd18b4fc263`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/e3cd18b4fc263) -
9
+ Create a new action insertSmartLinks to allow inserting multiple smart links in page
10
+
3
11
  ## 3.0.1
4
12
 
5
13
  ### Patch Changes
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.insertSmartLinks = void 0;
7
+ var _main = require("./main");
8
+ var _utils = require("./utils");
9
+ var _getOffsetByPath = require("./utils/getOffsetByPath");
10
+ var insertLinkTr = function insertLinkTr(tr, link, offset, schema) {
11
+ var newFromPos = tr.mapping.map(offset.from);
12
+ var newToPos = tr.mapping.map(offset.to || offset.from);
13
+ var smartLink = schema.nodes.inlineCard.createChecked({
14
+ url: link
15
+ });
16
+ return tr.replaceWith(newFromPos, newToPos, smartLink);
17
+ };
18
+ var insertSmartLinks = exports.insertSmartLinks = function insertSmartLinks(linkInsertionOption, selectedNodeAdf) {
19
+ return function (state, dispatch) {
20
+ var tr = state.tr,
21
+ schema = state.schema;
22
+ if (!Array.isArray(linkInsertionOption)) {
23
+ linkInsertionOption = [linkInsertionOption];
24
+ }
25
+ if (linkInsertionOption.length === 0) {
26
+ return {
27
+ status: 'error',
28
+ message: 'No link insertion options provided'
29
+ };
30
+ }
31
+
32
+ // TODO: ED-28365 - we need to track if any changes were made since user click the toolbar button
33
+ // if there is change, we should insert the link at the bottom of the page
34
+
35
+ var newTr = tr;
36
+ try {
37
+ var _selectionExtensionPl, _selectionExtensionPl2;
38
+ var selectedNode = (_selectionExtensionPl = _main.selectionExtensionPluginKey.getState(state)) === null || _selectionExtensionPl === void 0 ? void 0 : _selectionExtensionPl.selectedNode;
39
+ var nodePos = (_selectionExtensionPl2 = _main.selectionExtensionPluginKey.getState(state)) === null || _selectionExtensionPl2 === void 0 ? void 0 : _selectionExtensionPl2.nodePos;
40
+ if (!selectedNode || nodePos === undefined) {
41
+ throw new Error('No selected node or node position found');
42
+ }
43
+
44
+ // Validate if the selectedNodeAdf matches the selected node we have in the state
45
+ if (!(0, _utils.validateSelectedNode)(selectedNodeAdf, selectedNode)) {
46
+ throw new Error('Selected node ADF does not match the previous stored node');
47
+ }
48
+ linkInsertionOption.forEach(function (option) {
49
+ var link = option.link,
50
+ insertPosition = option.insertPosition;
51
+ var pointer = insertPosition.pointer,
52
+ from = insertPosition.from,
53
+ to = insertPosition.to;
54
+ var offset = (0, _getOffsetByPath.getOffsetByPath)(selectedNode, nodePos, pointer, from, to);
55
+ newTr = insertLinkTr(tr, link, offset, schema);
56
+ });
57
+ } catch (error) {
58
+ return {
59
+ status: 'error',
60
+ message: error instanceof Error ? error.message : 'Unknown error'
61
+ };
62
+ }
63
+ dispatch(newTr);
64
+ return {
65
+ status: 'success',
66
+ message: 'Links inserted successfully'
67
+ };
68
+ };
69
+ };
@@ -32,6 +32,11 @@ var createPlugin = exports.createPlugin = function createPlugin() {
32
32
  return _objectSpread(_objectSpread({}, pluginState), {}, {
33
33
  activeExtension: undefined
34
34
  });
35
+ case _types.SelectionExtensionActionTypes.SET_SELECTED_NODE:
36
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
37
+ selectedNode: meta.selectedNode,
38
+ nodePos: meta.nodePos
39
+ });
35
40
  }
36
41
  return pluginState;
37
42
  }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getOffsetByPath = getOffsetByPath;
7
+ var _model = require("@atlaskit/editor-prosemirror/model");
8
+ // TODO: ED-28434 - move this to a shared package
9
+ // mirror code from https://bitbucket.org/atlassian/pf-adf-service/src/master/src/lib/update/get-offset.ts
10
+
11
+ function getOffsetByPath(root, pos, pointer) {
12
+ var from = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
13
+ var to = arguments.length > 4 ? arguments[4] : undefined;
14
+ var parts = pointer.split('/');
15
+ var ref = root;
16
+ var len = parts.length;
17
+ // -1 to account for the root node (usually doc)
18
+ // The start of the document, right before the first content, is position 0.
19
+ var offset = pos; //-1;
20
+
21
+ for (var i = 1; i < len; i++) {
22
+ var key = parts[i];
23
+ if (ref instanceof _model.Fragment && /^[0-9]+$/.test(key)) {
24
+ var index = parseInt(key, 10);
25
+ if (index >= ref.childCount) {
26
+ throw new Error("JSON pointer \"".concat(pointer, "\" points to non-existing location."));
27
+ }
28
+ var nextRef = ref.child(index);
29
+ while (index--) {
30
+ offset += ref.child(index).nodeSize;
31
+ }
32
+ ref = nextRef;
33
+ } else if (ref instanceof _model.Node) {
34
+ /**
35
+ * Reference: https://prosemirror.net/docs/guide/#doc.data_structures
36
+ * +----------------------------------+
37
+ * | Node |
38
+ * | ^^^^ |
39
+ * | type: NodeType |
40
+ * | content: Fragment |
41
+ * | [ Node, Node, ...] |
42
+ * | attrs: Object |
43
+ * | marks: Mark |
44
+ * | [ |
45
+ * | type: MarkType |
46
+ * | attrs: Object |
47
+ * | ] |
48
+ * +----------------------------------+
49
+ */
50
+ switch (key) {
51
+ case 'content':
52
+ // Entering or leaving a node that is not a leaf node (i.e. supports content) counts as one token.
53
+ offset++;
54
+ ref = ref.content;
55
+ break;
56
+ case 'attrs':
57
+ return {
58
+ type: 'attrs',
59
+ from: offset + from,
60
+ path: parts.slice(i + 1)
61
+ };
62
+ case 'text':
63
+ if (!ref.isText) {
64
+ throw new Error("\"".concat(parts.slice(0, i).join('/'), "\" doesn't have any \"text\" node!"));
65
+ }
66
+ continue;
67
+ default:
68
+ throw new Error("JSON pointer \"".concat(pointer, "\" points to an unsupported location."));
69
+ }
70
+ } else {
71
+ throw new Error("JSON pointer \"".concat(pointer, "\" points to an unsupported entity."));
72
+ }
73
+ }
74
+ if (ref instanceof _model.Fragment) {
75
+ throw new Error("Expected a Node, but the JSON pointer \"".concat(pointer, "\" points to a Fragment."));
76
+ }
77
+ return {
78
+ type: 'node',
79
+ from: offset + from,
80
+ to: offset + (to !== null && to !== void 0 ? to : ref.nodeSize),
81
+ matches: [to ? ref.textBetween(from, to) : ref.textContent]
82
+ };
83
+ }
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
- exports.getSelectionInfo = void 0;
7
+ exports.validateSelectedNode = exports.getSelectionInfo = void 0;
8
+ var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
7
9
  var _editorJsonTransformer = require("@atlaskit/editor-json-transformer");
8
10
  var _state = require("@atlaskit/editor-prosemirror/state");
9
11
  var _editorTables = require("@atlaskit/editor-tables");
@@ -30,11 +32,13 @@ var getSelectionInfoFromSameNode = function getSelectionInfoFromSameNode(selecti
30
32
  pointer: "/content/".concat($from.index(), "/text"),
31
33
  position: $to.parentOffset - 1
32
34
  }
33
- }]
35
+ }],
36
+ nodePos: $from.before() // position before the selection
34
37
  };
35
38
  };
36
39
  var getSelectionInfoFromCellSelection = function getSelectionInfoFromCellSelection(selection) {
37
40
  var selectedNode = selection.$anchorCell.node(-1);
41
+ var nodePos = selection.$anchorCell.before(-1);
38
42
  var selectionRanges = [];
39
43
  var rect = getSelectedRect(selection);
40
44
  for (var row = rect.top; row < rect.bottom; row++) {
@@ -49,14 +53,16 @@ var getSelectionInfoFromCellSelection = function getSelectionInfoFromCellSelecti
49
53
  }
50
54
  return {
51
55
  selectedNode: selectedNode,
52
- selectionRanges: selectionRanges
56
+ selectionRanges: selectionRanges,
57
+ nodePos: nodePos
53
58
  };
54
59
  };
55
60
  var getSelectionInfo = exports.getSelectionInfo = function getSelectionInfo(state) {
56
61
  var selection = state.selection;
57
62
  var selectionInfo = {
58
63
  selectedNode: selection.$from.node(),
59
- selectionRanges: []
64
+ selectionRanges: [],
65
+ nodePos: selection.$from.before() // default to the position before the selection
60
66
  };
61
67
  if (selection instanceof _state.TextSelection) {
62
68
  var $from = selection.$from,
@@ -70,9 +76,20 @@ var getSelectionInfo = exports.getSelectionInfo = function getSelectionInfo(stat
70
76
  selectionInfo = getSelectionInfoFromCellSelection(selection);
71
77
  }
72
78
  var serializer = new _editorJsonTransformer.JSONTransformer();
73
- var selectedNodeAdf = serializer.encodeNode(selectionInfo.selectedNode);
79
+ var _selectionInfo = selectionInfo,
80
+ selectionRanges = _selectionInfo.selectionRanges,
81
+ selectedNode = _selectionInfo.selectedNode,
82
+ nodePos = _selectionInfo.nodePos;
83
+ var selectedNodeAdf = serializer.encodeNode(selectedNode);
74
84
  return {
75
85
  selectedNodeAdf: selectedNodeAdf,
76
- selectionRanges: selectionInfo.selectionRanges
86
+ selectionRanges: selectionRanges,
87
+ selectedNode: selectedNode,
88
+ nodePos: nodePos
77
89
  };
90
+ };
91
+ var validateSelectedNode = exports.validateSelectedNode = function validateSelectedNode(selectedNodeAdf, selectedNode) {
92
+ var serializer = new _editorJsonTransformer.JSONTransformer();
93
+ var selectedNodeAdfFromState = serializer.encodeNode(selectedNode);
94
+ return (0, _isEqual.default)(selectedNodeAdf, selectedNodeAdfFromState);
78
95
  };
@@ -10,8 +10,10 @@ var _react = _interopRequireDefault(require("react"));
10
10
  var _messages = require("@atlaskit/editor-common/messages");
11
11
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
12
12
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
13
+ var _actions = require("./pm-plugins/actions");
13
14
  var _main = require("./pm-plugins/main");
14
15
  var _utils = require("./pm-plugins/utils");
16
+ var _types = require("./types");
15
17
  var _SelectionExtensionComponentWrapper = require("./ui/extension/SelectionExtensionComponentWrapper");
16
18
  var _getBoundingBoxFromSelection = require("./ui/getBoundingBoxFromSelection");
17
19
  var _selectionToolbar2 = require("./ui/selectionToolbar");
@@ -50,6 +52,20 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
50
52
  };
51
53
  }
52
54
  },
55
+ actions: {
56
+ insertSmartLinks: function insertSmartLinks(linkInsertionOptions, selectedNodeAdf) {
57
+ if (!editorViewRef.current) {
58
+ return {
59
+ status: 'error',
60
+ message: 'Editor view is not available'
61
+ };
62
+ }
63
+ var _editorViewRef$curren = editorViewRef.current,
64
+ state = _editorViewRef$curren.state,
65
+ dispatch = _editorViewRef$curren.dispatch;
66
+ return (0, _actions.insertSmartLinks)(linkInsertionOptions, selectedNodeAdf)(state, dispatch);
67
+ }
68
+ },
53
69
  contentComponent: function contentComponent(_ref4) {
54
70
  var _api$analytics;
55
71
  var editorView = _ref4.editorView;
@@ -137,15 +153,26 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
137
153
  selection: selection
138
154
  };
139
155
  if ((0, _platformFeatureFlags.fg)('platform_editor_selection_extension_api_v2')) {
140
- var _extension$onClick;
156
+ var _extension$onClick, _api$core;
141
157
  var _getSelectionInfo = (0, _utils.getSelectionInfo)(view.state),
142
158
  selectedNodeAdf = _getSelectionInfo.selectedNodeAdf,
143
- selectionRanges = _getSelectionInfo.selectionRanges;
159
+ selectionRanges = _getSelectionInfo.selectionRanges,
160
+ selectedNode = _getSelectionInfo.selectedNode,
161
+ nodePos = _getSelectionInfo.nodePos;
144
162
  onClickCallbackOptions = {
145
163
  selectedNodeAdf: selectedNodeAdf,
146
164
  selectionRanges: selectionRanges
147
165
  };
148
166
  (_extension$onClick = extension.onClick) === null || _extension$onClick === void 0 || _extension$onClick.call(extension, onClickCallbackOptions);
167
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref5) {
168
+ var tr = _ref5.tr;
169
+ tr.setMeta(_main.selectionExtensionPluginKey, {
170
+ type: _types.SelectionExtensionActionTypes.SET_SELECTED_NODE,
171
+ selectedNode: selectedNode,
172
+ nodePos: nodePos
173
+ });
174
+ return tr;
175
+ });
149
176
  } else {
150
177
  if (extension.onClick) {
151
178
  extension.onClick(onClickCallbackOptions);
@@ -14,5 +14,6 @@ var SelectionExtensionActionTypes = exports.SelectionExtensionActionTypes = /*#_
14
14
  SelectionExtensionActionTypes["SET_ACTIVE_EXTENSION"] = "set-active-extension";
15
15
  SelectionExtensionActionTypes["UPDATE_ACTIVE_EXTENSION_COORDS"] = "update-active-extension-coords";
16
16
  SelectionExtensionActionTypes["CLEAR_ACTIVE_EXTENSION"] = "clear-active-extension";
17
+ SelectionExtensionActionTypes["SET_SELECTED_NODE"] = "set-selected-node";
17
18
  return SelectionExtensionActionTypes;
18
19
  }({});
@@ -0,0 +1,67 @@
1
+ import { selectionExtensionPluginKey } from './main';
2
+ import { validateSelectedNode } from './utils';
3
+ import { getOffsetByPath } from './utils/getOffsetByPath';
4
+ const insertLinkTr = (tr, link, offset, schema) => {
5
+ const newFromPos = tr.mapping.map(offset.from);
6
+ const newToPos = tr.mapping.map(offset.to || offset.from);
7
+ const smartLink = schema.nodes.inlineCard.createChecked({
8
+ url: link
9
+ });
10
+ return tr.replaceWith(newFromPos, newToPos, smartLink);
11
+ };
12
+ export const insertSmartLinks = (linkInsertionOption, selectedNodeAdf) => (state, dispatch) => {
13
+ const {
14
+ tr,
15
+ schema
16
+ } = state;
17
+ if (!Array.isArray(linkInsertionOption)) {
18
+ linkInsertionOption = [linkInsertionOption];
19
+ }
20
+ if (linkInsertionOption.length === 0) {
21
+ return {
22
+ status: 'error',
23
+ message: 'No link insertion options provided'
24
+ };
25
+ }
26
+
27
+ // TODO: ED-28365 - we need to track if any changes were made since user click the toolbar button
28
+ // if there is change, we should insert the link at the bottom of the page
29
+
30
+ let newTr = tr;
31
+ try {
32
+ var _selectionExtensionPl, _selectionExtensionPl2;
33
+ const selectedNode = (_selectionExtensionPl = selectionExtensionPluginKey.getState(state)) === null || _selectionExtensionPl === void 0 ? void 0 : _selectionExtensionPl.selectedNode;
34
+ const nodePos = (_selectionExtensionPl2 = selectionExtensionPluginKey.getState(state)) === null || _selectionExtensionPl2 === void 0 ? void 0 : _selectionExtensionPl2.nodePos;
35
+ if (!selectedNode || nodePos === undefined) {
36
+ throw new Error('No selected node or node position found');
37
+ }
38
+
39
+ // Validate if the selectedNodeAdf matches the selected node we have in the state
40
+ if (!validateSelectedNode(selectedNodeAdf, selectedNode)) {
41
+ throw new Error('Selected node ADF does not match the previous stored node');
42
+ }
43
+ linkInsertionOption.forEach(option => {
44
+ const {
45
+ link,
46
+ insertPosition
47
+ } = option;
48
+ const {
49
+ pointer,
50
+ from,
51
+ to
52
+ } = insertPosition;
53
+ const offset = getOffsetByPath(selectedNode, nodePos, pointer, from, to);
54
+ newTr = insertLinkTr(tr, link, offset, schema);
55
+ });
56
+ } catch (error) {
57
+ return {
58
+ status: 'error',
59
+ message: error instanceof Error ? error.message : 'Unknown error'
60
+ };
61
+ }
62
+ dispatch(newTr);
63
+ return {
64
+ status: 'success',
65
+ message: 'Links inserted successfully'
66
+ };
67
+ };
@@ -24,6 +24,12 @@ export const createPlugin = () => {
24
24
  ...pluginState,
25
25
  activeExtension: undefined
26
26
  };
27
+ case SelectionExtensionActionTypes.SET_SELECTED_NODE:
28
+ return {
29
+ ...pluginState,
30
+ selectedNode: meta.selectedNode,
31
+ nodePos: meta.nodePos
32
+ };
27
33
  }
28
34
  return pluginState;
29
35
  }
@@ -0,0 +1,76 @@
1
+ import { Fragment, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
2
+
3
+ // TODO: ED-28434 - move this to a shared package
4
+ // mirror code from https://bitbucket.org/atlassian/pf-adf-service/src/master/src/lib/update/get-offset.ts
5
+
6
+ export function getOffsetByPath(root, pos, pointer, from = 0, to) {
7
+ const parts = pointer.split('/');
8
+ let ref = root;
9
+ const len = parts.length;
10
+ // -1 to account for the root node (usually doc)
11
+ // The start of the document, right before the first content, is position 0.
12
+ let offset = pos; //-1;
13
+
14
+ for (let i = 1; i < len; i++) {
15
+ const key = parts[i];
16
+ if (ref instanceof Fragment && /^[0-9]+$/u.test(key)) {
17
+ let index = parseInt(key, 10);
18
+ if (index >= ref.childCount) {
19
+ throw new Error(`JSON pointer "${pointer}" points to non-existing location.`);
20
+ }
21
+ const nextRef = ref.child(index);
22
+ while (index--) {
23
+ offset += ref.child(index).nodeSize;
24
+ }
25
+ ref = nextRef;
26
+ } else if (ref instanceof PMNode) {
27
+ /**
28
+ * Reference: https://prosemirror.net/docs/guide/#doc.data_structures
29
+ * +----------------------------------+
30
+ * | Node |
31
+ * | ^^^^ |
32
+ * | type: NodeType |
33
+ * | content: Fragment |
34
+ * | [ Node, Node, ...] |
35
+ * | attrs: Object |
36
+ * | marks: Mark |
37
+ * | [ |
38
+ * | type: MarkType |
39
+ * | attrs: Object |
40
+ * | ] |
41
+ * +----------------------------------+
42
+ */
43
+ switch (key) {
44
+ case 'content':
45
+ // Entering or leaving a node that is not a leaf node (i.e. supports content) counts as one token.
46
+ offset++;
47
+ ref = ref.content;
48
+ break;
49
+ case 'attrs':
50
+ return {
51
+ type: 'attrs',
52
+ from: offset + from,
53
+ path: parts.slice(i + 1)
54
+ };
55
+ case 'text':
56
+ if (!ref.isText) {
57
+ throw new Error(`"${parts.slice(0, i).join('/')}" doesn't have any "text" node!`);
58
+ }
59
+ continue;
60
+ default:
61
+ throw new Error(`JSON pointer "${pointer}" points to an unsupported location.`);
62
+ }
63
+ } else {
64
+ throw new Error(`JSON pointer "${pointer}" points to an unsupported entity.`);
65
+ }
66
+ }
67
+ if (ref instanceof Fragment) {
68
+ throw new Error(`Expected a Node, but the JSON pointer "${pointer}" points to a Fragment.`);
69
+ }
70
+ return {
71
+ type: 'node',
72
+ from: offset + from,
73
+ to: offset + (to !== null && to !== void 0 ? to : ref.nodeSize),
74
+ matches: [to ? ref.textBetween(from, to) : ref.textContent]
75
+ };
76
+ }
@@ -1,3 +1,4 @@
1
+ import isEqual from 'lodash/isEqual';
1
2
  import { JSONTransformer } from '@atlaskit/editor-json-transformer';
2
3
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
3
4
  import { CellSelection, TableMap } from '@atlaskit/editor-tables';
@@ -28,11 +29,13 @@ const getSelectionInfoFromSameNode = selection => {
28
29
  pointer: `/content/${$from.index()}/text`,
29
30
  position: $to.parentOffset - 1
30
31
  }
31
- }]
32
+ }],
33
+ nodePos: $from.before() // position before the selection
32
34
  };
33
35
  };
34
36
  const getSelectionInfoFromCellSelection = selection => {
35
37
  const selectedNode = selection.$anchorCell.node(-1);
38
+ const nodePos = selection.$anchorCell.before(-1);
36
39
  const selectionRanges = [];
37
40
  const rect = getSelectedRect(selection);
38
41
  for (let row = rect.top; row < rect.bottom; row++) {
@@ -47,14 +50,16 @@ const getSelectionInfoFromCellSelection = selection => {
47
50
  }
48
51
  return {
49
52
  selectedNode,
50
- selectionRanges
53
+ selectionRanges,
54
+ nodePos
51
55
  };
52
56
  };
53
57
  export const getSelectionInfo = state => {
54
58
  const selection = state.selection;
55
59
  let selectionInfo = {
56
60
  selectedNode: selection.$from.node(),
57
- selectionRanges: []
61
+ selectionRanges: [],
62
+ nodePos: selection.$from.before() // default to the position before the selection
58
63
  };
59
64
  if (selection instanceof TextSelection) {
60
65
  const {
@@ -70,9 +75,21 @@ export const getSelectionInfo = state => {
70
75
  selectionInfo = getSelectionInfoFromCellSelection(selection);
71
76
  }
72
77
  const serializer = new JSONTransformer();
73
- const selectedNodeAdf = serializer.encodeNode(selectionInfo.selectedNode);
78
+ const {
79
+ selectionRanges,
80
+ selectedNode,
81
+ nodePos
82
+ } = selectionInfo;
83
+ const selectedNodeAdf = serializer.encodeNode(selectedNode);
74
84
  return {
75
85
  selectedNodeAdf,
76
- selectionRanges: selectionInfo.selectionRanges
86
+ selectionRanges,
87
+ selectedNode,
88
+ nodePos
77
89
  };
90
+ };
91
+ export const validateSelectedNode = (selectedNodeAdf, selectedNode) => {
92
+ const serializer = new JSONTransformer();
93
+ const selectedNodeAdfFromState = serializer.encodeNode(selectedNode);
94
+ return isEqual(selectedNodeAdf, selectedNodeAdfFromState);
78
95
  };
@@ -2,8 +2,10 @@ import React from 'react';
2
2
  import { selectionExtensionMessages } from '@atlaskit/editor-common/messages';
3
3
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
4
4
  import { fg } from '@atlaskit/platform-feature-flags';
5
+ import { insertSmartLinks } from './pm-plugins/actions';
5
6
  import { createPlugin, selectionExtensionPluginKey } from './pm-plugins/main';
6
7
  import { getSelectionInfo } from './pm-plugins/utils';
8
+ import { SelectionExtensionActionTypes } from './types';
7
9
  import { SelectionExtensionComponentWrapper } from './ui/extension/SelectionExtensionComponentWrapper';
8
10
  import { getBoundingBoxFromSelection } from './ui/getBoundingBoxFromSelection';
9
11
  import { selectionToolbar } from './ui/selectionToolbar';
@@ -41,6 +43,21 @@ export const selectionExtensionPlugin = ({
41
43
  });
42
44
  }
43
45
  },
46
+ actions: {
47
+ insertSmartLinks: (linkInsertionOptions, selectedNodeAdf) => {
48
+ if (!editorViewRef.current) {
49
+ return {
50
+ status: 'error',
51
+ message: 'Editor view is not available'
52
+ };
53
+ }
54
+ const {
55
+ state,
56
+ dispatch
57
+ } = editorViewRef.current;
58
+ return insertSmartLinks(linkInsertionOptions, selectedNodeAdf)(state, dispatch);
59
+ }
60
+ },
44
61
  contentComponent: ({
45
62
  editorView
46
63
  }) => {
@@ -134,16 +151,28 @@ export const selectionExtensionPlugin = ({
134
151
  selection
135
152
  };
136
153
  if (fg('platform_editor_selection_extension_api_v2')) {
137
- var _extension$onClick;
154
+ var _extension$onClick, _api$core;
138
155
  const {
139
156
  selectedNodeAdf,
140
- selectionRanges
157
+ selectionRanges,
158
+ selectedNode,
159
+ nodePos
141
160
  } = getSelectionInfo(view.state);
142
161
  onClickCallbackOptions = {
143
162
  selectedNodeAdf,
144
163
  selectionRanges
145
164
  };
146
165
  (_extension$onClick = extension.onClick) === null || _extension$onClick === void 0 ? void 0 : _extension$onClick.call(extension, onClickCallbackOptions);
166
+ api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
167
+ tr
168
+ }) => {
169
+ tr.setMeta(selectionExtensionPluginKey, {
170
+ type: SelectionExtensionActionTypes.SET_SELECTED_NODE,
171
+ selectedNode,
172
+ nodePos
173
+ });
174
+ return tr;
175
+ });
147
176
  } else {
148
177
  if (extension.onClick) {
149
178
  extension.onClick(onClickCallbackOptions);
@@ -10,5 +10,6 @@ export let SelectionExtensionActionTypes = /*#__PURE__*/function (SelectionExten
10
10
  SelectionExtensionActionTypes["SET_ACTIVE_EXTENSION"] = "set-active-extension";
11
11
  SelectionExtensionActionTypes["UPDATE_ACTIVE_EXTENSION_COORDS"] = "update-active-extension-coords";
12
12
  SelectionExtensionActionTypes["CLEAR_ACTIVE_EXTENSION"] = "clear-active-extension";
13
+ SelectionExtensionActionTypes["SET_SELECTED_NODE"] = "set-selected-node";
13
14
  return SelectionExtensionActionTypes;
14
15
  }({});
@@ -0,0 +1,63 @@
1
+ import { selectionExtensionPluginKey } from './main';
2
+ import { validateSelectedNode } from './utils';
3
+ import { getOffsetByPath } from './utils/getOffsetByPath';
4
+ var insertLinkTr = function insertLinkTr(tr, link, offset, schema) {
5
+ var newFromPos = tr.mapping.map(offset.from);
6
+ var newToPos = tr.mapping.map(offset.to || offset.from);
7
+ var smartLink = schema.nodes.inlineCard.createChecked({
8
+ url: link
9
+ });
10
+ return tr.replaceWith(newFromPos, newToPos, smartLink);
11
+ };
12
+ export var insertSmartLinks = function insertSmartLinks(linkInsertionOption, selectedNodeAdf) {
13
+ return function (state, dispatch) {
14
+ var tr = state.tr,
15
+ schema = state.schema;
16
+ if (!Array.isArray(linkInsertionOption)) {
17
+ linkInsertionOption = [linkInsertionOption];
18
+ }
19
+ if (linkInsertionOption.length === 0) {
20
+ return {
21
+ status: 'error',
22
+ message: 'No link insertion options provided'
23
+ };
24
+ }
25
+
26
+ // TODO: ED-28365 - we need to track if any changes were made since user click the toolbar button
27
+ // if there is change, we should insert the link at the bottom of the page
28
+
29
+ var newTr = tr;
30
+ try {
31
+ var _selectionExtensionPl, _selectionExtensionPl2;
32
+ var selectedNode = (_selectionExtensionPl = selectionExtensionPluginKey.getState(state)) === null || _selectionExtensionPl === void 0 ? void 0 : _selectionExtensionPl.selectedNode;
33
+ var nodePos = (_selectionExtensionPl2 = selectionExtensionPluginKey.getState(state)) === null || _selectionExtensionPl2 === void 0 ? void 0 : _selectionExtensionPl2.nodePos;
34
+ if (!selectedNode || nodePos === undefined) {
35
+ throw new Error('No selected node or node position found');
36
+ }
37
+
38
+ // Validate if the selectedNodeAdf matches the selected node we have in the state
39
+ if (!validateSelectedNode(selectedNodeAdf, selectedNode)) {
40
+ throw new Error('Selected node ADF does not match the previous stored node');
41
+ }
42
+ linkInsertionOption.forEach(function (option) {
43
+ var link = option.link,
44
+ insertPosition = option.insertPosition;
45
+ var pointer = insertPosition.pointer,
46
+ from = insertPosition.from,
47
+ to = insertPosition.to;
48
+ var offset = getOffsetByPath(selectedNode, nodePos, pointer, from, to);
49
+ newTr = insertLinkTr(tr, link, offset, schema);
50
+ });
51
+ } catch (error) {
52
+ return {
53
+ status: 'error',
54
+ message: error instanceof Error ? error.message : 'Unknown error'
55
+ };
56
+ }
57
+ dispatch(newTr);
58
+ return {
59
+ status: 'success',
60
+ message: 'Links inserted successfully'
61
+ };
62
+ };
63
+ };
@@ -25,6 +25,11 @@ export var createPlugin = function createPlugin() {
25
25
  return _objectSpread(_objectSpread({}, pluginState), {}, {
26
26
  activeExtension: undefined
27
27
  });
28
+ case SelectionExtensionActionTypes.SET_SELECTED_NODE:
29
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
30
+ selectedNode: meta.selectedNode,
31
+ nodePos: meta.nodePos
32
+ });
28
33
  }
29
34
  return pluginState;
30
35
  }
@@ -0,0 +1,78 @@
1
+ import { Fragment, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
2
+
3
+ // TODO: ED-28434 - move this to a shared package
4
+ // mirror code from https://bitbucket.org/atlassian/pf-adf-service/src/master/src/lib/update/get-offset.ts
5
+
6
+ export function getOffsetByPath(root, pos, pointer) {
7
+ var from = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
8
+ var to = arguments.length > 4 ? arguments[4] : undefined;
9
+ var parts = pointer.split('/');
10
+ var ref = root;
11
+ var len = parts.length;
12
+ // -1 to account for the root node (usually doc)
13
+ // The start of the document, right before the first content, is position 0.
14
+ var offset = pos; //-1;
15
+
16
+ for (var i = 1; i < len; i++) {
17
+ var key = parts[i];
18
+ if (ref instanceof Fragment && /^[0-9]+$/.test(key)) {
19
+ var index = parseInt(key, 10);
20
+ if (index >= ref.childCount) {
21
+ throw new Error("JSON pointer \"".concat(pointer, "\" points to non-existing location."));
22
+ }
23
+ var nextRef = ref.child(index);
24
+ while (index--) {
25
+ offset += ref.child(index).nodeSize;
26
+ }
27
+ ref = nextRef;
28
+ } else if (ref instanceof PMNode) {
29
+ /**
30
+ * Reference: https://prosemirror.net/docs/guide/#doc.data_structures
31
+ * +----------------------------------+
32
+ * | Node |
33
+ * | ^^^^ |
34
+ * | type: NodeType |
35
+ * | content: Fragment |
36
+ * | [ Node, Node, ...] |
37
+ * | attrs: Object |
38
+ * | marks: Mark |
39
+ * | [ |
40
+ * | type: MarkType |
41
+ * | attrs: Object |
42
+ * | ] |
43
+ * +----------------------------------+
44
+ */
45
+ switch (key) {
46
+ case 'content':
47
+ // Entering or leaving a node that is not a leaf node (i.e. supports content) counts as one token.
48
+ offset++;
49
+ ref = ref.content;
50
+ break;
51
+ case 'attrs':
52
+ return {
53
+ type: 'attrs',
54
+ from: offset + from,
55
+ path: parts.slice(i + 1)
56
+ };
57
+ case 'text':
58
+ if (!ref.isText) {
59
+ throw new Error("\"".concat(parts.slice(0, i).join('/'), "\" doesn't have any \"text\" node!"));
60
+ }
61
+ continue;
62
+ default:
63
+ throw new Error("JSON pointer \"".concat(pointer, "\" points to an unsupported location."));
64
+ }
65
+ } else {
66
+ throw new Error("JSON pointer \"".concat(pointer, "\" points to an unsupported entity."));
67
+ }
68
+ }
69
+ if (ref instanceof Fragment) {
70
+ throw new Error("Expected a Node, but the JSON pointer \"".concat(pointer, "\" points to a Fragment."));
71
+ }
72
+ return {
73
+ type: 'node',
74
+ from: offset + from,
75
+ to: offset + (to !== null && to !== void 0 ? to : ref.nodeSize),
76
+ matches: [to ? ref.textBetween(from, to) : ref.textContent]
77
+ };
78
+ }
@@ -1,3 +1,4 @@
1
+ import isEqual from 'lodash/isEqual';
1
2
  import { JSONTransformer } from '@atlaskit/editor-json-transformer';
2
3
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
3
4
  import { CellSelection, TableMap } from '@atlaskit/editor-tables';
@@ -24,11 +25,13 @@ var getSelectionInfoFromSameNode = function getSelectionInfoFromSameNode(selecti
24
25
  pointer: "/content/".concat($from.index(), "/text"),
25
26
  position: $to.parentOffset - 1
26
27
  }
27
- }]
28
+ }],
29
+ nodePos: $from.before() // position before the selection
28
30
  };
29
31
  };
30
32
  var getSelectionInfoFromCellSelection = function getSelectionInfoFromCellSelection(selection) {
31
33
  var selectedNode = selection.$anchorCell.node(-1);
34
+ var nodePos = selection.$anchorCell.before(-1);
32
35
  var selectionRanges = [];
33
36
  var rect = getSelectedRect(selection);
34
37
  for (var row = rect.top; row < rect.bottom; row++) {
@@ -43,14 +46,16 @@ var getSelectionInfoFromCellSelection = function getSelectionInfoFromCellSelecti
43
46
  }
44
47
  return {
45
48
  selectedNode: selectedNode,
46
- selectionRanges: selectionRanges
49
+ selectionRanges: selectionRanges,
50
+ nodePos: nodePos
47
51
  };
48
52
  };
49
53
  export var getSelectionInfo = function getSelectionInfo(state) {
50
54
  var selection = state.selection;
51
55
  var selectionInfo = {
52
56
  selectedNode: selection.$from.node(),
53
- selectionRanges: []
57
+ selectionRanges: [],
58
+ nodePos: selection.$from.before() // default to the position before the selection
54
59
  };
55
60
  if (selection instanceof TextSelection) {
56
61
  var $from = selection.$from,
@@ -64,9 +69,20 @@ export var getSelectionInfo = function getSelectionInfo(state) {
64
69
  selectionInfo = getSelectionInfoFromCellSelection(selection);
65
70
  }
66
71
  var serializer = new JSONTransformer();
67
- var selectedNodeAdf = serializer.encodeNode(selectionInfo.selectedNode);
72
+ var _selectionInfo = selectionInfo,
73
+ selectionRanges = _selectionInfo.selectionRanges,
74
+ selectedNode = _selectionInfo.selectedNode,
75
+ nodePos = _selectionInfo.nodePos;
76
+ var selectedNodeAdf = serializer.encodeNode(selectedNode);
68
77
  return {
69
78
  selectedNodeAdf: selectedNodeAdf,
70
- selectionRanges: selectionInfo.selectionRanges
79
+ selectionRanges: selectionRanges,
80
+ selectedNode: selectedNode,
81
+ nodePos: nodePos
71
82
  };
83
+ };
84
+ export var validateSelectedNode = function validateSelectedNode(selectedNodeAdf, selectedNode) {
85
+ var serializer = new JSONTransformer();
86
+ var selectedNodeAdfFromState = serializer.encodeNode(selectedNode);
87
+ return isEqual(selectedNodeAdf, selectedNodeAdfFromState);
72
88
  };
@@ -3,8 +3,10 @@ import React from 'react';
3
3
  import { selectionExtensionMessages } from '@atlaskit/editor-common/messages';
4
4
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
5
5
  import { fg } from '@atlaskit/platform-feature-flags';
6
+ import { insertSmartLinks as _insertSmartLinks } from './pm-plugins/actions';
6
7
  import { createPlugin, selectionExtensionPluginKey } from './pm-plugins/main';
7
8
  import { getSelectionInfo } from './pm-plugins/utils';
9
+ import { SelectionExtensionActionTypes } from './types';
8
10
  import { SelectionExtensionComponentWrapper } from './ui/extension/SelectionExtensionComponentWrapper';
9
11
  import { getBoundingBoxFromSelection } from './ui/getBoundingBoxFromSelection';
10
12
  import { selectionToolbar as _selectionToolbar } from './ui/selectionToolbar';
@@ -43,6 +45,20 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
43
45
  };
44
46
  }
45
47
  },
48
+ actions: {
49
+ insertSmartLinks: function insertSmartLinks(linkInsertionOptions, selectedNodeAdf) {
50
+ if (!editorViewRef.current) {
51
+ return {
52
+ status: 'error',
53
+ message: 'Editor view is not available'
54
+ };
55
+ }
56
+ var _editorViewRef$curren = editorViewRef.current,
57
+ state = _editorViewRef$curren.state,
58
+ dispatch = _editorViewRef$curren.dispatch;
59
+ return _insertSmartLinks(linkInsertionOptions, selectedNodeAdf)(state, dispatch);
60
+ }
61
+ },
46
62
  contentComponent: function contentComponent(_ref4) {
47
63
  var _api$analytics;
48
64
  var editorView = _ref4.editorView;
@@ -130,15 +146,26 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
130
146
  selection: selection
131
147
  };
132
148
  if (fg('platform_editor_selection_extension_api_v2')) {
133
- var _extension$onClick;
149
+ var _extension$onClick, _api$core;
134
150
  var _getSelectionInfo = getSelectionInfo(view.state),
135
151
  selectedNodeAdf = _getSelectionInfo.selectedNodeAdf,
136
- selectionRanges = _getSelectionInfo.selectionRanges;
152
+ selectionRanges = _getSelectionInfo.selectionRanges,
153
+ selectedNode = _getSelectionInfo.selectedNode,
154
+ nodePos = _getSelectionInfo.nodePos;
137
155
  onClickCallbackOptions = {
138
156
  selectedNodeAdf: selectedNodeAdf,
139
157
  selectionRanges: selectionRanges
140
158
  };
141
159
  (_extension$onClick = extension.onClick) === null || _extension$onClick === void 0 || _extension$onClick.call(extension, onClickCallbackOptions);
160
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref5) {
161
+ var tr = _ref5.tr;
162
+ tr.setMeta(selectionExtensionPluginKey, {
163
+ type: SelectionExtensionActionTypes.SET_SELECTED_NODE,
164
+ selectedNode: selectedNode,
165
+ nodePos: nodePos
166
+ });
167
+ return tr;
168
+ });
142
169
  } else {
143
170
  if (extension.onClick) {
144
171
  extension.onClick(onClickCallbackOptions);
@@ -10,5 +10,6 @@ export var SelectionExtensionActionTypes = /*#__PURE__*/function (SelectionExten
10
10
  SelectionExtensionActionTypes["SET_ACTIVE_EXTENSION"] = "set-active-extension";
11
11
  SelectionExtensionActionTypes["UPDATE_ACTIVE_EXTENSION_COORDS"] = "update-active-extension-coords";
12
12
  SelectionExtensionActionTypes["CLEAR_ACTIVE_EXTENSION"] = "clear-active-extension";
13
+ SelectionExtensionActionTypes["SET_SELECTED_NODE"] = "set-selected-node";
13
14
  return SelectionExtensionActionTypes;
14
15
  }({});
@@ -0,0 +1,8 @@
1
+ import { type ADFEntity } from '@atlaskit/adf-utils/types';
2
+ import type { CommandDispatch } from '@atlaskit/editor-common/types';
3
+ import type { EditorState } from '@atlaskit/editor-prosemirror/state';
4
+ import { type LinkInsertionOption } from '../types';
5
+ export declare const insertSmartLinks: (linkInsertionOption: LinkInsertionOption | LinkInsertionOption[], selectedNodeAdf: ADFEntity) => (state: EditorState, dispatch: CommandDispatch) => {
6
+ status: 'success' | 'error';
7
+ message?: string;
8
+ };
@@ -4,4 +4,14 @@ import { type SelectionExtensionPluginState } from '../types';
4
4
  export declare const selectionExtensionPluginKey: PluginKey<SelectionExtensionPluginState>;
5
5
  export declare const createPlugin: () => SafePlugin<SelectionExtensionPluginState | {
6
6
  activeExtension: any;
7
+ selectedNode?: import("prosemirror-model").Node | undefined;
8
+ nodePos?: number | undefined;
9
+ } | {
10
+ selectedNode: any;
11
+ nodePos: any;
12
+ activeExtension?: {
13
+ extension: import("../types").SelectionExtension;
14
+ selection: import("../types").SelectionExtensionSelectionInfo;
15
+ coords: import("../types").SelectionExtensionCoords;
16
+ } | undefined;
7
17
  }>;
@@ -0,0 +1,14 @@
1
+ import { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
2
+ export interface NodeOffset {
3
+ type: 'node';
4
+ from: number;
5
+ to: number;
6
+ matches: string[];
7
+ }
8
+ export interface AttrsOffset {
9
+ type: 'attrs';
10
+ from: number;
11
+ path: string[];
12
+ }
13
+ export type Offset = NodeOffset | AttrsOffset;
14
+ export declare function getOffsetByPath(root: PMNode, pos: number, pointer: string, from?: number, to?: number): Offset;
@@ -1,3 +1,13 @@
1
+ import { type ADFEntity } from '@atlaskit/adf-utils/types';
2
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
1
3
  import { type EditorState } from '@atlaskit/editor-prosemirror/state';
2
- import { type SelectionExtensionFnOptions } from '../../types';
3
- export declare const getSelectionInfo: (state: EditorState) => SelectionExtensionFnOptions;
4
+ import { type SelectionRange } from '../../types';
5
+ type SelectionInfo = {
6
+ selectedNodeAdf: ADFEntity;
7
+ selectionRanges: SelectionRange[];
8
+ selectedNode: PMNode;
9
+ nodePos: number;
10
+ };
11
+ export declare const getSelectionInfo: (state: EditorState) => SelectionInfo;
12
+ export declare const validateSelectedNode: (selectedNodeAdf: ADFEntity, selectedNode: PMNode) => boolean;
13
+ export {};
@@ -1,8 +1,9 @@
1
+ import { type ADFEntity } from '@atlaskit/adf-utils/types';
1
2
  import type { EditorCommand, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
2
3
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
3
4
  import type { EditorViewModePlugin } from '@atlaskit/editor-plugin-editor-viewmode';
4
5
  import type { SelectionToolbarPlugin } from '@atlaskit/editor-plugin-selection-toolbar';
5
- import type { SelectionExtension, SelectionExtensionPluginOptions, SelectionExtensionPluginState, SelectionExtensionSelectionInfo } from './types';
6
+ import type { LinkInsertionOption, SelectionExtension, SelectionExtensionPluginOptions, SelectionExtensionPluginState, SelectionExtensionSelectionInfo } from './types';
6
7
  export type SelectionExtensionPlugin = NextEditorPlugin<'selectionExtension', {
7
8
  pluginConfiguration: SelectionExtensionPluginOptions | undefined;
8
9
  dependencies: [
@@ -18,4 +19,10 @@ export type SelectionExtensionPlugin = NextEditorPlugin<'selectionExtension', {
18
19
  }) => EditorCommand;
19
20
  clearActiveExtension: () => EditorCommand;
20
21
  };
22
+ actions: {
23
+ insertSmartLinks: (linkInsertionOption: LinkInsertionOption | LinkInsertionOption[], selectedNodeAdf: ADFEntity) => {
24
+ status: 'success' | 'error';
25
+ message?: string;
26
+ };
27
+ };
21
28
  }>;
@@ -2,6 +2,7 @@
2
2
  import type { ADFEntity } from '@atlaskit/adf-utils/types';
3
3
  import { type MenuItem } from '@atlaskit/editor-common/ui-menu';
4
4
  import type { ViewMode } from '@atlaskit/editor-plugin-editor-viewmode';
5
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
5
6
  export type MenuItemsType = Array<{
6
7
  items: MenuItem[];
7
8
  }>;
@@ -70,10 +71,20 @@ export type SelectionExtensionCoords = {
70
71
  top: number;
71
72
  bottom: number;
72
73
  };
74
+ export type InsertPosition = {
75
+ pointer: string;
76
+ from?: number;
77
+ to?: number;
78
+ };
79
+ export type LinkInsertionOption = {
80
+ link: string;
81
+ insertPosition: InsertPosition;
82
+ };
73
83
  export declare enum SelectionExtensionActionTypes {
74
84
  SET_ACTIVE_EXTENSION = "set-active-extension",
75
85
  UPDATE_ACTIVE_EXTENSION_COORDS = "update-active-extension-coords",
76
- CLEAR_ACTIVE_EXTENSION = "clear-active-extension"
86
+ CLEAR_ACTIVE_EXTENSION = "clear-active-extension",
87
+ SET_SELECTED_NODE = "set-selected-node"
77
88
  }
78
89
  export type UpdateActiveExtensionAction = {
79
90
  type: SelectionExtensionActionTypes.SET_ACTIVE_EXTENSION;
@@ -90,5 +101,7 @@ export type SelectionExtensionPluginState = {
90
101
  selection: SelectionExtensionSelectionInfo;
91
102
  coords: SelectionExtensionCoords;
92
103
  };
104
+ selectedNode?: PMNode;
105
+ nodePos?: number;
93
106
  };
94
107
  export {};
@@ -0,0 +1,8 @@
1
+ import { type ADFEntity } from '@atlaskit/adf-utils/types';
2
+ import type { CommandDispatch } from '@atlaskit/editor-common/types';
3
+ import type { EditorState } from '@atlaskit/editor-prosemirror/state';
4
+ import { type LinkInsertionOption } from '../types';
5
+ export declare const insertSmartLinks: (linkInsertionOption: LinkInsertionOption | LinkInsertionOption[], selectedNodeAdf: ADFEntity) => (state: EditorState, dispatch: CommandDispatch) => {
6
+ status: 'success' | 'error';
7
+ message?: string;
8
+ };
@@ -4,4 +4,14 @@ import { type SelectionExtensionPluginState } from '../types';
4
4
  export declare const selectionExtensionPluginKey: PluginKey<SelectionExtensionPluginState>;
5
5
  export declare const createPlugin: () => SafePlugin<SelectionExtensionPluginState | {
6
6
  activeExtension: any;
7
+ selectedNode?: import("prosemirror-model").Node | undefined;
8
+ nodePos?: number | undefined;
9
+ } | {
10
+ selectedNode: any;
11
+ nodePos: any;
12
+ activeExtension?: {
13
+ extension: import("../types").SelectionExtension;
14
+ selection: import("../types").SelectionExtensionSelectionInfo;
15
+ coords: import("../types").SelectionExtensionCoords;
16
+ } | undefined;
7
17
  }>;
@@ -0,0 +1,14 @@
1
+ import { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
2
+ export interface NodeOffset {
3
+ type: 'node';
4
+ from: number;
5
+ to: number;
6
+ matches: string[];
7
+ }
8
+ export interface AttrsOffset {
9
+ type: 'attrs';
10
+ from: number;
11
+ path: string[];
12
+ }
13
+ export type Offset = NodeOffset | AttrsOffset;
14
+ export declare function getOffsetByPath(root: PMNode, pos: number, pointer: string, from?: number, to?: number): Offset;
@@ -1,3 +1,13 @@
1
+ import { type ADFEntity } from '@atlaskit/adf-utils/types';
2
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
1
3
  import { type EditorState } from '@atlaskit/editor-prosemirror/state';
2
- import { type SelectionExtensionFnOptions } from '../../types';
3
- export declare const getSelectionInfo: (state: EditorState) => SelectionExtensionFnOptions;
4
+ import { type SelectionRange } from '../../types';
5
+ type SelectionInfo = {
6
+ selectedNodeAdf: ADFEntity;
7
+ selectionRanges: SelectionRange[];
8
+ selectedNode: PMNode;
9
+ nodePos: number;
10
+ };
11
+ export declare const getSelectionInfo: (state: EditorState) => SelectionInfo;
12
+ export declare const validateSelectedNode: (selectedNodeAdf: ADFEntity, selectedNode: PMNode) => boolean;
13
+ export {};
@@ -1,8 +1,9 @@
1
+ import { type ADFEntity } from '@atlaskit/adf-utils/types';
1
2
  import type { EditorCommand, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
2
3
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
3
4
  import type { EditorViewModePlugin } from '@atlaskit/editor-plugin-editor-viewmode';
4
5
  import type { SelectionToolbarPlugin } from '@atlaskit/editor-plugin-selection-toolbar';
5
- import type { SelectionExtension, SelectionExtensionPluginOptions, SelectionExtensionPluginState, SelectionExtensionSelectionInfo } from './types';
6
+ import type { LinkInsertionOption, SelectionExtension, SelectionExtensionPluginOptions, SelectionExtensionPluginState, SelectionExtensionSelectionInfo } from './types';
6
7
  export type SelectionExtensionPlugin = NextEditorPlugin<'selectionExtension', {
7
8
  pluginConfiguration: SelectionExtensionPluginOptions | undefined;
8
9
  dependencies: [
@@ -18,4 +19,10 @@ export type SelectionExtensionPlugin = NextEditorPlugin<'selectionExtension', {
18
19
  }) => EditorCommand;
19
20
  clearActiveExtension: () => EditorCommand;
20
21
  };
22
+ actions: {
23
+ insertSmartLinks: (linkInsertionOption: LinkInsertionOption | LinkInsertionOption[], selectedNodeAdf: ADFEntity) => {
24
+ status: 'success' | 'error';
25
+ message?: string;
26
+ };
27
+ };
21
28
  }>;
@@ -2,6 +2,7 @@
2
2
  import type { ADFEntity } from '@atlaskit/adf-utils/types';
3
3
  import { type MenuItem } from '@atlaskit/editor-common/ui-menu';
4
4
  import type { ViewMode } from '@atlaskit/editor-plugin-editor-viewmode';
5
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
5
6
  export type MenuItemsType = Array<{
6
7
  items: MenuItem[];
7
8
  }>;
@@ -70,10 +71,20 @@ export type SelectionExtensionCoords = {
70
71
  top: number;
71
72
  bottom: number;
72
73
  };
74
+ export type InsertPosition = {
75
+ pointer: string;
76
+ from?: number;
77
+ to?: number;
78
+ };
79
+ export type LinkInsertionOption = {
80
+ link: string;
81
+ insertPosition: InsertPosition;
82
+ };
73
83
  export declare enum SelectionExtensionActionTypes {
74
84
  SET_ACTIVE_EXTENSION = "set-active-extension",
75
85
  UPDATE_ACTIVE_EXTENSION_COORDS = "update-active-extension-coords",
76
- CLEAR_ACTIVE_EXTENSION = "clear-active-extension"
86
+ CLEAR_ACTIVE_EXTENSION = "clear-active-extension",
87
+ SET_SELECTED_NODE = "set-selected-node"
77
88
  }
78
89
  export type UpdateActiveExtensionAction = {
79
90
  type: SelectionExtensionActionTypes.SET_ACTIVE_EXTENSION;
@@ -90,5 +101,7 @@ export type SelectionExtensionPluginState = {
90
101
  selection: SelectionExtensionSelectionInfo;
91
102
  coords: SelectionExtensionCoords;
92
103
  };
104
+ selectedNode?: PMNode;
105
+ nodePos?: number;
93
106
  };
94
107
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-selection-extension",
3
- "version": "3.0.1",
3
+ "version": "3.1.0",
4
4
  "description": "editor-plugin-selection-extension plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -47,6 +47,7 @@
47
47
  "@atlaskit/primitives": "^14.10.0",
48
48
  "@atlaskit/tmp-editor-statsig": "^9.0.0",
49
49
  "@babel/runtime": "^7.0.0",
50
+ "lodash": "^4.17.21",
50
51
  "react-intl-next": "npm:react-intl@^5.18.1",
51
52
  "uuid": "^3.1.0"
52
53
  },