@atlaskit/editor-plugin-selection-extension 3.4.0 → 3.4.2

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 (31) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/selectionExtensionPlugin.js +75 -35
  3. package/dist/cjs/ui/LegacyToolbarComponent.js +120 -0
  4. package/dist/cjs/ui/extensions.js +60 -0
  5. package/dist/cjs/ui/getBoundingBoxFromSelection.js +7 -0
  6. package/dist/cjs/ui/selectionToolbar.js +32 -5
  7. package/dist/es2019/selectionExtensionPlugin.js +63 -27
  8. package/dist/es2019/ui/LegacyToolbarComponent.js +109 -0
  9. package/dist/es2019/ui/extensions.js +54 -0
  10. package/dist/es2019/ui/getBoundingBoxFromSelection.js +7 -1
  11. package/dist/es2019/ui/selectionToolbar.js +38 -14
  12. package/dist/esm/selectionExtensionPlugin.js +75 -35
  13. package/dist/esm/ui/LegacyToolbarComponent.js +111 -0
  14. package/dist/esm/ui/extensions.js +54 -0
  15. package/dist/esm/ui/getBoundingBoxFromSelection.js +7 -0
  16. package/dist/esm/ui/selectionToolbar.js +31 -5
  17. package/dist/types/index.d.ts +1 -1
  18. package/dist/types/selectionExtensionPluginType.d.ts +4 -0
  19. package/dist/types/types/index.d.ts +41 -1
  20. package/dist/types/ui/LegacyToolbarComponent.d.ts +16 -0
  21. package/dist/types/ui/extensions.d.ts +27 -0
  22. package/dist/types/ui/getBoundingBoxFromSelection.d.ts +3 -2
  23. package/dist/types/ui/selectionToolbar.d.ts +7 -17
  24. package/dist/types-ts4.5/index.d.ts +1 -1
  25. package/dist/types-ts4.5/selectionExtensionPluginType.d.ts +4 -0
  26. package/dist/types-ts4.5/types/index.d.ts +41 -1
  27. package/dist/types-ts4.5/ui/LegacyToolbarComponent.d.ts +16 -0
  28. package/dist/types-ts4.5/ui/extensions.d.ts +27 -0
  29. package/dist/types-ts4.5/ui/getBoundingBoxFromSelection.d.ts +3 -2
  30. package/dist/types-ts4.5/ui/selectionToolbar.d.ts +7 -17
  31. package/package.json +10 -4
@@ -0,0 +1,109 @@
1
+ import React, { useState } from 'react';
2
+ import { ArrowKeyNavigationType, DropdownMenuWithKeyboardNavigation as DropdownMenu, ToolbarButton } from '@atlaskit/editor-common/ui-menu';
3
+ export const LegacyPrimaryToolbarComponent = ({
4
+ primaryToolbarItemExtensions
5
+ }) => {
6
+ // NEXT PR: need to render a separator after – if there are extensions added
7
+ return /*#__PURE__*/React.createElement(React.Fragment, null, primaryToolbarItemExtensions.map((toolbarItemExtension, i) => {
8
+ const toolbarItem = toolbarItemExtension.getToolbarItem();
9
+ return /*#__PURE__*/React.createElement(LegacyExtensionToolbarItem, {
10
+ key: toolbarItem.tooltip,
11
+ toolbarItem: toolbarItem
12
+ });
13
+ }));
14
+ };
15
+ export const LegacyExtensionToolbarItem = ({
16
+ toolbarItem,
17
+ getMenuItems
18
+ }) => {
19
+ const [isOpen, setIsOpen] = useState(false);
20
+ const {
21
+ icon: Icon,
22
+ tooltip,
23
+ isDisabled,
24
+ onClick,
25
+ label: _label
26
+ } = toolbarItem;
27
+ if (!getMenuItems) {
28
+ return /*#__PURE__*/React.createElement(ToolbarButton, {
29
+ spacing: "default",
30
+ disabled: isDisabled,
31
+ selected: isOpen,
32
+ title: tooltip,
33
+ "aria-label": tooltip,
34
+ "aria-expanded": isOpen,
35
+ "aria-haspopup": true,
36
+ onClick: onClick,
37
+ iconBefore: /*#__PURE__*/React.createElement(Icon, {
38
+ label: tooltip
39
+ })
40
+ });
41
+ }
42
+ const toggleOpen = () => {
43
+ setIsOpen(prev => !prev);
44
+ };
45
+ const toggleOpenByKeyboard = event => {
46
+ if (event.key === 'Enter' || event.key === ' ') {
47
+ event.preventDefault();
48
+ toggleOpen();
49
+ }
50
+ };
51
+ const handleItemActivated = ({
52
+ item,
53
+ shouldCloseMenu = true
54
+ }) => {
55
+ var _item$onClick;
56
+ (_item$onClick = item.onClick) === null || _item$onClick === void 0 ? void 0 : _item$onClick.call(item);
57
+ if (shouldCloseMenu) {
58
+ setIsOpen(false);
59
+ }
60
+ };
61
+ const handleOnOpenChange = attrs => {
62
+ setIsOpen(!!(attrs !== null && attrs !== void 0 && attrs.isOpen));
63
+ };
64
+ const items = isOpen ? getMenuItems().map((menuItem, i) => {
65
+ return {
66
+ key: `menu-item-${i}`,
67
+ content: menuItem.label,
68
+ elemBefore: /*#__PURE__*/React.createElement(menuItem.icon, {
69
+ label: menuItem.label
70
+ }),
71
+ onClick: () => {
72
+ var _menuItem$onClick;
73
+ (_menuItem$onClick = menuItem.onClick) === null || _menuItem$onClick === void 0 ? void 0 : _menuItem$onClick.call(menuItem);
74
+ // NEXT PR: here we need to set the active extension so the contentComponent can render
75
+ // menuItem.contentComponent
76
+ },
77
+ isDisabled: menuItem.isDisabled,
78
+ 'aria-label': menuItem.label,
79
+ value: {
80
+ name: menuItem.label
81
+ }
82
+ };
83
+ }) : [];
84
+ return /*#__PURE__*/React.createElement(DropdownMenu, {
85
+ arrowKeyNavigationProviderOptions: {
86
+ type: ArrowKeyNavigationType.MENU
87
+ },
88
+ items: [{
89
+ items
90
+ }],
91
+ isOpen: isOpen,
92
+ onItemActivated: handleItemActivated,
93
+ onOpenChange: handleOnOpenChange,
94
+ fitWidth: 200
95
+ }, /*#__PURE__*/React.createElement(ToolbarButton, {
96
+ spacing: "default",
97
+ disabled: isDisabled,
98
+ selected: isOpen,
99
+ title: tooltip,
100
+ "aria-label": tooltip,
101
+ "aria-expanded": isOpen,
102
+ "aria-haspopup": true,
103
+ onClick: toggleOpen,
104
+ onKeyDown: toggleOpenByKeyboard,
105
+ iconBefore: /*#__PURE__*/React.createElement(Icon, {
106
+ label: tooltip
107
+ })
108
+ }));
109
+ };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * From the full list of extensions, extract only those that have a toolbar item configuration
3
+ * for the specified type (either 'primaryToolbar' or 'inlineToolbar').
4
+ *
5
+ * @param extensionList - List of all extensions
6
+ * @param toolbarType - Type of toolbar ('primaryToolbar' or 'inlineToolbar')
7
+ * @returns Array of ToolbarItemExtension objects
8
+ * @example
9
+ */
10
+ export const getToolbarItemExtensions = (extensionList, toolbarType) => {
11
+ return extensionList.reduce((acc, extension) => {
12
+ const toolbarConfig = extension[toolbarType];
13
+ if (toolbarConfig !== null && toolbarConfig !== void 0 && toolbarConfig.getToolbarItem) {
14
+ acc.push({
15
+ getToolbarItem: toolbarConfig.getToolbarItem,
16
+ getMenuItems: toolbarConfig.getMenuItems
17
+ });
18
+ }
19
+ return acc;
20
+ }, []);
21
+ };
22
+
23
+ /**
24
+ * From the full list of extensions, extract only those that have a menu item configuration
25
+ * for the specified source (either 'first-party' or 'external').
26
+ *
27
+ * Map each to the legacy format for SelectionExtensionConfig.
28
+ *
29
+ * @param extensionList - List of all extensions
30
+ * @param targetSource - Source of the extensions ('first-party' or 'external')
31
+ * @returns Array of SelectionExtensionConfig objects
32
+ * @example
33
+ */
34
+ export const getMenuItemExtensions = (extensionList, targetSource) => {
35
+ return extensionList.reduce((acc, extension) => {
36
+ const {
37
+ source,
38
+ inlineToolbar
39
+ } = extension;
40
+ if (source === targetSource && inlineToolbar !== null && inlineToolbar !== void 0 && inlineToolbar.getMenuItems && !inlineToolbar.getToolbarItem) {
41
+ const menuItems = inlineToolbar.getMenuItems();
42
+ menuItems.forEach(menuItem => {
43
+ acc.push({
44
+ name: menuItem.label,
45
+ icon: menuItem.icon,
46
+ onClick: menuItem.onClick,
47
+ isDisabled: () => !!menuItem.isDisabled,
48
+ component: menuItem.contentComponent
49
+ });
50
+ });
51
+ }
52
+ return acc;
53
+ }, []);
54
+ };
@@ -4,9 +4,13 @@
4
4
  * @param view - The editor view instance.
5
5
  * @param from - The starting position of the selection.
6
6
  * @param to - The ending position of the selection.
7
+ * @param offset - Optional offset to adjust the top and bottom coordinates of the bounding box.`
7
8
  * @returns An object containing the top, left, bottom, and right coordinates of the bounding box.
8
9
  */
9
- export const getBoundingBoxFromSelection = (view, from, to) => {
10
+ export const getBoundingBoxFromSelection = (view, from, to, offset = {
11
+ top: 0,
12
+ bottom: 0
13
+ }) => {
10
14
  let top = Infinity,
11
15
  left = Infinity,
12
16
  bottom = -Infinity,
@@ -20,6 +24,8 @@ export const getBoundingBoxFromSelection = (view, from, to) => {
20
24
  bottom = Math.max(bottom, coords.bottom);
21
25
  right = Math.max(right, coords.right);
22
26
  }
27
+ top = top - offset.top;
28
+ bottom = bottom - offset.bottom;
23
29
  return {
24
30
  top,
25
31
  left,
@@ -1,14 +1,38 @@
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
- });
1
+ import React from 'react';
2
+ import { fg } from '@atlaskit/platform-feature-flags';
3
+ import { getToolbarItemExtensions } from './extensions';
4
+ import { LegacyExtensionToolbarItem } from './LegacyToolbarComponent';
5
+ export const selectionToolbar = ({
6
+ overflowOptions,
7
+ extensionList = []
8
+ }) => {
9
+ const inlineToolbarItemExtensions = fg('platform_editor_selection_extension_api_v2') ? getToolbarItemExtensions(extensionList, 'inlineToolbar') : [];
10
+ return {
11
+ items: [...(inlineToolbarItemExtensions.length ? [{
12
+ type: 'separator',
13
+ fullHeight: true,
14
+ supportsViewMode: true
15
+ }, ...inlineToolbarItemExtensions.map(({
16
+ getToolbarItem,
17
+ getMenuItems
18
+ }) => ({
19
+ type: 'custom',
20
+ render: () => /*#__PURE__*/React.createElement(LegacyExtensionToolbarItem, {
21
+ toolbarItem: getToolbarItem(),
22
+ getMenuItems: getMenuItems
23
+ }),
24
+ fallback: [],
25
+ supportsViewMode: true
26
+ }))] : []), {
27
+ type: 'separator',
28
+ fullHeight: true,
29
+ supportsViewMode: true
30
+ }, {
31
+ type: 'overflow-dropdown',
32
+ dropdownWidth: 240,
33
+ supportsViewMode: true,
34
+ options: overflowOptions
35
+ }],
36
+ rank: -6
37
+ };
38
+ };
@@ -2,6 +2,7 @@ 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 { akEditorFullPageToolbarHeight } from '@atlaskit/editor-shared-styles';
5
6
  import { fg } from '@atlaskit/platform-feature-flags';
6
7
  import { insertSmartLinks as _insertSmartLinks } from './pm-plugins/actions';
7
8
  import { insertAdfAtEndOfDoc as _insertAdfAtEndOfDoc } from './pm-plugins/actions/insertAdfAtEndOfDoc';
@@ -10,7 +11,9 @@ import { createPlugin, selectionExtensionPluginKey } from './pm-plugins/main';
10
11
  import { getSelectionInfo } from './pm-plugins/utils';
11
12
  import { SelectionExtensionActionTypes } from './types';
12
13
  import { SelectionExtensionComponentWrapper } from './ui/extension/SelectionExtensionComponentWrapper';
14
+ import { getMenuItemExtensions, getToolbarItemExtensions } from './ui/extensions';
13
15
  import { getBoundingBoxFromSelection } from './ui/getBoundingBoxFromSelection';
16
+ import { LegacyPrimaryToolbarComponent } from './ui/LegacyToolbarComponent';
14
17
  import { selectionToolbar as _selectionToolbar } from './ui/selectionToolbar';
15
18
  export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
16
19
  var api = _ref.api,
@@ -20,6 +23,30 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
20
23
  };
21
24
  var cachedSelection;
22
25
  var cachedOverflowMenuOptions;
26
+ var _ref2 = config || {},
27
+ _ref2$extensionList = _ref2.extensionList,
28
+ extensionList = _ref2$extensionList === void 0 ? [] : _ref2$extensionList,
29
+ _ref2$extensions = _ref2.extensions,
30
+ extensions = _ref2$extensions === void 0 ? {} : _ref2$extensions;
31
+ var _ref3 = extensions || {},
32
+ _ref3$firstParty = _ref3.firstParty,
33
+ firstParty = _ref3$firstParty === void 0 ? [] : _ref3$firstParty,
34
+ _ref3$external = _ref3.external,
35
+ external = _ref3$external === void 0 ? [] : _ref3$external;
36
+ if (fg('platform_editor_selection_extension_api_v2')) {
37
+ var primaryToolbarItemExtensions = getToolbarItemExtensions(extensionList, 'primaryToolbar');
38
+ if (primaryToolbarItemExtensions !== null && primaryToolbarItemExtensions !== void 0 && primaryToolbarItemExtensions.length) {
39
+ var _api$primaryToolbar;
40
+ api === null || api === void 0 || (_api$primaryToolbar = api.primaryToolbar) === null || _api$primaryToolbar === void 0 || (_api$primaryToolbar = _api$primaryToolbar.actions) === null || _api$primaryToolbar === void 0 || _api$primaryToolbar.registerComponent({
41
+ name: 'selectionExtension',
42
+ component: function component() {
43
+ return /*#__PURE__*/React.createElement(LegacyPrimaryToolbarComponent, {
44
+ primaryToolbarItemExtensions: primaryToolbarItemExtensions
45
+ });
46
+ }
47
+ });
48
+ }
49
+ }
23
50
  return {
24
51
  name: 'selectionExtension',
25
52
  getSharedState: function getSharedState(editorState) {
@@ -30,8 +57,8 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
30
57
  },
31
58
  commands: {
32
59
  setActiveExtension: function setActiveExtension(extension) {
33
- return function (_ref2) {
34
- var tr = _ref2.tr;
60
+ return function (_ref4) {
61
+ var tr = _ref4.tr;
35
62
  return tr.setMeta(selectionExtensionPluginKey, {
36
63
  type: 'set-active-extension',
37
64
  extension: extension
@@ -39,8 +66,8 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
39
66
  };
40
67
  },
41
68
  clearActiveExtension: function clearActiveExtension() {
42
- return function (_ref3) {
43
- var tr = _ref3.tr;
69
+ return function (_ref5) {
70
+ var tr = _ref5.tr;
44
71
  return tr.setMeta(selectionExtensionPluginKey, {
45
72
  type: 'clear-active-extension'
46
73
  });
@@ -82,10 +109,14 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
82
109
  dispatch = _editorViewRef$curren3.dispatch;
83
110
  return _insertAdfAtEndOfDoc(nodeAdf)(state, dispatch);
84
111
  }
112
+ // NEXT PR: Implement this to return selectedNodeAdf, selectionRanges
113
+ // getSelectionAdf: () => {},
114
+ // NEXT PR: Implement this to return text, coords
115
+ // getSelectionText: () => {},
85
116
  },
86
- contentComponent: function contentComponent(_ref4) {
117
+ contentComponent: function contentComponent(_ref6) {
87
118
  var _api$analytics;
88
- var editorView = _ref4.editorView;
119
+ var editorView = _ref6.editorView;
89
120
  return /*#__PURE__*/React.createElement(SelectionExtensionComponentWrapper, {
90
121
  editorView: editorView,
91
122
  api: api,
@@ -98,25 +129,17 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
98
129
  if (!config) {
99
130
  return;
100
131
  }
101
- var pageModes = config.pageModes,
102
- extensions = config.extensions;
132
+ var pageModes = config.pageModes;
103
133
 
104
- /**
105
- * Extensions Config Validation
106
- *
107
- * Check whether plugin contains any selection extensions
108
- */
109
- if ((!(extensions !== null && extensions !== void 0 && extensions.firstParty) || extensions.firstParty.length === 0) && (!(extensions !== null && extensions !== void 0 && extensions.external) || extensions.external.length === 0)) {
134
+ // Extensions Config Validation
135
+ // Check whether plugin contains any selection extensions
136
+ if (!(firstParty !== null && firstParty !== void 0 && firstParty.length) && !(external !== null && external !== void 0 && external.length) && !(extensionList !== null && extensionList !== void 0 && extensionList.length)) {
110
137
  return;
111
138
  }
112
139
 
113
- /**
114
- * Content Mode Validation
115
- *
116
- * Check if pageModes is provided and matches against current content mode
117
- *
118
- * TODO: This will eventially transition from mode to contentMode
119
- */
140
+ // Content Mode Validation
141
+ // Check if pageModes is provided and matches against current content mode
142
+ // This will eventially transition from mode to contentMode
120
143
  var editorContentMode = api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 || (_api$editorViewMode = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.mode;
121
144
  if (pageModes) {
122
145
  // Early Exit: consumer has set pageModes but editorContentMode is undefined
@@ -133,22 +156,26 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
133
156
  }
134
157
  }
135
158
 
136
- /**
137
- * Active Extension
138
- *
139
- * Check if there is an active extension and hide the selection extension dropdown
140
- */
159
+ // Active Extension
160
+ // Check if there is an active extension and hide the selection extension dropdown
141
161
  var selectionExtensionState = selectionExtensionPluginKey.getState(state);
142
162
  if (selectionExtensionState !== null && selectionExtensionState !== void 0 && selectionExtensionState.activeExtension) {
143
163
  return;
144
164
  }
145
165
  var getSelection = function getSelection(view) {
166
+ var _api$userPreferences, _api$selectionToolbar, _api$editorViewMode2;
146
167
  // ensure the same document state is applied to editor view to avoid mismatches
147
168
  var currentSelection = view.state.selection;
169
+ var toolbarDocking = fg('platform_editor_use_preferences_plugin') ? api === null || api === void 0 || (_api$userPreferences = api.userPreferences) === null || _api$userPreferences === void 0 || (_api$userPreferences = _api$userPreferences.sharedState.currentState()) === null || _api$userPreferences === void 0 || (_api$userPreferences = _api$userPreferences.preferences) === null || _api$userPreferences === void 0 ? void 0 : _api$userPreferences.toolbarDockingPosition : api === null || api === void 0 || (_api$selectionToolbar = api.selectionToolbar) === null || _api$selectionToolbar === void 0 || (_api$selectionToolbar = _api$selectionToolbar.sharedState) === null || _api$selectionToolbar === void 0 || (_api$selectionToolbar = _api$selectionToolbar.currentState()) === null || _api$selectionToolbar === void 0 ? void 0 : _api$selectionToolbar.toolbarDocking;
170
+ var isEditMode = Boolean((api === null || api === void 0 || (_api$editorViewMode2 = api.editorViewMode) === null || _api$editorViewMode2 === void 0 || (_api$editorViewMode2 = _api$editorViewMode2.sharedState.currentState()) === null || _api$editorViewMode2 === void 0 ? void 0 : _api$editorViewMode2.mode) === 'edit');
171
+ var shouldOffsetToolbarHeight = toolbarDocking === 'top' && isEditMode && fg('platform_editor_selection_extension_api_v2');
148
172
  var from = currentSelection.from,
149
173
  to = currentSelection.to;
150
174
  var text = view.state.doc.textBetween(from, to, '\n');
151
- var coords = getBoundingBoxFromSelection(view, from, to);
175
+ var coords = getBoundingBoxFromSelection(view, from, to, {
176
+ top: shouldOffsetToolbarHeight ? akEditorFullPageToolbarHeight : 0,
177
+ bottom: shouldOffsetToolbarHeight ? akEditorFullPageToolbarHeight : 0
178
+ });
152
179
  return {
153
180
  text: text,
154
181
  from: from,
@@ -180,8 +207,8 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
180
207
  selectionRanges: selectionRanges
181
208
  };
182
209
  (_extension$onClick = extension.onClick) === null || _extension$onClick === void 0 || _extension$onClick.call(extension, onClickCallbackOptions);
183
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref5) {
184
- var tr = _ref5.tr;
210
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref7) {
211
+ var tr = _ref7.tr;
185
212
  tr.setMeta(selectionExtensionPluginKey, {
186
213
  type: SelectionExtensionActionTypes.SET_SELECTED_NODE,
187
214
  selectedNode: selectedNode,
@@ -244,9 +271,7 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
244
271
  });
245
272
  };
246
273
 
247
- /**
248
- * Add a heading to the external extensions
249
- */
274
+ // Add a heading to the external extensions
250
275
  var getExternalExtensions = function getExternalExtensions(extensions) {
251
276
  var prefilteredExtensions = prefilterExtensions(extensions);
252
277
  var externalExtensions = [];
@@ -263,13 +288,28 @@ export var selectionExtensionPlugin = function selectionExtensionPlugin(_ref) {
263
288
  }
264
289
  return externalExtensions;
265
290
  };
291
+
292
+ // NEXT PR: Make sure we cache the whole generated selection toolbar
293
+ // also debug this to make sure it's actually preventing unnecessary re-renders / work
266
294
  if (cachedOverflowMenuOptions && state.selection.eq(cachedSelection) && fg('platform_editor_selection_extension_api_v2')) {
267
- return _selectionToolbar(cachedOverflowMenuOptions);
295
+ return _selectionToolbar({
296
+ overflowOptions: cachedOverflowMenuOptions,
297
+ extensionList: extensionList
298
+ });
299
+ }
300
+ var allFirstParty = _toConsumableArray(firstParty);
301
+ var allExternal = _toConsumableArray(external);
302
+ if (fg('platform_editor_selection_extension_api_v2')) {
303
+ allFirstParty = [].concat(_toConsumableArray(firstParty), _toConsumableArray(getMenuItemExtensions(extensionList, 'first-party')));
304
+ allExternal = [].concat(_toConsumableArray(external), _toConsumableArray(getMenuItemExtensions(extensionList, 'external')));
268
305
  }
269
- var groupedExtensionsArray = [].concat(_toConsumableArray(getFirstPartyExtensions(extensions.firstParty || [])), _toConsumableArray(getExternalExtensions(extensions.external || [])));
306
+ var groupedExtensionsArray = [].concat(_toConsumableArray(getFirstPartyExtensions(allFirstParty)), _toConsumableArray(getExternalExtensions(allExternal)));
270
307
  cachedOverflowMenuOptions = groupedExtensionsArray;
271
308
  cachedSelection = state.selection;
272
- return _selectionToolbar(groupedExtensionsArray);
309
+ return _selectionToolbar({
310
+ overflowOptions: cachedOverflowMenuOptions,
311
+ extensionList: extensionList
312
+ });
273
313
  }
274
314
  },
275
315
  pmPlugins: function pmPlugins() {
@@ -0,0 +1,111 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import React, { useState } from 'react';
3
+ import { ArrowKeyNavigationType, DropdownMenuWithKeyboardNavigation as DropdownMenu, ToolbarButton } from '@atlaskit/editor-common/ui-menu';
4
+ export var LegacyPrimaryToolbarComponent = function LegacyPrimaryToolbarComponent(_ref) {
5
+ var primaryToolbarItemExtensions = _ref.primaryToolbarItemExtensions;
6
+ // NEXT PR: need to render a separator after – if there are extensions added
7
+ return /*#__PURE__*/React.createElement(React.Fragment, null, primaryToolbarItemExtensions.map(function (toolbarItemExtension, i) {
8
+ var toolbarItem = toolbarItemExtension.getToolbarItem();
9
+ return /*#__PURE__*/React.createElement(LegacyExtensionToolbarItem, {
10
+ key: toolbarItem.tooltip,
11
+ toolbarItem: toolbarItem
12
+ });
13
+ }));
14
+ };
15
+ export var LegacyExtensionToolbarItem = function LegacyExtensionToolbarItem(_ref2) {
16
+ var toolbarItem = _ref2.toolbarItem,
17
+ getMenuItems = _ref2.getMenuItems;
18
+ var _useState = useState(false),
19
+ _useState2 = _slicedToArray(_useState, 2),
20
+ isOpen = _useState2[0],
21
+ setIsOpen = _useState2[1];
22
+ var Icon = toolbarItem.icon,
23
+ tooltip = toolbarItem.tooltip,
24
+ isDisabled = toolbarItem.isDisabled,
25
+ onClick = toolbarItem.onClick,
26
+ _label = toolbarItem.label;
27
+ if (!getMenuItems) {
28
+ return /*#__PURE__*/React.createElement(ToolbarButton, {
29
+ spacing: "default",
30
+ disabled: isDisabled,
31
+ selected: isOpen,
32
+ title: tooltip,
33
+ "aria-label": tooltip,
34
+ "aria-expanded": isOpen,
35
+ "aria-haspopup": true,
36
+ onClick: onClick,
37
+ iconBefore: /*#__PURE__*/React.createElement(Icon, {
38
+ label: tooltip
39
+ })
40
+ });
41
+ }
42
+ var toggleOpen = function toggleOpen() {
43
+ setIsOpen(function (prev) {
44
+ return !prev;
45
+ });
46
+ };
47
+ var toggleOpenByKeyboard = function toggleOpenByKeyboard(event) {
48
+ if (event.key === 'Enter' || event.key === ' ') {
49
+ event.preventDefault();
50
+ toggleOpen();
51
+ }
52
+ };
53
+ var handleItemActivated = function handleItemActivated(_ref3) {
54
+ var _item$onClick;
55
+ var item = _ref3.item,
56
+ _ref3$shouldCloseMenu = _ref3.shouldCloseMenu,
57
+ shouldCloseMenu = _ref3$shouldCloseMenu === void 0 ? true : _ref3$shouldCloseMenu;
58
+ (_item$onClick = item.onClick) === null || _item$onClick === void 0 || _item$onClick.call(item);
59
+ if (shouldCloseMenu) {
60
+ setIsOpen(false);
61
+ }
62
+ };
63
+ var handleOnOpenChange = function handleOnOpenChange(attrs) {
64
+ setIsOpen(!!(attrs !== null && attrs !== void 0 && attrs.isOpen));
65
+ };
66
+ var items = isOpen ? getMenuItems().map(function (menuItem, i) {
67
+ return {
68
+ key: "menu-item-".concat(i),
69
+ content: menuItem.label,
70
+ elemBefore: /*#__PURE__*/React.createElement(menuItem.icon, {
71
+ label: menuItem.label
72
+ }),
73
+ onClick: function onClick() {
74
+ var _menuItem$onClick;
75
+ (_menuItem$onClick = menuItem.onClick) === null || _menuItem$onClick === void 0 || _menuItem$onClick.call(menuItem);
76
+ // NEXT PR: here we need to set the active extension so the contentComponent can render
77
+ // menuItem.contentComponent
78
+ },
79
+ isDisabled: menuItem.isDisabled,
80
+ 'aria-label': menuItem.label,
81
+ value: {
82
+ name: menuItem.label
83
+ }
84
+ };
85
+ }) : [];
86
+ return /*#__PURE__*/React.createElement(DropdownMenu, {
87
+ arrowKeyNavigationProviderOptions: {
88
+ type: ArrowKeyNavigationType.MENU
89
+ },
90
+ items: [{
91
+ items: items
92
+ }],
93
+ isOpen: isOpen,
94
+ onItemActivated: handleItemActivated,
95
+ onOpenChange: handleOnOpenChange,
96
+ fitWidth: 200
97
+ }, /*#__PURE__*/React.createElement(ToolbarButton, {
98
+ spacing: "default",
99
+ disabled: isDisabled,
100
+ selected: isOpen,
101
+ title: tooltip,
102
+ "aria-label": tooltip,
103
+ "aria-expanded": isOpen,
104
+ "aria-haspopup": true,
105
+ onClick: toggleOpen,
106
+ onKeyDown: toggleOpenByKeyboard,
107
+ iconBefore: /*#__PURE__*/React.createElement(Icon, {
108
+ label: tooltip
109
+ })
110
+ }));
111
+ };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * From the full list of extensions, extract only those that have a toolbar item configuration
3
+ * for the specified type (either 'primaryToolbar' or 'inlineToolbar').
4
+ *
5
+ * @param extensionList - List of all extensions
6
+ * @param toolbarType - Type of toolbar ('primaryToolbar' or 'inlineToolbar')
7
+ * @returns Array of ToolbarItemExtension objects
8
+ * @example
9
+ */
10
+ export var getToolbarItemExtensions = function getToolbarItemExtensions(extensionList, toolbarType) {
11
+ return extensionList.reduce(function (acc, extension) {
12
+ var toolbarConfig = extension[toolbarType];
13
+ if (toolbarConfig !== null && toolbarConfig !== void 0 && toolbarConfig.getToolbarItem) {
14
+ acc.push({
15
+ getToolbarItem: toolbarConfig.getToolbarItem,
16
+ getMenuItems: toolbarConfig.getMenuItems
17
+ });
18
+ }
19
+ return acc;
20
+ }, []);
21
+ };
22
+
23
+ /**
24
+ * From the full list of extensions, extract only those that have a menu item configuration
25
+ * for the specified source (either 'first-party' or 'external').
26
+ *
27
+ * Map each to the legacy format for SelectionExtensionConfig.
28
+ *
29
+ * @param extensionList - List of all extensions
30
+ * @param targetSource - Source of the extensions ('first-party' or 'external')
31
+ * @returns Array of SelectionExtensionConfig objects
32
+ * @example
33
+ */
34
+ export var getMenuItemExtensions = function getMenuItemExtensions(extensionList, targetSource) {
35
+ return extensionList.reduce(function (acc, extension) {
36
+ var source = extension.source,
37
+ inlineToolbar = extension.inlineToolbar;
38
+ if (source === targetSource && inlineToolbar !== null && inlineToolbar !== void 0 && inlineToolbar.getMenuItems && !inlineToolbar.getToolbarItem) {
39
+ var menuItems = inlineToolbar.getMenuItems();
40
+ menuItems.forEach(function (menuItem) {
41
+ acc.push({
42
+ name: menuItem.label,
43
+ icon: menuItem.icon,
44
+ onClick: menuItem.onClick,
45
+ isDisabled: function isDisabled() {
46
+ return !!menuItem.isDisabled;
47
+ },
48
+ component: menuItem.contentComponent
49
+ });
50
+ });
51
+ }
52
+ return acc;
53
+ }, []);
54
+ };
@@ -4,9 +4,14 @@
4
4
  * @param view - The editor view instance.
5
5
  * @param from - The starting position of the selection.
6
6
  * @param to - The ending position of the selection.
7
+ * @param offset - Optional offset to adjust the top and bottom coordinates of the bounding box.`
7
8
  * @returns An object containing the top, left, bottom, and right coordinates of the bounding box.
8
9
  */
9
10
  export var getBoundingBoxFromSelection = function getBoundingBoxFromSelection(view, from, to) {
11
+ var offset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {
12
+ top: 0,
13
+ bottom: 0
14
+ };
10
15
  var top = Infinity,
11
16
  left = Infinity,
12
17
  bottom = -Infinity,
@@ -20,6 +25,8 @@ export var getBoundingBoxFromSelection = function getBoundingBoxFromSelection(vi
20
25
  bottom = Math.max(bottom, coords.bottom);
21
26
  right = Math.max(right, coords.right);
22
27
  }
28
+ top = top - offset.top;
29
+ bottom = bottom - offset.bottom;
23
30
  return {
24
31
  top: top,
25
32
  left: left,