@atlaskit/editor-plugin-block-controls 24.0.0 → 24.0.2

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.
Files changed (25) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/cjs/pm-plugins/{handle-sparse-mouse-over.js → handle-sparse-hover-target.js} +37 -20
  3. package/dist/cjs/pm-plugins/main.js +7 -7
  4. package/dist/cjs/pm-plugins/sparse-gutter-hover.js +317 -0
  5. package/dist/cjs/pm-plugins/{utils/sparse-surfaces-view.js → sparse-surfaces-view.js} +10 -4
  6. package/dist/cjs/pm-plugins/sparse-surfaces.js +1 -1
  7. package/dist/cjs/pm-plugins/utils/visible-box.js +27 -0
  8. package/dist/es2019/pm-plugins/{handle-sparse-mouse-over.js → handle-sparse-hover-target.js} +36 -19
  9. package/dist/es2019/pm-plugins/main.js +7 -7
  10. package/dist/es2019/pm-plugins/sparse-gutter-hover.js +305 -0
  11. package/dist/es2019/pm-plugins/{utils/sparse-surfaces-view.js → sparse-surfaces-view.js} +10 -4
  12. package/dist/es2019/pm-plugins/sparse-surfaces.js +1 -1
  13. package/dist/es2019/pm-plugins/utils/visible-box.js +21 -0
  14. package/dist/esm/pm-plugins/{handle-sparse-mouse-over.js → handle-sparse-hover-target.js} +36 -19
  15. package/dist/esm/pm-plugins/main.js +7 -7
  16. package/dist/esm/pm-plugins/sparse-gutter-hover.js +311 -0
  17. package/dist/esm/pm-plugins/{utils/sparse-surfaces-view.js → sparse-surfaces-view.js} +10 -4
  18. package/dist/esm/pm-plugins/sparse-surfaces.js +1 -1
  19. package/dist/esm/pm-plugins/utils/visible-box.js +21 -0
  20. package/dist/types/pm-plugins/handle-sparse-hover-target.d.ts +10 -0
  21. package/dist/types/pm-plugins/sparse-gutter-hover.d.ts +27 -0
  22. package/dist/types/pm-plugins/{utils/sparse-surfaces-view.d.ts → sparse-surfaces-view.d.ts} +2 -2
  23. package/dist/types/pm-plugins/utils/visible-box.d.ts +5 -0
  24. package/package.json +4 -4
  25. package/dist/types/pm-plugins/handle-sparse-mouse-over.d.ts +0 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 24.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [`834a52c0cc828`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/834a52c0cc828) -
8
+ Allow sparse gutter hover to target blocks nested inside table cells, matching legacy hover zone
9
+ behaviour
10
+ - Updated dependencies
11
+
12
+ ## 24.0.1
13
+
14
+ ### Patch Changes
15
+
16
+ - [`6b9023d88752e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6b9023d88752e) -
17
+ Restore left-gutter hover for top-level blocks, nested blocks, layout columns, and tables or media
18
+ wider than the line length under platform_editor_block_control_migration, matching the legacy
19
+ hover zone sizes.
20
+ - Updated dependencies
21
+
3
22
  ## 24.0.0
4
23
 
5
24
  ### Patch Changes
@@ -3,12 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.handleSparseMouseOver = void 0;
6
+ exports.isSparseHoverSuppressed = exports.handleSparseHoverTarget = void 0;
7
+ var _nodeAnchor = require("@atlaskit/editor-common/node-anchor");
7
8
  var _fg = require("@atlaskit/platform-feature-flags/fg");
8
9
  var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
9
10
  var _decorationsCommon = require("./decorations-common");
10
- var OPAQUE_BLOCKS = new Set(['table', 'bulletList', 'orderedList', 'taskList', 'decisionList', 'mediaSingle']);
11
+ var OPAQUE_BLOCKS = new Set(['bulletList', 'orderedList', 'taskList', 'decisionList', 'mediaSingle']);
11
12
  var EXCLUDED_BLOCKS = new Set(['listItem', 'tableRow', 'tableCell', 'tableHeader', 'caption']);
13
+ var TABLE_CELLS = new Set(['tableCell', 'tableHeader']);
12
14
  var isPanelNodeTypeName = function isPanelNodeTypeName(nodeTypeName) {
13
15
  return nodeTypeName === 'panel' || nodeTypeName === 'panel_c1' && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_controls_reliable_anchor', 'isEnabled', true) && (0, _fg.fg)('platform_editor_controls_reliable_anchor_patch_1');
14
16
  };
@@ -40,26 +42,38 @@ var redirectParagraphToWrappedMedia = function redirectParagraphToWrappedMedia(v
40
42
  };
41
43
  };
42
44
 
43
- /** Resolves hover from PM structure so the first hover can create its sparse candidate. */
44
- var handleSparseMouseOver = exports.handleSparseMouseOver = function handleSparseMouseOver(view, event, api) {
45
- var _api$editorDisabled$s, _api$editorDisabled, _api$editorViewMode, _api$showDiff$sharedS, _api$showDiff, _api$userIntent, _$pos$nodeAfter, _controls$activeNode, _anchors$get, _anchors$get2;
45
+ /** Whether hovering must not show block controls under sparse surfaces. */
46
+ var isSparseHoverSuppressed = exports.isSparseHoverSuppressed = function isSparseHoverSuppressed(view, api) {
47
+ var _api$limitedMode, _getNodeIdProvider, _api$typeAhead, _api$editorDisabled$s, _api$editorDisabled, _api$editorViewMode, _api$showDiff$sharedS, _api$showDiff, _api$userIntent;
48
+ if (api !== null && api !== void 0 && (_api$limitedMode = api.limitedMode) !== null && _api$limitedMode !== void 0 && (_api$limitedMode = _api$limitedMode.sharedState.currentState()) !== null && _api$limitedMode !== void 0 && _api$limitedMode.enabled || (_getNodeIdProvider = (0, _nodeAnchor.getNodeIdProvider)(view)) !== null && _getNodeIdProvider !== void 0 && _getNodeIdProvider.isLimitedMode()) {
49
+ return true;
50
+ }
46
51
  var controls = api === null || api === void 0 ? void 0 : api.blockControls.sharedState.currentState();
47
- if (!api || controls !== null && controls !== void 0 && controls.isDragging || !(event.target instanceof Element) || event.target === view.dom) {
48
- return false;
52
+ if (controls !== null && controls !== void 0 && controls.isDragging || api !== null && api !== void 0 && (_api$typeAhead = api.typeAhead) !== null && _api$typeAhead !== void 0 && (_api$typeAhead = _api$typeAhead.sharedState.currentState()) !== null && _api$typeAhead !== void 0 && _api$typeAhead.isOpen) {
53
+ return true;
49
54
  }
50
- var editorDisabled = (_api$editorDisabled$s = (_api$editorDisabled = api.editorDisabled) === null || _api$editorDisabled === void 0 || (_api$editorDisabled = _api$editorDisabled.sharedState.currentState()) === null || _api$editorDisabled === void 0 ? void 0 : _api$editorDisabled.editorDisabled) !== null && _api$editorDisabled$s !== void 0 ? _api$editorDisabled$s : false;
51
- var editorViewMode = (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 || (_api$editorViewMode = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.mode;
55
+ var editorDisabled = (_api$editorDisabled$s = api === null || api === void 0 || (_api$editorDisabled = api.editorDisabled) === null || _api$editorDisabled === void 0 || (_api$editorDisabled = _api$editorDisabled.sharedState.currentState()) === null || _api$editorDisabled === void 0 ? void 0 : _api$editorDisabled.editorDisabled) !== null && _api$editorDisabled$s !== void 0 ? _api$editorDisabled$s : false;
56
+ var editorViewMode = api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 || (_api$editorViewMode = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.mode;
52
57
  if (editorDisabled && (editorViewMode !== 'view' || !(controls !== null && controls !== void 0 && controls.rightSideControlsEnabled))) {
53
- return false;
58
+ return true;
54
59
  }
55
- var isDisplayingDiff = (_api$showDiff$sharedS = (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 || (_api$showDiff = _api$showDiff.sharedState.currentState()) === null || _api$showDiff === void 0 ? void 0 : _api$showDiff.isDisplayingChanges) !== null && _api$showDiff$sharedS !== void 0 ? _api$showDiff$sharedS : false;
56
- var currentUserIntent = (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent;
57
- if (isDisplayingDiff || currentUserIntent === 'reviewing') {
60
+ var isDisplayingDiff = (_api$showDiff$sharedS = api === null || api === void 0 || (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 || (_api$showDiff = _api$showDiff.sharedState.currentState()) === null || _api$showDiff === void 0 ? void 0 : _api$showDiff.isDisplayingChanges) !== null && _api$showDiff$sharedS !== void 0 ? _api$showDiff$sharedS : false;
61
+ return isDisplayingDiff || (api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent) === 'reviewing';
62
+ };
63
+
64
+ /**
65
+ * Shows controls for the block that owns `target`, resolved from PM structure so the first hover
66
+ * can create its sparse candidate. Callers check `isSparseHoverSuppressed` first.
67
+ */
68
+ var handleSparseHoverTarget = exports.handleSparseHoverTarget = function handleSparseHoverTarget(view, target, api) {
69
+ var _$pos$nodeAfter, _controls$activeNode, _anchors$get, _anchors$get2;
70
+ if (!api || target === view.dom) {
58
71
  return false;
59
72
  }
73
+ var controls = api.blockControls.sharedState.currentState();
60
74
  var position;
61
75
  try {
62
- position = view.posAtDOM(event.target, 0);
76
+ position = view.posAtDOM(target, 0);
63
77
  } catch (_unused) {
64
78
  return false;
65
79
  }
@@ -71,17 +85,20 @@ var handleSparseMouseOver = exports.handleSparseMouseOver = function handleSpars
71
85
  if (!_node.isBlock || EXCLUDED_BLOCKS.has(_node.type.name)) {
72
86
  continue;
73
87
  }
74
- targetPosition = $pos.before(depth);
88
+ // Legacy hover excludes direct cell children, but can target deeper blocks inside them.
89
+ if (!TABLE_CELLS.has($pos.node(depth - 1).type.name)) {
90
+ targetPosition = $pos.before(depth);
91
+ }
75
92
  if (OPAQUE_BLOCKS.has(_node.type.name)) {
76
93
  opaque = true;
77
94
  break;
78
95
  }
79
96
  }
80
- if (!opaque && (_$pos$nodeAfter = $pos.nodeAfter) !== null && _$pos$nodeAfter !== void 0 && _$pos$nodeAfter.isBlock && !EXCLUDED_BLOCKS.has($pos.nodeAfter.type.name)) {
97
+ if (!opaque && (_$pos$nodeAfter = $pos.nodeAfter) !== null && _$pos$nodeAfter !== void 0 && _$pos$nodeAfter.isBlock && !EXCLUDED_BLOCKS.has($pos.nodeAfter.type.name) && !TABLE_CELLS.has($pos.parent.type.name)) {
81
98
  // Browser positions over non-editable node chrome can resolve to the first editable child.
82
99
  // Keep the ancestor unless that child actually owns the hovered DOM target.
83
100
  var nodeAfterDOM = view.nodeDOM(position);
84
- if (nodeAfterDOM instanceof Element && nodeAfterDOM.contains(event.target)) {
101
+ if (nodeAfterDOM instanceof Element && nodeAfterDOM.contains(target)) {
85
102
  targetPosition = position;
86
103
  }
87
104
  }
@@ -92,9 +109,9 @@ var handleSparseMouseOver = exports.handleSparseMouseOver = function handleSpars
92
109
  if (!initialNode) {
93
110
  return false;
94
111
  }
95
- var target = redirectParagraphToWrappedMedia(view, view.state.doc.resolve(targetPosition), targetPosition);
96
- targetPosition = target.position;
97
- var node = target.node;
112
+ var redirected = redirectParagraphToWrappedMedia(view, view.state.doc.resolve(targetPosition), targetPosition);
113
+ targetPosition = redirected.position;
114
+ var node = redirected.node;
98
115
  if (!node || (controls === null || controls === void 0 || (_controls$activeNode = controls.activeNode) === null || _controls$activeNode === void 0 ? void 0 : _controls$activeNode.pos) === targetPosition) {
99
116
  return false;
100
117
  }
@@ -37,7 +37,6 @@ var _decorationsDropTargetActive = require("./decorations-drop-target-active");
37
37
  var _decorationsQuickInsertButton = require("./decorations-quick-insert-button");
38
38
  var _handleMouseDown = require("./handle-mouse-down");
39
39
  var _handleMouseOver = require("./handle-mouse-over");
40
- var _handleSparseMouseOver = require("./handle-sparse-mouse-over");
41
40
  var _keymap = require("./keymap");
42
41
  var _activeAnchorTracker = require("./utils/active-anchor-tracker");
43
42
  var _analytics2 = require("./utils/analytics");
@@ -1165,6 +1164,11 @@ var createPlugin = exports.createPlugin = function createPlugin(api, getIntl, no
1165
1164
  },
1166
1165
  mouseover: function mouseover(view, event) {
1167
1166
  var _api$limitedMode7, _getNodeIdProvider;
1167
+ // Sparse surfaces handle all hover in bindSparseGutterHover, which also covers the gutters
1168
+ // outside view.dom.
1169
+ if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_block_control_migration')) {
1170
+ return false;
1171
+ }
1168
1172
  if (api !== null && api !== void 0 && (_api$limitedMode7 = api.limitedMode) !== null && _api$limitedMode7 !== void 0 && (_api$limitedMode7 = _api$limitedMode7.sharedState.currentState()) !== null && _api$limitedMode7 !== void 0 && _api$limitedMode7.enabled) {
1169
1173
  return;
1170
1174
  }
@@ -1172,14 +1176,10 @@ var createPlugin = exports.createPlugin = function createPlugin(api, getIntl, no
1172
1176
  // another layer of protection to prevent handling mouseover
1173
1177
  // when native anchor with dnd is enabled in limited mode
1174
1178
  // in case there are descripancies between getNodeIdProvider limited mode state
1175
- if ((_getNodeIdProvider = (0, _nodeAnchor.getNodeIdProvider)(view)) !== null && _getNodeIdProvider !== void 0 && _getNodeIdProvider.isLimitedMode() && ((0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) || (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_block_control_migration'))) {
1179
+ if ((_getNodeIdProvider = (0, _nodeAnchor.getNodeIdProvider)(view)) !== null && _getNodeIdProvider !== void 0 && _getNodeIdProvider.isLimitedMode() && (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true)) {
1176
1180
  return;
1177
1181
  }
1178
- if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_block_control_migration')) {
1179
- (0, _handleSparseMouseOver.handleSparseMouseOver)(view, event, api);
1180
- } else {
1181
- (0, _handleMouseOver.handleMouseOver)(view, event, api);
1182
- }
1182
+ (0, _handleMouseOver.handleMouseOver)(view, event, api);
1183
1183
  return false;
1184
1184
  },
1185
1185
  mousedown: function mousedown(view, event) {
@@ -0,0 +1,317 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isPossibleGutterElement = exports.createGutterBlockFinder = exports.bindSparseGutterHover = void 0;
7
+ var _bindEventListener = require("bind-event-listener");
8
+ var _styles = require("@atlaskit/editor-common/styles");
9
+ var _constants = require("@atlaskit/editor-shared-styles/constants");
10
+ var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
11
+ var _consts = require("../ui/consts");
12
+ var _handleSparseHoverTarget = require("./handle-sparse-hover-target");
13
+ var _commands = require("./interaction-tracking/commands");
14
+ var _handleMouseMove = require("./interaction-tracking/handle-mouse-move");
15
+ var _pmPlugin = require("./interaction-tracking/pm-plugin");
16
+ var _visibleBox = require("./utils/visible-box");
17
+ var BLOCK_SELECTOR = '[data-prosemirror-node-block="true"]';
18
+ var TABLE_STRUCTURE_NODES = new Set(['table', 'tableRow', 'tableCell', 'tableHeader']);
19
+ var LAYOUT_SECTION_SELECTOR = '[data-prosemirror-node-name="layoutSection"]';
20
+ var LAYOUT_COLUMN_SELECTOR = '[data-prosemirror-node-name="layoutColumn"]';
21
+
22
+ // `layoutColumnExtendedHoverZone` is a DRAG_HANDLE_WIDTH-tall strip centred on the column's top edge.
23
+ var LAYOUT_COLUMN_STRIP_REACH = _styles.DRAG_HANDLE_WIDTH / 2;
24
+ var isBlockElement = function isBlockElement(element) {
25
+ return element.getAttribute('data-prosemirror-node-block') === 'true';
26
+ };
27
+ var getVisibleLeft = function getVisibleLeft(block) {
28
+ var _block$getAttribute;
29
+ return (0, _visibleBox.getVisibleBox)(block, (_block$getAttribute = block.getAttribute('data-prosemirror-node-name')) !== null && _block$getAttribute !== void 0 ? _block$getAttribute : '').getBoundingClientRect().left;
30
+ };
31
+
32
+ /**
33
+ * Whether the element under the pointer can be part of a block's left gutter: a container block's
34
+ * own padding (panel, layout, list…) or an editor wrapper around the content. Text and leaf blocks
35
+ * have no gutter inside them. Table and cell padding shows the table's controls, but containers
36
+ * nested inside cells can own gutters for their children, as in the legacy hover zones. Elements
37
+ * rendered beside the editor (title, popups, toolbars) cover any gutter beneath them.
38
+ */
39
+ var isPossibleGutterElement = exports.isPossibleGutterElement = function isPossibleGutterElement(view, hoveredElement) {
40
+ if (!(hoveredElement instanceof Element)) {
41
+ return false;
42
+ }
43
+ var owner = hoveredElement.closest("".concat(BLOCK_SELECTOR, ", ").concat(_consts.BLOCK_CONTROLS_SURFACE_SELECTOR));
44
+ if (owner !== null && owner !== void 0 && owner.matches(_consts.BLOCK_CONTROLS_SURFACE_SELECTOR)) {
45
+ return false;
46
+ }
47
+ if (owner && view.dom.contains(owner)) {
48
+ var _owner$getAttribute, _owner$getAttribute2;
49
+ if (TABLE_STRUCTURE_NODES.has((_owner$getAttribute = owner.getAttribute('data-prosemirror-node-name')) !== null && _owner$getAttribute !== void 0 ? _owner$getAttribute : '')) {
50
+ return false;
51
+ }
52
+ var nodeType = view.state.schema.nodes[(_owner$getAttribute2 = owner.getAttribute('data-prosemirror-node-name')) !== null && _owner$getAttribute2 !== void 0 ? _owner$getAttribute2 : ''];
53
+ return !(nodeType !== null && nodeType !== void 0 && nodeType.isTextblock) && !(nodeType !== null && nodeType !== void 0 && nodeType.isLeaf);
54
+ }
55
+ return view.dom.contains(hoveredElement) || hoveredElement.contains(view.dom);
56
+ };
57
+ var getGutterWidths = function getGutterWidths() {
58
+ return {
59
+ topLevel: (0, _constants.akEditorGutterPaddingDynamic)(),
60
+ narrowTopLevel: (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_preview_panel_responsiveness', 'isEnabled', true) ? _constants.akEditorGutterPaddingReduced : undefined
61
+ };
62
+ };
63
+ var getTopLevelGutterWidth = function getTopLevelGutterWidth(api, widths) {
64
+ var _api$width;
65
+ var editorWidth = api === null || api === void 0 || (_api$width = api.width) === null || _api$width === void 0 || (_api$width = _api$width.sharedState.currentState()) === null || _api$width === void 0 ? void 0 : _api$width.width;
66
+ return widths.narrowTopLevel !== undefined && editorWidth !== undefined && editorWidth <= _constants.akEditorFullPageNarrowBreakout ? widths.narrowTopLevel : widths.topLevel;
67
+ };
68
+ var getEditorElementAt = function getEditorElementAt(view, clientX, clientY) {
69
+ var element = view.dom.ownerDocument.elementFromPoint(clientX, clientY);
70
+ return element && element !== view.dom && view.dom.contains(element) ? element : null;
71
+ };
72
+
73
+ /** The outermost block around `element` that does not contain the pointer owns that gutter. */
74
+ var findGutterOwner = function findGutterOwner(view, element, pointerTarget) {
75
+ var owner = null;
76
+ for (var current = element; current && current !== view.dom;) {
77
+ if (isBlockElement(current) && !current.contains(pointerTarget) &&
78
+ // Layout columns have a top hover strip instead of a left gutter.
79
+ current.getAttribute('data-prosemirror-node-name') !== 'layoutColumn') {
80
+ owner = current;
81
+ }
82
+ current = current.parentElement;
83
+ }
84
+ return owner;
85
+ };
86
+ var findBlockBesidePointer = function findBlockBesidePointer(view, api, event, pointerTarget, widths) {
87
+ var pointerBlock = pointerTarget.closest(BLOCK_SELECTOR);
88
+ var gutterWidth = pointerBlock && view.dom.contains(pointerBlock) ? _consts.DRAG_HANDLE_MAX_WIDTH_PLUS_GAP : getTopLevelGutterWidth(api, widths);
89
+ var sample = getEditorElementAt(view, event.clientX + gutterWidth, event.clientY);
90
+ var owner = sample && findGutterOwner(view, sample, pointerTarget);
91
+ if (!owner) {
92
+ return null;
93
+ }
94
+ var left = getVisibleLeft(owner);
95
+ return event.clientX < left && left - event.clientX <= gutterWidth ? owner : null;
96
+ };
97
+ var findColumnBelowPointer = function findColumnBelowPointer(view, event, pointerTarget) {
98
+ if (pointerTarget.closest(LAYOUT_COLUMN_SELECTOR) || pointerTarget !== view.dom && !pointerTarget.closest(LAYOUT_SECTION_SELECTOR)) {
99
+ return null;
100
+ }
101
+ var sample = getEditorElementAt(view, event.clientX, event.clientY + LAYOUT_COLUMN_STRIP_REACH);
102
+ var column = sample === null || sample === void 0 ? void 0 : sample.closest(LAYOUT_COLUMN_SELECTOR);
103
+ return column && view.dom.contains(column) && event.clientY < column.getBoundingClientRect().top ? column : null;
104
+ };
105
+
106
+ /** Returns a function that finds the block whose legacy hover zone contains the pointer. */
107
+ var createGutterBlockFinder = exports.createGutterBlockFinder = function createGutterBlockFinder(view, api) {
108
+ var widths = getGutterWidths();
109
+ return function (event) {
110
+ var _findBlockBesidePoint;
111
+ if (!(event.target instanceof Element)) {
112
+ return null;
113
+ }
114
+ return (_findBlockBesidePoint = findBlockBesidePointer(view, api, event, event.target, widths)) !== null && _findBlockBesidePoint !== void 0 ? _findBlockBesidePoint : view.dom.contains(event.target) ? findColumnBelowPointer(view, event, event.target) : null;
115
+ };
116
+ };
117
+
118
+ /** The latest gutter event waiting to be handled after the frame renders. */
119
+
120
+ /** What gutter hover does with a pointer position. */
121
+
122
+ /**
123
+ * Leaving block controls does not count as reaching a new element: a nested block's drag handle
124
+ * overhangs the block, so leaving the handle can put the pointer on the container with no block
125
+ * beside it, and that exit must keep the controls on the block.
126
+ */
127
+ var isLeavingBlockControls = function isLeavingBlockControls(event) {
128
+ return event.relatedTarget instanceof Element && Boolean(event.relatedTarget.closest(_consts.BLOCK_CONTROLS_SURFACE_SELECTOR));
129
+ };
130
+ var classifyGutterHover = function classifyGutterHover(view, findGutterBlock, _ref) {
131
+ var event = _ref.event,
132
+ reachedNewElement = _ref.reachedNewElement;
133
+ var block = findGutterBlock(event);
134
+ if (block) {
135
+ return {
136
+ kind: 'block',
137
+ element: block
138
+ };
139
+ }
140
+ var hoveredElement = event.target;
141
+ if (reachedNewElement && hoveredElement instanceof Element && view.dom.contains(hoveredElement)) {
142
+ return {
143
+ kind: 'container',
144
+ element: hoveredElement
145
+ };
146
+ }
147
+ return {
148
+ kind: 'none'
149
+ };
150
+ };
151
+
152
+ /**
153
+ * Runs `callback` once after the next frame renders (`requestAnimationFrame`, then `setTimeout`).
154
+ * Layout is clean then, so reading positions never forces a synchronous layout. Calls made while a
155
+ * run is already scheduled are ignored.
156
+ */
157
+ var createAfterRenderScheduler = function createAfterRenderScheduler(win, callback) {
158
+ var frame;
159
+ var timeout;
160
+ return {
161
+ schedule: function schedule() {
162
+ if (frame !== undefined || timeout !== undefined) {
163
+ return;
164
+ }
165
+ frame = win.requestAnimationFrame(function () {
166
+ frame = undefined;
167
+ timeout = win.setTimeout(function () {
168
+ timeout = undefined;
169
+ callback();
170
+ }, 0);
171
+ });
172
+ },
173
+ cancel: function cancel() {
174
+ if (frame !== undefined) {
175
+ win.cancelAnimationFrame(frame);
176
+ frame = undefined;
177
+ }
178
+ if (timeout !== undefined) {
179
+ win.clearTimeout(timeout);
180
+ timeout = undefined;
181
+ }
182
+ }
183
+ };
184
+ };
185
+
186
+ /**
187
+ * Keeps interaction tracking in step with gutter hover, as the legacy hover zones did. They sat
188
+ * inside the block tree, so hovering one cleared the typing state, counted as inside the editor even
189
+ * where the zone reached past the content area, and put the pointer on the left side of the
190
+ * right-side controls split.
191
+ */
192
+ var createInteractionTrackingSync = function createInteractionTrackingSync(view, api, contentArea) {
193
+ // Set after gutter hover calls mouseEnter for a gutter outside the content area, so that leaving
194
+ // that gutter calls mouseLeave.
195
+ var isHoldingMouseEnter = false;
196
+ return function (event, isOverGutter) {
197
+ var _api$blockControls$sh;
198
+ var state = (0, _pmPlugin.getInteractionTrackingState)(view.state);
199
+ var rightSideControlsEnabled = api === null || api === void 0 || (_api$blockControls$sh = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh === void 0 ? void 0 : _api$blockControls$sh.rightSideControlsEnabled;
200
+ var side = rightSideControlsEnabled ? 'left' : undefined;
201
+ if (isOverGutter && state !== null && state !== void 0 && state.isEditing) {
202
+ (0, _commands.stopEditing)(view);
203
+ }
204
+ if (event.target instanceof Node && contentArea !== null && contentArea !== void 0 && contentArea.contains(event.target)) {
205
+ isHoldingMouseEnter = false;
206
+ } else if (isOverGutter && state !== null && state !== void 0 && state.isMouseOut) {
207
+ isHoldingMouseEnter = true;
208
+ (0, _commands.mouseEnter)(view, side);
209
+ return;
210
+ } else if (!isOverGutter && isHoldingMouseEnter) {
211
+ isHoldingMouseEnter = false;
212
+ (0, _handleMouseMove.handleMouseLeave)(view, rightSideControlsEnabled);
213
+ return;
214
+ }
215
+ if (isOverGutter && side && (state === null || state === void 0 ? void 0 : state.hoverSide) !== side) {
216
+ (0, _commands.setHoverSide)(view, side);
217
+ }
218
+ };
219
+ };
220
+
221
+ /**
222
+ * Shows block controls for the block the pointer hovers under sparse surfaces, including the empty
223
+ * space to a block's left (the gutter). This is the only hover handler for sparse surfaces.
224
+ *
225
+ * Reaching text or a leaf block shows its controls straight away. Over an element that can be part
226
+ * of a gutter, only the latest pointer event is kept, and it is resolved once after the frame
227
+ * renders so that reading positions never forces a layout.
228
+ *
229
+ * Returns an unbind function, or `undefined` when the editor's document has no window.
230
+ */
231
+ var bindSparseGutterHover = exports.bindSparseGutterHover = function bindSparseGutterHover(_ref2) {
232
+ var _ref3, _ref4, _view$dom$closest;
233
+ var api = _ref2.api,
234
+ view = _ref2.view;
235
+ var win = view.dom.ownerDocument.defaultView;
236
+ if (!win) {
237
+ return undefined;
238
+ }
239
+ var contentArea = view.dom.closest('.ak-editor-content-area');
240
+ // A wide block's gutter can sit outside the padded content area. Editors without a scroll
241
+ // container or content area still get hover, over as much of the gutter as their wrapper covers.
242
+ var eventArea = (_ref3 = (_ref4 = (_view$dom$closest = view.dom.closest('[data-editor-scroll-container="true"]')) !== null && _view$dom$closest !== void 0 ? _view$dom$closest : contentArea) !== null && _ref4 !== void 0 ? _ref4 : view.dom.parentElement) !== null && _ref3 !== void 0 ? _ref3 : view.dom;
243
+ var findGutterBlock = createGutterBlockFinder(view, api);
244
+ var syncInteractionTracking = createInteractionTrackingSync(view, api, contentArea);
245
+ var pending;
246
+ var handlePendingPointer = function handlePendingPointer() {
247
+ var pointer = pending;
248
+ pending = undefined;
249
+ if (!pointer || (0, _handleSparseHoverTarget.isSparseHoverSuppressed)(view, api)) {
250
+ return;
251
+ }
252
+ var hover = classifyGutterHover(view, findGutterBlock, pointer);
253
+ if (hover.kind !== 'none') {
254
+ (0, _handleSparseHoverTarget.handleSparseHoverTarget)(view, hover.element, api);
255
+ }
256
+ syncInteractionTracking(pointer.event, hover.kind === 'block');
257
+ };
258
+ var scheduler = createAfterRenderScheduler(win, handlePendingPointer);
259
+ var queueGutterEvent = function queueGutterEvent(event, reachedNewElement) {
260
+ var _pending;
261
+ pending = {
262
+ event: event,
263
+ reachedNewElement: Boolean((_pending = pending) === null || _pending === void 0 ? void 0 : _pending.reachedNewElement) || reachedNewElement
264
+ };
265
+ scheduler.schedule();
266
+ };
267
+
268
+ // Classified on every mouseover and reused while the pointer moves within that element, so
269
+ // movement over text costs one comparison.
270
+ var hovered;
271
+ var classifyHoveredElement = function classifyHoveredElement(element) {
272
+ hovered = {
273
+ element: element,
274
+ isPossibleGutter: isPossibleGutterElement(view, element)
275
+ };
276
+ return hovered.isPossibleGutter;
277
+ };
278
+ var onMouseOver = function onMouseOver(event) {
279
+ if (classifyHoveredElement(event.target)) {
280
+ queueGutterEvent(event, !isLeavingBlockControls(event));
281
+ return;
282
+ }
283
+ pending = undefined;
284
+ var hoveredElement = event.target;
285
+ if (hoveredElement instanceof Element && view.dom.contains(hoveredElement) && !(0, _handleSparseHoverTarget.isSparseHoverSuppressed)(view, api)) {
286
+ (0, _handleSparseHoverTarget.handleSparseHoverTarget)(view, hoveredElement, api);
287
+ }
288
+ };
289
+ var onMouseMove = function onMouseMove(event) {
290
+ var _hovered;
291
+ var isPossibleGutter = ((_hovered = hovered) === null || _hovered === void 0 ? void 0 : _hovered.element) === event.target ? hovered.isPossibleGutter : classifyHoveredElement(event.target);
292
+ if (isPossibleGutter) {
293
+ queueGutterEvent(event, false);
294
+ }
295
+ };
296
+ var bindMouseListener = function bindMouseListener(type, _listener) {
297
+ return (0, _bindEventListener.bind)(eventArea, {
298
+ type: type,
299
+ listener: function listener(event) {
300
+ if (event instanceof win.MouseEvent) {
301
+ _listener(event);
302
+ }
303
+ },
304
+ options: {
305
+ passive: true
306
+ }
307
+ });
308
+ };
309
+ var unbindMouseOver = bindMouseListener('mouseover', onMouseOver);
310
+ var unbindMouseMove = bindMouseListener('mousemove', onMouseMove);
311
+ return function () {
312
+ unbindMouseOver();
313
+ unbindMouseMove();
314
+ scheduler.cancel();
315
+ pending = undefined;
316
+ };
317
+ };
@@ -10,10 +10,11 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
10
10
  var _surfaceKeys = require("@atlaskit/editor-common/block-controls/surface-keys");
11
11
  var _nodeAnchor = require("@atlaskit/editor-common/node-anchor");
12
12
  var _surfaceRenderer = require("@atlaskit/editor-ui-control-model/surface-renderer");
13
- var _decorationsSurfaceAnchor = require("../decorations-surface-anchor");
14
- var _main = require("../main");
15
- var _sparseSurfaceCandidates = require("./sparse-surface-candidates");
16
- var _surfaceViewport = require("./surface-viewport");
13
+ var _decorationsSurfaceAnchor = require("./decorations-surface-anchor");
14
+ var _main = require("./main");
15
+ var _sparseGutterHover = require("./sparse-gutter-hover");
16
+ var _sparseSurfaceCandidates = require("./utils/sparse-surface-candidates");
17
+ var _surfaceViewport = require("./utils/surface-viewport");
17
18
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
18
19
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
19
20
  var haveAnchorPositionsChanged = function haveAnchorPositionsChanged(positions, anchors) {
@@ -106,6 +107,10 @@ var createSparseSurfacesView = exports.createSparseSurfacesView = function creat
106
107
  schedule();
107
108
  };
108
109
  var unsubscribe = api === null || api === void 0 || (_api$uiControlRegistr3 = api.uiControlRegistry) === null || _api$uiControlRegistr3 === void 0 ? void 0 : _api$uiControlRegistr3.actions.subscribe(invalidate);
110
+ var unbindGutterHover = (0, _sparseGutterHover.bindSparseGutterHover)({
111
+ api: api,
112
+ view: view
113
+ });
109
114
  var unsubscribers = [api === null || api === void 0 || (_api$editorDisabled = api.editorDisabled) === null || _api$editorDisabled === void 0 ? void 0 : _api$editorDisabled.sharedState.onChange(invalidate), api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.sharedState.onChange(invalidate), api === null || api === void 0 || (_api$limitedMode2 = api.limitedMode) === null || _api$limitedMode2 === void 0 ? void 0 : _api$limitedMode2.sharedState.onChange(invalidate), api === null || api === void 0 || (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 ? void 0 : _api$showDiff.sharedState.onChange(invalidate), api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.sharedState.onChange(invalidate)];
110
115
  return {
111
116
  update: function update(currentView, previousState) {
@@ -125,6 +130,7 @@ var createSparseSurfacesView = exports.createSparseSurfacesView = function creat
125
130
  win === null || win === void 0 || win.cancelAnimationFrame(frame);
126
131
  }
127
132
  observer.destroy();
133
+ unbindGutterHover === null || unbindGutterHover === void 0 || unbindGutterHover();
128
134
  unsubscribe === null || unsubscribe === void 0 || unsubscribe();
129
135
  unsubscribers.forEach(function (unsubscribe) {
130
136
  return unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
@@ -9,9 +9,9 @@ var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
9
9
  var _state = require("@atlaskit/editor-prosemirror/state");
10
10
  var _view2 = require("@atlaskit/editor-prosemirror/view");
11
11
  var _main = require("./main");
12
+ var _sparseSurfacesView = require("./sparse-surfaces-view");
12
13
  var _sparseSurfaceCandidates = require("./utils/sparse-surface-candidates");
13
14
  var _sparseSurfacesState = require("./utils/sparse-surfaces-state");
14
- var _sparseSurfacesView = require("./utils/sparse-surfaces-view");
15
15
  var sparseSurfacesKey = exports.sparseSurfacesKey = new _state.PluginKey('blockControlsSparseSurfaces');
16
16
 
17
17
  /** Owns sparse anchors independently of drag/drop decorations and their lifecycle. */
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getVisibleBox = void 0;
7
+ // Matches the anchor selectors in `ui/global-styles.tsx`. A table's `.pm-table-resizer-container`
8
+ // wraps its cells too, so only a direct child `.resizer-item` is the table's own box, not the
9
+ // resizer of media inside a cell.
10
+ var VISIBLE_BOX_SELECTORS = {
11
+ table: '.pm-table-resizer-container > .resizer-item',
12
+ mediaSingle: '.resizer-item',
13
+ embedCard: '.rich-media-item',
14
+ extension: '.extension-container[data-layout]',
15
+ bodiedExtension: '.extension-container[data-layout]',
16
+ multiBodiedExtension: '.extension-container[data-layout]',
17
+ blockCard: '.datasourceView-content-inner-wrap'
18
+ };
19
+
20
+ /**
21
+ * The box a block's controls anchor to under sparse surfaces. For some blocks this is an inner
22
+ * element, which can be wider than the block itself.
23
+ */
24
+ var getVisibleBox = exports.getVisibleBox = function getVisibleBox(dom, nodeType) {
25
+ var selector = VISIBLE_BOX_SELECTORS[nodeType];
26
+ return selector && dom.querySelector(selector) || dom;
27
+ };