@atlaskit/editor-plugin-paste-options-toolbar 9.1.6 → 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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # @atlaskit/editor-plugin-paste-options-toolbar
2
2
 
3
+ ## 9.1.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [`3b007c601e102`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3b007c601e102) -
8
+ EDITOR-6092 Remove 100 character limit on legacy paste actions for new editor AI paste actions
9
+ menu. Also redesigns the legacy paste actions to more closely resemble the legacy paste floating
10
+ toolbar when there are no AI actions present.
11
+ - Updated dependencies
12
+
3
13
  ## 9.1.6
4
14
 
5
15
  ### Patch Changes
@@ -9,6 +9,7 @@ exports.PasteActionsMenu = void 0;
9
9
  exports.findBlockAncestorDOM = findBlockAncestorDOM;
10
10
  exports.getTargetElement = getTargetElement;
11
11
  exports.getVisualEndBottom = getVisualEndBottom;
12
+ exports.onInlinePositionCalculated = onInlinePositionCalculated;
12
13
  exports.onPositionCalculated = onPositionCalculated;
13
14
  exports.resolveTableAfterPos = resolveTableAfterPos;
14
15
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
@@ -118,6 +119,35 @@ function findBlockAncestorDOM(editorView, pos) {
118
119
  return null;
119
120
  }
120
121
 
122
+ /**
123
+ * Positions the paste menu inline, immediately to the right of the cursor
124
+ * at the paste end position, vertically centered with the line.
125
+ * Used for short pastes without AI actions.
126
+ */
127
+ function onInlinePositionCalculated(editorView, pasteEndPos, targetElement, popupContentRef) {
128
+ return function (position) {
129
+ var _popupContentRef$curr, _popupContentRef$curr2, _position$top, _position$left;
130
+ var endCoords = editorView.coordsAtPos(pasteEndPos);
131
+ var targetRect = targetElement.getBoundingClientRect();
132
+
133
+ // Vertical: center the menu with the line at the paste end position.
134
+ var lineHeight = endCoords.bottom - endCoords.top;
135
+ var lineMidpoint = endCoords.top + lineHeight / 2;
136
+ var 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;
137
+ var menuTop = lineMidpoint - menuHeight / 2;
138
+ var topDelta = menuTop - (targetRect.top + targetRect.height);
139
+ var adjustedTop = ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta;
140
+
141
+ // Horizontal: position to the right of the cursor
142
+ var leftDelta = endCoords.right - targetRect.right;
143
+ var adjustedLeft = ((_position$left = position.left) !== null && _position$left !== void 0 ? _position$left : 0) + leftDelta;
144
+ return _objectSpread(_objectSpread({}, position), {}, {
145
+ top: adjustedTop,
146
+ left: adjustedLeft
147
+ });
148
+ };
149
+ }
150
+
121
151
  /**
122
152
  * Adjusts the position of the paste menu so that:
123
153
  *
@@ -148,7 +178,7 @@ function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, targetElem
148
178
  var tableAfterPos = resolveTableAfterPos(editorView, pasteEndPos);
149
179
  var blockAncestorDOM = findBlockAncestorDOM(editorView, pasteStartPos);
150
180
  return function (position) {
151
- var _position$top;
181
+ var _position$top2;
152
182
  var startCoords = editorView.coordsAtPos(pasteStartPos);
153
183
  var endBottom = getVisualEndBottom(editorView, pasteEndPos, tableAfterPos);
154
184
  var targetRect = targetElement.getBoundingClientRect();
@@ -158,7 +188,7 @@ function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, targetElem
158
188
  // We shift it up so it aligns with the paste start position.
159
189
  // Both coordinates are in viewport space, so the delta is offset-parent agnostic.
160
190
  var topDelta = startCoords.top - (targetRect.top + targetRect.height);
161
- var adjustedTop = ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta;
191
+ var adjustedTop = ((_position$top2 = position.top) !== null && _position$top2 !== void 0 ? _position$top2 : 0) + topDelta;
162
192
 
163
193
  // Sticky-top: clamp to the scroll container's top edge when the paste
164
194
  // start has scrolled above the visible area, but only while some pasted
@@ -180,13 +210,13 @@ function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, targetElem
180
210
  // re-anchoring to its right edge.
181
211
  var adjustedLeft = position.left;
182
212
  if (blockAncestorDOM && blockAncestorDOM !== targetElement) {
183
- var _position$left;
213
+ var _position$left2;
184
214
  var blockRect = blockAncestorDOM.getBoundingClientRect();
185
215
  // Shift left by the difference between the block's right edge and
186
216
  // the inline target's right edge. This mirrors what alignX="end"
187
217
  // would have computed if the target were the block element.
188
218
  var leftDelta = blockRect.right - targetRect.right;
189
- adjustedLeft = ((_position$left = position.left) !== null && _position$left !== void 0 ? _position$left : 0) + leftDelta;
219
+ adjustedLeft = ((_position$left2 = position.left) !== null && _position$left2 !== void 0 ? _position$left2 : 0) + leftDelta;
190
220
  }
191
221
  return _objectSpread(_objectSpread({}, position), {}, {
192
222
  top: adjustedTop,
@@ -210,8 +240,8 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
210
240
  }),
211
241
  lastContentPasted = _useSharedPluginState.lastContentPasted;
212
242
  var prevShowToolbarRef = (0, _react.useRef)(false);
243
+ var popupContentRef = (0, _react.useRef)(null);
213
244
  (0, _react.useEffect)(function () {
214
- var _lastContentPasted$te, _lastContentPasted$te2;
215
245
  if (!lastContentPasted) {
216
246
  (0, _commands.hideToolbar)()(editorView.state, editorView.dispatch);
217
247
  return;
@@ -234,7 +264,7 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
234
264
  pasteAncestorNodeNames.push($pos.node(depth).type.name);
235
265
  }
236
266
  }
237
- var legacyVisible = (0, _toolbar2.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;
267
+ var legacyVisible = (0, _toolbar2.isToolbarVisible)(editorView.state, lastContentPasted);
238
268
  (0, _commands.showToolbar)(lastContentPasted, selectedOption, legacyVisible, pasteAncestorNodeNames)(editorView.state, editorView.dispatch);
239
269
  }, [lastContentPasted, editorView]);
240
270
  var _useSharedPluginState2 = (0, _hooks.useSharedPluginStateWithSelector)(api, ['pasteOptionsToolbarPlugin'], function (states) {
@@ -298,16 +328,36 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
298
328
  var effectiveScrollableElement = overflowScrollParent || scrollableElement;
299
329
  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 : [];
300
330
  var anyComponentVisible = (0, _hasVisibleButton.hasVisibleButton)(pasteMenuComponents);
331
+
332
+ // Two positioning modes:
333
+ // 1. Inline: no AI actions visible — menu appears to the right of the cursor,
334
+ // vertically centered with the text line.
335
+ // 2. Block-anchored: AI actions are visible — menu appears at the right edge
336
+ // of the content block, aligned with paste start.
337
+ var hasVisibleAiActions = (0, _hasVisibleButton.getVisibleKeys)(
338
+ // eslint-disable-next-line @atlassian/perf-linting/no-expensive-computations-in-render -- pasteMenuComponents changes by reference each render; filter is small (< 10 items)
339
+ pasteMenuComponents.filter(function (c) {
340
+ var _c$parents;
341
+ return c.type === 'menu-item' && ((_c$parents = c.parents) === null || _c$parents === void 0 ? void 0 : _c$parents.some(function (p) {
342
+ return p.key === _toolbar.AI_PASTE_MENU_SECTION.key;
343
+ }));
344
+ }), ['menu-item']).length > 0;
345
+ var useInlinePosition = !hasVisibleAiActions;
301
346
  if (!isToolbarShown) {
302
347
  return null;
303
348
  }
304
349
  if (!anyComponentVisible) {
305
350
  return null;
306
351
  }
307
- var target = getTargetElement(editorView, pasteStartPos);
352
+ var target = getTargetElement(editorView, useInlinePosition ? pasteEndPos : pasteStartPos);
308
353
  if (!target) {
309
354
  return null;
310
355
  }
356
+
357
+ // Choose positioning strategy based on whether the menu appears inline
358
+ // (right of cursor for short pastes) or anchored to the block ancestor
359
+ // (right side of content area for longer pastes / AI actions).
360
+ var positionCalculator = useInlinePosition ? onInlinePositionCalculated(editorView, pasteEndPos, target, popupContentRef) : onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement);
311
361
  return /*#__PURE__*/_react.default.createElement(PopupWithListeners, {
312
362
  target: target,
313
363
  mountTo: mountTo,
@@ -316,10 +366,10 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
316
366
  minPopupMargin: _constants.PASTE_MENU_GAP_HORIZONTAL,
317
367
  zIndex: _editorSharedStyles.akEditorFloatingPanelZIndex,
318
368
  alignX: "end",
319
- alignY: "bottom"
369
+ alignY: useInlinePosition ? 'top' : 'bottom'
320
370
  /* eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) */,
321
371
  offset: [_constants.PASTE_MENU_GAP_HORIZONTAL, 0],
322
- onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement),
372
+ onPositionCalculated: positionCalculator,
323
373
  handleClickOutside: handleClickOutside,
324
374
  handleEscapeKeydown: handleDismiss
325
375
  }, /*#__PURE__*/_react.default.createElement(_toolbar.EditorToolbarProvider, {
@@ -330,6 +380,7 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
330
380
  }, /*#__PURE__*/_react.default.createElement(_PasteActionsMenuContent.PasteActionsMenuContent, {
331
381
  onMouseDown: preventEditorFocusLoss,
332
382
  onMouseEnter: handleMouseEnter,
333
- components: pasteMenuComponents
383
+ components: pasteMenuComponents,
384
+ contentRef: popupContentRef
334
385
  }))));
335
386
  };
@@ -9,34 +9,37 @@ exports.PasteActionsMenuContent = void 0;
9
9
  require("./PasteActionsMenuContent.compiled.css");
10
10
  var _runtime = require("@compiled/react/runtime");
11
11
  var _react = _interopRequireWildcard(require("react"));
12
- var _reactIntlNext = require("react-intl-next");
13
- var _messages = require("@atlaskit/editor-common/messages");
12
+ var _toolbar = require("@atlaskit/editor-common/toolbar");
14
13
  var _uiReact = require("@atlaskit/editor-common/ui-react");
15
- var _editorToolbar = require("@atlaskit/editor-toolbar");
16
14
  var _editorUiControlModel = require("@atlaskit/editor-ui-control-model");
17
15
  var _compiled = require("@atlaskit/primitives/compiled");
18
16
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
19
17
  var styles = {
20
18
  container: "_2rko12b0 _bfhk1bhr _16qs130s"
21
19
  };
20
+ var pasteMenuSurface = {
21
+ type: _toolbar.PASTE_MENU.type,
22
+ key: _toolbar.PASTE_MENU.key
23
+ };
22
24
  var PasteActionsMenuContent = exports.PasteActionsMenuContent = function PasteActionsMenuContent(_ref) {
23
25
  var onMouseDown = _ref.onMouseDown,
24
26
  onMouseEnter = _ref.onMouseEnter,
25
- components = _ref.components;
27
+ components = _ref.components,
28
+ contentRef = _ref.contentRef;
26
29
  var setOutsideClickTargetRef = (0, _react.useContext)(_uiReact.OutsideClickTargetRefContext);
27
- var intl = (0, _reactIntlNext.useIntl)();
30
+ var mergedRef = (0, _react.useCallback)(function (node) {
31
+ setOutsideClickTargetRef === null || setOutsideClickTargetRef === void 0 || setOutsideClickTargetRef(node);
32
+ if (contentRef) {
33
+ contentRef.current = node;
34
+ }
35
+ }, [setOutsideClickTargetRef, contentRef]);
28
36
  return /*#__PURE__*/_react.default.createElement(_compiled.Box, {
29
- ref: setOutsideClickTargetRef,
37
+ ref: mergedRef,
30
38
  xcss: styles.container,
31
39
  onMouseDown: onMouseDown,
32
40
  onMouseEnter: onMouseEnter
33
- }, /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownItemSection, {
34
- title: intl.formatMessage(_messages.pasteOptionsToolbarMessages.pasteMenuActionsTitle)
35
41
  }, /*#__PURE__*/_react.default.createElement(_editorUiControlModel.SurfaceRenderer, {
36
- surface: {
37
- type: 'menu',
38
- key: 'paste-menu'
39
- },
42
+ surface: pasteMenuSurface,
40
43
  components: components
41
- })));
44
+ }));
42
45
  };
@@ -16,12 +16,14 @@ var _hooks = require("@atlaskit/editor-common/hooks");
16
16
  var _messages = require("@atlaskit/editor-common/messages");
17
17
  var _toolbar = require("@atlaskit/editor-common/toolbar");
18
18
  var _editorToolbar = require("@atlaskit/editor-toolbar");
19
+ var _chevronDown = _interopRequireDefault(require("@atlaskit/icon/core/chevron-down"));
19
20
  var _chevronRight = _interopRequireDefault(require("@atlaskit/icon/core/chevron-right"));
20
21
  var _clipboard = _interopRequireDefault(require("@atlaskit/icon/core/clipboard"));
21
22
  var _compiled = require("@atlaskit/primitives/compiled");
22
23
  var _commands = require("../../editor-commands/commands");
23
24
  var _types = require("../../types/types");
24
25
  var _hasVisibleButton = require("./hasVisibleButton");
26
+ var _PasteOptionsDropdownButton = require("./PasteOptionsDropdownButton");
25
27
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
26
28
  var nestedMenuStyles = {
27
29
  narrowSection: "_10gv1lit"
@@ -100,21 +102,49 @@ var PasteMenuItem = function PasteMenuItem(_ref) {
100
102
  }, displayLabel);
101
103
  };
102
104
  var PasteOptionsNestedMenu = function PasteOptionsNestedMenu(_ref2) {
103
- var children = _ref2.children;
105
+ var children = _ref2.children,
106
+ hasVisibleAiActions = _ref2.hasVisibleAiActions;
104
107
  var intl = (0, _reactIntlNext.useIntl)();
108
+ var label = intl.formatMessage(_messages.pasteOptionsToolbarMessages.pasteMenuActionsPasteAs);
109
+ if (!hasVisibleAiActions) {
110
+ return /*#__PURE__*/_react.default.createElement(_PasteOptionsDropdownButton.PasteOptionsDropdownButton, {
111
+ elemBefore: /*#__PURE__*/_react.default.createElement(_clipboard.default, {
112
+ size: "small",
113
+ label: ""
114
+ }),
115
+ elemAfter: /*#__PURE__*/_react.default.createElement(_chevronDown.default, {
116
+ size: "small",
117
+ label: ""
118
+ }),
119
+ label: label,
120
+ testId: "paste-options-nested-menu",
121
+ tooltipContent: label
122
+ }, children);
123
+ }
105
124
  return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarNestedDropdownMenu, {
106
125
  elemBefore: /*#__PURE__*/_react.default.createElement(_clipboard.default, {
107
126
  size: "small",
108
- label: intl.formatMessage(_messages.pasteOptionsToolbarMessages.pasteOptions)
127
+ label: label
109
128
  }),
110
129
  elemAfter: /*#__PURE__*/_react.default.createElement(_chevronRight.default, {
111
130
  size: "small",
112
- label: intl.formatMessage(_messages.pasteOptionsToolbarMessages.pasteOptions)
131
+ label: ""
113
132
  }),
114
133
  testId: "paste-options-nested-menu",
115
- text: intl.formatMessage(_messages.pasteOptionsToolbarMessages.pasteOptions)
134
+ text: label
116
135
  }, children);
117
136
  };
137
+ var getHasVisibleAiActions = function getHasVisibleAiActions(api) {
138
+ var _api$uiControlRegistr, _api$uiControlRegistr2;
139
+ var allComponents = (_api$uiControlRegistr = api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(_toolbar.PASTE_MENU.key)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
140
+ var aiMenuItems = allComponents.filter(function (c) {
141
+ var _c$parents;
142
+ return c.type === 'menu-item' && ((_c$parents = c.parents) === null || _c$parents === void 0 ? void 0 : _c$parents.some(function (p) {
143
+ return p.key === _toolbar.AI_PASTE_MENU_SECTION.key;
144
+ }));
145
+ });
146
+ return (0, _hasVisibleButton.getVisibleKeys)(aiMenuItems, ['menu-item']).length > 0;
147
+ };
118
148
  var getPasteMenuComponents = exports.getPasteMenuComponents = function getPasteMenuComponents(_ref3) {
119
149
  var api = _ref3.api;
120
150
  return [{
@@ -134,17 +164,14 @@ var getPasteMenuComponents = exports.getPasteMenuComponents = function getPasteM
134
164
  return !((_pluginState$showLega = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showLegacyOptions) !== null && _pluginState$showLega !== void 0 ? _pluginState$showLega : false);
135
165
  },
136
166
  component: function component(props) {
137
- var _api$uiControlRegistr, _api$uiControlRegistr2;
138
- var allComponents = (_api$uiControlRegistr = api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(_toolbar.PASTE_MENU.key)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
139
- var aiMenuItems = allComponents.filter(function (c) {
140
- var _c$parents;
141
- return c.type === 'menu-item' && ((_c$parents = c.parents) === null || _c$parents === void 0 ? void 0 : _c$parents.some(function (p) {
142
- return p.key === _toolbar.AI_PASTE_MENU_SECTION.key;
143
- }));
144
- });
145
- var hasVisibleAiActions = (0, _hasVisibleButton.getVisibleKeys)(aiMenuItems, ['menu-item']).length > 0;
167
+ var hasVisibleAiActions = getHasVisibleAiActions(api);
168
+ if (!hasVisibleAiActions) {
169
+ return /*#__PURE__*/_react.default.createElement(_compiled.Box, {
170
+ padding: "space.050"
171
+ }, props.children);
172
+ }
146
173
  return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownItemSection, {
147
- hasSeparator: hasVisibleAiActions
174
+ hasSeparator: true
148
175
  }, props.children);
149
176
  }
150
177
  }, {
@@ -156,7 +183,10 @@ var getPasteMenuComponents = exports.getPasteMenuComponents = function getPasteM
156
183
  rank: _toolbar.PASTE_MENU_SECTION_RANK[_toolbar.PASTE_NESTED_MENU.key]
157
184
  }],
158
185
  component: function component(props) {
159
- return /*#__PURE__*/_react.default.createElement(PasteOptionsNestedMenu, null, props.children);
186
+ var hasVisibleAiActions = getHasVisibleAiActions(api);
187
+ return /*#__PURE__*/_react.default.createElement(PasteOptionsNestedMenu, {
188
+ hasVisibleAiActions: hasVisibleAiActions
189
+ }, props.children);
160
190
  }
161
191
  }, {
162
192
  type: _toolbar.PASTE_MENU_NESTED_SECTION.type,
@@ -0,0 +1,52 @@
1
+ /* PasteOptionsDropdownButton.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ "use strict";
3
+
4
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
5
+ var _typeof = require("@babel/runtime/helpers/typeof");
6
+ Object.defineProperty(exports, "__esModule", {
7
+ value: true
8
+ });
9
+ exports.PasteOptionsDropdownButton = void 0;
10
+ var _runtime = require("@compiled/react/runtime");
11
+ var _react = _interopRequireWildcard(require("react"));
12
+ var _dropdownMenu = _interopRequireDefault(require("@atlaskit/dropdown-menu"));
13
+ var _editorToolbar = require("@atlaskit/editor-toolbar");
14
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
15
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
16
+ /**
17
+ * A compact dropdown button for paste options, styled like floating toolbar buttons.
18
+ * Renders as a ToolbarButton with an icon and dropdown caret that opens a
19
+ * dropdown menu below. Used when AI actions are not visible and the paste
20
+ * options menu is the only content.
21
+ */
22
+ var PasteOptionsDropdownButton = exports.PasteOptionsDropdownButton = function PasteOptionsDropdownButton(_ref) {
23
+ var children = _ref.children,
24
+ elemBefore = _ref.elemBefore,
25
+ elemAfter = _ref.elemAfter,
26
+ label = _ref.label,
27
+ testId = _ref.testId,
28
+ tooltipContent = _ref.tooltipContent;
29
+ var trigger = (0, _react.useCallback)(function (triggerProps) {
30
+ var button = /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarButton, {
31
+ ref: triggerProps.triggerRef,
32
+ isSelected: triggerProps.isSelected,
33
+ "aria-expanded": triggerProps['aria-expanded'],
34
+ "aria-haspopup": triggerProps['aria-haspopup'],
35
+ onClick: triggerProps.onClick,
36
+ testId: testId,
37
+ iconBefore: elemBefore,
38
+ label: label
39
+ }, elemAfter);
40
+ if (tooltipContent) {
41
+ return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarTooltip, {
42
+ content: tooltipContent,
43
+ position: "top"
44
+ }, button);
45
+ }
46
+ return button;
47
+ }, [testId, elemBefore, elemAfter, label, tooltipContent]);
48
+ return /*#__PURE__*/_react.default.createElement(_dropdownMenu.default, {
49
+ placement: "bottom-start",
50
+ trigger: trigger
51
+ }, children);
52
+ };
@@ -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';
@@ -101,6 +101,36 @@ export function findBlockAncestorDOM(editorView, pos) {
101
101
  return null;
102
102
  }
103
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
+
104
134
  /**
105
135
  * Adjusts the position of the paste menu so that:
106
136
  *
@@ -131,7 +161,7 @@ export function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, tar
131
161
  const tableAfterPos = resolveTableAfterPos(editorView, pasteEndPos);
132
162
  const blockAncestorDOM = findBlockAncestorDOM(editorView, pasteStartPos);
133
163
  return position => {
134
- var _position$top;
164
+ var _position$top2;
135
165
  const startCoords = editorView.coordsAtPos(pasteStartPos);
136
166
  const endBottom = getVisualEndBottom(editorView, pasteEndPos, tableAfterPos);
137
167
  const targetRect = targetElement.getBoundingClientRect();
@@ -141,7 +171,7 @@ export function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, tar
141
171
  // We shift it up so it aligns with the paste start position.
142
172
  // Both coordinates are in viewport space, so the delta is offset-parent agnostic.
143
173
  const topDelta = startCoords.top - (targetRect.top + targetRect.height);
144
- 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;
145
175
 
146
176
  // Sticky-top: clamp to the scroll container's top edge when the paste
147
177
  // start has scrolled above the visible area, but only while some pasted
@@ -163,13 +193,13 @@ export function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, tar
163
193
  // re-anchoring to its right edge.
164
194
  let adjustedLeft = position.left;
165
195
  if (blockAncestorDOM && blockAncestorDOM !== targetElement) {
166
- var _position$left;
196
+ var _position$left2;
167
197
  const blockRect = blockAncestorDOM.getBoundingClientRect();
168
198
  // Shift left by the difference between the block's right edge and
169
199
  // the inline target's right edge. This mirrors what alignX="end"
170
200
  // would have computed if the target were the block element.
171
201
  const leftDelta = blockRect.right - targetRect.right;
172
- adjustedLeft = ((_position$left = position.left) !== null && _position$left !== void 0 ? _position$left : 0) + leftDelta;
202
+ adjustedLeft = ((_position$left2 = position.left) !== null && _position$left2 !== void 0 ? _position$left2 : 0) + leftDelta;
173
203
  }
174
204
  return {
175
205
  ...position,
@@ -196,8 +226,8 @@ export const PasteActionsMenu = ({
196
226
  };
197
227
  });
198
228
  const prevShowToolbarRef = useRef(false);
229
+ const popupContentRef = useRef(null);
199
230
  useEffect(() => {
200
- var _lastContentPasted$te, _lastContentPasted$te2;
201
231
  if (!lastContentPasted) {
202
232
  hideToolbar()(editorView.state, editorView.dispatch);
203
233
  return;
@@ -220,7 +250,7 @@ export const PasteActionsMenu = ({
220
250
  pasteAncestorNodeNames.push($pos.node(depth).type.name);
221
251
  }
222
252
  }
223
- 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);
224
254
  showToolbar(lastContentPasted, selectedOption, legacyVisible, pasteAncestorNodeNames)(editorView.state, editorView.dispatch);
225
255
  }, [lastContentPasted, editorView]);
226
256
  const {
@@ -285,16 +315,34 @@ export const PasteActionsMenu = ({
285
315
  const effectiveScrollableElement = overflowScrollParent || scrollableElement;
286
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 : [];
287
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;
288
331
  if (!isToolbarShown) {
289
332
  return null;
290
333
  }
291
334
  if (!anyComponentVisible) {
292
335
  return null;
293
336
  }
294
- const target = getTargetElement(editorView, pasteStartPos);
337
+ const target = getTargetElement(editorView, useInlinePosition ? pasteEndPos : pasteStartPos);
295
338
  if (!target) {
296
339
  return null;
297
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);
298
346
  return /*#__PURE__*/React.createElement(PopupWithListeners, {
299
347
  target: target,
300
348
  mountTo: mountTo,
@@ -303,10 +351,10 @@ export const PasteActionsMenu = ({
303
351
  minPopupMargin: PASTE_MENU_GAP_HORIZONTAL,
304
352
  zIndex: akEditorFloatingPanelZIndex,
305
353
  alignX: "end",
306
- alignY: "bottom"
354
+ alignY: useInlinePosition ? 'top' : 'bottom'
307
355
  /* eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) */,
308
356
  offset: [PASTE_MENU_GAP_HORIZONTAL, 0],
309
- onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement),
357
+ onPositionCalculated: positionCalculator,
310
358
  handleClickOutside: handleClickOutside,
311
359
  handleEscapeKeydown: handleDismiss
312
360
  }, /*#__PURE__*/React.createElement(EditorToolbarProvider, {
@@ -317,6 +365,7 @@ export const PasteActionsMenu = ({
317
365
  }, /*#__PURE__*/React.createElement(PasteActionsMenuContent, {
318
366
  onMouseDown: preventEditorFocusLoss,
319
367
  onMouseEnter: handleMouseEnter,
320
- components: pasteMenuComponents
368
+ components: pasteMenuComponents,
369
+ contentRef: popupContentRef
321
370
  }))));
322
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
+ };
@@ -4,7 +4,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
4
4
  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
- import { EditorToolbarProvider, PASTE_MENU } from '@atlaskit/editor-common/toolbar';
7
+ import { AI_PASTE_MENU_SECTION, EditorToolbarProvider, PASTE_MENU } from '@atlaskit/editor-common/toolbar';
8
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';
@@ -104,6 +104,35 @@ export function findBlockAncestorDOM(editorView, pos) {
104
104
  return null;
105
105
  }
106
106
 
107
+ /**
108
+ * Positions the paste menu inline, immediately to the right of the cursor
109
+ * at the paste end position, vertically centered with the line.
110
+ * Used for short pastes without AI actions.
111
+ */
112
+ export function onInlinePositionCalculated(editorView, pasteEndPos, targetElement, popupContentRef) {
113
+ return function (position) {
114
+ var _popupContentRef$curr, _popupContentRef$curr2, _position$top, _position$left;
115
+ var endCoords = editorView.coordsAtPos(pasteEndPos);
116
+ var targetRect = targetElement.getBoundingClientRect();
117
+
118
+ // Vertical: center the menu with the line at the paste end position.
119
+ var lineHeight = endCoords.bottom - endCoords.top;
120
+ var lineMidpoint = endCoords.top + lineHeight / 2;
121
+ var 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;
122
+ var menuTop = lineMidpoint - menuHeight / 2;
123
+ var topDelta = menuTop - (targetRect.top + targetRect.height);
124
+ var adjustedTop = ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta;
125
+
126
+ // Horizontal: position to the right of the cursor
127
+ var leftDelta = endCoords.right - targetRect.right;
128
+ var adjustedLeft = ((_position$left = position.left) !== null && _position$left !== void 0 ? _position$left : 0) + leftDelta;
129
+ return _objectSpread(_objectSpread({}, position), {}, {
130
+ top: adjustedTop,
131
+ left: adjustedLeft
132
+ });
133
+ };
134
+ }
135
+
107
136
  /**
108
137
  * Adjusts the position of the paste menu so that:
109
138
  *
@@ -134,7 +163,7 @@ export function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, tar
134
163
  var tableAfterPos = resolveTableAfterPos(editorView, pasteEndPos);
135
164
  var blockAncestorDOM = findBlockAncestorDOM(editorView, pasteStartPos);
136
165
  return function (position) {
137
- var _position$top;
166
+ var _position$top2;
138
167
  var startCoords = editorView.coordsAtPos(pasteStartPos);
139
168
  var endBottom = getVisualEndBottom(editorView, pasteEndPos, tableAfterPos);
140
169
  var targetRect = targetElement.getBoundingClientRect();
@@ -144,7 +173,7 @@ export function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, tar
144
173
  // We shift it up so it aligns with the paste start position.
145
174
  // Both coordinates are in viewport space, so the delta is offset-parent agnostic.
146
175
  var topDelta = startCoords.top - (targetRect.top + targetRect.height);
147
- var adjustedTop = ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta;
176
+ var adjustedTop = ((_position$top2 = position.top) !== null && _position$top2 !== void 0 ? _position$top2 : 0) + topDelta;
148
177
 
149
178
  // Sticky-top: clamp to the scroll container's top edge when the paste
150
179
  // start has scrolled above the visible area, but only while some pasted
@@ -166,13 +195,13 @@ export function onPositionCalculated(editorView, pasteStartPos, pasteEndPos, tar
166
195
  // re-anchoring to its right edge.
167
196
  var adjustedLeft = position.left;
168
197
  if (blockAncestorDOM && blockAncestorDOM !== targetElement) {
169
- var _position$left;
198
+ var _position$left2;
170
199
  var blockRect = blockAncestorDOM.getBoundingClientRect();
171
200
  // Shift left by the difference between the block's right edge and
172
201
  // the inline target's right edge. This mirrors what alignX="end"
173
202
  // would have computed if the target were the block element.
174
203
  var leftDelta = blockRect.right - targetRect.right;
175
- adjustedLeft = ((_position$left = position.left) !== null && _position$left !== void 0 ? _position$left : 0) + leftDelta;
204
+ adjustedLeft = ((_position$left2 = position.left) !== null && _position$left2 !== void 0 ? _position$left2 : 0) + leftDelta;
176
205
  }
177
206
  return _objectSpread(_objectSpread({}, position), {}, {
178
207
  top: adjustedTop,
@@ -196,8 +225,8 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
196
225
  }),
197
226
  lastContentPasted = _useSharedPluginState.lastContentPasted;
198
227
  var prevShowToolbarRef = useRef(false);
228
+ var popupContentRef = useRef(null);
199
229
  useEffect(function () {
200
- var _lastContentPasted$te, _lastContentPasted$te2;
201
230
  if (!lastContentPasted) {
202
231
  hideToolbar()(editorView.state, editorView.dispatch);
203
232
  return;
@@ -220,7 +249,7 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
220
249
  pasteAncestorNodeNames.push($pos.node(depth).type.name);
221
250
  }
222
251
  }
223
- var 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;
252
+ var legacyVisible = isToolbarVisible(editorView.state, lastContentPasted);
224
253
  showToolbar(lastContentPasted, selectedOption, legacyVisible, pasteAncestorNodeNames)(editorView.state, editorView.dispatch);
225
254
  }, [lastContentPasted, editorView]);
226
255
  var _useSharedPluginState2 = useSharedPluginStateWithSelector(api, ['pasteOptionsToolbarPlugin'], function (states) {
@@ -284,16 +313,36 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
284
313
  var effectiveScrollableElement = overflowScrollParent || scrollableElement;
285
314
  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 : [];
286
315
  var anyComponentVisible = hasVisibleButton(pasteMenuComponents);
316
+
317
+ // Two positioning modes:
318
+ // 1. Inline: no AI actions visible — menu appears to the right of the cursor,
319
+ // vertically centered with the text line.
320
+ // 2. Block-anchored: AI actions are visible — menu appears at the right edge
321
+ // of the content block, aligned with paste start.
322
+ var hasVisibleAiActions = getVisibleKeys(
323
+ // eslint-disable-next-line @atlassian/perf-linting/no-expensive-computations-in-render -- pasteMenuComponents changes by reference each render; filter is small (< 10 items)
324
+ pasteMenuComponents.filter(function (c) {
325
+ var _c$parents;
326
+ return c.type === 'menu-item' && ((_c$parents = c.parents) === null || _c$parents === void 0 ? void 0 : _c$parents.some(function (p) {
327
+ return p.key === AI_PASTE_MENU_SECTION.key;
328
+ }));
329
+ }), ['menu-item']).length > 0;
330
+ var useInlinePosition = !hasVisibleAiActions;
287
331
  if (!isToolbarShown) {
288
332
  return null;
289
333
  }
290
334
  if (!anyComponentVisible) {
291
335
  return null;
292
336
  }
293
- var target = getTargetElement(editorView, pasteStartPos);
337
+ var target = getTargetElement(editorView, useInlinePosition ? pasteEndPos : pasteStartPos);
294
338
  if (!target) {
295
339
  return null;
296
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
+ var positionCalculator = useInlinePosition ? onInlinePositionCalculated(editorView, pasteEndPos, target, popupContentRef) : onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement);
297
346
  return /*#__PURE__*/React.createElement(PopupWithListeners, {
298
347
  target: target,
299
348
  mountTo: mountTo,
@@ -302,10 +351,10 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
302
351
  minPopupMargin: PASTE_MENU_GAP_HORIZONTAL,
303
352
  zIndex: akEditorFloatingPanelZIndex,
304
353
  alignX: "end",
305
- alignY: "bottom"
354
+ alignY: useInlinePosition ? 'top' : 'bottom'
306
355
  /* eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) */,
307
356
  offset: [PASTE_MENU_GAP_HORIZONTAL, 0],
308
- onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement),
357
+ onPositionCalculated: positionCalculator,
309
358
  handleClickOutside: handleClickOutside,
310
359
  handleEscapeKeydown: handleDismiss
311
360
  }, /*#__PURE__*/React.createElement(EditorToolbarProvider, {
@@ -316,6 +365,7 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
316
365
  }, /*#__PURE__*/React.createElement(PasteActionsMenuContent, {
317
366
  onMouseDown: preventEditorFocusLoss,
318
367
  onMouseEnter: handleMouseEnter,
319
- components: pasteMenuComponents
368
+ components: pasteMenuComponents,
369
+ contentRef: popupContentRef
320
370
  }))));
321
371
  };
@@ -1,34 +1,37 @@
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
  var styles = {
12
10
  container: "_2rko12b0 _bfhk1bhr _16qs130s"
13
11
  };
12
+ var pasteMenuSurface = {
13
+ type: PASTE_MENU.type,
14
+ key: PASTE_MENU.key
15
+ };
14
16
  export var PasteActionsMenuContent = function PasteActionsMenuContent(_ref) {
15
17
  var onMouseDown = _ref.onMouseDown,
16
18
  onMouseEnter = _ref.onMouseEnter,
17
- components = _ref.components;
19
+ components = _ref.components,
20
+ contentRef = _ref.contentRef;
18
21
  var setOutsideClickTargetRef = useContext(OutsideClickTargetRefContext);
19
- var intl = useIntl();
22
+ var mergedRef = useCallback(function (node) {
23
+ setOutsideClickTargetRef === null || setOutsideClickTargetRef === void 0 || setOutsideClickTargetRef(node);
24
+ if (contentRef) {
25
+ contentRef.current = node;
26
+ }
27
+ }, [setOutsideClickTargetRef, contentRef]);
20
28
  return /*#__PURE__*/React.createElement(Box, {
21
- ref: setOutsideClickTargetRef,
29
+ ref: mergedRef,
22
30
  xcss: styles.container,
23
31
  onMouseDown: onMouseDown,
24
32
  onMouseEnter: onMouseEnter
25
- }, /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, {
26
- title: intl.formatMessage(messages.pasteMenuActionsTitle)
27
33
  }, /*#__PURE__*/React.createElement(SurfaceRenderer, {
28
- surface: {
29
- type: 'menu',
30
- key: 'paste-menu'
31
- },
34
+ surface: pasteMenuSurface,
32
35
  components: components
33
- })));
36
+ }));
34
37
  };
@@ -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
  var nestedMenuStyles = {
18
20
  narrowSection: "_10gv1lit"
19
21
  };
@@ -91,21 +93,49 @@ var PasteMenuItem = function PasteMenuItem(_ref) {
91
93
  }, displayLabel);
92
94
  };
93
95
  var PasteOptionsNestedMenu = function PasteOptionsNestedMenu(_ref2) {
94
- var children = _ref2.children;
96
+ var children = _ref2.children,
97
+ hasVisibleAiActions = _ref2.hasVisibleAiActions;
95
98
  var intl = useIntl();
99
+ var label = intl.formatMessage(messages.pasteMenuActionsPasteAs);
100
+ if (!hasVisibleAiActions) {
101
+ return /*#__PURE__*/React.createElement(PasteOptionsDropdownButton, {
102
+ elemBefore: /*#__PURE__*/React.createElement(ClipboardIcon, {
103
+ size: "small",
104
+ label: ""
105
+ }),
106
+ elemAfter: /*#__PURE__*/React.createElement(ChevronDownIcon, {
107
+ size: "small",
108
+ label: ""
109
+ }),
110
+ label: label,
111
+ testId: "paste-options-nested-menu",
112
+ tooltipContent: label
113
+ }, children);
114
+ }
96
115
  return /*#__PURE__*/React.createElement(ToolbarNestedDropdownMenu, {
97
116
  elemBefore: /*#__PURE__*/React.createElement(ClipboardIcon, {
98
117
  size: "small",
99
- label: intl.formatMessage(messages.pasteOptions)
118
+ label: label
100
119
  }),
101
120
  elemAfter: /*#__PURE__*/React.createElement(ChevronRightIcon, {
102
121
  size: "small",
103
- label: intl.formatMessage(messages.pasteOptions)
122
+ label: ""
104
123
  }),
105
124
  testId: "paste-options-nested-menu",
106
- text: intl.formatMessage(messages.pasteOptions)
125
+ text: label
107
126
  }, children);
108
127
  };
128
+ var getHasVisibleAiActions = function getHasVisibleAiActions(api) {
129
+ var _api$uiControlRegistr, _api$uiControlRegistr2;
130
+ var allComponents = (_api$uiControlRegistr = api === null || api === 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 : [];
131
+ var aiMenuItems = allComponents.filter(function (c) {
132
+ var _c$parents;
133
+ return c.type === 'menu-item' && ((_c$parents = c.parents) === null || _c$parents === void 0 ? void 0 : _c$parents.some(function (p) {
134
+ return p.key === AI_PASTE_MENU_SECTION.key;
135
+ }));
136
+ });
137
+ return getVisibleKeys(aiMenuItems, ['menu-item']).length > 0;
138
+ };
109
139
  export var getPasteMenuComponents = function getPasteMenuComponents(_ref3) {
110
140
  var api = _ref3.api;
111
141
  return [{
@@ -125,17 +155,14 @@ export var getPasteMenuComponents = function getPasteMenuComponents(_ref3) {
125
155
  return !((_pluginState$showLega = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showLegacyOptions) !== null && _pluginState$showLega !== void 0 ? _pluginState$showLega : false);
126
156
  },
127
157
  component: function component(props) {
128
- var _api$uiControlRegistr, _api$uiControlRegistr2;
129
- var allComponents = (_api$uiControlRegistr = api === null || api === 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 : [];
130
- var aiMenuItems = allComponents.filter(function (c) {
131
- var _c$parents;
132
- return c.type === 'menu-item' && ((_c$parents = c.parents) === null || _c$parents === void 0 ? void 0 : _c$parents.some(function (p) {
133
- return p.key === AI_PASTE_MENU_SECTION.key;
134
- }));
135
- });
136
- var hasVisibleAiActions = getVisibleKeys(aiMenuItems, ['menu-item']).length > 0;
158
+ var hasVisibleAiActions = getHasVisibleAiActions(api);
159
+ if (!hasVisibleAiActions) {
160
+ return /*#__PURE__*/React.createElement(Box, {
161
+ padding: "space.050"
162
+ }, props.children);
163
+ }
137
164
  return /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, {
138
- hasSeparator: hasVisibleAiActions
165
+ hasSeparator: true
139
166
  }, props.children);
140
167
  }
141
168
  }, {
@@ -147,7 +174,10 @@ export var getPasteMenuComponents = function getPasteMenuComponents(_ref3) {
147
174
  rank: PASTE_MENU_SECTION_RANK[PASTE_NESTED_MENU.key]
148
175
  }],
149
176
  component: function component(props) {
150
- return /*#__PURE__*/React.createElement(PasteOptionsNestedMenu, null, props.children);
177
+ var hasVisibleAiActions = getHasVisibleAiActions(api);
178
+ return /*#__PURE__*/React.createElement(PasteOptionsNestedMenu, {
179
+ hasVisibleAiActions: hasVisibleAiActions
180
+ }, props.children);
151
181
  }
152
182
  }, {
153
183
  type: PASTE_MENU_NESTED_SECTION.type,
@@ -0,0 +1,46 @@
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 var PasteOptionsDropdownButton = function PasteOptionsDropdownButton(_ref) {
17
+ var children = _ref.children,
18
+ elemBefore = _ref.elemBefore,
19
+ elemAfter = _ref.elemAfter,
20
+ label = _ref.label,
21
+ testId = _ref.testId,
22
+ tooltipContent = _ref.tooltipContent;
23
+ var trigger = useCallback(function (triggerProps) {
24
+ var button = /*#__PURE__*/React.createElement(ToolbarButton, {
25
+ ref: triggerProps.triggerRef,
26
+ isSelected: triggerProps.isSelected,
27
+ "aria-expanded": triggerProps['aria-expanded'],
28
+ "aria-haspopup": triggerProps['aria-haspopup'],
29
+ onClick: triggerProps.onClick,
30
+ testId: testId,
31
+ iconBefore: elemBefore,
32
+ label: label
33
+ }, elemAfter);
34
+ if (tooltipContent) {
35
+ return /*#__PURE__*/React.createElement(ToolbarTooltip, {
36
+ content: tooltipContent,
37
+ position: "top"
38
+ }, button);
39
+ }
40
+ return button;
41
+ }, [testId, elemBefore, elemAfter, label, tooltipContent]);
42
+ return /*#__PURE__*/React.createElement(DropdownMenu, {
43
+ placement: "bottom-start",
44
+ trigger: trigger
45
+ }, children);
46
+ };
@@ -33,6 +33,22 @@ export declare function getVisualEndBottom(editorView: EditorView, pasteEndPos:
33
33
  * Returns `null` if no block ancestor can be resolved to a DOM element.
34
34
  */
35
35
  export declare function findBlockAncestorDOM(editorView: EditorView, pos: number): HTMLElement | null;
36
+ /**
37
+ * Positions the paste menu inline, immediately to the right of the cursor
38
+ * at the paste end position, vertically centered with the line.
39
+ * Used for short pastes without AI actions.
40
+ */
41
+ export declare function onInlinePositionCalculated(editorView: EditorView, pasteEndPos: number, targetElement: HTMLElement, popupContentRef: React.RefObject<HTMLDivElement | null>): (position: {
42
+ bottom?: number;
43
+ left?: number;
44
+ right?: number;
45
+ top?: number;
46
+ }) => {
47
+ bottom?: number;
48
+ left?: number;
49
+ right?: number;
50
+ top: number;
51
+ };
36
52
  /**
37
53
  * Adjusts the position of the paste menu so that:
38
54
  *
@@ -2,8 +2,9 @@ import React from 'react';
2
2
  import type { RegisterComponent } from '@atlaskit/editor-ui-control-model';
3
3
  interface PasteActionsMenuContentProps {
4
4
  components: RegisterComponent[];
5
+ contentRef?: React.RefObject<HTMLDivElement | null>;
5
6
  onMouseDown: (e: React.MouseEvent) => void;
6
7
  onMouseEnter: () => void;
7
8
  }
8
- export declare const PasteActionsMenuContent: ({ onMouseDown, onMouseEnter, components, }: PasteActionsMenuContentProps) => React.JSX.Element;
9
+ export declare const PasteActionsMenuContent: ({ onMouseDown, onMouseEnter, components, contentRef, }: PasteActionsMenuContentProps) => React.JSX.Element;
9
10
  export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @jsxRuntime classic
3
+ * @jsx jsx
4
+ */
5
+ import React from 'react';
6
+ /**
7
+ * A compact dropdown button for paste options, styled like floating toolbar buttons.
8
+ * Renders as a ToolbarButton with an icon and dropdown caret that opens a
9
+ * dropdown menu below. Used when AI actions are not visible and the paste
10
+ * options menu is the only content.
11
+ */
12
+ export declare const PasteOptionsDropdownButton: ({ children, elemBefore, elemAfter, label, testId, tooltipContent, }: {
13
+ children?: React.ReactNode;
14
+ elemAfter: React.ReactNode;
15
+ elemBefore: React.ReactNode;
16
+ label: string;
17
+ testId?: string;
18
+ tooltipContent?: string;
19
+ }) => JSX.Element;
@@ -33,6 +33,22 @@ export declare function getVisualEndBottom(editorView: EditorView, pasteEndPos:
33
33
  * Returns `null` if no block ancestor can be resolved to a DOM element.
34
34
  */
35
35
  export declare function findBlockAncestorDOM(editorView: EditorView, pos: number): HTMLElement | null;
36
+ /**
37
+ * Positions the paste menu inline, immediately to the right of the cursor
38
+ * at the paste end position, vertically centered with the line.
39
+ * Used for short pastes without AI actions.
40
+ */
41
+ export declare function onInlinePositionCalculated(editorView: EditorView, pasteEndPos: number, targetElement: HTMLElement, popupContentRef: React.RefObject<HTMLDivElement | null>): (position: {
42
+ bottom?: number;
43
+ left?: number;
44
+ right?: number;
45
+ top?: number;
46
+ }) => {
47
+ bottom?: number;
48
+ left?: number;
49
+ right?: number;
50
+ top: number;
51
+ };
36
52
  /**
37
53
  * Adjusts the position of the paste menu so that:
38
54
  *
@@ -2,8 +2,9 @@ import React from 'react';
2
2
  import type { RegisterComponent } from '@atlaskit/editor-ui-control-model';
3
3
  interface PasteActionsMenuContentProps {
4
4
  components: RegisterComponent[];
5
+ contentRef?: React.RefObject<HTMLDivElement | null>;
5
6
  onMouseDown: (e: React.MouseEvent) => void;
6
7
  onMouseEnter: () => void;
7
8
  }
8
- export declare const PasteActionsMenuContent: ({ onMouseDown, onMouseEnter, components, }: PasteActionsMenuContentProps) => React.JSX.Element;
9
+ export declare const PasteActionsMenuContent: ({ onMouseDown, onMouseEnter, components, contentRef, }: PasteActionsMenuContentProps) => React.JSX.Element;
9
10
  export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @jsxRuntime classic
3
+ * @jsx jsx
4
+ */
5
+ import React from 'react';
6
+ /**
7
+ * A compact dropdown button for paste options, styled like floating toolbar buttons.
8
+ * Renders as a ToolbarButton with an icon and dropdown caret that opens a
9
+ * dropdown menu below. Used when AI actions are not visible and the paste
10
+ * options menu is the only content.
11
+ */
12
+ export declare const PasteOptionsDropdownButton: ({ children, elemBefore, elemAfter, label, testId, tooltipContent, }: {
13
+ children?: React.ReactNode;
14
+ elemAfter: React.ReactNode;
15
+ elemBefore: React.ReactNode;
16
+ label: string;
17
+ testId?: string;
18
+ tooltipContent?: string;
19
+ }) => JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-paste-options-toolbar",
3
- "version": "9.1.6",
3
+ "version": "9.1.7",
4
4
  "description": "Paste options toolbar for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -30,6 +30,7 @@
30
30
  "atlaskit:src": "src/index.ts",
31
31
  "dependencies": {
32
32
  "@atlaskit/css": "^0.19.0",
33
+ "@atlaskit/dropdown-menu": "^16.8.0",
33
34
  "@atlaskit/editor-markdown-transformer": "^5.20.0",
34
35
  "@atlaskit/editor-plugin-analytics": "^8.0.0",
35
36
  "@atlaskit/editor-plugin-paste": "^9.0.0",
@@ -38,17 +39,17 @@
38
39
  "@atlaskit/editor-shared-styles": "^3.10.0",
39
40
  "@atlaskit/editor-toolbar": "^0.20.0",
40
41
  "@atlaskit/editor-ui-control-model": "^1.1.0",
41
- "@atlaskit/icon": "^33.0.0",
42
+ "@atlaskit/icon": "^33.1.0",
42
43
  "@atlaskit/platform-feature-flags": "^1.1.0",
43
- "@atlaskit/primitives": "^18.0.0",
44
- "@atlaskit/tokens": "^11.1.0",
44
+ "@atlaskit/primitives": "^18.1.0",
45
+ "@atlaskit/tokens": "^11.3.0",
45
46
  "@babel/runtime": "^7.0.0",
46
47
  "@compiled/react": "^0.20.0",
47
48
  "@emotion/react": "^11.7.1",
48
49
  "react-intl-next": "npm:react-intl@^5.18.1"
49
50
  },
50
51
  "peerDependencies": {
51
- "@atlaskit/editor-common": "^112.8.0",
52
+ "@atlaskit/editor-common": "^112.11.0",
52
53
  "react": "^18.2.0",
53
54
  "react-dom": "^18.2.0"
54
55
  },