@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.
Files changed (23) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/cjs/pm-plugins/util/format-handlers.js +1 -1
  3. package/dist/cjs/ui/on-paste-actions-menu/PasteActionsMenu.js +133 -13
  4. package/dist/cjs/ui/on-paste-actions-menu/PasteActionsMenuContent.js +16 -13
  5. package/dist/cjs/ui/on-paste-actions-menu/PasteMenuComponents.js +45 -15
  6. package/dist/cjs/ui/on-paste-actions-menu/PasteOptionsDropdownButton.js +52 -0
  7. package/dist/es2019/pm-plugins/util/format-handlers.js +1 -1
  8. package/dist/es2019/ui/on-paste-actions-menu/PasteActionsMenu.js +131 -14
  9. package/dist/es2019/ui/on-paste-actions-menu/PasteActionsMenuContent.js +17 -14
  10. package/dist/es2019/ui/on-paste-actions-menu/PasteMenuComponents.js +45 -13
  11. package/dist/es2019/ui/on-paste-actions-menu/PasteOptionsDropdownButton.js +47 -0
  12. package/dist/esm/pm-plugins/util/format-handlers.js +1 -1
  13. package/dist/esm/ui/on-paste-actions-menu/PasteActionsMenu.js +132 -14
  14. package/dist/esm/ui/on-paste-actions-menu/PasteActionsMenuContent.js +17 -14
  15. package/dist/esm/ui/on-paste-actions-menu/PasteMenuComponents.js +45 -15
  16. package/dist/esm/ui/on-paste-actions-menu/PasteOptionsDropdownButton.js +46 -0
  17. package/dist/types/ui/on-paste-actions-menu/PasteActionsMenu.d.ts +38 -4
  18. package/dist/types/ui/on-paste-actions-menu/PasteActionsMenuContent.d.ts +2 -1
  19. package/dist/types/ui/on-paste-actions-menu/PasteOptionsDropdownButton.d.ts +19 -0
  20. package/dist/types-ts4.5/ui/on-paste-actions-menu/PasteActionsMenu.d.ts +38 -4
  21. package/dist/types-ts4.5/ui/on-paste-actions-menu/PasteActionsMenuContent.d.ts +2 -1
  22. package/dist/types-ts4.5/ui/on-paste-actions-menu/PasteOptionsDropdownButton.d.ts +19 -0
  23. 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
- * Adjusts the vertical position of the paste menu to align with the top of the
70
- * pasted content using the exact coordinates at the paste start position,
71
- * and sticks the menu to the top of the scroll container when the pasted
72
- * content scrolls above the visible area.
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$top;
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$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta;
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) && ((_lastContentPasted$te = (_lastContentPasted$te2 = lastContentPasted.text) === null || _lastContentPasted$te2 === void 0 ? void 0 : _lastContentPasted$te2.length) !== null && _lastContentPasted$te !== void 0 ? _lastContentPasted$te : 0) >= 100;
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: "bottom",
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: onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement),
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 { useIntl } from 'react-intl-next';
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 intl = useIntl();
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: setOutsideClickTargetRef,
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: intl.formatMessage(messages.pasteOptions)
122
+ label: label
104
123
  }),
105
124
  elemAfter: /*#__PURE__*/React.createElement(ChevronRightIcon, {
106
125
  size: "small",
107
- label: intl.formatMessage(messages.pasteOptions)
126
+ label: ""
108
127
  }),
109
128
  testId: "paste-options-nested-menu",
110
- text: intl.formatMessage(messages.pasteOptions)
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
- var _api$uiControlRegistr, _api$uiControlRegistr2;
133
- 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 : [];
134
- const aiMenuItems = allComponents.filter(c => {
135
- var _c$parents;
136
- 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));
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: hasVisibleAiActions
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 => /*#__PURE__*/React.createElement(PasteOptionsNestedMenu, null, props.children)
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
  }