@atlaskit/editor-plugin-block-controls 8.0.5 → 8.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 8.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [`81c5041650743`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/81c5041650743) -
8
+ EDITOR-4284 Fix move down visual selection persisting
9
+ - Updated dependencies
10
+
11
+ ## 8.0.6
12
+
13
+ ### Patch Changes
14
+
15
+ - [`52a4db18c2a6a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/52a4db18c2a6a) -
16
+ EDITOR-4161 Fix drag handle selection collapse in nested scenarios
17
+ - Updated dependencies
18
+
3
19
  ## 8.0.5
4
20
 
5
21
  ### Patch Changes
@@ -66,7 +66,7 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
66
66
  if (newState.preservedSelection && tr.docChanged) {
67
67
  newState.preservedSelection = (0, _selection.mapPreservedSelection)(newState.preservedSelection, tr);
68
68
  }
69
- if (newState.preservedSelection !== pluginState.preservedSelection) {
69
+ if (!(0, _utils.compareSelections)(newState.preservedSelection, pluginState.preservedSelection)) {
70
70
  if (newState !== null && newState !== void 0 && newState.preservedSelection) {
71
71
  var _api$selection;
72
72
  api === null || api === void 0 || api.core.actions.execute(api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || (_api$selection = _api$selection.commands) === null || _api$selection === void 0 ? void 0 : _api$selection.setBlockSelection(newState.preservedSelection));
@@ -80,26 +80,25 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
80
80
  },
81
81
  appendTransaction: function appendTransaction(transactions, _oldState, newState) {
82
82
  var pluginState = _pluginKey.selectionPreservationPluginKey.getState(newState);
83
- var savedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
84
- if (!savedSel) {
83
+ var preservedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
84
+ var stateSel = newState.selection;
85
+ if (!preservedSel) {
85
86
  return null;
86
87
  }
87
88
 
88
89
  // Auto-stop if user explicitly changes selection or selection is set within a code block
89
- if ((0, _utils.hasUserSelectionChange)(transactions) || (0, _utils.isSelectionWithinCodeBlock)(newState.selection)) {
90
- // Auto-stop if user explicitly changes selection
90
+ if ((0, _utils.hasUserSelectionChange)(transactions) || (0, _utils.isSelectionWithinCodeBlock)(stateSel)) {
91
91
  return (0, _editorCommands.stopPreservingSelection)({
92
92
  tr: newState.tr
93
93
  });
94
94
  }
95
- var currSel = newState.selection;
96
- var selectionUnchanged = currSel.from === savedSel.from && currSel.to === savedSel.to;
97
- var selectionInvalid = savedSel.from < 0 || savedSel.to > newState.doc.content.size;
95
+ var selectionUnchanged = stateSel.from === preservedSel.from && stateSel.to === preservedSel.to;
96
+ var selectionInvalid = preservedSel.from < 0 || preservedSel.to > newState.doc.content.size;
98
97
  if (selectionUnchanged || selectionInvalid) {
99
98
  return null;
100
99
  }
101
100
  try {
102
- return newState.tr.setSelection(savedSel);
101
+ return newState.tr.setSelection(preservedSel);
103
102
  } catch (error) {
104
103
  (0, _monitoring.logException)(error, {
105
104
  location: 'editor-plugin-block-controls/SelectionPreservationPlugin'
@@ -107,6 +106,15 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
107
106
  }
108
107
  return null;
109
108
  },
109
+ view: function view() {
110
+ return {
111
+ update: function update(view, prevState) {
112
+ if ((0, _utils.isPreservedSelectionChanged)(view.state, prevState)) {
113
+ (0, _utils.syncDOMSelection)(view.state.selection);
114
+ }
115
+ }
116
+ };
117
+ },
110
118
  props: {
111
119
  handleClick: function handleClick(view, pos, event) {
112
120
  var _ref = _pluginKey.selectionPreservationPluginKey.getState(view.state) || {},
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isSelectionWithinCodeBlock = exports.hasUserSelectionChange = exports.getSelectionPreservationMeta = void 0;
6
+ exports.syncDOMSelection = exports.isSelectionWithinCodeBlock = exports.isPreservedSelectionChanged = exports.hasUserSelectionChange = exports.getSelectionPreservationMeta = exports.compareSelections = void 0;
7
7
  var _pluginKey = require("./plugin-key");
8
8
  /**
9
9
  * Detects if any of the transactions include user-driven selection changes.
@@ -30,4 +30,52 @@ var isSelectionWithinCodeBlock = exports.isSelectionWithinCodeBlock = function i
30
30
  var $from = _ref.$from,
31
31
  $to = _ref.$to;
32
32
  return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
33
+ };
34
+
35
+ /**
36
+ * Compares two selections for equality based on their from and to positions.
37
+ *
38
+ * @param a The first selection to compare.
39
+ * @param b The second selection to compare.
40
+ * @returns True if both selections are equal, otherwise false.
41
+ */
42
+ var compareSelections = exports.compareSelections = function compareSelections(a, b) {
43
+ return (a === null || a === void 0 ? void 0 : a.from) === (b === null || b === void 0 ? void 0 : b.from) && (a === null || a === void 0 ? void 0 : a.to) === (b === null || b === void 0 ? void 0 : b.to);
44
+ };
45
+
46
+ /**
47
+ * Returns true/false indicating whether the preserved selection
48
+ * has changed between the old and new editor states.
49
+ *
50
+ * @param newState The new editor state.
51
+ * @param oldState The old editor state.
52
+ * @returns True if the preserved selection has changed, otherwise false.
53
+ */
54
+ var isPreservedSelectionChanged = exports.isPreservedSelectionChanged = function isPreservedSelectionChanged(newState, oldState) {
55
+ var _selectionPreservatio, _selectionPreservatio2;
56
+ var prev = (_selectionPreservatio = _pluginKey.selectionPreservationPluginKey.getState(oldState)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection;
57
+ var curr = (_selectionPreservatio2 = _pluginKey.selectionPreservationPluginKey.getState(newState)) === null || _selectionPreservatio2 === void 0 ? void 0 : _selectionPreservatio2.preservedSelection;
58
+ return !!prev && !!curr && !compareSelections(prev, curr);
59
+ };
60
+
61
+ /**
62
+ * Triggers a DOM selection sync by resetting the current native selection range
63
+ * only if it is out of sync with the provided ProseMirror selection state.
64
+ *
65
+ * This is a necessary workaround to ensure the browser's native selection state
66
+ * stays in sync with the preserved selection, particularly after transactions
67
+ * that shift document content.
68
+ *
69
+ * @param selection The current ProseMirror selection state to compare against.
70
+ */
71
+ var syncDOMSelection = exports.syncDOMSelection = function syncDOMSelection(selection) {
72
+ var domSelection = window.getSelection();
73
+ var domRange = domSelection && domSelection.rangeCount === 1 && domSelection.getRangeAt(0).cloneRange();
74
+ var isOutOfSync = domRange && (selection.from !== domRange.startOffset || selection.to !== domRange.endOffset);
75
+ if (isOutOfSync) {
76
+ // Force the DOM selection to refresh, setting it to the same range
77
+ // This will trigger ProseMirror to re-apply its selection logic based on the current state
78
+ domSelection.removeAllRanges();
79
+ domSelection.addRange(domRange);
80
+ }
33
81
  };
@@ -463,6 +463,12 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref3) {
463
463
  }
464
464
  }
465
465
  }, [anchorName, nodeType, view.dom]);
466
+ var handleMouseUp = (0, _react.useCallback)(function (e) {
467
+ // Stop propagation so that for drag handles in nested scenarios the click is captured
468
+ // and doesn't propagate to the edge of the element and trigger a node selection
469
+ // on the parent element
470
+ e.stopPropagation();
471
+ }, []);
466
472
  var handleOnClickNew = (0, _react.useCallback)(function (e) {
467
473
  var _api$core;
468
474
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref4) {
@@ -1085,6 +1091,7 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref3) {
1085
1091
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
1086
1092
  ,
1087
1093
  style: !(0, _experiments.editorExperiment)('platform_editor_controls', 'variant1') ? (0, _experiments.editorExperiment)('platform_editor_block_control_optimise_render', true) ? positionStyles : positionStylesOld : {},
1094
+ onMouseUp: (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true) ? handleMouseUp : undefined,
1088
1095
  onClick: (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true) ? handleOnClickNew : handleOnClick,
1089
1096
  onKeyDown: (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true) ? handleKeyDownNew : handleKeyDown
1090
1097
  // eslint-disable-next-line @atlaskit/design-system/no-direct-use-of-web-platform-drag-and-drop
@@ -4,7 +4,7 @@ import { DRAG_HANDLE_SELECTOR } from '@atlaskit/editor-common/styles';
4
4
  import { mapPreservedSelection } from '../utils/selection';
5
5
  import { stopPreservingSelection } from './editor-commands';
6
6
  import { selectionPreservationPluginKey } from './plugin-key';
7
- import { getSelectionPreservationMeta, hasUserSelectionChange, isSelectionWithinCodeBlock } from './utils';
7
+ import { compareSelections, getSelectionPreservationMeta, hasUserSelectionChange, isPreservedSelectionChanged, isSelectionWithinCodeBlock, syncDOMSelection } from './utils';
8
8
 
9
9
  /**
10
10
  * Selection Preservation Plugin
@@ -58,7 +58,7 @@ export const createSelectionPreservationPlugin = api => () => {
58
58
  if (newState.preservedSelection && tr.docChanged) {
59
59
  newState.preservedSelection = mapPreservedSelection(newState.preservedSelection, tr);
60
60
  }
61
- if (newState.preservedSelection !== pluginState.preservedSelection) {
61
+ if (!compareSelections(newState.preservedSelection, pluginState.preservedSelection)) {
62
62
  if (newState !== null && newState !== void 0 && newState.preservedSelection) {
63
63
  var _api$selection, _api$selection$comman;
64
64
  api === null || api === void 0 ? void 0 : api.core.actions.execute(api === null || api === void 0 ? void 0 : (_api$selection = api.selection) === null || _api$selection === void 0 ? void 0 : (_api$selection$comman = _api$selection.commands) === null || _api$selection$comman === void 0 ? void 0 : _api$selection$comman.setBlockSelection(newState.preservedSelection));
@@ -72,26 +72,25 @@ export const createSelectionPreservationPlugin = api => () => {
72
72
  },
73
73
  appendTransaction(transactions, _oldState, newState) {
74
74
  const pluginState = selectionPreservationPluginKey.getState(newState);
75
- const savedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
76
- if (!savedSel) {
75
+ const preservedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
76
+ const stateSel = newState.selection;
77
+ if (!preservedSel) {
77
78
  return null;
78
79
  }
79
80
 
80
81
  // Auto-stop if user explicitly changes selection or selection is set within a code block
81
- if (hasUserSelectionChange(transactions) || isSelectionWithinCodeBlock(newState.selection)) {
82
- // Auto-stop if user explicitly changes selection
82
+ if (hasUserSelectionChange(transactions) || isSelectionWithinCodeBlock(stateSel)) {
83
83
  return stopPreservingSelection({
84
84
  tr: newState.tr
85
85
  });
86
86
  }
87
- const currSel = newState.selection;
88
- const selectionUnchanged = currSel.from === savedSel.from && currSel.to === savedSel.to;
89
- const selectionInvalid = savedSel.from < 0 || savedSel.to > newState.doc.content.size;
87
+ const selectionUnchanged = stateSel.from === preservedSel.from && stateSel.to === preservedSel.to;
88
+ const selectionInvalid = preservedSel.from < 0 || preservedSel.to > newState.doc.content.size;
90
89
  if (selectionUnchanged || selectionInvalid) {
91
90
  return null;
92
91
  }
93
92
  try {
94
- return newState.tr.setSelection(savedSel);
93
+ return newState.tr.setSelection(preservedSel);
95
94
  } catch (error) {
96
95
  logException(error, {
97
96
  location: 'editor-plugin-block-controls/SelectionPreservationPlugin'
@@ -99,6 +98,15 @@ export const createSelectionPreservationPlugin = api => () => {
99
98
  }
100
99
  return null;
101
100
  },
101
+ view() {
102
+ return {
103
+ update(view, prevState) {
104
+ if (isPreservedSelectionChanged(view.state, prevState)) {
105
+ syncDOMSelection(view.state.selection);
106
+ }
107
+ }
108
+ };
109
+ },
102
110
  props: {
103
111
  handleClick: (view, pos, event) => {
104
112
  const {
@@ -23,4 +23,52 @@ export const isSelectionWithinCodeBlock = ({
23
23
  $to
24
24
  }) => {
25
25
  return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
26
+ };
27
+
28
+ /**
29
+ * Compares two selections for equality based on their from and to positions.
30
+ *
31
+ * @param a The first selection to compare.
32
+ * @param b The second selection to compare.
33
+ * @returns True if both selections are equal, otherwise false.
34
+ */
35
+ export const compareSelections = (a, b) => {
36
+ return (a === null || a === void 0 ? void 0 : a.from) === (b === null || b === void 0 ? void 0 : b.from) && (a === null || a === void 0 ? void 0 : a.to) === (b === null || b === void 0 ? void 0 : b.to);
37
+ };
38
+
39
+ /**
40
+ * Returns true/false indicating whether the preserved selection
41
+ * has changed between the old and new editor states.
42
+ *
43
+ * @param newState The new editor state.
44
+ * @param oldState The old editor state.
45
+ * @returns True if the preserved selection has changed, otherwise false.
46
+ */
47
+ export const isPreservedSelectionChanged = (newState, oldState) => {
48
+ var _selectionPreservatio, _selectionPreservatio2;
49
+ const prev = (_selectionPreservatio = selectionPreservationPluginKey.getState(oldState)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection;
50
+ const curr = (_selectionPreservatio2 = selectionPreservationPluginKey.getState(newState)) === null || _selectionPreservatio2 === void 0 ? void 0 : _selectionPreservatio2.preservedSelection;
51
+ return !!prev && !!curr && !compareSelections(prev, curr);
52
+ };
53
+
54
+ /**
55
+ * Triggers a DOM selection sync by resetting the current native selection range
56
+ * only if it is out of sync with the provided ProseMirror selection state.
57
+ *
58
+ * This is a necessary workaround to ensure the browser's native selection state
59
+ * stays in sync with the preserved selection, particularly after transactions
60
+ * that shift document content.
61
+ *
62
+ * @param selection The current ProseMirror selection state to compare against.
63
+ */
64
+ export const syncDOMSelection = selection => {
65
+ const domSelection = window.getSelection();
66
+ const domRange = domSelection && domSelection.rangeCount === 1 && domSelection.getRangeAt(0).cloneRange();
67
+ const isOutOfSync = domRange && (selection.from !== domRange.startOffset || selection.to !== domRange.endOffset);
68
+ if (isOutOfSync) {
69
+ // Force the DOM selection to refresh, setting it to the same range
70
+ // This will trigger ProseMirror to re-apply its selection logic based on the current state
71
+ domSelection.removeAllRanges();
72
+ domSelection.addRange(domRange);
73
+ }
26
74
  };
@@ -441,6 +441,12 @@ export const DragHandle = ({
441
441
  }
442
442
  }
443
443
  }, [anchorName, nodeType, view.dom]);
444
+ const handleMouseUp = useCallback(e => {
445
+ // Stop propagation so that for drag handles in nested scenarios the click is captured
446
+ // and doesn't propagate to the edge of the element and trigger a node selection
447
+ // on the parent element
448
+ e.stopPropagation();
449
+ }, []);
444
450
  const handleOnClickNew = useCallback(e => {
445
451
  var _api$core;
446
452
  api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
@@ -1073,6 +1079,7 @@ export const DragHandle = ({
1073
1079
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
1074
1080
  ,
1075
1081
  style: !editorExperiment('platform_editor_controls', 'variant1') ? editorExperiment('platform_editor_block_control_optimise_render', true) ? positionStyles : positionStylesOld : {},
1082
+ onMouseUp: expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? handleMouseUp : undefined,
1076
1083
  onClick: expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? handleOnClickNew : handleOnClick,
1077
1084
  onKeyDown: expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? handleKeyDownNew : handleKeyDown
1078
1085
  // eslint-disable-next-line @atlaskit/design-system/no-direct-use-of-web-platform-drag-and-drop
@@ -7,7 +7,7 @@ import { DRAG_HANDLE_SELECTOR } from '@atlaskit/editor-common/styles';
7
7
  import { mapPreservedSelection } from '../utils/selection';
8
8
  import { stopPreservingSelection } from './editor-commands';
9
9
  import { selectionPreservationPluginKey } from './plugin-key';
10
- import { getSelectionPreservationMeta, hasUserSelectionChange, isSelectionWithinCodeBlock } from './utils';
10
+ import { compareSelections, getSelectionPreservationMeta, hasUserSelectionChange, isPreservedSelectionChanged, isSelectionWithinCodeBlock, syncDOMSelection } from './utils';
11
11
 
12
12
  /**
13
13
  * Selection Preservation Plugin
@@ -60,7 +60,7 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
60
60
  if (newState.preservedSelection && tr.docChanged) {
61
61
  newState.preservedSelection = mapPreservedSelection(newState.preservedSelection, tr);
62
62
  }
63
- if (newState.preservedSelection !== pluginState.preservedSelection) {
63
+ if (!compareSelections(newState.preservedSelection, pluginState.preservedSelection)) {
64
64
  if (newState !== null && newState !== void 0 && newState.preservedSelection) {
65
65
  var _api$selection;
66
66
  api === null || api === void 0 || api.core.actions.execute(api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || (_api$selection = _api$selection.commands) === null || _api$selection === void 0 ? void 0 : _api$selection.setBlockSelection(newState.preservedSelection));
@@ -74,26 +74,25 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
74
74
  },
75
75
  appendTransaction: function appendTransaction(transactions, _oldState, newState) {
76
76
  var pluginState = selectionPreservationPluginKey.getState(newState);
77
- var savedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
78
- if (!savedSel) {
77
+ var preservedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
78
+ var stateSel = newState.selection;
79
+ if (!preservedSel) {
79
80
  return null;
80
81
  }
81
82
 
82
83
  // Auto-stop if user explicitly changes selection or selection is set within a code block
83
- if (hasUserSelectionChange(transactions) || isSelectionWithinCodeBlock(newState.selection)) {
84
- // Auto-stop if user explicitly changes selection
84
+ if (hasUserSelectionChange(transactions) || isSelectionWithinCodeBlock(stateSel)) {
85
85
  return stopPreservingSelection({
86
86
  tr: newState.tr
87
87
  });
88
88
  }
89
- var currSel = newState.selection;
90
- var selectionUnchanged = currSel.from === savedSel.from && currSel.to === savedSel.to;
91
- var selectionInvalid = savedSel.from < 0 || savedSel.to > newState.doc.content.size;
89
+ var selectionUnchanged = stateSel.from === preservedSel.from && stateSel.to === preservedSel.to;
90
+ var selectionInvalid = preservedSel.from < 0 || preservedSel.to > newState.doc.content.size;
92
91
  if (selectionUnchanged || selectionInvalid) {
93
92
  return null;
94
93
  }
95
94
  try {
96
- return newState.tr.setSelection(savedSel);
95
+ return newState.tr.setSelection(preservedSel);
97
96
  } catch (error) {
98
97
  logException(error, {
99
98
  location: 'editor-plugin-block-controls/SelectionPreservationPlugin'
@@ -101,6 +100,15 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
101
100
  }
102
101
  return null;
103
102
  },
103
+ view: function view() {
104
+ return {
105
+ update: function update(view, prevState) {
106
+ if (isPreservedSelectionChanged(view.state, prevState)) {
107
+ syncDOMSelection(view.state.selection);
108
+ }
109
+ }
110
+ };
111
+ },
104
112
  props: {
105
113
  handleClick: function handleClick(view, pos, event) {
106
114
  var _ref = selectionPreservationPluginKey.getState(view.state) || {},
@@ -24,4 +24,52 @@ export var isSelectionWithinCodeBlock = function isSelectionWithinCodeBlock(_ref
24
24
  var $from = _ref.$from,
25
25
  $to = _ref.$to;
26
26
  return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
27
+ };
28
+
29
+ /**
30
+ * Compares two selections for equality based on their from and to positions.
31
+ *
32
+ * @param a The first selection to compare.
33
+ * @param b The second selection to compare.
34
+ * @returns True if both selections are equal, otherwise false.
35
+ */
36
+ export var compareSelections = function compareSelections(a, b) {
37
+ return (a === null || a === void 0 ? void 0 : a.from) === (b === null || b === void 0 ? void 0 : b.from) && (a === null || a === void 0 ? void 0 : a.to) === (b === null || b === void 0 ? void 0 : b.to);
38
+ };
39
+
40
+ /**
41
+ * Returns true/false indicating whether the preserved selection
42
+ * has changed between the old and new editor states.
43
+ *
44
+ * @param newState The new editor state.
45
+ * @param oldState The old editor state.
46
+ * @returns True if the preserved selection has changed, otherwise false.
47
+ */
48
+ export var isPreservedSelectionChanged = function isPreservedSelectionChanged(newState, oldState) {
49
+ var _selectionPreservatio, _selectionPreservatio2;
50
+ var prev = (_selectionPreservatio = selectionPreservationPluginKey.getState(oldState)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection;
51
+ var curr = (_selectionPreservatio2 = selectionPreservationPluginKey.getState(newState)) === null || _selectionPreservatio2 === void 0 ? void 0 : _selectionPreservatio2.preservedSelection;
52
+ return !!prev && !!curr && !compareSelections(prev, curr);
53
+ };
54
+
55
+ /**
56
+ * Triggers a DOM selection sync by resetting the current native selection range
57
+ * only if it is out of sync with the provided ProseMirror selection state.
58
+ *
59
+ * This is a necessary workaround to ensure the browser's native selection state
60
+ * stays in sync with the preserved selection, particularly after transactions
61
+ * that shift document content.
62
+ *
63
+ * @param selection The current ProseMirror selection state to compare against.
64
+ */
65
+ export var syncDOMSelection = function syncDOMSelection(selection) {
66
+ var domSelection = window.getSelection();
67
+ var domRange = domSelection && domSelection.rangeCount === 1 && domSelection.getRangeAt(0).cloneRange();
68
+ var isOutOfSync = domRange && (selection.from !== domRange.startOffset || selection.to !== domRange.endOffset);
69
+ if (isOutOfSync) {
70
+ // Force the DOM selection to refresh, setting it to the same range
71
+ // This will trigger ProseMirror to re-apply its selection logic based on the current state
72
+ domSelection.removeAllRanges();
73
+ domSelection.addRange(domRange);
74
+ }
27
75
  };
@@ -460,6 +460,12 @@ export var DragHandle = function DragHandle(_ref3) {
460
460
  }
461
461
  }
462
462
  }, [anchorName, nodeType, view.dom]);
463
+ var handleMouseUp = useCallback(function (e) {
464
+ // Stop propagation so that for drag handles in nested scenarios the click is captured
465
+ // and doesn't propagate to the edge of the element and trigger a node selection
466
+ // on the parent element
467
+ e.stopPropagation();
468
+ }, []);
463
469
  var handleOnClickNew = useCallback(function (e) {
464
470
  var _api$core;
465
471
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref4) {
@@ -1082,6 +1088,7 @@ export var DragHandle = function DragHandle(_ref3) {
1082
1088
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
1083
1089
  ,
1084
1090
  style: !editorExperiment('platform_editor_controls', 'variant1') ? editorExperiment('platform_editor_block_control_optimise_render', true) ? positionStyles : positionStylesOld : {},
1091
+ onMouseUp: expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? handleMouseUp : undefined,
1085
1092
  onClick: expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? handleOnClickNew : handleOnClick,
1086
1093
  onKeyDown: expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? handleKeyDownNew : handleKeyDown
1087
1094
  // eslint-disable-next-line @atlaskit/design-system/no-direct-use-of-web-platform-drag-and-drop
@@ -1,4 +1,4 @@
1
- import type { ReadonlyTransaction, Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
1
+ import type { EditorState, ReadonlyTransaction, Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
2
2
  import type { SelectionPreservationMeta } from './types';
3
3
  /**
4
4
  * Detects if any of the transactions include user-driven selection changes.
@@ -15,3 +15,31 @@ export declare const getSelectionPreservationMeta: (tr: Transaction | ReadonlyTr
15
15
  * @returns True if the selection is within a code block, otherwise false.
16
16
  */
17
17
  export declare const isSelectionWithinCodeBlock: ({ $from, $to }: Selection) => boolean;
18
+ /**
19
+ * Compares two selections for equality based on their from and to positions.
20
+ *
21
+ * @param a The first selection to compare.
22
+ * @param b The second selection to compare.
23
+ * @returns True if both selections are equal, otherwise false.
24
+ */
25
+ export declare const compareSelections: (a?: Selection, b?: Selection) => boolean;
26
+ /**
27
+ * Returns true/false indicating whether the preserved selection
28
+ * has changed between the old and new editor states.
29
+ *
30
+ * @param newState The new editor state.
31
+ * @param oldState The old editor state.
32
+ * @returns True if the preserved selection has changed, otherwise false.
33
+ */
34
+ export declare const isPreservedSelectionChanged: (newState: EditorState, oldState: EditorState) => boolean;
35
+ /**
36
+ * Triggers a DOM selection sync by resetting the current native selection range
37
+ * only if it is out of sync with the provided ProseMirror selection state.
38
+ *
39
+ * This is a necessary workaround to ensure the browser's native selection state
40
+ * stays in sync with the preserved selection, particularly after transactions
41
+ * that shift document content.
42
+ *
43
+ * @param selection The current ProseMirror selection state to compare against.
44
+ */
45
+ export declare const syncDOMSelection: (selection: Selection) => void;
@@ -1,4 +1,4 @@
1
- import type { ReadonlyTransaction, Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
1
+ import type { EditorState, ReadonlyTransaction, Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
2
2
  import type { SelectionPreservationMeta } from './types';
3
3
  /**
4
4
  * Detects if any of the transactions include user-driven selection changes.
@@ -15,3 +15,31 @@ export declare const getSelectionPreservationMeta: (tr: Transaction | ReadonlyTr
15
15
  * @returns True if the selection is within a code block, otherwise false.
16
16
  */
17
17
  export declare const isSelectionWithinCodeBlock: ({ $from, $to }: Selection) => boolean;
18
+ /**
19
+ * Compares two selections for equality based on their from and to positions.
20
+ *
21
+ * @param a The first selection to compare.
22
+ * @param b The second selection to compare.
23
+ * @returns True if both selections are equal, otherwise false.
24
+ */
25
+ export declare const compareSelections: (a?: Selection, b?: Selection) => boolean;
26
+ /**
27
+ * Returns true/false indicating whether the preserved selection
28
+ * has changed between the old and new editor states.
29
+ *
30
+ * @param newState The new editor state.
31
+ * @param oldState The old editor state.
32
+ * @returns True if the preserved selection has changed, otherwise false.
33
+ */
34
+ export declare const isPreservedSelectionChanged: (newState: EditorState, oldState: EditorState) => boolean;
35
+ /**
36
+ * Triggers a DOM selection sync by resetting the current native selection range
37
+ * only if it is out of sync with the provided ProseMirror selection state.
38
+ *
39
+ * This is a necessary workaround to ensure the browser's native selection state
40
+ * stays in sync with the preserved selection, particularly after transactions
41
+ * that shift document content.
42
+ *
43
+ * @param selection The current ProseMirror selection state to compare against.
44
+ */
45
+ export declare const syncDOMSelection: (selection: Selection) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-controls",
3
- "version": "8.0.5",
3
+ "version": "8.0.7",
4
4
  "description": "Block controls plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -37,7 +37,7 @@
37
37
  "@atlaskit/editor-plugin-interaction": "^12.0.0",
38
38
  "@atlaskit/editor-plugin-limited-mode": "^4.0.0",
39
39
  "@atlaskit/editor-plugin-metrics": "^8.0.0",
40
- "@atlaskit/editor-plugin-quick-insert": "^7.0.0",
40
+ "@atlaskit/editor-plugin-quick-insert": "^7.1.0",
41
41
  "@atlaskit/editor-plugin-selection": "^7.0.0",
42
42
  "@atlaskit/editor-plugin-toolbar": "^4.0.0",
43
43
  "@atlaskit/editor-plugin-type-ahead": "^7.0.0",
@@ -54,9 +54,9 @@
54
54
  "@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "^3.2.0",
55
55
  "@atlaskit/primitives": "^17.0.0",
56
56
  "@atlaskit/theme": "^21.0.0",
57
- "@atlaskit/tmp-editor-statsig": "^16.11.0",
57
+ "@atlaskit/tmp-editor-statsig": "^16.13.0",
58
58
  "@atlaskit/tokens": "^9.1.0",
59
- "@atlaskit/tooltip": "^20.12.0",
59
+ "@atlaskit/tooltip": "^20.13.0",
60
60
  "@babel/runtime": "^7.0.0",
61
61
  "@emotion/react": "^11.7.1",
62
62
  "@popperjs/core": "^2.11.8",
@@ -1,21 +0,0 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- Object.defineProperty(exports, "__esModule", {
5
- value: true
6
- });
7
- exports.DragHandleMenu = void 0;
8
- var _react = _interopRequireDefault(require("react"));
9
- var _hooks = require("@atlaskit/editor-common/hooks");
10
- var DragHandleMenu = exports.DragHandleMenu = function DragHandleMenu(_ref) {
11
- var api = _ref.api;
12
- var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['blockControls'], function (states) {
13
- var _states$blockControls;
14
- return {
15
- isMenuOpen: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isMenuOpen
16
- };
17
- }),
18
- isMenuOpen = _useSharedPluginState.isMenuOpen;
19
- // eslint-disable-next-line @atlassian/i18n/no-literal-string-in-jsx
20
- return isMenuOpen ? /*#__PURE__*/_react.default.createElement("div", null, "menu") : null;
21
- };
@@ -1,16 +0,0 @@
1
- import React from 'react';
2
- import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
- export const DragHandleMenu = ({
4
- api
5
- }) => {
6
- const {
7
- isMenuOpen
8
- } = useSharedPluginStateWithSelector(api, ['blockControls'], states => {
9
- var _states$blockControls;
10
- return {
11
- isMenuOpen: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isMenuOpen
12
- };
13
- });
14
- // eslint-disable-next-line @atlassian/i18n/no-literal-string-in-jsx
15
- return isMenuOpen ? /*#__PURE__*/React.createElement("div", null, "menu") : null;
16
- };
@@ -1,14 +0,0 @@
1
- import React from 'react';
2
- import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
- export var DragHandleMenu = function DragHandleMenu(_ref) {
4
- var api = _ref.api;
5
- var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['blockControls'], function (states) {
6
- var _states$blockControls;
7
- return {
8
- isMenuOpen: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isMenuOpen
9
- };
10
- }),
11
- isMenuOpen = _useSharedPluginState.isMenuOpen;
12
- // eslint-disable-next-line @atlassian/i18n/no-literal-string-in-jsx
13
- return isMenuOpen ? /*#__PURE__*/React.createElement("div", null, "menu") : null;
14
- };
@@ -1,6 +0,0 @@
1
- import React from 'react';
2
- import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
- import type { BlockControlsPlugin } from '../blockControlsPluginType';
4
- export declare const DragHandleMenu: ({ api, }: {
5
- api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
6
- }) => React.JSX.Element | null;
@@ -1,6 +0,0 @@
1
- import React from 'react';
2
- import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
- import type { BlockControlsPlugin } from '../blockControlsPluginType';
4
- export declare const DragHandleMenu: ({ api, }: {
5
- api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
6
- }) => React.JSX.Element | null;