@atlaskit/editor-plugin-block-controls 7.13.2 → 7.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 7.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`5f21c5a85f65a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/5f21c5a85f65a) -
8
+ ED-29714 fix some alignment issues
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies
13
+
14
+ ## 7.13.3
15
+
16
+ ### Patch Changes
17
+
18
+ - [`7c295bfea1292`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/7c295bfea1292) -
19
+ EDITOR-3380 Use preserved selection when deleting selected range
20
+ - Updated dependencies
21
+
3
22
  ## 7.13.2
4
23
 
5
24
  ### Patch Changes
@@ -49,7 +49,7 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
49
49
  if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true)) {
50
50
  pmPlugins.push({
51
51
  name: 'blockControlsSelectionPreservationPlugin',
52
- plugin: _pmPlugin2.createSelectionPreservationPlugin
52
+ plugin: (0, _pmPlugin2.createSelectionPreservationPlugin)(api)
53
53
  });
54
54
  }
55
55
 
@@ -12,6 +12,7 @@ var _reactDom = _interopRequireDefault(require("react-dom"));
12
12
  var _uuid = _interopRequireDefault(require("uuid"));
13
13
  var _view = require("@atlaskit/editor-prosemirror/view");
14
14
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
15
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
15
16
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
16
17
  var _dragHandle = require("../ui/drag-handle");
17
18
  var _decorationsCommon = require("./decorations-common");
@@ -32,6 +33,50 @@ var findHandleDec = exports.findHandleDec = function findHandleDec(decorations,
32
33
  return spec.type === _decorationsCommon.TYPE_HANDLE_DEC;
33
34
  });
34
35
  };
36
+ /**
37
+ * Fix for widget positioning to ensure it is not placed into a previous mark's DOM structure.
38
+ * A ProseMirror widget can appear in the wrong DOM position, specifically being added
39
+ * to a previous mark instead of its intended location, which leads to various rendering issues.
40
+ * For example, when nodeBefore has an alignment mark but the current node doesn't, ProseMirror may
41
+ * incorrectly render the widget inside the previous node's alignment mark wrapper instead.
42
+ */
43
+ var fixWidgetSide = function fixWidgetSide($pos) {
44
+ var defaultSide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
45
+ var alignmentMark = $pos.doc.type.schema.marks.alignment;
46
+ var indentationMark = $pos.doc.type.schema.marks.indentation;
47
+
48
+ // Only apply fix for alignment and indent marks
49
+ if (!alignmentMark && !indentationMark) {
50
+ return defaultSide;
51
+ }
52
+ if ($pos.nodeBefore && $pos.nodeAfter) {
53
+ var _$pos$nodeBefore, _$pos$nodeAfter;
54
+ var beforeMarks = ((_$pos$nodeBefore = $pos.nodeBefore) === null || _$pos$nodeBefore === void 0 ? void 0 : _$pos$nodeBefore.marks.filter(function (mark) {
55
+ return mark.type === alignmentMark || mark.type === indentationMark;
56
+ })) || [];
57
+ var afterMarks = ((_$pos$nodeAfter = $pos.nodeAfter) === null || _$pos$nodeAfter === void 0 ? void 0 : _$pos$nodeAfter.marks.filter(function (mark) {
58
+ return mark.type === alignmentMark || mark.type === indentationMark;
59
+ })) || [];
60
+ if (beforeMarks.length === 0) {
61
+ return defaultSide;
62
+ } else if (afterMarks.length === 0) {
63
+ return 0;
64
+ }
65
+
66
+ // Check if previous node has marks that current node doesn't have
67
+ var hasMissingMark = beforeMarks.some(function (mark) {
68
+ return !afterMarks.some(function (nextMark) {
69
+ return nextMark.eq(mark);
70
+ });
71
+ });
72
+
73
+ // if we have missing mark, we set side to 0 to render widget outside previous mark DOM
74
+ if (hasMissingMark) {
75
+ return 0;
76
+ }
77
+ }
78
+ return defaultSide;
79
+ };
35
80
  var dragHandleDecoration = exports.dragHandleDecoration = function dragHandleDecoration(_ref) {
36
81
  var api = _ref.api,
37
82
  formatMessage = _ref.formatMessage,
@@ -50,8 +95,10 @@ var dragHandleDecoration = exports.dragHandleDecoration = function dragHandleDec
50
95
  var unbind;
51
96
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
52
97
  var key = (0, _uuid.default)();
98
+ var $pos = editorState.doc.resolve(pos);
99
+ var side = (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && (0, _platformFeatureFlags.fg)('platform_editor_native_anchor_patch_1') ? fixWidgetSide($pos) : -1;
53
100
  var widgetSpec = (0, _experiments.editorExperiment)('platform_editor_breakout_resizing', true) ? {
54
- side: -1,
101
+ side: side,
55
102
  type: _decorationsCommon.TYPE_HANDLE_DEC,
56
103
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
57
104
  testid: "".concat(_decorationsCommon.TYPE_HANDLE_DEC, "-").concat((0, _uuid.default)()),
@@ -68,7 +115,7 @@ var dragHandleDecoration = exports.dragHandleDecoration = function dragHandleDec
68
115
  }
69
116
  }
70
117
  } : {
71
- side: -1,
118
+ side: side,
72
119
  type: _decorationsCommon.TYPE_HANDLE_DEC,
73
120
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
74
121
  testid: "".concat(_decorationsCommon.TYPE_HANDLE_DEC, "-").concat((0, _uuid.default)()),
@@ -91,13 +138,14 @@ var dragHandleDecoration = exports.dragHandleDecoration = function dragHandleDec
91
138
  try {
92
139
  return getPosUnsafe();
93
140
  } catch (e) {
141
+ // Ignore errors from getPosUnsafe
94
142
  return undefined;
95
143
  }
96
144
  };
97
145
  var newPos = getPos();
98
146
  if (typeof newPos === 'number') {
99
- var $pos = view.state.doc.resolve(newPos);
100
- isTopLevelNode = ($pos === null || $pos === void 0 ? void 0 : $pos.parent.type.name) === 'doc';
147
+ var _$pos = view.state.doc.resolve(newPos);
148
+ isTopLevelNode = (_$pos === null || _$pos === void 0 ? void 0 : _$pos.parent.type.name) === 'doc';
101
149
  }
102
150
  /*
103
151
  * We disable mouseover event to fix flickering issue on hover
@@ -42,55 +42,86 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
42
42
  * when modal dialogs are active. In these states, any selection changes are from ProseMirror's
43
43
  * internal behavior (not user input) and should be prevented. Do not use during normal editing.
44
44
  */
45
- var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugin = function createSelectionPreservationPlugin() {
46
- return new _safePlugin.SafePlugin({
47
- key: _pluginKey.selectionPreservationPluginKey,
48
- state: {
49
- init: function init() {
50
- return {
51
- preservedSelection: undefined
52
- };
45
+ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugin = function createSelectionPreservationPlugin(api) {
46
+ return function () {
47
+ return new _safePlugin.SafePlugin({
48
+ key: _pluginKey.selectionPreservationPluginKey,
49
+ state: {
50
+ init: function init() {
51
+ return {
52
+ preservedSelection: undefined
53
+ };
54
+ },
55
+ apply: function apply(tr, pluginState) {
56
+ var meta = (0, _utils.getSelectionPreservationMeta)(tr);
57
+ var newState = _objectSpread({}, pluginState);
58
+ if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
59
+ newState.preservedSelection = tr.selection;
60
+ } else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
61
+ newState.preservedSelection = undefined;
62
+ }
63
+ if (newState.preservedSelection && tr.docChanged) {
64
+ newState.preservedSelection = (0, _selection.mapPreservedSelection)(newState.preservedSelection, tr);
65
+ }
66
+ return newState;
67
+ }
53
68
  },
54
- apply: function apply(tr, pluginState) {
55
- var meta = (0, _utils.getSelectionPreservationMeta)(tr);
56
- var newState = _objectSpread({}, pluginState);
57
- if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
58
- newState.preservedSelection = tr.selection;
59
- } else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
60
- newState.preservedSelection = undefined;
69
+ appendTransaction: function appendTransaction(transactions, _oldState, newState) {
70
+ var pluginState = _pluginKey.selectionPreservationPluginKey.getState(newState);
71
+ var savedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
72
+ if (!savedSel) {
73
+ return null;
61
74
  }
62
- if (newState.preservedSelection && tr.docChanged) {
63
- newState.preservedSelection = (0, _selection.mapPreservedSelection)(newState.preservedSelection, tr);
75
+ if ((0, _utils.hasUserSelectionChange)(transactions)) {
76
+ // Auto-stop if user explicitly changes selection
77
+ return (0, _editorCommands.stopPreservingSelection)({
78
+ tr: newState.tr
79
+ });
80
+ }
81
+ var currSel = newState.selection;
82
+ var selectionUnchanged = currSel.from === savedSel.from && currSel.to === savedSel.to;
83
+ var selectionInvalid = savedSel.from < 0 || savedSel.to > newState.doc.content.size;
84
+ if (selectionUnchanged || selectionInvalid) {
85
+ return null;
86
+ }
87
+ try {
88
+ return newState.tr.setSelection(savedSel);
89
+ } catch (error) {
90
+ (0, _monitoring.logException)(error, {
91
+ location: 'editor-plugin-block-controls/SelectionPreservationPlugin'
92
+ });
64
93
  }
65
- return newState;
66
- }
67
- },
68
- appendTransaction: function appendTransaction(transactions, _oldState, newState) {
69
- var pluginState = _pluginKey.selectionPreservationPluginKey.getState(newState);
70
- var savedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
71
- if (!savedSel) {
72
- return null;
73
- }
74
- if ((0, _utils.hasUserSelectionChange)(transactions)) {
75
- // Auto-stop if user explicitly changes selection
76
- return (0, _editorCommands.stopPreservingSelection)({
77
- tr: newState.tr
78
- });
79
- }
80
- var currSel = newState.selection;
81
- var selectionUnchanged = currSel.from === savedSel.from && currSel.to === savedSel.to;
82
- var selectionInvalid = savedSel.from < 0 || savedSel.to > newState.doc.content.size;
83
- if (selectionUnchanged || selectionInvalid) {
84
94
  return null;
95
+ },
96
+ props: {
97
+ handleKeyDown: function handleKeyDown(view, event) {
98
+ var _api$userIntent;
99
+ var _ref = _pluginKey.selectionPreservationPluginKey.getState(view.state) || {},
100
+ preservedSelection = _ref.preservedSelection;
101
+
102
+ // If there is no current preserved selection, do nothing
103
+ if (!preservedSelection) {
104
+ return false;
105
+ }
106
+
107
+ // While preserving selection, if user presses delete/backspace, prevent event from being
108
+ // handled by ProseMirror natively so that we can apply custom delete logic in block menu
109
+ if (event.key === 'Backspace' || event.key === 'Delete') {
110
+ return true;
111
+ }
112
+ var blockMenuOpen = (api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent) === 'blockMenuOpen';
113
+
114
+ // When block menu isn't open and user presses any key, stop preserving selection
115
+ if (!blockMenuOpen) {
116
+ var tr = view.state.tr;
117
+ (0, _editorCommands.stopPreservingSelection)({
118
+ tr: tr
119
+ });
120
+ view.dispatch(tr);
121
+ return false;
122
+ }
123
+ }
85
124
  }
86
- try {
87
- return newState.tr.setSelection(savedSel);
88
- } catch (error) {
89
- (0, _monitoring.logException)(error, {
90
- location: 'editor-plugin-block-controls/SelectionPreservationPlugin'
91
- });
92
- }
93
- return null;
94
- }
95
- });
125
+ });
126
+ };
96
127
  };
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.getActiveBlockMarks = void 0;
7
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
8
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
7
9
  /**
8
10
  * Returns list of block marks on schema that widgets are allowed to render inside
9
11
  * Currently
@@ -21,9 +23,36 @@ exports.getActiveBlockMarks = void 0;
21
23
  var getActiveBlockMarks = exports.getActiveBlockMarks = function getActiveBlockMarks(state, pos) {
22
24
  var alignment = state.schema.marks.alignment;
23
25
  var resolvedPos = state.doc.resolve(pos);
26
+
24
27
  // find all active marks at the position
25
28
  var marks = resolvedPos.marks();
26
- return marks.filter(function (mark) {
29
+ var supportedMarks = marks.filter(function (mark) {
27
30
  return mark.type === alignment;
28
31
  });
32
+
33
+ /**
34
+ * Fix for widget positioning at alignment mark boundaries.
35
+ * When the previous node has alignment but the next node doesn't, we need to prevent
36
+ * the widget from inheriting alignment marks. This ensures the widget is positioned
37
+ * correctly at the boundary rather than being absorbed into the alignment wrapper.
38
+ */
39
+ if (supportedMarks.length > 0 && (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && (0, _platformFeatureFlags.fg)('platform_editor_native_anchor_patch_1')) {
40
+ var _resolvedPos$nodeAfte;
41
+ var nextNodeMarks = ((_resolvedPos$nodeAfte = resolvedPos.nodeAfter) === null || _resolvedPos$nodeAfte === void 0 ? void 0 : _resolvedPos$nodeAfte.marks.filter(function (mark) {
42
+ return mark.type === alignment;
43
+ })) || [];
44
+
45
+ // Compare alignment values to ensure they are the same
46
+ var alignmentValuesMatch = supportedMarks.length === nextNodeMarks.length && supportedMarks.some(function (mark) {
47
+ return nextNodeMarks.some(function (nextMark) {
48
+ return nextMark.eq(mark);
49
+ });
50
+ });
51
+
52
+ // previous node has alignment but next node does not have alignment or alignment values differ
53
+ if (nextNodeMarks.length === 0 || !alignmentValuesMatch) {
54
+ return [];
55
+ }
56
+ }
57
+ return supportedMarks;
29
58
  };
@@ -17,7 +17,6 @@ var _browser = require("@atlaskit/editor-common/browser");
17
17
  var _hooks = require("@atlaskit/editor-common/hooks");
18
18
  var _keymaps = require("@atlaskit/editor-common/keymaps");
19
19
  var _messages = require("@atlaskit/editor-common/messages");
20
- var _selection = require("@atlaskit/editor-common/selection");
21
20
  var _styles = require("@atlaskit/editor-common/styles");
22
21
  var _useSharedPluginStateSelector = require("@atlaskit/editor-common/use-shared-plugin-state-selector");
23
22
  var _state = require("@atlaskit/editor-prosemirror/state");
@@ -39,7 +38,7 @@ var _pluginKey = require("../pm-plugins/selection-preservation/plugin-key");
39
38
  var _analytics2 = require("../pm-plugins/utils/analytics");
40
39
  var _dragHandlePositions = require("../pm-plugins/utils/drag-handle-positions");
41
40
  var _getSelection = require("../pm-plugins/utils/getSelection");
42
- var _selection2 = require("../pm-plugins/utils/selection");
41
+ var _selection = require("../pm-plugins/utils/selection");
43
42
  var _consts2 = require("./consts");
44
43
  var _dragPreview = require("./drag-preview");
45
44
  var _anchorName = require("./utils/anchor-name");
@@ -482,8 +481,8 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
482
481
  tr = (0, _getSelection.selectNode)(tr, startPos, nodeType, api);
483
482
  } else if (isTopLevelNode && $anchor.depth <= _consts2.DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH && e.shiftKey && (0, _platformFeatureFlags.fg)('platform_editor_elements_dnd_shift_click_select')) {
484
483
  var _api$blockControls3;
485
- var alignAnchorHeadToSel = (0, _selection2.alignAnchorHeadInDirectionOfPos)(tr.selection, startPos);
486
- var selectionWithExpandedHead = (0, _selection2.expandSelectionHeadToNodeAtPos)(alignAnchorHeadToSel, startPos);
484
+ var alignAnchorHeadToSel = (0, _selection.alignAnchorHeadInDirectionOfPos)(tr.selection, startPos);
485
+ var selectionWithExpandedHead = (0, _selection.expandSelectionHeadToNodeAtPos)(alignAnchorHeadToSel, startPos);
487
486
  tr.setSelection(selectionWithExpandedHead);
488
487
  api === null || api === void 0 || (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 || _api$blockControls3.commands.setMultiSelectPositions()({
489
488
  tr: tr
@@ -574,20 +573,6 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
574
573
  });
575
574
  return tr;
576
575
  });
577
- } else if (e.key === 'Backspace' || e.key === 'Delete') {
578
- e.preventDefault();
579
- e.stopPropagation();
580
- api === null || api === void 0 || api.core.actions.execute(function (_ref7) {
581
- var _api$blockControls5;
582
- var tr = _ref7.tr;
583
- (0, _selection.deleteSelectedRange)(tr);
584
- api === null || api === void 0 || (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 || _api$blockControls5.commands.toggleBlockMenu({
585
- closeMenu: true
586
- })({
587
- tr: tr
588
- });
589
- return tr;
590
- });
591
576
  } else if (![e.altKey, e.ctrlKey, e.shiftKey].some(function (pressed) {
592
577
  return pressed;
593
578
  })) {
@@ -609,21 +594,21 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
609
594
  start: start
610
595
  };
611
596
  },
612
- onGenerateDragPreview: function onGenerateDragPreview(_ref8) {
597
+ onGenerateDragPreview: function onGenerateDragPreview(_ref7) {
613
598
  var _api$blockControls$sh2;
614
- var nativeSetDragImage = _ref8.nativeSetDragImage;
599
+ var nativeSetDragImage = _ref7.nativeSetDragImage;
615
600
  if (isMultiSelect) {
616
601
  var _api$core6;
617
- api === null || api === void 0 || (_api$core6 = api.core) === null || _api$core6 === void 0 || _api$core6.actions.execute(function (_ref9) {
618
- var tr = _ref9.tr;
602
+ api === null || api === void 0 || (_api$core6 = api.core) === null || _api$core6 === void 0 || _api$core6.actions.execute(function (_ref8) {
603
+ var tr = _ref8.tr;
619
604
  var handlePos = getPos();
620
605
  if (typeof handlePos !== 'number') {
621
606
  return tr;
622
607
  }
623
608
  var newHandlePosCheck = (0, _getSelection.isHandleCorrelatedToSelection)(view.state, tr.selection, handlePos);
624
609
  if (!tr.selection.empty && newHandlePosCheck) {
625
- var _api$blockControls6;
626
- api === null || api === void 0 || (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 || _api$blockControls6.commands.setMultiSelectPositions()({
610
+ var _api$blockControls5;
611
+ api === null || api === void 0 || (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 || _api$blockControls5.commands.setMultiSelectPositions()({
627
612
  tr: tr
628
613
  });
629
614
  } else if ((0, _platformFeatureFlags.fg)('platform_editor_elements_dnd_select_node_on_drag')) {
@@ -693,8 +678,8 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
693
678
  };
694
679
  }
695
680
  },
696
- render: function render(_ref0) {
697
- var container = _ref0.container;
681
+ render: function render(_ref9) {
682
+ var container = _ref9.container;
698
683
  var dom = view.dom.querySelector("[".concat((0, _domAttrName.getAnchorAttrName)(), "=\"").concat(anchorName, "\"]"));
699
684
  if (!dom) {
700
685
  return;
@@ -730,9 +715,9 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
730
715
  if (start === undefined) {
731
716
  return;
732
717
  }
733
- api === null || api === void 0 || (_api$core7 = api.core) === null || _api$core7 === void 0 || _api$core7.actions.execute(function (_ref1) {
734
- var _api$blockControls$sh3, _api$blockControls7, _api$analytics3;
735
- var tr = _ref1.tr;
718
+ api === null || api === void 0 || (_api$core7 = api.core) === null || _api$core7 === void 0 || _api$core7.actions.execute(function (_ref0) {
719
+ var _api$blockControls$sh3, _api$blockControls6, _api$analytics3;
720
+ var tr = _ref0.tr;
736
721
  var nodeTypes, hasSelectedMultipleNodes;
737
722
  var resolvedMovingNode = tr.doc.resolve(start);
738
723
  var maybeNode = resolvedMovingNode.nodeAfter;
@@ -745,7 +730,7 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
745
730
  nodeTypes = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name;
746
731
  hasSelectedMultipleNodes = false;
747
732
  }
748
- api === null || api === void 0 || (_api$blockControls7 = api.blockControls) === null || _api$blockControls7 === void 0 || _api$blockControls7.commands.setNodeDragged(getPos, anchorName, nodeType)({
733
+ api === null || api === void 0 || (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 || _api$blockControls6.commands.setNodeDragged(getPos, anchorName, nodeType)({
749
734
  tr: tr
750
735
  });
751
736
  tr.setMeta('scrollIntoView', false);
@@ -1128,15 +1113,15 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
1128
1113
  var render = isTooltip ? buttonWithTooltip() : renderButton();
1129
1114
  return (0, _experiments.editorExperiment)('platform_editor_controls', 'variant1') ? stickyRender : render;
1130
1115
  };
1131
- var DragHandleWithVisibility = exports.DragHandleWithVisibility = function DragHandleWithVisibility(_ref10) {
1132
- var view = _ref10.view,
1133
- api = _ref10.api,
1134
- formatMessage = _ref10.formatMessage,
1135
- getPos = _ref10.getPos,
1136
- anchorName = _ref10.anchorName,
1137
- nodeType = _ref10.nodeType,
1138
- handleOptions = _ref10.handleOptions,
1139
- anchorRectCache = _ref10.anchorRectCache;
1116
+ var DragHandleWithVisibility = exports.DragHandleWithVisibility = function DragHandleWithVisibility(_ref1) {
1117
+ var view = _ref1.view,
1118
+ api = _ref1.api,
1119
+ formatMessage = _ref1.formatMessage,
1120
+ getPos = _ref1.getPos,
1121
+ anchorName = _ref1.anchorName,
1122
+ nodeType = _ref1.nodeType,
1123
+ handleOptions = _ref1.handleOptions,
1124
+ anchorRectCache = _ref1.anchorRectCache;
1140
1125
  return (0, _react2.jsx)(_visibilityContainer.VisibilityContainer, {
1141
1126
  api: api
1142
1127
  }, (0, _react2.jsx)(DragHandle, {
@@ -38,7 +38,7 @@ export const blockControlsPlugin = ({
38
38
  if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
39
39
  pmPlugins.push({
40
40
  name: 'blockControlsSelectionPreservationPlugin',
41
- plugin: createSelectionPreservationPlugin
41
+ plugin: createSelectionPreservationPlugin(api)
42
42
  });
43
43
  }
44
44
 
@@ -5,6 +5,7 @@ import ReactDOM from 'react-dom';
5
5
  import uuid from 'uuid';
6
6
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
7
7
  import { fg } from '@atlaskit/platform-feature-flags';
8
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
8
9
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
9
10
  import { DragHandle, DragHandleWithVisibility } from '../ui/drag-handle';
10
11
  import { TYPE_HANDLE_DEC, TYPE_NODE_DEC, unmountDecorations } from './decorations-common';
@@ -22,6 +23,43 @@ export const emptyParagraphNodeDecorations = () => {
22
23
  export const findHandleDec = (decorations, from, to) => {
23
24
  return decorations.find(from, to, spec => spec.type === TYPE_HANDLE_DEC);
24
25
  };
26
+ /**
27
+ * Fix for widget positioning to ensure it is not placed into a previous mark's DOM structure.
28
+ * A ProseMirror widget can appear in the wrong DOM position, specifically being added
29
+ * to a previous mark instead of its intended location, which leads to various rendering issues.
30
+ * For example, when nodeBefore has an alignment mark but the current node doesn't, ProseMirror may
31
+ * incorrectly render the widget inside the previous node's alignment mark wrapper instead.
32
+ */
33
+ const fixWidgetSide = ($pos, defaultSide = -1) => {
34
+ const alignmentMark = $pos.doc.type.schema.marks.alignment;
35
+ const indentationMark = $pos.doc.type.schema.marks.indentation;
36
+
37
+ // Only apply fix for alignment and indent marks
38
+ if (!alignmentMark && !indentationMark) {
39
+ return defaultSide;
40
+ }
41
+ if ($pos.nodeBefore && $pos.nodeAfter) {
42
+ var _$pos$nodeBefore, _$pos$nodeAfter;
43
+ const beforeMarks = ((_$pos$nodeBefore = $pos.nodeBefore) === null || _$pos$nodeBefore === void 0 ? void 0 : _$pos$nodeBefore.marks.filter(mark => mark.type === alignmentMark || mark.type === indentationMark)) || [];
44
+ const afterMarks = ((_$pos$nodeAfter = $pos.nodeAfter) === null || _$pos$nodeAfter === void 0 ? void 0 : _$pos$nodeAfter.marks.filter(mark => mark.type === alignmentMark || mark.type === indentationMark)) || [];
45
+ if (beforeMarks.length === 0) {
46
+ return defaultSide;
47
+ } else if (afterMarks.length === 0) {
48
+ return 0;
49
+ }
50
+
51
+ // Check if previous node has marks that current node doesn't have
52
+ const hasMissingMark = beforeMarks.some(mark => {
53
+ return !afterMarks.some(nextMark => nextMark.eq(mark));
54
+ });
55
+
56
+ // if we have missing mark, we set side to 0 to render widget outside previous mark DOM
57
+ if (hasMissingMark) {
58
+ return 0;
59
+ }
60
+ }
61
+ return defaultSide;
62
+ };
25
63
  export const dragHandleDecoration = ({
26
64
  api,
27
65
  formatMessage,
@@ -41,8 +79,10 @@ export const dragHandleDecoration = ({
41
79
  let unbind;
42
80
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
43
81
  const key = uuid();
82
+ const $pos = editorState.doc.resolve(pos);
83
+ const side = expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && fg('platform_editor_native_anchor_patch_1') ? fixWidgetSide($pos) : -1;
44
84
  const widgetSpec = editorExperiment('platform_editor_breakout_resizing', true) ? {
45
- side: -1,
85
+ side,
46
86
  type: TYPE_HANDLE_DEC,
47
87
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
48
88
  testid: `${TYPE_HANDLE_DEC}-${uuid()}`,
@@ -59,7 +99,7 @@ export const dragHandleDecoration = ({
59
99
  }
60
100
  }
61
101
  } : {
62
- side: -1,
102
+ side,
63
103
  type: TYPE_HANDLE_DEC,
64
104
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
65
105
  testid: `${TYPE_HANDLE_DEC}-${uuid()}`,
@@ -82,6 +122,7 @@ export const dragHandleDecoration = ({
82
122
  try {
83
123
  return getPosUnsafe();
84
124
  } catch (e) {
125
+ // Ignore errors from getPosUnsafe
85
126
  return undefined;
86
127
  }
87
128
  };
@@ -33,7 +33,7 @@ import { getSelectionPreservationMeta, hasUserSelectionChange } from './utils';
33
33
  * when modal dialogs are active. In these states, any selection changes are from ProseMirror's
34
34
  * internal behavior (not user input) and should be prevented. Do not use during normal editing.
35
35
  */
36
- export const createSelectionPreservationPlugin = () => {
36
+ export const createSelectionPreservationPlugin = api => () => {
37
37
  return new SafePlugin({
38
38
  key: selectionPreservationPluginKey,
39
39
  state: {
@@ -84,6 +84,36 @@ export const createSelectionPreservationPlugin = () => {
84
84
  });
85
85
  }
86
86
  return null;
87
+ },
88
+ props: {
89
+ handleKeyDown: (view, event) => {
90
+ var _api$userIntent, _api$userIntent$share;
91
+ const {
92
+ preservedSelection
93
+ } = selectionPreservationPluginKey.getState(view.state) || {};
94
+
95
+ // If there is no current preserved selection, do nothing
96
+ if (!preservedSelection) {
97
+ return false;
98
+ }
99
+
100
+ // While preserving selection, if user presses delete/backspace, prevent event from being
101
+ // handled by ProseMirror natively so that we can apply custom delete logic in block menu
102
+ if (event.key === 'Backspace' || event.key === 'Delete') {
103
+ return true;
104
+ }
105
+ const blockMenuOpen = (api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : (_api$userIntent$share = _api$userIntent.sharedState.currentState()) === null || _api$userIntent$share === void 0 ? void 0 : _api$userIntent$share.currentUserIntent) === 'blockMenuOpen';
106
+
107
+ // When block menu isn't open and user presses any key, stop preserving selection
108
+ if (!blockMenuOpen) {
109
+ const tr = view.state.tr;
110
+ stopPreservingSelection({
111
+ tr
112
+ });
113
+ view.dispatch(tr);
114
+ return false;
115
+ }
116
+ }
87
117
  }
88
118
  });
89
119
  };
@@ -1,3 +1,6 @@
1
+ import { fg } from '@atlaskit/platform-feature-flags';
2
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
3
+
1
4
  /**
2
5
  * Returns list of block marks on schema that widgets are allowed to render inside
3
6
  * Currently
@@ -17,7 +20,28 @@ export const getActiveBlockMarks = (state, pos) => {
17
20
  alignment
18
21
  } = state.schema.marks;
19
22
  const resolvedPos = state.doc.resolve(pos);
23
+
20
24
  // find all active marks at the position
21
25
  const marks = resolvedPos.marks();
22
- return marks.filter(mark => mark.type === alignment);
26
+ const supportedMarks = marks.filter(mark => mark.type === alignment);
27
+
28
+ /**
29
+ * Fix for widget positioning at alignment mark boundaries.
30
+ * When the previous node has alignment but the next node doesn't, we need to prevent
31
+ * the widget from inheriting alignment marks. This ensures the widget is positioned
32
+ * correctly at the boundary rather than being absorbed into the alignment wrapper.
33
+ */
34
+ if (supportedMarks.length > 0 && expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && fg('platform_editor_native_anchor_patch_1')) {
35
+ var _resolvedPos$nodeAfte;
36
+ const nextNodeMarks = ((_resolvedPos$nodeAfte = resolvedPos.nodeAfter) === null || _resolvedPos$nodeAfte === void 0 ? void 0 : _resolvedPos$nodeAfte.marks.filter(mark => mark.type === alignment)) || [];
37
+
38
+ // Compare alignment values to ensure they are the same
39
+ const alignmentValuesMatch = supportedMarks.length === nextNodeMarks.length && supportedMarks.some(mark => nextNodeMarks.some(nextMark => nextMark.eq(mark)));
40
+
41
+ // previous node has alignment but next node does not have alignment or alignment values differ
42
+ if (nextNodeMarks.length === 0 || !alignmentValuesMatch) {
43
+ return [];
44
+ }
45
+ }
46
+ return supportedMarks;
23
47
  };
@@ -14,7 +14,6 @@ import { browser as browserLegacy, getBrowserInfo } from '@atlaskit/editor-commo
14
14
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
15
15
  import { dragToMoveDown, dragToMoveLeft, dragToMoveRight, dragToMoveUp, getAriaKeyshortcuts, TooltipContentWithMultipleShortcuts } from '@atlaskit/editor-common/keymaps';
16
16
  import { blockControlsMessages } from '@atlaskit/editor-common/messages';
17
- import { deleteSelectedRange } from '@atlaskit/editor-common/selection';
18
17
  import { DRAG_HANDLE_WIDTH, tableControlsSpacing } from '@atlaskit/editor-common/styles';
19
18
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
20
19
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
@@ -553,21 +552,6 @@ export const DragHandle = ({
553
552
  });
554
553
  return tr;
555
554
  });
556
- } else if (e.key === 'Backspace' || e.key === 'Delete') {
557
- e.preventDefault();
558
- e.stopPropagation();
559
- api === null || api === void 0 ? void 0 : api.core.actions.execute(({
560
- tr
561
- }) => {
562
- var _api$blockControls5;
563
- deleteSelectedRange(tr);
564
- api === null || api === void 0 ? void 0 : (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 ? void 0 : _api$blockControls5.commands.toggleBlockMenu({
565
- closeMenu: true
566
- })({
567
- tr
568
- });
569
- return tr;
570
- });
571
555
  } else if (![e.altKey, e.ctrlKey, e.shiftKey].some(pressed => pressed)) {
572
556
  // If not trying to press shortcut keys,
573
557
  // return focus to editor to resume editing from caret position
@@ -600,8 +584,8 @@ export const DragHandle = ({
600
584
  }
601
585
  const newHandlePosCheck = isHandleCorrelatedToSelection(view.state, tr.selection, handlePos);
602
586
  if (!tr.selection.empty && newHandlePosCheck) {
603
- var _api$blockControls6;
604
- api === null || api === void 0 ? void 0 : (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 ? void 0 : _api$blockControls6.commands.setMultiSelectPositions()({
587
+ var _api$blockControls5;
588
+ api === null || api === void 0 ? void 0 : (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 ? void 0 : _api$blockControls5.commands.setMultiSelectPositions()({
605
589
  tr
606
590
  });
607
591
  } else if (fg('platform_editor_elements_dnd_select_node_on_drag')) {
@@ -716,7 +700,7 @@ export const DragHandle = ({
716
700
  api === null || api === void 0 ? void 0 : (_api$core7 = api.core) === null || _api$core7 === void 0 ? void 0 : _api$core7.actions.execute(({
717
701
  tr
718
702
  }) => {
719
- var _api$blockControls$sh3, _api$blockControls7, _api$analytics3;
703
+ var _api$blockControls$sh3, _api$blockControls6, _api$analytics3;
720
704
  let nodeTypes, hasSelectedMultipleNodes;
721
705
  const resolvedMovingNode = tr.doc.resolve(start);
722
706
  const maybeNode = resolvedMovingNode.nodeAfter;
@@ -729,7 +713,7 @@ export const DragHandle = ({
729
713
  nodeTypes = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name;
730
714
  hasSelectedMultipleNodes = false;
731
715
  }
732
- api === null || api === void 0 ? void 0 : (_api$blockControls7 = api.blockControls) === null || _api$blockControls7 === void 0 ? void 0 : _api$blockControls7.commands.setNodeDragged(getPos, anchorName, nodeType)({
716
+ api === null || api === void 0 ? void 0 : (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 ? void 0 : _api$blockControls6.commands.setNodeDragged(getPos, anchorName, nodeType)({
733
717
  tr
734
718
  });
735
719
  tr.setMeta('scrollIntoView', false);
@@ -42,7 +42,7 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
42
42
  if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
43
43
  pmPlugins.push({
44
44
  name: 'blockControlsSelectionPreservationPlugin',
45
- plugin: createSelectionPreservationPlugin
45
+ plugin: createSelectionPreservationPlugin(api)
46
46
  });
47
47
  }
48
48
 
@@ -6,6 +6,7 @@ import ReactDOM from 'react-dom';
6
6
  import uuid from 'uuid';
7
7
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
8
8
  import { fg } from '@atlaskit/platform-feature-flags';
9
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
9
10
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
10
11
  import { DragHandle, DragHandleWithVisibility } from '../ui/drag-handle';
11
12
  import { TYPE_HANDLE_DEC, TYPE_NODE_DEC, unmountDecorations } from './decorations-common';
@@ -24,6 +25,50 @@ export var findHandleDec = function findHandleDec(decorations, from, to) {
24
25
  return spec.type === TYPE_HANDLE_DEC;
25
26
  });
26
27
  };
28
+ /**
29
+ * Fix for widget positioning to ensure it is not placed into a previous mark's DOM structure.
30
+ * A ProseMirror widget can appear in the wrong DOM position, specifically being added
31
+ * to a previous mark instead of its intended location, which leads to various rendering issues.
32
+ * For example, when nodeBefore has an alignment mark but the current node doesn't, ProseMirror may
33
+ * incorrectly render the widget inside the previous node's alignment mark wrapper instead.
34
+ */
35
+ var fixWidgetSide = function fixWidgetSide($pos) {
36
+ var defaultSide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
37
+ var alignmentMark = $pos.doc.type.schema.marks.alignment;
38
+ var indentationMark = $pos.doc.type.schema.marks.indentation;
39
+
40
+ // Only apply fix for alignment and indent marks
41
+ if (!alignmentMark && !indentationMark) {
42
+ return defaultSide;
43
+ }
44
+ if ($pos.nodeBefore && $pos.nodeAfter) {
45
+ var _$pos$nodeBefore, _$pos$nodeAfter;
46
+ var beforeMarks = ((_$pos$nodeBefore = $pos.nodeBefore) === null || _$pos$nodeBefore === void 0 ? void 0 : _$pos$nodeBefore.marks.filter(function (mark) {
47
+ return mark.type === alignmentMark || mark.type === indentationMark;
48
+ })) || [];
49
+ var afterMarks = ((_$pos$nodeAfter = $pos.nodeAfter) === null || _$pos$nodeAfter === void 0 ? void 0 : _$pos$nodeAfter.marks.filter(function (mark) {
50
+ return mark.type === alignmentMark || mark.type === indentationMark;
51
+ })) || [];
52
+ if (beforeMarks.length === 0) {
53
+ return defaultSide;
54
+ } else if (afterMarks.length === 0) {
55
+ return 0;
56
+ }
57
+
58
+ // Check if previous node has marks that current node doesn't have
59
+ var hasMissingMark = beforeMarks.some(function (mark) {
60
+ return !afterMarks.some(function (nextMark) {
61
+ return nextMark.eq(mark);
62
+ });
63
+ });
64
+
65
+ // if we have missing mark, we set side to 0 to render widget outside previous mark DOM
66
+ if (hasMissingMark) {
67
+ return 0;
68
+ }
69
+ }
70
+ return defaultSide;
71
+ };
27
72
  export var dragHandleDecoration = function dragHandleDecoration(_ref) {
28
73
  var api = _ref.api,
29
74
  formatMessage = _ref.formatMessage,
@@ -42,8 +87,10 @@ export var dragHandleDecoration = function dragHandleDecoration(_ref) {
42
87
  var unbind;
43
88
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
44
89
  var key = uuid();
90
+ var $pos = editorState.doc.resolve(pos);
91
+ var side = expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && fg('platform_editor_native_anchor_patch_1') ? fixWidgetSide($pos) : -1;
45
92
  var widgetSpec = editorExperiment('platform_editor_breakout_resizing', true) ? {
46
- side: -1,
93
+ side: side,
47
94
  type: TYPE_HANDLE_DEC,
48
95
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
49
96
  testid: "".concat(TYPE_HANDLE_DEC, "-").concat(uuid()),
@@ -60,7 +107,7 @@ export var dragHandleDecoration = function dragHandleDecoration(_ref) {
60
107
  }
61
108
  }
62
109
  } : {
63
- side: -1,
110
+ side: side,
64
111
  type: TYPE_HANDLE_DEC,
65
112
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
66
113
  testid: "".concat(TYPE_HANDLE_DEC, "-").concat(uuid()),
@@ -83,13 +130,14 @@ export var dragHandleDecoration = function dragHandleDecoration(_ref) {
83
130
  try {
84
131
  return getPosUnsafe();
85
132
  } catch (e) {
133
+ // Ignore errors from getPosUnsafe
86
134
  return undefined;
87
135
  }
88
136
  };
89
137
  var newPos = getPos();
90
138
  if (typeof newPos === 'number') {
91
- var $pos = view.state.doc.resolve(newPos);
92
- isTopLevelNode = ($pos === null || $pos === void 0 ? void 0 : $pos.parent.type.name) === 'doc';
139
+ var _$pos = view.state.doc.resolve(newPos);
140
+ isTopLevelNode = (_$pos === null || _$pos === void 0 ? void 0 : _$pos.parent.type.name) === 'doc';
93
141
  }
94
142
  /*
95
143
  * We disable mouseover event to fix flickering issue on hover
@@ -36,55 +36,86 @@ import { getSelectionPreservationMeta, hasUserSelectionChange } from './utils';
36
36
  * when modal dialogs are active. In these states, any selection changes are from ProseMirror's
37
37
  * internal behavior (not user input) and should be prevented. Do not use during normal editing.
38
38
  */
39
- export var createSelectionPreservationPlugin = function createSelectionPreservationPlugin() {
40
- return new SafePlugin({
41
- key: selectionPreservationPluginKey,
42
- state: {
43
- init: function init() {
44
- return {
45
- preservedSelection: undefined
46
- };
39
+ export var createSelectionPreservationPlugin = function createSelectionPreservationPlugin(api) {
40
+ return function () {
41
+ return new SafePlugin({
42
+ key: selectionPreservationPluginKey,
43
+ state: {
44
+ init: function init() {
45
+ return {
46
+ preservedSelection: undefined
47
+ };
48
+ },
49
+ apply: function apply(tr, pluginState) {
50
+ var meta = getSelectionPreservationMeta(tr);
51
+ var newState = _objectSpread({}, pluginState);
52
+ if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
53
+ newState.preservedSelection = tr.selection;
54
+ } else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
55
+ newState.preservedSelection = undefined;
56
+ }
57
+ if (newState.preservedSelection && tr.docChanged) {
58
+ newState.preservedSelection = mapPreservedSelection(newState.preservedSelection, tr);
59
+ }
60
+ return newState;
61
+ }
47
62
  },
48
- apply: function apply(tr, pluginState) {
49
- var meta = getSelectionPreservationMeta(tr);
50
- var newState = _objectSpread({}, pluginState);
51
- if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
52
- newState.preservedSelection = tr.selection;
53
- } else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
54
- newState.preservedSelection = undefined;
63
+ appendTransaction: function appendTransaction(transactions, _oldState, newState) {
64
+ var pluginState = selectionPreservationPluginKey.getState(newState);
65
+ var savedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
66
+ if (!savedSel) {
67
+ return null;
55
68
  }
56
- if (newState.preservedSelection && tr.docChanged) {
57
- newState.preservedSelection = mapPreservedSelection(newState.preservedSelection, tr);
69
+ if (hasUserSelectionChange(transactions)) {
70
+ // Auto-stop if user explicitly changes selection
71
+ return stopPreservingSelection({
72
+ tr: newState.tr
73
+ });
74
+ }
75
+ var currSel = newState.selection;
76
+ var selectionUnchanged = currSel.from === savedSel.from && currSel.to === savedSel.to;
77
+ var selectionInvalid = savedSel.from < 0 || savedSel.to > newState.doc.content.size;
78
+ if (selectionUnchanged || selectionInvalid) {
79
+ return null;
80
+ }
81
+ try {
82
+ return newState.tr.setSelection(savedSel);
83
+ } catch (error) {
84
+ logException(error, {
85
+ location: 'editor-plugin-block-controls/SelectionPreservationPlugin'
86
+ });
58
87
  }
59
- return newState;
60
- }
61
- },
62
- appendTransaction: function appendTransaction(transactions, _oldState, newState) {
63
- var pluginState = selectionPreservationPluginKey.getState(newState);
64
- var savedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
65
- if (!savedSel) {
66
- return null;
67
- }
68
- if (hasUserSelectionChange(transactions)) {
69
- // Auto-stop if user explicitly changes selection
70
- return stopPreservingSelection({
71
- tr: newState.tr
72
- });
73
- }
74
- var currSel = newState.selection;
75
- var selectionUnchanged = currSel.from === savedSel.from && currSel.to === savedSel.to;
76
- var selectionInvalid = savedSel.from < 0 || savedSel.to > newState.doc.content.size;
77
- if (selectionUnchanged || selectionInvalid) {
78
88
  return null;
89
+ },
90
+ props: {
91
+ handleKeyDown: function handleKeyDown(view, event) {
92
+ var _api$userIntent;
93
+ var _ref = selectionPreservationPluginKey.getState(view.state) || {},
94
+ preservedSelection = _ref.preservedSelection;
95
+
96
+ // If there is no current preserved selection, do nothing
97
+ if (!preservedSelection) {
98
+ return false;
99
+ }
100
+
101
+ // While preserving selection, if user presses delete/backspace, prevent event from being
102
+ // handled by ProseMirror natively so that we can apply custom delete logic in block menu
103
+ if (event.key === 'Backspace' || event.key === 'Delete') {
104
+ return true;
105
+ }
106
+ var blockMenuOpen = (api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent) === 'blockMenuOpen';
107
+
108
+ // When block menu isn't open and user presses any key, stop preserving selection
109
+ if (!blockMenuOpen) {
110
+ var tr = view.state.tr;
111
+ stopPreservingSelection({
112
+ tr: tr
113
+ });
114
+ view.dispatch(tr);
115
+ return false;
116
+ }
117
+ }
79
118
  }
80
- try {
81
- return newState.tr.setSelection(savedSel);
82
- } catch (error) {
83
- logException(error, {
84
- location: 'editor-plugin-block-controls/SelectionPreservationPlugin'
85
- });
86
- }
87
- return null;
88
- }
89
- });
119
+ });
120
+ };
90
121
  };
@@ -1,3 +1,6 @@
1
+ import { fg } from '@atlaskit/platform-feature-flags';
2
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
3
+
1
4
  /**
2
5
  * Returns list of block marks on schema that widgets are allowed to render inside
3
6
  * Currently
@@ -15,9 +18,36 @@
15
18
  export var getActiveBlockMarks = function getActiveBlockMarks(state, pos) {
16
19
  var alignment = state.schema.marks.alignment;
17
20
  var resolvedPos = state.doc.resolve(pos);
21
+
18
22
  // find all active marks at the position
19
23
  var marks = resolvedPos.marks();
20
- return marks.filter(function (mark) {
24
+ var supportedMarks = marks.filter(function (mark) {
21
25
  return mark.type === alignment;
22
26
  });
27
+
28
+ /**
29
+ * Fix for widget positioning at alignment mark boundaries.
30
+ * When the previous node has alignment but the next node doesn't, we need to prevent
31
+ * the widget from inheriting alignment marks. This ensures the widget is positioned
32
+ * correctly at the boundary rather than being absorbed into the alignment wrapper.
33
+ */
34
+ if (supportedMarks.length > 0 && expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && fg('platform_editor_native_anchor_patch_1')) {
35
+ var _resolvedPos$nodeAfte;
36
+ var nextNodeMarks = ((_resolvedPos$nodeAfte = resolvedPos.nodeAfter) === null || _resolvedPos$nodeAfte === void 0 ? void 0 : _resolvedPos$nodeAfte.marks.filter(function (mark) {
37
+ return mark.type === alignment;
38
+ })) || [];
39
+
40
+ // Compare alignment values to ensure they are the same
41
+ var alignmentValuesMatch = supportedMarks.length === nextNodeMarks.length && supportedMarks.some(function (mark) {
42
+ return nextNodeMarks.some(function (nextMark) {
43
+ return nextMark.eq(mark);
44
+ });
45
+ });
46
+
47
+ // previous node has alignment but next node does not have alignment or alignment values differ
48
+ if (nextNodeMarks.length === 0 || !alignmentValuesMatch) {
49
+ return [];
50
+ }
51
+ }
52
+ return supportedMarks;
23
53
  };
@@ -19,7 +19,6 @@ import { browser as browserLegacy, getBrowserInfo } from '@atlaskit/editor-commo
19
19
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
20
20
  import { dragToMoveDown, dragToMoveLeft, dragToMoveRight, dragToMoveUp, getAriaKeyshortcuts, TooltipContentWithMultipleShortcuts } from '@atlaskit/editor-common/keymaps';
21
21
  import { blockControlsMessages } from '@atlaskit/editor-common/messages';
22
- import { deleteSelectedRange } from '@atlaskit/editor-common/selection';
23
22
  import { DRAG_HANDLE_WIDTH, tableControlsSpacing } from '@atlaskit/editor-common/styles';
24
23
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
25
24
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
@@ -571,20 +570,6 @@ export var DragHandle = function DragHandle(_ref2) {
571
570
  });
572
571
  return tr;
573
572
  });
574
- } else if (e.key === 'Backspace' || e.key === 'Delete') {
575
- e.preventDefault();
576
- e.stopPropagation();
577
- api === null || api === void 0 || api.core.actions.execute(function (_ref7) {
578
- var _api$blockControls5;
579
- var tr = _ref7.tr;
580
- deleteSelectedRange(tr);
581
- api === null || api === void 0 || (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 || _api$blockControls5.commands.toggleBlockMenu({
582
- closeMenu: true
583
- })({
584
- tr: tr
585
- });
586
- return tr;
587
- });
588
573
  } else if (![e.altKey, e.ctrlKey, e.shiftKey].some(function (pressed) {
589
574
  return pressed;
590
575
  })) {
@@ -606,21 +591,21 @@ export var DragHandle = function DragHandle(_ref2) {
606
591
  start: start
607
592
  };
608
593
  },
609
- onGenerateDragPreview: function onGenerateDragPreview(_ref8) {
594
+ onGenerateDragPreview: function onGenerateDragPreview(_ref7) {
610
595
  var _api$blockControls$sh2;
611
- var nativeSetDragImage = _ref8.nativeSetDragImage;
596
+ var nativeSetDragImage = _ref7.nativeSetDragImage;
612
597
  if (isMultiSelect) {
613
598
  var _api$core6;
614
- api === null || api === void 0 || (_api$core6 = api.core) === null || _api$core6 === void 0 || _api$core6.actions.execute(function (_ref9) {
615
- var tr = _ref9.tr;
599
+ api === null || api === void 0 || (_api$core6 = api.core) === null || _api$core6 === void 0 || _api$core6.actions.execute(function (_ref8) {
600
+ var tr = _ref8.tr;
616
601
  var handlePos = getPos();
617
602
  if (typeof handlePos !== 'number') {
618
603
  return tr;
619
604
  }
620
605
  var newHandlePosCheck = isHandleCorrelatedToSelection(view.state, tr.selection, handlePos);
621
606
  if (!tr.selection.empty && newHandlePosCheck) {
622
- var _api$blockControls6;
623
- api === null || api === void 0 || (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 || _api$blockControls6.commands.setMultiSelectPositions()({
607
+ var _api$blockControls5;
608
+ api === null || api === void 0 || (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 || _api$blockControls5.commands.setMultiSelectPositions()({
624
609
  tr: tr
625
610
  });
626
611
  } else if (fg('platform_editor_elements_dnd_select_node_on_drag')) {
@@ -690,8 +675,8 @@ export var DragHandle = function DragHandle(_ref2) {
690
675
  };
691
676
  }
692
677
  },
693
- render: function render(_ref0) {
694
- var container = _ref0.container;
678
+ render: function render(_ref9) {
679
+ var container = _ref9.container;
695
680
  var dom = view.dom.querySelector("[".concat(getAnchorAttrName(), "=\"").concat(anchorName, "\"]"));
696
681
  if (!dom) {
697
682
  return;
@@ -727,9 +712,9 @@ export var DragHandle = function DragHandle(_ref2) {
727
712
  if (start === undefined) {
728
713
  return;
729
714
  }
730
- api === null || api === void 0 || (_api$core7 = api.core) === null || _api$core7 === void 0 || _api$core7.actions.execute(function (_ref1) {
731
- var _api$blockControls$sh3, _api$blockControls7, _api$analytics3;
732
- var tr = _ref1.tr;
715
+ api === null || api === void 0 || (_api$core7 = api.core) === null || _api$core7 === void 0 || _api$core7.actions.execute(function (_ref0) {
716
+ var _api$blockControls$sh3, _api$blockControls6, _api$analytics3;
717
+ var tr = _ref0.tr;
733
718
  var nodeTypes, hasSelectedMultipleNodes;
734
719
  var resolvedMovingNode = tr.doc.resolve(start);
735
720
  var maybeNode = resolvedMovingNode.nodeAfter;
@@ -742,7 +727,7 @@ export var DragHandle = function DragHandle(_ref2) {
742
727
  nodeTypes = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name;
743
728
  hasSelectedMultipleNodes = false;
744
729
  }
745
- api === null || api === void 0 || (_api$blockControls7 = api.blockControls) === null || _api$blockControls7 === void 0 || _api$blockControls7.commands.setNodeDragged(getPos, anchorName, nodeType)({
730
+ api === null || api === void 0 || (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 || _api$blockControls6.commands.setNodeDragged(getPos, anchorName, nodeType)({
746
731
  tr: tr
747
732
  });
748
733
  tr.setMeta('scrollIntoView', false);
@@ -1125,15 +1110,15 @@ export var DragHandle = function DragHandle(_ref2) {
1125
1110
  var render = isTooltip ? buttonWithTooltip() : renderButton();
1126
1111
  return editorExperiment('platform_editor_controls', 'variant1') ? stickyRender : render;
1127
1112
  };
1128
- export var DragHandleWithVisibility = function DragHandleWithVisibility(_ref10) {
1129
- var view = _ref10.view,
1130
- api = _ref10.api,
1131
- formatMessage = _ref10.formatMessage,
1132
- getPos = _ref10.getPos,
1133
- anchorName = _ref10.anchorName,
1134
- nodeType = _ref10.nodeType,
1135
- handleOptions = _ref10.handleOptions,
1136
- anchorRectCache = _ref10.anchorRectCache;
1113
+ export var DragHandleWithVisibility = function DragHandleWithVisibility(_ref1) {
1114
+ var view = _ref1.view,
1115
+ api = _ref1.api,
1116
+ formatMessage = _ref1.formatMessage,
1117
+ getPos = _ref1.getPos,
1118
+ anchorName = _ref1.anchorName,
1119
+ nodeType = _ref1.nodeType,
1120
+ handleOptions = _ref1.handleOptions,
1121
+ anchorRectCache = _ref1.anchorRectCache;
1137
1122
  return jsx(VisibilityContainer, {
1138
1123
  api: api
1139
1124
  }, jsx(DragHandle, {
@@ -1,4 +1,6 @@
1
1
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { BlockControlsPlugin } from '../../blockControlsPluginType';
2
4
  import type { SelectionPreservationPluginState } from './types';
3
5
  /**
4
6
  * Selection Preservation Plugin
@@ -28,4 +30,4 @@ import type { SelectionPreservationPluginState } from './types';
28
30
  * when modal dialogs are active. In these states, any selection changes are from ProseMirror's
29
31
  * internal behavior (not user input) and should be prevented. Do not use during normal editing.
30
32
  */
31
- export declare const createSelectionPreservationPlugin: () => SafePlugin<SelectionPreservationPluginState>;
33
+ export declare const createSelectionPreservationPlugin: (api: ExtractInjectionAPI<BlockControlsPlugin> | undefined) => () => SafePlugin<SelectionPreservationPluginState>;
@@ -1,4 +1,6 @@
1
1
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { BlockControlsPlugin } from '../../blockControlsPluginType';
2
4
  import type { SelectionPreservationPluginState } from './types';
3
5
  /**
4
6
  * Selection Preservation Plugin
@@ -28,4 +30,4 @@ import type { SelectionPreservationPluginState } from './types';
28
30
  * when modal dialogs are active. In these states, any selection changes are from ProseMirror's
29
31
  * internal behavior (not user input) and should be prevented. Do not use during normal editing.
30
32
  */
31
- export declare const createSelectionPreservationPlugin: () => SafePlugin<SelectionPreservationPluginState>;
33
+ export declare const createSelectionPreservationPlugin: (api: ExtractInjectionAPI<BlockControlsPlugin> | undefined) => () => SafePlugin<SelectionPreservationPluginState>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-controls",
3
- "version": "7.13.2",
3
+ "version": "7.14.0",
4
4
  "description": "Block controls plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -10,7 +10,7 @@
10
10
  "atlassian": {
11
11
  "team": "Editor: Jenga"
12
12
  },
13
- "repository": "https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo",
13
+ "repository": "https://bitbucket.org/atlassian/atlassian-frontend-monorepo",
14
14
  "main": "dist/cjs/index.js",
15
15
  "module": "dist/esm/index.js",
16
16
  "module:es2019": "dist/es2019/index.js",
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "atlaskit:src": "src/index.ts",
30
30
  "dependencies": {
31
- "@atlaskit/adf-schema": "^51.5.1",
31
+ "@atlaskit/adf-schema": "^51.5.0",
32
32
  "@atlaskit/browser-apis": "^0.0.1",
33
33
  "@atlaskit/editor-plugin-accessibility-utils": "^6.0.0",
34
34
  "@atlaskit/editor-plugin-analytics": "^6.2.0",
@@ -43,10 +43,10 @@
43
43
  "@atlaskit/editor-plugin-type-ahead": "^6.5.0",
44
44
  "@atlaskit/editor-plugin-user-intent": "^4.0.0",
45
45
  "@atlaskit/editor-plugin-width": "^7.0.0",
46
- "@atlaskit/editor-prosemirror": "7.0.0",
46
+ "@atlaskit/editor-prosemirror": "^7.2.0",
47
47
  "@atlaskit/editor-shared-styles": "^3.10.0",
48
48
  "@atlaskit/editor-tables": "^2.9.0",
49
- "@atlaskit/icon": "^29.0.0",
49
+ "@atlaskit/icon": "^29.1.0",
50
50
  "@atlaskit/link": "^3.2.0",
51
51
  "@atlaskit/platform-feature-flags": "^1.1.0",
52
52
  "@atlaskit/pragmatic-drag-and-drop": "^1.7.0",
@@ -54,7 +54,7 @@
54
54
  "@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "^3.2.0",
55
55
  "@atlaskit/primitives": "^16.4.0",
56
56
  "@atlaskit/theme": "^21.0.0",
57
- "@atlaskit/tmp-editor-statsig": "^15.0.0",
57
+ "@atlaskit/tmp-editor-statsig": "^15.10.0",
58
58
  "@atlaskit/tokens": "^8.4.0",
59
59
  "@atlaskit/tooltip": "^20.11.0",
60
60
  "@babel/runtime": "^7.0.0",
@@ -66,7 +66,7 @@
66
66
  "uuid": "^3.1.0"
67
67
  },
68
68
  "peerDependencies": {
69
- "@atlaskit/editor-common": "^110.40.0",
69
+ "@atlaskit/editor-common": "^110.42.0",
70
70
  "react": "^18.2.0",
71
71
  "react-dom": "^18.2.0",
72
72
  "react-intl-next": "npm:react-intl@^5.18.1"
@@ -151,6 +151,9 @@
151
151
  },
152
152
  "platform_editor_toolbar_aifc_user_intent_fix": {
153
153
  "type": "boolean"
154
+ },
155
+ "platform_editor_native_anchor_patch_1": {
156
+ "type": "boolean"
154
157
  }
155
158
  }
156
159
  }