@atlaskit/editor-plugin-block-controls 8.0.14 → 8.1.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.
@@ -37,8 +37,9 @@ import { selectionPreservationPluginKey } from '../pm-plugins/selection-preserva
37
37
  import { getMultiSelectAnalyticsAttributes } from '../pm-plugins/utils/analytics';
38
38
  import { getControlBottomCSSValue, getControlHeightCSSValue, getLeftPosition, getNodeHeight, getTopPosition, shouldBeSticky, shouldMaskNodeControls } from '../pm-plugins/utils/drag-handle-positions';
39
39
  import { isHandleCorrelatedToSelection, selectNode } from '../pm-plugins/utils/getSelection';
40
- import { alignAnchorHeadInDirectionOfPos, expandSelectionHeadToNodeAtPos } from '../pm-plugins/utils/selection';
40
+ import { adjustSelectionBoundsForEdgePositions, alignAnchorHeadInDirectionOfPos, expandSelectionHeadToNodeAtPos } from '../pm-plugins/utils/selection';
41
41
  import { DRAG_HANDLE_BORDER_RADIUS, DRAG_HANDLE_HEIGHT, DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH, DRAG_HANDLE_ZINDEX, dragHandleGap, nodeMargins, spacingBetweenNodesForPreview, STICKY_CONTROLS_TOP_MARGIN, STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER, topPositionAdjustment } from './consts';
42
+ import { DragHandleNestedIcon } from './drag-handle-nested-icon';
42
43
  import { dragPreview } from './drag-preview';
43
44
  import { refreshAnchorName } from './utils/anchor-name';
44
45
  import { getAnchorAttrName } from './utils/dom-attr-name';
@@ -354,7 +355,11 @@ const getExpandedSelectionRange = ({
354
355
  const selectUp = resolvedStartPos.pos < selection.from;
355
356
  const $from = isShiftPressed && selectUp ? resolvedStartPos : selection.$from;
356
357
  const $to = isShiftPressed && !selectUp ? doc.resolve(resolvedStartPos.pos + 1) : selection.$to;
357
- return expandToBlockRange($from, $to);
358
+ const adjusted = isShiftPressed ? {
359
+ $from,
360
+ $to
361
+ } : adjustSelectionBoundsForEdgePositions($from, $to);
362
+ return expandToBlockRange(adjusted.$from, adjusted.$to);
358
363
  };
359
364
  /**
360
365
  * Updates the transaction with preserved selection logic.
@@ -423,6 +428,24 @@ export const DragHandle = ({
423
428
  const start = getPos();
424
429
  const isLayoutColumn = nodeType === 'layoutColumn';
425
430
  const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true);
431
+
432
+ // Dynamically calculate if node is top-level based on current position (gated by experiment)
433
+ const isTopLevelNodeDynamic = useMemo(() => {
434
+ if (!expValEquals('platform_editor_nested_drag_handle_icon', 'isEnabled', true)) {
435
+ return isTopLevelNode;
436
+ }
437
+ const pos = getPos();
438
+ if (typeof pos === 'number') {
439
+ const $pos = view.state.doc.resolve(pos);
440
+ return ($pos === null || $pos === void 0 ? void 0 : $pos.parent.type.name) === 'doc';
441
+ }
442
+ return true;
443
+ }, [getPos, view.state.doc, isTopLevelNode]);
444
+
445
+ // Use the dynamic value when experiment is on, otherwise use the prop
446
+ // When cleaning up the experiment, you can safely remove the isTopLevelNode as an prop and
447
+ // just rely on the dynamic value (rename it to isTopLevelNode for simplicitiy)
448
+ const isTopLevelNodeValue = expValEquals('platform_editor_nested_drag_handle_icon', 'isEnabled', true) ? isTopLevelNodeDynamic : isTopLevelNode;
426
449
  useEffect(() => {
427
450
  if (editorExperiment('platform_editor_block_control_optimise_render', true)) {
428
451
  return;
@@ -513,7 +536,7 @@ export const DragHandle = ({
513
536
  const $anchor = (mSelect === null || mSelect === void 0 ? void 0 : mSelect.anchor) !== undefined ? tr.doc.resolve(mSelect === null || mSelect === void 0 ? void 0 : mSelect.anchor) : tr.selection.$anchor;
514
537
  if (!isMultiSelect || tr.selection.empty || !e.shiftKey) {
515
538
  tr = selectNode(tr, startPos, nodeType, api);
516
- } else if (isTopLevelNode && $anchor.depth <= DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH && e.shiftKey && fg('platform_editor_elements_dnd_shift_click_select')) {
539
+ } else if (isTopLevelNodeValue && $anchor.depth <= DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH && e.shiftKey && fg('platform_editor_elements_dnd_shift_click_select')) {
517
540
  var _api$blockControls3;
518
541
  const alignAnchorHeadToSel = alignAnchorHeadInDirectionOfPos(tr.selection, startPos);
519
542
  const selectionWithExpandedHead = expandSelectionHeadToNodeAtPos(alignAnchorHeadToSel, startPos);
@@ -538,7 +561,7 @@ export const DragHandle = ({
538
561
  return tr;
539
562
  });
540
563
  view.focus();
541
- }, [isMultiSelect, api, view, dragHandleSelected, getPos, isTopLevelNode, nodeType]);
564
+ }, [isMultiSelect, api, view, dragHandleSelected, getPos, isTopLevelNodeValue, nodeType]);
542
565
  const handleKeyDown = useCallback(e => {
543
566
  // allow user to use spacebar to select the node
544
567
  if (!e.repeat && e.key === ' ') {
@@ -843,20 +866,20 @@ export const DragHandle = ({
843
866
  const isEdgeCase = (hasResizer || isExtension || isEmbedCard || isBlockCard) && innerContainer;
844
867
  const isSticky = shouldBeSticky(nodeType);
845
868
  if (supportsAnchor) {
846
- const bottom = editorExperiment('platform_editor_controls', 'variant1') ? getControlBottomCSSValue(safeAnchorName, isSticky, isTopLevelNode, isLayoutColumn) : {};
869
+ const bottom = editorExperiment('platform_editor_controls', 'variant1') ? getControlBottomCSSValue(safeAnchorName, isSticky, isTopLevelNodeValue, isLayoutColumn) : {};
847
870
  return {
848
871
  left: isEdgeCase ? `calc(anchor(${safeAnchorName} start) + ${getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType)})` : editorExperiment('advanced_layouts', true) && isLayoutColumn ? `calc((anchor(${safeAnchorName} right) + anchor(${safeAnchorName} left))/2 - ${DRAG_HANDLE_HEIGHT / 2}px)` : `calc(anchor(${safeAnchorName} start) - ${DRAG_HANDLE_WIDTH}px - ${dragHandleGap(nodeType, parentNodeType)}px)`,
849
872
  top: editorExperiment('advanced_layouts', true) && isLayoutColumn ? `calc(anchor(${safeAnchorName} top) - ${DRAG_HANDLE_WIDTH}px)` : `calc(anchor(${safeAnchorName} start)+ ${topPositionAdjustment(expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? $pos && $pos.nodeAfter && getNodeTypeWithLevel($pos.nodeAfter) || nodeType : nodeType)}px)`,
850
873
  ...bottom
851
874
  };
852
875
  }
853
- const height = editorExperiment('platform_editor_controls', 'variant1') ? getControlHeightCSSValue(getNodeHeight(dom, safeAnchorName, anchorRectCache) || 0, isSticky, isTopLevelNode, `${DRAG_HANDLE_HEIGHT}`, isLayoutColumn) : {};
876
+ const height = editorExperiment('platform_editor_controls', 'variant1') ? getControlHeightCSSValue(getNodeHeight(dom, safeAnchorName, anchorRectCache) || 0, isSticky, isTopLevelNodeValue, `${DRAG_HANDLE_HEIGHT}`, isLayoutColumn) : {};
854
877
  return {
855
878
  left: isEdgeCase ? `calc(${(dom === null || dom === void 0 ? void 0 : dom.offsetLeft) || 0}px + ${getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType)})` : getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType),
856
879
  top: getTopPosition(dom, nodeType),
857
880
  ...height
858
881
  };
859
- }, [anchorName, getPos, view, nodeType, macroInteractionUpdates, anchorRectCache, isTopLevelNode, isLayoutColumn, recalculatePosition]);
882
+ }, [anchorName, getPos, view, nodeType, macroInteractionUpdates, anchorRectCache, isTopLevelNodeValue, isLayoutColumn, recalculatePosition]);
860
883
  const calculatePositionOld = useCallback(() => {
861
884
  const pos = getPos();
862
885
  const $pos = expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? typeof pos === 'number' && view.state.doc.resolve(pos) : pos && view.state.doc.resolve(pos);
@@ -891,20 +914,20 @@ export const DragHandle = ({
891
914
  const isEdgeCase = (hasResizer || isExtension || isEmbedCard || isBlockCard) && innerContainer;
892
915
  const isSticky = shouldBeSticky(nodeType);
893
916
  if (supportsAnchor) {
894
- const bottom = editorExperiment('platform_editor_controls', 'variant1') ? getControlBottomCSSValue(safeAnchorName, isSticky, isTopLevelNode, isLayoutColumn) : {};
917
+ const bottom = editorExperiment('platform_editor_controls', 'variant1') ? getControlBottomCSSValue(safeAnchorName, isSticky, isTopLevelNodeValue, isLayoutColumn) : {};
895
918
  return {
896
919
  left: isEdgeCase ? `calc(anchor(${safeAnchorName} start) + ${getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType)})` : editorExperiment('advanced_layouts', true) && isLayoutColumn ? `calc((anchor(${safeAnchorName} right) + anchor(${safeAnchorName} left))/2 - ${DRAG_HANDLE_HEIGHT / 2}px)` : `calc(anchor(${safeAnchorName} start) - ${DRAG_HANDLE_WIDTH}px - ${dragHandleGap(nodeType, parentNodeType)}px)`,
897
920
  top: editorExperiment('advanced_layouts', true) && isLayoutColumn ? `calc(anchor(${safeAnchorName} top) - ${DRAG_HANDLE_WIDTH}px)` : `calc(anchor(${safeAnchorName} start) + ${topPositionAdjustment(expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? $pos && $pos.nodeAfter && getNodeTypeWithLevel($pos.nodeAfter) || nodeType : nodeType)}px)`,
898
921
  ...bottom
899
922
  };
900
923
  }
901
- const height = editorExperiment('platform_editor_controls', 'variant1') ? getControlHeightCSSValue(getNodeHeight(dom, safeAnchorName, anchorRectCache) || 0, isSticky, isTopLevelNode, `${DRAG_HANDLE_HEIGHT}`, isLayoutColumn) : {};
924
+ const height = editorExperiment('platform_editor_controls', 'variant1') ? getControlHeightCSSValue(getNodeHeight(dom, safeAnchorName, anchorRectCache) || 0, isSticky, isTopLevelNodeValue, `${DRAG_HANDLE_HEIGHT}`, isLayoutColumn) : {};
902
925
  return {
903
926
  left: isEdgeCase ? `calc(${(dom === null || dom === void 0 ? void 0 : dom.offsetLeft) || 0}px + ${getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType)})` : getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType),
904
927
  top: getTopPosition(dom, nodeType),
905
928
  ...height
906
929
  };
907
- }, [anchorName, getPos, view, nodeType, blockCardWidth, macroInteractionUpdates, anchorRectCache, isTopLevelNode, isLayoutColumn]);
930
+ }, [anchorName, getPos, view, nodeType, blockCardWidth, macroInteractionUpdates, anchorRectCache, isTopLevelNodeValue, isLayoutColumn]);
908
931
  useEffect(() => {
909
932
  if (editorExperiment('platform_editor_block_control_optimise_render', true)) {
910
933
  return;
@@ -980,12 +1003,12 @@ export const DragHandle = ({
980
1003
  }
981
1004
  const mSelect = api === null || api === void 0 ? void 0 : (_api$blockControls$sh4 = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh4 === void 0 ? void 0 : _api$blockControls$sh4.multiSelectDnD;
982
1005
  const $anchor = (mSelect === null || mSelect === void 0 ? void 0 : mSelect.anchor) !== undefined ? view.state.doc.resolve(mSelect === null || mSelect === void 0 ? void 0 : mSelect.anchor) : view.state.selection.$anchor;
983
- if (isShiftDown && (!isTopLevelNode || isTopLevelNode && $anchor.depth > DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH)) {
1006
+ if (isShiftDown && (!isTopLevelNodeValue || isTopLevelNodeValue && $anchor.depth > DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH)) {
984
1007
  setDragHandleDisabled(true);
985
1008
  } else {
986
1009
  setDragHandleDisabled(false);
987
1010
  }
988
- }, [api === null || api === void 0 ? void 0 : api.blockControls.sharedState, isMultiSelect, isShiftDown, isTopLevelNode, view]);
1011
+ }, [api === null || api === void 0 ? void 0 : api.blockControls.sharedState, isMultiSelect, isShiftDown, isTopLevelNodeValue, view]);
989
1012
  const dragHandleMessage = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? formatMessage(blockControlsMessages.dragToMoveClickToOpen, {
990
1013
  br: jsx("br", null)
991
1014
  }) : formatMessage(blockControlsMessages.dragToMove);
@@ -994,7 +1017,7 @@ export const DragHandle = ({
994
1017
  const dragHandleAriaLabel = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? formatMessage(blockControlsMessages.dragToMoveClickToOpen, {
995
1018
  br: ' '
996
1019
  }) : formatMessage(blockControlsMessages.dragToMove);
997
- let helpDescriptors = isTopLevelNode ? [{
1020
+ let helpDescriptors = isTopLevelNodeValue ? [{
998
1021
  description: dragHandleMessage
999
1022
  }, {
1000
1023
  description: formatMessage(blockControlsMessages.moveUp),
@@ -1018,7 +1041,7 @@ export const DragHandle = ({
1018
1041
  keymap: dragToMoveDown
1019
1042
  }];
1020
1043
  let isParentNodeOfTypeLayout;
1021
- if (!isTopLevelNode) {
1044
+ if (!isTopLevelNodeValue) {
1022
1045
  const pos = getPos();
1023
1046
  if (typeof pos === 'number') {
1024
1047
  var _$pos$parent;
@@ -1095,7 +1118,7 @@ export const DragHandle = ({
1095
1118
  // eslint-disable-next-line @atlaskit/design-system/no-direct-use-of-web-platform-drag-and-drop
1096
1119
  ,
1097
1120
  onDragStart: handleIconDragStart
1098
- }, jsx(DragHandleVerticalIcon, {
1121
+ }, expValEquals('platform_editor_nested_drag_handle_icon', 'isEnabled', true) && !isTopLevelNodeValue ? jsx(DragHandleNestedIcon, null) : jsx(DragHandleVerticalIcon, {
1099
1122
  spacing: "spacious",
1100
1123
  label: "",
1101
1124
  size: "small"
@@ -1108,7 +1131,7 @@ export const DragHandle = ({
1108
1131
  as: "span",
1109
1132
  testId: "block-ctrl-drag-handle-container"
1110
1133
  }, jsx("span", {
1111
- css: [tooltipContainerStyles, shouldMaskNodeControls(nodeType, isTopLevelNode) && (expValEquals('platform_editor_table_sticky_header_improvements', 'cohort', 'test_with_overflow') && fg('platform_editor_table_sticky_header_patch_6') ? tooltipContainerStylesImprovedStickyHeaderWithMask : tooltipContainerStylesStickyHeaderWithMask), !shouldMaskNodeControls(nodeType, isTopLevelNode) && tooltipContainerStylesStickyHeaderWithoutMask]
1134
+ css: [tooltipContainerStyles, shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && (expValEquals('platform_editor_table_sticky_header_improvements', 'cohort', 'test_with_overflow') && fg('platform_editor_table_sticky_header_patch_6') ? tooltipContainerStylesImprovedStickyHeaderWithMask : tooltipContainerStylesStickyHeaderWithMask), !shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && tooltipContainerStylesStickyHeaderWithoutMask]
1112
1135
  }, jsx(Tooltip, {
1113
1136
  content: jsx(TooltipContentWithMultipleShortcuts, {
1114
1137
  helpDescriptors: helpDescriptors
@@ -1122,7 +1145,7 @@ export const DragHandle = ({
1122
1145
  });
1123
1146
  }
1124
1147
  }, jsx("span", {
1125
- css: [shouldMaskNodeControls(nodeType, isTopLevelNode) && buttonWrapperStyles, buttonWrapperStylesPatch]
1148
+ css: [shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && buttonWrapperStyles, buttonWrapperStylesPatch]
1126
1149
  }, renderButton()))));
1127
1150
  const stickyWithoutTooltip = () => jsx(Box
1128
1151
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
@@ -1132,9 +1155,9 @@ export const DragHandle = ({
1132
1155
  as: "span",
1133
1156
  testId: "block-ctrl-drag-handle-container"
1134
1157
  }, jsx("span", {
1135
- css: [tooltipContainerStyles, shouldMaskNodeControls(nodeType, isTopLevelNode) && tooltipContainerStylesStickyHeaderWithMask, !shouldMaskNodeControls(nodeType, isTopLevelNode) && tooltipContainerStylesStickyHeaderWithoutMask]
1158
+ css: [tooltipContainerStyles, shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && tooltipContainerStylesStickyHeaderWithMask, !shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && tooltipContainerStylesStickyHeaderWithoutMask]
1136
1159
  }, jsx("span", {
1137
- css: [shouldMaskNodeControls(nodeType, isTopLevelNode) && buttonWrapperStyles, buttonWrapperStylesPatch]
1160
+ css: [shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && buttonWrapperStyles, buttonWrapperStylesPatch]
1138
1161
  }, renderButton())));
1139
1162
  const buttonWithTooltip = () => jsx(Tooltip, {
1140
1163
  content: jsx(TooltipContentWithMultipleShortcuts, {
@@ -1161,6 +1184,7 @@ export const DragHandleWithVisibility = ({
1161
1184
  anchorName,
1162
1185
  nodeType,
1163
1186
  handleOptions,
1187
+ isTopLevelNode,
1164
1188
  anchorRectCache
1165
1189
  }) => {
1166
1190
  return jsx(VisibilityContainer, {
@@ -1173,6 +1197,7 @@ export const DragHandleWithVisibility = ({
1173
1197
  anchorName: anchorName,
1174
1198
  nodeType: nodeType,
1175
1199
  handleOptions: handleOptions,
1200
+ isTopLevelNode: isTopLevelNode,
1176
1201
  anchorRectCache: anchorRectCache
1177
1202
  }));
1178
1203
  };
@@ -133,7 +133,7 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
133
133
  }));
134
134
  if ((menuTriggerBy === undefined || !!menuTriggerBy && menuTriggerBy === (options === null || options === void 0 ? void 0 : options.anchorName)) && currentUserIntent === 'blockMenuOpen') {
135
135
  var state = api === null || api === void 0 ? void 0 : api.blockControls.sharedState.currentState();
136
- if (state !== null && state !== void 0 && state.isSelectedViaDragHandle && fg('platform_editor_toolbar_aifc_user_intent_fix')) {
136
+ if (state !== null && state !== void 0 && state.isSelectedViaDragHandle) {
137
137
  var _api$userIntent4;
138
138
  api === null || api === void 0 || (_api$userIntent4 = api.userIntent) === null || _api$userIntent4 === void 0 || _api$userIntent4.commands.setCurrentUserIntent('dragHandleSelected')({
139
139
  tr: tr
@@ -84,7 +84,7 @@ export var dragHandleDecoration = function dragHandleDecoration(_ref) {
84
84
  var getPos = function getPos() {
85
85
  try {
86
86
  return getPosUnsafe();
87
- } catch (e) {
87
+ } catch (_unused) {
88
88
  // Ignore errors from getPosUnsafe
89
89
  return undefined;
90
90
  }
@@ -1,5 +1,4 @@
1
1
  import { DRAG_HANDLE_SELECTOR } from '@atlaskit/editor-common/styles';
2
- import { fg } from '@atlaskit/platform-feature-flags';
3
2
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
4
3
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
5
4
  export var handleMouseDown = function handleMouseDown(api) {
@@ -39,21 +38,19 @@ export var handleMouseDown = function handleMouseDown(api) {
39
38
  * When block menu is enabled, reset intent back to 'default' as editor-plugin-block-menu sets the user intent to 'blockMenuOpen', and setting here
40
39
  * causes flickering as this runs before editor-plugin-block-menu.
41
40
  */
42
- if (fg('platform_editor_toolbar_aifc_user_intent_fix')) {
43
- if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
44
- // if target is drag handle, block menu will be opened
45
- if (!isDragHandle) {
46
- var _api$userIntent;
47
- api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || _api$userIntent.commands.setCurrentUserIntent('default')({
48
- tr: tr
49
- });
50
- }
51
- } else {
52
- var _api$userIntent2;
53
- (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 || _api$userIntent2.commands.setCurrentUserIntent(isDragHandle ? 'dragHandleSelected' : 'default')({
41
+ if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
42
+ // if target is drag handle, block menu will be opened
43
+ if (!isDragHandle) {
44
+ var _api$userIntent;
45
+ api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || _api$userIntent.commands.setCurrentUserIntent('default')({
54
46
  tr: tr
55
47
  });
56
48
  }
49
+ } else {
50
+ var _api$userIntent2;
51
+ (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 || _api$userIntent2.commands.setCurrentUserIntent(isDragHandle ? 'dragHandleSelected' : 'default')({
52
+ tr: tr
53
+ });
57
54
  }
58
55
  return tr;
59
56
  });
@@ -97,7 +97,58 @@ export var mapPreservedSelection = function mapPreservedSelection(selection, tr)
97
97
  }
98
98
  return createPreservedSelection(tr.doc.resolve(from), tr.doc.resolve(to));
99
99
  };
100
+ var isInsideEmptyTextblock = function isInsideEmptyTextblock($pos) {
101
+ return $pos.parent.isTextblock && $pos.parent.content.size === 0;
102
+ };
103
+ var isAtEndOfParent = function isAtEndOfParent($pos) {
104
+ return $pos.parentOffset === $pos.parent.content.size;
105
+ };
106
+ var isAtStartOfParent = function isAtStartOfParent($pos) {
107
+ return $pos.parentOffset === 0;
108
+ };
100
109
 
110
+ /**
111
+ * Adjust selection bounds to exclude nodes where the selection only touches
112
+ * the edge position without selecting any content.
113
+ *
114
+ * Exception: Don't adjust if the selection is inside an empty textblock,
115
+ * as we want to include empty paragraphs in block operations.
116
+ *
117
+ * @param $from The resolved position of the start of the selection
118
+ * @param $to The resolved position of the end of the selection
119
+ * @returns Adjusted $from and $to positions
120
+ */
121
+ export var adjustSelectionBoundsForEdgePositions = function adjustSelectionBoundsForEdgePositions($from, $to) {
122
+ var doc = $from.doc;
123
+
124
+ // Walk $from forward while at end of ancestors
125
+ var adjustedFrom = $from;
126
+ if (!isInsideEmptyTextblock($from)) {
127
+ while (adjustedFrom.depth > 0 && isAtEndOfParent(adjustedFrom)) {
128
+ var nextPos = adjustedFrom.after();
129
+ if (nextPos > doc.content.size || nextPos === adjustedFrom.pos) {
130
+ break;
131
+ }
132
+ adjustedFrom = doc.resolve(nextPos);
133
+ }
134
+ }
135
+
136
+ // Walk $to backward while at start of ancestors
137
+ var adjustedTo = $to;
138
+ if (!isInsideEmptyTextblock($to)) {
139
+ while (adjustedTo.depth > 0 && isAtStartOfParent(adjustedTo)) {
140
+ var prevPos = adjustedTo.before();
141
+ if (prevPos < 0 || prevPos === adjustedTo.pos) {
142
+ break;
143
+ }
144
+ adjustedTo = doc.resolve(prevPos);
145
+ }
146
+ }
147
+ return {
148
+ $from: adjustedFrom,
149
+ $to: adjustedTo
150
+ };
151
+ };
101
152
  /**
102
153
  * Creates a preserved selection which is expanded to block boundaries.
103
154
  *
@@ -113,9 +164,17 @@ export var mapPreservedSelection = function mapPreservedSelection(selection, tr)
113
164
  export var createPreservedSelection = function createPreservedSelection($from, $to) {
114
165
  var _doc$nodeAt;
115
166
  var doc = $from.doc;
167
+ var isCollapsed = $from.pos === $to.pos;
168
+ var adjusted = isCollapsed ? {
169
+ $from: $from,
170
+ $to: $to
171
+ } : adjustSelectionBoundsForEdgePositions($from, $to);
172
+ if (!isCollapsed && adjusted.$from.pos >= adjusted.$to.pos) {
173
+ return undefined;
174
+ }
116
175
 
117
176
  // expand the selection range to block boundaries, so selection always includes whole nodes
118
- var expanded = expandToBlockRange($from, $to);
177
+ var expanded = expandToBlockRange(adjusted.$from, adjusted.$to);
119
178
 
120
179
  // stop preserving if selection becomes invalid
121
180
  if (expanded.$from.pos < 0 || expanded.$to.pos > doc.content.size || expanded.$from.pos >= expanded.$to.pos) {
@@ -0,0 +1,57 @@
1
+ /** @jsxRuntime classic */
2
+ /**
3
+ * @jsxRuntime classic
4
+ * @jsx jsx
5
+ */
6
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
7
+ import { css, jsx } from '@emotion/react';
8
+ var spanStyles = css({
9
+ display: 'inline-block',
10
+ boxSizing: 'border-box',
11
+ flexShrink: 0
12
+ });
13
+ var svgSizeStyles = css({
14
+ width: '24px',
15
+ height: '24px'
16
+ });
17
+ var svgStyles = css({
18
+ color: 'currentColor',
19
+ overflow: 'hidden',
20
+ pointerEvents: 'none',
21
+ verticalAlign: 'bottom'
22
+ });
23
+
24
+ /**
25
+ * Custom 3-dot vertical drag handle icon for nested nodes.
26
+ * Similar to DragHandleVerticalIcon but with only 3 dots instead of 6.
27
+ * Hardcoded to medium size with spacious spacing.
28
+ */
29
+ export var DragHandleNestedIcon = function DragHandleNestedIcon() {
30
+ return jsx("span", {
31
+ "aria-hidden": true,
32
+ css: spanStyles
33
+ }, jsx("svg", {
34
+ width: 24,
35
+ height: 24,
36
+ viewBox: "-4 -4 24 24",
37
+ fill: "none",
38
+ xmlns: "http://www.w3.org/2000/svg",
39
+ role: "presentation",
40
+ css: [svgStyles, svgSizeStyles]
41
+ }, jsx("circle", {
42
+ cx: "8",
43
+ cy: "4",
44
+ r: "1.5",
45
+ fill: "currentColor"
46
+ }), jsx("circle", {
47
+ cx: "8",
48
+ cy: "8",
49
+ r: "1.5",
50
+ fill: "currentColor"
51
+ }), jsx("circle", {
52
+ cx: "8",
53
+ cy: "12",
54
+ r: "1.5",
55
+ fill: "currentColor"
56
+ })));
57
+ };
@@ -42,8 +42,9 @@ import { selectionPreservationPluginKey } from '../pm-plugins/selection-preserva
42
42
  import { getMultiSelectAnalyticsAttributes } from '../pm-plugins/utils/analytics';
43
43
  import { getControlBottomCSSValue, getControlHeightCSSValue, getLeftPosition, getNodeHeight, getTopPosition, shouldBeSticky, shouldMaskNodeControls } from '../pm-plugins/utils/drag-handle-positions';
44
44
  import { isHandleCorrelatedToSelection, selectNode } from '../pm-plugins/utils/getSelection';
45
- import { alignAnchorHeadInDirectionOfPos, expandSelectionHeadToNodeAtPos } from '../pm-plugins/utils/selection';
45
+ import { adjustSelectionBoundsForEdgePositions, alignAnchorHeadInDirectionOfPos, expandSelectionHeadToNodeAtPos } from '../pm-plugins/utils/selection';
46
46
  import { DRAG_HANDLE_BORDER_RADIUS, DRAG_HANDLE_HEIGHT, DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH, DRAG_HANDLE_ZINDEX, dragHandleGap, nodeMargins, spacingBetweenNodesForPreview, STICKY_CONTROLS_TOP_MARGIN, STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER, topPositionAdjustment } from './consts';
47
+ import { DragHandleNestedIcon } from './drag-handle-nested-icon';
47
48
  import { dragPreview } from './drag-preview';
48
49
  import { refreshAnchorName } from './utils/anchor-name';
49
50
  import { getAnchorAttrName } from './utils/dom-attr-name';
@@ -355,7 +356,11 @@ var getExpandedSelectionRange = function getExpandedSelectionRange(_ref) {
355
356
  var selectUp = resolvedStartPos.pos < selection.from;
356
357
  var $from = isShiftPressed && selectUp ? resolvedStartPos : selection.$from;
357
358
  var $to = isShiftPressed && !selectUp ? doc.resolve(resolvedStartPos.pos + 1) : selection.$to;
358
- return expandToBlockRange($from, $to);
359
+ var adjusted = isShiftPressed ? {
360
+ $from: $from,
361
+ $to: $to
362
+ } : adjustSelectionBoundsForEdgePositions($from, $to);
363
+ return expandToBlockRange(adjusted.$from, adjusted.$to);
359
364
  };
360
365
  /**
361
366
  * Updates the transaction with preserved selection logic.
@@ -440,6 +445,24 @@ export var DragHandle = function DragHandle(_ref3) {
440
445
  var start = getPos();
441
446
  var isLayoutColumn = nodeType === 'layoutColumn';
442
447
  var isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true);
448
+
449
+ // Dynamically calculate if node is top-level based on current position (gated by experiment)
450
+ var isTopLevelNodeDynamic = useMemo(function () {
451
+ if (!expValEquals('platform_editor_nested_drag_handle_icon', 'isEnabled', true)) {
452
+ return isTopLevelNode;
453
+ }
454
+ var pos = getPos();
455
+ if (typeof pos === 'number') {
456
+ var $pos = view.state.doc.resolve(pos);
457
+ return ($pos === null || $pos === void 0 ? void 0 : $pos.parent.type.name) === 'doc';
458
+ }
459
+ return true;
460
+ }, [getPos, view.state.doc, isTopLevelNode]);
461
+
462
+ // Use the dynamic value when experiment is on, otherwise use the prop
463
+ // When cleaning up the experiment, you can safely remove the isTopLevelNode as an prop and
464
+ // just rely on the dynamic value (rename it to isTopLevelNode for simplicitiy)
465
+ var isTopLevelNodeValue = expValEquals('platform_editor_nested_drag_handle_icon', 'isEnabled', true) ? isTopLevelNodeDynamic : isTopLevelNode;
443
466
  useEffect(function () {
444
467
  if (editorExperiment('platform_editor_block_control_optimise_render', true)) {
445
468
  return;
@@ -530,7 +553,7 @@ export var DragHandle = function DragHandle(_ref3) {
530
553
  var $anchor = (mSelect === null || mSelect === void 0 ? void 0 : mSelect.anchor) !== undefined ? tr.doc.resolve(mSelect === null || mSelect === void 0 ? void 0 : mSelect.anchor) : tr.selection.$anchor;
531
554
  if (!isMultiSelect || tr.selection.empty || !e.shiftKey) {
532
555
  tr = selectNode(tr, startPos, nodeType, api);
533
- } else if (isTopLevelNode && $anchor.depth <= DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH && e.shiftKey && fg('platform_editor_elements_dnd_shift_click_select')) {
556
+ } else if (isTopLevelNodeValue && $anchor.depth <= DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH && e.shiftKey && fg('platform_editor_elements_dnd_shift_click_select')) {
534
557
  var _api$blockControls3;
535
558
  var alignAnchorHeadToSel = alignAnchorHeadInDirectionOfPos(tr.selection, startPos);
536
559
  var selectionWithExpandedHead = expandSelectionHeadToNodeAtPos(alignAnchorHeadToSel, startPos);
@@ -555,7 +578,7 @@ export var DragHandle = function DragHandle(_ref3) {
555
578
  return tr;
556
579
  });
557
580
  view.focus();
558
- }, [isMultiSelect, api, view, dragHandleSelected, getPos, isTopLevelNode, nodeType]);
581
+ }, [isMultiSelect, api, view, dragHandleSelected, getPos, isTopLevelNodeValue, nodeType]);
559
582
  var handleKeyDown = useCallback(function (e) {
560
583
  // allow user to use spacebar to select the node
561
584
  if (!e.repeat && e.key === ' ') {
@@ -855,18 +878,18 @@ export var DragHandle = function DragHandle(_ref3) {
855
878
  var isEdgeCase = (hasResizer || isExtension || isEmbedCard || isBlockCard) && innerContainer;
856
879
  var isSticky = shouldBeSticky(nodeType);
857
880
  if (supportsAnchor) {
858
- var bottom = editorExperiment('platform_editor_controls', 'variant1') ? getControlBottomCSSValue(safeAnchorName, isSticky, isTopLevelNode, isLayoutColumn) : {};
881
+ var bottom = editorExperiment('platform_editor_controls', 'variant1') ? getControlBottomCSSValue(safeAnchorName, isSticky, isTopLevelNodeValue, isLayoutColumn) : {};
859
882
  return _objectSpread({
860
883
  left: isEdgeCase ? "calc(anchor(".concat(safeAnchorName, " start) + ").concat(getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType), ")") : editorExperiment('advanced_layouts', true) && isLayoutColumn ? "calc((anchor(".concat(safeAnchorName, " right) + anchor(").concat(safeAnchorName, " left))/2 - ").concat(DRAG_HANDLE_HEIGHT / 2, "px)") : "calc(anchor(".concat(safeAnchorName, " start) - ").concat(DRAG_HANDLE_WIDTH, "px - ").concat(dragHandleGap(nodeType, parentNodeType), "px)"),
861
884
  top: editorExperiment('advanced_layouts', true) && isLayoutColumn ? "calc(anchor(".concat(safeAnchorName, " top) - ").concat(DRAG_HANDLE_WIDTH, "px)") : "calc(anchor(".concat(safeAnchorName, " start)+ ").concat(topPositionAdjustment(expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? $pos && $pos.nodeAfter && getNodeTypeWithLevel($pos.nodeAfter) || nodeType : nodeType), "px)")
862
885
  }, bottom);
863
886
  }
864
- var height = editorExperiment('platform_editor_controls', 'variant1') ? getControlHeightCSSValue(getNodeHeight(dom, safeAnchorName, anchorRectCache) || 0, isSticky, isTopLevelNode, "".concat(DRAG_HANDLE_HEIGHT), isLayoutColumn) : {};
887
+ var height = editorExperiment('platform_editor_controls', 'variant1') ? getControlHeightCSSValue(getNodeHeight(dom, safeAnchorName, anchorRectCache) || 0, isSticky, isTopLevelNodeValue, "".concat(DRAG_HANDLE_HEIGHT), isLayoutColumn) : {};
865
888
  return _objectSpread({
866
889
  left: isEdgeCase ? "calc(".concat((dom === null || dom === void 0 ? void 0 : dom.offsetLeft) || 0, "px + ").concat(getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType), ")") : getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType),
867
890
  top: getTopPosition(dom, nodeType)
868
891
  }, height);
869
- }, [anchorName, getPos, view, nodeType, macroInteractionUpdates, anchorRectCache, isTopLevelNode, isLayoutColumn, recalculatePosition]);
892
+ }, [anchorName, getPos, view, nodeType, macroInteractionUpdates, anchorRectCache, isTopLevelNodeValue, isLayoutColumn, recalculatePosition]);
870
893
  var calculatePositionOld = useCallback(function () {
871
894
  var pos = getPos();
872
895
  var $pos = expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? typeof pos === 'number' && view.state.doc.resolve(pos) : pos && view.state.doc.resolve(pos);
@@ -901,18 +924,18 @@ export var DragHandle = function DragHandle(_ref3) {
901
924
  var isEdgeCase = (hasResizer || isExtension || isEmbedCard || isBlockCard) && innerContainer;
902
925
  var isSticky = shouldBeSticky(nodeType);
903
926
  if (supportsAnchor) {
904
- var bottom = editorExperiment('platform_editor_controls', 'variant1') ? getControlBottomCSSValue(safeAnchorName, isSticky, isTopLevelNode, isLayoutColumn) : {};
927
+ var bottom = editorExperiment('platform_editor_controls', 'variant1') ? getControlBottomCSSValue(safeAnchorName, isSticky, isTopLevelNodeValue, isLayoutColumn) : {};
905
928
  return _objectSpread({
906
929
  left: isEdgeCase ? "calc(anchor(".concat(safeAnchorName, " start) + ").concat(getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType), ")") : editorExperiment('advanced_layouts', true) && isLayoutColumn ? "calc((anchor(".concat(safeAnchorName, " right) + anchor(").concat(safeAnchorName, " left))/2 - ").concat(DRAG_HANDLE_HEIGHT / 2, "px)") : "calc(anchor(".concat(safeAnchorName, " start) - ").concat(DRAG_HANDLE_WIDTH, "px - ").concat(dragHandleGap(nodeType, parentNodeType), "px)"),
907
930
  top: editorExperiment('advanced_layouts', true) && isLayoutColumn ? "calc(anchor(".concat(safeAnchorName, " top) - ").concat(DRAG_HANDLE_WIDTH, "px)") : "calc(anchor(".concat(safeAnchorName, " start) + ").concat(topPositionAdjustment(expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? $pos && $pos.nodeAfter && getNodeTypeWithLevel($pos.nodeAfter) || nodeType : nodeType), "px)")
908
931
  }, bottom);
909
932
  }
910
- var height = editorExperiment('platform_editor_controls', 'variant1') ? getControlHeightCSSValue(getNodeHeight(dom, safeAnchorName, anchorRectCache) || 0, isSticky, isTopLevelNode, "".concat(DRAG_HANDLE_HEIGHT), isLayoutColumn) : {};
933
+ var height = editorExperiment('platform_editor_controls', 'variant1') ? getControlHeightCSSValue(getNodeHeight(dom, safeAnchorName, anchorRectCache) || 0, isSticky, isTopLevelNodeValue, "".concat(DRAG_HANDLE_HEIGHT), isLayoutColumn) : {};
911
934
  return _objectSpread({
912
935
  left: isEdgeCase ? "calc(".concat((dom === null || dom === void 0 ? void 0 : dom.offsetLeft) || 0, "px + ").concat(getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType), ")") : getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType),
913
936
  top: getTopPosition(dom, nodeType)
914
937
  }, height);
915
- }, [anchorName, getPos, view, nodeType, blockCardWidth, macroInteractionUpdates, anchorRectCache, isTopLevelNode, isLayoutColumn]);
938
+ }, [anchorName, getPos, view, nodeType, blockCardWidth, macroInteractionUpdates, anchorRectCache, isTopLevelNodeValue, isLayoutColumn]);
916
939
  useEffect(function () {
917
940
  if (editorExperiment('platform_editor_block_control_optimise_render', true)) {
918
941
  return;
@@ -988,12 +1011,12 @@ export var DragHandle = function DragHandle(_ref3) {
988
1011
  }
989
1012
  var mSelect = api === null || api === void 0 || (_api$blockControls$sh4 = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh4 === void 0 ? void 0 : _api$blockControls$sh4.multiSelectDnD;
990
1013
  var $anchor = (mSelect === null || mSelect === void 0 ? void 0 : mSelect.anchor) !== undefined ? view.state.doc.resolve(mSelect === null || mSelect === void 0 ? void 0 : mSelect.anchor) : view.state.selection.$anchor;
991
- if (isShiftDown && (!isTopLevelNode || isTopLevelNode && $anchor.depth > DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH)) {
1014
+ if (isShiftDown && (!isTopLevelNodeValue || isTopLevelNodeValue && $anchor.depth > DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH)) {
992
1015
  setDragHandleDisabled(true);
993
1016
  } else {
994
1017
  setDragHandleDisabled(false);
995
1018
  }
996
- }, [api === null || api === void 0 ? void 0 : api.blockControls.sharedState, isMultiSelect, isShiftDown, isTopLevelNode, view]);
1019
+ }, [api === null || api === void 0 ? void 0 : api.blockControls.sharedState, isMultiSelect, isShiftDown, isTopLevelNodeValue, view]);
997
1020
  var dragHandleMessage = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? formatMessage(blockControlsMessages.dragToMoveClickToOpen, {
998
1021
  br: jsx("br", null)
999
1022
  }) : formatMessage(blockControlsMessages.dragToMove);
@@ -1002,7 +1025,7 @@ export var DragHandle = function DragHandle(_ref3) {
1002
1025
  var dragHandleAriaLabel = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? formatMessage(blockControlsMessages.dragToMoveClickToOpen, {
1003
1026
  br: ' '
1004
1027
  }) : formatMessage(blockControlsMessages.dragToMove);
1005
- var helpDescriptors = isTopLevelNode ? [{
1028
+ var helpDescriptors = isTopLevelNodeValue ? [{
1006
1029
  description: dragHandleMessage
1007
1030
  }, {
1008
1031
  description: formatMessage(blockControlsMessages.moveUp),
@@ -1026,7 +1049,7 @@ export var DragHandle = function DragHandle(_ref3) {
1026
1049
  keymap: dragToMoveDown
1027
1050
  }];
1028
1051
  var isParentNodeOfTypeLayout;
1029
- if (!isTopLevelNode) {
1052
+ if (!isTopLevelNodeValue) {
1030
1053
  var pos = getPos();
1031
1054
  if (typeof pos === 'number') {
1032
1055
  var _$pos$parent;
@@ -1106,7 +1129,7 @@ export var DragHandle = function DragHandle(_ref3) {
1106
1129
  // eslint-disable-next-line @atlaskit/design-system/no-direct-use-of-web-platform-drag-and-drop
1107
1130
  ,
1108
1131
  onDragStart: handleIconDragStart
1109
- }, jsx(DragHandleVerticalIcon, {
1132
+ }, expValEquals('platform_editor_nested_drag_handle_icon', 'isEnabled', true) && !isTopLevelNodeValue ? jsx(DragHandleNestedIcon, null) : jsx(DragHandleVerticalIcon, {
1110
1133
  spacing: "spacious",
1111
1134
  label: "",
1112
1135
  size: "small"
@@ -1122,7 +1145,7 @@ export var DragHandle = function DragHandle(_ref3) {
1122
1145
  as: "span",
1123
1146
  testId: "block-ctrl-drag-handle-container"
1124
1147
  }, jsx("span", {
1125
- css: [tooltipContainerStyles, shouldMaskNodeControls(nodeType, isTopLevelNode) && (expValEquals('platform_editor_table_sticky_header_improvements', 'cohort', 'test_with_overflow') && fg('platform_editor_table_sticky_header_patch_6') ? tooltipContainerStylesImprovedStickyHeaderWithMask : tooltipContainerStylesStickyHeaderWithMask), !shouldMaskNodeControls(nodeType, isTopLevelNode) && tooltipContainerStylesStickyHeaderWithoutMask]
1148
+ css: [tooltipContainerStyles, shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && (expValEquals('platform_editor_table_sticky_header_improvements', 'cohort', 'test_with_overflow') && fg('platform_editor_table_sticky_header_patch_6') ? tooltipContainerStylesImprovedStickyHeaderWithMask : tooltipContainerStylesStickyHeaderWithMask), !shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && tooltipContainerStylesStickyHeaderWithoutMask]
1126
1149
  }, jsx(Tooltip, {
1127
1150
  content: jsx(TooltipContentWithMultipleShortcuts, {
1128
1151
  helpDescriptors: helpDescriptors
@@ -1136,7 +1159,7 @@ export var DragHandle = function DragHandle(_ref3) {
1136
1159
  });
1137
1160
  }
1138
1161
  }, jsx("span", {
1139
- css: [shouldMaskNodeControls(nodeType, isTopLevelNode) && buttonWrapperStyles, buttonWrapperStylesPatch]
1162
+ css: [shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && buttonWrapperStyles, buttonWrapperStylesPatch]
1140
1163
  }, renderButton()))));
1141
1164
  };
1142
1165
  var stickyWithoutTooltip = function stickyWithoutTooltip() {
@@ -1148,9 +1171,9 @@ export var DragHandle = function DragHandle(_ref3) {
1148
1171
  as: "span",
1149
1172
  testId: "block-ctrl-drag-handle-container"
1150
1173
  }, jsx("span", {
1151
- css: [tooltipContainerStyles, shouldMaskNodeControls(nodeType, isTopLevelNode) && tooltipContainerStylesStickyHeaderWithMask, !shouldMaskNodeControls(nodeType, isTopLevelNode) && tooltipContainerStylesStickyHeaderWithoutMask]
1174
+ css: [tooltipContainerStyles, shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && tooltipContainerStylesStickyHeaderWithMask, !shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && tooltipContainerStylesStickyHeaderWithoutMask]
1152
1175
  }, jsx("span", {
1153
- css: [shouldMaskNodeControls(nodeType, isTopLevelNode) && buttonWrapperStyles, buttonWrapperStylesPatch]
1176
+ css: [shouldMaskNodeControls(nodeType, isTopLevelNodeValue) && buttonWrapperStyles, buttonWrapperStylesPatch]
1154
1177
  }, renderButton())));
1155
1178
  };
1156
1179
  var buttonWithTooltip = function buttonWithTooltip() {
@@ -1180,6 +1203,7 @@ export var DragHandleWithVisibility = function DragHandleWithVisibility(_ref10)
1180
1203
  anchorName = _ref10.anchorName,
1181
1204
  nodeType = _ref10.nodeType,
1182
1205
  handleOptions = _ref10.handleOptions,
1206
+ isTopLevelNode = _ref10.isTopLevelNode,
1183
1207
  anchorRectCache = _ref10.anchorRectCache;
1184
1208
  return jsx(VisibilityContainer, {
1185
1209
  api: api
@@ -1191,6 +1215,7 @@ export var DragHandleWithVisibility = function DragHandleWithVisibility(_ref10)
1191
1215
  anchorName: anchorName,
1192
1216
  nodeType: nodeType,
1193
1217
  handleOptions: handleOptions,
1218
+ isTopLevelNode: isTopLevelNode,
1194
1219
  anchorRectCache: anchorRectCache
1195
1220
  }));
1196
1221
  };
@@ -39,6 +39,21 @@ export declare const alignAnchorHeadInDirectionOfPos: (selection: Selection, pos
39
39
  * @returns The mapped selection or undefined if mapping is not possible
40
40
  */
41
41
  export declare const mapPreservedSelection: (selection: Selection, tr: ReadonlyTransaction | Transaction) => Selection | undefined;
42
+ /**
43
+ * Adjust selection bounds to exclude nodes where the selection only touches
44
+ * the edge position without selecting any content.
45
+ *
46
+ * Exception: Don't adjust if the selection is inside an empty textblock,
47
+ * as we want to include empty paragraphs in block operations.
48
+ *
49
+ * @param $from The resolved position of the start of the selection
50
+ * @param $to The resolved position of the end of the selection
51
+ * @returns Adjusted $from and $to positions
52
+ */
53
+ export declare const adjustSelectionBoundsForEdgePositions: ($from: ResolvedPos, $to: ResolvedPos) => {
54
+ $from: ResolvedPos;
55
+ $to: ResolvedPos;
56
+ };
42
57
  /**
43
58
  * Creates a preserved selection which is expanded to block boundaries.
44
59
  *
@@ -0,0 +1,12 @@
1
+ /** @jsxRuntime classic */
2
+ /**
3
+ * @jsxRuntime classic
4
+ * @jsx jsx
5
+ */
6
+ import { jsx } from '@emotion/react';
7
+ /**
8
+ * Custom 3-dot vertical drag handle icon for nested nodes.
9
+ * Similar to DragHandleVerticalIcon but with only 3 dots instead of 6.
10
+ * Hardcoded to medium size with spacious spacing.
11
+ */
12
+ export declare const DragHandleNestedIcon: () => jsx.JSX.Element;
@@ -16,5 +16,5 @@ type DragHandleProps = {
16
16
  view: EditorView;
17
17
  };
18
18
  export declare const DragHandle: ({ view, api, formatMessage, getPos, anchorName, nodeType, handleOptions, isTopLevelNode, anchorRectCache, }: DragHandleProps) => jsx.JSX.Element;
19
- export declare const DragHandleWithVisibility: ({ view, api, formatMessage, getPos, anchorName, nodeType, handleOptions, anchorRectCache, }: DragHandleProps) => jsx.JSX.Element;
19
+ export declare const DragHandleWithVisibility: ({ view, api, formatMessage, getPos, anchorName, nodeType, handleOptions, isTopLevelNode, anchorRectCache, }: DragHandleProps) => jsx.JSX.Element;
20
20
  export {};