@atlaskit/editor-plugin-paste-options-toolbar 9.0.1 → 9.0.3

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,21 @@
1
1
  # @atlaskit/editor-plugin-paste-options-toolbar
2
2
 
3
+ ## 9.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`bba4ad9eec00a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/bba4ad9eec00a) -
8
+ Ensure `invokedFrom` is passed to on-paste actions legacy paste events
9
+ - Updated dependencies
10
+
11
+ ## 9.0.2
12
+
13
+ ### Patch Changes
14
+
15
+ - [`6ebc53b2b151d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6ebc53b2b151d) -
16
+ [ux] [EDITOR-5821] updated paste menu position so that its the same as the inline comment editor'
17
+ - Updated dependencies
18
+
3
19
  ## 9.0.1
4
20
 
5
21
  ### Patch Changes
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.TEXT_HIGHLIGHT_CLASS = exports.PASTE_TOOLBAR_MENU_ID = exports.PASTE_TOOLBAR_ITEM_CLASS = exports.PASTE_TOOLBAR_CLASS = exports.PASTE_OPTIONS_TEST_ID = exports.PASTE_OPTIONS_META_ID = exports.PASTE_HIGHLIGHT_DECORATION_KEY = exports.EDITOR_WRAPPER_CLASS = void 0;
6
+ exports.TEXT_HIGHLIGHT_CLASS = exports.PASTE_TOOLBAR_MENU_ID = exports.PASTE_TOOLBAR_ITEM_CLASS = exports.PASTE_TOOLBAR_CLASS = exports.PASTE_OPTIONS_TEST_ID = exports.PASTE_OPTIONS_META_ID = exports.PASTE_MENU_GAP = exports.PASTE_HIGHLIGHT_DECORATION_KEY = exports.EDITOR_WRAPPER_CLASS = void 0;
7
7
  var PASTE_TOOLBAR_CLASS = exports.PASTE_TOOLBAR_CLASS = 'ak-editor-paste-toolbar';
8
8
  var PASTE_TOOLBAR_MENU_ID = exports.PASTE_TOOLBAR_MENU_ID = 'ak-editor-paste-toolbar-item-dropdownList';
9
9
  var TEXT_HIGHLIGHT_CLASS = exports.TEXT_HIGHLIGHT_CLASS = 'text-highlight';
@@ -11,4 +11,7 @@ var PASTE_HIGHLIGHT_DECORATION_KEY = exports.PASTE_HIGHLIGHT_DECORATION_KEY = 'p
11
11
  var PASTE_TOOLBAR_ITEM_CLASS = exports.PASTE_TOOLBAR_ITEM_CLASS = 'ak-editor-paste-toolbar-item';
12
12
  var EDITOR_WRAPPER_CLASS = exports.EDITOR_WRAPPER_CLASS = 'akEditor';
13
13
  var PASTE_OPTIONS_TEST_ID = exports.PASTE_OPTIONS_TEST_ID = 'paste-options-testid';
14
- var PASTE_OPTIONS_META_ID = exports.PASTE_OPTIONS_META_ID = 'paste-options$';
14
+ var PASTE_OPTIONS_META_ID = exports.PASTE_OPTIONS_META_ID = 'paste-options$';
15
+
16
+ // Gap (in px) between the right edge of the pasted content and the left edge of the paste menu.
17
+ var PASTE_MENU_GAP = exports.PASTE_MENU_GAP = 12;
@@ -1,10 +1,13 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  var _typeof = require("@babel/runtime/helpers/typeof");
4
5
  Object.defineProperty(exports, "__esModule", {
5
6
  value: true
6
7
  });
7
8
  exports.PasteActionsMenu = void 0;
9
+ exports.onPositionCalculated = onPositionCalculated;
10
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
8
11
  var _react = _interopRequireWildcard(require("react"));
9
12
  var _analytics = require("@atlaskit/editor-common/analytics");
10
13
  var _hooks = require("@atlaskit/editor-common/hooks");
@@ -15,16 +18,18 @@ var _utils = require("@atlaskit/editor-prosemirror/utils");
15
18
  var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
16
19
  var _editorToolbar = require("@atlaskit/editor-toolbar");
17
20
  var _commands = require("../../editor-commands/commands");
21
+ var _constants = require("../../pm-plugins/constants");
18
22
  var _types = require("../../types/types");
19
23
  var _toolbar2 = require("../toolbar");
20
24
  var _hasVisibleButton = require("./hasVisibleButton");
21
25
  var _PasteActionsMenuContent = require("./PasteActionsMenuContent");
22
26
  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); }
27
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
28
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
23
29
  var PopupWithListeners = (0, _uiReact.withReactEditorViewOuterListeners)(_ui.Popup);
24
- function getTargetElement(editorView) {
25
- var from = editorView.state.selection.from;
30
+ function getTargetElement(editorView, pos) {
26
31
  try {
27
- var domRef = (0, _utils.findDomRefAtPos)(from, editorView.domAtPos.bind(editorView));
32
+ var domRef = (0, _utils.findDomRefAtPos)(pos, editorView.domAtPos.bind(editorView));
28
33
  if (domRef instanceof HTMLElement) {
29
34
  return domRef;
30
35
  }
@@ -33,12 +38,31 @@ function getTargetElement(editorView) {
33
38
  return null;
34
39
  }
35
40
  }
36
- function getPopupOffset(dom) {
37
- if (!dom) {
38
- return [0, 20];
39
- }
40
- var rightEdge = dom.getBoundingClientRect().right;
41
- return [-(window.innerWidth - rightEdge - 50), 20];
41
+
42
+ /**
43
+ * Adjusts the vertical position of the paste menu to align with the top of the
44
+ * pasted content using the exact coordinates at the paste start position.
45
+ *
46
+ * The Popup's vertical placement may place the popup below the target element
47
+ * (alignY="bottom"). This override computes the correct top position using
48
+ * coordsAtPos for the paste start, then converts to the Popup's coordinate
49
+ * system by calculating the delta from the target element's bottom (where the
50
+ * Popup positions by default) to the paste start coordinates.
51
+ */
52
+ function onPositionCalculated(editorView, pasteStartPos, targetElement) {
53
+ return function (position) {
54
+ var _position$top;
55
+ var startCoords = editorView.coordsAtPos(pasteStartPos);
56
+ var targetRect = targetElement.getBoundingClientRect();
57
+
58
+ // The Popup places the menu at the target's bottom edge by default.
59
+ // We need to shift it up so it aligns with the paste start position.
60
+ // Both coordinates are in viewport space, so the delta is offset-parent agnostic.
61
+ var topDelta = startCoords.top - (targetRect.top + targetRect.height);
62
+ return _objectSpread(_objectSpread({}, position), {}, {
63
+ top: ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta
64
+ });
65
+ };
42
66
  }
43
67
  var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref) {
44
68
  var _api$analytics, _api$uiControlRegistr, _api$uiControlRegistr2, _api$uiControlRegistr3, _api$uiControlRegistr4;
@@ -78,13 +102,15 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
78
102
  (0, _commands.showToolbar)(lastContentPasted, selectedOption, legacyVisible, pasteAncestorNodeNames)(editorView.state, editorView.dispatch);
79
103
  }, [lastContentPasted, editorView]);
80
104
  var _useSharedPluginState2 = (0, _hooks.useSharedPluginStateWithSelector)(api, ['pasteOptionsToolbarPlugin'], function (states) {
81
- var _pluginState$showTool;
105
+ var _pluginState$showTool, _pluginState$pasteSta;
82
106
  var pluginState = states.pasteOptionsToolbarPluginState;
83
107
  return {
84
- showToolbar: (_pluginState$showTool = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showToolbar) !== null && _pluginState$showTool !== void 0 ? _pluginState$showTool : false
108
+ showToolbar: (_pluginState$showTool = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showToolbar) !== null && _pluginState$showTool !== void 0 ? _pluginState$showTool : false,
109
+ pasteStartPos: (_pluginState$pasteSta = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteStartPos) !== null && _pluginState$pasteSta !== void 0 ? _pluginState$pasteSta : 0
85
110
  };
86
111
  }),
87
- isToolbarShown = _useSharedPluginState2.showToolbar;
112
+ isToolbarShown = _useSharedPluginState2.showToolbar,
113
+ pasteStartPos = _useSharedPluginState2.pasteStartPos;
88
114
  var aiSurfaceComponents = (_api$uiControlRegistr = api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents('ai-paste-menu')) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
89
115
  var visibleAiActionKeys = (0, _hasVisibleButton.getVisibleKeys)(aiSurfaceComponents, ['button', 'menu-item']);
90
116
  (0, _react.useEffect)(function () {
@@ -132,7 +158,7 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
132
158
  if (!anyComponentVisible) {
133
159
  return null;
134
160
  }
135
- var target = getTargetElement(editorView);
161
+ var target = getTargetElement(editorView, pasteStartPos);
136
162
  if (!target) {
137
163
  return null;
138
164
  }
@@ -141,10 +167,11 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
141
167
  mountTo: mountTo,
142
168
  boundariesElement: boundariesElement,
143
169
  scrollableElement: scrollableElement,
144
- offset: getPopupOffset(target),
145
170
  zIndex: _editorSharedStyles.akEditorFloatingPanelZIndex,
146
- alignX: "right",
171
+ alignX: "end",
147
172
  alignY: "bottom",
173
+ offset: [_constants.PASTE_MENU_GAP, 0],
174
+ onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, target),
148
175
  handleClickOutside: handleClickOutside,
149
176
  handleEscapeKeydown: handleDismiss
150
177
  }, /*#__PURE__*/_react.default.createElement(_toolbar.EditorToolbarProvider, {
@@ -77,13 +77,13 @@ var PasteMenuItem = function PasteMenuItem(_ref) {
77
77
  }
78
78
  switch (pasteType) {
79
79
  case 'rich-text':
80
- (0, _commands.changeToRichTextWithAnalytics)(editorAnalyticsAPI)()(editorView.state, editorView.dispatch);
80
+ (0, _commands.changeToRichTextWithAnalytics)(editorAnalyticsAPI, 'pasteMenu')()(editorView.state, editorView.dispatch);
81
81
  break;
82
82
  case 'markdown':
83
- (0, _commands.changeToMarkdownWithAnalytics)(editorAnalyticsAPI, plaintextLength)()(editorView.state, editorView.dispatch);
83
+ (0, _commands.changeToMarkdownWithAnalytics)(editorAnalyticsAPI, plaintextLength, 'pasteMenu')()(editorView.state, editorView.dispatch);
84
84
  break;
85
85
  case 'plain-text':
86
- (0, _commands.changeToPlainTextWithAnalytics)(editorAnalyticsAPI, plaintextLength)()(editorView.state, editorView.dispatch);
86
+ (0, _commands.changeToPlainTextWithAnalytics)(editorAnalyticsAPI, plaintextLength, 'pasteMenu')()(editorView.state, editorView.dispatch);
87
87
  break;
88
88
  }
89
89
  }, [editorView, editorAnalyticsAPI, plaintextLength, pasteType]);
@@ -5,4 +5,7 @@ export const PASTE_HIGHLIGHT_DECORATION_KEY = 'paste-highlight-decoration-key';
5
5
  export const PASTE_TOOLBAR_ITEM_CLASS = 'ak-editor-paste-toolbar-item';
6
6
  export const EDITOR_WRAPPER_CLASS = 'akEditor';
7
7
  export const PASTE_OPTIONS_TEST_ID = 'paste-options-testid';
8
- export const PASTE_OPTIONS_META_ID = 'paste-options$';
8
+ export const PASTE_OPTIONS_META_ID = 'paste-options$';
9
+
10
+ // Gap (in px) between the right edge of the pasted content and the left edge of the paste menu.
11
+ export const PASTE_MENU_GAP = 12;
@@ -8,17 +8,15 @@ import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
8
8
  import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
9
9
  import { ToolbarDropdownMenuProvider } from '@atlaskit/editor-toolbar';
10
10
  import { hideToolbar, highlightContent, showToolbar } from '../../editor-commands/commands';
11
+ import { PASTE_MENU_GAP } from '../../pm-plugins/constants';
11
12
  import { ToolbarDropdownOption } from '../../types/types';
12
13
  import { isToolbarVisible } from '../toolbar';
13
14
  import { getVisibleKeys, hasVisibleButton } from './hasVisibleButton';
14
15
  import { PasteActionsMenuContent } from './PasteActionsMenuContent';
15
16
  const PopupWithListeners = withReactEditorViewOuterListeners(Popup);
16
- function getTargetElement(editorView) {
17
- const {
18
- from
19
- } = editorView.state.selection;
17
+ function getTargetElement(editorView, pos) {
20
18
  try {
21
- const domRef = findDomRefAtPos(from, editorView.domAtPos.bind(editorView));
19
+ const domRef = findDomRefAtPos(pos, editorView.domAtPos.bind(editorView));
22
20
  if (domRef instanceof HTMLElement) {
23
21
  return domRef;
24
22
  }
@@ -27,12 +25,32 @@ function getTargetElement(editorView) {
27
25
  return null;
28
26
  }
29
27
  }
30
- function getPopupOffset(dom) {
31
- if (!dom) {
32
- return [0, 20];
33
- }
34
- const rightEdge = dom.getBoundingClientRect().right;
35
- return [-(window.innerWidth - rightEdge - 50), 20];
28
+
29
+ /**
30
+ * Adjusts the vertical position of the paste menu to align with the top of the
31
+ * pasted content using the exact coordinates at the paste start position.
32
+ *
33
+ * The Popup's vertical placement may place the popup below the target element
34
+ * (alignY="bottom"). This override computes the correct top position using
35
+ * coordsAtPos for the paste start, then converts to the Popup's coordinate
36
+ * system by calculating the delta from the target element's bottom (where the
37
+ * Popup positions by default) to the paste start coordinates.
38
+ */
39
+ export function onPositionCalculated(editorView, pasteStartPos, targetElement) {
40
+ return position => {
41
+ var _position$top;
42
+ const startCoords = editorView.coordsAtPos(pasteStartPos);
43
+ const targetRect = targetElement.getBoundingClientRect();
44
+
45
+ // The Popup places the menu at the target's bottom edge by default.
46
+ // We need to shift it up so it aligns with the paste start position.
47
+ // Both coordinates are in viewport space, so the delta is offset-parent agnostic.
48
+ const topDelta = startCoords.top - (targetRect.top + targetRect.height);
49
+ return {
50
+ ...position,
51
+ top: ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta
52
+ };
53
+ };
36
54
  }
37
55
  export const PasteActionsMenu = ({
38
56
  api,
@@ -74,12 +92,14 @@ export const PasteActionsMenu = ({
74
92
  showToolbar(lastContentPasted, selectedOption, legacyVisible, pasteAncestorNodeNames)(editorView.state, editorView.dispatch);
75
93
  }, [lastContentPasted, editorView]);
76
94
  const {
77
- showToolbar: isToolbarShown
95
+ showToolbar: isToolbarShown,
96
+ pasteStartPos
78
97
  } = useSharedPluginStateWithSelector(api, ['pasteOptionsToolbarPlugin'], states => {
79
- var _pluginState$showTool;
98
+ var _pluginState$showTool, _pluginState$pasteSta;
80
99
  const pluginState = states.pasteOptionsToolbarPluginState;
81
100
  return {
82
- showToolbar: (_pluginState$showTool = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showToolbar) !== null && _pluginState$showTool !== void 0 ? _pluginState$showTool : false
101
+ showToolbar: (_pluginState$showTool = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showToolbar) !== null && _pluginState$showTool !== void 0 ? _pluginState$showTool : false,
102
+ pasteStartPos: (_pluginState$pasteSta = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteStartPos) !== null && _pluginState$pasteSta !== void 0 ? _pluginState$pasteSta : 0
83
103
  };
84
104
  });
85
105
  const aiSurfaceComponents = (_api$uiControlRegistr = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents('ai-paste-menu')) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
@@ -129,7 +149,7 @@ export const PasteActionsMenu = ({
129
149
  if (!anyComponentVisible) {
130
150
  return null;
131
151
  }
132
- const target = getTargetElement(editorView);
152
+ const target = getTargetElement(editorView, pasteStartPos);
133
153
  if (!target) {
134
154
  return null;
135
155
  }
@@ -138,10 +158,11 @@ export const PasteActionsMenu = ({
138
158
  mountTo: mountTo,
139
159
  boundariesElement: boundariesElement,
140
160
  scrollableElement: scrollableElement,
141
- offset: getPopupOffset(target),
142
161
  zIndex: akEditorFloatingPanelZIndex,
143
- alignX: "right",
162
+ alignX: "end",
144
163
  alignY: "bottom",
164
+ offset: [PASTE_MENU_GAP, 0],
165
+ onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, target),
145
166
  handleClickOutside: handleClickOutside,
146
167
  handleEscapeKeydown: handleDismiss
147
168
  }, /*#__PURE__*/React.createElement(EditorToolbarProvider, {
@@ -71,13 +71,13 @@ const PasteMenuItem = ({
71
71
  }
72
72
  switch (pasteType) {
73
73
  case 'rich-text':
74
- changeToRichTextWithAnalytics(editorAnalyticsAPI)()(editorView.state, editorView.dispatch);
74
+ changeToRichTextWithAnalytics(editorAnalyticsAPI, 'pasteMenu')()(editorView.state, editorView.dispatch);
75
75
  break;
76
76
  case 'markdown':
77
- changeToMarkdownWithAnalytics(editorAnalyticsAPI, plaintextLength)()(editorView.state, editorView.dispatch);
77
+ changeToMarkdownWithAnalytics(editorAnalyticsAPI, plaintextLength, 'pasteMenu')()(editorView.state, editorView.dispatch);
78
78
  break;
79
79
  case 'plain-text':
80
- changeToPlainTextWithAnalytics(editorAnalyticsAPI, plaintextLength)()(editorView.state, editorView.dispatch);
80
+ changeToPlainTextWithAnalytics(editorAnalyticsAPI, plaintextLength, 'pasteMenu')()(editorView.state, editorView.dispatch);
81
81
  break;
82
82
  }
83
83
  }, [editorView, editorAnalyticsAPI, plaintextLength, pasteType]);
@@ -5,4 +5,7 @@ export var PASTE_HIGHLIGHT_DECORATION_KEY = 'paste-highlight-decoration-key';
5
5
  export var PASTE_TOOLBAR_ITEM_CLASS = 'ak-editor-paste-toolbar-item';
6
6
  export var EDITOR_WRAPPER_CLASS = 'akEditor';
7
7
  export var PASTE_OPTIONS_TEST_ID = 'paste-options-testid';
8
- export var PASTE_OPTIONS_META_ID = 'paste-options$';
8
+ export var PASTE_OPTIONS_META_ID = 'paste-options$';
9
+
10
+ // Gap (in px) between the right edge of the pasted content and the left edge of the paste menu.
11
+ export var PASTE_MENU_GAP = 12;
@@ -1,3 +1,6 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
1
4
  import React, { useCallback, useEffect, useRef } from 'react';
2
5
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
3
6
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
@@ -8,15 +11,15 @@ import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
8
11
  import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
9
12
  import { ToolbarDropdownMenuProvider } from '@atlaskit/editor-toolbar';
10
13
  import { hideToolbar, highlightContent, showToolbar } from '../../editor-commands/commands';
14
+ import { PASTE_MENU_GAP } from '../../pm-plugins/constants';
11
15
  import { ToolbarDropdownOption } from '../../types/types';
12
16
  import { isToolbarVisible } from '../toolbar';
13
17
  import { getVisibleKeys, hasVisibleButton } from './hasVisibleButton';
14
18
  import { PasteActionsMenuContent } from './PasteActionsMenuContent';
15
19
  var PopupWithListeners = withReactEditorViewOuterListeners(Popup);
16
- function getTargetElement(editorView) {
17
- var from = editorView.state.selection.from;
20
+ function getTargetElement(editorView, pos) {
18
21
  try {
19
- var domRef = findDomRefAtPos(from, editorView.domAtPos.bind(editorView));
22
+ var domRef = findDomRefAtPos(pos, editorView.domAtPos.bind(editorView));
20
23
  if (domRef instanceof HTMLElement) {
21
24
  return domRef;
22
25
  }
@@ -25,12 +28,31 @@ function getTargetElement(editorView) {
25
28
  return null;
26
29
  }
27
30
  }
28
- function getPopupOffset(dom) {
29
- if (!dom) {
30
- return [0, 20];
31
- }
32
- var rightEdge = dom.getBoundingClientRect().right;
33
- return [-(window.innerWidth - rightEdge - 50), 20];
31
+
32
+ /**
33
+ * Adjusts the vertical position of the paste menu to align with the top of the
34
+ * pasted content using the exact coordinates at the paste start position.
35
+ *
36
+ * The Popup's vertical placement may place the popup below the target element
37
+ * (alignY="bottom"). This override computes the correct top position using
38
+ * coordsAtPos for the paste start, then converts to the Popup's coordinate
39
+ * system by calculating the delta from the target element's bottom (where the
40
+ * Popup positions by default) to the paste start coordinates.
41
+ */
42
+ export function onPositionCalculated(editorView, pasteStartPos, targetElement) {
43
+ return function (position) {
44
+ var _position$top;
45
+ var startCoords = editorView.coordsAtPos(pasteStartPos);
46
+ var targetRect = targetElement.getBoundingClientRect();
47
+
48
+ // The Popup places the menu at the target's bottom edge by default.
49
+ // We need to shift it up so it aligns with the paste start position.
50
+ // Both coordinates are in viewport space, so the delta is offset-parent agnostic.
51
+ var topDelta = startCoords.top - (targetRect.top + targetRect.height);
52
+ return _objectSpread(_objectSpread({}, position), {}, {
53
+ top: ((_position$top = position.top) !== null && _position$top !== void 0 ? _position$top : 0) + topDelta
54
+ });
55
+ };
34
56
  }
35
57
  export var PasteActionsMenu = function PasteActionsMenu(_ref) {
36
58
  var _api$analytics, _api$uiControlRegistr, _api$uiControlRegistr2, _api$uiControlRegistr3, _api$uiControlRegistr4;
@@ -70,13 +92,15 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
70
92
  showToolbar(lastContentPasted, selectedOption, legacyVisible, pasteAncestorNodeNames)(editorView.state, editorView.dispatch);
71
93
  }, [lastContentPasted, editorView]);
72
94
  var _useSharedPluginState2 = useSharedPluginStateWithSelector(api, ['pasteOptionsToolbarPlugin'], function (states) {
73
- var _pluginState$showTool;
95
+ var _pluginState$showTool, _pluginState$pasteSta;
74
96
  var pluginState = states.pasteOptionsToolbarPluginState;
75
97
  return {
76
- showToolbar: (_pluginState$showTool = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showToolbar) !== null && _pluginState$showTool !== void 0 ? _pluginState$showTool : false
98
+ showToolbar: (_pluginState$showTool = pluginState === null || pluginState === void 0 ? void 0 : pluginState.showToolbar) !== null && _pluginState$showTool !== void 0 ? _pluginState$showTool : false,
99
+ pasteStartPos: (_pluginState$pasteSta = pluginState === null || pluginState === void 0 ? void 0 : pluginState.pasteStartPos) !== null && _pluginState$pasteSta !== void 0 ? _pluginState$pasteSta : 0
77
100
  };
78
101
  }),
79
- isToolbarShown = _useSharedPluginState2.showToolbar;
102
+ isToolbarShown = _useSharedPluginState2.showToolbar,
103
+ pasteStartPos = _useSharedPluginState2.pasteStartPos;
80
104
  var aiSurfaceComponents = (_api$uiControlRegistr = api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents('ai-paste-menu')) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
81
105
  var visibleAiActionKeys = getVisibleKeys(aiSurfaceComponents, ['button', 'menu-item']);
82
106
  useEffect(function () {
@@ -124,7 +148,7 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
124
148
  if (!anyComponentVisible) {
125
149
  return null;
126
150
  }
127
- var target = getTargetElement(editorView);
151
+ var target = getTargetElement(editorView, pasteStartPos);
128
152
  if (!target) {
129
153
  return null;
130
154
  }
@@ -133,10 +157,11 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
133
157
  mountTo: mountTo,
134
158
  boundariesElement: boundariesElement,
135
159
  scrollableElement: scrollableElement,
136
- offset: getPopupOffset(target),
137
160
  zIndex: akEditorFloatingPanelZIndex,
138
- alignX: "right",
161
+ alignX: "end",
139
162
  alignY: "bottom",
163
+ offset: [PASTE_MENU_GAP, 0],
164
+ onPositionCalculated: onPositionCalculated(editorView, pasteStartPos, target),
140
165
  handleClickOutside: handleClickOutside,
141
166
  handleEscapeKeydown: handleDismiss
142
167
  }, /*#__PURE__*/React.createElement(EditorToolbarProvider, {
@@ -68,13 +68,13 @@ var PasteMenuItem = function PasteMenuItem(_ref) {
68
68
  }
69
69
  switch (pasteType) {
70
70
  case 'rich-text':
71
- changeToRichTextWithAnalytics(editorAnalyticsAPI)()(editorView.state, editorView.dispatch);
71
+ changeToRichTextWithAnalytics(editorAnalyticsAPI, 'pasteMenu')()(editorView.state, editorView.dispatch);
72
72
  break;
73
73
  case 'markdown':
74
- changeToMarkdownWithAnalytics(editorAnalyticsAPI, plaintextLength)()(editorView.state, editorView.dispatch);
74
+ changeToMarkdownWithAnalytics(editorAnalyticsAPI, plaintextLength, 'pasteMenu')()(editorView.state, editorView.dispatch);
75
75
  break;
76
76
  case 'plain-text':
77
- changeToPlainTextWithAnalytics(editorAnalyticsAPI, plaintextLength)()(editorView.state, editorView.dispatch);
77
+ changeToPlainTextWithAnalytics(editorAnalyticsAPI, plaintextLength, 'pasteMenu')()(editorView.state, editorView.dispatch);
78
78
  break;
79
79
  }
80
80
  }, [editorView, editorAnalyticsAPI, plaintextLength, pasteType]);
@@ -6,3 +6,4 @@ export declare const PASTE_TOOLBAR_ITEM_CLASS = "ak-editor-paste-toolbar-item";
6
6
  export declare const EDITOR_WRAPPER_CLASS = "akEditor";
7
7
  export declare const PASTE_OPTIONS_TEST_ID = "paste-options-testid";
8
8
  export declare const PASTE_OPTIONS_META_ID = "paste-options$";
9
+ export declare const PASTE_MENU_GAP = 12;
@@ -9,5 +9,26 @@ interface PasteActionsMenuProps {
9
9
  mountTo?: HTMLElement;
10
10
  scrollableElement?: HTMLElement;
11
11
  }
12
+ /**
13
+ * Adjusts the vertical position of the paste menu to align with the top of the
14
+ * pasted content using the exact coordinates at the paste start position.
15
+ *
16
+ * The Popup's vertical placement may place the popup below the target element
17
+ * (alignY="bottom"). This override computes the correct top position using
18
+ * coordsAtPos for the paste start, then converts to the Popup's coordinate
19
+ * system by calculating the delta from the target element's bottom (where the
20
+ * Popup positions by default) to the paste start coordinates.
21
+ */
22
+ export declare function onPositionCalculated(editorView: EditorView, pasteStartPos: number, targetElement: HTMLElement): (position: {
23
+ bottom?: number;
24
+ left?: number;
25
+ right?: number;
26
+ top?: number;
27
+ }) => {
28
+ bottom?: number;
29
+ left?: number;
30
+ right?: number;
31
+ top: number;
32
+ };
12
33
  export declare const PasteActionsMenu: ({ api, editorView, mountTo, boundariesElement, scrollableElement, }: PasteActionsMenuProps) => React.JSX.Element | null;
13
34
  export {};
@@ -6,5 +6,5 @@ export declare const isPasteOptionSelected: (pasteType: PasteType, selectedOptio
6
6
  interface PasteMenuComponentsConfig {
7
7
  api: ExtractInjectionAPI<PasteOptionsToolbarPlugin> | undefined;
8
8
  }
9
- export declare const getPasteMenuComponents: ({ api, }: PasteMenuComponentsConfig) => RegisterComponent[];
9
+ export declare const getPasteMenuComponents: ({ api }: PasteMenuComponentsConfig) => RegisterComponent[];
10
10
  export {};
@@ -6,3 +6,4 @@ export declare const PASTE_TOOLBAR_ITEM_CLASS = "ak-editor-paste-toolbar-item";
6
6
  export declare const EDITOR_WRAPPER_CLASS = "akEditor";
7
7
  export declare const PASTE_OPTIONS_TEST_ID = "paste-options-testid";
8
8
  export declare const PASTE_OPTIONS_META_ID = "paste-options$";
9
+ export declare const PASTE_MENU_GAP = 12;
@@ -9,5 +9,26 @@ interface PasteActionsMenuProps {
9
9
  mountTo?: HTMLElement;
10
10
  scrollableElement?: HTMLElement;
11
11
  }
12
+ /**
13
+ * Adjusts the vertical position of the paste menu to align with the top of the
14
+ * pasted content using the exact coordinates at the paste start position.
15
+ *
16
+ * The Popup's vertical placement may place the popup below the target element
17
+ * (alignY="bottom"). This override computes the correct top position using
18
+ * coordsAtPos for the paste start, then converts to the Popup's coordinate
19
+ * system by calculating the delta from the target element's bottom (where the
20
+ * Popup positions by default) to the paste start coordinates.
21
+ */
22
+ export declare function onPositionCalculated(editorView: EditorView, pasteStartPos: number, targetElement: HTMLElement): (position: {
23
+ bottom?: number;
24
+ left?: number;
25
+ right?: number;
26
+ top?: number;
27
+ }) => {
28
+ bottom?: number;
29
+ left?: number;
30
+ right?: number;
31
+ top: number;
32
+ };
12
33
  export declare const PasteActionsMenu: ({ api, editorView, mountTo, boundariesElement, scrollableElement, }: PasteActionsMenuProps) => React.JSX.Element | null;
13
34
  export {};
@@ -6,5 +6,5 @@ export declare const isPasteOptionSelected: (pasteType: PasteType, selectedOptio
6
6
  interface PasteMenuComponentsConfig {
7
7
  api: ExtractInjectionAPI<PasteOptionsToolbarPlugin> | undefined;
8
8
  }
9
- export declare const getPasteMenuComponents: ({ api, }: PasteMenuComponentsConfig) => RegisterComponent[];
9
+ export declare const getPasteMenuComponents: ({ api }: PasteMenuComponentsConfig) => RegisterComponent[];
10
10
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-paste-options-toolbar",
3
- "version": "9.0.1",
3
+ "version": "9.0.3",
4
4
  "description": "Paste options toolbar for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -41,7 +41,7 @@
41
41
  "@atlaskit/icon": "^32.0.0",
42
42
  "@atlaskit/platform-feature-flags": "^1.1.0",
43
43
  "@atlaskit/primitives": "^18.0.0",
44
- "@atlaskit/tmp-editor-statsig": "^35.10.0",
44
+ "@atlaskit/tmp-editor-statsig": "^36.1.0",
45
45
  "@atlaskit/tokens": "^11.1.0",
46
46
  "@babel/runtime": "^7.0.0",
47
47
  "@compiled/react": "^0.20.0",
@@ -49,7 +49,7 @@
49
49
  "react-intl-next": "npm:react-intl@^5.18.1"
50
50
  },
51
51
  "peerDependencies": {
52
- "@atlaskit/editor-common": "^112.0.0",
52
+ "@atlaskit/editor-common": "^112.2.0",
53
53
  "react": "^18.2.0",
54
54
  "react-dom": "^18.2.0"
55
55
  },