@atlaskit/editor-plugin-selection-extension 2.4.6 → 3.0.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,54 @@
1
1
  # @atlaskit/editor-plugin-selection-extension
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#181024](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/181024)
8
+ [`8e80c487ca307`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8e80c487ca307) - ##
9
+ Make `@atlaskit/editor-common` a peer dependency
10
+
11
+ **WHAT:** `@atlaskit/editor-common` has been moved from `dependencies` to `peerDependencies` in
12
+ all editor plugin packages.
13
+
14
+ **WHY:** This change ensures that only a single version of `@atlaskit/editor-common` is used in
15
+ consuming applications, preventing issues caused by multiple versions of singleton libraries (such
16
+ as context mismatches or duplicated state). This is especially important for packages that rely on
17
+ shared context or singletons.
18
+
19
+ **HOW TO ADJUST:**
20
+
21
+ - Consumers must now explicitly install `@atlaskit/editor-common` in their own project if they use
22
+ any of these editor plugins.
23
+ - Ensure the version you install matches the version required by the plugins.
24
+ - You can use the
25
+ [`check-peer-dependencies`](https://www.npmjs.com/package/check-peer-dependencies) package to
26
+ verify that all required peer dependencies are installed and compatible.
27
+ - Example install command:
28
+ ```
29
+ npm install @atlaskit/editor-common
30
+ ```
31
+ or
32
+ ```
33
+ yarn add @atlaskit/editor-common
34
+ ```
35
+
36
+ **Note:** This is a breaking change. If `@atlaskit/editor-common` is not installed at the
37
+ application level, you may see errors or unexpected behavior.
38
+
39
+ ### Patch Changes
40
+
41
+ - Updated dependencies
42
+
43
+ ## 2.4.7
44
+
45
+ ### Patch Changes
46
+
47
+ - [#177823](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/177823)
48
+ [`8bd9bca774e4f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8bd9bca774e4f) -
49
+ Extend selection extension API to support dynamic loading of extension configuration
50
+ - Updated dependencies
51
+
3
52
  ## 2.4.6
4
53
 
5
54
  ### 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
+ };
@@ -11,14 +11,18 @@ 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
13
  var _main = require("./pm-plugins/main");
14
+ var _utils = require("./pm-plugins/utils");
14
15
  var _SelectionExtensionComponentWrapper = require("./ui/extension/SelectionExtensionComponentWrapper");
15
16
  var _getBoundingBoxFromSelection = require("./ui/getBoundingBoxFromSelection");
17
+ var _selectionToolbar2 = require("./ui/selectionToolbar");
16
18
  var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
17
19
  var api = _ref.api,
18
20
  config = _ref.config;
19
21
  var editorViewRef = {
20
22
  current: null
21
23
  };
24
+ var cachedSelection;
25
+ var cachedOverflowMenuOptions;
22
26
  return {
23
27
  name: 'selectionExtension',
24
28
  getSharedState: function getSharedState(editorState) {
@@ -129,10 +133,23 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
129
133
  selection: selection
130
134
  }));
131
135
  }
132
- if (extension.onClick) {
133
- extension.onClick({
134
- selection: selection
135
- });
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
+ }
136
153
  }
137
154
  };
138
155
  };
@@ -153,8 +170,32 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
153
170
  }
154
171
  };
155
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
+ };
156
195
  var getFirstPartyExtensions = function getFirstPartyExtensions(extensions) {
157
- 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;
158
199
  return convertExtensionToDropdownMenuItem(ext, 30);
159
200
  });
160
201
  };
@@ -163,9 +204,11 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
163
204
  * Add a heading to the external extensions
164
205
  */
165
206
  var getExternalExtensions = function getExternalExtensions(extensions) {
207
+ var prefilteredExtensions = prefilterExtensions(extensions);
166
208
  var externalExtensions = [];
167
- if (extensions !== null && extensions !== void 0 && extensions.length) {
168
- 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;
169
212
  return convertExtensionToDropdownMenuItem(ext);
170
213
  });
171
214
  var externalExtensionsHeading = {
@@ -176,22 +219,13 @@ var selectionExtensionPlugin = exports.selectionExtensionPlugin = function selec
176
219
  }
177
220
  return externalExtensions;
178
221
  };
222
+ if (cachedOverflowMenuOptions && state.selection.eq(cachedSelection) && (0, _platformFeatureFlags.fg)('platform_editor_selection_extension_api_v2')) {
223
+ return (0, _selectionToolbar2.selectionToolbar)(cachedOverflowMenuOptions);
224
+ }
179
225
  var groupedExtensionsArray = [].concat((0, _toConsumableArray2.default)(getFirstPartyExtensions(extensions.firstParty || [])), (0, _toConsumableArray2.default)(getExternalExtensions(extensions.external || [])));
180
- var overflowMenu = {
181
- type: 'overflow-dropdown',
182
- dropdownWidth: 240,
183
- supportsViewMode: true,
184
- options: groupedExtensionsArray
185
- };
186
- return {
187
- isToolbarAbove: true,
188
- items: [{
189
- type: 'separator',
190
- fullHeight: true,
191
- supportsViewMode: true
192
- }, overflowMenu],
193
- rank: -6
194
- };
226
+ cachedOverflowMenuOptions = groupedExtensionsArray;
227
+ cachedSelection = state.selection;
228
+ return (0, _selectionToolbar2.selectionToolbar)(groupedExtensionsArray);
195
229
  }
196
230
  },
197
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
+ };
@@ -3,8 +3,10 @@ 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
5
  import { createPlugin, selectionExtensionPluginKey } from './pm-plugins/main';
6
+ import { getSelectionInfo } from './pm-plugins/utils';
6
7
  import { SelectionExtensionComponentWrapper } from './ui/extension/SelectionExtensionComponentWrapper';
7
8
  import { getBoundingBoxFromSelection } from './ui/getBoundingBoxFromSelection';
9
+ import { selectionToolbar } from './ui/selectionToolbar';
8
10
  export const selectionExtensionPlugin = ({
9
11
  api,
10
12
  config
@@ -12,6 +14,8 @@ export const selectionExtensionPlugin = ({
12
14
  const editorViewRef = {
13
15
  current: null
14
16
  };
17
+ let cachedSelection;
18
+ let cachedOverflowMenuOptions;
15
19
  return {
16
20
  name: 'selectionExtension',
17
21
  getSharedState(editorState) {
@@ -126,10 +130,24 @@ export const selectionExtensionPlugin = ({
126
130
  selection
127
131
  }));
128
132
  }
129
- if (extension.onClick) {
130
- extension.onClick({
131
- selection
132
- });
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
+ }
133
151
  }
134
152
  };
135
153
  const convertExtensionToDropdownMenuItem = (extension, rank) => {
@@ -149,17 +167,46 @@ export const selectionExtensionPlugin = ({
149
167
  }
150
168
  };
151
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
+ };
152
191
  const getFirstPartyExtensions = extensions => {
153
- 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
+ });
154
197
  };
155
198
 
156
199
  /**
157
200
  * Add a heading to the external extensions
158
201
  */
159
202
  const getExternalExtensions = extensions => {
203
+ const prefilteredExtensions = prefilterExtensions(extensions);
160
204
  let externalExtensions = [];
161
- if (extensions !== null && extensions !== void 0 && extensions.length) {
162
- 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
+ });
163
210
  const externalExtensionsHeading = {
164
211
  type: 'overflow-dropdown-heading',
165
212
  title: intl.formatMessage(selectionExtensionMessages.externalExtensionsHeading)
@@ -168,22 +215,13 @@ export const selectionExtensionPlugin = ({
168
215
  }
169
216
  return externalExtensions;
170
217
  };
218
+ if (cachedOverflowMenuOptions && state.selection.eq(cachedSelection) && fg('platform_editor_selection_extension_api_v2')) {
219
+ return selectionToolbar(cachedOverflowMenuOptions);
220
+ }
171
221
  const groupedExtensionsArray = [...getFirstPartyExtensions(extensions.firstParty || []), ...getExternalExtensions(extensions.external || [])];
172
- const overflowMenu = {
173
- type: 'overflow-dropdown',
174
- dropdownWidth: 240,
175
- supportsViewMode: true,
176
- options: groupedExtensionsArray
177
- };
178
- return {
179
- isToolbarAbove: true,
180
- items: [{
181
- type: 'separator',
182
- fullHeight: true,
183
- supportsViewMode: true
184
- }, overflowMenu],
185
- rank: -6
186
- };
222
+ cachedOverflowMenuOptions = groupedExtensionsArray;
223
+ cachedSelection = state.selection;
224
+ return selectionToolbar(groupedExtensionsArray);
187
225
  }
188
226
  },
189
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
+ };
@@ -4,14 +4,18 @@ 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
6
  import { createPlugin, selectionExtensionPluginKey } from './pm-plugins/main';
7
+ import { getSelectionInfo } from './pm-plugins/utils';
7
8
  import { SelectionExtensionComponentWrapper } from './ui/extension/SelectionExtensionComponentWrapper';
8
9
  import { getBoundingBoxFromSelection } from './ui/getBoundingBoxFromSelection';
10
+ import { selectionToolbar as _selectionToolbar } from './ui/selectionToolbar';
9
11
  export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
10
12
  var api = _ref.api,
11
13
  config = _ref.config;
12
14
  var editorViewRef = {
13
15
  current: null
14
16
  };
17
+ var cachedSelection;
18
+ var cachedOverflowMenuOptions;
15
19
  return {
16
20
  name: 'selectionExtension',
17
21
  getSharedState: function getSharedState(editorState) {
@@ -122,10 +126,23 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
122
126
  selection: selection
123
127
  }));
124
128
  }
125
- if (extension.onClick) {
126
- extension.onClick({
127
- selection: selection
128
- });
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
+ }
129
146
  }
130
147
  };
131
148
  };
@@ -146,8 +163,32 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
146
163
  }
147
164
  };
148
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
+ };
149
188
  var getFirstPartyExtensions = function getFirstPartyExtensions(extensions) {
150
- 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;
151
192
  return convertExtensionToDropdownMenuItem(ext, 30);
152
193
  });
153
194
  };
@@ -156,9 +197,11 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
156
197
  * Add a heading to the external extensions
157
198
  */
158
199
  var getExternalExtensions = function getExternalExtensions(extensions) {
200
+ var prefilteredExtensions = prefilterExtensions(extensions);
159
201
  var externalExtensions = [];
160
- if (extensions !== null && extensions !== void 0 && extensions.length) {
161
- 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;
162
205
  return convertExtensionToDropdownMenuItem(ext);
163
206
  });
164
207
  var externalExtensionsHeading = {
@@ -169,22 +212,13 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
169
212
  }
170
213
  return externalExtensions;
171
214
  };
215
+ if (cachedOverflowMenuOptions && state.selection.eq(cachedSelection) && fg('platform_editor_selection_extension_api_v2')) {
216
+ return _selectionToolbar(cachedOverflowMenuOptions);
217
+ }
172
218
  var groupedExtensionsArray = [].concat(_toConsumableArray(getFirstPartyExtensions(extensions.firstParty || [])), _toConsumableArray(getExternalExtensions(extensions.external || [])));
173
- var overflowMenu = {
174
- type: 'overflow-dropdown',
175
- dropdownWidth: 240,
176
- supportsViewMode: true,
177
- options: groupedExtensionsArray
178
- };
179
- return {
180
- isToolbarAbove: true,
181
- items: [{
182
- type: 'separator',
183
- fullHeight: true,
184
- supportsViewMode: true
185
- }, overflowMenu],
186
- rank: -6
187
- };
219
+ cachedOverflowMenuOptions = groupedExtensionsArray;
220
+ cachedSelection = state.selection;
221
+ return _selectionToolbar(groupedExtensionsArray);
188
222
  }
189
223
  },
190
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.6",
3
+ "version": "3.0.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",
@@ -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,20 +35,23 @@
36
35
  ".": "./src/index.ts"
37
36
  },
38
37
  "dependencies": {
39
- "@atlaskit/editor-common": "^107.2.0",
40
- "@atlaskit/editor-plugin-analytics": "^2.3.0",
41
- "@atlaskit/editor-plugin-editor-viewmode": "^4.0.0",
42
- "@atlaskit/editor-plugin-selection-toolbar": "^3.7.0",
38
+ "@atlaskit/adf-utils": "^19.20.0",
39
+ "@atlaskit/editor-json-transformer": "^8.24.0",
40
+ "@atlaskit/editor-plugin-analytics": "^3.0.0",
41
+ "@atlaskit/editor-plugin-editor-viewmode": "^5.0.0",
42
+ "@atlaskit/editor-plugin-selection-toolbar": "^4.0.0",
43
43
  "@atlaskit/editor-prosemirror": "7.0.0",
44
- "@atlaskit/icon": "^27.1.0",
44
+ "@atlaskit/editor-tables": "^2.9.0",
45
+ "@atlaskit/icon": "^27.2.0",
45
46
  "@atlaskit/platform-feature-flags": "^1.1.0",
46
- "@atlaskit/primitives": "^14.9.0",
47
- "@atlaskit/tmp-editor-statsig": "^8.0.0",
47
+ "@atlaskit/primitives": "^14.10.0",
48
+ "@atlaskit/tmp-editor-statsig": "^8.7.0",
48
49
  "@babel/runtime": "^7.0.0",
49
50
  "react-intl-next": "npm:react-intl@^5.18.1",
50
51
  "uuid": "^3.1.0"
51
52
  },
52
53
  "peerDependencies": {
54
+ "@atlaskit/editor-common": "^107.6.0",
53
55
  "react": "^18.2.0"
54
56
  },
55
57
  "devDependencies": {
@@ -101,6 +103,9 @@
101
103
  }
102
104
  },
103
105
  "platform-feature-flags": {
106
+ "platform_editor_selection_extension_api_v2": {
107
+ "type": "boolean"
108
+ },
104
109
  "platform_editor_fix_get_selection_state_mismatch": {
105
110
  "type": "boolean"
106
111
  }