@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.
- package/CHANGELOG.md +17 -0
- package/dist/cjs/selectionExtensionPlugin.js +75 -35
- package/dist/cjs/ui/LegacyToolbarComponent.js +120 -0
- package/dist/cjs/ui/extensions.js +60 -0
- package/dist/cjs/ui/getBoundingBoxFromSelection.js +7 -0
- package/dist/cjs/ui/selectionToolbar.js +32 -5
- package/dist/es2019/selectionExtensionPlugin.js +63 -27
- package/dist/es2019/ui/LegacyToolbarComponent.js +109 -0
- package/dist/es2019/ui/extensions.js +54 -0
- package/dist/es2019/ui/getBoundingBoxFromSelection.js +7 -1
- package/dist/es2019/ui/selectionToolbar.js +38 -14
- package/dist/esm/selectionExtensionPlugin.js +75 -35
- package/dist/esm/ui/LegacyToolbarComponent.js +111 -0
- package/dist/esm/ui/extensions.js +54 -0
- package/dist/esm/ui/getBoundingBoxFromSelection.js +7 -0
- package/dist/esm/ui/selectionToolbar.js +31 -5
- package/dist/types/index.d.ts +1 -1
- package/dist/types/selectionExtensionPluginType.d.ts +4 -0
- package/dist/types/types/index.d.ts +41 -1
- package/dist/types/ui/LegacyToolbarComponent.d.ts +16 -0
- package/dist/types/ui/extensions.d.ts +27 -0
- package/dist/types/ui/getBoundingBoxFromSelection.d.ts +3 -2
- package/dist/types/ui/selectionToolbar.d.ts +7 -17
- package/dist/types-ts4.5/index.d.ts +1 -1
- package/dist/types-ts4.5/selectionExtensionPluginType.d.ts +4 -0
- package/dist/types-ts4.5/types/index.d.ts +41 -1
- package/dist/types-ts4.5/ui/LegacyToolbarComponent.d.ts +16 -0
- package/dist/types-ts4.5/ui/extensions.d.ts +27 -0
- package/dist/types-ts4.5/ui/getBoundingBoxFromSelection.d.ts +3 -2
- package/dist/types-ts4.5/ui/selectionToolbar.d.ts +7 -17
- 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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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 (
|
|
34
|
-
var 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 (
|
|
43
|
-
var 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(
|
|
117
|
+
contentComponent: function contentComponent(_ref6) {
|
|
87
118
|
var _api$analytics;
|
|
88
|
-
var 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
184
|
-
var 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(
|
|
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(
|
|
306
|
+
var groupedExtensionsArray = [].concat(_toConsumableArray(getFirstPartyExtensions(allFirstParty)), _toConsumableArray(getExternalExtensions(allExternal)));
|
|
270
307
|
cachedOverflowMenuOptions = groupedExtensionsArray;
|
|
271
308
|
cachedSelection = state.selection;
|
|
272
|
-
return _selectionToolbar(
|
|
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,
|