@atlaskit/editor-plugin-block-controls 8.8.2 → 8.9.0

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,16 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 8.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`e15637c7f5994`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/e15637c7f5994) -
8
+ [ux] Fix hover logic for block
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies
13
+
3
14
  ## 8.8.2
4
15
 
5
16
  ### Patch Changes
@@ -11,7 +11,6 @@ var _selection = require("@atlaskit/editor-common/selection");
11
11
  var _toolbarFlagCheck = require("@atlaskit/editor-common/toolbar-flag-check");
12
12
  var _state = require("@atlaskit/editor-prosemirror/state");
13
13
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
14
- var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
15
14
  var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
16
15
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
17
16
  var _handleKeyDownWithPreservedSelection = require("./editor-commands/handle-key-down-with-preserved-selection");
@@ -57,14 +56,14 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
57
56
  plugin: function plugin(_ref2) {
58
57
  var getIntl = _ref2.getIntl,
59
58
  nodeViewPortalProviderAPI = _ref2.nodeViewPortalProviderAPI;
60
- return (0, _main.createPlugin)(api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true));
59
+ return (0, _main.createPlugin)(api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled);
61
60
  }
62
61
  }];
63
62
  if ((0, _experiments.editorExperiment)('platform_editor_controls', 'variant1')) {
64
63
  pmPlugins.push({
65
64
  name: 'blockControlsInteractionTrackingPlugin',
66
65
  plugin: function plugin() {
67
- return (0, _pmPlugin.createInteractionTrackingPlugin)(rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true));
66
+ return (0, _pmPlugin.createInteractionTrackingPlugin)(rightSideControlsEnabled);
68
67
  }
69
68
  });
70
69
  }
@@ -336,13 +335,11 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
336
335
  isSelectedViaDragHandle: (_key$getState$isSelec = (_key$getState10 = _main.key.getState(editorState)) === null || _key$getState10 === void 0 ? void 0 : _key$getState10.isSelectedViaDragHandle) !== null && _key$getState$isSelec !== void 0 ? _key$getState$isSelec : false
337
336
  };
338
337
  if ((0, _experiments.editorExperiment)('platform_editor_controls', 'variant1')) {
339
- var _interactionTrackingP2, _interactionTrackingP3;
338
+ var _interactionTrackingP2, _interactionTrackingP3, _interactionTrackingP4;
340
339
  sharedState.isMouseOut = (_interactionTrackingP2 = (_interactionTrackingP3 = _pmPlugin.interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP3 === void 0 ? void 0 : _interactionTrackingP3.isMouseOut) !== null && _interactionTrackingP2 !== void 0 ? _interactionTrackingP2 : false;
341
- sharedState.rightSideControlsEnabled = rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true);
342
- if (rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true)) {
343
- var _interactionTrackingP4;
344
- sharedState.hoverSide = (_interactionTrackingP4 = _pmPlugin.interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP4 === void 0 ? void 0 : _interactionTrackingP4.hoverSide;
345
- }
340
+ // rightSideControlsEnabled is the single source of truth (confluence_remix_icon_right_side from preset)
341
+ sharedState.rightSideControlsEnabled = rightSideControlsEnabled;
342
+ sharedState.hoverSide = rightSideControlsEnabled ? (_interactionTrackingP4 = _pmPlugin.interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP4 === void 0 ? void 0 : _interactionTrackingP4.hoverSide : undefined;
346
343
  }
347
344
  if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true)) {
348
345
  var _selectionPreservatio;
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.handleMouseMove = exports.handleMouseLeave = exports.handleMouseEnter = void 0;
7
- var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
8
7
  var _commands = require("./commands");
9
8
  var _pmPlugin = require("./pm-plugin");
10
9
  /** Per-view pending hover state; avoids cross-editor singleton. */
@@ -24,12 +23,12 @@ var clearPendingHoverSide = function clearPendingHoverSide(view) {
24
23
  cancelScheduledProcessForView(view);
25
24
  };
26
25
  var BLOCK_SELECTORS = '[data-node-anchor], [data-drag-handler-anchor-name]';
27
- var TABLE_SELECTOR = '[data-prosemirror-node-name="table"]';
28
26
  var RIGHT_EDGE_SELECTOR = '[data-blocks-right-edge-button-container]';
29
- var isInsideTable = function isInsideTable(element) {
30
- var _element$getAttribute;
31
- return element.closest(TABLE_SELECTOR) !== null || ((_element$getAttribute = element.getAttribute) === null || _element$getAttribute === void 0 ? void 0 : _element$getAttribute.call(element, 'data-prosemirror-node-name')) === 'table';
32
- };
27
+
28
+ /**
29
+ * Process hover position and set left/right side. Only invoked when right-side controls are
30
+ * enabled (confluence_remix_icon_right_side); handleMouseMove returns early otherwise.
31
+ */
33
32
  var processHoverSide = function processHoverSide(view) {
34
33
  var event = pendingByView.get(view);
35
34
  if (!event) {
@@ -61,13 +60,12 @@ var processHoverSide = function processHoverSide(view) {
61
60
  return;
62
61
  }
63
62
 
64
- // Use the hovered block's midpoint when hovering over block content.
63
+ // Primary path: depth-1 block (doc direct child). Decorations-anchor sets [data-drag-handler-anchor-depth="1"]
64
+ // on every root block (table, layoutSection, expand, etc.), so we get the whole block without per-type logic.
65
65
  var blockElement = target === null || target === void 0 ? void 0 : target.closest(BLOCK_SELECTORS);
66
- var boundsElement = blockElement instanceof HTMLElement ? blockElement : editorContentArea;
67
-
68
- // Tables show block controls at table level; don't restrict by side so drag handle
69
- // stays visible when hovering anywhere in the table (e.g. paragraph in second cell).
70
- if (isInsideTable(boundsElement)) {
66
+ var depth1Block = blockElement instanceof HTMLElement ? blockElement.closest('[data-drag-handler-anchor-depth="1"]') : null;
67
+ var boundsElement = depth1Block instanceof HTMLElement ? depth1Block : editorContentArea;
68
+ if (!boundsElement) {
71
69
  if ((state === null || state === void 0 ? void 0 : state.hoverSide) !== undefined) {
72
70
  (0, _commands.clearHoverSide)(view);
73
71
  }
@@ -90,8 +88,8 @@ var handleMouseMove = exports.handleMouseMove = function handleMouseMove(view, e
90
88
  (0, _commands.stopEditing)(view);
91
89
  }
92
90
 
93
- // Only track hover side when right-side controls are enabled and experiment is on (performance)
94
- if (!rightSideControlsEnabled || !(0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true)) {
91
+ // Only track hover side when right-side controls are enabled (single source: confluence_remix_icon_right_side via config)
92
+ if (!rightSideControlsEnabled) {
95
93
  return false;
96
94
  }
97
95
  if (!(event instanceof MouseEvent)) {
@@ -107,7 +105,7 @@ var handleMouseMove = exports.handleMouseMove = function handleMouseMove(view, e
107
105
  };
108
106
  var handleMouseLeave = exports.handleMouseLeave = function handleMouseLeave(view) {
109
107
  var rightSideControlsEnabled = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
110
- if (rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true)) {
108
+ if (rightSideControlsEnabled) {
111
109
  clearPendingHoverSide(view);
112
110
  }
113
111
  (0, _commands.mouseLeave)(view);
@@ -9,7 +9,6 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
9
9
  var _bindEventListener = require("bind-event-listener");
10
10
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
11
11
  var _state = require("@atlaskit/editor-prosemirror/state");
12
- var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
13
12
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
14
13
  var _handleKeyDown = require("./handle-key-down");
15
14
  var _handleMouseMove = require("./handle-mouse-move");
@@ -67,13 +66,14 @@ var createInteractionTrackingPlugin = exports.createInteractionTrackingPlugin =
67
66
  handleKeyDown: _handleKeyDown.handleKeyDown,
68
67
  handleDOMEvents: {
69
68
  mousemove: function mousemove(view, event) {
70
- return (0, _handleMouseMove.handleMouseMove)(view, event, rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true));
69
+ return (0, _handleMouseMove.handleMouseMove)(view, event, rightSideControlsEnabled);
71
70
  }
72
71
  }
73
72
  },
74
73
  view: (0, _experiments.editorExperiment)('platform_editor_controls', 'variant1') ? function (view) {
75
74
  var editorContentArea = view.dom.closest('.ak-editor-content-area');
76
- var remixRightSideEnabled = rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true);
75
+ // rightSideControlsEnabled is the single source of truth (confluence_remix_icon_right_side from preset)
76
+ var remixRightSideEnabled = rightSideControlsEnabled;
77
77
  var unbindMouseEnter;
78
78
  var unbindMouseLeave;
79
79
  var unbindDocumentMouseMove;
@@ -89,19 +89,19 @@ var createInteractionTrackingPlugin = exports.createInteractionTrackingPlugin =
89
89
  }
90
90
 
91
91
  // Don't set isMouseOut when moving to block controls (right-edge button, drag handle, etc.)
92
- if (rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true) && isMovingToBlockControlsArea(event.relatedTarget)) {
92
+ if (rightSideControlsEnabled && isMovingToBlockControlsArea(event.relatedTarget)) {
93
93
  return;
94
94
  }
95
95
  mouseLeaveTimeoutId = setTimeout(function () {
96
96
  mouseLeaveTimeoutId = null;
97
97
  // Before dispatching, check if mouse has moved to block controls (e.g. through empty space)
98
- if (rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true) && typeof document !== 'undefined') {
98
+ if (rightSideControlsEnabled && typeof document !== 'undefined') {
99
99
  var el = document.elementFromPoint(lastMousePosition.x, lastMousePosition.y);
100
100
  if (el && isMovingToBlockControlsArea(el)) {
101
101
  return;
102
102
  }
103
103
  }
104
- (0, _handleMouseMove.handleMouseLeave)(view, rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true));
104
+ (0, _handleMouseMove.handleMouseLeave)(view, rightSideControlsEnabled);
105
105
  }, MOUSE_LEAVE_DEBOUNCE_MS);
106
106
  };
107
107
  var cancelScheduledMouseLeave = function cancelScheduledMouseLeave() {
@@ -123,7 +123,7 @@ var createInteractionTrackingPlugin = exports.createInteractionTrackingPlugin =
123
123
  // controls (which may be in portals outside the editor DOM). Without this,
124
124
  // handleDOMEvents.mousemove only fires when over the editor content.
125
125
  if (editorContentArea.contains(event.target) || isMovingToBlockControlsArea(event.target)) {
126
- (0, _handleMouseMove.handleMouseMove)(view, event, rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true));
126
+ (0, _handleMouseMove.handleMouseMove)(view, event, rightSideControlsEnabled);
127
127
  }
128
128
  },
129
129
  options: {
@@ -535,13 +535,9 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
535
535
  _step3;
536
536
  try {
537
537
  var _loop3 = function _loop3() {
538
- var _activeNode15, _activeNode16, _latestActiveNode1, _latestActiveNode10, _latestActiveNode11, _latestActiveNode12, _latestActiveNode13;
538
+ var _latestActiveNode1, _latestActiveNode10, _latestActiveNode11, _latestActiveNode12, _latestActiveNode13, _activeNode15, _activeNode16;
539
539
  var factory = _step3.value;
540
- var old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, function (spec) {
541
- return spec.type === factory.type;
542
- });
543
- decorations = decorations.remove(old);
544
- var dec = factory.create({
540
+ var params = {
545
541
  editorState: newState,
546
542
  nodeViewPortalProviderAPI: nodeViewPortalProviderAPI,
547
543
  anchorName: (_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.anchorName,
@@ -549,7 +545,12 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
549
545
  rootPos: (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.rootPos,
550
546
  rootAnchorName: (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.rootAnchorName,
551
547
  rootNodeType: (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.rootNodeType
548
+ };
549
+ var old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, function (spec) {
550
+ return spec.type === factory.type;
552
551
  });
552
+ decorations = decorations.remove(old);
553
+ var dec = factory.create(params);
553
554
  decorations = decorations.add(newState.doc, [dec]);
554
555
  };
555
556
  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
@@ -564,8 +565,10 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
564
565
  }
565
566
 
566
567
  // In view mode (edit/live pages), show right-side controls on block hover (without drag handle or quick insert)
567
- if (isViewMode && ((_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.rootPos) !== undefined && flags.toolbarFlagsEnabled && rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true)) {
568
- var rootPos = latestActiveNode.rootPos;
568
+ var rootPos = (_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.rootPos;
569
+ // rootPos is computed using the same logic as the floating insert menu, so it always points to a top-level (doc child) block when defined.
570
+ var isDocLevel = rootPos !== undefined && !isNaN(rootPos);
571
+ if (isViewMode && isDocLevel && flags.toolbarFlagsEnabled && rightSideControlsEnabled) {
569
572
  var _iterator4 = _createForOfIteratorHelper(nodeDecorationRegistry),
570
573
  _step4;
571
574
  try {
@@ -13,7 +13,6 @@ var _model = require("@atlaskit/editor-prosemirror/model");
13
13
  var _state = require("@atlaskit/editor-prosemirror/state");
14
14
  var _utils = require("@atlaskit/editor-prosemirror/utils");
15
15
  var _cellSelection = require("@atlaskit/editor-tables/cell-selection");
16
- var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
17
16
  var _documentChecks = require("../ui/utils/document-checks");
18
17
  var _editorCommands = require("../ui/utils/editor-commands");
19
18
  var _quickInsertCalculatePosition = require("./quick-insert-calculate-position");
@@ -91,7 +90,7 @@ var createVanillaButton = exports.createVanillaButton = function createVanillaBu
91
90
  return;
92
91
  }
93
92
  var isViewMode = editorViewMode === 'view';
94
- var shouldRestrictBySide = rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true) && !isViewMode;
93
+ var shouldRestrictBySide = rightSideControlsEnabled && !isViewMode;
95
94
  // Only restrict by side when hoverSide is known. When undefined, show quick insert.
96
95
  var sideHidden = shouldRestrictBySide && hoverSide !== undefined ? hoverSide !== 'left' : false;
97
96
  if (isTypeAheadOpen || isEditing || sideHidden) {
@@ -117,7 +116,7 @@ var createVanillaButton = exports.createVanillaButton = function createVanillaBu
117
116
  changeDOMVisibility();
118
117
  }));
119
118
  // Only subscribe to view mode when right-side controls are enabled (editorViewMode affects side restriction)
120
- if (rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true)) {
119
+ if (rightSideControlsEnabled) {
121
120
  var _props$api$editorView2, _props$api$editorView3, _props$api$editorView4;
122
121
  var unsubscribeViewMode = (_props$api$editorView2 = props.api.editorViewMode) === null || _props$api$editorView2 === void 0 || (_props$api$editorView3 = (_props$api$editorView4 = _props$api$editorView2.sharedState).onChange) === null || _props$api$editorView3 === void 0 ? void 0 : _props$api$editorView3.call(_props$api$editorView4, function (_ref4) {
123
122
  var nextSharedState = _ref4.nextSharedState;
@@ -13,7 +13,6 @@ var _react2 = require("@emotion/react");
13
13
  var _hooks = require("@atlaskit/editor-common/hooks");
14
14
  var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
15
15
  var _primitives = require("@atlaskit/primitives");
16
- var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
17
16
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
18
17
  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
18
  /**
@@ -75,7 +74,8 @@ var VisibilityContainer = exports.VisibilityContainer = function VisibilityConta
75
74
  userIntent = _useSharedPluginState.userIntent,
76
75
  rightSideControlsEnabled = _useSharedPluginState.rightSideControlsEnabled;
77
76
  var isViewMode = editorViewMode === 'view';
78
- var shouldRestrictBySide = rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true) && controlSide !== undefined && !isViewMode;
77
+ // rightSideControlsEnabled is the single source of truth (confluence_remix_icon_right_side from preset)
78
+ var shouldRestrictBySide = rightSideControlsEnabled && controlSide !== undefined && !isViewMode;
79
79
  // Only restrict by side when hoverSide is known (after mousemove). When undefined, show both
80
80
  // controls so drag handle is visible on load and for keyboard-only users.
81
81
  var sideHidden = shouldRestrictBySide && hoverSide !== undefined ? hoverSide !== controlSide : false;
@@ -87,7 +87,7 @@ var VisibilityContainer = exports.VisibilityContainer = function VisibilityConta
87
87
 
88
88
  // Delay hiding the right control in view mode to reduce flickering when moving from block
89
89
  // toward the right-edge button (avoids rapid show/hide as mouse crosses boundaries).
90
- var isRightControlViewMode = isViewMode && rightSideControlsEnabled && (0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true) && controlSide === 'right';
90
+ var isRightControlViewMode = isViewMode && rightSideControlsEnabled && controlSide === 'right';
91
91
  // When in right-control view mode, we delay hiding so start visible; useEffect will update after delay
92
92
  var _useState = (0, _react.useState)(false),
93
93
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
@@ -3,7 +3,6 @@ import { expandSelectionBounds } from '@atlaskit/editor-common/selection';
3
3
  import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check';
4
4
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
5
5
  import { fg } from '@atlaskit/platform-feature-flags';
6
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
6
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
8
7
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
9
8
  import { handleKeyDownWithPreservedSelection } from './editor-commands/handle-key-down-with-preserved-selection';
@@ -46,12 +45,12 @@ export const blockControlsPlugin = ({
46
45
  plugin: ({
47
46
  getIntl,
48
47
  nodeViewPortalProviderAPI
49
- }) => createPlugin(api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true))
48
+ }) => createPlugin(api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled)
50
49
  }];
51
50
  if (editorExperiment('platform_editor_controls', 'variant1')) {
52
51
  pmPlugins.push({
53
52
  name: 'blockControlsInteractionTrackingPlugin',
54
- plugin: () => createInteractionTrackingPlugin(rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true))
53
+ plugin: () => createInteractionTrackingPlugin(rightSideControlsEnabled)
55
54
  });
56
55
  }
57
56
  if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
@@ -319,13 +318,11 @@ export const blockControlsPlugin = ({
319
318
  isSelectedViaDragHandle: (_key$getState$isSelec = (_key$getState10 = key.getState(editorState)) === null || _key$getState10 === void 0 ? void 0 : _key$getState10.isSelectedViaDragHandle) !== null && _key$getState$isSelec !== void 0 ? _key$getState$isSelec : false
320
319
  };
321
320
  if (editorExperiment('platform_editor_controls', 'variant1')) {
322
- var _interactionTrackingP2, _interactionTrackingP3;
321
+ var _interactionTrackingP2, _interactionTrackingP3, _interactionTrackingP4;
323
322
  sharedState.isMouseOut = (_interactionTrackingP2 = (_interactionTrackingP3 = interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP3 === void 0 ? void 0 : _interactionTrackingP3.isMouseOut) !== null && _interactionTrackingP2 !== void 0 ? _interactionTrackingP2 : false;
324
- sharedState.rightSideControlsEnabled = rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true);
325
- if (rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
326
- var _interactionTrackingP4;
327
- sharedState.hoverSide = (_interactionTrackingP4 = interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP4 === void 0 ? void 0 : _interactionTrackingP4.hoverSide;
328
- }
323
+ // rightSideControlsEnabled is the single source of truth (confluence_remix_icon_right_side from preset)
324
+ sharedState.rightSideControlsEnabled = rightSideControlsEnabled;
325
+ sharedState.hoverSide = rightSideControlsEnabled ? (_interactionTrackingP4 = interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP4 === void 0 ? void 0 : _interactionTrackingP4.hoverSide : undefined;
329
326
  }
330
327
  if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
331
328
  var _selectionPreservatio;
@@ -1,4 +1,3 @@
1
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
2
1
  import { clearHoverSide, mouseEnter, mouseLeave, setHoverSide, stopEditing } from './commands';
3
2
  import { getInteractionTrackingState } from './pm-plugin';
4
3
 
@@ -19,12 +18,12 @@ const clearPendingHoverSide = view => {
19
18
  cancelScheduledProcessForView(view);
20
19
  };
21
20
  const BLOCK_SELECTORS = '[data-node-anchor], [data-drag-handler-anchor-name]';
22
- const TABLE_SELECTOR = '[data-prosemirror-node-name="table"]';
23
21
  const RIGHT_EDGE_SELECTOR = '[data-blocks-right-edge-button-container]';
24
- const isInsideTable = element => {
25
- var _element$getAttribute;
26
- return element.closest(TABLE_SELECTOR) !== null || ((_element$getAttribute = element.getAttribute) === null || _element$getAttribute === void 0 ? void 0 : _element$getAttribute.call(element, 'data-prosemirror-node-name')) === 'table';
27
- };
22
+
23
+ /**
24
+ * Process hover position and set left/right side. Only invoked when right-side controls are
25
+ * enabled (confluence_remix_icon_right_side); handleMouseMove returns early otherwise.
26
+ */
28
27
  const processHoverSide = view => {
29
28
  const event = pendingByView.get(view);
30
29
  if (!event) {
@@ -56,13 +55,12 @@ const processHoverSide = view => {
56
55
  return;
57
56
  }
58
57
 
59
- // Use the hovered block's midpoint when hovering over block content.
58
+ // Primary path: depth-1 block (doc direct child). Decorations-anchor sets [data-drag-handler-anchor-depth="1"]
59
+ // on every root block (table, layoutSection, expand, etc.), so we get the whole block without per-type logic.
60
60
  const blockElement = target === null || target === void 0 ? void 0 : target.closest(BLOCK_SELECTORS);
61
- const boundsElement = blockElement instanceof HTMLElement ? blockElement : editorContentArea;
62
-
63
- // Tables show block controls at table level; don't restrict by side so drag handle
64
- // stays visible when hovering anywhere in the table (e.g. paragraph in second cell).
65
- if (isInsideTable(boundsElement)) {
61
+ const depth1Block = blockElement instanceof HTMLElement ? blockElement.closest('[data-drag-handler-anchor-depth="1"]') : null;
62
+ const boundsElement = depth1Block instanceof HTMLElement ? depth1Block : editorContentArea;
63
+ if (!boundsElement) {
66
64
  if ((state === null || state === void 0 ? void 0 : state.hoverSide) !== undefined) {
67
65
  clearHoverSide(view);
68
66
  }
@@ -85,8 +83,8 @@ export const handleMouseMove = (view, event, rightSideControlsEnabled = false) =
85
83
  stopEditing(view);
86
84
  }
87
85
 
88
- // Only track hover side when right-side controls are enabled and experiment is on (performance)
89
- if (!rightSideControlsEnabled || !expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
86
+ // Only track hover side when right-side controls are enabled (single source: confluence_remix_icon_right_side via config)
87
+ if (!rightSideControlsEnabled) {
90
88
  return false;
91
89
  }
92
90
  if (!(event instanceof MouseEvent)) {
@@ -101,7 +99,7 @@ export const handleMouseMove = (view, event, rightSideControlsEnabled = false) =
101
99
  return false;
102
100
  };
103
101
  export const handleMouseLeave = (view, rightSideControlsEnabled = false) => {
104
- if (rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
102
+ if (rightSideControlsEnabled) {
105
103
  clearPendingHoverSide(view);
106
104
  }
107
105
  mouseLeave(view);
@@ -1,7 +1,6 @@
1
1
  import { bind } from 'bind-event-listener';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
3
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
4
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
5
4
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
6
5
  import { handleKeyDown } from './handle-key-down';
7
6
  import { handleMouseEnter, handleMouseLeave, handleMouseMove } from './handle-mouse-move';
@@ -57,12 +56,13 @@ export const createInteractionTrackingPlugin = (rightSideControlsEnabled = false
57
56
  props: {
58
57
  handleKeyDown,
59
58
  handleDOMEvents: {
60
- mousemove: (view, event) => handleMouseMove(view, event, rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true))
59
+ mousemove: (view, event) => handleMouseMove(view, event, rightSideControlsEnabled)
61
60
  }
62
61
  },
63
62
  view: editorExperiment('platform_editor_controls', 'variant1') ? view => {
64
63
  const editorContentArea = view.dom.closest('.ak-editor-content-area');
65
- const remixRightSideEnabled = rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true);
64
+ // rightSideControlsEnabled is the single source of truth (confluence_remix_icon_right_side from preset)
65
+ const remixRightSideEnabled = rightSideControlsEnabled;
66
66
  let unbindMouseEnter;
67
67
  let unbindMouseLeave;
68
68
  let unbindDocumentMouseMove;
@@ -78,19 +78,19 @@ export const createInteractionTrackingPlugin = (rightSideControlsEnabled = false
78
78
  }
79
79
 
80
80
  // Don't set isMouseOut when moving to block controls (right-edge button, drag handle, etc.)
81
- if (rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true) && isMovingToBlockControlsArea(event.relatedTarget)) {
81
+ if (rightSideControlsEnabled && isMovingToBlockControlsArea(event.relatedTarget)) {
82
82
  return;
83
83
  }
84
84
  mouseLeaveTimeoutId = setTimeout(() => {
85
85
  mouseLeaveTimeoutId = null;
86
86
  // Before dispatching, check if mouse has moved to block controls (e.g. through empty space)
87
- if (rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true) && typeof document !== 'undefined') {
87
+ if (rightSideControlsEnabled && typeof document !== 'undefined') {
88
88
  const el = document.elementFromPoint(lastMousePosition.x, lastMousePosition.y);
89
89
  if (el && isMovingToBlockControlsArea(el)) {
90
90
  return;
91
91
  }
92
92
  }
93
- handleMouseLeave(view, rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true));
93
+ handleMouseLeave(view, rightSideControlsEnabled);
94
94
  }, MOUSE_LEAVE_DEBOUNCE_MS);
95
95
  };
96
96
  const cancelScheduledMouseLeave = () => {
@@ -112,7 +112,7 @@ export const createInteractionTrackingPlugin = (rightSideControlsEnabled = false
112
112
  // controls (which may be in portals outside the editor DOM). Without this,
113
113
  // handleDOMEvents.mousemove only fires when over the editor content.
114
114
  if (editorContentArea.contains(event.target) || isMovingToBlockControlsArea(event.target)) {
115
- handleMouseMove(view, event, rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true));
115
+ handleMouseMove(view, event, rightSideControlsEnabled);
116
116
  }
117
117
  },
118
118
  options: {
@@ -494,10 +494,8 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
494
494
  decorations = decorations.add(newState.doc, [quickInsertButton]);
495
495
  if (fg('platform_editor_expose_block_controls_deco_api')) {
496
496
  for (const factory of nodeDecorationRegistry) {
497
- var _activeNode15, _activeNode16, _latestActiveNode1, _latestActiveNode10, _latestActiveNode11, _latestActiveNode12, _latestActiveNode13;
498
- const old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, spec => spec.type === factory.type);
499
- decorations = decorations.remove(old);
500
- const dec = factory.create({
497
+ var _latestActiveNode1, _latestActiveNode10, _latestActiveNode11, _latestActiveNode12, _latestActiveNode13, _activeNode15, _activeNode16;
498
+ const params = {
501
499
  editorState: newState,
502
500
  nodeViewPortalProviderAPI,
503
501
  anchorName: (_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.anchorName,
@@ -505,15 +503,20 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
505
503
  rootPos: (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.rootPos,
506
504
  rootAnchorName: (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.rootAnchorName,
507
505
  rootNodeType: (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.rootNodeType
508
- });
506
+ };
507
+ const old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, spec => spec.type === factory.type);
508
+ decorations = decorations.remove(old);
509
+ const dec = factory.create(params);
509
510
  decorations = decorations.add(newState.doc, [dec]);
510
511
  }
511
512
  }
512
513
  }
513
514
 
514
515
  // In view mode (edit/live pages), show right-side controls on block hover (without drag handle or quick insert)
515
- if (isViewMode && ((_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.rootPos) !== undefined && flags.toolbarFlagsEnabled && rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
516
- const rootPos = latestActiveNode.rootPos;
516
+ const rootPos = (_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.rootPos;
517
+ // rootPos is computed using the same logic as the floating insert menu, so it always points to a top-level (doc child) block when defined.
518
+ const isDocLevel = rootPos !== undefined && !isNaN(rootPos);
519
+ if (isViewMode && isDocLevel && flags.toolbarFlagsEnabled && rightSideControlsEnabled) {
517
520
  for (const factory of nodeDecorationRegistry) {
518
521
  if (factory.showInViewMode) {
519
522
  var _latestActiveNode15, _latestActiveNode16, _latestActiveNode17, _latestActiveNode18, _latestActiveNode19;
@@ -5,7 +5,6 @@ import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
5
5
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
6
6
  import { findParentNode, findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
7
7
  import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
8
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
9
8
  import { isInTextSelection, isNestedNodeSelected, isNonEditableBlock, isSelectionInNode } from '../ui/utils/document-checks';
10
9
  import { createNewLine } from '../ui/utils/editor-commands';
11
10
  import { calculatePosition } from './quick-insert-calculate-position';
@@ -82,7 +81,7 @@ export const createVanillaButton = props => {
82
81
  return;
83
82
  }
84
83
  const isViewMode = editorViewMode === 'view';
85
- const shouldRestrictBySide = rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true) && !isViewMode;
84
+ const shouldRestrictBySide = rightSideControlsEnabled && !isViewMode;
86
85
  // Only restrict by side when hoverSide is known. When undefined, show quick insert.
87
86
  const sideHidden = shouldRestrictBySide && hoverSide !== undefined ? hoverSide !== 'left' : false;
88
87
  if (isTypeAheadOpen || isEditing || sideHidden) {
@@ -110,7 +109,7 @@ export const createVanillaButton = props => {
110
109
  changeDOMVisibility();
111
110
  }));
112
111
  // Only subscribe to view mode when right-side controls are enabled (editorViewMode affects side restriction)
113
- if (rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
112
+ if (rightSideControlsEnabled) {
114
113
  var _props$api$editorView3, _props$api$editorView4, _props$api$editorView5;
115
114
  const unsubscribeViewMode = (_props$api$editorView3 = props.api.editorViewMode) === null || _props$api$editorView3 === void 0 ? void 0 : (_props$api$editorView4 = (_props$api$editorView5 = _props$api$editorView3.sharedState).onChange) === null || _props$api$editorView4 === void 0 ? void 0 : _props$api$editorView4.call(_props$api$editorView5, ({
116
115
  nextSharedState
@@ -10,7 +10,6 @@ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'
10
10
  import { akEditorFullPageNarrowBreakout } from '@atlaskit/editor-shared-styles';
11
11
  // eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- to be migrated to @atlaskit/primitives/compiled – go/akcss
12
12
  import { Box, xcss } from '@atlaskit/primitives';
13
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
14
13
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
15
14
  const RIGHT_CONTROL_HIDE_DELAY_MS = 150;
16
15
  const baseStyles = xcss({
@@ -66,7 +65,8 @@ export const VisibilityContainer = ({
66
65
  };
67
66
  });
68
67
  const isViewMode = editorViewMode === 'view';
69
- const shouldRestrictBySide = rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true) && controlSide !== undefined && !isViewMode;
68
+ // rightSideControlsEnabled is the single source of truth (confluence_remix_icon_right_side from preset)
69
+ const shouldRestrictBySide = rightSideControlsEnabled && controlSide !== undefined && !isViewMode;
70
70
  // Only restrict by side when hoverSide is known (after mousemove). When undefined, show both
71
71
  // controls so drag handle is visible on load and for keyboard-only users.
72
72
  const sideHidden = shouldRestrictBySide && hoverSide !== undefined ? hoverSide !== controlSide : false;
@@ -78,7 +78,7 @@ export const VisibilityContainer = ({
78
78
 
79
79
  // Delay hiding the right control in view mode to reduce flickering when moving from block
80
80
  // toward the right-edge button (avoids rapid show/hide as mouse crosses boundaries).
81
- const isRightControlViewMode = isViewMode && rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true) && controlSide === 'right';
81
+ const isRightControlViewMode = isViewMode && rightSideControlsEnabled && controlSide === 'right';
82
82
  // When in right-control view mode, we delay hiding so start visible; useEffect will update after delay
83
83
  const [delayedShouldHide, setDelayedShouldHide] = useState(false);
84
84
  const hideTimeoutRef = useRef(null);
@@ -6,7 +6,6 @@ import { expandSelectionBounds } from '@atlaskit/editor-common/selection';
6
6
  import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check';
7
7
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
8
8
  import { fg } from '@atlaskit/platform-feature-flags';
9
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
10
9
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
11
10
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
12
11
  import { handleKeyDownWithPreservedSelection } from './editor-commands/handle-key-down-with-preserved-selection';
@@ -50,14 +49,14 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
50
49
  plugin: function plugin(_ref2) {
51
50
  var getIntl = _ref2.getIntl,
52
51
  nodeViewPortalProviderAPI = _ref2.nodeViewPortalProviderAPI;
53
- return createPlugin(api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true));
52
+ return createPlugin(api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled);
54
53
  }
55
54
  }];
56
55
  if (editorExperiment('platform_editor_controls', 'variant1')) {
57
56
  pmPlugins.push({
58
57
  name: 'blockControlsInteractionTrackingPlugin',
59
58
  plugin: function plugin() {
60
- return createInteractionTrackingPlugin(rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true));
59
+ return createInteractionTrackingPlugin(rightSideControlsEnabled);
61
60
  }
62
61
  });
63
62
  }
@@ -329,13 +328,11 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
329
328
  isSelectedViaDragHandle: (_key$getState$isSelec = (_key$getState10 = key.getState(editorState)) === null || _key$getState10 === void 0 ? void 0 : _key$getState10.isSelectedViaDragHandle) !== null && _key$getState$isSelec !== void 0 ? _key$getState$isSelec : false
330
329
  };
331
330
  if (editorExperiment('platform_editor_controls', 'variant1')) {
332
- var _interactionTrackingP2, _interactionTrackingP3;
331
+ var _interactionTrackingP2, _interactionTrackingP3, _interactionTrackingP4;
333
332
  sharedState.isMouseOut = (_interactionTrackingP2 = (_interactionTrackingP3 = interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP3 === void 0 ? void 0 : _interactionTrackingP3.isMouseOut) !== null && _interactionTrackingP2 !== void 0 ? _interactionTrackingP2 : false;
334
- sharedState.rightSideControlsEnabled = rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true);
335
- if (rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
336
- var _interactionTrackingP4;
337
- sharedState.hoverSide = (_interactionTrackingP4 = interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP4 === void 0 ? void 0 : _interactionTrackingP4.hoverSide;
338
- }
333
+ // rightSideControlsEnabled is the single source of truth (confluence_remix_icon_right_side from preset)
334
+ sharedState.rightSideControlsEnabled = rightSideControlsEnabled;
335
+ sharedState.hoverSide = rightSideControlsEnabled ? (_interactionTrackingP4 = interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP4 === void 0 ? void 0 : _interactionTrackingP4.hoverSide : undefined;
339
336
  }
340
337
  if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
341
338
  var _selectionPreservatio;
@@ -1,4 +1,3 @@
1
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
2
1
  import { clearHoverSide, mouseEnter, mouseLeave, setHoverSide, stopEditing } from './commands';
3
2
  import { getInteractionTrackingState } from './pm-plugin';
4
3
 
@@ -19,12 +18,12 @@ var clearPendingHoverSide = function clearPendingHoverSide(view) {
19
18
  cancelScheduledProcessForView(view);
20
19
  };
21
20
  var BLOCK_SELECTORS = '[data-node-anchor], [data-drag-handler-anchor-name]';
22
- var TABLE_SELECTOR = '[data-prosemirror-node-name="table"]';
23
21
  var RIGHT_EDGE_SELECTOR = '[data-blocks-right-edge-button-container]';
24
- var isInsideTable = function isInsideTable(element) {
25
- var _element$getAttribute;
26
- return element.closest(TABLE_SELECTOR) !== null || ((_element$getAttribute = element.getAttribute) === null || _element$getAttribute === void 0 ? void 0 : _element$getAttribute.call(element, 'data-prosemirror-node-name')) === 'table';
27
- };
22
+
23
+ /**
24
+ * Process hover position and set left/right side. Only invoked when right-side controls are
25
+ * enabled (confluence_remix_icon_right_side); handleMouseMove returns early otherwise.
26
+ */
28
27
  var processHoverSide = function processHoverSide(view) {
29
28
  var event = pendingByView.get(view);
30
29
  if (!event) {
@@ -56,13 +55,12 @@ var processHoverSide = function processHoverSide(view) {
56
55
  return;
57
56
  }
58
57
 
59
- // Use the hovered block's midpoint when hovering over block content.
58
+ // Primary path: depth-1 block (doc direct child). Decorations-anchor sets [data-drag-handler-anchor-depth="1"]
59
+ // on every root block (table, layoutSection, expand, etc.), so we get the whole block without per-type logic.
60
60
  var blockElement = target === null || target === void 0 ? void 0 : target.closest(BLOCK_SELECTORS);
61
- var boundsElement = blockElement instanceof HTMLElement ? blockElement : editorContentArea;
62
-
63
- // Tables show block controls at table level; don't restrict by side so drag handle
64
- // stays visible when hovering anywhere in the table (e.g. paragraph in second cell).
65
- if (isInsideTable(boundsElement)) {
61
+ var depth1Block = blockElement instanceof HTMLElement ? blockElement.closest('[data-drag-handler-anchor-depth="1"]') : null;
62
+ var boundsElement = depth1Block instanceof HTMLElement ? depth1Block : editorContentArea;
63
+ if (!boundsElement) {
66
64
  if ((state === null || state === void 0 ? void 0 : state.hoverSide) !== undefined) {
67
65
  clearHoverSide(view);
68
66
  }
@@ -85,8 +83,8 @@ export var handleMouseMove = function handleMouseMove(view, event) {
85
83
  stopEditing(view);
86
84
  }
87
85
 
88
- // Only track hover side when right-side controls are enabled and experiment is on (performance)
89
- if (!rightSideControlsEnabled || !expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
86
+ // Only track hover side when right-side controls are enabled (single source: confluence_remix_icon_right_side via config)
87
+ if (!rightSideControlsEnabled) {
90
88
  return false;
91
89
  }
92
90
  if (!(event instanceof MouseEvent)) {
@@ -102,7 +100,7 @@ export var handleMouseMove = function handleMouseMove(view, event) {
102
100
  };
103
101
  export var handleMouseLeave = function handleMouseLeave(view) {
104
102
  var rightSideControlsEnabled = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
105
- if (rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
103
+ if (rightSideControlsEnabled) {
106
104
  clearPendingHoverSide(view);
107
105
  }
108
106
  mouseLeave(view);
@@ -4,7 +4,6 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
4
4
  import { bind } from 'bind-event-listener';
5
5
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
6
6
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
8
7
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
9
8
  import { handleKeyDown } from './handle-key-down';
10
9
  import { handleMouseEnter, handleMouseLeave, handleMouseMove } from './handle-mouse-move';
@@ -61,13 +60,14 @@ export var createInteractionTrackingPlugin = function createInteractionTrackingP
61
60
  handleKeyDown: handleKeyDown,
62
61
  handleDOMEvents: {
63
62
  mousemove: function mousemove(view, event) {
64
- return handleMouseMove(view, event, rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true));
63
+ return handleMouseMove(view, event, rightSideControlsEnabled);
65
64
  }
66
65
  }
67
66
  },
68
67
  view: editorExperiment('platform_editor_controls', 'variant1') ? function (view) {
69
68
  var editorContentArea = view.dom.closest('.ak-editor-content-area');
70
- var remixRightSideEnabled = rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true);
69
+ // rightSideControlsEnabled is the single source of truth (confluence_remix_icon_right_side from preset)
70
+ var remixRightSideEnabled = rightSideControlsEnabled;
71
71
  var unbindMouseEnter;
72
72
  var unbindMouseLeave;
73
73
  var unbindDocumentMouseMove;
@@ -83,19 +83,19 @@ export var createInteractionTrackingPlugin = function createInteractionTrackingP
83
83
  }
84
84
 
85
85
  // Don't set isMouseOut when moving to block controls (right-edge button, drag handle, etc.)
86
- if (rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true) && isMovingToBlockControlsArea(event.relatedTarget)) {
86
+ if (rightSideControlsEnabled && isMovingToBlockControlsArea(event.relatedTarget)) {
87
87
  return;
88
88
  }
89
89
  mouseLeaveTimeoutId = setTimeout(function () {
90
90
  mouseLeaveTimeoutId = null;
91
91
  // Before dispatching, check if mouse has moved to block controls (e.g. through empty space)
92
- if (rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true) && typeof document !== 'undefined') {
92
+ if (rightSideControlsEnabled && typeof document !== 'undefined') {
93
93
  var el = document.elementFromPoint(lastMousePosition.x, lastMousePosition.y);
94
94
  if (el && isMovingToBlockControlsArea(el)) {
95
95
  return;
96
96
  }
97
97
  }
98
- handleMouseLeave(view, rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true));
98
+ handleMouseLeave(view, rightSideControlsEnabled);
99
99
  }, MOUSE_LEAVE_DEBOUNCE_MS);
100
100
  };
101
101
  var cancelScheduledMouseLeave = function cancelScheduledMouseLeave() {
@@ -117,7 +117,7 @@ export var createInteractionTrackingPlugin = function createInteractionTrackingP
117
117
  // controls (which may be in portals outside the editor DOM). Without this,
118
118
  // handleDOMEvents.mousemove only fires when over the editor content.
119
119
  if (editorContentArea.contains(event.target) || isMovingToBlockControlsArea(event.target)) {
120
- handleMouseMove(view, event, rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true));
120
+ handleMouseMove(view, event, rightSideControlsEnabled);
121
121
  }
122
122
  },
123
123
  options: {
@@ -528,13 +528,9 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
528
528
  _step3;
529
529
  try {
530
530
  var _loop3 = function _loop3() {
531
- var _activeNode15, _activeNode16, _latestActiveNode1, _latestActiveNode10, _latestActiveNode11, _latestActiveNode12, _latestActiveNode13;
531
+ var _latestActiveNode1, _latestActiveNode10, _latestActiveNode11, _latestActiveNode12, _latestActiveNode13, _activeNode15, _activeNode16;
532
532
  var factory = _step3.value;
533
- var old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, function (spec) {
534
- return spec.type === factory.type;
535
- });
536
- decorations = decorations.remove(old);
537
- var dec = factory.create({
533
+ var params = {
538
534
  editorState: newState,
539
535
  nodeViewPortalProviderAPI: nodeViewPortalProviderAPI,
540
536
  anchorName: (_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.anchorName,
@@ -542,7 +538,12 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
542
538
  rootPos: (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.rootPos,
543
539
  rootAnchorName: (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.rootAnchorName,
544
540
  rootNodeType: (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.rootNodeType
541
+ };
542
+ var old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, function (spec) {
543
+ return spec.type === factory.type;
545
544
  });
545
+ decorations = decorations.remove(old);
546
+ var dec = factory.create(params);
546
547
  decorations = decorations.add(newState.doc, [dec]);
547
548
  };
548
549
  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
@@ -557,8 +558,10 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
557
558
  }
558
559
 
559
560
  // In view mode (edit/live pages), show right-side controls on block hover (without drag handle or quick insert)
560
- if (isViewMode && ((_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.rootPos) !== undefined && flags.toolbarFlagsEnabled && rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
561
- var rootPos = latestActiveNode.rootPos;
561
+ var rootPos = (_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.rootPos;
562
+ // rootPos is computed using the same logic as the floating insert menu, so it always points to a top-level (doc child) block when defined.
563
+ var isDocLevel = rootPos !== undefined && !isNaN(rootPos);
564
+ if (isViewMode && isDocLevel && flags.toolbarFlagsEnabled && rightSideControlsEnabled) {
562
565
  var _iterator4 = _createForOfIteratorHelper(nodeDecorationRegistry),
563
566
  _step4;
564
567
  try {
@@ -8,7 +8,6 @@ import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
8
8
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
9
9
  import { findParentNode, findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
11
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
12
11
  import { isInTextSelection, isNestedNodeSelected, isNonEditableBlock, isSelectionInNode } from '../ui/utils/document-checks';
13
12
  import { createNewLine } from '../ui/utils/editor-commands';
14
13
  import { calculatePosition } from './quick-insert-calculate-position';
@@ -84,7 +83,7 @@ export var createVanillaButton = function createVanillaButton(props) {
84
83
  return;
85
84
  }
86
85
  var isViewMode = editorViewMode === 'view';
87
- var shouldRestrictBySide = rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true) && !isViewMode;
86
+ var shouldRestrictBySide = rightSideControlsEnabled && !isViewMode;
88
87
  // Only restrict by side when hoverSide is known. When undefined, show quick insert.
89
88
  var sideHidden = shouldRestrictBySide && hoverSide !== undefined ? hoverSide !== 'left' : false;
90
89
  if (isTypeAheadOpen || isEditing || sideHidden) {
@@ -110,7 +109,7 @@ export var createVanillaButton = function createVanillaButton(props) {
110
109
  changeDOMVisibility();
111
110
  }));
112
111
  // Only subscribe to view mode when right-side controls are enabled (editorViewMode affects side restriction)
113
- if (rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
112
+ if (rightSideControlsEnabled) {
114
113
  var _props$api$editorView2, _props$api$editorView3, _props$api$editorView4;
115
114
  var unsubscribeViewMode = (_props$api$editorView2 = props.api.editorViewMode) === null || _props$api$editorView2 === void 0 || (_props$api$editorView3 = (_props$api$editorView4 = _props$api$editorView2.sharedState).onChange) === null || _props$api$editorView3 === void 0 ? void 0 : _props$api$editorView3.call(_props$api$editorView4, function (_ref4) {
116
115
  var nextSharedState = _ref4.nextSharedState;
@@ -12,7 +12,6 @@ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'
12
12
  import { akEditorFullPageNarrowBreakout } from '@atlaskit/editor-shared-styles';
13
13
  // eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- to be migrated to @atlaskit/primitives/compiled – go/akcss
14
14
  import { Box, xcss } from '@atlaskit/primitives';
15
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
16
15
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
17
16
  var RIGHT_CONTROL_HIDE_DELAY_MS = 150;
18
17
  var baseStyles = xcss({
@@ -64,7 +63,8 @@ export var VisibilityContainer = function VisibilityContainer(_ref) {
64
63
  userIntent = _useSharedPluginState.userIntent,
65
64
  rightSideControlsEnabled = _useSharedPluginState.rightSideControlsEnabled;
66
65
  var isViewMode = editorViewMode === 'view';
67
- var shouldRestrictBySide = rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true) && controlSide !== undefined && !isViewMode;
66
+ // rightSideControlsEnabled is the single source of truth (confluence_remix_icon_right_side from preset)
67
+ var shouldRestrictBySide = rightSideControlsEnabled && controlSide !== undefined && !isViewMode;
68
68
  // Only restrict by side when hoverSide is known (after mousemove). When undefined, show both
69
69
  // controls so drag handle is visible on load and for keyboard-only users.
70
70
  var sideHidden = shouldRestrictBySide && hoverSide !== undefined ? hoverSide !== controlSide : false;
@@ -76,7 +76,7 @@ export var VisibilityContainer = function VisibilityContainer(_ref) {
76
76
 
77
77
  // Delay hiding the right control in view mode to reduce flickering when moving from block
78
78
  // toward the right-edge button (avoids rapid show/hide as mouse crosses boundaries).
79
- var isRightControlViewMode = isViewMode && rightSideControlsEnabled && expValEquals('confluence_remix_icon_right_side', 'isEnabled', true) && controlSide === 'right';
79
+ var isRightControlViewMode = isViewMode && rightSideControlsEnabled && controlSide === 'right';
80
80
  // When in right-control view mode, we delay hiding so start visible; useEffect will update after delay
81
81
  var _useState = useState(false),
82
82
  _useState2 = _slicedToArray(_useState, 2),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-controls",
3
- "version": "8.8.2",
3
+ "version": "8.9.0",
4
4
  "description": "Block controls plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -57,7 +57,7 @@
57
57
  "@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "^3.2.0",
58
58
  "@atlaskit/primitives": "^18.0.0",
59
59
  "@atlaskit/theme": "^22.0.0",
60
- "@atlaskit/tmp-editor-statsig": "^32.6.0",
60
+ "@atlaskit/tmp-editor-statsig": "^32.11.0",
61
61
  "@atlaskit/tokens": "^11.0.0",
62
62
  "@atlaskit/tooltip": "^20.14.0",
63
63
  "@babel/runtime": "^7.0.0",
@@ -68,7 +68,7 @@
68
68
  "uuid": "^3.1.0"
69
69
  },
70
70
  "peerDependencies": {
71
- "@atlaskit/editor-common": "^111.23.0",
71
+ "@atlaskit/editor-common": "^111.25.0",
72
72
  "react": "^18.2.0",
73
73
  "react-dom": "^18.2.0",
74
74
  "react-intl-next": "npm:react-intl@^5.18.1"