@atlaskit/editor-plugin-block-controls 12.4.1 → 13.0.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 +38 -0
- package/block-decoration-utils/anchor-name/package.json +1 -8
- package/block-decoration-utils/consts/package.json +1 -8
- package/block-decoration-utils/dom-attr-name/package.json +1 -8
- package/block-decoration-utils/drag-handle-positions/package.json +1 -8
- package/block-decoration-utils/package.json +1 -8
- package/block-decoration-utils/visibility-container/package.json +1 -8
- package/block-decoration-utils/widget-positions/package.json +1 -8
- package/blockControlsPlugin/package.json +1 -8
- package/blockControlsPluginType/package.json +1 -8
- package/dist/cjs/blockControlsPlugin.js +1 -1
- package/dist/cjs/pm-plugins/interaction-tracking/constants.js +8 -0
- package/dist/cjs/pm-plugins/interaction-tracking/handle-mouse-move.js +102 -14
- package/dist/cjs/pm-plugins/interaction-tracking/pm-plugin.js +44 -10
- package/dist/cjs/ui/visibility-container.js +2 -0
- package/dist/es2019/blockControlsPlugin.js +1 -1
- package/dist/es2019/pm-plugins/interaction-tracking/constants.js +2 -0
- package/dist/es2019/pm-plugins/interaction-tracking/handle-mouse-move.js +86 -13
- package/dist/es2019/pm-plugins/interaction-tracking/pm-plugin.js +44 -11
- package/dist/es2019/ui/visibility-container.js +2 -0
- package/dist/esm/blockControlsPlugin.js +1 -1
- package/dist/esm/pm-plugins/interaction-tracking/constants.js +2 -0
- package/dist/esm/pm-plugins/interaction-tracking/handle-mouse-move.js +102 -14
- package/dist/esm/pm-plugins/interaction-tracking/pm-plugin.js +44 -10
- package/dist/esm/ui/visibility-container.js +2 -0
- package/dist/types/pm-plugins/interaction-tracking/constants.d.ts +2 -0
- package/dist/types/pm-plugins/interaction-tracking/handle-mouse-move.d.ts +3 -1
- package/dist/types/pm-plugins/interaction-tracking/pm-plugin.d.ts +3 -1
- package/package.json +37 -41
- package/dist/types-ts4.5/blockControlsPlugin.d.ts +0 -2
- package/dist/types-ts4.5/blockControlsPluginType.d.ts +0 -250
- package/dist/types-ts4.5/editor-commands/handle-key-down-with-preserved-selection.d.ts +0 -17
- package/dist/types-ts4.5/editor-commands/map-preserved-selection.d.ts +0 -13
- package/dist/types-ts4.5/editor-commands/move-node-with-block-menu.d.ts +0 -4
- package/dist/types-ts4.5/editor-commands/move-node.d.ts +0 -5
- package/dist/types-ts4.5/editor-commands/move-to-layout.d.ts +0 -7
- package/dist/types-ts4.5/editor-commands/show-drag-handle.d.ts +0 -3
- package/dist/types-ts4.5/editor-commands/utils/move-node-utils.d.ts +0 -31
- package/dist/types-ts4.5/entry-points/block-decoration-utils-anchor-name.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/block-decoration-utils-consts.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/block-decoration-utils-dom-attr-name.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/block-decoration-utils-drag-handle-positions.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/block-decoration-utils-visibility-container.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/block-decoration-utils-widget-positions.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/blockControlsPlugin.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/blockControlsPluginType.d.ts +0 -1
- package/dist/types-ts4.5/index.d.ts +0 -2
- package/dist/types-ts4.5/pm-plugins/decorations-anchor.d.ts +0 -15
- package/dist/types-ts4.5/pm-plugins/decorations-common.d.ts +0 -9
- package/dist/types-ts4.5/pm-plugins/decorations-drag-handle.d.ts +0 -30
- package/dist/types-ts4.5/pm-plugins/decorations-drop-target-active.d.ts +0 -13
- package/dist/types-ts4.5/pm-plugins/decorations-drop-target.d.ts +0 -21
- package/dist/types-ts4.5/pm-plugins/decorations-find-surrounding-nodes.d.ts +0 -22
- package/dist/types-ts4.5/pm-plugins/decorations-quick-insert-button.d.ts +0 -30
- package/dist/types-ts4.5/pm-plugins/first-node-dec-plugin.d.ts +0 -5
- package/dist/types-ts4.5/pm-plugins/handle-mouse-down.d.ts +0 -4
- package/dist/types-ts4.5/pm-plugins/handle-mouse-over.d.ts +0 -4
- package/dist/types-ts4.5/pm-plugins/interaction-tracking/commands.d.ts +0 -7
- package/dist/types-ts4.5/pm-plugins/interaction-tracking/handle-key-down.d.ts +0 -2
- package/dist/types-ts4.5/pm-plugins/interaction-tracking/handle-mouse-move.d.ts +0 -4
- package/dist/types-ts4.5/pm-plugins/interaction-tracking/pm-plugin.d.ts +0 -20
- package/dist/types-ts4.5/pm-plugins/keymap.d.ts +0 -5
- package/dist/types-ts4.5/pm-plugins/main.d.ts +0 -63
- package/dist/types-ts4.5/pm-plugins/quick-insert-calculate-position.d.ts +0 -12
- package/dist/types-ts4.5/pm-plugins/selection-preservation/editor-commands.d.ts +0 -13
- package/dist/types-ts4.5/pm-plugins/selection-preservation/plugin-key.d.ts +0 -3
- package/dist/types-ts4.5/pm-plugins/selection-preservation/pm-plugin.d.ts +0 -35
- package/dist/types-ts4.5/pm-plugins/selection-preservation/types.d.ts +0 -7
- package/dist/types-ts4.5/pm-plugins/selection-preservation/utils.d.ts +0 -30
- package/dist/types-ts4.5/pm-plugins/utils/active-anchor-tracker.d.ts +0 -16
- package/dist/types-ts4.5/pm-plugins/utils/analytics.d.ts +0 -12
- package/dist/types-ts4.5/pm-plugins/utils/anchor-utils.d.ts +0 -27
- package/dist/types-ts4.5/pm-plugins/utils/check-fragment.d.ts +0 -9
- package/dist/types-ts4.5/pm-plugins/utils/check-media-layout.d.ts +0 -2
- package/dist/types-ts4.5/pm-plugins/utils/consts.d.ts +0 -2
- package/dist/types-ts4.5/pm-plugins/utils/drag-handle-positions.d.ts +0 -12
- package/dist/types-ts4.5/pm-plugins/utils/expand-and-update-selection.d.ts +0 -20
- package/dist/types-ts4.5/pm-plugins/utils/getNestedNodePosition.d.ts +0 -16
- package/dist/types-ts4.5/pm-plugins/utils/getSelection.d.ts +0 -32
- package/dist/types-ts4.5/pm-plugins/utils/inline-drop-target.d.ts +0 -7
- package/dist/types-ts4.5/pm-plugins/utils/marks.d.ts +0 -25
- package/dist/types-ts4.5/pm-plugins/utils/remove-from-source.d.ts +0 -3
- package/dist/types-ts4.5/pm-plugins/utils/selection.d.ts +0 -71
- package/dist/types-ts4.5/pm-plugins/utils/transactions.d.ts +0 -31
- package/dist/types-ts4.5/pm-plugins/utils/update-column-widths.d.ts +0 -6
- package/dist/types-ts4.5/pm-plugins/utils/update-selection.d.ts +0 -4
- package/dist/types-ts4.5/pm-plugins/utils/validation.d.ts +0 -24
- package/dist/types-ts4.5/pm-plugins/utils/widget-positions.d.ts +0 -14
- package/dist/types-ts4.5/pm-plugins/vanilla-quick-insert.d.ts +0 -21
- package/dist/types-ts4.5/ui/block-decoration-utils.d.ts +0 -6
- package/dist/types-ts4.5/ui/consts.d.ts +0 -67
- package/dist/types-ts4.5/ui/drag-handle-nested-icon.d.ts +0 -12
- package/dist/types-ts4.5/ui/drag-handle.d.ts +0 -20
- package/dist/types-ts4.5/ui/drag-preview.d.ts +0 -9
- package/dist/types-ts4.5/ui/drop-target-layout.d.ts +0 -18
- package/dist/types-ts4.5/ui/drop-target.d.ts +0 -22
- package/dist/types-ts4.5/ui/global-styles.d.ts +0 -10
- package/dist/types-ts4.5/ui/inline-drop-target.d.ts +0 -7
- package/dist/types-ts4.5/ui/quick-insert-button.d.ts +0 -24
- package/dist/types-ts4.5/ui/utils/anchor-name.d.ts +0 -12
- package/dist/types-ts4.5/ui/utils/document-checks.d.ts +0 -5
- package/dist/types-ts4.5/ui/utils/dom-attr-name.d.ts +0 -5
- package/dist/types-ts4.5/ui/utils/editor-commands.d.ts +0 -2
- package/dist/types-ts4.5/ui/visibility-container.d.ts +0 -16
|
@@ -2,15 +2,42 @@ 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
4
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
5
|
+
import { RIGHT_MARGIN_ROVO_GAP_PX } from './constants';
|
|
5
6
|
import { handleKeyDown } from './handle-key-down';
|
|
6
7
|
import { handleMouseEnter, handleMouseLeave, handleMouseMove } from './handle-mouse-move';
|
|
7
8
|
|
|
8
9
|
/** Elements that extend the editor hover area (block controls, right-edge button, etc.) */
|
|
9
10
|
const BLOCK_CONTROLS_HOVER_AREA_SELECTOR = '[data-blocks-right-edge-button-container], [data-blocks-drag-handle-container], [data-testid="block-ctrl-drag-handle"], [data-testid="block-ctrl-drag-handle-container"], [data-testid="block-ctrl-decorator-widget"], [data-testid="block-ctrl-quick-insert-button"]';
|
|
10
11
|
const MOUSE_LEAVE_DEBOUNCE_MS = 200;
|
|
12
|
+
|
|
13
|
+
/** ClickAreaBlock overlay that wraps the editor content and covers the right margin. */
|
|
14
|
+
const CLICK_AREA_SELECTOR = '[data-editor-click-wrapper]';
|
|
11
15
|
const isMovingToBlockControlsArea = target => target instanceof Element && !!target.closest(BLOCK_CONTROLS_HOVER_AREA_SELECTOR);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The right margin is covered by the ClickAreaBlock overlay, which sits outside .ak-editor-content-area.
|
|
19
|
+
* Hovering there should still surface the right-side Remix button, so keep controls alive — but only on
|
|
20
|
+
* the right (past the content's right edge), and not in the far-right Rovo gap. The left gutter must
|
|
21
|
+
* dismiss like the experiment-off path, so it is treated as inactive.
|
|
22
|
+
*/
|
|
23
|
+
const isOverActiveClickArea = (target, clientX) => {
|
|
24
|
+
var _clickArea$querySelec, _target$ownerDocument, _target$ownerDocument2;
|
|
25
|
+
if (!(target instanceof Element)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const clickArea = target.closest(CLICK_AREA_SELECTOR);
|
|
29
|
+
if (!clickArea) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const contentRight = (_clickArea$querySelec = clickArea.querySelector('.ak-editor-content-area')) === null || _clickArea$querySelec === void 0 ? void 0 : _clickArea$querySelec.getBoundingClientRect().right;
|
|
33
|
+
if (contentRight !== undefined && clientX <= contentRight) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const innerWidth = (_target$ownerDocument = (_target$ownerDocument2 = target.ownerDocument.defaultView) === null || _target$ownerDocument2 === void 0 ? void 0 : _target$ownerDocument2.innerWidth) !== null && _target$ownerDocument !== void 0 ? _target$ownerDocument : Number.POSITIVE_INFINITY;
|
|
37
|
+
return clientX <= innerWidth - RIGHT_MARGIN_ROVO_GAP_PX;
|
|
38
|
+
};
|
|
12
39
|
export const interactionTrackingPluginKey = new PluginKey('interactionTrackingPlugin');
|
|
13
|
-
export const createInteractionTrackingPlugin = (rightSideControlsEnabled = false) => {
|
|
40
|
+
export const createInteractionTrackingPlugin = (rightSideControlsEnabled = false, api) => {
|
|
14
41
|
return new SafePlugin({
|
|
15
42
|
key: interactionTrackingPluginKey,
|
|
16
43
|
state: {
|
|
@@ -56,7 +83,7 @@ export const createInteractionTrackingPlugin = (rightSideControlsEnabled = false
|
|
|
56
83
|
props: {
|
|
57
84
|
handleKeyDown,
|
|
58
85
|
handleDOMEvents: {
|
|
59
|
-
mousemove: (view, event) => handleMouseMove(view, event, rightSideControlsEnabled)
|
|
86
|
+
mousemove: (view, event) => handleMouseMove(view, event, rightSideControlsEnabled, api)
|
|
60
87
|
}
|
|
61
88
|
},
|
|
62
89
|
view: editorExperiment('platform_editor_controls', 'variant1') ? view => {
|
|
@@ -71,22 +98,28 @@ export const createInteractionTrackingPlugin = (rightSideControlsEnabled = false
|
|
|
71
98
|
x: 0,
|
|
72
99
|
y: 0
|
|
73
100
|
};
|
|
101
|
+
|
|
102
|
+
// The active right margin only counts as "still hovering" when our experiment is on;
|
|
103
|
+
// otherwise leaving the content area (e.g. exiting left) must dismiss as on master.
|
|
104
|
+
const marginHoverEnabled = editorExperiment('remix_button_right_margin_hover', true);
|
|
74
105
|
const scheduleMouseLeave = event => {
|
|
75
106
|
if (mouseLeaveTimeoutId) {
|
|
76
107
|
clearTimeout(mouseLeaveTimeoutId);
|
|
77
108
|
mouseLeaveTimeoutId = null;
|
|
78
109
|
}
|
|
79
110
|
|
|
80
|
-
//
|
|
81
|
-
|
|
111
|
+
// Keep controls visible when moving to block controls (or, with the experiment on,
|
|
112
|
+
// the active right margin — the Rovo gap is excluded so controls still clear there).
|
|
113
|
+
if (rightSideControlsEnabled && (isMovingToBlockControlsArea(event.relatedTarget) || marginHoverEnabled && isOverActiveClickArea(event.relatedTarget, event.clientX))) {
|
|
82
114
|
return;
|
|
83
115
|
}
|
|
84
116
|
mouseLeaveTimeoutId = setTimeout(() => {
|
|
85
117
|
mouseLeaveTimeoutId = null;
|
|
86
|
-
//
|
|
118
|
+
// Re-check after the debounce: keep controls if the cursor landed on block controls
|
|
119
|
+
// (or, with the experiment on, the active right margin).
|
|
87
120
|
if (rightSideControlsEnabled && typeof document !== 'undefined') {
|
|
88
121
|
const el = document.elementFromPoint(lastMousePosition.x, lastMousePosition.y);
|
|
89
|
-
if (el && isMovingToBlockControlsArea(el)) {
|
|
122
|
+
if (el && (isMovingToBlockControlsArea(el) || marginHoverEnabled && isOverActiveClickArea(el, lastMousePosition.x))) {
|
|
90
123
|
return;
|
|
91
124
|
}
|
|
92
125
|
}
|
|
@@ -108,11 +141,11 @@ export const createInteractionTrackingPlugin = (rightSideControlsEnabled = false
|
|
|
108
141
|
x: event.clientX,
|
|
109
142
|
y: event.clientY
|
|
110
143
|
};
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
if (editorContentArea.contains(event.target) || isMovingToBlockControlsArea(event.target)) {
|
|
115
|
-
handleMouseMove(view, event, rightSideControlsEnabled);
|
|
144
|
+
// Catches block controls in portals that handleDOMEvents.mousemove misses.
|
|
145
|
+
// The right-margin overlay is only relevant with the experiment on.
|
|
146
|
+
const overClickArea = marginHoverEnabled && event.target instanceof Element && !!event.target.closest(CLICK_AREA_SELECTOR);
|
|
147
|
+
if (editorContentArea.contains(event.target) || isMovingToBlockControlsArea(event.target) || overClickArea) {
|
|
148
|
+
handleMouseMove(view, event, rightSideControlsEnabled, api);
|
|
116
149
|
}
|
|
117
150
|
},
|
|
118
151
|
options: {
|
|
@@ -68,6 +68,8 @@ export const VisibilityContainer = ({
|
|
|
68
68
|
});
|
|
69
69
|
const isViewMode = editorViewMode === 'view';
|
|
70
70
|
// rightSideControlsEnabled is the single source of truth (confluence_remix_button_right_side_block_fg from preset)
|
|
71
|
+
// Both controls are restricted by side: the drag handle shows on the left half, the Remix button on
|
|
72
|
+
// the right half (and the right margin), matching the midpoint split in handle-mouse-move.
|
|
71
73
|
const shouldRestrictBySide = rightSideControlsEnabled && controlSide !== undefined && !isViewMode;
|
|
72
74
|
// Only restrict by side when hoverSide is known (after mousemove). When undefined, show both
|
|
73
75
|
// controls so drag handle is visible on load and for keyboard-only users.
|
|
@@ -83,7 +83,7 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
|
|
|
83
83
|
pmPlugins.push({
|
|
84
84
|
name: 'blockControlsInteractionTrackingPlugin',
|
|
85
85
|
plugin: function plugin() {
|
|
86
|
-
return createInteractionTrackingPlugin(rightSideControlsEnabled);
|
|
86
|
+
return createInteractionTrackingPlugin(rightSideControlsEnabled, api);
|
|
87
87
|
}
|
|
88
88
|
});
|
|
89
89
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
2
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
3
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
4
|
+
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
5
|
+
import { handleMouseOver } from '../handle-mouse-over';
|
|
1
6
|
import { clearHoverSide, mouseEnter, mouseLeave, setHoverSide, stopEditing } from './commands';
|
|
7
|
+
import { RIGHT_MARGIN_ROVO_GAP_PX } from './constants';
|
|
2
8
|
import { getInteractionTrackingState } from './pm-plugin';
|
|
3
9
|
|
|
4
10
|
/** Per-view pending hover state; avoids cross-editor singleton. */
|
|
@@ -20,11 +26,67 @@ var clearPendingHoverSide = function clearPendingHoverSide(view) {
|
|
|
20
26
|
var BLOCK_SELECTORS = '[data-node-anchor], [data-drag-handler-anchor-name]';
|
|
21
27
|
var RIGHT_EDGE_SELECTOR = '[data-blocks-right-edge-button-container]';
|
|
22
28
|
|
|
29
|
+
// Top-level blocks (no block ancestor), matched by anchor attribute so it works under both the
|
|
30
|
+
// legacy and native-anchor schemes.
|
|
31
|
+
var getRootBlocks = function getRootBlocks(view) {
|
|
32
|
+
return Array.from(view.dom.querySelectorAll(BLOCK_SELECTORS)).filter(function (el) {
|
|
33
|
+
var _el$parentElement;
|
|
34
|
+
return !((_el$parentElement = el.parentElement) !== null && _el$parentElement !== void 0 && _el$parentElement.closest(BLOCK_SELECTORS));
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Find the root block whose vertical bounds contain clientY. Returns the measured rect so callers
|
|
39
|
+
// can reuse it without re-measuring.
|
|
40
|
+
var findBlockAtY = function findBlockAtY(view, clientY) {
|
|
41
|
+
var _iterator = _createForOfIteratorHelper(getRootBlocks(view)),
|
|
42
|
+
_step;
|
|
43
|
+
try {
|
|
44
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
45
|
+
var el = _step.value;
|
|
46
|
+
var rect = el.getBoundingClientRect();
|
|
47
|
+
if (clientY >= rect.top && clientY <= rect.bottom) {
|
|
48
|
+
return {
|
|
49
|
+
block: el,
|
|
50
|
+
rect: rect
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} catch (err) {
|
|
55
|
+
_iterator.e(err);
|
|
56
|
+
} finally {
|
|
57
|
+
_iterator.f();
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
};
|
|
61
|
+
var getRightMarginBoundary = function getRightMarginBoundary(view) {
|
|
62
|
+
var _view$dom$ownerDocume, _view$dom$ownerDocume2;
|
|
63
|
+
return ((_view$dom$ownerDocume = (_view$dom$ownerDocume2 = view.dom.ownerDocument.defaultView) === null || _view$dom$ownerDocume2 === void 0 ? void 0 : _view$dom$ownerDocume2.innerWidth) !== null && _view$dom$ownerDocume !== void 0 ? _view$dom$ownerDocume : Number.POSITIVE_INFINITY) - RIGHT_MARGIN_ROVO_GAP_PX;
|
|
64
|
+
};
|
|
65
|
+
// not in the right margin
|
|
66
|
+
|
|
67
|
+
// Classify where the cursor sits in the right margin beside the block at its height, running the
|
|
68
|
+
// block lookup once so the caller doesn't traverse the DOM twice per mousemove.
|
|
69
|
+
var classifyRightMarginPosition = function classifyRightMarginPosition(view, event) {
|
|
70
|
+
var found = findBlockAtY(view, event.clientY);
|
|
71
|
+
if (!found) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (event.clientX <= found.rect.right) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return event.clientX > getRightMarginBoundary(view) ? {
|
|
78
|
+
type: 'gap'
|
|
79
|
+
} : {
|
|
80
|
+
type: 'active',
|
|
81
|
+
block: found.block
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
23
85
|
/**
|
|
24
86
|
* Process hover position and set left/right side. Only invoked when right-side controls are
|
|
25
87
|
* enabled (confluence_remix_button_right_side_block_fg); handleMouseMove returns early otherwise.
|
|
26
88
|
*/
|
|
27
|
-
var processHoverSide = function processHoverSide(view) {
|
|
89
|
+
var processHoverSide = function processHoverSide(view, api) {
|
|
28
90
|
var event = pendingByView.get(view);
|
|
29
91
|
if (!event) {
|
|
30
92
|
return;
|
|
@@ -55,20 +117,45 @@ var processHoverSide = function processHoverSide(view) {
|
|
|
55
117
|
return;
|
|
56
118
|
}
|
|
57
119
|
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
120
|
+
// Added right-margin hover, gated so it can be rolled back. When off, fall through to midpoint.
|
|
121
|
+
if (editorExperiment('remix_button_right_margin_hover', true)) {
|
|
122
|
+
var closestBlock = target === null || target === void 0 ? void 0 : target.closest(BLOCK_SELECTORS);
|
|
123
|
+
var blockElement = closestBlock instanceof HTMLElement ? closestBlock : null;
|
|
124
|
+
|
|
125
|
+
// Not over a block: the cursor may be in the right margin beside content.
|
|
126
|
+
var marginZone = blockElement ? null : classifyRightMarginPosition(view, event);
|
|
127
|
+
if ((marginZone === null || marginZone === void 0 ? void 0 : marginZone.type) === 'active' && api) {
|
|
128
|
+
// handleMouseOver only reads event.target, so a target-only stand-in is enough here.
|
|
129
|
+
handleMouseOver(view, {
|
|
130
|
+
target: marginZone.block
|
|
131
|
+
}, api);
|
|
132
|
+
// mouseenter doesn't fire over the click overlay, so clear isMouseOut here to re-show.
|
|
133
|
+
if (state !== null && state !== void 0 && state.isMouseOut) {
|
|
134
|
+
mouseEnter(view);
|
|
135
|
+
}
|
|
136
|
+
if ((state === null || state === void 0 ? void 0 : state.hoverSide) !== 'right') {
|
|
137
|
+
setHoverSide(view, 'right');
|
|
138
|
+
}
|
|
139
|
+
return;
|
|
66
140
|
}
|
|
67
|
-
|
|
141
|
+
|
|
142
|
+
// In the Rovo gap, dismiss the controls so the button doesn't linger over the Rovo button.
|
|
143
|
+
if ((marginZone === null || marginZone === void 0 ? void 0 : marginZone.type) === 'gap' || event.clientX > getRightMarginBoundary(view)) {
|
|
144
|
+
if (!(state !== null && state !== void 0 && state.isMouseOut)) {
|
|
145
|
+
clearHoverSide(view);
|
|
146
|
+
mouseLeave(view);
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Over a block: fall through to the midpoint split so the left half keeps the drag handle and
|
|
152
|
+
// the right half shows the Remix button, matching the experiment-off behaviour.
|
|
68
153
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
154
|
+
|
|
155
|
+
// Pick the side from the content midpoint, keeping the original left/right halves.
|
|
156
|
+
var _editorContentArea$ge = editorContentArea.getBoundingClientRect(),
|
|
157
|
+
left = _editorContentArea$ge.left,
|
|
158
|
+
right = _editorContentArea$ge.right;
|
|
72
159
|
var midpoint = (left + right) / 2;
|
|
73
160
|
var nextHoverSide = event.clientX > midpoint ? 'right' : 'left';
|
|
74
161
|
if ((state === null || state === void 0 ? void 0 : state.hoverSide) !== nextHoverSide) {
|
|
@@ -77,6 +164,7 @@ var processHoverSide = function processHoverSide(view) {
|
|
|
77
164
|
};
|
|
78
165
|
export var handleMouseMove = function handleMouseMove(view, event) {
|
|
79
166
|
var rightSideControlsEnabled = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
167
|
+
var api = arguments.length > 3 ? arguments[3] : undefined;
|
|
80
168
|
var state = getInteractionTrackingState(view.state);
|
|
81
169
|
// if user has stopped editing and moved their mouse, show block controls again
|
|
82
170
|
if (state !== null && state !== void 0 && state.isEditing) {
|
|
@@ -93,7 +181,7 @@ export var handleMouseMove = function handleMouseMove(view, event) {
|
|
|
93
181
|
pendingByView.set(view, event);
|
|
94
182
|
cancelScheduledProcessForView(view);
|
|
95
183
|
var id = requestAnimationFrame(function () {
|
|
96
|
-
processHoverSide(view);
|
|
184
|
+
processHoverSide(view, api);
|
|
97
185
|
});
|
|
98
186
|
rafIdByView.set(view, id);
|
|
99
187
|
return false;
|
|
@@ -5,18 +5,46 @@ 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
7
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
8
|
+
import { RIGHT_MARGIN_ROVO_GAP_PX } from './constants';
|
|
8
9
|
import { handleKeyDown } from './handle-key-down';
|
|
9
10
|
import { handleMouseEnter, handleMouseLeave, handleMouseMove } from './handle-mouse-move';
|
|
10
11
|
|
|
11
12
|
/** Elements that extend the editor hover area (block controls, right-edge button, etc.) */
|
|
12
13
|
var BLOCK_CONTROLS_HOVER_AREA_SELECTOR = '[data-blocks-right-edge-button-container], [data-blocks-drag-handle-container], [data-testid="block-ctrl-drag-handle"], [data-testid="block-ctrl-drag-handle-container"], [data-testid="block-ctrl-decorator-widget"], [data-testid="block-ctrl-quick-insert-button"]';
|
|
13
14
|
var MOUSE_LEAVE_DEBOUNCE_MS = 200;
|
|
15
|
+
|
|
16
|
+
/** ClickAreaBlock overlay that wraps the editor content and covers the right margin. */
|
|
17
|
+
var CLICK_AREA_SELECTOR = '[data-editor-click-wrapper]';
|
|
14
18
|
var isMovingToBlockControlsArea = function isMovingToBlockControlsArea(target) {
|
|
15
19
|
return target instanceof Element && !!target.closest(BLOCK_CONTROLS_HOVER_AREA_SELECTOR);
|
|
16
20
|
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The right margin is covered by the ClickAreaBlock overlay, which sits outside .ak-editor-content-area.
|
|
24
|
+
* Hovering there should still surface the right-side Remix button, so keep controls alive — but only on
|
|
25
|
+
* the right (past the content's right edge), and not in the far-right Rovo gap. The left gutter must
|
|
26
|
+
* dismiss like the experiment-off path, so it is treated as inactive.
|
|
27
|
+
*/
|
|
28
|
+
var isOverActiveClickArea = function isOverActiveClickArea(target, clientX) {
|
|
29
|
+
var _clickArea$querySelec, _target$ownerDocument, _target$ownerDocument2;
|
|
30
|
+
if (!(target instanceof Element)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
var clickArea = target.closest(CLICK_AREA_SELECTOR);
|
|
34
|
+
if (!clickArea) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
var contentRight = (_clickArea$querySelec = clickArea.querySelector('.ak-editor-content-area')) === null || _clickArea$querySelec === void 0 ? void 0 : _clickArea$querySelec.getBoundingClientRect().right;
|
|
38
|
+
if (contentRight !== undefined && clientX <= contentRight) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
var innerWidth = (_target$ownerDocument = (_target$ownerDocument2 = target.ownerDocument.defaultView) === null || _target$ownerDocument2 === void 0 ? void 0 : _target$ownerDocument2.innerWidth) !== null && _target$ownerDocument !== void 0 ? _target$ownerDocument : Number.POSITIVE_INFINITY;
|
|
42
|
+
return clientX <= innerWidth - RIGHT_MARGIN_ROVO_GAP_PX;
|
|
43
|
+
};
|
|
17
44
|
export var interactionTrackingPluginKey = new PluginKey('interactionTrackingPlugin');
|
|
18
45
|
export var createInteractionTrackingPlugin = function createInteractionTrackingPlugin() {
|
|
19
46
|
var rightSideControlsEnabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
|
47
|
+
var api = arguments.length > 1 ? arguments[1] : undefined;
|
|
20
48
|
return new SafePlugin({
|
|
21
49
|
key: interactionTrackingPluginKey,
|
|
22
50
|
state: {
|
|
@@ -60,7 +88,7 @@ export var createInteractionTrackingPlugin = function createInteractionTrackingP
|
|
|
60
88
|
handleKeyDown: handleKeyDown,
|
|
61
89
|
handleDOMEvents: {
|
|
62
90
|
mousemove: function mousemove(view, event) {
|
|
63
|
-
return handleMouseMove(view, event, rightSideControlsEnabled);
|
|
91
|
+
return handleMouseMove(view, event, rightSideControlsEnabled, api);
|
|
64
92
|
}
|
|
65
93
|
}
|
|
66
94
|
},
|
|
@@ -76,22 +104,28 @@ export var createInteractionTrackingPlugin = function createInteractionTrackingP
|
|
|
76
104
|
x: 0,
|
|
77
105
|
y: 0
|
|
78
106
|
};
|
|
107
|
+
|
|
108
|
+
// The active right margin only counts as "still hovering" when our experiment is on;
|
|
109
|
+
// otherwise leaving the content area (e.g. exiting left) must dismiss as on master.
|
|
110
|
+
var marginHoverEnabled = editorExperiment('remix_button_right_margin_hover', true);
|
|
79
111
|
var scheduleMouseLeave = function scheduleMouseLeave(event) {
|
|
80
112
|
if (mouseLeaveTimeoutId) {
|
|
81
113
|
clearTimeout(mouseLeaveTimeoutId);
|
|
82
114
|
mouseLeaveTimeoutId = null;
|
|
83
115
|
}
|
|
84
116
|
|
|
85
|
-
//
|
|
86
|
-
|
|
117
|
+
// Keep controls visible when moving to block controls (or, with the experiment on,
|
|
118
|
+
// the active right margin — the Rovo gap is excluded so controls still clear there).
|
|
119
|
+
if (rightSideControlsEnabled && (isMovingToBlockControlsArea(event.relatedTarget) || marginHoverEnabled && isOverActiveClickArea(event.relatedTarget, event.clientX))) {
|
|
87
120
|
return;
|
|
88
121
|
}
|
|
89
122
|
mouseLeaveTimeoutId = setTimeout(function () {
|
|
90
123
|
mouseLeaveTimeoutId = null;
|
|
91
|
-
//
|
|
124
|
+
// Re-check after the debounce: keep controls if the cursor landed on block controls
|
|
125
|
+
// (or, with the experiment on, the active right margin).
|
|
92
126
|
if (rightSideControlsEnabled && typeof document !== 'undefined') {
|
|
93
127
|
var el = document.elementFromPoint(lastMousePosition.x, lastMousePosition.y);
|
|
94
|
-
if (el && isMovingToBlockControlsArea(el)) {
|
|
128
|
+
if (el && (isMovingToBlockControlsArea(el) || marginHoverEnabled && isOverActiveClickArea(el, lastMousePosition.x))) {
|
|
95
129
|
return;
|
|
96
130
|
}
|
|
97
131
|
}
|
|
@@ -113,11 +147,11 @@ export var createInteractionTrackingPlugin = function createInteractionTrackingP
|
|
|
113
147
|
x: event.clientX,
|
|
114
148
|
y: event.clientY
|
|
115
149
|
};
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
if (editorContentArea.contains(event.target) || isMovingToBlockControlsArea(event.target)) {
|
|
120
|
-
handleMouseMove(view, event, rightSideControlsEnabled);
|
|
150
|
+
// Catches block controls in portals that handleDOMEvents.mousemove misses.
|
|
151
|
+
// The right-margin overlay is only relevant with the experiment on.
|
|
152
|
+
var overClickArea = marginHoverEnabled && event.target instanceof Element && !!event.target.closest(CLICK_AREA_SELECTOR);
|
|
153
|
+
if (editorContentArea.contains(event.target) || isMovingToBlockControlsArea(event.target) || overClickArea) {
|
|
154
|
+
handleMouseMove(view, event, rightSideControlsEnabled, api);
|
|
121
155
|
}
|
|
122
156
|
},
|
|
123
157
|
options: {
|
|
@@ -66,6 +66,8 @@ export var VisibilityContainer = function VisibilityContainer(_ref) {
|
|
|
66
66
|
rightSideControlsEnabled = _useSharedPluginState.rightSideControlsEnabled;
|
|
67
67
|
var isViewMode = editorViewMode === 'view';
|
|
68
68
|
// rightSideControlsEnabled is the single source of truth (confluence_remix_button_right_side_block_fg from preset)
|
|
69
|
+
// Both controls are restricted by side: the drag handle shows on the left half, the Remix button on
|
|
70
|
+
// the right half (and the right margin), matching the midpoint split in handle-mouse-move.
|
|
69
71
|
var shouldRestrictBySide = rightSideControlsEnabled && controlSide !== undefined && !isViewMode;
|
|
70
72
|
// Only restrict by side when hoverSide is known (after mousemove). When undefined, show both
|
|
71
73
|
// controls so drag handle is visible on load and for keyboard-only users.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
1
2
|
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
2
|
-
|
|
3
|
+
import type { BlockControlsPlugin } from '../../blockControlsPluginType';
|
|
4
|
+
export declare const handleMouseMove: (view: EditorView, event: Event, rightSideControlsEnabled?: boolean, api?: ExtractInjectionAPI<BlockControlsPlugin>) => boolean;
|
|
3
5
|
export declare const handleMouseLeave: (view: EditorView, rightSideControlsEnabled?: boolean) => boolean;
|
|
4
6
|
export declare const handleMouseEnter: (view: EditorView) => boolean;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
2
3
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
3
4
|
import type { EditorState } from '@atlaskit/editor-prosemirror/state';
|
|
5
|
+
import type { BlockControlsPlugin } from '../../blockControlsPluginType';
|
|
4
6
|
export type InteractionTrackingPluginState = {
|
|
5
7
|
/**
|
|
6
8
|
* Tracks which side of the editor the mouse is currently on.
|
|
@@ -16,5 +18,5 @@ export type InteractionTrackingPluginState = {
|
|
|
16
18
|
isMouseOut?: boolean;
|
|
17
19
|
};
|
|
18
20
|
export declare const interactionTrackingPluginKey: PluginKey<InteractionTrackingPluginState>;
|
|
19
|
-
export declare const createInteractionTrackingPlugin: (rightSideControlsEnabled?: boolean) => SafePlugin<InteractionTrackingPluginState>;
|
|
21
|
+
export declare const createInteractionTrackingPlugin: (rightSideControlsEnabled?: boolean, api?: ExtractInjectionAPI<BlockControlsPlugin>) => SafePlugin<InteractionTrackingPluginState>;
|
|
20
22
|
export declare const getInteractionTrackingState: (state: EditorState) => InteractionTrackingPluginState | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-block-controls",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.0",
|
|
4
4
|
"description": "Block controls plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -15,50 +15,42 @@
|
|
|
15
15
|
"module": "dist/esm/index.js",
|
|
16
16
|
"module:es2019": "dist/es2019/index.js",
|
|
17
17
|
"types": "dist/types/index.d.ts",
|
|
18
|
-
"typesVersions": {
|
|
19
|
-
">=4.5 <4.9": {
|
|
20
|
-
"*": [
|
|
21
|
-
"dist/types-ts4.5/*",
|
|
22
|
-
"dist/types-ts4.5/index.d.ts"
|
|
23
|
-
]
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
18
|
"sideEffects": [
|
|
27
19
|
"*.compiled.css"
|
|
28
20
|
],
|
|
29
21
|
"atlaskit:src": "src/index.ts",
|
|
30
22
|
"dependencies": {
|
|
31
|
-
"@atlaskit/browser-apis": "^0.0
|
|
32
|
-
"@atlaskit/button": "^
|
|
33
|
-
"@atlaskit/editor-plugin-accessibility-utils": "^
|
|
34
|
-
"@atlaskit/editor-plugin-analytics": "^
|
|
35
|
-
"@atlaskit/editor-plugin-editor-disabled": "^
|
|
36
|
-
"@atlaskit/editor-plugin-editor-viewmode": "^
|
|
37
|
-
"@atlaskit/editor-plugin-feature-flags": "^
|
|
38
|
-
"@atlaskit/editor-plugin-interaction": "^
|
|
39
|
-
"@atlaskit/editor-plugin-limited-mode": "^
|
|
40
|
-
"@atlaskit/editor-plugin-metrics": "^
|
|
41
|
-
"@atlaskit/editor-plugin-quick-insert": "^
|
|
42
|
-
"@atlaskit/editor-plugin-selection": "^
|
|
43
|
-
"@atlaskit/editor-plugin-toolbar": "^
|
|
44
|
-
"@atlaskit/editor-plugin-type-ahead": "^
|
|
45
|
-
"@atlaskit/editor-plugin-user-intent": "^
|
|
46
|
-
"@atlaskit/editor-plugin-width": "^
|
|
47
|
-
"@atlaskit/editor-prosemirror": "^
|
|
48
|
-
"@atlaskit/editor-shared-styles": "^
|
|
49
|
-
"@atlaskit/editor-tables": "^
|
|
50
|
-
"@atlaskit/icon": "^
|
|
51
|
-
"@atlaskit/icon-lab": "^
|
|
52
|
-
"@atlaskit/link": "^
|
|
53
|
-
"@atlaskit/platform-feature-flags": "^
|
|
54
|
-
"@atlaskit/pragmatic-drag-and-drop": "^
|
|
55
|
-
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^
|
|
56
|
-
"@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "^
|
|
57
|
-
"@atlaskit/primitives": "^
|
|
58
|
-
"@atlaskit/theme": "^
|
|
59
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
60
|
-
"@atlaskit/tokens": "^
|
|
61
|
-
"@atlaskit/tooltip": "^
|
|
23
|
+
"@atlaskit/browser-apis": "^1.0.0",
|
|
24
|
+
"@atlaskit/button": "^24.0.0",
|
|
25
|
+
"@atlaskit/editor-plugin-accessibility-utils": "^12.0.0",
|
|
26
|
+
"@atlaskit/editor-plugin-analytics": "^12.0.0",
|
|
27
|
+
"@atlaskit/editor-plugin-editor-disabled": "^12.0.0",
|
|
28
|
+
"@atlaskit/editor-plugin-editor-viewmode": "^14.0.0",
|
|
29
|
+
"@atlaskit/editor-plugin-feature-flags": "^11.0.0",
|
|
30
|
+
"@atlaskit/editor-plugin-interaction": "^21.0.0",
|
|
31
|
+
"@atlaskit/editor-plugin-limited-mode": "^9.0.0",
|
|
32
|
+
"@atlaskit/editor-plugin-metrics": "^13.0.0",
|
|
33
|
+
"@atlaskit/editor-plugin-quick-insert": "^12.0.0",
|
|
34
|
+
"@atlaskit/editor-plugin-selection": "^12.0.0",
|
|
35
|
+
"@atlaskit/editor-plugin-toolbar": "^9.0.0",
|
|
36
|
+
"@atlaskit/editor-plugin-type-ahead": "^12.0.0",
|
|
37
|
+
"@atlaskit/editor-plugin-user-intent": "^10.0.0",
|
|
38
|
+
"@atlaskit/editor-plugin-width": "^13.0.0",
|
|
39
|
+
"@atlaskit/editor-prosemirror": "^8.0.0",
|
|
40
|
+
"@atlaskit/editor-shared-styles": "^4.0.0",
|
|
41
|
+
"@atlaskit/editor-tables": "^3.0.0",
|
|
42
|
+
"@atlaskit/icon": "^36.0.0",
|
|
43
|
+
"@atlaskit/icon-lab": "^7.0.0",
|
|
44
|
+
"@atlaskit/link": "^4.0.0",
|
|
45
|
+
"@atlaskit/platform-feature-flags": "^2.0.0",
|
|
46
|
+
"@atlaskit/pragmatic-drag-and-drop": "^2.0.0",
|
|
47
|
+
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^3.0.0",
|
|
48
|
+
"@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "^4.0.0",
|
|
49
|
+
"@atlaskit/primitives": "^20.0.0",
|
|
50
|
+
"@atlaskit/theme": "^26.0.0",
|
|
51
|
+
"@atlaskit/tmp-editor-statsig": "^104.0.0",
|
|
52
|
+
"@atlaskit/tokens": "^14.0.0",
|
|
53
|
+
"@atlaskit/tooltip": "^23.0.0",
|
|
62
54
|
"@babel/runtime": "^7.0.0",
|
|
63
55
|
"@emotion/react": "^11.7.1",
|
|
64
56
|
"bind-event-listener": "^3.0.0",
|
|
@@ -67,7 +59,7 @@
|
|
|
67
59
|
"uuid": "^3.1.0"
|
|
68
60
|
},
|
|
69
61
|
"peerDependencies": {
|
|
70
|
-
"@atlaskit/editor-common": "^
|
|
62
|
+
"@atlaskit/editor-common": "^116.0.0",
|
|
71
63
|
"react": "^18.2.0",
|
|
72
64
|
"react-dom": "^18.2.0",
|
|
73
65
|
"react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
|
|
@@ -149,6 +141,10 @@
|
|
|
149
141
|
},
|
|
150
142
|
"confluence_frontend_native_tabs_extension": {
|
|
151
143
|
"type": "boolean"
|
|
144
|
+
},
|
|
145
|
+
"remix_button_right_margin_hover": {
|
|
146
|
+
"type": "boolean",
|
|
147
|
+
"referenceOnly": true
|
|
152
148
|
}
|
|
153
149
|
},
|
|
154
150
|
"devDependencies": {
|