@atlaskit/editor-plugin-selection-extension 2.4.5 → 2.4.7

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,22 @@
1
1
  # @atlaskit/editor-plugin-selection-extension
2
2
 
3
+ ## 2.4.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [#177823](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/177823)
8
+ [`8bd9bca774e4f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8bd9bca774e4f) -
9
+ Extend selection extension API to support dynamic loading of extension configuration
10
+ - Updated dependencies
11
+
12
+ ## 2.4.6
13
+
14
+ ### Patch Changes
15
+
16
+ - [#175281](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/175281)
17
+ [`f720d2c80d128`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/f720d2c80d128) -
18
+ [ux] Fix inconsistent references in getSelection to state that could cause unhandled exceptions
19
+
3
20
  ## 2.4.5
4
21
 
5
22
  ### Patch Changes
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getSelectionInfo = void 0;
7
+ var _editorJsonTransformer = require("@atlaskit/editor-json-transformer");
8
+ var _state = require("@atlaskit/editor-prosemirror/state");
9
+ var _editorTables = require("@atlaskit/editor-tables");
10
+ var getSelectedRect = function getSelectedRect(selection) {
11
+ var $anchorCell = selection.$anchorCell,
12
+ $headCell = selection.$headCell;
13
+ var table = $anchorCell.node(-1);
14
+ var map = _editorTables.TableMap.get(table);
15
+ var start = $anchorCell.start(-1);
16
+ var rect = map.rectBetween($anchorCell.pos - start, $headCell.pos - start);
17
+ return rect;
18
+ };
19
+ var getSelectionInfoFromSameNode = function getSelectionInfoFromSameNode(selection) {
20
+ var $from = selection.$from,
21
+ $to = selection.$to;
22
+ return {
23
+ selectedNode: $from.node(),
24
+ selectionRanges: [{
25
+ start: {
26
+ pointer: "/content/".concat($from.index(), "/text"),
27
+ position: $from.parentOffset
28
+ },
29
+ end: {
30
+ pointer: "/content/".concat($from.index(), "/text"),
31
+ position: $to.parentOffset - 1
32
+ }
33
+ }]
34
+ };
35
+ };
36
+ var getSelectionInfoFromCellSelection = function getSelectionInfoFromCellSelection(selection) {
37
+ var selectedNode = selection.$anchorCell.node(-1);
38
+ var selectionRanges = [];
39
+ var rect = getSelectedRect(selection);
40
+ for (var row = rect.top; row < rect.bottom; row++) {
41
+ selectionRanges.push({
42
+ start: {
43
+ pointer: "/content/".concat(row, "/content/").concat(rect.left)
44
+ },
45
+ end: {
46
+ pointer: "/content/".concat(row, "/content/").concat(rect.right - 1)
47
+ }
48
+ });
49
+ }
50
+ return {
51
+ selectedNode: selectedNode,
52
+ selectionRanges: selectionRanges
53
+ };
54
+ };
55
+ var getSelectionInfo = exports.getSelectionInfo = function getSelectionInfo(state) {
56
+ var selection = state.selection;
57
+ var selectionInfo = {
58
+ selectedNode: selection.$from.node(),
59
+ selectionRanges: []
60
+ };
61
+ if (selection instanceof _state.TextSelection) {
62
+ var $from = selection.$from,
63
+ $to = selection.$to;
64
+ if ($from.parent === $to.parent) {
65
+ selectionInfo = getSelectionInfoFromSameNode(selection);
66
+ } else {
67
+ // TODO: ED-28405 - when selection spans multiple nodes including nested node, we need to iterate through the nodes
68
+ }
69
+ } else if (selection instanceof _editorTables.CellSelection) {
70
+ selectionInfo = getSelectionInfoFromCellSelection(selection);
71
+ }
72
+ var serializer = new _editorJsonTransformer.JSONTransformer();
73
+ var selectedNodeAdf = serializer.encodeNode(selectionInfo.selectedNode);
74
+ return {
75
+ selectedNodeAdf: selectedNodeAdf,
76
+ selectionRanges: selectionInfo.selectionRanges
77
+ };
78
+ };
@@ -9,15 +9,20 @@ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers
9
9
  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
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
12
13
  var _main = require("./pm-plugins/main");
14
+ var _utils = require("./pm-plugins/utils");
13
15
  var _SelectionExtensionComponentWrapper = require("./ui/extension/SelectionExtensionComponentWrapper");
14
16
  var _getBoundingBoxFromSelection = require("./ui/getBoundingBoxFromSelection");
17
+ var _selectionToolbar2 = require("./ui/selectionToolbar");
15
18
  var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
16
19
  var api = _ref.api,
17
20
  config = _ref.config;
18
21
  var editorViewRef = {
19
22
  current: null
20
23
  };
24
+ var cachedSelection;
25
+ var cachedOverflowMenuOptions;
21
26
  return {
22
27
  name: 'selectionExtension',
23
28
  getSharedState: function getSharedState(editorState) {
@@ -109,7 +114,8 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
109
114
  var currentSelection = view.state.selection;
110
115
  var from = currentSelection.from,
111
116
  to = currentSelection.to;
112
- var text = state.doc.textBetween(from, to, '\n');
117
+ var currentState = (0, _platformFeatureFlags.fg)('platform_editor_fix_get_selection_state_mismatch') ? view.state : state;
118
+ var text = currentState.doc.textBetween(from, to, '\n');
113
119
  var coords = (0, _getBoundingBoxFromSelection.getBoundingBoxFromSelection)(view, from, to);
114
120
  return {
115
121
  text: text,
@@ -127,10 +133,23 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
127
133
  selection: selection
128
134
  }));
129
135
  }
130
- if (extension.onClick) {
131
- extension.onClick({
132
- selection: selection
133
- });
136
+ var onClickCallbackOptions = {
137
+ selection: selection
138
+ };
139
+ if ((0, _platformFeatureFlags.fg)('platform_editor_selection_extension_api_v2')) {
140
+ var _extension$onClick;
141
+ var _getSelectionInfo = (0, _utils.getSelectionInfo)(view.state),
142
+ selectedNodeAdf = _getSelectionInfo.selectedNodeAdf,
143
+ selectionRanges = _getSelectionInfo.selectionRanges;
144
+ onClickCallbackOptions = {
145
+ selectedNodeAdf: selectedNodeAdf,
146
+ selectionRanges: selectionRanges
147
+ };
148
+ (_extension$onClick = extension.onClick) === null || _extension$onClick === void 0 || _extension$onClick.call(extension, onClickCallbackOptions);
149
+ } else {
150
+ if (extension.onClick) {
151
+ extension.onClick(onClickCallbackOptions);
152
+ }
134
153
  }
135
154
  };
136
155
  };
@@ -151,8 +170,32 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
151
170
  }
152
171
  };
153
172
  };
173
+ var getConfigFromExtensionCallback = function getConfigFromExtensionCallback(extension) {
174
+ if (typeof extension === 'function') {
175
+ var _getSelectionInfo2 = (0, _utils.getSelectionInfo)(state),
176
+ selectedNodeAdf = _getSelectionInfo2.selectedNodeAdf,
177
+ selectionRanges = _getSelectionInfo2.selectionRanges;
178
+ return extension({
179
+ selectedNodeAdf: selectedNodeAdf,
180
+ selectionRanges: selectionRanges
181
+ });
182
+ }
183
+ return extension;
184
+ };
185
+ var prefilterExtensions = function prefilterExtensions(extensions) {
186
+ // this is to prevent integration issues when passing in a function as an extension
187
+ // but not having platform_editor_selection_extension_api_v2 FG on
188
+ if (!(0, _platformFeatureFlags.fg)('platform_editor_selection_extension_api_v2')) {
189
+ return extensions.filter(function (ext) {
190
+ return typeof ext !== 'function';
191
+ });
192
+ }
193
+ return extensions;
194
+ };
154
195
  var getFirstPartyExtensions = function getFirstPartyExtensions(extensions) {
155
- return extensions.map(function (ext) {
196
+ var prefilteredExtensions = prefilterExtensions(extensions);
197
+ return prefilteredExtensions.map(function (extension) {
198
+ var ext = (0, _platformFeatureFlags.fg)('platform_editor_selection_extension_api_v2') ? getConfigFromExtensionCallback(extension) : extension;
156
199
  return convertExtensionToDropdownMenuItem(ext, 30);
157
200
  });
158
201
  };
@@ -161,9 +204,11 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
161
204
  * Add a heading to the external extensions
162
205
  */
163
206
  var getExternalExtensions = function getExternalExtensions(extensions) {
207
+ var prefilteredExtensions = prefilterExtensions(extensions);
164
208
  var externalExtensions = [];
165
- if (extensions !== null && extensions !== void 0 && extensions.length) {
166
- externalExtensions = extensions.map(function (ext, index) {
209
+ if (prefilteredExtensions !== null && prefilteredExtensions !== void 0 && prefilteredExtensions.length) {
210
+ externalExtensions = prefilteredExtensions.map(function (extension) {
211
+ var ext = (0, _platformFeatureFlags.fg)('platform_editor_selection_extension_api_v2') ? getConfigFromExtensionCallback(extension) : extension;
167
212
  return convertExtensionToDropdownMenuItem(ext);
168
213
  });
169
214
  var externalExtensionsHeading = {
@@ -174,22 +219,13 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
174
219
  }
175
220
  return externalExtensions;
176
221
  };
222
+ if (cachedOverflowMenuOptions && state.selection.eq(cachedSelection) && (0, _platformFeatureFlags.fg)('platform_editor_selection_extension_api_v2')) {
223
+ return (0, _selectionToolbar2.selectionToolbar)(cachedOverflowMenuOptions);
224
+ }
177
225
  var groupedExtensionsArray = [].concat((0, _toConsumableArray2.default)(getFirstPartyExtensions(extensions.firstParty || [])), (0, _toConsumableArray2.default)(getExternalExtensions(extensions.external || [])));
178
- var overflowMenu = {
179
- type: 'overflow-dropdown',
180
- dropdownWidth: 240,
181
- supportsViewMode: true,
182
- options: groupedExtensionsArray
183
- };
184
- return {
185
- isToolbarAbove: true,
186
- items: [{
187
- type: 'separator',
188
- fullHeight: true,
189
- supportsViewMode: true
190
- }, overflowMenu],
191
- rank: -6
192
- };
226
+ cachedOverflowMenuOptions = groupedExtensionsArray;
227
+ cachedSelection = state.selection;
228
+ return (0, _selectionToolbar2.selectionToolbar)(groupedExtensionsArray);
193
229
  }
194
230
  },
195
231
  pmPlugins: function pmPlugins() {
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.SelectionExtensionActionTypes = void 0;
7
+ // inspired by content api operation https://bitbucket.org/atlassian/pf-adf-service/src/master/src/lib/update/types.ts
7
8
  /**
8
9
  * @private
9
10
  * @deprecated Use {@link SelectionExtensionPluginOptions} instead.
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.selectionToolbar = void 0;
7
+ var selectionToolbar = exports.selectionToolbar = function selectionToolbar(options) {
8
+ return {
9
+ isToolbarAbove: true,
10
+ items: [{
11
+ type: 'separator',
12
+ fullHeight: true,
13
+ supportsViewMode: true
14
+ }, {
15
+ type: 'overflow-dropdown',
16
+ dropdownWidth: 240,
17
+ supportsViewMode: true,
18
+ options: options
19
+ }],
20
+ rank: -6
21
+ };
22
+ };
@@ -0,0 +1,78 @@
1
+ import { JSONTransformer } from '@atlaskit/editor-json-transformer';
2
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
3
+ import { CellSelection, TableMap } from '@atlaskit/editor-tables';
4
+ const getSelectedRect = selection => {
5
+ const {
6
+ $anchorCell,
7
+ $headCell
8
+ } = selection;
9
+ const table = $anchorCell.node(-1);
10
+ const map = TableMap.get(table);
11
+ const start = $anchorCell.start(-1);
12
+ const rect = map.rectBetween($anchorCell.pos - start, $headCell.pos - start);
13
+ return rect;
14
+ };
15
+ const getSelectionInfoFromSameNode = selection => {
16
+ const {
17
+ $from,
18
+ $to
19
+ } = selection;
20
+ return {
21
+ selectedNode: $from.node(),
22
+ selectionRanges: [{
23
+ start: {
24
+ pointer: `/content/${$from.index()}/text`,
25
+ position: $from.parentOffset
26
+ },
27
+ end: {
28
+ pointer: `/content/${$from.index()}/text`,
29
+ position: $to.parentOffset - 1
30
+ }
31
+ }]
32
+ };
33
+ };
34
+ const getSelectionInfoFromCellSelection = selection => {
35
+ const selectedNode = selection.$anchorCell.node(-1);
36
+ const selectionRanges = [];
37
+ const rect = getSelectedRect(selection);
38
+ for (let row = rect.top; row < rect.bottom; row++) {
39
+ selectionRanges.push({
40
+ start: {
41
+ pointer: `/content/${row}/content/${rect.left}`
42
+ },
43
+ end: {
44
+ pointer: `/content/${row}/content/${rect.right - 1}`
45
+ }
46
+ });
47
+ }
48
+ return {
49
+ selectedNode,
50
+ selectionRanges
51
+ };
52
+ };
53
+ export const getSelectionInfo = state => {
54
+ const selection = state.selection;
55
+ let selectionInfo = {
56
+ selectedNode: selection.$from.node(),
57
+ selectionRanges: []
58
+ };
59
+ if (selection instanceof TextSelection) {
60
+ const {
61
+ $from,
62
+ $to
63
+ } = selection;
64
+ if ($from.parent === $to.parent) {
65
+ selectionInfo = getSelectionInfoFromSameNode(selection);
66
+ } else {
67
+ // TODO: ED-28405 - when selection spans multiple nodes including nested node, we need to iterate through the nodes
68
+ }
69
+ } else if (selection instanceof CellSelection) {
70
+ selectionInfo = getSelectionInfoFromCellSelection(selection);
71
+ }
72
+ const serializer = new JSONTransformer();
73
+ const selectedNodeAdf = serializer.encodeNode(selectionInfo.selectedNode);
74
+ return {
75
+ selectedNodeAdf,
76
+ selectionRanges: selectionInfo.selectionRanges
77
+ };
78
+ };
@@ -1,9 +1,12 @@
1
1
  import React from 'react';
2
2
  import { selectionExtensionMessages } from '@atlaskit/editor-common/messages';
3
3
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
4
+ import { fg } from '@atlaskit/platform-feature-flags';
4
5
  import { createPlugin, selectionExtensionPluginKey } from './pm-plugins/main';
6
+ import { getSelectionInfo } from './pm-plugins/utils';
5
7
  import { SelectionExtensionComponentWrapper } from './ui/extension/SelectionExtensionComponentWrapper';
6
8
  import { getBoundingBoxFromSelection } from './ui/getBoundingBoxFromSelection';
9
+ import { selectionToolbar } from './ui/selectionToolbar';
7
10
  export const selectionExtensionPlugin = ({
8
11
  api,
9
12
  config
@@ -11,6 +14,8 @@ export const selectionExtensionPlugin = ({
11
14
  const editorViewRef = {
12
15
  current: null
13
16
  };
17
+ let cachedSelection;
18
+ let cachedOverflowMenuOptions;
14
19
  return {
15
20
  name: 'selectionExtension',
16
21
  getSharedState(editorState) {
@@ -107,7 +112,8 @@ export const selectionExtensionPlugin = ({
107
112
  from,
108
113
  to
109
114
  } = currentSelection;
110
- const text = state.doc.textBetween(from, to, '\n');
115
+ const currentState = fg('platform_editor_fix_get_selection_state_mismatch') ? view.state : state;
116
+ const text = currentState.doc.textBetween(from, to, '\n');
111
117
  const coords = getBoundingBoxFromSelection(view, from, to);
112
118
  return {
113
119
  text,
@@ -124,10 +130,24 @@ export const selectionExtensionPlugin = ({
124
130
  selection
125
131
  }));
126
132
  }
127
- if (extension.onClick) {
128
- extension.onClick({
129
- selection
130
- });
133
+ let onClickCallbackOptions = {
134
+ selection
135
+ };
136
+ if (fg('platform_editor_selection_extension_api_v2')) {
137
+ var _extension$onClick;
138
+ const {
139
+ selectedNodeAdf,
140
+ selectionRanges
141
+ } = getSelectionInfo(view.state);
142
+ onClickCallbackOptions = {
143
+ selectedNodeAdf,
144
+ selectionRanges
145
+ };
146
+ (_extension$onClick = extension.onClick) === null || _extension$onClick === void 0 ? void 0 : _extension$onClick.call(extension, onClickCallbackOptions);
147
+ } else {
148
+ if (extension.onClick) {
149
+ extension.onClick(onClickCallbackOptions);
150
+ }
131
151
  }
132
152
  };
133
153
  const convertExtensionToDropdownMenuItem = (extension, rank) => {
@@ -147,17 +167,46 @@ export const selectionExtensionPlugin = ({
147
167
  }
148
168
  };
149
169
  };
170
+ const getConfigFromExtensionCallback = extension => {
171
+ if (typeof extension === 'function') {
172
+ const {
173
+ selectedNodeAdf,
174
+ selectionRanges
175
+ } = getSelectionInfo(state);
176
+ return extension({
177
+ selectedNodeAdf,
178
+ selectionRanges
179
+ });
180
+ }
181
+ return extension;
182
+ };
183
+ const prefilterExtensions = extensions => {
184
+ // this is to prevent integration issues when passing in a function as an extension
185
+ // but not having platform_editor_selection_extension_api_v2 FG on
186
+ if (!fg('platform_editor_selection_extension_api_v2')) {
187
+ return extensions.filter(ext => typeof ext !== 'function');
188
+ }
189
+ return extensions;
190
+ };
150
191
  const getFirstPartyExtensions = extensions => {
151
- return extensions.map(ext => convertExtensionToDropdownMenuItem(ext, 30));
192
+ const prefilteredExtensions = prefilterExtensions(extensions);
193
+ return prefilteredExtensions.map(extension => {
194
+ const ext = fg('platform_editor_selection_extension_api_v2') ? getConfigFromExtensionCallback(extension) : extension;
195
+ return convertExtensionToDropdownMenuItem(ext, 30);
196
+ });
152
197
  };
153
198
 
154
199
  /**
155
200
  * Add a heading to the external extensions
156
201
  */
157
202
  const getExternalExtensions = extensions => {
203
+ const prefilteredExtensions = prefilterExtensions(extensions);
158
204
  let externalExtensions = [];
159
- if (extensions !== null && extensions !== void 0 && extensions.length) {
160
- externalExtensions = extensions.map((ext, index) => convertExtensionToDropdownMenuItem(ext));
205
+ if (prefilteredExtensions !== null && prefilteredExtensions !== void 0 && prefilteredExtensions.length) {
206
+ externalExtensions = prefilteredExtensions.map(extension => {
207
+ const ext = fg('platform_editor_selection_extension_api_v2') ? getConfigFromExtensionCallback(extension) : extension;
208
+ return convertExtensionToDropdownMenuItem(ext);
209
+ });
161
210
  const externalExtensionsHeading = {
162
211
  type: 'overflow-dropdown-heading',
163
212
  title: intl.formatMessage(selectionExtensionMessages.externalExtensionsHeading)
@@ -166,22 +215,13 @@ export const selectionExtensionPlugin = ({
166
215
  }
167
216
  return externalExtensions;
168
217
  };
218
+ if (cachedOverflowMenuOptions && state.selection.eq(cachedSelection) && fg('platform_editor_selection_extension_api_v2')) {
219
+ return selectionToolbar(cachedOverflowMenuOptions);
220
+ }
169
221
  const groupedExtensionsArray = [...getFirstPartyExtensions(extensions.firstParty || []), ...getExternalExtensions(extensions.external || [])];
170
- const overflowMenu = {
171
- type: 'overflow-dropdown',
172
- dropdownWidth: 240,
173
- supportsViewMode: true,
174
- options: groupedExtensionsArray
175
- };
176
- return {
177
- isToolbarAbove: true,
178
- items: [{
179
- type: 'separator',
180
- fullHeight: true,
181
- supportsViewMode: true
182
- }, overflowMenu],
183
- rank: -6
184
- };
222
+ cachedOverflowMenuOptions = groupedExtensionsArray;
223
+ cachedSelection = state.selection;
224
+ return selectionToolbar(groupedExtensionsArray);
185
225
  }
186
226
  },
187
227
  pmPlugins: () => [{
@@ -1,3 +1,5 @@
1
+ // inspired by content api operation https://bitbucket.org/atlassian/pf-adf-service/src/master/src/lib/update/types.ts
2
+
1
3
  /**
2
4
  * @private
3
5
  * @deprecated Use {@link SelectionExtensionPluginOptions} instead.
@@ -0,0 +1,14 @@
1
+ export const selectionToolbar = options => ({
2
+ isToolbarAbove: true,
3
+ items: [{
4
+ type: 'separator',
5
+ fullHeight: true,
6
+ supportsViewMode: true
7
+ }, {
8
+ type: 'overflow-dropdown',
9
+ dropdownWidth: 240,
10
+ supportsViewMode: true,
11
+ options
12
+ }],
13
+ rank: -6
14
+ });
@@ -0,0 +1,72 @@
1
+ import { JSONTransformer } from '@atlaskit/editor-json-transformer';
2
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
3
+ import { CellSelection, TableMap } from '@atlaskit/editor-tables';
4
+ var getSelectedRect = function getSelectedRect(selection) {
5
+ var $anchorCell = selection.$anchorCell,
6
+ $headCell = selection.$headCell;
7
+ var table = $anchorCell.node(-1);
8
+ var map = TableMap.get(table);
9
+ var start = $anchorCell.start(-1);
10
+ var rect = map.rectBetween($anchorCell.pos - start, $headCell.pos - start);
11
+ return rect;
12
+ };
13
+ var getSelectionInfoFromSameNode = function getSelectionInfoFromSameNode(selection) {
14
+ var $from = selection.$from,
15
+ $to = selection.$to;
16
+ return {
17
+ selectedNode: $from.node(),
18
+ selectionRanges: [{
19
+ start: {
20
+ pointer: "/content/".concat($from.index(), "/text"),
21
+ position: $from.parentOffset
22
+ },
23
+ end: {
24
+ pointer: "/content/".concat($from.index(), "/text"),
25
+ position: $to.parentOffset - 1
26
+ }
27
+ }]
28
+ };
29
+ };
30
+ var getSelectionInfoFromCellSelection = function getSelectionInfoFromCellSelection(selection) {
31
+ var selectedNode = selection.$anchorCell.node(-1);
32
+ var selectionRanges = [];
33
+ var rect = getSelectedRect(selection);
34
+ for (var row = rect.top; row < rect.bottom; row++) {
35
+ selectionRanges.push({
36
+ start: {
37
+ pointer: "/content/".concat(row, "/content/").concat(rect.left)
38
+ },
39
+ end: {
40
+ pointer: "/content/".concat(row, "/content/").concat(rect.right - 1)
41
+ }
42
+ });
43
+ }
44
+ return {
45
+ selectedNode: selectedNode,
46
+ selectionRanges: selectionRanges
47
+ };
48
+ };
49
+ export var getSelectionInfo = function getSelectionInfo(state) {
50
+ var selection = state.selection;
51
+ var selectionInfo = {
52
+ selectedNode: selection.$from.node(),
53
+ selectionRanges: []
54
+ };
55
+ if (selection instanceof TextSelection) {
56
+ var $from = selection.$from,
57
+ $to = selection.$to;
58
+ if ($from.parent === $to.parent) {
59
+ selectionInfo = getSelectionInfoFromSameNode(selection);
60
+ } else {
61
+ // TODO: ED-28405 - when selection spans multiple nodes including nested node, we need to iterate through the nodes
62
+ }
63
+ } else if (selection instanceof CellSelection) {
64
+ selectionInfo = getSelectionInfoFromCellSelection(selection);
65
+ }
66
+ var serializer = new JSONTransformer();
67
+ var selectedNodeAdf = serializer.encodeNode(selectionInfo.selectedNode);
68
+ return {
69
+ selectedNodeAdf: selectedNodeAdf,
70
+ selectionRanges: selectionInfo.selectionRanges
71
+ };
72
+ };
@@ -2,15 +2,20 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import React from 'react';
3
3
  import { selectionExtensionMessages } from '@atlaskit/editor-common/messages';
4
4
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
5
+ import { fg } from '@atlaskit/platform-feature-flags';
5
6
  import { createPlugin, selectionExtensionPluginKey } from './pm-plugins/main';
7
+ import { getSelectionInfo } from './pm-plugins/utils';
6
8
  import { SelectionExtensionComponentWrapper } from './ui/extension/SelectionExtensionComponentWrapper';
7
9
  import { getBoundingBoxFromSelection } from './ui/getBoundingBoxFromSelection';
10
+ import { selectionToolbar as _selectionToolbar } from './ui/selectionToolbar';
8
11
  export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
9
12
  var api = _ref.api,
10
13
  config = _ref.config;
11
14
  var editorViewRef = {
12
15
  current: null
13
16
  };
17
+ var cachedSelection;
18
+ var cachedOverflowMenuOptions;
14
19
  return {
15
20
  name: 'selectionExtension',
16
21
  getSharedState: function getSharedState(editorState) {
@@ -102,7 +107,8 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
102
107
  var currentSelection = view.state.selection;
103
108
  var from = currentSelection.from,
104
109
  to = currentSelection.to;
105
- var text = state.doc.textBetween(from, to, '\n');
110
+ var currentState = fg('platform_editor_fix_get_selection_state_mismatch') ? view.state : state;
111
+ var text = currentState.doc.textBetween(from, to, '\n');
106
112
  var coords = getBoundingBoxFromSelection(view, from, to);
107
113
  return {
108
114
  text: text,
@@ -120,10 +126,23 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
120
126
  selection: selection
121
127
  }));
122
128
  }
123
- if (extension.onClick) {
124
- extension.onClick({
125
- selection: selection
126
- });
129
+ var onClickCallbackOptions = {
130
+ selection: selection
131
+ };
132
+ if (fg('platform_editor_selection_extension_api_v2')) {
133
+ var _extension$onClick;
134
+ var _getSelectionInfo = getSelectionInfo(view.state),
135
+ selectedNodeAdf = _getSelectionInfo.selectedNodeAdf,
136
+ selectionRanges = _getSelectionInfo.selectionRanges;
137
+ onClickCallbackOptions = {
138
+ selectedNodeAdf: selectedNodeAdf,
139
+ selectionRanges: selectionRanges
140
+ };
141
+ (_extension$onClick = extension.onClick) === null || _extension$onClick === void 0 || _extension$onClick.call(extension, onClickCallbackOptions);
142
+ } else {
143
+ if (extension.onClick) {
144
+ extension.onClick(onClickCallbackOptions);
145
+ }
127
146
  }
128
147
  };
129
148
  };
@@ -144,8 +163,32 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
144
163
  }
145
164
  };
146
165
  };
166
+ var getConfigFromExtensionCallback = function getConfigFromExtensionCallback(extension) {
167
+ if (typeof extension === 'function') {
168
+ var _getSelectionInfo2 = getSelectionInfo(state),
169
+ selectedNodeAdf = _getSelectionInfo2.selectedNodeAdf,
170
+ selectionRanges = _getSelectionInfo2.selectionRanges;
171
+ return extension({
172
+ selectedNodeAdf: selectedNodeAdf,
173
+ selectionRanges: selectionRanges
174
+ });
175
+ }
176
+ return extension;
177
+ };
178
+ var prefilterExtensions = function prefilterExtensions(extensions) {
179
+ // this is to prevent integration issues when passing in a function as an extension
180
+ // but not having platform_editor_selection_extension_api_v2 FG on
181
+ if (!fg('platform_editor_selection_extension_api_v2')) {
182
+ return extensions.filter(function (ext) {
183
+ return typeof ext !== 'function';
184
+ });
185
+ }
186
+ return extensions;
187
+ };
147
188
  var getFirstPartyExtensions = function getFirstPartyExtensions(extensions) {
148
- return extensions.map(function (ext) {
189
+ var prefilteredExtensions = prefilterExtensions(extensions);
190
+ return prefilteredExtensions.map(function (extension) {
191
+ var ext = fg('platform_editor_selection_extension_api_v2') ? getConfigFromExtensionCallback(extension) : extension;
149
192
  return convertExtensionToDropdownMenuItem(ext, 30);
150
193
  });
151
194
  };
@@ -154,9 +197,11 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
154
197
  * Add a heading to the external extensions
155
198
  */
156
199
  var getExternalExtensions = function getExternalExtensions(extensions) {
200
+ var prefilteredExtensions = prefilterExtensions(extensions);
157
201
  var externalExtensions = [];
158
- if (extensions !== null && extensions !== void 0 && extensions.length) {
159
- externalExtensions = extensions.map(function (ext, index) {
202
+ if (prefilteredExtensions !== null && prefilteredExtensions !== void 0 && prefilteredExtensions.length) {
203
+ externalExtensions = prefilteredExtensions.map(function (extension) {
204
+ var ext = fg('platform_editor_selection_extension_api_v2') ? getConfigFromExtensionCallback(extension) : extension;
160
205
  return convertExtensionToDropdownMenuItem(ext);
161
206
  });
162
207
  var externalExtensionsHeading = {
@@ -167,22 +212,13 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
167
212
  }
168
213
  return externalExtensions;
169
214
  };
215
+ if (cachedOverflowMenuOptions && state.selection.eq(cachedSelection) && fg('platform_editor_selection_extension_api_v2')) {
216
+ return _selectionToolbar(cachedOverflowMenuOptions);
217
+ }
170
218
  var groupedExtensionsArray = [].concat(_toConsumableArray(getFirstPartyExtensions(extensions.firstParty || [])), _toConsumableArray(getExternalExtensions(extensions.external || [])));
171
- var overflowMenu = {
172
- type: 'overflow-dropdown',
173
- dropdownWidth: 240,
174
- supportsViewMode: true,
175
- options: groupedExtensionsArray
176
- };
177
- return {
178
- isToolbarAbove: true,
179
- items: [{
180
- type: 'separator',
181
- fullHeight: true,
182
- supportsViewMode: true
183
- }, overflowMenu],
184
- rank: -6
185
- };
219
+ cachedOverflowMenuOptions = groupedExtensionsArray;
220
+ cachedSelection = state.selection;
221
+ return _selectionToolbar(groupedExtensionsArray);
186
222
  }
187
223
  },
188
224
  pmPlugins: function pmPlugins() {
@@ -1,3 +1,5 @@
1
+ // inspired by content api operation https://bitbucket.org/atlassian/pf-adf-service/src/master/src/lib/update/types.ts
2
+
1
3
  /**
2
4
  * @private
3
5
  * @deprecated Use {@link SelectionExtensionPluginOptions} instead.
@@ -0,0 +1,16 @@
1
+ export var selectionToolbar = function selectionToolbar(options) {
2
+ return {
3
+ isToolbarAbove: true,
4
+ items: [{
5
+ type: 'separator',
6
+ fullHeight: true,
7
+ supportsViewMode: true
8
+ }, {
9
+ type: 'overflow-dropdown',
10
+ dropdownWidth: 240,
11
+ supportsViewMode: true,
12
+ options: options
13
+ }],
14
+ rank: -6
15
+ };
16
+ };
@@ -0,0 +1,3 @@
1
+ import { type EditorState } from '@atlaskit/editor-prosemirror/state';
2
+ import { type SelectionExtensionFnOptions } from '../../types';
3
+ export declare const getSelectionInfo: (state: EditorState) => SelectionExtensionFnOptions;
@@ -1,4 +1,5 @@
1
1
  /// <reference types="react" />
2
+ import type { ADFEntity } from '@atlaskit/adf-utils/types';
2
3
  import { type MenuItem } from '@atlaskit/editor-common/ui-menu';
3
4
  import type { ViewMode } from '@atlaskit/editor-plugin-editor-viewmode';
4
5
  export type MenuItemsType = Array<{
@@ -10,6 +11,8 @@ export type SelectionExtensionComponentProps = {
10
11
  };
11
12
  export type SelectionExtensionCallbackOptions = {
12
13
  selection?: SelectionExtensionSelectionInfo;
14
+ selectedNodeAdf?: ADFEntity;
15
+ selectionRanges?: SelectionRange[];
13
16
  };
14
17
  export type SelectionExtensionSelectionInfo = {
15
18
  text: string;
@@ -32,9 +35,23 @@ export type SelectionExtension = {
32
35
  onClick?: (params: SelectionExtensionCallbackOptions) => void;
33
36
  component?: React.ComponentType<SelectionExtensionComponentProps>;
34
37
  };
38
+ export type SelectionPointer = {
39
+ pointer: string;
40
+ position?: number;
41
+ };
42
+ export type SelectionRange = {
43
+ start: SelectionPointer;
44
+ end: SelectionPointer;
45
+ };
46
+ export type SelectionExtensionFnOptions = {
47
+ selectedNodeAdf: ADFEntity;
48
+ selectionRanges: SelectionRange[];
49
+ };
50
+ export type SelectionExtensionFn = ({ selectedNodeAdf, selectionRanges, }: SelectionExtensionFnOptions) => SelectionExtension;
51
+ export type SelectionExtensionConfig = SelectionExtension | SelectionExtensionFn;
35
52
  export type SelectionExtensions = {
36
- firstParty?: SelectionExtension[];
37
- external?: SelectionExtension[];
53
+ firstParty?: SelectionExtensionConfig[];
54
+ external?: SelectionExtensionConfig[];
38
55
  };
39
56
  type SelectionExtensionModes = ViewMode;
40
57
  export type SelectionExtensionPluginOptions = {
@@ -0,0 +1,18 @@
1
+ import { type Command, type FloatingToolbarOverflowDropdownOptions } from '@atlaskit/editor-common/types';
2
+ export declare const selectionToolbar: (options: FloatingToolbarOverflowDropdownOptions<Command>) => {
3
+ isToolbarAbove: boolean;
4
+ items: ({
5
+ type: "separator";
6
+ fullHeight: boolean;
7
+ supportsViewMode: boolean;
8
+ dropdownWidth?: undefined;
9
+ options?: undefined;
10
+ } | {
11
+ type: "overflow-dropdown";
12
+ dropdownWidth: number;
13
+ supportsViewMode: boolean;
14
+ options: FloatingToolbarOverflowDropdownOptions<Command>;
15
+ fullHeight?: undefined;
16
+ })[];
17
+ rank: number;
18
+ };
@@ -0,0 +1,3 @@
1
+ import { type EditorState } from '@atlaskit/editor-prosemirror/state';
2
+ import { type SelectionExtensionFnOptions } from '../../types';
3
+ export declare const getSelectionInfo: (state: EditorState) => SelectionExtensionFnOptions;
@@ -1,4 +1,5 @@
1
1
  /// <reference types="react" />
2
+ import type { ADFEntity } from '@atlaskit/adf-utils/types';
2
3
  import { type MenuItem } from '@atlaskit/editor-common/ui-menu';
3
4
  import type { ViewMode } from '@atlaskit/editor-plugin-editor-viewmode';
4
5
  export type MenuItemsType = Array<{
@@ -10,6 +11,8 @@ export type SelectionExtensionComponentProps = {
10
11
  };
11
12
  export type SelectionExtensionCallbackOptions = {
12
13
  selection?: SelectionExtensionSelectionInfo;
14
+ selectedNodeAdf?: ADFEntity;
15
+ selectionRanges?: SelectionRange[];
13
16
  };
14
17
  export type SelectionExtensionSelectionInfo = {
15
18
  text: string;
@@ -32,9 +35,23 @@ export type SelectionExtension = {
32
35
  onClick?: (params: SelectionExtensionCallbackOptions) => void;
33
36
  component?: React.ComponentType<SelectionExtensionComponentProps>;
34
37
  };
38
+ export type SelectionPointer = {
39
+ pointer: string;
40
+ position?: number;
41
+ };
42
+ export type SelectionRange = {
43
+ start: SelectionPointer;
44
+ end: SelectionPointer;
45
+ };
46
+ export type SelectionExtensionFnOptions = {
47
+ selectedNodeAdf: ADFEntity;
48
+ selectionRanges: SelectionRange[];
49
+ };
50
+ export type SelectionExtensionFn = ({ selectedNodeAdf, selectionRanges, }: SelectionExtensionFnOptions) => SelectionExtension;
51
+ export type SelectionExtensionConfig = SelectionExtension | SelectionExtensionFn;
35
52
  export type SelectionExtensions = {
36
- firstParty?: SelectionExtension[];
37
- external?: SelectionExtension[];
53
+ firstParty?: SelectionExtensionConfig[];
54
+ external?: SelectionExtensionConfig[];
38
55
  };
39
56
  type SelectionExtensionModes = ViewMode;
40
57
  export type SelectionExtensionPluginOptions = {
@@ -0,0 +1,18 @@
1
+ import { type Command, type FloatingToolbarOverflowDropdownOptions } from '@atlaskit/editor-common/types';
2
+ export declare const selectionToolbar: (options: FloatingToolbarOverflowDropdownOptions<Command>) => {
3
+ isToolbarAbove: boolean;
4
+ items: ({
5
+ type: "separator";
6
+ fullHeight: boolean;
7
+ supportsViewMode: boolean;
8
+ dropdownWidth?: undefined;
9
+ options?: undefined;
10
+ } | {
11
+ type: "overflow-dropdown";
12
+ dropdownWidth: number;
13
+ supportsViewMode: boolean;
14
+ options: FloatingToolbarOverflowDropdownOptions<Command>;
15
+ fullHeight?: undefined;
16
+ })[];
17
+ rank: number;
18
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-selection-extension",
3
- "version": "2.4.5",
3
+ "version": "2.4.7",
4
4
  "description": "editor-plugin-selection-extension plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -9,7 +9,6 @@
9
9
  },
10
10
  "atlassian": {
11
11
  "team": "Editor: Core Experiences",
12
- "runReact18": true,
13
12
  "website": {
14
13
  "name": "EditorPluginSelectionExtension",
15
14
  "category": "Layout and structure"
@@ -36,15 +35,18 @@
36
35
  ".": "./src/index.ts"
37
36
  },
38
37
  "dependencies": {
39
- "@atlaskit/editor-common": "^107.0.0",
38
+ "@atlaskit/adf-utils": "^19.20.0",
39
+ "@atlaskit/editor-common": "^107.6.0",
40
+ "@atlaskit/editor-json-transformer": "^8.24.0",
40
41
  "@atlaskit/editor-plugin-analytics": "^2.3.0",
41
42
  "@atlaskit/editor-plugin-editor-viewmode": "^4.0.0",
42
- "@atlaskit/editor-plugin-selection-toolbar": "^3.7.0",
43
+ "@atlaskit/editor-plugin-selection-toolbar": "^3.8.0",
43
44
  "@atlaskit/editor-prosemirror": "7.0.0",
44
- "@atlaskit/icon": "^27.0.0",
45
+ "@atlaskit/editor-tables": "^2.9.0",
46
+ "@atlaskit/icon": "^27.2.0",
45
47
  "@atlaskit/platform-feature-flags": "^1.1.0",
46
- "@atlaskit/primitives": "^14.9.0",
47
- "@atlaskit/tmp-editor-statsig": "^8.0.0",
48
+ "@atlaskit/primitives": "^14.10.0",
49
+ "@atlaskit/tmp-editor-statsig": "^8.7.0",
48
50
  "@babel/runtime": "^7.0.0",
49
51
  "react-intl-next": "npm:react-intl@^5.18.1",
50
52
  "uuid": "^3.1.0"
@@ -99,5 +101,13 @@
99
101
  "import-no-extraneous-disable-for-examples-and-docs"
100
102
  ]
101
103
  }
104
+ },
105
+ "platform-feature-flags": {
106
+ "platform_editor_selection_extension_api_v2": {
107
+ "type": "boolean"
108
+ },
109
+ "platform_editor_fix_get_selection_state_mismatch": {
110
+ "type": "boolean"
111
+ }
102
112
  }
103
113
  }