@atlaskit/editor-plugin-block-controls 7.7.2 → 7.7.3

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 (35) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cjs/blockControlsPlugin.js +14 -0
  3. package/dist/cjs/pm-plugins/selection-preservation/editor-commands.js +32 -0
  4. package/dist/cjs/pm-plugins/selection-preservation/plugin-key.js +8 -0
  5. package/dist/cjs/pm-plugins/selection-preservation/pm-plugin.js +99 -0
  6. package/dist/cjs/pm-plugins/selection-preservation/types.js +5 -0
  7. package/dist/cjs/pm-plugins/selection-preservation/utils.js +24 -0
  8. package/dist/cjs/ui/drag-handle.js +182 -62
  9. package/dist/es2019/blockControlsPlugin.js +11 -1
  10. package/dist/es2019/pm-plugins/selection-preservation/editor-commands.js +28 -0
  11. package/dist/es2019/pm-plugins/selection-preservation/plugin-key.js +2 -0
  12. package/dist/es2019/pm-plugins/selection-preservation/pm-plugin.js +92 -0
  13. package/dist/es2019/pm-plugins/selection-preservation/types.js +1 -0
  14. package/dist/es2019/pm-plugins/selection-preservation/utils.js +16 -0
  15. package/dist/es2019/ui/drag-handle.js +156 -33
  16. package/dist/esm/blockControlsPlugin.js +14 -0
  17. package/dist/esm/pm-plugins/selection-preservation/editor-commands.js +26 -0
  18. package/dist/esm/pm-plugins/selection-preservation/plugin-key.js +2 -0
  19. package/dist/esm/pm-plugins/selection-preservation/pm-plugin.js +93 -0
  20. package/dist/esm/pm-plugins/selection-preservation/types.js +1 -0
  21. package/dist/esm/pm-plugins/selection-preservation/utils.js +18 -0
  22. package/dist/esm/ui/drag-handle.js +184 -64
  23. package/dist/types/blockControlsPluginType.d.ts +12 -1
  24. package/dist/types/pm-plugins/selection-preservation/editor-commands.d.ts +13 -0
  25. package/dist/types/pm-plugins/selection-preservation/plugin-key.d.ts +3 -0
  26. package/dist/types/pm-plugins/selection-preservation/pm-plugin.d.ts +26 -0
  27. package/dist/types/pm-plugins/selection-preservation/types.d.ts +7 -0
  28. package/dist/types/pm-plugins/selection-preservation/utils.d.ts +10 -0
  29. package/dist/types-ts4.5/blockControlsPluginType.d.ts +12 -1
  30. package/dist/types-ts4.5/pm-plugins/selection-preservation/editor-commands.d.ts +13 -0
  31. package/dist/types-ts4.5/pm-plugins/selection-preservation/plugin-key.d.ts +3 -0
  32. package/dist/types-ts4.5/pm-plugins/selection-preservation/pm-plugin.d.ts +26 -0
  33. package/dist/types-ts4.5/pm-plugins/selection-preservation/types.d.ts +7 -0
  34. package/dist/types-ts4.5/pm-plugins/selection-preservation/utils.d.ts +10 -0
  35. package/package.json +4 -3
@@ -0,0 +1,18 @@
1
+ import { selectionPreservationPluginKey } from './plugin-key';
2
+ /**
3
+ * Detects if any of the transactions include user-driven selection changes.
4
+ *
5
+ * @param transactions The list of transactions to check.
6
+ * @returns True if any transaction includes a user-driven selection change, otherwise false.
7
+ */
8
+ export var hasUserSelectionChange = function hasUserSelectionChange(transactions) {
9
+ return transactions.some(function (tr) {
10
+ return tr.getMeta('pointer') || tr.getMeta('uiEvent') || tr.getMeta('paste') || tr.getMeta('cut') || tr.getMeta('composition') ||
11
+ // IME input
12
+ // Keyboard events that change selection
13
+ tr.getMeta('addToHistory') && tr.selectionSet;
14
+ });
15
+ };
16
+ export var getSelectionPreservationMeta = function getSelectionPreservationMeta(tr) {
17
+ return tr.getMeta(selectionPreservationPluginKey);
18
+ };
@@ -13,17 +13,18 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
13
13
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
14
14
  import { css, jsx } from '@emotion/react';
15
15
  import { bind } from 'bind-event-listener';
16
+ import { getDocument } from '@atlaskit/browser-apis';
16
17
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
17
18
  import { browser as browserLegacy, getBrowserInfo } from '@atlaskit/editor-common/browser';
18
19
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
19
20
  import { dragToMoveDown, dragToMoveLeft, dragToMoveRight, dragToMoveUp, getAriaKeyshortcuts, TooltipContentWithMultipleShortcuts } from '@atlaskit/editor-common/keymaps';
20
21
  import { blockControlsMessages } from '@atlaskit/editor-common/messages';
21
22
  import { deleteSelectedRange } from '@atlaskit/editor-common/selection';
22
- import { tableControlsSpacing, DRAG_HANDLE_WIDTH } from '@atlaskit/editor-common/styles';
23
+ import { DRAG_HANDLE_WIDTH, tableControlsSpacing } from '@atlaskit/editor-common/styles';
23
24
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
24
25
  import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
25
26
  import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
26
- import { akEditorTableToolbarSize, akEditorFullPageNarrowBreakout, relativeSizeToBaseFontSize } from '@atlaskit/editor-shared-styles/consts';
27
+ import { akEditorFullPageNarrowBreakout, akEditorTableToolbarSize, relativeSizeToBaseFontSize } from '@atlaskit/editor-shared-styles/consts';
27
28
  import DragHandleVerticalIcon from '@atlaskit/icon/core/drag-handle-vertical';
28
29
  import DragHandlerIcon from '@atlaskit/icon/glyph/drag-handler';
29
30
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -37,6 +38,7 @@ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
37
38
  import Tooltip from '@atlaskit/tooltip';
38
39
  import { getNodeTypeWithLevel } from '../pm-plugins/decorations-common';
39
40
  import { key } from '../pm-plugins/main';
41
+ import { selectionPreservationPluginKey } from '../pm-plugins/selection-preservation/plugin-key';
40
42
  import { getMultiSelectAnalyticsAttributes } from '../pm-plugins/utils/analytics';
41
43
  import { getControlBottomCSSValue, getControlHeightCSSValue, getLeftPosition, getNodeHeight, getTopPosition, shouldBeSticky, shouldMaskNodeControls } from '../pm-plugins/utils/drag-handle-positions';
42
44
  import { isHandleCorrelatedToSelection, isNodeWithCodeBlock, selectNode } from '../pm-plugins/utils/getSelection';
@@ -293,18 +295,81 @@ var getNodeMargins = function getNodeMargins(node) {
293
295
  }
294
296
  return nodeMargins[nodeTypeName] || nodeMargins['default'];
295
297
  };
296
- export var DragHandle = function DragHandle(_ref) {
297
- var _api$core3;
298
- var view = _ref.view,
299
- api = _ref.api,
300
- formatMessage = _ref.formatMessage,
301
- getPos = _ref.getPos,
302
- anchorName = _ref.anchorName,
303
- nodeType = _ref.nodeType,
304
- handleOptions = _ref.handleOptions,
305
- _ref$isTopLevelNode = _ref.isTopLevelNode,
306
- isTopLevelNode = _ref$isTopLevelNode === void 0 ? true : _ref$isTopLevelNode,
307
- anchorRectCache = _ref.anchorRectCache;
298
+ var isRangeSpanningMultipleNodes = function isRangeSpanningMultipleNodes(range) {
299
+ if (range.endIndex - range.startIndex <= 1) {
300
+ return false; // At most one child
301
+ }
302
+
303
+ // Count block nodes in the range, return true if more than one
304
+ var blockCount = 0;
305
+ for (var i = range.startIndex; i < range.endIndex; i++) {
306
+ if (range.parent.child(i).isBlock) {
307
+ blockCount++;
308
+ }
309
+ if (blockCount > 1) {
310
+ return true;
311
+ }
312
+ }
313
+ return false;
314
+ };
315
+ var shouldExpandSelection = function shouldExpandSelection(range, startPos) {
316
+ return !!range && isRangeSpanningMultipleNodes(range) && range.start <= startPos && range.end >= startPos + 1;
317
+ };
318
+ var calculateBlockRange = function calculateBlockRange(_ref) {
319
+ var selection = _ref.selection,
320
+ doc = _ref.doc,
321
+ resolvedStartPos = _ref.resolvedStartPos,
322
+ isShiftPressed = _ref.isShiftPressed;
323
+ if (!isShiftPressed) {
324
+ // When not pressing shift, create range including all block nodes within the selection
325
+ return selection.$from.blockRange(selection.$to);
326
+ }
327
+ if (resolvedStartPos.pos < selection.from) {
328
+ // If shift+click selecting upwards, get range from start of node to end of selection
329
+ return resolvedStartPos.blockRange(selection.$to);
330
+ }
331
+
332
+ // Shift+click selecting downwards, get range from start of selection to pos within or after node
333
+ var resolvedPosWithinOrAfterNode = doc.resolve(resolvedStartPos.pos + 1);
334
+ return selection.$from.blockRange(resolvedPosWithinOrAfterNode);
335
+ };
336
+ var createExpandedSelection = function createExpandedSelection(doc, selection, range) {
337
+ return TextSelection.create(doc, Math.min(selection.from, range.start), Math.max(selection.to, range.end));
338
+ };
339
+ var createSelectionFromRange = function createSelectionFromRange(_ref2) {
340
+ var tr = _ref2.tr,
341
+ selection = _ref2.selection,
342
+ startPos = _ref2.startPos,
343
+ nodeType = _ref2.nodeType,
344
+ range = _ref2.range,
345
+ api = _ref2.api;
346
+ if (range && shouldExpandSelection(range, startPos)) {
347
+ var expandedSelection = createExpandedSelection(tr.doc, selection, range);
348
+ if (!expandedSelection.eq(tr.selection)) {
349
+ tr.setSelection(expandedSelection);
350
+ }
351
+ return tr;
352
+ }
353
+ var node = tr.doc.nodeAt(startPos);
354
+ var isEmptyNode = (node === null || node === void 0 ? void 0 : node.content.size) === 0;
355
+ if (isEmptyNode && node.type.name !== 'paragraph') {
356
+ tr.setSelection(new NodeSelection(tr.doc.resolve(startPos)));
357
+ return tr;
358
+ }
359
+ return selectNode(tr, startPos, nodeType, api);
360
+ };
361
+ export var DragHandle = function DragHandle(_ref3) {
362
+ var _api$core4;
363
+ var view = _ref3.view,
364
+ api = _ref3.api,
365
+ formatMessage = _ref3.formatMessage,
366
+ getPos = _ref3.getPos,
367
+ anchorName = _ref3.anchorName,
368
+ nodeType = _ref3.nodeType,
369
+ handleOptions = _ref3.handleOptions,
370
+ _ref3$isTopLevelNode = _ref3.isTopLevelNode,
371
+ isTopLevelNode = _ref3$isTopLevelNode === void 0 ? true : _ref3$isTopLevelNode,
372
+ anchorRectCache = _ref3.anchorRectCache;
308
373
  var buttonRef = useRef(null);
309
374
  var _useState = useState(false),
310
375
  _useState2 = _slicedToArray(_useState, 2),
@@ -365,14 +430,69 @@ export var DragHandle = function DragHandle(_ref) {
365
430
  }
366
431
  }
367
432
  }, [anchorName, nodeType, view.dom]);
368
- var handleOnClick = useCallback(function (e) {
433
+ var handleOnClickNew = useCallback(function (e) {
369
434
  var _api$core;
435
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref4) {
436
+ var _api$analytics, _resolvedStartPos$nod, _selectionPreservatio, _api$blockControls, _api$blockControls2;
437
+ var tr = _ref4.tr;
438
+ var startPos = getPos();
439
+ if (startPos === undefined) {
440
+ return tr;
441
+ }
442
+ var resolvedStartPos = tr.doc.resolve(startPos);
443
+ api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.attachAnalyticsEvent({
444
+ eventType: EVENT_TYPE.UI,
445
+ action: ACTION.CLICKED,
446
+ actionSubject: ACTION_SUBJECT.BUTTON,
447
+ actionSubjectId: ACTION_SUBJECT_ID.ELEMENT_DRAG_HANDLE,
448
+ attributes: {
449
+ nodeDepth: resolvedStartPos.depth,
450
+ nodeType: ((_resolvedStartPos$nod = resolvedStartPos.nodeAfter) === null || _resolvedStartPos$nod === void 0 ? void 0 : _resolvedStartPos$nod.type.name) || ''
451
+ }
452
+ })(tr);
453
+ var preservedSelection = (_selectionPreservatio = selectionPreservationPluginKey.getState(view.state)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection;
454
+ var selection = preservedSelection || tr.selection;
455
+ var range = calculateBlockRange({
456
+ doc: tr.doc,
457
+ selection: selection,
458
+ resolvedStartPos: resolvedStartPos,
459
+ isShiftPressed: e.shiftKey
460
+ });
461
+ tr = createSelectionFromRange({
462
+ tr: tr,
463
+ selection: selection,
464
+ startPos: startPos,
465
+ nodeType: nodeType,
466
+ range: range,
467
+ api: api
468
+ });
469
+ api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || _api$blockControls.commands.startPreservingSelection()({
470
+ tr: tr
471
+ });
472
+ api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || _api$blockControls2.commands.toggleBlockMenu({
473
+ anchorName: anchorName,
474
+ openedViaKeyboard: false,
475
+ triggerByNode: expValEqualsNoExposure('platform_synced_block', 'isEnabled', true) ? {
476
+ nodeType: nodeType,
477
+ pos: startPos,
478
+ rootPos: tr.doc.resolve(startPos).before(1)
479
+ } : undefined
480
+ })({
481
+ tr: tr
482
+ });
483
+ tr.setMeta('scrollIntoView', false);
484
+ return tr;
485
+ });
486
+ view.focus();
487
+ }, [api, view, getPos, nodeType, anchorName]);
488
+ var handleOnClick = useCallback(function (e) {
489
+ var _api$core2;
370
490
  if (!isMultiSelect) {
371
491
  setDragHandleSelected(!dragHandleSelected);
372
492
  }
373
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref2) {
374
- var _api$blockControls$sh, _api$analytics;
375
- var tr = _ref2.tr;
493
+ api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (_ref5) {
494
+ var _api$blockControls$sh, _api$analytics2;
495
+ var tr = _ref5.tr;
376
496
  var startPos = getPos();
377
497
  if (startPos === undefined) {
378
498
  return tr;
@@ -401,8 +521,8 @@ export var DragHandle = function DragHandle(_ref) {
401
521
  rootPos: rootPos
402
522
  } : undefined;
403
523
  if (BLOCK_MENU_ENABLED && editorExperiment('platform_editor_controls', 'variant1')) {
404
- var _api$blockControls;
405
- api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || _api$blockControls.commands.toggleBlockMenu({
524
+ var _api$blockControls3;
525
+ api === null || api === void 0 || (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 || _api$blockControls3.commands.toggleBlockMenu({
406
526
  anchorName: anchorName,
407
527
  triggerByNode: triggerByNode,
408
528
  openedViaKeyboard: expValEqualsNoExposure('platform_editor_block_menu_keyboard_navigation', 'isEnabled', true) ? false : undefined
@@ -411,8 +531,8 @@ export var DragHandle = function DragHandle(_ref) {
411
531
  });
412
532
  e.stopPropagation();
413
533
  } else if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
414
- var _api$blockControls2;
415
- api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || _api$blockControls2.commands.toggleBlockMenu({
534
+ var _api$blockControls4;
535
+ api === null || api === void 0 || (_api$blockControls4 = api.blockControls) === null || _api$blockControls4 === void 0 || _api$blockControls4.commands.toggleBlockMenu({
416
536
  anchorName: anchorName,
417
537
  triggerByNode: triggerByNode,
418
538
  openedViaKeyboard: expValEqualsNoExposure('platform_editor_block_menu_keyboard_navigation', 'isEnabled', true) ? false : undefined
@@ -422,18 +542,18 @@ export var DragHandle = function DragHandle(_ref) {
422
542
  e.stopPropagation();
423
543
  }
424
544
  } else if (isTopLevelNode && $anchor.depth <= DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH && e.shiftKey && fg('platform_editor_elements_dnd_shift_click_select')) {
425
- var _api$blockControls3;
545
+ var _api$blockControls5;
426
546
  var alignAnchorHeadToSel = alignAnchorHeadInDirectionOfPos(tr.selection, startPos);
427
547
  var selectionWithExpandedHead = expandSelectionHeadToNodeAtPos(alignAnchorHeadToSel, startPos);
428
548
  tr.setSelection(selectionWithExpandedHead);
429
- api === null || api === void 0 || (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 || _api$blockControls3.commands.setMultiSelectPositions()({
549
+ api === null || api === void 0 || (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 || _api$blockControls5.commands.setMultiSelectPositions()({
430
550
  tr: tr
431
551
  });
432
552
  }
433
553
  var resolvedMovingNode = tr.doc.resolve(startPos);
434
554
  var maybeNode = resolvedMovingNode.nodeAfter;
435
555
  tr.setMeta('scrollIntoView', false);
436
- api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.attachAnalyticsEvent({
556
+ api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || _api$analytics2.actions.attachAnalyticsEvent({
437
557
  eventType: EVENT_TYPE.UI,
438
558
  action: ACTION.CLICKED,
439
559
  actionSubject: ACTION_SUBJECT.BUTTON,
@@ -450,10 +570,10 @@ export var DragHandle = function DragHandle(_ref) {
450
570
  var handleKeyDown = useCallback(function (e) {
451
571
  // allow user to use spacebar to select the node
452
572
  if (!e.repeat && e.key === ' ') {
453
- var _api$core2;
573
+ var _api$core3;
454
574
  var startPos = getPos();
455
- api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (_ref3) {
456
- var tr = _ref3.tr;
575
+ api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (_ref6) {
576
+ var tr = _ref6.tr;
457
577
  if (startPos === undefined) {
458
578
  return tr;
459
579
  }
@@ -476,20 +596,20 @@ export var DragHandle = function DragHandle(_ref) {
476
596
  // return focus to editor to resume editing from caret position
477
597
  view.focus();
478
598
  }
479
- }, [getPos, api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions, isMultiSelect, view]);
599
+ }, [getPos, api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions, isMultiSelect, view]);
480
600
  var handleKeyDownNew = useCallback(function (e) {
481
601
  // allow user to use spacebar to select the node
482
602
  if (e.key === 'Enter' || !e.repeat && e.key === ' ') {
483
- var _api$core4;
484
- if (document.activeElement !== buttonRef.current) {
603
+ var _getDocument, _api$core5;
604
+ if (((_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.activeElement) !== buttonRef.current) {
485
605
  return;
486
606
  }
487
607
  e.preventDefault();
488
608
  e.stopPropagation();
489
609
  var startPos = getPos();
490
- api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(function (_ref4) {
491
- var _api$blockControls4, _api$userIntent;
492
- var tr = _ref4.tr;
610
+ api === null || api === void 0 || (_api$core5 = api.core) === null || _api$core5 === void 0 || _api$core5.actions.execute(function (_ref7) {
611
+ var _api$blockControls6, _api$userIntent;
612
+ var tr = _ref7.tr;
493
613
  if (startPos === undefined) {
494
614
  return tr;
495
615
  }
@@ -503,7 +623,7 @@ export var DragHandle = function DragHandle(_ref) {
503
623
  pos: startPos,
504
624
  rootPos: rootPos
505
625
  } : undefined;
506
- api === null || api === void 0 || (_api$blockControls4 = api.blockControls) === null || _api$blockControls4 === void 0 || _api$blockControls4.commands.toggleBlockMenu({
626
+ api === null || api === void 0 || (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 || _api$blockControls6.commands.toggleBlockMenu({
507
627
  anchorName: anchorName,
508
628
  triggerByNode: triggerByNode,
509
629
  openedViaKeyboard: true
@@ -518,11 +638,11 @@ export var DragHandle = function DragHandle(_ref) {
518
638
  } else if (e.key === 'Backspace' || e.key === 'Delete') {
519
639
  e.preventDefault();
520
640
  e.stopPropagation();
521
- api === null || api === void 0 || api.core.actions.execute(function (_ref5) {
522
- var _api$blockControls5;
523
- var tr = _ref5.tr;
641
+ api === null || api === void 0 || api.core.actions.execute(function (_ref8) {
642
+ var _api$blockControls7;
643
+ var tr = _ref8.tr;
524
644
  deleteSelectedRange(tr);
525
- api === null || api === void 0 || (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 || _api$blockControls5.commands.toggleBlockMenu({
645
+ api === null || api === void 0 || (_api$blockControls7 = api.blockControls) === null || _api$blockControls7 === void 0 || _api$blockControls7.commands.toggleBlockMenu({
526
646
  closeMenu: true
527
647
  })({
528
648
  tr: tr
@@ -550,21 +670,21 @@ export var DragHandle = function DragHandle(_ref) {
550
670
  start: start
551
671
  };
552
672
  },
553
- onGenerateDragPreview: function onGenerateDragPreview(_ref6) {
673
+ onGenerateDragPreview: function onGenerateDragPreview(_ref9) {
554
674
  var _api$blockControls$sh2;
555
- var nativeSetDragImage = _ref6.nativeSetDragImage;
675
+ var nativeSetDragImage = _ref9.nativeSetDragImage;
556
676
  if (isMultiSelect) {
557
- var _api$core5;
558
- api === null || api === void 0 || (_api$core5 = api.core) === null || _api$core5 === void 0 || _api$core5.actions.execute(function (_ref7) {
559
- var tr = _ref7.tr;
677
+ var _api$core6;
678
+ api === null || api === void 0 || (_api$core6 = api.core) === null || _api$core6 === void 0 || _api$core6.actions.execute(function (_ref0) {
679
+ var tr = _ref0.tr;
560
680
  var handlePos = getPos();
561
681
  if (typeof handlePos !== 'number') {
562
682
  return tr;
563
683
  }
564
684
  var newHandlePosCheck = isHandleCorrelatedToSelection(view.state, tr.selection, handlePos);
565
685
  if (!tr.selection.empty && newHandlePosCheck) {
566
- var _api$blockControls6;
567
- api === null || api === void 0 || (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 || _api$blockControls6.commands.setMultiSelectPositions()({
686
+ var _api$blockControls8;
687
+ api === null || api === void 0 || (_api$blockControls8 = api.blockControls) === null || _api$blockControls8 === void 0 || _api$blockControls8.commands.setMultiSelectPositions()({
568
688
  tr: tr
569
689
  });
570
690
  } else if (fg('platform_editor_elements_dnd_select_node_on_drag')) {
@@ -634,8 +754,8 @@ export var DragHandle = function DragHandle(_ref) {
634
754
  };
635
755
  }
636
756
  },
637
- render: function render(_ref8) {
638
- var container = _ref8.container;
757
+ render: function render(_ref1) {
758
+ var container = _ref1.container;
639
759
  var dom = view.dom.querySelector("[".concat(getAnchorAttrName(), "=\"").concat(anchorName, "\"]"));
640
760
  if (!dom) {
641
761
  return;
@@ -667,13 +787,13 @@ export var DragHandle = function DragHandle(_ref) {
667
787
  });
668
788
  },
669
789
  onDragStart: function onDragStart() {
670
- var _api$core6;
790
+ var _api$core7;
671
791
  if (start === undefined) {
672
792
  return;
673
793
  }
674
- api === null || api === void 0 || (_api$core6 = api.core) === null || _api$core6 === void 0 || _api$core6.actions.execute(function (_ref9) {
675
- var _api$blockControls$sh3, _api$blockControls7, _api$analytics2;
676
- var tr = _ref9.tr;
794
+ api === null || api === void 0 || (_api$core7 = api.core) === null || _api$core7 === void 0 || _api$core7.actions.execute(function (_ref10) {
795
+ var _api$blockControls$sh3, _api$blockControls9, _api$analytics3;
796
+ var tr = _ref10.tr;
677
797
  var nodeTypes, hasSelectedMultipleNodes;
678
798
  var resolvedMovingNode = tr.doc.resolve(start);
679
799
  var maybeNode = resolvedMovingNode.nodeAfter;
@@ -686,11 +806,11 @@ export var DragHandle = function DragHandle(_ref) {
686
806
  nodeTypes = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name;
687
807
  hasSelectedMultipleNodes = false;
688
808
  }
689
- api === null || api === void 0 || (_api$blockControls7 = api.blockControls) === null || _api$blockControls7 === void 0 || _api$blockControls7.commands.setNodeDragged(getPos, anchorName, nodeType)({
809
+ api === null || api === void 0 || (_api$blockControls9 = api.blockControls) === null || _api$blockControls9 === void 0 || _api$blockControls9.commands.setNodeDragged(getPos, anchorName, nodeType)({
690
810
  tr: tr
691
811
  });
692
812
  tr.setMeta('scrollIntoView', false);
693
- api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || _api$analytics2.actions.attachAnalyticsEvent({
813
+ api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 || _api$analytics3.actions.attachAnalyticsEvent({
694
814
  eventType: EVENT_TYPE.UI,
695
815
  action: ACTION.DRAGGED,
696
816
  actionSubject: ACTION_SUBJECT.ELEMENT,
@@ -984,7 +1104,7 @@ export var DragHandle = function DragHandle(_ref) {
984
1104
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
985
1105
  ,
986
1106
  style: !editorExperiment('platform_editor_controls', 'variant1') ? editorExperiment('platform_editor_block_control_optimise_render', true) ? positionStyles : positionStylesOld : {},
987
- onClick: handleOnClick,
1107
+ onClick: expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? handleOnClickNew : handleOnClick,
988
1108
  onKeyDown: expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) && expValEqualsNoExposure('platform_editor_block_menu_keyboard_navigation', 'isEnabled', true) ? handleKeyDownNew : handleKeyDown
989
1109
  // eslint-disable-next-line @atlaskit/design-system/no-direct-use-of-web-platform-drag-and-drop
990
1110
  ,
@@ -1069,15 +1189,15 @@ export var DragHandle = function DragHandle(_ref) {
1069
1189
  var render = isTooltip ? buttonWithTooltip() : renderButton();
1070
1190
  return editorExperiment('platform_editor_controls', 'variant1') ? stickyRender : render;
1071
1191
  };
1072
- export var DragHandleWithVisibility = function DragHandleWithVisibility(_ref0) {
1073
- var view = _ref0.view,
1074
- api = _ref0.api,
1075
- formatMessage = _ref0.formatMessage,
1076
- getPos = _ref0.getPos,
1077
- anchorName = _ref0.anchorName,
1078
- nodeType = _ref0.nodeType,
1079
- handleOptions = _ref0.handleOptions,
1080
- anchorRectCache = _ref0.anchorRectCache;
1192
+ export var DragHandleWithVisibility = function DragHandleWithVisibility(_ref11) {
1193
+ var view = _ref11.view,
1194
+ api = _ref11.api,
1195
+ formatMessage = _ref11.formatMessage,
1196
+ getPos = _ref11.getPos,
1197
+ anchorName = _ref11.anchorName,
1198
+ nodeType = _ref11.nodeType,
1199
+ handleOptions = _ref11.handleOptions,
1200
+ anchorRectCache = _ref11.anchorRectCache;
1081
1201
  return jsx(VisibilityContainer, {
1082
1202
  api: api
1083
1203
  }, jsx(DragHandle, {
@@ -1,6 +1,6 @@
1
1
  import { type IntlShape } from 'react-intl-next';
2
2
  import { type INPUT_METHOD } from '@atlaskit/editor-common/analytics';
3
- import type { EditorCommand, NextEditorPlugin, OptionalPlugin, DIRECTION } from '@atlaskit/editor-common/types';
3
+ import type { DIRECTION, EditorCommand, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
4
4
  import type { AccessibilityUtilsPlugin } from '@atlaskit/editor-plugin-accessibility-utils';
5
5
  import { type AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
6
6
  import type { EditorDisabledPlugin } from '@atlaskit/editor-plugin-editor-disabled';
@@ -132,6 +132,17 @@ export type BlockControlsPlugin = NextEditorPlugin<'blockControls', {
132
132
  setNodeDragged: (getPos: () => number | undefined, anchorName: string, nodeType: string) => EditorCommand;
133
133
  setSelectedViaDragHandle: (isSelectedViaDragHandle?: boolean) => EditorCommand;
134
134
  showDragHandleAt: (pos: number, anchorName: string, nodeType: string, handleOptions?: HandleOptions, rootPos?: number, rootAnchorName?: string, rootNodeType?: string) => EditorCommand;
135
+ /**
136
+ * Starts preserving the current selection across transactions.
137
+ * Used when opening block menus, dragging, or other interactions
138
+ * where multi-node selections should remain stable.
139
+ */
140
+ startPreservingSelection: () => EditorCommand;
141
+ /**
142
+ * Stops preserving the selection, allowing it to change freely.
143
+ * Called when block menus close or drag operations end.
144
+ */
145
+ stopPreservingSelection: () => EditorCommand;
135
146
  toggleBlockMenu: (options?: {
136
147
  anchorName?: string;
137
148
  closeMenu?: boolean;
@@ -0,0 +1,13 @@
1
+ import type { EditorCommand } from '@atlaskit/editor-common/types';
2
+ /**
3
+ * Start preserving the selection when a UI interaction requires it
4
+ *
5
+ * e.g., block menu open, drag-and-drop in progress
6
+ */
7
+ export declare const startPreservingSelection: EditorCommand;
8
+ /**
9
+ * Stop preserving the selection when a UI interaction completes
10
+ *
11
+ * e.g., block menu closed, drag-and-drop ended
12
+ */
13
+ export declare const stopPreservingSelection: EditorCommand;
@@ -0,0 +1,3 @@
1
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
2
+ import type { SelectionPreservationPluginState } from './types';
3
+ export declare const selectionPreservationPluginKey: PluginKey<SelectionPreservationPluginState>;
@@ -0,0 +1,26 @@
1
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import type { SelectionPreservationPluginState } from './types';
3
+ /**
4
+ * Selection Preservation Plugin for ProseMirror Editor
5
+ *
6
+ * Solves a ProseMirror limitation where TextSelection cannot include positions at node boundaries
7
+ * (like media/images). When a selection spans text + media nodes, subsequent transactions cause
8
+ * ProseMirror to collapse the selection to the nearest inline position, excluding the media node.
9
+ * This is problematic for features like block menus and drag-and-drop that need stable multi-node
10
+ * selections while performing operations.
11
+ *
12
+ * The plugin works in three phases:
13
+ * (1) Explicitly save a selection via startPreservingSelection() when opening block menus or starting drag operations.
14
+ * (2) Map the saved selection through document changes to keep positions valid.
15
+ * (3) Detect when transactions collapse the selection and restore it via appendTransaction().
16
+ *
17
+ * Stops preserving via stopPreservingSelection() when the menu closes or operation completes.
18
+ *
19
+ * Commands: startPreservingSelection() to begin preservation, stopPreservingSelection() to end it.
20
+ *
21
+ * NOTE: Only use when the UI blocks user selection changes. For example: when a block menu overlay
22
+ * is open (editor becomes non-interactive), during drag-and-drop operations (user is mid-drag), or
23
+ * when modal dialogs are active. In these states, any selection changes are from ProseMirror's
24
+ * internal behavior (not user input) and should be prevented. Do not use during normal editing.
25
+ */
26
+ export declare const createSelectionPreservationPlugin: () => SafePlugin<SelectionPreservationPluginState>;
@@ -0,0 +1,7 @@
1
+ import type { Selection } from '@atlaskit/editor-prosemirror/state';
2
+ export type SelectionPreservationPluginState = {
3
+ preservedSelection?: Selection;
4
+ };
5
+ export type SelectionPreservationMeta = {
6
+ type: 'startPreserving' | 'stopPreserving';
7
+ };
@@ -0,0 +1,10 @@
1
+ import type { ReadonlyTransaction, Transaction } from '@atlaskit/editor-prosemirror/state';
2
+ import type { SelectionPreservationMeta } from './types';
3
+ /**
4
+ * Detects if any of the transactions include user-driven selection changes.
5
+ *
6
+ * @param transactions The list of transactions to check.
7
+ * @returns True if any transaction includes a user-driven selection change, otherwise false.
8
+ */
9
+ export declare const hasUserSelectionChange: (transactions: readonly Transaction[]) => boolean;
10
+ export declare const getSelectionPreservationMeta: (tr: Transaction | ReadonlyTransaction) => SelectionPreservationMeta | undefined;
@@ -1,6 +1,6 @@
1
1
  import { type IntlShape } from 'react-intl-next';
2
2
  import { type INPUT_METHOD } from '@atlaskit/editor-common/analytics';
3
- import type { EditorCommand, NextEditorPlugin, OptionalPlugin, DIRECTION } from '@atlaskit/editor-common/types';
3
+ import type { DIRECTION, EditorCommand, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
4
4
  import type { AccessibilityUtilsPlugin } from '@atlaskit/editor-plugin-accessibility-utils';
5
5
  import { type AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
6
6
  import type { EditorDisabledPlugin } from '@atlaskit/editor-plugin-editor-disabled';
@@ -132,6 +132,17 @@ export type BlockControlsPlugin = NextEditorPlugin<'blockControls', {
132
132
  setNodeDragged: (getPos: () => number | undefined, anchorName: string, nodeType: string) => EditorCommand;
133
133
  setSelectedViaDragHandle: (isSelectedViaDragHandle?: boolean) => EditorCommand;
134
134
  showDragHandleAt: (pos: number, anchorName: string, nodeType: string, handleOptions?: HandleOptions, rootPos?: number, rootAnchorName?: string, rootNodeType?: string) => EditorCommand;
135
+ /**
136
+ * Starts preserving the current selection across transactions.
137
+ * Used when opening block menus, dragging, or other interactions
138
+ * where multi-node selections should remain stable.
139
+ */
140
+ startPreservingSelection: () => EditorCommand;
141
+ /**
142
+ * Stops preserving the selection, allowing it to change freely.
143
+ * Called when block menus close or drag operations end.
144
+ */
145
+ stopPreservingSelection: () => EditorCommand;
135
146
  toggleBlockMenu: (options?: {
136
147
  anchorName?: string;
137
148
  closeMenu?: boolean;
@@ -0,0 +1,13 @@
1
+ import type { EditorCommand } from '@atlaskit/editor-common/types';
2
+ /**
3
+ * Start preserving the selection when a UI interaction requires it
4
+ *
5
+ * e.g., block menu open, drag-and-drop in progress
6
+ */
7
+ export declare const startPreservingSelection: EditorCommand;
8
+ /**
9
+ * Stop preserving the selection when a UI interaction completes
10
+ *
11
+ * e.g., block menu closed, drag-and-drop ended
12
+ */
13
+ export declare const stopPreservingSelection: EditorCommand;
@@ -0,0 +1,3 @@
1
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
2
+ import type { SelectionPreservationPluginState } from './types';
3
+ export declare const selectionPreservationPluginKey: PluginKey<SelectionPreservationPluginState>;
@@ -0,0 +1,26 @@
1
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import type { SelectionPreservationPluginState } from './types';
3
+ /**
4
+ * Selection Preservation Plugin for ProseMirror Editor
5
+ *
6
+ * Solves a ProseMirror limitation where TextSelection cannot include positions at node boundaries
7
+ * (like media/images). When a selection spans text + media nodes, subsequent transactions cause
8
+ * ProseMirror to collapse the selection to the nearest inline position, excluding the media node.
9
+ * This is problematic for features like block menus and drag-and-drop that need stable multi-node
10
+ * selections while performing operations.
11
+ *
12
+ * The plugin works in three phases:
13
+ * (1) Explicitly save a selection via startPreservingSelection() when opening block menus or starting drag operations.
14
+ * (2) Map the saved selection through document changes to keep positions valid.
15
+ * (3) Detect when transactions collapse the selection and restore it via appendTransaction().
16
+ *
17
+ * Stops preserving via stopPreservingSelection() when the menu closes or operation completes.
18
+ *
19
+ * Commands: startPreservingSelection() to begin preservation, stopPreservingSelection() to end it.
20
+ *
21
+ * NOTE: Only use when the UI blocks user selection changes. For example: when a block menu overlay
22
+ * is open (editor becomes non-interactive), during drag-and-drop operations (user is mid-drag), or
23
+ * when modal dialogs are active. In these states, any selection changes are from ProseMirror's
24
+ * internal behavior (not user input) and should be prevented. Do not use during normal editing.
25
+ */
26
+ export declare const createSelectionPreservationPlugin: () => SafePlugin<SelectionPreservationPluginState>;
@@ -0,0 +1,7 @@
1
+ import type { Selection } from '@atlaskit/editor-prosemirror/state';
2
+ export type SelectionPreservationPluginState = {
3
+ preservedSelection?: Selection;
4
+ };
5
+ export type SelectionPreservationMeta = {
6
+ type: 'startPreserving' | 'stopPreserving';
7
+ };
@@ -0,0 +1,10 @@
1
+ import type { ReadonlyTransaction, Transaction } from '@atlaskit/editor-prosemirror/state';
2
+ import type { SelectionPreservationMeta } from './types';
3
+ /**
4
+ * Detects if any of the transactions include user-driven selection changes.
5
+ *
6
+ * @param transactions The list of transactions to check.
7
+ * @returns True if any transaction includes a user-driven selection change, otherwise false.
8
+ */
9
+ export declare const hasUserSelectionChange: (transactions: readonly Transaction[]) => boolean;
10
+ export declare const getSelectionPreservationMeta: (tr: Transaction | ReadonlyTransaction) => SelectionPreservationMeta | undefined;