@atlaskit/editor-plugin-paste-options-toolbar 9.0.4 → 9.0.6
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 +14 -0
- package/dist/cjs/pm-plugins/constants.js +3 -4
- package/dist/cjs/ui/on-paste-actions-menu/PasteActionsMenu.js +45 -15
- package/dist/es2019/pm-plugins/constants.js +2 -3
- package/dist/es2019/ui/on-paste-actions-menu/PasteActionsMenu.js +47 -17
- package/dist/esm/pm-plugins/constants.js +2 -3
- package/dist/esm/ui/on-paste-actions-menu/PasteActionsMenu.js +47 -17
- package/dist/types/pm-plugins/constants.d.ts +2 -1
- package/dist/types/ui/on-paste-actions-menu/PasteActionsMenu.d.ts +13 -7
- package/dist/types-ts4.5/pm-plugins/constants.d.ts +2 -1
- package/dist/types-ts4.5/ui/on-paste-actions-menu/PasteActionsMenu.d.ts +13 -7
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-paste-options-toolbar
|
|
2
2
|
|
|
3
|
+
## 9.0.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`2b33e02e33a67`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/2b33e02e33a67) -
|
|
8
|
+
[ux] [EDITOR-5880] paste menu position fix so it sticks on screen when pasting large content
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
|
|
11
|
+
## 9.0.5
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
|
|
3
17
|
## 9.0.4
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.TEXT_HIGHLIGHT_CLASS = exports.PASTE_TOOLBAR_MENU_ID = exports.PASTE_TOOLBAR_ITEM_CLASS = exports.PASTE_TOOLBAR_CLASS = exports.PASTE_OPTIONS_TEST_ID = exports.PASTE_OPTIONS_META_ID = exports.
|
|
6
|
+
exports.TEXT_HIGHLIGHT_CLASS = exports.PASTE_TOOLBAR_MENU_ID = exports.PASTE_TOOLBAR_ITEM_CLASS = exports.PASTE_TOOLBAR_CLASS = exports.PASTE_OPTIONS_TEST_ID = exports.PASTE_OPTIONS_META_ID = exports.PASTE_MENU_GAP_TOP = exports.PASTE_MENU_GAP_HORIZONTAL = exports.PASTE_HIGHLIGHT_DECORATION_KEY = exports.EDITOR_WRAPPER_CLASS = void 0;
|
|
7
7
|
var PASTE_TOOLBAR_CLASS = exports.PASTE_TOOLBAR_CLASS = 'ak-editor-paste-toolbar';
|
|
8
8
|
var PASTE_TOOLBAR_MENU_ID = exports.PASTE_TOOLBAR_MENU_ID = 'ak-editor-paste-toolbar-item-dropdownList';
|
|
9
9
|
var TEXT_HIGHLIGHT_CLASS = exports.TEXT_HIGHLIGHT_CLASS = 'text-highlight';
|
|
@@ -12,6 +12,5 @@ var PASTE_TOOLBAR_ITEM_CLASS = exports.PASTE_TOOLBAR_ITEM_CLASS = 'ak-editor-pas
|
|
|
12
12
|
var EDITOR_WRAPPER_CLASS = exports.EDITOR_WRAPPER_CLASS = 'akEditor';
|
|
13
13
|
var PASTE_OPTIONS_TEST_ID = exports.PASTE_OPTIONS_TEST_ID = 'paste-options-testid';
|
|
14
14
|
var PASTE_OPTIONS_META_ID = exports.PASTE_OPTIONS_META_ID = 'paste-options$';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
var PASTE_MENU_GAP = exports.PASTE_MENU_GAP = 12;
|
|
15
|
+
var PASTE_MENU_GAP_HORIZONTAL = exports.PASTE_MENU_GAP_HORIZONTAL = 8;
|
|
16
|
+
var PASTE_MENU_GAP_TOP = exports.PASTE_MENU_GAP_TOP = 24;
|
|
@@ -41,26 +41,44 @@ function getTargetElement(editorView, pos) {
|
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Adjusts the vertical position of the paste menu to align with the top of the
|
|
44
|
-
* pasted content using the exact coordinates at the paste start position
|
|
44
|
+
* pasted content using the exact coordinates at the paste start position,
|
|
45
|
+
* and sticks the menu to the top of the scroll container when the pasted
|
|
46
|
+
* content scrolls above the visible area.
|
|
45
47
|
*
|
|
46
|
-
* The Popup
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
48
|
+
* The Popup uses alignY="bottom", which positions the popup below the target
|
|
49
|
+
* element's bottom edge. This override:
|
|
50
|
+
*
|
|
51
|
+
* 1. Shifts the popup from the target's bottom edge to align with the paste
|
|
52
|
+
* start position.
|
|
53
|
+
* 2. When the paste start scrolls above the scroll container, clamps the menu
|
|
54
|
+
* to the scroll container's top edge (sticky-top).
|
|
55
|
+
* 3. Stops sticking once the entire pasted range (pasteEndPos) has scrolled
|
|
56
|
+
* above the visible area.
|
|
51
57
|
*/
|
|
52
|
-
function onPositionCalculated(editorView, pasteStartPos, targetElement) {
|
|
58
|
+
function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, targetElement, scrollableElement) {
|
|
53
59
|
return function (position) {
|
|
54
60
|
var _position$top;
|
|
55
61
|
var startCoords = editorView.coordsAtPos(pasteStartPos);
|
|
62
|
+
var endCoords = editorView.coordsAtPos(pasteEndPos);
|
|
56
63
|
var targetRect = targetElement.getBoundingClientRect();
|
|
57
64
|
|
|
58
65
|
// The Popup places the menu at the target's bottom edge by default.
|
|
59
|
-
// We
|
|
66
|
+
// We shift it up so it aligns with the paste start position.
|
|
60
67
|
// Both coordinates are in viewport space, so the delta is offset-parent agnostic.
|
|
61
68
|
var topDelta = startCoords.top - (targetRect.top + targetRect.height);
|
|
69
|
+
var adjustedTop = ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta;
|
|
70
|
+
|
|
71
|
+
// Sticky-top: clamp to the scroll container's top edge when the paste
|
|
72
|
+
// start has scrolled above the visible area, but only while some pasted
|
|
73
|
+
// content is still visible.
|
|
74
|
+
if (scrollableElement) {
|
|
75
|
+
var scrollContainerTop = scrollableElement.getBoundingClientRect().top;
|
|
76
|
+
if (startCoords.top < scrollContainerTop && endCoords.bottom > scrollContainerTop) {
|
|
77
|
+
adjustedTop += scrollContainerTop - startCoords.top + _constants.PASTE_MENU_GAP_TOP;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
62
80
|
return _objectSpread(_objectSpread({}, position), {}, {
|
|
63
|
-
top:
|
|
81
|
+
top: adjustedTop
|
|
64
82
|
});
|
|
65
83
|
};
|
|
66
84
|
}
|
|
@@ -102,15 +120,17 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
|
|
|
102
120
|
(0, _commands.showToolbar)(lastContentPasted, selectedOption, legacyVisible, pasteAncestorNodeNames)(editorView.state, editorView.dispatch);
|
|
103
121
|
}, [lastContentPasted, editorView]);
|
|
104
122
|
var _useSharedPluginState2 = (0, _hooks.useSharedPluginStateWithSelector)(api, ['pasteOptionsToolbarPlugin'], function (states) {
|
|
105
|
-
var _pluginState$showTool, _pluginState$pasteSta;
|
|
123
|
+
var _pluginState$showTool, _pluginState$pasteSta, _pluginState$pasteEnd;
|
|
106
124
|
var pluginState = states.pasteOptionsToolbarPluginState;
|
|
107
125
|
return {
|
|
108
126
|
showToolbar: (_pluginState$showTool = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showToolbar) !== null && _pluginState$showTool !== void 0 ? _pluginState$showTool : false,
|
|
109
|
-
pasteStartPos: (_pluginState$pasteSta = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteStartPos) !== null && _pluginState$pasteSta !== void 0 ? _pluginState$pasteSta : 0
|
|
127
|
+
pasteStartPos: (_pluginState$pasteSta = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteStartPos) !== null && _pluginState$pasteSta !== void 0 ? _pluginState$pasteSta : 0,
|
|
128
|
+
pasteEndPos: (_pluginState$pasteEnd = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteEndPos) !== null && _pluginState$pasteEnd !== void 0 ? _pluginState$pasteEnd : 0
|
|
110
129
|
};
|
|
111
130
|
}),
|
|
112
131
|
isToolbarShown = _useSharedPluginState2.showToolbar,
|
|
113
|
-
pasteStartPos = _useSharedPluginState2.pasteStartPos
|
|
132
|
+
pasteStartPos = _useSharedPluginState2.pasteStartPos,
|
|
133
|
+
pasteEndPos = _useSharedPluginState2.pasteEndPos;
|
|
114
134
|
var aiSurfaceComponents = (_api$uiControlRegistr = api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents('ai-paste-menu')) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
|
|
115
135
|
var visibleAiActionKeys = (0, _hasVisibleButton.getVisibleKeys)(aiSurfaceComponents, ['button', 'menu-item']);
|
|
116
136
|
(0, _react.useEffect)(function () {
|
|
@@ -150,6 +170,15 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
|
|
|
150
170
|
handleDismiss();
|
|
151
171
|
}
|
|
152
172
|
}, [handleDismiss]);
|
|
173
|
+
|
|
174
|
+
// Find the actual scroll container using the same utility the Popup's
|
|
175
|
+
// stick prop uses internally. We pass this as the scrollableElement prop
|
|
176
|
+
// so the Popup attaches its built-in scroll listener, which calls
|
|
177
|
+
// scheduledUpdatePosition (RAF-throttled) on each scroll event — triggering
|
|
178
|
+
// onPositionCalculated with fresh viewport coordinates.
|
|
179
|
+
var targetForScroll = isToolbarShown ? getTargetElement(editorView, pasteStartPos) : null;
|
|
180
|
+
var overflowScrollParent = targetForScroll ? (0, _ui.findOverflowScrollParent)(targetForScroll) : false;
|
|
181
|
+
var effectiveScrollableElement = overflowScrollParent || scrollableElement;
|
|
153
182
|
var pasteMenuComponents = (_api$uiControlRegistr3 = api === null || api === void 0 || (_api$uiControlRegistr4 = api.uiControlRegistry) === null || _api$uiControlRegistr4 === void 0 ? void 0 : _api$uiControlRegistr4.actions.getComponents(_toolbar.PASTE_MENU.key)) !== null && _api$uiControlRegistr3 !== void 0 ? _api$uiControlRegistr3 : [];
|
|
154
183
|
var anyComponentVisible = (0, _hasVisibleButton.hasVisibleButton)(pasteMenuComponents);
|
|
155
184
|
if (!isToolbarShown) {
|
|
@@ -166,12 +195,13 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
|
|
|
166
195
|
target: target,
|
|
167
196
|
mountTo: mountTo,
|
|
168
197
|
boundariesElement: boundariesElement,
|
|
169
|
-
scrollableElement:
|
|
198
|
+
scrollableElement: effectiveScrollableElement,
|
|
199
|
+
minPopupMargin: _constants.PASTE_MENU_GAP_HORIZONTAL,
|
|
170
200
|
zIndex: _editorSharedStyles.akEditorFloatingPanelZIndex,
|
|
171
201
|
alignX: "end",
|
|
172
202
|
alignY: "bottom",
|
|
173
|
-
offset: [_constants.
|
|
174
|
-
onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, target),
|
|
203
|
+
offset: [_constants.PASTE_MENU_GAP_HORIZONTAL, 0],
|
|
204
|
+
onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement),
|
|
175
205
|
handleClickOutside: handleClickOutside,
|
|
176
206
|
handleEscapeKeydown: handleDismiss
|
|
177
207
|
}, /*#__PURE__*/_react.default.createElement(_toolbar.EditorToolbarProvider, {
|
|
@@ -6,6 +6,5 @@ export const PASTE_TOOLBAR_ITEM_CLASS = 'ak-editor-paste-toolbar-item';
|
|
|
6
6
|
export const EDITOR_WRAPPER_CLASS = 'akEditor';
|
|
7
7
|
export const PASTE_OPTIONS_TEST_ID = 'paste-options-testid';
|
|
8
8
|
export const PASTE_OPTIONS_META_ID = 'paste-options$';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export const PASTE_MENU_GAP = 12;
|
|
9
|
+
export const PASTE_MENU_GAP_HORIZONTAL = 8;
|
|
10
|
+
export const PASTE_MENU_GAP_TOP = 24;
|
|
@@ -2,13 +2,13 @@ import React, { useCallback, useEffect, useRef } from 'react';
|
|
|
2
2
|
import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
3
3
|
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
4
4
|
import { EditorToolbarProvider, PASTE_MENU } from '@atlaskit/editor-common/toolbar';
|
|
5
|
-
import { Popup } from '@atlaskit/editor-common/ui';
|
|
5
|
+
import { findOverflowScrollParent, Popup } from '@atlaskit/editor-common/ui';
|
|
6
6
|
import { withReactEditorViewOuterListeners } from '@atlaskit/editor-common/ui-react';
|
|
7
7
|
import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
|
|
8
8
|
import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
|
|
9
9
|
import { ToolbarDropdownMenuProvider } from '@atlaskit/editor-toolbar';
|
|
10
10
|
import { hideToolbar, highlightContent, showToolbar } from '../../editor-commands/commands';
|
|
11
|
-
import {
|
|
11
|
+
import { PASTE_MENU_GAP_HORIZONTAL, PASTE_MENU_GAP_TOP } from '../../pm-plugins/constants';
|
|
12
12
|
import { ToolbarDropdownOption } from '../../types/types';
|
|
13
13
|
import { isToolbarVisible } from '../toolbar';
|
|
14
14
|
import { getVisibleKeys, hasVisibleButton } from './hasVisibleButton';
|
|
@@ -28,27 +28,45 @@ function getTargetElement(editorView, pos) {
|
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Adjusts the vertical position of the paste menu to align with the top of the
|
|
31
|
-
* pasted content using the exact coordinates at the paste start position
|
|
31
|
+
* pasted content using the exact coordinates at the paste start position,
|
|
32
|
+
* and sticks the menu to the top of the scroll container when the pasted
|
|
33
|
+
* content scrolls above the visible area.
|
|
32
34
|
*
|
|
33
|
-
* The Popup
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* The Popup uses alignY="bottom", which positions the popup below the target
|
|
36
|
+
* element's bottom edge. This override:
|
|
37
|
+
*
|
|
38
|
+
* 1. Shifts the popup from the target's bottom edge to align with the paste
|
|
39
|
+
* start position.
|
|
40
|
+
* 2. When the paste start scrolls above the scroll container, clamps the menu
|
|
41
|
+
* to the scroll container's top edge (sticky-top).
|
|
42
|
+
* 3. Stops sticking once the entire pasted range (pasteEndPos) has scrolled
|
|
43
|
+
* above the visible area.
|
|
38
44
|
*/
|
|
39
|
-
export function onPositionCalculated(editorView, pasteStartPos, targetElement) {
|
|
45
|
+
export function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, targetElement, scrollableElement) {
|
|
40
46
|
return position => {
|
|
41
47
|
var _position$top;
|
|
42
48
|
const startCoords = editorView.coordsAtPos(pasteStartPos);
|
|
49
|
+
const endCoords = editorView.coordsAtPos(pasteEndPos);
|
|
43
50
|
const targetRect = targetElement.getBoundingClientRect();
|
|
44
51
|
|
|
45
52
|
// The Popup places the menu at the target's bottom edge by default.
|
|
46
|
-
// We
|
|
53
|
+
// We shift it up so it aligns with the paste start position.
|
|
47
54
|
// Both coordinates are in viewport space, so the delta is offset-parent agnostic.
|
|
48
55
|
const topDelta = startCoords.top - (targetRect.top + targetRect.height);
|
|
56
|
+
let adjustedTop = ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta;
|
|
57
|
+
|
|
58
|
+
// Sticky-top: clamp to the scroll container's top edge when the paste
|
|
59
|
+
// start has scrolled above the visible area, but only while some pasted
|
|
60
|
+
// content is still visible.
|
|
61
|
+
if (scrollableElement) {
|
|
62
|
+
const scrollContainerTop = scrollableElement.getBoundingClientRect().top;
|
|
63
|
+
if (startCoords.top < scrollContainerTop && endCoords.bottom > scrollContainerTop) {
|
|
64
|
+
adjustedTop += scrollContainerTop - startCoords.top + PASTE_MENU_GAP_TOP;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
49
67
|
return {
|
|
50
68
|
...position,
|
|
51
|
-
top:
|
|
69
|
+
top: adjustedTop
|
|
52
70
|
};
|
|
53
71
|
};
|
|
54
72
|
}
|
|
@@ -93,13 +111,15 @@ export const PasteActionsMenu = ({
|
|
|
93
111
|
}, [lastContentPasted, editorView]);
|
|
94
112
|
const {
|
|
95
113
|
showToolbar: isToolbarShown,
|
|
96
|
-
pasteStartPos
|
|
114
|
+
pasteStartPos,
|
|
115
|
+
pasteEndPos
|
|
97
116
|
} = useSharedPluginStateWithSelector(api, ['pasteOptionsToolbarPlugin'], states => {
|
|
98
|
-
var _pluginState$showTool, _pluginState$pasteSta;
|
|
117
|
+
var _pluginState$showTool, _pluginState$pasteSta, _pluginState$pasteEnd;
|
|
99
118
|
const pluginState = states.pasteOptionsToolbarPluginState;
|
|
100
119
|
return {
|
|
101
120
|
showToolbar: (_pluginState$showTool = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showToolbar) !== null && _pluginState$showTool !== void 0 ? _pluginState$showTool : false,
|
|
102
|
-
pasteStartPos: (_pluginState$pasteSta = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteStartPos) !== null && _pluginState$pasteSta !== void 0 ? _pluginState$pasteSta : 0
|
|
121
|
+
pasteStartPos: (_pluginState$pasteSta = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteStartPos) !== null && _pluginState$pasteSta !== void 0 ? _pluginState$pasteSta : 0,
|
|
122
|
+
pasteEndPos: (_pluginState$pasteEnd = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteEndPos) !== null && _pluginState$pasteEnd !== void 0 ? _pluginState$pasteEnd : 0
|
|
103
123
|
};
|
|
104
124
|
});
|
|
105
125
|
const aiSurfaceComponents = (_api$uiControlRegistr = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents('ai-paste-menu')) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
|
|
@@ -141,6 +161,15 @@ export const PasteActionsMenu = ({
|
|
|
141
161
|
handleDismiss();
|
|
142
162
|
}
|
|
143
163
|
}, [handleDismiss]);
|
|
164
|
+
|
|
165
|
+
// Find the actual scroll container using the same utility the Popup's
|
|
166
|
+
// stick prop uses internally. We pass this as the scrollableElement prop
|
|
167
|
+
// so the Popup attaches its built-in scroll listener, which calls
|
|
168
|
+
// scheduledUpdatePosition (RAF-throttled) on each scroll event — triggering
|
|
169
|
+
// onPositionCalculated with fresh viewport coordinates.
|
|
170
|
+
const targetForScroll = isToolbarShown ? getTargetElement(editorView, pasteStartPos) : null;
|
|
171
|
+
const overflowScrollParent = targetForScroll ? findOverflowScrollParent(targetForScroll) : false;
|
|
172
|
+
const effectiveScrollableElement = overflowScrollParent || scrollableElement;
|
|
144
173
|
const pasteMenuComponents = (_api$uiControlRegistr3 = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr4 = api.uiControlRegistry) === null || _api$uiControlRegistr4 === void 0 ? void 0 : _api$uiControlRegistr4.actions.getComponents(PASTE_MENU.key)) !== null && _api$uiControlRegistr3 !== void 0 ? _api$uiControlRegistr3 : [];
|
|
145
174
|
const anyComponentVisible = hasVisibleButton(pasteMenuComponents);
|
|
146
175
|
if (!isToolbarShown) {
|
|
@@ -157,12 +186,13 @@ export const PasteActionsMenu = ({
|
|
|
157
186
|
target: target,
|
|
158
187
|
mountTo: mountTo,
|
|
159
188
|
boundariesElement: boundariesElement,
|
|
160
|
-
scrollableElement:
|
|
189
|
+
scrollableElement: effectiveScrollableElement,
|
|
190
|
+
minPopupMargin: PASTE_MENU_GAP_HORIZONTAL,
|
|
161
191
|
zIndex: akEditorFloatingPanelZIndex,
|
|
162
192
|
alignX: "end",
|
|
163
193
|
alignY: "bottom",
|
|
164
|
-
offset: [
|
|
165
|
-
onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, target),
|
|
194
|
+
offset: [PASTE_MENU_GAP_HORIZONTAL, 0],
|
|
195
|
+
onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement),
|
|
166
196
|
handleClickOutside: handleClickOutside,
|
|
167
197
|
handleEscapeKeydown: handleDismiss
|
|
168
198
|
}, /*#__PURE__*/React.createElement(EditorToolbarProvider, {
|
|
@@ -6,6 +6,5 @@ export var PASTE_TOOLBAR_ITEM_CLASS = 'ak-editor-paste-toolbar-item';
|
|
|
6
6
|
export var EDITOR_WRAPPER_CLASS = 'akEditor';
|
|
7
7
|
export var PASTE_OPTIONS_TEST_ID = 'paste-options-testid';
|
|
8
8
|
export var PASTE_OPTIONS_META_ID = 'paste-options$';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export var PASTE_MENU_GAP = 12;
|
|
9
|
+
export var PASTE_MENU_GAP_HORIZONTAL = 8;
|
|
10
|
+
export var PASTE_MENU_GAP_TOP = 24;
|
|
@@ -5,13 +5,13 @@ import React, { useCallback, useEffect, useRef } from 'react';
|
|
|
5
5
|
import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
6
6
|
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
7
7
|
import { EditorToolbarProvider, PASTE_MENU } from '@atlaskit/editor-common/toolbar';
|
|
8
|
-
import { Popup } from '@atlaskit/editor-common/ui';
|
|
8
|
+
import { findOverflowScrollParent, Popup } from '@atlaskit/editor-common/ui';
|
|
9
9
|
import { withReactEditorViewOuterListeners } from '@atlaskit/editor-common/ui-react';
|
|
10
10
|
import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
|
|
11
11
|
import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
|
|
12
12
|
import { ToolbarDropdownMenuProvider } from '@atlaskit/editor-toolbar';
|
|
13
13
|
import { hideToolbar, highlightContent, showToolbar } from '../../editor-commands/commands';
|
|
14
|
-
import {
|
|
14
|
+
import { PASTE_MENU_GAP_HORIZONTAL, PASTE_MENU_GAP_TOP } from '../../pm-plugins/constants';
|
|
15
15
|
import { ToolbarDropdownOption } from '../../types/types';
|
|
16
16
|
import { isToolbarVisible } from '../toolbar';
|
|
17
17
|
import { getVisibleKeys, hasVisibleButton } from './hasVisibleButton';
|
|
@@ -31,26 +31,44 @@ function getTargetElement(editorView, pos) {
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Adjusts the vertical position of the paste menu to align with the top of the
|
|
34
|
-
* pasted content using the exact coordinates at the paste start position
|
|
34
|
+
* pasted content using the exact coordinates at the paste start position,
|
|
35
|
+
* and sticks the menu to the top of the scroll container when the pasted
|
|
36
|
+
* content scrolls above the visible area.
|
|
35
37
|
*
|
|
36
|
-
* The Popup
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* The Popup uses alignY="bottom", which positions the popup below the target
|
|
39
|
+
* element's bottom edge. This override:
|
|
40
|
+
*
|
|
41
|
+
* 1. Shifts the popup from the target's bottom edge to align with the paste
|
|
42
|
+
* start position.
|
|
43
|
+
* 2. When the paste start scrolls above the scroll container, clamps the menu
|
|
44
|
+
* to the scroll container's top edge (sticky-top).
|
|
45
|
+
* 3. Stops sticking once the entire pasted range (pasteEndPos) has scrolled
|
|
46
|
+
* above the visible area.
|
|
41
47
|
*/
|
|
42
|
-
export function onPositionCalculated(editorView, pasteStartPos, targetElement) {
|
|
48
|
+
export function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, targetElement, scrollableElement) {
|
|
43
49
|
return function (position) {
|
|
44
50
|
var _position$top;
|
|
45
51
|
var startCoords = editorView.coordsAtPos(pasteStartPos);
|
|
52
|
+
var endCoords = editorView.coordsAtPos(pasteEndPos);
|
|
46
53
|
var targetRect = targetElement.getBoundingClientRect();
|
|
47
54
|
|
|
48
55
|
// The Popup places the menu at the target's bottom edge by default.
|
|
49
|
-
// We
|
|
56
|
+
// We shift it up so it aligns with the paste start position.
|
|
50
57
|
// Both coordinates are in viewport space, so the delta is offset-parent agnostic.
|
|
51
58
|
var topDelta = startCoords.top - (targetRect.top + targetRect.height);
|
|
59
|
+
var adjustedTop = ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta;
|
|
60
|
+
|
|
61
|
+
// Sticky-top: clamp to the scroll container's top edge when the paste
|
|
62
|
+
// start has scrolled above the visible area, but only while some pasted
|
|
63
|
+
// content is still visible.
|
|
64
|
+
if (scrollableElement) {
|
|
65
|
+
var scrollContainerTop = scrollableElement.getBoundingClientRect().top;
|
|
66
|
+
if (startCoords.top < scrollContainerTop && endCoords.bottom > scrollContainerTop) {
|
|
67
|
+
adjustedTop += scrollContainerTop - startCoords.top + PASTE_MENU_GAP_TOP;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
52
70
|
return _objectSpread(_objectSpread({}, position), {}, {
|
|
53
|
-
top:
|
|
71
|
+
top: adjustedTop
|
|
54
72
|
});
|
|
55
73
|
};
|
|
56
74
|
}
|
|
@@ -92,15 +110,17 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
|
|
|
92
110
|
showToolbar(lastContentPasted, selectedOption, legacyVisible, pasteAncestorNodeNames)(editorView.state, editorView.dispatch);
|
|
93
111
|
}, [lastContentPasted, editorView]);
|
|
94
112
|
var _useSharedPluginState2 = useSharedPluginStateWithSelector(api, ['pasteOptionsToolbarPlugin'], function (states) {
|
|
95
|
-
var _pluginState$showTool, _pluginState$pasteSta;
|
|
113
|
+
var _pluginState$showTool, _pluginState$pasteSta, _pluginState$pasteEnd;
|
|
96
114
|
var pluginState = states.pasteOptionsToolbarPluginState;
|
|
97
115
|
return {
|
|
98
116
|
showToolbar: (_pluginState$showTool = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showToolbar) !== null && _pluginState$showTool !== void 0 ? _pluginState$showTool : false,
|
|
99
|
-
pasteStartPos: (_pluginState$pasteSta = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteStartPos) !== null && _pluginState$pasteSta !== void 0 ? _pluginState$pasteSta : 0
|
|
117
|
+
pasteStartPos: (_pluginState$pasteSta = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteStartPos) !== null && _pluginState$pasteSta !== void 0 ? _pluginState$pasteSta : 0,
|
|
118
|
+
pasteEndPos: (_pluginState$pasteEnd = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteEndPos) !== null && _pluginState$pasteEnd !== void 0 ? _pluginState$pasteEnd : 0
|
|
100
119
|
};
|
|
101
120
|
}),
|
|
102
121
|
isToolbarShown = _useSharedPluginState2.showToolbar,
|
|
103
|
-
pasteStartPos = _useSharedPluginState2.pasteStartPos
|
|
122
|
+
pasteStartPos = _useSharedPluginState2.pasteStartPos,
|
|
123
|
+
pasteEndPos = _useSharedPluginState2.pasteEndPos;
|
|
104
124
|
var aiSurfaceComponents = (_api$uiControlRegistr = api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents('ai-paste-menu')) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
|
|
105
125
|
var visibleAiActionKeys = getVisibleKeys(aiSurfaceComponents, ['button', 'menu-item']);
|
|
106
126
|
useEffect(function () {
|
|
@@ -140,6 +160,15 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
|
|
|
140
160
|
handleDismiss();
|
|
141
161
|
}
|
|
142
162
|
}, [handleDismiss]);
|
|
163
|
+
|
|
164
|
+
// Find the actual scroll container using the same utility the Popup's
|
|
165
|
+
// stick prop uses internally. We pass this as the scrollableElement prop
|
|
166
|
+
// so the Popup attaches its built-in scroll listener, which calls
|
|
167
|
+
// scheduledUpdatePosition (RAF-throttled) on each scroll event — triggering
|
|
168
|
+
// onPositionCalculated with fresh viewport coordinates.
|
|
169
|
+
var targetForScroll = isToolbarShown ? getTargetElement(editorView, pasteStartPos) : null;
|
|
170
|
+
var overflowScrollParent = targetForScroll ? findOverflowScrollParent(targetForScroll) : false;
|
|
171
|
+
var effectiveScrollableElement = overflowScrollParent || scrollableElement;
|
|
143
172
|
var pasteMenuComponents = (_api$uiControlRegistr3 = api === null || api === void 0 || (_api$uiControlRegistr4 = api.uiControlRegistry) === null || _api$uiControlRegistr4 === void 0 ? void 0 : _api$uiControlRegistr4.actions.getComponents(PASTE_MENU.key)) !== null && _api$uiControlRegistr3 !== void 0 ? _api$uiControlRegistr3 : [];
|
|
144
173
|
var anyComponentVisible = hasVisibleButton(pasteMenuComponents);
|
|
145
174
|
if (!isToolbarShown) {
|
|
@@ -156,12 +185,13 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
|
|
|
156
185
|
target: target,
|
|
157
186
|
mountTo: mountTo,
|
|
158
187
|
boundariesElement: boundariesElement,
|
|
159
|
-
scrollableElement:
|
|
188
|
+
scrollableElement: effectiveScrollableElement,
|
|
189
|
+
minPopupMargin: PASTE_MENU_GAP_HORIZONTAL,
|
|
160
190
|
zIndex: akEditorFloatingPanelZIndex,
|
|
161
191
|
alignX: "end",
|
|
162
192
|
alignY: "bottom",
|
|
163
|
-
offset: [
|
|
164
|
-
onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, target),
|
|
193
|
+
offset: [PASTE_MENU_GAP_HORIZONTAL, 0],
|
|
194
|
+
onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement),
|
|
165
195
|
handleClickOutside: handleClickOutside,
|
|
166
196
|
handleEscapeKeydown: handleDismiss
|
|
167
197
|
}, /*#__PURE__*/React.createElement(EditorToolbarProvider, {
|
|
@@ -6,4 +6,5 @@ export declare const PASTE_TOOLBAR_ITEM_CLASS = "ak-editor-paste-toolbar-item";
|
|
|
6
6
|
export declare const EDITOR_WRAPPER_CLASS = "akEditor";
|
|
7
7
|
export declare const PASTE_OPTIONS_TEST_ID = "paste-options-testid";
|
|
8
8
|
export declare const PASTE_OPTIONS_META_ID = "paste-options$";
|
|
9
|
-
export declare const
|
|
9
|
+
export declare const PASTE_MENU_GAP_HORIZONTAL = 8;
|
|
10
|
+
export declare const PASTE_MENU_GAP_TOP = 24;
|
|
@@ -11,15 +11,21 @@ interface PasteActionsMenuProps {
|
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Adjusts the vertical position of the paste menu to align with the top of the
|
|
14
|
-
* pasted content using the exact coordinates at the paste start position
|
|
14
|
+
* pasted content using the exact coordinates at the paste start position,
|
|
15
|
+
* and sticks the menu to the top of the scroll container when the pasted
|
|
16
|
+
* content scrolls above the visible area.
|
|
15
17
|
*
|
|
16
|
-
* The Popup
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* The Popup uses alignY="bottom", which positions the popup below the target
|
|
19
|
+
* element's bottom edge. This override:
|
|
20
|
+
*
|
|
21
|
+
* 1. Shifts the popup from the target's bottom edge to align with the paste
|
|
22
|
+
* start position.
|
|
23
|
+
* 2. When the paste start scrolls above the scroll container, clamps the menu
|
|
24
|
+
* to the scroll container's top edge (sticky-top).
|
|
25
|
+
* 3. Stops sticking once the entire pasted range (pasteEndPos) has scrolled
|
|
26
|
+
* above the visible area.
|
|
21
27
|
*/
|
|
22
|
-
export declare function onPositionCalculated(editorView: EditorView, pasteStartPos: number, targetElement: HTMLElement): (position: {
|
|
28
|
+
export declare function onPositionCalculated(editorView: EditorView, pasteStartPos: number, pasteEndPos: number, targetElement: HTMLElement, scrollableElement?: HTMLElement | false): (position: {
|
|
23
29
|
bottom?: number;
|
|
24
30
|
left?: number;
|
|
25
31
|
right?: number;
|
|
@@ -6,4 +6,5 @@ export declare const PASTE_TOOLBAR_ITEM_CLASS = "ak-editor-paste-toolbar-item";
|
|
|
6
6
|
export declare const EDITOR_WRAPPER_CLASS = "akEditor";
|
|
7
7
|
export declare const PASTE_OPTIONS_TEST_ID = "paste-options-testid";
|
|
8
8
|
export declare const PASTE_OPTIONS_META_ID = "paste-options$";
|
|
9
|
-
export declare const
|
|
9
|
+
export declare const PASTE_MENU_GAP_HORIZONTAL = 8;
|
|
10
|
+
export declare const PASTE_MENU_GAP_TOP = 24;
|
|
@@ -11,15 +11,21 @@ interface PasteActionsMenuProps {
|
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Adjusts the vertical position of the paste menu to align with the top of the
|
|
14
|
-
* pasted content using the exact coordinates at the paste start position
|
|
14
|
+
* pasted content using the exact coordinates at the paste start position,
|
|
15
|
+
* and sticks the menu to the top of the scroll container when the pasted
|
|
16
|
+
* content scrolls above the visible area.
|
|
15
17
|
*
|
|
16
|
-
* The Popup
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* The Popup uses alignY="bottom", which positions the popup below the target
|
|
19
|
+
* element's bottom edge. This override:
|
|
20
|
+
*
|
|
21
|
+
* 1. Shifts the popup from the target's bottom edge to align with the paste
|
|
22
|
+
* start position.
|
|
23
|
+
* 2. When the paste start scrolls above the scroll container, clamps the menu
|
|
24
|
+
* to the scroll container's top edge (sticky-top).
|
|
25
|
+
* 3. Stops sticking once the entire pasted range (pasteEndPos) has scrolled
|
|
26
|
+
* above the visible area.
|
|
21
27
|
*/
|
|
22
|
-
export declare function onPositionCalculated(editorView: EditorView, pasteStartPos: number, targetElement: HTMLElement): (position: {
|
|
28
|
+
export declare function onPositionCalculated(editorView: EditorView, pasteStartPos: number, pasteEndPos: number, targetElement: HTMLElement, scrollableElement?: HTMLElement | false): (position: {
|
|
23
29
|
bottom?: number;
|
|
24
30
|
left?: number;
|
|
25
31
|
right?: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-paste-options-toolbar",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.6",
|
|
4
4
|
"description": "Paste options toolbar for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"@atlaskit/editor-shared-styles": "^3.10.0",
|
|
39
39
|
"@atlaskit/editor-toolbar": "^0.19.0",
|
|
40
40
|
"@atlaskit/editor-ui-control-model": "^1.1.0",
|
|
41
|
-
"@atlaskit/icon": "^32.
|
|
41
|
+
"@atlaskit/icon": "^32.1.0",
|
|
42
42
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
43
43
|
"@atlaskit/primitives": "^18.0.0",
|
|
44
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
44
|
+
"@atlaskit/tmp-editor-statsig": "^38.1.0",
|
|
45
45
|
"@atlaskit/tokens": "^11.1.0",
|
|
46
46
|
"@babel/runtime": "^7.0.0",
|
|
47
47
|
"@compiled/react": "^0.20.0",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"react-intl-next": "npm:react-intl@^5.18.1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@atlaskit/editor-common": "^112.
|
|
52
|
+
"@atlaskit/editor-common": "^112.3.0",
|
|
53
53
|
"react": "^18.2.0",
|
|
54
54
|
"react-dom": "^18.2.0"
|
|
55
55
|
},
|