@atlaskit/editor-plugin-block-controls 7.13.3 → 7.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 7.14.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`9391799e9bdf4`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9391799e9bdf4) -
8
+ Clean up platform_editor_elements_dnd_select_node_on_drag
9
+ - Updated dependencies
10
+
11
+ ## 7.14.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [`5f21c5a85f65a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/5f21c5a85f65a) -
16
+ ED-29714 fix some alignment issues
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies
21
+
3
22
  ## 7.13.3
4
23
 
5
24
  ### Patch Changes
@@ -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
@@ -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,6 +17,7 @@ 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");
20
21
  var _styles = require("@atlaskit/editor-common/styles");
21
22
  var _useSharedPluginStateSelector = require("@atlaskit/editor-common/use-shared-plugin-state-selector");
22
23
  var _state = require("@atlaskit/editor-prosemirror/state");
@@ -38,7 +39,7 @@ var _pluginKey = require("../pm-plugins/selection-preservation/plugin-key");
38
39
  var _analytics2 = require("../pm-plugins/utils/analytics");
39
40
  var _dragHandlePositions = require("../pm-plugins/utils/drag-handle-positions");
40
41
  var _getSelection = require("../pm-plugins/utils/getSelection");
41
- var _selection = require("../pm-plugins/utils/selection");
42
+ var _selection2 = require("../pm-plugins/utils/selection");
42
43
  var _consts2 = require("./consts");
43
44
  var _dragPreview = require("./drag-preview");
44
45
  var _anchorName = require("./utils/anchor-name");
@@ -332,7 +333,7 @@ var getExpandedSelectionRange = function getExpandedSelectionRange(_ref) {
332
333
  var selectUp = resolvedStartPos.pos < selection.from;
333
334
  var $from = isShiftPressed && selectUp ? resolvedStartPos : selection.$from;
334
335
  var $to = isShiftPressed && !selectUp ? doc.resolve(resolvedStartPos.pos + 1) : selection.$to;
335
- return $from.blockRange($to);
336
+ return (0, _selection.expandToBlockRange)($from, $to);
336
337
  };
337
338
  var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
338
339
  var _api$core4;
@@ -427,7 +428,7 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
427
428
  nodeType: ((_resolvedStartPos$nod = resolvedStartPos.nodeAfter) === null || _resolvedStartPos$nod === void 0 ? void 0 : _resolvedStartPos$nod.type.name) || ''
428
429
  }
429
430
  })(tr);
430
- var range = getExpandedSelectionRange({
431
+ var expandedRange = getExpandedSelectionRange({
431
432
  doc: tr.doc,
432
433
  selection: selection,
433
434
  resolvedStartPos: resolvedStartPos,
@@ -435,11 +436,11 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
435
436
  });
436
437
 
437
438
  // Set selection to expanded selection range if it encompases the clicked drag handle
438
- if (range && isPosWithinRange(startPos, range) && isMultiNodeRange(range)) {
439
- var collapsed = (0, _getSelection.collapseToSelectionRange)(tr.doc.resolve(range.start), tr.doc.resolve(range.end));
439
+ if (expandedRange.range && isPosWithinRange(startPos, expandedRange.range) && isMultiNodeRange(expandedRange.range)) {
440
+ var collapsed = (0, _getSelection.collapseToSelectionRange)(expandedRange.$from, expandedRange.$to);
440
441
 
441
442
  // Then create a selection from the start of the first node to the end of the last node
442
- tr.setSelection(_state.TextSelection.create(tr.doc, Math.min(selection.from, collapsed ? collapsed.$from.pos : range.start), Math.max(selection.to, collapsed ? collapsed.$to.pos : range.end)));
443
+ tr.setSelection(_state.TextSelection.create(tr.doc, Math.min(selection.from, collapsed.$from.pos), Math.max(selection.to, collapsed.$to.pos)));
443
444
  } else {
444
445
  // Select the clicked drag handle's node only
445
446
  tr = (0, _getSelection.selectNode)(tr, startPos, nodeType, api);
@@ -481,8 +482,8 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
481
482
  tr = (0, _getSelection.selectNode)(tr, startPos, nodeType, api);
482
483
  } else if (isTopLevelNode && $anchor.depth <= _consts2.DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH && e.shiftKey && (0, _platformFeatureFlags.fg)('platform_editor_elements_dnd_shift_click_select')) {
483
484
  var _api$blockControls3;
484
- var alignAnchorHeadToSel = (0, _selection.alignAnchorHeadInDirectionOfPos)(tr.selection, startPos);
485
- var selectionWithExpandedHead = (0, _selection.expandSelectionHeadToNodeAtPos)(alignAnchorHeadToSel, startPos);
485
+ var alignAnchorHeadToSel = (0, _selection2.alignAnchorHeadInDirectionOfPos)(tr.selection, startPos);
486
+ var selectionWithExpandedHead = (0, _selection2.expandSelectionHeadToNodeAtPos)(alignAnchorHeadToSel, startPos);
486
487
  tr.setSelection(selectionWithExpandedHead);
487
488
  api === null || api === void 0 || (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 || _api$blockControls3.commands.setMultiSelectPositions()({
488
489
  tr: tr
@@ -611,7 +612,7 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
611
612
  api === null || api === void 0 || (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 || _api$blockControls5.commands.setMultiSelectPositions()({
612
613
  tr: tr
613
614
  });
614
- } else if ((0, _platformFeatureFlags.fg)('platform_editor_elements_dnd_select_node_on_drag')) {
615
+ } else {
615
616
  tr = (0, _getSelection.selectNode)(tr, handlePos, nodeType, api);
616
617
  }
617
618
  return tr;
@@ -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
  };
@@ -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,6 +14,7 @@ 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 { expandToBlockRange } from '@atlaskit/editor-common/selection';
17
18
  import { DRAG_HANDLE_WIDTH, tableControlsSpacing } from '@atlaskit/editor-common/styles';
18
19
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
19
20
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
@@ -328,7 +329,7 @@ const getExpandedSelectionRange = ({
328
329
  const selectUp = resolvedStartPos.pos < selection.from;
329
330
  const $from = isShiftPressed && selectUp ? resolvedStartPos : selection.$from;
330
331
  const $to = isShiftPressed && !selectUp ? doc.resolve(resolvedStartPos.pos + 1) : selection.$to;
331
- return $from.blockRange($to);
332
+ return expandToBlockRange($from, $to);
332
333
  };
333
334
  export const DragHandle = ({
334
335
  view,
@@ -405,7 +406,7 @@ export const DragHandle = ({
405
406
  nodeType: ((_resolvedStartPos$nod = resolvedStartPos.nodeAfter) === null || _resolvedStartPos$nod === void 0 ? void 0 : _resolvedStartPos$nod.type.name) || ''
406
407
  }
407
408
  })(tr);
408
- const range = getExpandedSelectionRange({
409
+ const expandedRange = getExpandedSelectionRange({
409
410
  doc: tr.doc,
410
411
  selection,
411
412
  resolvedStartPos,
@@ -413,11 +414,11 @@ export const DragHandle = ({
413
414
  });
414
415
 
415
416
  // Set selection to expanded selection range if it encompases the clicked drag handle
416
- if (range && isPosWithinRange(startPos, range) && isMultiNodeRange(range)) {
417
- const collapsed = collapseToSelectionRange(tr.doc.resolve(range.start), tr.doc.resolve(range.end));
417
+ if (expandedRange.range && isPosWithinRange(startPos, expandedRange.range) && isMultiNodeRange(expandedRange.range)) {
418
+ const collapsed = collapseToSelectionRange(expandedRange.$from, expandedRange.$to);
418
419
 
419
420
  // Then create a selection from the start of the first node to the end of the last node
420
- tr.setSelection(TextSelection.create(tr.doc, Math.min(selection.from, collapsed ? collapsed.$from.pos : range.start), Math.max(selection.to, collapsed ? collapsed.$to.pos : range.end)));
421
+ tr.setSelection(TextSelection.create(tr.doc, Math.min(selection.from, collapsed.$from.pos), Math.max(selection.to, collapsed.$to.pos)));
421
422
  } else {
422
423
  // Select the clicked drag handle's node only
423
424
  tr = selectNode(tr, startPos, nodeType, api);
@@ -588,7 +589,7 @@ export const DragHandle = ({
588
589
  api === null || api === void 0 ? void 0 : (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 ? void 0 : _api$blockControls5.commands.setMultiSelectPositions()({
589
590
  tr
590
591
  });
591
- } else if (fg('platform_editor_elements_dnd_select_node_on_drag')) {
592
+ } else {
592
593
  tr = selectNode(tr, handlePos, nodeType, api);
593
594
  }
594
595
  return tr;
@@ -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
@@ -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,6 +19,7 @@ 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 { expandToBlockRange } from '@atlaskit/editor-common/selection';
22
23
  import { DRAG_HANDLE_WIDTH, tableControlsSpacing } from '@atlaskit/editor-common/styles';
23
24
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
24
25
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
@@ -329,7 +330,7 @@ var getExpandedSelectionRange = function getExpandedSelectionRange(_ref) {
329
330
  var selectUp = resolvedStartPos.pos < selection.from;
330
331
  var $from = isShiftPressed && selectUp ? resolvedStartPos : selection.$from;
331
332
  var $to = isShiftPressed && !selectUp ? doc.resolve(resolvedStartPos.pos + 1) : selection.$to;
332
- return $from.blockRange($to);
333
+ return expandToBlockRange($from, $to);
333
334
  };
334
335
  export var DragHandle = function DragHandle(_ref2) {
335
336
  var _api$core4;
@@ -424,7 +425,7 @@ export var DragHandle = function DragHandle(_ref2) {
424
425
  nodeType: ((_resolvedStartPos$nod = resolvedStartPos.nodeAfter) === null || _resolvedStartPos$nod === void 0 ? void 0 : _resolvedStartPos$nod.type.name) || ''
425
426
  }
426
427
  })(tr);
427
- var range = getExpandedSelectionRange({
428
+ var expandedRange = getExpandedSelectionRange({
428
429
  doc: tr.doc,
429
430
  selection: selection,
430
431
  resolvedStartPos: resolvedStartPos,
@@ -432,11 +433,11 @@ export var DragHandle = function DragHandle(_ref2) {
432
433
  });
433
434
 
434
435
  // Set selection to expanded selection range if it encompases the clicked drag handle
435
- if (range && isPosWithinRange(startPos, range) && isMultiNodeRange(range)) {
436
- var collapsed = collapseToSelectionRange(tr.doc.resolve(range.start), tr.doc.resolve(range.end));
436
+ if (expandedRange.range && isPosWithinRange(startPos, expandedRange.range) && isMultiNodeRange(expandedRange.range)) {
437
+ var collapsed = collapseToSelectionRange(expandedRange.$from, expandedRange.$to);
437
438
 
438
439
  // Then create a selection from the start of the first node to the end of the last node
439
- tr.setSelection(TextSelection.create(tr.doc, Math.min(selection.from, collapsed ? collapsed.$from.pos : range.start), Math.max(selection.to, collapsed ? collapsed.$to.pos : range.end)));
440
+ tr.setSelection(TextSelection.create(tr.doc, Math.min(selection.from, collapsed.$from.pos), Math.max(selection.to, collapsed.$to.pos)));
440
441
  } else {
441
442
  // Select the clicked drag handle's node only
442
443
  tr = selectNode(tr, startPos, nodeType, api);
@@ -608,7 +609,7 @@ export var DragHandle = function DragHandle(_ref2) {
608
609
  api === null || api === void 0 || (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 || _api$blockControls5.commands.setMultiSelectPositions()({
609
610
  tr: tr
610
611
  });
611
- } else if (fg('platform_editor_elements_dnd_select_node_on_drag')) {
612
+ } else {
612
613
  tr = selectNode(tr, handlePos, nodeType, api);
613
614
  }
614
615
  return tr;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-controls",
3
- "version": "7.13.3",
3
+ "version": "7.14.1",
4
4
  "description": "Block controls plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -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,8 +54,8 @@
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.6.0",
58
- "@atlaskit/tokens": "^8.4.0",
57
+ "@atlaskit/tmp-editor-statsig": "^15.12.0",
58
+ "@atlaskit/tokens": "^8.5.0",
59
59
  "@atlaskit/tooltip": "^20.11.0",
60
60
  "@babel/runtime": "^7.0.0",
61
61
  "@emotion/react": "^11.7.1",
@@ -66,7 +66,7 @@
66
66
  "uuid": "^3.1.0"
67
67
  },
68
68
  "peerDependencies": {
69
- "@atlaskit/editor-common": "^110.41.0",
69
+ "@atlaskit/editor-common": "^110.44.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"
@@ -119,9 +119,6 @@
119
119
  "platform_editor_elements_dnd_shift_click_select": {
120
120
  "type": "boolean"
121
121
  },
122
- "platform_editor_elements_dnd_select_node_on_drag": {
123
- "type": "boolean"
124
- },
125
122
  "platform_editor_ease_of_use_metrics": {
126
123
  "type": "boolean"
127
124
  },
@@ -151,6 +148,9 @@
151
148
  },
152
149
  "platform_editor_toolbar_aifc_user_intent_fix": {
153
150
  "type": "boolean"
151
+ },
152
+ "platform_editor_native_anchor_patch_1": {
153
+ "type": "boolean"
154
154
  }
155
155
  }
156
156
  }