@atlaskit/editor-plugin-paste-options-toolbar 9.1.5 → 9.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/cjs/pm-plugins/util/format-handlers.js +1 -1
- package/dist/cjs/ui/on-paste-actions-menu/PasteActionsMenu.js +133 -13
- package/dist/cjs/ui/on-paste-actions-menu/PasteActionsMenuContent.js +16 -13
- package/dist/cjs/ui/on-paste-actions-menu/PasteMenuComponents.js +45 -15
- package/dist/cjs/ui/on-paste-actions-menu/PasteOptionsDropdownButton.js +52 -0
- package/dist/es2019/pm-plugins/util/format-handlers.js +1 -1
- package/dist/es2019/ui/on-paste-actions-menu/PasteActionsMenu.js +131 -14
- package/dist/es2019/ui/on-paste-actions-menu/PasteActionsMenuContent.js +17 -14
- package/dist/es2019/ui/on-paste-actions-menu/PasteMenuComponents.js +45 -13
- package/dist/es2019/ui/on-paste-actions-menu/PasteOptionsDropdownButton.js +47 -0
- package/dist/esm/pm-plugins/util/format-handlers.js +1 -1
- package/dist/esm/ui/on-paste-actions-menu/PasteActionsMenu.js +132 -14
- package/dist/esm/ui/on-paste-actions-menu/PasteActionsMenuContent.js +17 -14
- package/dist/esm/ui/on-paste-actions-menu/PasteMenuComponents.js +45 -15
- package/dist/esm/ui/on-paste-actions-menu/PasteOptionsDropdownButton.js +46 -0
- package/dist/types/ui/on-paste-actions-menu/PasteActionsMenu.d.ts +38 -4
- package/dist/types/ui/on-paste-actions-menu/PasteActionsMenuContent.d.ts +2 -1
- package/dist/types/ui/on-paste-actions-menu/PasteOptionsDropdownButton.d.ts +19 -0
- package/dist/types-ts4.5/ui/on-paste-actions-menu/PasteActionsMenu.d.ts +38 -4
- package/dist/types-ts4.5/ui/on-paste-actions-menu/PasteActionsMenuContent.d.ts +2 -1
- package/dist/types-ts4.5/ui/on-paste-actions-menu/PasteOptionsDropdownButton.d.ts +19 -0
- package/package.json +6 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
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
|
-
import { EditorToolbarProvider, PASTE_MENU } from '@atlaskit/editor-common/toolbar';
|
|
4
|
+
import { AI_PASTE_MENU_SECTION, EditorToolbarProvider, PASTE_MENU } from '@atlaskit/editor-common/toolbar';
|
|
5
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';
|
|
@@ -66,10 +66,77 @@ export function getVisualEndBottom(editorView, pasteEndPos, tableAfterPos) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
69
|
+
* Finds the DOM element for the nearest block-level ProseMirror ancestor of
|
|
70
|
+
* the given document position. Uses ProseMirror's schema (`node.isBlock`)
|
|
71
|
+
* rather than CSS display properties, so the check is always in sync with the
|
|
72
|
+
* document model.
|
|
73
|
+
*
|
|
74
|
+
* Returns `null` if no block ancestor can be resolved to a DOM element.
|
|
75
|
+
*/
|
|
76
|
+
export function findBlockAncestorDOM(editorView, pos) {
|
|
77
|
+
try {
|
|
78
|
+
const $pos = editorView.state.doc.resolve(pos);
|
|
79
|
+
// Walk up the document tree from the resolved position's innermost
|
|
80
|
+
// node towards the root. $pos.node(depth) gives the ancestor at each
|
|
81
|
+
// depth; $pos.start(depth) gives the position just inside that ancestor,
|
|
82
|
+
// so `$pos.start(depth) - 1` is the position of the ancestor node itself
|
|
83
|
+
// (which is what nodeDOM expects).
|
|
84
|
+
for (let depth = $pos.depth; depth >= 0; depth--) {
|
|
85
|
+
const node = $pos.node(depth);
|
|
86
|
+
if (node.isBlock) {
|
|
87
|
+
const domNode = editorView.nodeDOM($pos.start(depth) - 1);
|
|
88
|
+
if (domNode instanceof HTMLElement) {
|
|
89
|
+
return domNode;
|
|
90
|
+
}
|
|
91
|
+
// depth 0 is the doc node — nodeDOM(–1) won't work, so try
|
|
92
|
+
// the editor's own DOM element as a fallback.
|
|
93
|
+
if (depth === 0 && editorView.dom instanceof HTMLElement) {
|
|
94
|
+
return editorView.dom;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
// Position may be out of range after a concurrent edit — fall through.
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Positions the paste menu inline, immediately to the right of the cursor
|
|
106
|
+
* at the paste end position, vertically centered with the line.
|
|
107
|
+
* Used for short pastes without AI actions.
|
|
108
|
+
*/
|
|
109
|
+
export function onInlinePositionCalculated(editorView, pasteEndPos, targetElement, popupContentRef) {
|
|
110
|
+
return position => {
|
|
111
|
+
var _popupContentRef$curr, _popupContentRef$curr2, _position$top, _position$left;
|
|
112
|
+
const endCoords = editorView.coordsAtPos(pasteEndPos);
|
|
113
|
+
const targetRect = targetElement.getBoundingClientRect();
|
|
114
|
+
|
|
115
|
+
// Vertical: center the menu with the line at the paste end position.
|
|
116
|
+
const lineHeight = endCoords.bottom - endCoords.top;
|
|
117
|
+
const lineMidpoint = endCoords.top + lineHeight / 2;
|
|
118
|
+
const menuHeight = (_popupContentRef$curr = (_popupContentRef$curr2 = popupContentRef.current) === null || _popupContentRef$curr2 === void 0 ? void 0 : _popupContentRef$curr2.getBoundingClientRect().height) !== null && _popupContentRef$curr !== void 0 ? _popupContentRef$curr : lineHeight;
|
|
119
|
+
const menuTop = lineMidpoint - menuHeight / 2;
|
|
120
|
+
const topDelta = menuTop - (targetRect.top + targetRect.height);
|
|
121
|
+
const adjustedTop = ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta;
|
|
122
|
+
|
|
123
|
+
// Horizontal: position to the right of the cursor
|
|
124
|
+
const leftDelta = endCoords.right - targetRect.right;
|
|
125
|
+
const adjustedLeft = ((_position$left = position.left) !== null && _position$left !== void 0 ? _position$left : 0) + leftDelta;
|
|
126
|
+
return {
|
|
127
|
+
...position,
|
|
128
|
+
top: adjustedTop,
|
|
129
|
+
left: adjustedLeft
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Adjusts the position of the paste menu so that:
|
|
136
|
+
*
|
|
137
|
+
* **Vertical:** The menu aligns with the top of the pasted content using the
|
|
138
|
+
* exact coordinates at the paste start position, and sticks to the top of the
|
|
139
|
+
* scroll container when the pasted content scrolls above the visible area.
|
|
73
140
|
*
|
|
74
141
|
* The Popup uses alignY="bottom", which positions the popup below the target
|
|
75
142
|
* element's bottom edge. This override:
|
|
@@ -80,21 +147,31 @@ export function getVisualEndBottom(editorView, pasteEndPos, tableAfterPos) {
|
|
|
80
147
|
* to the scroll container's top edge (sticky-top).
|
|
81
148
|
* 3. Stops sticking once the entire pasted range (pasteEndPos) has scrolled
|
|
82
149
|
* above the visible area.
|
|
150
|
+
*
|
|
151
|
+
* **Horizontal:** When the target element is an inline element (e.g. a mark
|
|
152
|
+
* wrapper like `<strong>`, or an inline node like an emoji), the Popup's
|
|
153
|
+
* `alignX="end"` would place the menu at the right edge of that narrow
|
|
154
|
+
* element. This override resolves the nearest block-level ProseMirror
|
|
155
|
+
* ancestor (using `node.isBlock` from the document schema) and re-anchors
|
|
156
|
+
* the horizontal position to its right edge, so the menu consistently
|
|
157
|
+
* appears at the right side of the content area.
|
|
83
158
|
*/
|
|
84
159
|
export function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, targetElement, scrollableElement) {
|
|
85
160
|
// Pre-compute once per render to avoid doc.resolve() on every scroll frame.
|
|
86
161
|
const tableAfterPos = resolveTableAfterPos(editorView, pasteEndPos);
|
|
162
|
+
const blockAncestorDOM = findBlockAncestorDOM(editorView, pasteStartPos);
|
|
87
163
|
return position => {
|
|
88
|
-
var _position$
|
|
164
|
+
var _position$top2;
|
|
89
165
|
const startCoords = editorView.coordsAtPos(pasteStartPos);
|
|
90
166
|
const endBottom = getVisualEndBottom(editorView, pasteEndPos, tableAfterPos);
|
|
91
167
|
const targetRect = targetElement.getBoundingClientRect();
|
|
92
168
|
|
|
169
|
+
// ── Vertical adjustment ──────────────────────────────────────────
|
|
93
170
|
// The Popup places the menu at the target's bottom edge by default.
|
|
94
171
|
// We shift it up so it aligns with the paste start position.
|
|
95
172
|
// Both coordinates are in viewport space, so the delta is offset-parent agnostic.
|
|
96
173
|
const topDelta = startCoords.top - (targetRect.top + targetRect.height);
|
|
97
|
-
let adjustedTop = ((_position$
|
|
174
|
+
let adjustedTop = ((_position$top2 = position.top) !== null && _position$top2 !== void 0 ? _position$top2 : 0) + topDelta;
|
|
98
175
|
|
|
99
176
|
// Sticky-top: clamp to the scroll container's top edge when the paste
|
|
100
177
|
// start has scrolled above the visible area, but only while some pasted
|
|
@@ -105,9 +182,29 @@ export function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, tar
|
|
|
105
182
|
adjustedTop += scrollContainerTop - startCoords.top + PASTE_MENU_GAP_TOP;
|
|
106
183
|
}
|
|
107
184
|
}
|
|
185
|
+
|
|
186
|
+
// ── Horizontal adjustment ────────────────────────────────────────
|
|
187
|
+
// When pasted content starts with a mark (bold, italic, link …) or
|
|
188
|
+
// an inline node (emoji, smart link, inline image …),
|
|
189
|
+
// findDomRefAtPos returns the narrow inline wrapper element. The
|
|
190
|
+
// Popup's alignX="end" then places the menu at that element's right
|
|
191
|
+
// edge instead of the content area's right edge. We correct this by
|
|
192
|
+
// resolving the nearest block-level ProseMirror ancestor and
|
|
193
|
+
// re-anchoring to its right edge.
|
|
194
|
+
let adjustedLeft = position.left;
|
|
195
|
+
if (blockAncestorDOM && blockAncestorDOM !== targetElement) {
|
|
196
|
+
var _position$left2;
|
|
197
|
+
const blockRect = blockAncestorDOM.getBoundingClientRect();
|
|
198
|
+
// Shift left by the difference between the block's right edge and
|
|
199
|
+
// the inline target's right edge. This mirrors what alignX="end"
|
|
200
|
+
// would have computed if the target were the block element.
|
|
201
|
+
const leftDelta = blockRect.right - targetRect.right;
|
|
202
|
+
adjustedLeft = ((_position$left2 = position.left) !== null && _position$left2 !== void 0 ? _position$left2 : 0) + leftDelta;
|
|
203
|
+
}
|
|
108
204
|
return {
|
|
109
205
|
...position,
|
|
110
|
-
top: adjustedTop
|
|
206
|
+
top: adjustedTop,
|
|
207
|
+
left: adjustedLeft
|
|
111
208
|
};
|
|
112
209
|
};
|
|
113
210
|
}
|
|
@@ -129,8 +226,8 @@ export const PasteActionsMenu = ({
|
|
|
129
226
|
};
|
|
130
227
|
});
|
|
131
228
|
const prevShowToolbarRef = useRef(false);
|
|
229
|
+
const popupContentRef = useRef(null);
|
|
132
230
|
useEffect(() => {
|
|
133
|
-
var _lastContentPasted$te, _lastContentPasted$te2;
|
|
134
231
|
if (!lastContentPasted) {
|
|
135
232
|
hideToolbar()(editorView.state, editorView.dispatch);
|
|
136
233
|
return;
|
|
@@ -153,7 +250,7 @@ export const PasteActionsMenu = ({
|
|
|
153
250
|
pasteAncestorNodeNames.push($pos.node(depth).type.name);
|
|
154
251
|
}
|
|
155
252
|
}
|
|
156
|
-
const legacyVisible = isToolbarVisible(editorView.state, lastContentPasted)
|
|
253
|
+
const legacyVisible = isToolbarVisible(editorView.state, lastContentPasted);
|
|
157
254
|
showToolbar(lastContentPasted, selectedOption, legacyVisible, pasteAncestorNodeNames)(editorView.state, editorView.dispatch);
|
|
158
255
|
}, [lastContentPasted, editorView]);
|
|
159
256
|
const {
|
|
@@ -218,16 +315,34 @@ export const PasteActionsMenu = ({
|
|
|
218
315
|
const effectiveScrollableElement = overflowScrollParent || scrollableElement;
|
|
219
316
|
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 : [];
|
|
220
317
|
const anyComponentVisible = hasVisibleButton(pasteMenuComponents);
|
|
318
|
+
|
|
319
|
+
// Two positioning modes:
|
|
320
|
+
// 1. Inline: no AI actions visible — menu appears to the right of the cursor,
|
|
321
|
+
// vertically centered with the text line.
|
|
322
|
+
// 2. Block-anchored: AI actions are visible — menu appears at the right edge
|
|
323
|
+
// of the content block, aligned with paste start.
|
|
324
|
+
const hasVisibleAiActions = getVisibleKeys(
|
|
325
|
+
// eslint-disable-next-line @atlassian/perf-linting/no-expensive-computations-in-render -- pasteMenuComponents changes by reference each render; filter is small (< 10 items)
|
|
326
|
+
pasteMenuComponents.filter(c => {
|
|
327
|
+
var _c$parents;
|
|
328
|
+
return c.type === 'menu-item' && ((_c$parents = c.parents) === null || _c$parents === void 0 ? void 0 : _c$parents.some(p => p.key === AI_PASTE_MENU_SECTION.key));
|
|
329
|
+
}), ['menu-item']).length > 0;
|
|
330
|
+
const useInlinePosition = !hasVisibleAiActions;
|
|
221
331
|
if (!isToolbarShown) {
|
|
222
332
|
return null;
|
|
223
333
|
}
|
|
224
334
|
if (!anyComponentVisible) {
|
|
225
335
|
return null;
|
|
226
336
|
}
|
|
227
|
-
const target = getTargetElement(editorView, pasteStartPos);
|
|
337
|
+
const target = getTargetElement(editorView, useInlinePosition ? pasteEndPos : pasteStartPos);
|
|
228
338
|
if (!target) {
|
|
229
339
|
return null;
|
|
230
340
|
}
|
|
341
|
+
|
|
342
|
+
// Choose positioning strategy based on whether the menu appears inline
|
|
343
|
+
// (right of cursor for short pastes) or anchored to the block ancestor
|
|
344
|
+
// (right side of content area for longer pastes / AI actions).
|
|
345
|
+
const positionCalculator = useInlinePosition ? onInlinePositionCalculated(editorView, pasteEndPos, target, popupContentRef) : onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement);
|
|
231
346
|
return /*#__PURE__*/React.createElement(PopupWithListeners, {
|
|
232
347
|
target: target,
|
|
233
348
|
mountTo: mountTo,
|
|
@@ -236,9 +351,10 @@ export const PasteActionsMenu = ({
|
|
|
236
351
|
minPopupMargin: PASTE_MENU_GAP_HORIZONTAL,
|
|
237
352
|
zIndex: akEditorFloatingPanelZIndex,
|
|
238
353
|
alignX: "end",
|
|
239
|
-
alignY:
|
|
354
|
+
alignY: useInlinePosition ? 'top' : 'bottom'
|
|
355
|
+
/* eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) */,
|
|
240
356
|
offset: [PASTE_MENU_GAP_HORIZONTAL, 0],
|
|
241
|
-
onPositionCalculated:
|
|
357
|
+
onPositionCalculated: positionCalculator,
|
|
242
358
|
handleClickOutside: handleClickOutside,
|
|
243
359
|
handleEscapeKeydown: handleDismiss
|
|
244
360
|
}, /*#__PURE__*/React.createElement(EditorToolbarProvider, {
|
|
@@ -249,6 +365,7 @@ export const PasteActionsMenu = ({
|
|
|
249
365
|
}, /*#__PURE__*/React.createElement(PasteActionsMenuContent, {
|
|
250
366
|
onMouseDown: preventEditorFocusLoss,
|
|
251
367
|
onMouseEnter: handleMouseEnter,
|
|
252
|
-
components: pasteMenuComponents
|
|
368
|
+
components: pasteMenuComponents,
|
|
369
|
+
contentRef: popupContentRef
|
|
253
370
|
}))));
|
|
254
371
|
};
|
|
@@ -1,35 +1,38 @@
|
|
|
1
1
|
/* PasteActionsMenuContent.tsx generated by @compiled/babel-plugin v0.39.1 */
|
|
2
2
|
import "./PasteActionsMenuContent.compiled.css";
|
|
3
3
|
import { ax, ix } from "@compiled/react/runtime";
|
|
4
|
-
import React, { useContext } from 'react';
|
|
5
|
-
import {
|
|
6
|
-
import { pasteOptionsToolbarMessages as messages } from '@atlaskit/editor-common/messages';
|
|
4
|
+
import React, { useCallback, useContext } from 'react';
|
|
5
|
+
import { PASTE_MENU } from '@atlaskit/editor-common/toolbar';
|
|
7
6
|
import { OutsideClickTargetRefContext } from '@atlaskit/editor-common/ui-react';
|
|
8
|
-
import { ToolbarDropdownItemSection } from '@atlaskit/editor-toolbar';
|
|
9
7
|
import { SurfaceRenderer } from '@atlaskit/editor-ui-control-model';
|
|
10
8
|
import { Box } from '@atlaskit/primitives/compiled';
|
|
11
9
|
const styles = {
|
|
12
10
|
container: "_2rko12b0 _bfhk1bhr _16qs130s"
|
|
13
11
|
};
|
|
12
|
+
const pasteMenuSurface = {
|
|
13
|
+
type: PASTE_MENU.type,
|
|
14
|
+
key: PASTE_MENU.key
|
|
15
|
+
};
|
|
14
16
|
export const PasteActionsMenuContent = ({
|
|
15
17
|
onMouseDown,
|
|
16
18
|
onMouseEnter,
|
|
17
|
-
components
|
|
19
|
+
components,
|
|
20
|
+
contentRef
|
|
18
21
|
}) => {
|
|
19
22
|
const setOutsideClickTargetRef = useContext(OutsideClickTargetRefContext);
|
|
20
|
-
const
|
|
23
|
+
const mergedRef = useCallback(node => {
|
|
24
|
+
setOutsideClickTargetRef === null || setOutsideClickTargetRef === void 0 ? void 0 : setOutsideClickTargetRef(node);
|
|
25
|
+
if (contentRef) {
|
|
26
|
+
contentRef.current = node;
|
|
27
|
+
}
|
|
28
|
+
}, [setOutsideClickTargetRef, contentRef]);
|
|
21
29
|
return /*#__PURE__*/React.createElement(Box, {
|
|
22
|
-
ref:
|
|
30
|
+
ref: mergedRef,
|
|
23
31
|
xcss: styles.container,
|
|
24
32
|
onMouseDown: onMouseDown,
|
|
25
33
|
onMouseEnter: onMouseEnter
|
|
26
|
-
}, /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, {
|
|
27
|
-
title: intl.formatMessage(messages.pasteMenuActionsTitle)
|
|
28
34
|
}, /*#__PURE__*/React.createElement(SurfaceRenderer, {
|
|
29
|
-
surface:
|
|
30
|
-
type: 'menu',
|
|
31
|
-
key: 'paste-menu'
|
|
32
|
-
},
|
|
35
|
+
surface: pasteMenuSurface,
|
|
33
36
|
components: components
|
|
34
|
-
}))
|
|
37
|
+
}));
|
|
35
38
|
};
|
|
@@ -8,12 +8,14 @@ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'
|
|
|
8
8
|
import { pasteOptionsToolbarMessages as messages } from '@atlaskit/editor-common/messages';
|
|
9
9
|
import { useEditorToolbar, PASTE_MENU, PASTE_MENU_SECTION, PASTE_NESTED_MENU, PASTE_MENU_NESTED_SECTION, PASTE_RICH_TEXT_MENU_ITEM, PASTE_MARKDOWN_MENU_ITEM, PASTE_PLAIN_TEXT_MENU_ITEM, PASTE_MENU_RANK, PASTE_MENU_SECTION_RANK, PASTE_NESTED_MENU_RANK, PASTE_MENU_NESTED_SECTION_RANK, AI_PASTE_MENU_SECTION } from '@atlaskit/editor-common/toolbar';
|
|
10
10
|
import { ToolbarDropdownItem, ToolbarDropdownItemSection, ToolbarNestedDropdownMenu } from '@atlaskit/editor-toolbar';
|
|
11
|
+
import ChevronDownIcon from '@atlaskit/icon/core/chevron-down';
|
|
11
12
|
import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
|
|
12
13
|
import ClipboardIcon from '@atlaskit/icon/core/clipboard';
|
|
13
14
|
import { Box } from '@atlaskit/primitives/compiled';
|
|
14
15
|
import { changeToMarkdownWithAnalytics, changeToPlainTextWithAnalytics, changeToRichTextWithAnalytics } from '../../editor-commands/commands';
|
|
15
16
|
import { ToolbarDropdownOption } from '../../types/types';
|
|
16
17
|
import { getVisibleKeys } from './hasVisibleButton';
|
|
18
|
+
import { PasteOptionsDropdownButton } from './PasteOptionsDropdownButton';
|
|
17
19
|
const nestedMenuStyles = {
|
|
18
20
|
narrowSection: "_10gv1lit"
|
|
19
21
|
};
|
|
@@ -94,22 +96,48 @@ const PasteMenuItem = ({
|
|
|
94
96
|
}, displayLabel);
|
|
95
97
|
};
|
|
96
98
|
const PasteOptionsNestedMenu = ({
|
|
97
|
-
children
|
|
99
|
+
children,
|
|
100
|
+
hasVisibleAiActions
|
|
98
101
|
}) => {
|
|
99
102
|
const intl = useIntl();
|
|
103
|
+
const label = intl.formatMessage(messages.pasteMenuActionsPasteAs);
|
|
104
|
+
if (!hasVisibleAiActions) {
|
|
105
|
+
return /*#__PURE__*/React.createElement(PasteOptionsDropdownButton, {
|
|
106
|
+
elemBefore: /*#__PURE__*/React.createElement(ClipboardIcon, {
|
|
107
|
+
size: "small",
|
|
108
|
+
label: ""
|
|
109
|
+
}),
|
|
110
|
+
elemAfter: /*#__PURE__*/React.createElement(ChevronDownIcon, {
|
|
111
|
+
size: "small",
|
|
112
|
+
label: ""
|
|
113
|
+
}),
|
|
114
|
+
label: label,
|
|
115
|
+
testId: "paste-options-nested-menu",
|
|
116
|
+
tooltipContent: label
|
|
117
|
+
}, children);
|
|
118
|
+
}
|
|
100
119
|
return /*#__PURE__*/React.createElement(ToolbarNestedDropdownMenu, {
|
|
101
120
|
elemBefore: /*#__PURE__*/React.createElement(ClipboardIcon, {
|
|
102
121
|
size: "small",
|
|
103
|
-
label:
|
|
122
|
+
label: label
|
|
104
123
|
}),
|
|
105
124
|
elemAfter: /*#__PURE__*/React.createElement(ChevronRightIcon, {
|
|
106
125
|
size: "small",
|
|
107
|
-
label:
|
|
126
|
+
label: ""
|
|
108
127
|
}),
|
|
109
128
|
testId: "paste-options-nested-menu",
|
|
110
|
-
text:
|
|
129
|
+
text: label
|
|
111
130
|
}, children);
|
|
112
131
|
};
|
|
132
|
+
const getHasVisibleAiActions = api => {
|
|
133
|
+
var _api$uiControlRegistr, _api$uiControlRegistr2;
|
|
134
|
+
const allComponents = (_api$uiControlRegistr = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(PASTE_MENU.key)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
|
|
135
|
+
const aiMenuItems = allComponents.filter(c => {
|
|
136
|
+
var _c$parents;
|
|
137
|
+
return c.type === 'menu-item' && ((_c$parents = c.parents) === null || _c$parents === void 0 ? void 0 : _c$parents.some(p => p.key === AI_PASTE_MENU_SECTION.key));
|
|
138
|
+
});
|
|
139
|
+
return getVisibleKeys(aiMenuItems, ['menu-item']).length > 0;
|
|
140
|
+
};
|
|
113
141
|
export const getPasteMenuComponents = ({
|
|
114
142
|
api
|
|
115
143
|
}) => [{
|
|
@@ -129,15 +157,14 @@ export const getPasteMenuComponents = ({
|
|
|
129
157
|
return !((_pluginState$showLega = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showLegacyOptions) !== null && _pluginState$showLega !== void 0 ? _pluginState$showLega : false);
|
|
130
158
|
},
|
|
131
159
|
component: props => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
const hasVisibleAiActions = getVisibleKeys(aiMenuItems, ['menu-item']).length > 0;
|
|
160
|
+
const hasVisibleAiActions = getHasVisibleAiActions(api);
|
|
161
|
+
if (!hasVisibleAiActions) {
|
|
162
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
163
|
+
padding: "space.050"
|
|
164
|
+
}, props.children);
|
|
165
|
+
}
|
|
139
166
|
return /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, {
|
|
140
|
-
hasSeparator:
|
|
167
|
+
hasSeparator: true
|
|
141
168
|
}, props.children);
|
|
142
169
|
}
|
|
143
170
|
}, {
|
|
@@ -148,7 +175,12 @@ export const getPasteMenuComponents = ({
|
|
|
148
175
|
key: PASTE_MENU_SECTION.key,
|
|
149
176
|
rank: PASTE_MENU_SECTION_RANK[PASTE_NESTED_MENU.key]
|
|
150
177
|
}],
|
|
151
|
-
component: props =>
|
|
178
|
+
component: props => {
|
|
179
|
+
const hasVisibleAiActions = getHasVisibleAiActions(api);
|
|
180
|
+
return /*#__PURE__*/React.createElement(PasteOptionsNestedMenu, {
|
|
181
|
+
hasVisibleAiActions: hasVisibleAiActions
|
|
182
|
+
}, props.children);
|
|
183
|
+
}
|
|
152
184
|
}, {
|
|
153
185
|
type: PASTE_MENU_NESTED_SECTION.type,
|
|
154
186
|
key: PASTE_MENU_NESTED_SECTION.key,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* PasteOptionsDropdownButton.tsx generated by @compiled/babel-plugin v0.39.1 */
|
|
2
|
+
import { ax, ix } from "@compiled/react/runtime";
|
|
3
|
+
import React, { useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
|
6
|
+
|
|
7
|
+
import DropdownMenu from '@atlaskit/dropdown-menu';
|
|
8
|
+
import { ToolbarButton, ToolbarTooltip } from '@atlaskit/editor-toolbar';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A compact dropdown button for paste options, styled like floating toolbar buttons.
|
|
12
|
+
* Renders as a ToolbarButton with an icon and dropdown caret that opens a
|
|
13
|
+
* dropdown menu below. Used when AI actions are not visible and the paste
|
|
14
|
+
* options menu is the only content.
|
|
15
|
+
*/
|
|
16
|
+
export const PasteOptionsDropdownButton = ({
|
|
17
|
+
children,
|
|
18
|
+
elemBefore,
|
|
19
|
+
elemAfter,
|
|
20
|
+
label,
|
|
21
|
+
testId,
|
|
22
|
+
tooltipContent
|
|
23
|
+
}) => {
|
|
24
|
+
const trigger = useCallback(triggerProps => {
|
|
25
|
+
const button = /*#__PURE__*/React.createElement(ToolbarButton, {
|
|
26
|
+
ref: triggerProps.triggerRef,
|
|
27
|
+
isSelected: triggerProps.isSelected,
|
|
28
|
+
"aria-expanded": triggerProps['aria-expanded'],
|
|
29
|
+
"aria-haspopup": triggerProps['aria-haspopup'],
|
|
30
|
+
onClick: triggerProps.onClick,
|
|
31
|
+
testId: testId,
|
|
32
|
+
iconBefore: elemBefore,
|
|
33
|
+
label: label
|
|
34
|
+
}, elemAfter);
|
|
35
|
+
if (tooltipContent) {
|
|
36
|
+
return /*#__PURE__*/React.createElement(ToolbarTooltip, {
|
|
37
|
+
content: tooltipContent,
|
|
38
|
+
position: "top"
|
|
39
|
+
}, button);
|
|
40
|
+
}
|
|
41
|
+
return button;
|
|
42
|
+
}, [testId, elemBefore, elemAfter, label, tooltipContent]);
|
|
43
|
+
return /*#__PURE__*/React.createElement(DropdownMenu, {
|
|
44
|
+
placement: "bottom-start",
|
|
45
|
+
trigger: trigger
|
|
46
|
+
}, children);
|
|
47
|
+
};
|
|
@@ -150,7 +150,7 @@ export function getMarkdownSlice(text, schema, selection) {
|
|
|
150
150
|
for (var i = 0; i < textSplitByCodeBlock.length; i++) {
|
|
151
151
|
if (i % 2 === 0) {
|
|
152
152
|
// Ignored via go/ees005
|
|
153
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
153
|
+
// eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace -- Ignored via go/ees017 (to be fixed)
|
|
154
154
|
textSplitByCodeBlock[i] = textSplitByCodeBlock[i].replace(/\\/g, '\\\\');
|
|
155
155
|
}
|
|
156
156
|
}
|