@atlaskit/editor-plugin-block-controls 23.0.9 → 24.0.1

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 (28) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/pm-plugins/{handle-sparse-mouse-over.js → handle-sparse-hover-target.js} +30 -17
  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/cjs/ui/drop-target.js +2 -0
  9. package/dist/es2019/pm-plugins/{handle-sparse-mouse-over.js → handle-sparse-hover-target.js} +29 -16
  10. package/dist/es2019/pm-plugins/main.js +7 -7
  11. package/dist/es2019/pm-plugins/sparse-gutter-hover.js +305 -0
  12. package/dist/es2019/pm-plugins/{utils/sparse-surfaces-view.js → sparse-surfaces-view.js} +10 -4
  13. package/dist/es2019/pm-plugins/sparse-surfaces.js +1 -1
  14. package/dist/es2019/pm-plugins/utils/visible-box.js +21 -0
  15. package/dist/es2019/ui/drop-target.js +1 -0
  16. package/dist/esm/pm-plugins/{handle-sparse-mouse-over.js → handle-sparse-hover-target.js} +29 -16
  17. package/dist/esm/pm-plugins/main.js +7 -7
  18. package/dist/esm/pm-plugins/sparse-gutter-hover.js +311 -0
  19. package/dist/esm/pm-plugins/{utils/sparse-surfaces-view.js → sparse-surfaces-view.js} +10 -4
  20. package/dist/esm/pm-plugins/sparse-surfaces.js +1 -1
  21. package/dist/esm/pm-plugins/utils/visible-box.js +21 -0
  22. package/dist/esm/ui/drop-target.js +1 -0
  23. package/dist/types/pm-plugins/handle-sparse-hover-target.d.ts +10 -0
  24. package/dist/types/pm-plugins/sparse-gutter-hover.d.ts +27 -0
  25. package/dist/types/pm-plugins/{utils/sparse-surfaces-view.d.ts → sparse-surfaces-view.d.ts} +2 -2
  26. package/dist/types/pm-plugins/utils/visible-box.d.ts +5 -0
  27. package/package.json +21 -21
  28. package/dist/types/pm-plugins/handle-sparse-mouse-over.d.ts +0 -5
@@ -0,0 +1,311 @@
1
+ import { bind } from 'bind-event-listener';
2
+ import { DRAG_HANDLE_WIDTH } from '@atlaskit/editor-common/styles';
3
+ import { akEditorFullPageNarrowBreakout, akEditorGutterPaddingDynamic, akEditorGutterPaddingReduced } from '@atlaskit/editor-shared-styles/constants';
4
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
5
+ import { BLOCK_CONTROLS_SURFACE_SELECTOR, DRAG_HANDLE_MAX_WIDTH_PLUS_GAP } from '../ui/consts';
6
+ import { handleSparseHoverTarget, isSparseHoverSuppressed } from './handle-sparse-hover-target';
7
+ import { mouseEnter, setHoverSide, stopEditing } from './interaction-tracking/commands';
8
+ import { handleMouseLeave } from './interaction-tracking/handle-mouse-move';
9
+ import { getInteractionTrackingState } from './interaction-tracking/pm-plugin';
10
+ import { getVisibleBox } from './utils/visible-box';
11
+ var BLOCK_SELECTOR = '[data-prosemirror-node-block="true"]';
12
+ var TABLE_SELECTOR = '[data-prosemirror-node-name="table"]';
13
+ var LAYOUT_SECTION_SELECTOR = '[data-prosemirror-node-name="layoutSection"]';
14
+ var LAYOUT_COLUMN_SELECTOR = '[data-prosemirror-node-name="layoutColumn"]';
15
+
16
+ // `layoutColumnExtendedHoverZone` is a DRAG_HANDLE_WIDTH-tall strip centred on the column's top edge.
17
+ var LAYOUT_COLUMN_STRIP_REACH = DRAG_HANDLE_WIDTH / 2;
18
+ var isBlockElement = function isBlockElement(element) {
19
+ return element.getAttribute('data-prosemirror-node-block') === 'true';
20
+ };
21
+ var getVisibleLeft = function getVisibleLeft(block) {
22
+ var _block$getAttribute;
23
+ return getVisibleBox(block, (_block$getAttribute = block.getAttribute('data-prosemirror-node-name')) !== null && _block$getAttribute !== void 0 ? _block$getAttribute : '').getBoundingClientRect().left;
24
+ };
25
+
26
+ /**
27
+ * Whether the element under the pointer can be part of a block's left gutter: a container block's
28
+ * own padding (panel, layout, list…) or an editor wrapper around the content. Text and leaf blocks
29
+ * have no gutter inside them, and neither do tables: like the legacy hover zones, anywhere inside a
30
+ * table, including cell padding, shows the table's controls. Elements rendered beside the editor
31
+ * (title, popups, toolbars) cover any gutter beneath them.
32
+ */
33
+ export var isPossibleGutterElement = function isPossibleGutterElement(view, hoveredElement) {
34
+ if (!(hoveredElement instanceof Element)) {
35
+ return false;
36
+ }
37
+ var owner = hoveredElement.closest("".concat(BLOCK_SELECTOR, ", ").concat(BLOCK_CONTROLS_SURFACE_SELECTOR));
38
+ if (owner !== null && owner !== void 0 && owner.matches(BLOCK_CONTROLS_SURFACE_SELECTOR)) {
39
+ return false;
40
+ }
41
+ if (owner && view.dom.contains(owner)) {
42
+ var _owner$getAttribute;
43
+ if (owner.closest(TABLE_SELECTOR)) {
44
+ return false;
45
+ }
46
+ var nodeType = view.state.schema.nodes[(_owner$getAttribute = owner.getAttribute('data-prosemirror-node-name')) !== null && _owner$getAttribute !== void 0 ? _owner$getAttribute : ''];
47
+ return !(nodeType !== null && nodeType !== void 0 && nodeType.isTextblock) && !(nodeType !== null && nodeType !== void 0 && nodeType.isLeaf);
48
+ }
49
+ return view.dom.contains(hoveredElement) || hoveredElement.contains(view.dom);
50
+ };
51
+ var getGutterWidths = function getGutterWidths() {
52
+ return {
53
+ topLevel: akEditorGutterPaddingDynamic(),
54
+ narrowTopLevel: expValEqualsNoExposure('platform_editor_preview_panel_responsiveness', 'isEnabled', true) ? akEditorGutterPaddingReduced : undefined
55
+ };
56
+ };
57
+ var getTopLevelGutterWidth = function getTopLevelGutterWidth(api, widths) {
58
+ var _api$width;
59
+ 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;
60
+ return widths.narrowTopLevel !== undefined && editorWidth !== undefined && editorWidth <= akEditorFullPageNarrowBreakout ? widths.narrowTopLevel : widths.topLevel;
61
+ };
62
+ var getEditorElementAt = function getEditorElementAt(view, clientX, clientY) {
63
+ var element = view.dom.ownerDocument.elementFromPoint(clientX, clientY);
64
+ return element && element !== view.dom && view.dom.contains(element) ? element : null;
65
+ };
66
+
67
+ /** The outermost block around `element` that does not contain the pointer owns that gutter. */
68
+ var findGutterOwner = function findGutterOwner(view, element, pointerTarget) {
69
+ var owner = null;
70
+ for (var current = element; current && current !== view.dom;) {
71
+ if (isBlockElement(current) && !current.contains(pointerTarget) &&
72
+ // Layout columns have a top hover strip instead of a left gutter.
73
+ current.getAttribute('data-prosemirror-node-name') !== 'layoutColumn') {
74
+ owner = current;
75
+ }
76
+ current = current.parentElement;
77
+ }
78
+ return owner;
79
+ };
80
+ var findBlockBesidePointer = function findBlockBesidePointer(view, api, event, pointerTarget, widths) {
81
+ var pointerBlock = pointerTarget.closest(BLOCK_SELECTOR);
82
+ var gutterWidth = pointerBlock && view.dom.contains(pointerBlock) ? DRAG_HANDLE_MAX_WIDTH_PLUS_GAP : getTopLevelGutterWidth(api, widths);
83
+ var sample = getEditorElementAt(view, event.clientX + gutterWidth, event.clientY);
84
+ var owner = sample && findGutterOwner(view, sample, pointerTarget);
85
+ if (!owner) {
86
+ return null;
87
+ }
88
+ var left = getVisibleLeft(owner);
89
+ return event.clientX < left && left - event.clientX <= gutterWidth ? owner : null;
90
+ };
91
+ var findColumnBelowPointer = function findColumnBelowPointer(view, event, pointerTarget) {
92
+ if (pointerTarget.closest(LAYOUT_COLUMN_SELECTOR) || pointerTarget !== view.dom && !pointerTarget.closest(LAYOUT_SECTION_SELECTOR)) {
93
+ return null;
94
+ }
95
+ var sample = getEditorElementAt(view, event.clientX, event.clientY + LAYOUT_COLUMN_STRIP_REACH);
96
+ var column = sample === null || sample === void 0 ? void 0 : sample.closest(LAYOUT_COLUMN_SELECTOR);
97
+ return column && view.dom.contains(column) && event.clientY < column.getBoundingClientRect().top ? column : null;
98
+ };
99
+
100
+ /** Returns a function that finds the block whose legacy hover zone contains the pointer. */
101
+ export var createGutterBlockFinder = function createGutterBlockFinder(view, api) {
102
+ var widths = getGutterWidths();
103
+ return function (event) {
104
+ var _findBlockBesidePoint;
105
+ if (!(event.target instanceof Element)) {
106
+ return null;
107
+ }
108
+ 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;
109
+ };
110
+ };
111
+
112
+ /** The latest gutter event waiting to be handled after the frame renders. */
113
+
114
+ /** What gutter hover does with a pointer position. */
115
+
116
+ /**
117
+ * Leaving block controls does not count as reaching a new element: a nested block's drag handle
118
+ * overhangs the block, so leaving the handle can put the pointer on the container with no block
119
+ * beside it, and that exit must keep the controls on the block.
120
+ */
121
+ var isLeavingBlockControls = function isLeavingBlockControls(event) {
122
+ return event.relatedTarget instanceof Element && Boolean(event.relatedTarget.closest(BLOCK_CONTROLS_SURFACE_SELECTOR));
123
+ };
124
+ var classifyGutterHover = function classifyGutterHover(view, findGutterBlock, _ref) {
125
+ var event = _ref.event,
126
+ reachedNewElement = _ref.reachedNewElement;
127
+ var block = findGutterBlock(event);
128
+ if (block) {
129
+ return {
130
+ kind: 'block',
131
+ element: block
132
+ };
133
+ }
134
+ var hoveredElement = event.target;
135
+ if (reachedNewElement && hoveredElement instanceof Element && view.dom.contains(hoveredElement)) {
136
+ return {
137
+ kind: 'container',
138
+ element: hoveredElement
139
+ };
140
+ }
141
+ return {
142
+ kind: 'none'
143
+ };
144
+ };
145
+
146
+ /**
147
+ * Runs `callback` once after the next frame renders (`requestAnimationFrame`, then `setTimeout`).
148
+ * Layout is clean then, so reading positions never forces a synchronous layout. Calls made while a
149
+ * run is already scheduled are ignored.
150
+ */
151
+ var createAfterRenderScheduler = function createAfterRenderScheduler(win, callback) {
152
+ var frame;
153
+ var timeout;
154
+ return {
155
+ schedule: function schedule() {
156
+ if (frame !== undefined || timeout !== undefined) {
157
+ return;
158
+ }
159
+ frame = win.requestAnimationFrame(function () {
160
+ frame = undefined;
161
+ timeout = win.setTimeout(function () {
162
+ timeout = undefined;
163
+ callback();
164
+ }, 0);
165
+ });
166
+ },
167
+ cancel: function cancel() {
168
+ if (frame !== undefined) {
169
+ win.cancelAnimationFrame(frame);
170
+ frame = undefined;
171
+ }
172
+ if (timeout !== undefined) {
173
+ win.clearTimeout(timeout);
174
+ timeout = undefined;
175
+ }
176
+ }
177
+ };
178
+ };
179
+
180
+ /**
181
+ * Keeps interaction tracking in step with gutter hover, as the legacy hover zones did. They sat
182
+ * inside the block tree, so hovering one cleared the typing state, counted as inside the editor even
183
+ * where the zone reached past the content area, and put the pointer on the left side of the
184
+ * right-side controls split.
185
+ */
186
+ var createInteractionTrackingSync = function createInteractionTrackingSync(view, api, contentArea) {
187
+ // Set after gutter hover calls mouseEnter for a gutter outside the content area, so that leaving
188
+ // that gutter calls mouseLeave.
189
+ var isHoldingMouseEnter = false;
190
+ return function (event, isOverGutter) {
191
+ var _api$blockControls$sh;
192
+ var state = getInteractionTrackingState(view.state);
193
+ 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;
194
+ var side = rightSideControlsEnabled ? 'left' : undefined;
195
+ if (isOverGutter && state !== null && state !== void 0 && state.isEditing) {
196
+ stopEditing(view);
197
+ }
198
+ if (event.target instanceof Node && contentArea !== null && contentArea !== void 0 && contentArea.contains(event.target)) {
199
+ isHoldingMouseEnter = false;
200
+ } else if (isOverGutter && state !== null && state !== void 0 && state.isMouseOut) {
201
+ isHoldingMouseEnter = true;
202
+ mouseEnter(view, side);
203
+ return;
204
+ } else if (!isOverGutter && isHoldingMouseEnter) {
205
+ isHoldingMouseEnter = false;
206
+ handleMouseLeave(view, rightSideControlsEnabled);
207
+ return;
208
+ }
209
+ if (isOverGutter && side && (state === null || state === void 0 ? void 0 : state.hoverSide) !== side) {
210
+ setHoverSide(view, side);
211
+ }
212
+ };
213
+ };
214
+
215
+ /**
216
+ * Shows block controls for the block the pointer hovers under sparse surfaces, including the empty
217
+ * space to a block's left (the gutter). This is the only hover handler for sparse surfaces.
218
+ *
219
+ * Reaching text or a leaf block shows its controls straight away. Over an element that can be part
220
+ * of a gutter, only the latest pointer event is kept, and it is resolved once after the frame
221
+ * renders so that reading positions never forces a layout.
222
+ *
223
+ * Returns an unbind function, or `undefined` when the editor's document has no window.
224
+ */
225
+ export var bindSparseGutterHover = function bindSparseGutterHover(_ref2) {
226
+ var _ref3, _ref4, _view$dom$closest;
227
+ var api = _ref2.api,
228
+ view = _ref2.view;
229
+ var win = view.dom.ownerDocument.defaultView;
230
+ if (!win) {
231
+ return undefined;
232
+ }
233
+ var contentArea = view.dom.closest('.ak-editor-content-area');
234
+ // A wide block's gutter can sit outside the padded content area. Editors without a scroll
235
+ // container or content area still get hover, over as much of the gutter as their wrapper covers.
236
+ 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;
237
+ var findGutterBlock = createGutterBlockFinder(view, api);
238
+ var syncInteractionTracking = createInteractionTrackingSync(view, api, contentArea);
239
+ var pending;
240
+ var handlePendingPointer = function handlePendingPointer() {
241
+ var pointer = pending;
242
+ pending = undefined;
243
+ if (!pointer || isSparseHoverSuppressed(view, api)) {
244
+ return;
245
+ }
246
+ var hover = classifyGutterHover(view, findGutterBlock, pointer);
247
+ if (hover.kind !== 'none') {
248
+ handleSparseHoverTarget(view, hover.element, api);
249
+ }
250
+ syncInteractionTracking(pointer.event, hover.kind === 'block');
251
+ };
252
+ var scheduler = createAfterRenderScheduler(win, handlePendingPointer);
253
+ var queueGutterEvent = function queueGutterEvent(event, reachedNewElement) {
254
+ var _pending;
255
+ pending = {
256
+ event: event,
257
+ reachedNewElement: Boolean((_pending = pending) === null || _pending === void 0 ? void 0 : _pending.reachedNewElement) || reachedNewElement
258
+ };
259
+ scheduler.schedule();
260
+ };
261
+
262
+ // Classified on every mouseover and reused while the pointer moves within that element, so
263
+ // movement over text costs one comparison.
264
+ var hovered;
265
+ var classifyHoveredElement = function classifyHoveredElement(element) {
266
+ hovered = {
267
+ element: element,
268
+ isPossibleGutter: isPossibleGutterElement(view, element)
269
+ };
270
+ return hovered.isPossibleGutter;
271
+ };
272
+ var onMouseOver = function onMouseOver(event) {
273
+ if (classifyHoveredElement(event.target)) {
274
+ queueGutterEvent(event, !isLeavingBlockControls(event));
275
+ return;
276
+ }
277
+ pending = undefined;
278
+ var hoveredElement = event.target;
279
+ if (hoveredElement instanceof Element && view.dom.contains(hoveredElement) && !isSparseHoverSuppressed(view, api)) {
280
+ handleSparseHoverTarget(view, hoveredElement, api);
281
+ }
282
+ };
283
+ var onMouseMove = function onMouseMove(event) {
284
+ var _hovered;
285
+ var isPossibleGutter = ((_hovered = hovered) === null || _hovered === void 0 ? void 0 : _hovered.element) === event.target ? hovered.isPossibleGutter : classifyHoveredElement(event.target);
286
+ if (isPossibleGutter) {
287
+ queueGutterEvent(event, false);
288
+ }
289
+ };
290
+ var bindMouseListener = function bindMouseListener(type, _listener) {
291
+ return bind(eventArea, {
292
+ type: type,
293
+ listener: function listener(event) {
294
+ if (event instanceof win.MouseEvent) {
295
+ _listener(event);
296
+ }
297
+ },
298
+ options: {
299
+ passive: true
300
+ }
301
+ });
302
+ };
303
+ var unbindMouseOver = bindMouseListener('mouseover', onMouseOver);
304
+ var unbindMouseMove = bindMouseListener('mousemove', onMouseMove);
305
+ return function () {
306
+ unbindMouseOver();
307
+ unbindMouseMove();
308
+ scheduler.cancel();
309
+ pending = undefined;
310
+ };
311
+ };
@@ -5,10 +5,11 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
5
5
  import { BLOCK_CONTROLS_LEFT_SURFACE, BLOCK_CONTROLS_RIGHT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
6
6
  import { getNodeIdProvider } from '@atlaskit/editor-common/node-anchor';
7
7
  import { resolveSurface } from '@atlaskit/editor-ui-control-model/surface-renderer';
8
- import { createSurfaceAnchorDecorations } from '../decorations-surface-anchor';
9
- import { key as blockControlsKey } from '../main';
10
- import { reconcileSparseSurfaceCandidates } from './sparse-surface-candidates';
11
- import { observeSurfaceViewport } from './surface-viewport';
8
+ import { createSurfaceAnchorDecorations } from './decorations-surface-anchor';
9
+ import { key as blockControlsKey } from './main';
10
+ import { bindSparseGutterHover } from './sparse-gutter-hover';
11
+ import { reconcileSparseSurfaceCandidates } from './utils/sparse-surface-candidates';
12
+ import { observeSurfaceViewport } from './utils/surface-viewport';
12
13
  var haveAnchorPositionsChanged = function haveAnchorPositionsChanged(positions, anchors) {
13
14
  return positions.length !== anchors.size || positions.some(function (position) {
14
15
  return !anchors.has(position);
@@ -99,6 +100,10 @@ export var createSparseSurfacesView = function createSparseSurfacesView(_ref) {
99
100
  schedule();
100
101
  };
101
102
  var unsubscribe = api === null || api === void 0 || (_api$uiControlRegistr3 = api.uiControlRegistry) === null || _api$uiControlRegistr3 === void 0 ? void 0 : _api$uiControlRegistr3.actions.subscribe(invalidate);
103
+ var unbindGutterHover = bindSparseGutterHover({
104
+ api: api,
105
+ view: view
106
+ });
102
107
  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)];
103
108
  return {
104
109
  update: function update(currentView, previousState) {
@@ -118,6 +123,7 @@ export var createSparseSurfacesView = function createSparseSurfacesView(_ref) {
118
123
  win === null || win === void 0 || win.cancelAnimationFrame(frame);
119
124
  }
120
125
  observer.destroy();
126
+ unbindGutterHover === null || unbindGutterHover === void 0 || unbindGutterHover();
121
127
  unsubscribe === null || unsubscribe === void 0 || unsubscribe();
122
128
  unsubscribers.forEach(function (unsubscribe) {
123
129
  return unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
@@ -3,9 +3,9 @@ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
3
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
4
4
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
5
5
  import { key as blockControlsKey } from './main';
6
+ import { createSparseSurfacesView } from './sparse-surfaces-view';
6
7
  import { emptySparseSurfaceCandidateState } from './utils/sparse-surface-candidates';
7
8
  import { applySparseSurfacesTransaction, createInitialSparseSurfacesState } from './utils/sparse-surfaces-state';
8
- import { createSparseSurfacesView } from './utils/sparse-surfaces-view';
9
9
  export var sparseSurfacesKey = new PluginKey('blockControlsSparseSurfaces');
10
10
 
11
11
  /** Owns sparse anchors independently of drag/drop decorations and their lifecycle. */
@@ -0,0 +1,21 @@
1
+ // Matches the anchor selectors in `ui/global-styles.tsx`. A table's `.pm-table-resizer-container`
2
+ // wraps its cells too, so only a direct child `.resizer-item` is the table's own box, not the
3
+ // resizer of media inside a cell.
4
+ var VISIBLE_BOX_SELECTORS = {
5
+ table: '.pm-table-resizer-container > .resizer-item',
6
+ mediaSingle: '.resizer-item',
7
+ embedCard: '.rich-media-item',
8
+ extension: '.extension-container[data-layout]',
9
+ bodiedExtension: '.extension-container[data-layout]',
10
+ multiBodiedExtension: '.extension-container[data-layout]',
11
+ blockCard: '.datasourceView-content-inner-wrap'
12
+ };
13
+
14
+ /**
15
+ * The box a block's controls anchor to under sparse surfaces. For some blocks this is an inner
16
+ * element, which can be wider than the block itself.
17
+ */
18
+ export var getVisibleBox = function getVisibleBox(dom, nodeType) {
19
+ var selector = VISIBLE_BOX_SELECTORS[nodeType];
20
+ return selector && dom.querySelector(selector) || dom;
21
+ };
@@ -13,6 +13,7 @@ import { getBaseNodeTypeName } from '@atlaskit/editor-common/utils/node-type-uti
13
13
  import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
14
14
  import { DropIndicator } from '@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box';
15
15
  import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter';
16
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-atlaskit-theme
16
17
  import { layers } from '@atlaskit/theme/constants';
17
18
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/editor-experiment';
18
19
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
@@ -0,0 +1,10 @@
1
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
+ import type { BlockControlsPlugin } from '../blockControlsPluginType';
4
+ /** Whether hovering must not show block controls under sparse surfaces. */
5
+ export declare const isSparseHoverSuppressed: (view: EditorView, api: ExtractInjectionAPI<BlockControlsPlugin> | undefined) => boolean;
6
+ /**
7
+ * Shows controls for the block that owns `target`, resolved from PM structure so the first hover
8
+ * can create its sparse candidate. Callers check `isSparseHoverSuppressed` first.
9
+ */
10
+ export declare const handleSparseHoverTarget: (view: EditorView, target: Element, api: ExtractInjectionAPI<BlockControlsPlugin> | undefined) => boolean;
@@ -0,0 +1,27 @@
1
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
+ import type { BlockControlsPlugin } from '../blockControlsPluginType';
4
+ /**
5
+ * Whether the element under the pointer can be part of a block's left gutter: a container block's
6
+ * own padding (panel, layout, list…) or an editor wrapper around the content. Text and leaf blocks
7
+ * have no gutter inside them, and neither do tables: like the legacy hover zones, anywhere inside a
8
+ * table, including cell padding, shows the table's controls. Elements rendered beside the editor
9
+ * (title, popups, toolbars) cover any gutter beneath them.
10
+ */
11
+ export declare const isPossibleGutterElement: (view: EditorView, hoveredElement: EventTarget | null) => hoveredElement is Element;
12
+ /** Returns a function that finds the block whose legacy hover zone contains the pointer. */
13
+ export declare const createGutterBlockFinder: (view: EditorView, api: ExtractInjectionAPI<BlockControlsPlugin> | undefined) => ((event: MouseEvent) => Element | null);
14
+ /**
15
+ * Shows block controls for the block the pointer hovers under sparse surfaces, including the empty
16
+ * space to a block's left (the gutter). This is the only hover handler for sparse surfaces.
17
+ *
18
+ * Reaching text or a leaf block shows its controls straight away. Over an element that can be part
19
+ * of a gutter, only the latest pointer event is kept, and it is resolved once after the frame
20
+ * renders so that reading positions never forces a layout.
21
+ *
22
+ * Returns an unbind function, or `undefined` when the editor's document has no window.
23
+ */
24
+ export declare const bindSparseGutterHover: ({ api, view, }: {
25
+ api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
26
+ view: EditorView;
27
+ }) => (() => void) | undefined;
@@ -1,8 +1,8 @@
1
1
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
2
  import type { EditorState, PluginKey } from '@atlaskit/editor-prosemirror/state';
3
3
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
4
- import type { BlockControlsPlugin } from '../../blockControlsPluginType';
5
- import type { SparseSurfacesState } from './sparse-surfaces-state';
4
+ import type { BlockControlsPlugin } from '../blockControlsPluginType';
5
+ import type { SparseSurfacesState } from './utils/sparse-surfaces-state';
6
6
  type SparseSurfacesPluginView = {
7
7
  destroy: () => void;
8
8
  update: (view: EditorView, previousState: EditorState) => void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The box a block's controls anchor to under sparse surfaces. For some blocks this is an inner
3
+ * element, which can be wider than the block itself.
4
+ */
5
+ export declare const getVisibleBox: (dom: Element, nodeType: string) => Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-controls",
3
- "version": "23.0.9",
3
+ "version": "24.0.1",
4
4
  "description": "Block controls plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -29,23 +29,23 @@
29
29
  "@atlaskit/button": "^25.4.0",
30
30
  "@atlaskit/css": "^1.1.0",
31
31
  "@atlaskit/docs": "^12.3.0",
32
- "@atlaskit/editor-plugin-accessibility-utils": "^22.0.0",
33
- "@atlaskit/editor-plugin-analytics": "^22.0.0",
34
- "@atlaskit/editor-plugin-block-collapse": "^8.0.0",
35
- "@atlaskit/editor-plugin-editor-disabled": "^22.0.0",
36
- "@atlaskit/editor-plugin-editor-viewmode": "^24.0.0",
37
- "@atlaskit/editor-plugin-feature-flags": "^21.0.0",
38
- "@atlaskit/editor-plugin-interaction": "^36.0.0",
39
- "@atlaskit/editor-plugin-limited-mode": "^19.0.0",
40
- "@atlaskit/editor-plugin-metrics": "^23.0.0",
41
- "@atlaskit/editor-plugin-quick-insert": "^22.0.0",
42
- "@atlaskit/editor-plugin-selection": "^22.0.0",
43
- "@atlaskit/editor-plugin-show-diff": "^20.0.0",
44
- "@atlaskit/editor-plugin-toolbar": "^19.0.0",
45
- "@atlaskit/editor-plugin-type-ahead": "^23.0.0",
46
- "@atlaskit/editor-plugin-ui-control-registry": "^16.0.0",
47
- "@atlaskit/editor-plugin-user-intent": "^20.0.0",
48
- "@atlaskit/editor-plugin-width": "^23.0.0",
32
+ "@atlaskit/editor-plugin-accessibility-utils": "^23.0.0",
33
+ "@atlaskit/editor-plugin-analytics": "^23.0.0",
34
+ "@atlaskit/editor-plugin-block-collapse": "^9.0.0",
35
+ "@atlaskit/editor-plugin-editor-disabled": "^23.0.0",
36
+ "@atlaskit/editor-plugin-editor-viewmode": "^25.0.0",
37
+ "@atlaskit/editor-plugin-feature-flags": "^22.0.0",
38
+ "@atlaskit/editor-plugin-interaction": "^37.0.0",
39
+ "@atlaskit/editor-plugin-limited-mode": "^20.0.0",
40
+ "@atlaskit/editor-plugin-metrics": "^24.0.0",
41
+ "@atlaskit/editor-plugin-quick-insert": "^23.0.0",
42
+ "@atlaskit/editor-plugin-selection": "^23.0.0",
43
+ "@atlaskit/editor-plugin-show-diff": "^21.0.0",
44
+ "@atlaskit/editor-plugin-toolbar": "^20.0.0",
45
+ "@atlaskit/editor-plugin-type-ahead": "^24.0.0",
46
+ "@atlaskit/editor-plugin-ui-control-registry": "^17.0.0",
47
+ "@atlaskit/editor-plugin-user-intent": "^21.0.0",
48
+ "@atlaskit/editor-plugin-width": "^24.1.0",
49
49
  "@atlaskit/editor-prosemirror": "^9.0.0",
50
50
  "@atlaskit/editor-shared-styles": "^4.3.0",
51
51
  "@atlaskit/editor-tables": "^3.2.0",
@@ -61,9 +61,9 @@
61
61
  "@atlaskit/primitives": "^22.5.0",
62
62
  "@atlaskit/section-message": "^10.2.0",
63
63
  "@atlaskit/theme": "^28.2.0",
64
- "@atlaskit/tmp-editor-statsig": "^218.0.0",
64
+ "@atlaskit/tmp-editor-statsig": "^219.0.0",
65
65
  "@atlaskit/tokens": "^20.1.0",
66
- "@atlaskit/tooltip": "^24.3.0",
66
+ "@atlaskit/tooltip": "^24.4.0",
67
67
  "@babel/runtime": "^7.0.0",
68
68
  "@compiled/react": "^1.0.2",
69
69
  "@emotion/react": "^11.7.1",
@@ -73,7 +73,7 @@
73
73
  "uuid": "^11.1.1"
74
74
  },
75
75
  "peerDependencies": {
76
- "@atlaskit/editor-common": "^126.4.0",
76
+ "@atlaskit/editor-common": "^127.1.0",
77
77
  "react": "^18.2.0 || ^19.2.0",
78
78
  "react-dom": "^18.2.0 || ^19.2.0",
79
79
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
@@ -1,5 +0,0 @@
1
- import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
- import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
- import type { BlockControlsPlugin } from '../blockControlsPluginType';
4
- /** Resolves hover from PM structure so the first hover can create its sparse candidate. */
5
- export declare const handleSparseMouseOver: (view: EditorView, event: Event, api: ExtractInjectionAPI<BlockControlsPlugin> | undefined) => boolean;