@atlaskit/editor-plugin-block-controls 14.0.4 → 14.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 14.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [`2857e277050c6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/2857e277050c6) -
8
+ Behind the `platform-dst-top-layer` feature gate, `@atlaskit/popper/unsafe-imperative`'s
9
+ `createPopper` now renders and positions in the browser top layer via `@atlaskit/top-layer`
10
+ instead of running the Popper.js engine. Positioning and teardown are applied asynchronously, as
11
+ Popper.js' own first update is. Flag-off behaviour is unchanged.
12
+
13
+ `@atlaskit/top-layer`: the JavaScript positioning fallback no longer clears a consumer's own
14
+ inline positioning and visibility styles.
15
+
16
+ - [`d1b5e85073e48`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d1b5e85073e48) -
17
+ [ED-7926] Fix block controls hover jitter when viewing an AI Suggested Edits diff.
18
+
19
+ While a diff is on screen the block controls UI is hidden, but the hover handlers kept running:
20
+ `handleMouseOver` dispatched `showDragHandleAt` (flip-flopping the active node between
21
+ neighbouring blocks) and the right-side hover-side tracker kept dispatching
22
+ `setHoverSide`/`mouseEnter`, both causing visible jitter. Block controls now suppress both hover
23
+ paths (and hide the drag handle) while a diff is displayed, detected via the show-diff plugin's
24
+ `isDisplayingChanges` shared state (plus the `reviewing` user intent for AI suggestions with no
25
+ diff decorations), gated behind `platform_editor_diff_plugin_extended`.
26
+
27
+ To let block controls read that state without forming a circular project reference
28
+ (`block-controls -> show-diff -> expand -> block-controls`), the `toggleExpandRange` command and
29
+ `TOGGLE_EXPAND_RANGE_META_KEY` were moved from `@atlaskit/editor-plugin-expand` into
30
+ `@atlaskit/editor-common` (re-exported from expand for backwards compatibility).
31
+ `@atlaskit/editor-plugin-show-diff` no longer depends on `@atlaskit/editor-plugin-expand`, and
32
+ `@atlaskit/editor-plugin-block-controls` now takes an optional dependency on
33
+ `@atlaskit/editor-plugin-show-diff`.
34
+
35
+ - Updated dependencies
36
+
37
+ ## 14.0.5
38
+
39
+ ### Patch Changes
40
+
41
+ - Updated dependencies
42
+
3
43
  ## 14.0.4
4
44
 
5
45
  ### Patch Changes
@@ -11,6 +11,7 @@ var _selection = require("@atlaskit/editor-common/selection");
11
11
  var _toolbarFlagCheck = require("@atlaskit/editor-common/toolbar-flag-check");
12
12
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
13
13
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
14
+ var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
14
15
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
15
16
  var _domAttrName = require("../ui/utils/dom-attr-name");
16
17
  var _decorationsAnchor = require("./decorations-anchor");
@@ -82,7 +83,7 @@ var redirectParagraphToWrappedMedia = function redirectParagraphToWrappedMedia(r
82
83
  return rootElement;
83
84
  };
84
85
  var handleMouseOver = exports.handleMouseOver = function handleMouseOver(view, event, api) {
85
- var _api$blockControls, _api$editorDisabled, _api$editorViewMode, _api$blockControls$sh, _api$blockControls2, _target$classList;
86
+ var _api$blockControls, _api$editorDisabled, _api$editorViewMode, _api$userIntent, _api$showDiff$sharedS, _api$showDiff, _api$blockControls$sh, _api$blockControls2, _target$classList;
86
87
  var _ref = (api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {},
87
88
  isDragging = _ref.isDragging,
88
89
  activeNode = _ref.activeNode,
@@ -97,6 +98,15 @@ var handleMouseOver = exports.handleMouseOver = function handleMouseOver(view, e
97
98
  var isViewMode = editorViewMode === 'view';
98
99
  var toolbarFlagsEnabled = (0, _toolbarFlagCheck.areToolbarFlagsEnabled)(Boolean(api === null || api === void 0 ? void 0 : api.toolbar));
99
100
 
101
+ // EDITOR-7926: bail out while a diff is on screen (or a suggestion card is open) so hovering
102
+ // doesn't dispatch showDragHandleAt and cause the drag handle to jitter over the diff.
103
+ // No-exposure check as this runs on every mouseover.
104
+ var currentUserIntent = api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent;
105
+ var isDisplayingDiff = (_api$showDiff$sharedS = api === null || api === void 0 || (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 || (_api$showDiff = _api$showDiff.sharedState.currentState()) === null || _api$showDiff === void 0 ? void 0 : _api$showDiff.isDisplayingChanges) !== null && _api$showDiff$sharedS !== void 0 ? _api$showDiff$sharedS : false;
106
+ if ((isDisplayingDiff || currentUserIntent === 'reviewing') && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
107
+ return false;
108
+ }
109
+
100
110
  // We shouldn't be firing mouse over transactions when the editor is disabled,
101
111
  // except in view mode when right-side controls are enabled (show controls on block hover)
102
112
  var rightSideControlsEnabled = (_api$blockControls$sh = api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || (_api$blockControls2 = _api$blockControls2.sharedState.currentState()) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.rightSideControlsEnabled) !== null && _api$blockControls$sh !== void 0 ? _api$blockControls$sh : false;
@@ -281,8 +291,8 @@ var handleMouseOver = exports.handleMouseOver = function handleMouseOver(view, e
281
291
  api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(api === null || api === void 0 || (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 ? void 0 : _api$blockControls5.commands.showDragHandleAt(targetPos, anchorName, nodeType));
282
292
  }
283
293
  if ((0, _experiments.editorExperiment)('platform_editor_block_menu', true)) {
284
- var _api$userIntent;
285
- if (isMenuOpen && originalAnchorName && (api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent) === 'blockMenuOpen') {
294
+ var _api$userIntent2;
295
+ if (isMenuOpen && originalAnchorName && (api === null || api === void 0 || (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 || (_api$userIntent2 = _api$userIntent2.sharedState.currentState()) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.currentUserIntent) === 'blockMenuOpen') {
286
296
  var _api$core4, _api$blockControls6;
287
297
  api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(api === null || api === void 0 || (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 ? void 0 : _api$blockControls6.commands.toggleBlockMenu());
288
298
  }
@@ -92,6 +92,7 @@ var classifyRightMarginPosition = function classifyRightMarginPosition(view, eve
92
92
  * enabled (confluence_remix_button_right_side_block_fg); handleMouseMove returns early otherwise.
93
93
  */
94
94
  var processHoverSide = function processHoverSide(view, api) {
95
+ var _api$userIntent, _api$showDiff$sharedS, _api$showDiff;
95
96
  var event = pendingByView.get(view);
96
97
  if (!event) {
97
98
  return;
@@ -105,6 +106,15 @@ var processHoverSide = function processHoverSide(view, api) {
105
106
  var state = (0, _pmPlugin.getInteractionTrackingState)(view.state);
106
107
  var target = event.target instanceof HTMLElement ? event.target : null;
107
108
 
109
+ // EDITOR-7926: skip hover-side tracking while a diff is on screen (or a suggestion card is open)
110
+ // so setHoverSide/mouseEnter don't flip-flop and jitter the controls. No-exposure check as this
111
+ // runs on every mouse move.
112
+ var currentUserIntent = api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent;
113
+ var isDisplayingDiff = (_api$showDiff$sharedS = api === null || api === void 0 || (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 || (_api$showDiff = _api$showDiff.sharedState.currentState()) === null || _api$showDiff === void 0 ? void 0 : _api$showDiff.isDisplayingChanges) !== null && _api$showDiff$sharedS !== void 0 ? _api$showDiff$sharedS : false;
114
+ if ((isDisplayingDiff || currentUserIntent === 'reviewing') && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
115
+ return;
116
+ }
117
+
108
118
  // When hovering over block controls directly, infer side from which control we're over.
109
119
  // This is more reliable than bounds when controls are in portals outside the editor DOM.
110
120
  var rightEdgeElement = target === null || target === void 0 ? void 0 : target.closest(RIGHT_EDGE_SELECTOR);
@@ -72,6 +72,9 @@ var createVanillaButton = exports.createVanillaButton = function createVanillaBu
72
72
  button.onclick = function () {
73
73
  return handleQuickInsert(props);
74
74
  };
75
+
76
+ // Flag-on (platform-dst-top-layer), positioning is async via a React root;
77
+ // safe here as VanillaTooltip stays hidden until positioned.
75
78
  var tooltip = new _vanillaTooltip.VanillaTooltip(button, props.formatMessage(_messages.blockControlsMessages.insert), 'quick-insert-button-tooltip', 'blocks-quick-insert-tooltip');
76
79
  props.cleanupCallbacks.push(function () {
77
80
  tooltip.destroy();
@@ -63,8 +63,8 @@ var VisibilityContainer = exports.VisibilityContainer = function VisibilityConta
63
63
  controlSide = _ref.controlSide,
64
64
  forceVisibleOnMouseOut = _ref.forceVisibleOnMouseOut,
65
65
  shouldUseDisplayContents = _ref.shouldUseDisplayContents;
66
- var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['typeAhead', 'blockControls', 'editorViewMode', 'userIntent'], function (states) {
67
- var _states$typeAheadStat, _states$blockControls, _states$blockControls2, _states$blockControls3, _states$editorViewMod, _states$userIntentSta, _states$blockControls4;
66
+ var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['typeAhead', 'blockControls', 'editorViewMode', 'userIntent', 'showDiff'], function (states) {
67
+ var _states$typeAheadStat, _states$blockControls, _states$blockControls2, _states$blockControls3, _states$editorViewMod, _states$userIntentSta, _states$showDiffState, _states$blockControls4;
68
68
  return {
69
69
  isTypeAheadOpen: (_states$typeAheadStat = states.typeAheadState) === null || _states$typeAheadStat === void 0 ? void 0 : _states$typeAheadStat.isOpen,
70
70
  isEditing: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isEditing,
@@ -72,6 +72,7 @@ var VisibilityContainer = exports.VisibilityContainer = function VisibilityConta
72
72
  hoverSide: (_states$blockControls3 = states.blockControlsState) === null || _states$blockControls3 === void 0 ? void 0 : _states$blockControls3.hoverSide,
73
73
  editorViewMode: (_states$editorViewMod = states.editorViewModeState) === null || _states$editorViewMod === void 0 ? void 0 : _states$editorViewMod.mode,
74
74
  userIntent: (_states$userIntentSta = states.userIntentState) === null || _states$userIntentSta === void 0 ? void 0 : _states$userIntentSta.currentUserIntent,
75
+ isDisplayingDiff: (_states$showDiffState = states.showDiffState) === null || _states$showDiffState === void 0 ? void 0 : _states$showDiffState.isDisplayingChanges,
75
76
  rightSideControlsEnabled: (_states$blockControls4 = states.blockControlsState) === null || _states$blockControls4 === void 0 ? void 0 : _states$blockControls4.rightSideControlsEnabled
76
77
  };
77
78
  }),
@@ -81,6 +82,7 @@ var VisibilityContainer = exports.VisibilityContainer = function VisibilityConta
81
82
  hoverSide = _useSharedPluginState.hoverSide,
82
83
  editorViewMode = _useSharedPluginState.editorViewMode,
83
84
  userIntent = _useSharedPluginState.userIntent,
85
+ isDisplayingDiff = _useSharedPluginState.isDisplayingDiff,
84
86
  rightSideControlsEnabled = _useSharedPluginState.rightSideControlsEnabled;
85
87
  var isViewMode = editorViewMode === 'view';
86
88
  // rightSideControlsEnabled is the single source of truth (confluence_remix_button_right_side_block_fg from preset)
@@ -97,7 +99,9 @@ var VisibilityContainer = exports.VisibilityContainer = function VisibilityConta
97
99
  // When forceVisibleOnMouseOut is true (e.g. drag handle focused via keyboard Shift+Ctrl+H),
98
100
  // override the mouse-out condition so the control stays visible regardless of mouse position.
99
101
  var shouldHideWhenMouseOut = forceVisibleOnMouseOut ? false : hideOnMouseOut;
100
- var shouldHideImmediate = isTypeAheadOpen || isEditing || shouldHideWhenMouseOut || userIntent === 'aiStreaming' || userIntent === 'viewingDiff' && (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) || sideHidden;
102
+ var shouldHideImmediate = isTypeAheadOpen || isEditing || shouldHideWhenMouseOut || userIntent === 'aiStreaming' ||
103
+ // EDITOR-7926: hide the drag handle while a diff is on screen or a suggestion card is open.
104
+ (isDisplayingDiff || userIntent === 'reviewing') && (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) || sideHidden;
101
105
 
102
106
  // Delay hiding the right control in view mode to reduce flickering when moving from block
103
107
  // toward the right-edge button (avoids rapid show/hide as mouse crosses boundaries).
@@ -3,6 +3,7 @@ import { isMultiBlockSelection } from '@atlaskit/editor-common/selection';
3
3
  import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check';
4
4
  import { fg } from '@atlaskit/platform-feature-flags';
5
5
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
6
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
6
7
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
7
8
  import { getAnchorAttrName, getTypeNameAttrName, getTypeNameFromDom, NODE_ANCHOR_ATTR_NAME } from '../ui/utils/dom-attr-name';
8
9
  import { IGNORE_NODE_DESCENDANTS_ADVANCED_LAYOUT, IGNORE_NODES_NEXT } from './decorations-anchor';
@@ -72,7 +73,7 @@ const redirectParagraphToWrappedMedia = (rootElement, isNativeAnchorSupported) =
72
73
  return rootElement;
73
74
  };
74
75
  export const handleMouseOver = (view, event, api) => {
75
- var _api$blockControls, _api$editorDisabled, _api$editorViewMode, _api$editorViewMode$s, _api$blockControls$sh, _api$blockControls2, _api$blockControls2$s, _target$classList;
76
+ var _api$blockControls, _api$editorDisabled, _api$editorViewMode, _api$editorViewMode$s, _api$userIntent, _api$userIntent$share, _api$showDiff$sharedS, _api$showDiff, _api$showDiff$sharedS2, _api$blockControls$sh, _api$blockControls2, _api$blockControls2$s, _target$classList;
76
77
  const {
77
78
  isDragging,
78
79
  activeNode,
@@ -89,6 +90,15 @@ export const handleMouseOver = (view, event, api) => {
89
90
  const isViewMode = editorViewMode === 'view';
90
91
  const toolbarFlagsEnabled = areToolbarFlagsEnabled(Boolean(api === null || api === void 0 ? void 0 : api.toolbar));
91
92
 
93
+ // EDITOR-7926: bail out while a diff is on screen (or a suggestion card is open) so hovering
94
+ // doesn't dispatch showDragHandleAt and cause the drag handle to jitter over the diff.
95
+ // No-exposure check as this runs on every mouseover.
96
+ const currentUserIntent = api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : (_api$userIntent$share = _api$userIntent.sharedState.currentState()) === null || _api$userIntent$share === void 0 ? void 0 : _api$userIntent$share.currentUserIntent;
97
+ const isDisplayingDiff = (_api$showDiff$sharedS = api === null || api === void 0 ? void 0 : (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 ? void 0 : (_api$showDiff$sharedS2 = _api$showDiff.sharedState.currentState()) === null || _api$showDiff$sharedS2 === void 0 ? void 0 : _api$showDiff$sharedS2.isDisplayingChanges) !== null && _api$showDiff$sharedS !== void 0 ? _api$showDiff$sharedS : false;
98
+ if ((isDisplayingDiff || currentUserIntent === 'reviewing') && expValEqualsNoExposure('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
99
+ return false;
100
+ }
101
+
92
102
  // We shouldn't be firing mouse over transactions when the editor is disabled,
93
103
  // except in view mode when right-side controls are enabled (show controls on block hover)
94
104
  const rightSideControlsEnabled = (_api$blockControls$sh = api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : (_api$blockControls2$s = _api$blockControls2.sharedState.currentState()) === null || _api$blockControls2$s === void 0 ? void 0 : _api$blockControls2$s.rightSideControlsEnabled) !== null && _api$blockControls$sh !== void 0 ? _api$blockControls$sh : false;
@@ -273,8 +283,8 @@ export const handleMouseOver = (view, event, api) => {
273
283
  api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 ? void 0 : _api$blockControls5.commands.showDragHandleAt(targetPos, anchorName, nodeType));
274
284
  }
275
285
  if (editorExperiment('platform_editor_block_menu', true)) {
276
- var _api$userIntent, _api$userIntent$share;
277
- if (isMenuOpen && originalAnchorName && (api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : (_api$userIntent$share = _api$userIntent.sharedState.currentState()) === null || _api$userIntent$share === void 0 ? void 0 : _api$userIntent$share.currentUserIntent) === 'blockMenuOpen') {
286
+ var _api$userIntent2, _api$userIntent2$shar;
287
+ if (isMenuOpen && originalAnchorName && (api === null || api === void 0 ? void 0 : (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : (_api$userIntent2$shar = _api$userIntent2.sharedState.currentState()) === null || _api$userIntent2$shar === void 0 ? void 0 : _api$userIntent2$shar.currentUserIntent) === 'blockMenuOpen') {
278
288
  var _api$core4, _api$blockControls6;
279
289
  api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 ? void 0 : _api$blockControls6.commands.toggleBlockMenu());
280
290
  }
@@ -73,6 +73,7 @@ const classifyRightMarginPosition = (view, event) => {
73
73
  * enabled (confluence_remix_button_right_side_block_fg); handleMouseMove returns early otherwise.
74
74
  */
75
75
  const processHoverSide = (view, api) => {
76
+ var _api$userIntent, _api$userIntent$share, _api$showDiff$sharedS, _api$showDiff, _api$showDiff$sharedS2;
76
77
  const event = pendingByView.get(view);
77
78
  if (!event) {
78
79
  return;
@@ -86,6 +87,15 @@ const processHoverSide = (view, api) => {
86
87
  const state = getInteractionTrackingState(view.state);
87
88
  const target = event.target instanceof HTMLElement ? event.target : null;
88
89
 
90
+ // EDITOR-7926: skip hover-side tracking while a diff is on screen (or a suggestion card is open)
91
+ // so setHoverSide/mouseEnter don't flip-flop and jitter the controls. No-exposure check as this
92
+ // runs on every mouse move.
93
+ const currentUserIntent = api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : (_api$userIntent$share = _api$userIntent.sharedState.currentState()) === null || _api$userIntent$share === void 0 ? void 0 : _api$userIntent$share.currentUserIntent;
94
+ const isDisplayingDiff = (_api$showDiff$sharedS = api === null || api === void 0 ? void 0 : (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 ? void 0 : (_api$showDiff$sharedS2 = _api$showDiff.sharedState.currentState()) === null || _api$showDiff$sharedS2 === void 0 ? void 0 : _api$showDiff$sharedS2.isDisplayingChanges) !== null && _api$showDiff$sharedS !== void 0 ? _api$showDiff$sharedS : false;
95
+ if ((isDisplayingDiff || currentUserIntent === 'reviewing') && expValEqualsNoExposure('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
96
+ return;
97
+ }
98
+
89
99
  // When hovering over block controls directly, infer side from which control we're over.
90
100
  // This is more reliable than bounds when controls are in portals outside the editor DOM.
91
101
  const rightEdgeElement = target === null || target === void 0 ? void 0 : target.closest(RIGHT_EDGE_SELECTOR);
@@ -63,6 +63,9 @@ export const createVanillaButton = props => {
63
63
  const button = dom.querySelector('button[data-testid="editor-quick-insert-button"]');
64
64
  if (button instanceof HTMLButtonElement) {
65
65
  button.onclick = () => handleQuickInsert(props);
66
+
67
+ // Flag-on (platform-dst-top-layer), positioning is async via a React root;
68
+ // safe here as VanillaTooltip stays hidden until positioned.
66
69
  const tooltip = new VanillaTooltip(button, props.formatMessage(messages.insert), 'quick-insert-button-tooltip', 'blocks-quick-insert-tooltip');
67
70
  props.cleanupCallbacks.push(() => {
68
71
  tooltip.destroy();
@@ -60,9 +60,10 @@ export const VisibilityContainer = ({
60
60
  hoverSide,
61
61
  editorViewMode,
62
62
  userIntent,
63
+ isDisplayingDiff,
63
64
  rightSideControlsEnabled
64
- } = useSharedPluginStateWithSelector(api, ['typeAhead', 'blockControls', 'editorViewMode', 'userIntent'], states => {
65
- var _states$typeAheadStat, _states$blockControls, _states$blockControls2, _states$blockControls3, _states$editorViewMod, _states$userIntentSta, _states$blockControls4;
65
+ } = useSharedPluginStateWithSelector(api, ['typeAhead', 'blockControls', 'editorViewMode', 'userIntent', 'showDiff'], states => {
66
+ var _states$typeAheadStat, _states$blockControls, _states$blockControls2, _states$blockControls3, _states$editorViewMod, _states$userIntentSta, _states$showDiffState, _states$blockControls4;
66
67
  return {
67
68
  isTypeAheadOpen: (_states$typeAheadStat = states.typeAheadState) === null || _states$typeAheadStat === void 0 ? void 0 : _states$typeAheadStat.isOpen,
68
69
  isEditing: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isEditing,
@@ -70,6 +71,7 @@ export const VisibilityContainer = ({
70
71
  hoverSide: (_states$blockControls3 = states.blockControlsState) === null || _states$blockControls3 === void 0 ? void 0 : _states$blockControls3.hoverSide,
71
72
  editorViewMode: (_states$editorViewMod = states.editorViewModeState) === null || _states$editorViewMod === void 0 ? void 0 : _states$editorViewMod.mode,
72
73
  userIntent: (_states$userIntentSta = states.userIntentState) === null || _states$userIntentSta === void 0 ? void 0 : _states$userIntentSta.currentUserIntent,
74
+ isDisplayingDiff: (_states$showDiffState = states.showDiffState) === null || _states$showDiffState === void 0 ? void 0 : _states$showDiffState.isDisplayingChanges,
73
75
  rightSideControlsEnabled: (_states$blockControls4 = states.blockControlsState) === null || _states$blockControls4 === void 0 ? void 0 : _states$blockControls4.rightSideControlsEnabled
74
76
  };
75
77
  });
@@ -88,7 +90,9 @@ export const VisibilityContainer = ({
88
90
  // When forceVisibleOnMouseOut is true (e.g. drag handle focused via keyboard Shift+Ctrl+H),
89
91
  // override the mouse-out condition so the control stays visible regardless of mouse position.
90
92
  const shouldHideWhenMouseOut = forceVisibleOnMouseOut ? false : hideOnMouseOut;
91
- const shouldHideImmediate = isTypeAheadOpen || isEditing || shouldHideWhenMouseOut || userIntent === 'aiStreaming' || userIntent === 'viewingDiff' && expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) || sideHidden;
93
+ const shouldHideImmediate = isTypeAheadOpen || isEditing || shouldHideWhenMouseOut || userIntent === 'aiStreaming' ||
94
+ // EDITOR-7926: hide the drag handle while a diff is on screen or a suggestion card is open.
95
+ (isDisplayingDiff || userIntent === 'reviewing') && expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) || sideHidden;
92
96
 
93
97
  // Delay hiding the right control in view mode to reduce flickering when moving from block
94
98
  // toward the right-edge button (avoids rapid show/hide as mouse crosses boundaries).
@@ -4,6 +4,7 @@ import { isMultiBlockSelection } from '@atlaskit/editor-common/selection';
4
4
  import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check';
5
5
  import { fg } from '@atlaskit/platform-feature-flags';
6
6
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
7
8
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
8
9
  import { getAnchorAttrName, getTypeNameAttrName, getTypeNameFromDom, NODE_ANCHOR_ATTR_NAME } from '../ui/utils/dom-attr-name';
9
10
  import { IGNORE_NODE_DESCENDANTS_ADVANCED_LAYOUT, IGNORE_NODES_NEXT } from './decorations-anchor';
@@ -75,7 +76,7 @@ var redirectParagraphToWrappedMedia = function redirectParagraphToWrappedMedia(r
75
76
  return rootElement;
76
77
  };
77
78
  export var handleMouseOver = function handleMouseOver(view, event, api) {
78
- var _api$blockControls, _api$editorDisabled, _api$editorViewMode, _api$blockControls$sh, _api$blockControls2, _target$classList;
79
+ var _api$blockControls, _api$editorDisabled, _api$editorViewMode, _api$userIntent, _api$showDiff$sharedS, _api$showDiff, _api$blockControls$sh, _api$blockControls2, _target$classList;
79
80
  var _ref = (api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {},
80
81
  isDragging = _ref.isDragging,
81
82
  activeNode = _ref.activeNode,
@@ -90,6 +91,15 @@ export var handleMouseOver = function handleMouseOver(view, event, api) {
90
91
  var isViewMode = editorViewMode === 'view';
91
92
  var toolbarFlagsEnabled = areToolbarFlagsEnabled(Boolean(api === null || api === void 0 ? void 0 : api.toolbar));
92
93
 
94
+ // EDITOR-7926: bail out while a diff is on screen (or a suggestion card is open) so hovering
95
+ // doesn't dispatch showDragHandleAt and cause the drag handle to jitter over the diff.
96
+ // No-exposure check as this runs on every mouseover.
97
+ var currentUserIntent = api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent;
98
+ var isDisplayingDiff = (_api$showDiff$sharedS = api === null || api === void 0 || (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 || (_api$showDiff = _api$showDiff.sharedState.currentState()) === null || _api$showDiff === void 0 ? void 0 : _api$showDiff.isDisplayingChanges) !== null && _api$showDiff$sharedS !== void 0 ? _api$showDiff$sharedS : false;
99
+ if ((isDisplayingDiff || currentUserIntent === 'reviewing') && expValEqualsNoExposure('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
100
+ return false;
101
+ }
102
+
93
103
  // We shouldn't be firing mouse over transactions when the editor is disabled,
94
104
  // except in view mode when right-side controls are enabled (show controls on block hover)
95
105
  var rightSideControlsEnabled = (_api$blockControls$sh = api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || (_api$blockControls2 = _api$blockControls2.sharedState.currentState()) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.rightSideControlsEnabled) !== null && _api$blockControls$sh !== void 0 ? _api$blockControls$sh : false;
@@ -274,8 +284,8 @@ export var handleMouseOver = function handleMouseOver(view, event, api) {
274
284
  api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(api === null || api === void 0 || (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 ? void 0 : _api$blockControls5.commands.showDragHandleAt(targetPos, anchorName, nodeType));
275
285
  }
276
286
  if (editorExperiment('platform_editor_block_menu', true)) {
277
- var _api$userIntent;
278
- if (isMenuOpen && originalAnchorName && (api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent) === 'blockMenuOpen') {
287
+ var _api$userIntent2;
288
+ if (isMenuOpen && originalAnchorName && (api === null || api === void 0 || (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 || (_api$userIntent2 = _api$userIntent2.sharedState.currentState()) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.currentUserIntent) === 'blockMenuOpen') {
279
289
  var _api$core4, _api$blockControls6;
280
290
  api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(api === null || api === void 0 || (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 ? void 0 : _api$blockControls6.commands.toggleBlockMenu());
281
291
  }
@@ -87,6 +87,7 @@ var classifyRightMarginPosition = function classifyRightMarginPosition(view, eve
87
87
  * enabled (confluence_remix_button_right_side_block_fg); handleMouseMove returns early otherwise.
88
88
  */
89
89
  var processHoverSide = function processHoverSide(view, api) {
90
+ var _api$userIntent, _api$showDiff$sharedS, _api$showDiff;
90
91
  var event = pendingByView.get(view);
91
92
  if (!event) {
92
93
  return;
@@ -100,6 +101,15 @@ var processHoverSide = function processHoverSide(view, api) {
100
101
  var state = getInteractionTrackingState(view.state);
101
102
  var target = event.target instanceof HTMLElement ? event.target : null;
102
103
 
104
+ // EDITOR-7926: skip hover-side tracking while a diff is on screen (or a suggestion card is open)
105
+ // so setHoverSide/mouseEnter don't flip-flop and jitter the controls. No-exposure check as this
106
+ // runs on every mouse move.
107
+ var currentUserIntent = api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent;
108
+ var isDisplayingDiff = (_api$showDiff$sharedS = api === null || api === void 0 || (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 || (_api$showDiff = _api$showDiff.sharedState.currentState()) === null || _api$showDiff === void 0 ? void 0 : _api$showDiff.isDisplayingChanges) !== null && _api$showDiff$sharedS !== void 0 ? _api$showDiff$sharedS : false;
109
+ if ((isDisplayingDiff || currentUserIntent === 'reviewing') && expValEqualsNoExposure('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
110
+ return;
111
+ }
112
+
103
113
  // When hovering over block controls directly, infer side from which control we're over.
104
114
  // This is more reliable than bounds when controls are in portals outside the editor DOM.
105
115
  var rightEdgeElement = target === null || target === void 0 ? void 0 : target.closest(RIGHT_EDGE_SELECTOR);
@@ -65,6 +65,9 @@ export var createVanillaButton = function createVanillaButton(props) {
65
65
  button.onclick = function () {
66
66
  return handleQuickInsert(props);
67
67
  };
68
+
69
+ // Flag-on (platform-dst-top-layer), positioning is async via a React root;
70
+ // safe here as VanillaTooltip stays hidden until positioned.
68
71
  var tooltip = new VanillaTooltip(button, props.formatMessage(messages.insert), 'quick-insert-button-tooltip', 'blocks-quick-insert-tooltip');
69
72
  props.cleanupCallbacks.push(function () {
70
73
  tooltip.destroy();
@@ -52,8 +52,8 @@ export var VisibilityContainer = function VisibilityContainer(_ref) {
52
52
  controlSide = _ref.controlSide,
53
53
  forceVisibleOnMouseOut = _ref.forceVisibleOnMouseOut,
54
54
  shouldUseDisplayContents = _ref.shouldUseDisplayContents;
55
- var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['typeAhead', 'blockControls', 'editorViewMode', 'userIntent'], function (states) {
56
- var _states$typeAheadStat, _states$blockControls, _states$blockControls2, _states$blockControls3, _states$editorViewMod, _states$userIntentSta, _states$blockControls4;
55
+ var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['typeAhead', 'blockControls', 'editorViewMode', 'userIntent', 'showDiff'], function (states) {
56
+ var _states$typeAheadStat, _states$blockControls, _states$blockControls2, _states$blockControls3, _states$editorViewMod, _states$userIntentSta, _states$showDiffState, _states$blockControls4;
57
57
  return {
58
58
  isTypeAheadOpen: (_states$typeAheadStat = states.typeAheadState) === null || _states$typeAheadStat === void 0 ? void 0 : _states$typeAheadStat.isOpen,
59
59
  isEditing: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isEditing,
@@ -61,6 +61,7 @@ export var VisibilityContainer = function VisibilityContainer(_ref) {
61
61
  hoverSide: (_states$blockControls3 = states.blockControlsState) === null || _states$blockControls3 === void 0 ? void 0 : _states$blockControls3.hoverSide,
62
62
  editorViewMode: (_states$editorViewMod = states.editorViewModeState) === null || _states$editorViewMod === void 0 ? void 0 : _states$editorViewMod.mode,
63
63
  userIntent: (_states$userIntentSta = states.userIntentState) === null || _states$userIntentSta === void 0 ? void 0 : _states$userIntentSta.currentUserIntent,
64
+ isDisplayingDiff: (_states$showDiffState = states.showDiffState) === null || _states$showDiffState === void 0 ? void 0 : _states$showDiffState.isDisplayingChanges,
64
65
  rightSideControlsEnabled: (_states$blockControls4 = states.blockControlsState) === null || _states$blockControls4 === void 0 ? void 0 : _states$blockControls4.rightSideControlsEnabled
65
66
  };
66
67
  }),
@@ -70,6 +71,7 @@ export var VisibilityContainer = function VisibilityContainer(_ref) {
70
71
  hoverSide = _useSharedPluginState.hoverSide,
71
72
  editorViewMode = _useSharedPluginState.editorViewMode,
72
73
  userIntent = _useSharedPluginState.userIntent,
74
+ isDisplayingDiff = _useSharedPluginState.isDisplayingDiff,
73
75
  rightSideControlsEnabled = _useSharedPluginState.rightSideControlsEnabled;
74
76
  var isViewMode = editorViewMode === 'view';
75
77
  // rightSideControlsEnabled is the single source of truth (confluence_remix_button_right_side_block_fg from preset)
@@ -86,7 +88,9 @@ export var VisibilityContainer = function VisibilityContainer(_ref) {
86
88
  // When forceVisibleOnMouseOut is true (e.g. drag handle focused via keyboard Shift+Ctrl+H),
87
89
  // override the mouse-out condition so the control stays visible regardless of mouse position.
88
90
  var shouldHideWhenMouseOut = forceVisibleOnMouseOut ? false : hideOnMouseOut;
89
- var shouldHideImmediate = isTypeAheadOpen || isEditing || shouldHideWhenMouseOut || userIntent === 'aiStreaming' || userIntent === 'viewingDiff' && expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) || sideHidden;
91
+ var shouldHideImmediate = isTypeAheadOpen || isEditing || shouldHideWhenMouseOut || userIntent === 'aiStreaming' ||
92
+ // EDITOR-7926: hide the drag handle while a diff is on screen or a suggestion card is open.
93
+ (isDisplayingDiff || userIntent === 'reviewing') && expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) || sideHidden;
90
94
 
91
95
  // Delay hiding the right control in view mode to reduce flickering when moving from block
92
96
  // toward the right-edge button (avoids rapid show/hide as mouse crosses boundaries).
@@ -12,6 +12,7 @@ import type { LimitedModePlugin } from '@atlaskit/editor-plugin-limited-mode';
12
12
  import type { MetricsPlugin } from '@atlaskit/editor-plugin-metrics';
13
13
  import type { QuickInsertPlugin } from '@atlaskit/editor-plugin-quick-insert';
14
14
  import type { SelectionPlugin } from '@atlaskit/editor-plugin-selection';
15
+ import type { ShowDiffPlugin } from '@atlaskit/editor-plugin-show-diff';
15
16
  import type { ToolbarPlugin } from '@atlaskit/editor-plugin-toolbar';
16
17
  import type { TypeAheadPlugin } from '@atlaskit/editor-plugin-type-ahead';
17
18
  import type { UserIntentPlugin } from '@atlaskit/editor-plugin-user-intent';
@@ -157,7 +158,8 @@ export type BlockControlsPluginDependencies = [
157
158
  OptionalPlugin<MetricsPlugin>,
158
159
  OptionalPlugin<InteractionPlugin>,
159
160
  OptionalPlugin<UserIntentPlugin>,
160
- OptionalPlugin<ToolbarPlugin>
161
+ OptionalPlugin<ToolbarPlugin>,
162
+ OptionalPlugin<ShowDiffPlugin>
161
163
  ];
162
164
  export type BlockControlsPlugin = NextEditorPlugin<'blockControls', {
163
165
  actions: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-controls",
3
- "version": "14.0.4",
3
+ "version": "14.0.6",
4
4
  "description": "Block controls plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -32,6 +32,7 @@
32
32
  "@atlaskit/editor-plugin-metrics": "^14.0.0",
33
33
  "@atlaskit/editor-plugin-quick-insert": "^13.1.0",
34
34
  "@atlaskit/editor-plugin-selection": "^13.0.0",
35
+ "@atlaskit/editor-plugin-show-diff": "^11.0.0",
35
36
  "@atlaskit/editor-plugin-toolbar": "^10.1.0",
36
37
  "@atlaskit/editor-plugin-type-ahead": "^14.0.0",
37
38
  "@atlaskit/editor-plugin-user-intent": "^11.0.0",
@@ -48,8 +49,8 @@
48
49
  "@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "^4.1.0",
49
50
  "@atlaskit/primitives": "^22.2.0",
50
51
  "@atlaskit/theme": "^27.0.0",
51
- "@atlaskit/tmp-editor-statsig": "^141.1.0",
52
- "@atlaskit/tokens": "^16.3.0",
52
+ "@atlaskit/tmp-editor-statsig": "^142.0.0",
53
+ "@atlaskit/tokens": "^16.4.0",
53
54
  "@atlaskit/tooltip": "^24.0.0",
54
55
  "@babel/runtime": "^7.0.0",
55
56
  "@emotion/react": "^11.7.1",
@@ -59,7 +60,7 @@
59
60
  "uuid": "^3.1.0"
60
61
  },
61
62
  "peerDependencies": {
62
- "@atlaskit/editor-common": "^117.3.0",
63
+ "@atlaskit/editor-common": "^117.6.0",
63
64
  "react": "^18.2.0 || ^19.2.0",
64
65
  "react-dom": "^18.2.0 || ^19.2.0",
65
66
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"