@atlaskit/editor-plugin-block-controls 7.11.1 → 7.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,14 +4,14 @@ import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state
4
4
  import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
5
5
  import { selectTableClosestToPos } from '@atlaskit/editor-tables/utils';
6
6
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
7
- export var getInlineNodePos = function getInlineNodePos(tr, start, nodeSize) {
8
- var $startPos = tr.doc.resolve(start);
7
+ export var getInlineNodePos = function getInlineNodePos(doc, start, nodeSize) {
8
+ var $startPos = doc.resolve(start);
9
9
  // To trigger the annotation floating toolbar for non-selectable node, we need to select inline nodes
10
10
  // Find the first inline node in the node
11
11
  var inlineNodePos = start;
12
12
  var foundInlineNode = false;
13
13
  var inlineNodeEndPos = 0;
14
- tr.doc.nodesBetween($startPos.pos, $startPos.pos + nodeSize, function (n, pos) {
14
+ doc.nodesBetween($startPos.pos, $startPos.pos + nodeSize, function (n, pos) {
15
15
  if (n.isInline) {
16
16
  inlineNodeEndPos = pos + n.nodeSize;
17
17
  }
@@ -36,10 +36,10 @@ export var isNodeWithCodeBlock = function isNodeWithCodeBlock(tr, start, nodeSiz
36
36
  });
37
37
  return hasCodeBlock;
38
38
  };
39
- var isNodeWithMediaOrExtension = function isNodeWithMediaOrExtension(tr, start, nodeSize) {
40
- var $startPos = tr.doc.resolve(start);
39
+ var isNodeWithMediaOrExtension = function isNodeWithMediaOrExtension(doc, start, nodeSize) {
40
+ var $startPos = doc.resolve(start);
41
41
  var hasMediaOrExtension = false;
42
- tr.doc.nodesBetween($startPos.pos, $startPos.pos + nodeSize, function (n) {
42
+ doc.nodesBetween($startPos.pos, $startPos.pos + nodeSize, function (n) {
43
43
  if (['media', 'extension'].includes(n.type.name)) {
44
44
  hasMediaOrExtension = true;
45
45
  }
@@ -52,8 +52,8 @@ var oldGetSelection = function oldGetSelection(tr, start) {
52
52
  var nodeSize = node ? node.nodeSize : 1;
53
53
  var $startPos = tr.doc.resolve(start);
54
54
  var nodeName = node === null || node === void 0 ? void 0 : node.type.name;
55
- var isBlockQuoteWithMediaOrExtension = nodeName === 'blockquote' && isNodeWithMediaOrExtension(tr, start, nodeSize);
56
- var isListWithMediaOrExtension = nodeName === 'bulletList' && isNodeWithMediaOrExtension(tr, start, nodeSize) || nodeName === 'orderedList' && isNodeWithMediaOrExtension(tr, start, nodeSize);
55
+ var isBlockQuoteWithMediaOrExtension = nodeName === 'blockquote' && isNodeWithMediaOrExtension(tr.doc, start, nodeSize);
56
+ var isListWithMediaOrExtension = nodeName === 'bulletList' && isNodeWithMediaOrExtension(tr.doc, start, nodeSize) || nodeName === 'orderedList' && isNodeWithMediaOrExtension(tr.doc, start, nodeSize);
57
57
  if (isNodeSelection && nodeName !== 'blockquote' || isListWithMediaOrExtension || isBlockQuoteWithMediaOrExtension ||
58
58
  // decisionList/layoutColumn node is not selectable, but we want to select the whole node not just text
59
59
  ['decisionList', 'layoutColumn'].includes(nodeName || '')) {
@@ -69,55 +69,55 @@ var oldGetSelection = function oldGetSelection(tr, start) {
69
69
  } else if (nodeName === 'taskList') {
70
70
  return TextSelection.create(tr.doc, start, start + nodeSize);
71
71
  } else {
72
- var _getInlineNodePos = getInlineNodePos(tr, start, nodeSize),
72
+ var _getInlineNodePos = getInlineNodePos(tr.doc, start, nodeSize),
73
73
  inlineNodePos = _getInlineNodePos.inlineNodePos,
74
74
  inlineNodeEndPos = _getInlineNodePos.inlineNodeEndPos;
75
75
  return new TextSelection(tr.doc.resolve(inlineNodePos), tr.doc.resolve(inlineNodeEndPos));
76
76
  }
77
77
  };
78
- var newGetSelection = function newGetSelection(tr, start) {
79
- var node = tr.doc.nodeAt(start);
78
+ var newGetSelection = function newGetSelection(doc, selectionEmpty, start) {
79
+ var node = doc.nodeAt(start);
80
80
  var isNodeSelection = node && NodeSelection.isSelectable(node);
81
81
  var nodeSize = node ? node.nodeSize : 1;
82
82
  var nodeName = node === null || node === void 0 ? void 0 : node.type.name;
83
83
 
84
84
  // this is a fix for empty paragraph selection - put first to avoid any extra work
85
- if (nodeName === 'paragraph' && tr.selection.empty && (node === null || node === void 0 ? void 0 : node.childCount) === 0 && !expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
85
+ if (nodeName === 'paragraph' && selectionEmpty && (node === null || node === void 0 ? void 0 : node.childCount) === 0 && !expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
86
86
  return false;
87
87
  }
88
- var isParagraphHeadingEmpty = ['paragraph', 'heading'].includes(nodeName || '') && tr.selection.empty && (node === null || node === void 0 ? void 0 : node.childCount) === 0;
88
+ var isParagraphHeadingEmpty = ['paragraph', 'heading'].includes(nodeName || '') && selectionEmpty && (node === null || node === void 0 ? void 0 : node.childCount) === 0;
89
89
  var isBlockQuoteEmpty = nodeName === 'blockquote' && (node === null || node === void 0 ? void 0 : node.textContent) === '';
90
- var isListEmpty = ['orderedList', 'bulletList', 'taskList'].includes(nodeName || '') && tr.selection.empty && (node === null || node === void 0 ? void 0 : node.textContent) === '';
90
+ var isListEmpty = ['orderedList', 'bulletList', 'taskList'].includes(nodeName || '') && selectionEmpty && (node === null || node === void 0 ? void 0 : node.textContent) === '';
91
91
 
92
92
  // if block menu and empty line format menu are enabled,
93
93
  // we want to set the selection to avoid the selection goes to the top of the document
94
94
  if ((isParagraphHeadingEmpty || isBlockQuoteEmpty || isListEmpty) && expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
95
- return TextSelection.create(tr.doc, start + 1, start + 1);
95
+ return TextSelection.create(doc, start + 1, start + 1);
96
96
  }
97
- var isBlockQuoteWithMediaOrExtension = nodeName === 'blockquote' && isNodeWithMediaOrExtension(tr, start, nodeSize);
98
- var isListWithMediaOrExtension = nodeName === 'bulletList' && isNodeWithMediaOrExtension(tr, start, nodeSize) || nodeName === 'orderedList' && isNodeWithMediaOrExtension(tr, start, nodeSize);
97
+ var isBlockQuoteWithMediaOrExtension = nodeName === 'blockquote' && isNodeWithMediaOrExtension(doc, start, nodeSize);
98
+ var isListWithMediaOrExtension = nodeName === 'bulletList' && isNodeWithMediaOrExtension(doc, start, nodeSize) || nodeName === 'orderedList' && isNodeWithMediaOrExtension(doc, start, nodeSize);
99
99
  if (isNodeSelection && nodeName !== 'blockquote' || isListWithMediaOrExtension || isBlockQuoteWithMediaOrExtension ||
100
100
  // decisionList/layoutColumn node is not selectable, but we want to select the whole node not just text
101
101
  ['decisionList', 'layoutColumn'].includes(nodeName || '') || nodeName === 'mediaGroup' && typeof (node === null || node === void 0 ? void 0 : node.childCount) === 'number' && (node === null || node === void 0 ? void 0 : node.childCount) > 1) {
102
- return new NodeSelection(tr.doc.resolve(start));
102
+ return new NodeSelection(doc.resolve(start));
103
103
  }
104
104
 
105
105
  // if mediaGroup only has a single child, we want to select the child
106
106
  if (nodeName === 'mediaGroup') {
107
- var $mediaStartPos = tr.doc.resolve(start + 1);
107
+ var $mediaStartPos = doc.resolve(start + 1);
108
108
  return new NodeSelection($mediaStartPos);
109
109
  }
110
110
  if (nodeName === 'taskList' && !expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
111
- return TextSelection.create(tr.doc, start, start + nodeSize);
111
+ return TextSelection.create(doc, start, start + nodeSize);
112
112
  }
113
- var _getInlineNodePos2 = getInlineNodePos(tr, start, nodeSize),
113
+ var _getInlineNodePos2 = getInlineNodePos(doc, start, nodeSize),
114
114
  inlineNodePos = _getInlineNodePos2.inlineNodePos,
115
115
  inlineNodeEndPos = _getInlineNodePos2.inlineNodeEndPos;
116
- return new TextSelection(tr.doc.resolve(inlineNodePos), tr.doc.resolve(inlineNodeEndPos));
116
+ return new TextSelection(doc.resolve(inlineNodePos), doc.resolve(inlineNodeEndPos));
117
117
  };
118
118
  export var getSelection = function getSelection(tr, start, api) {
119
119
  if (areToolbarFlagsEnabled(Boolean(api === null || api === void 0 ? void 0 : api.toolbar))) {
120
- return newGetSelection(tr, start);
120
+ return newGetSelection(tr.doc, tr.selection.empty, start);
121
121
  }
122
122
  return oldGetSelection(tr, start);
123
123
  };
@@ -152,7 +152,7 @@ export var setCursorPositionAtMovedNode = function setCursorPositionAtMovedNode(
152
152
  tr.setSelection(_selection);
153
153
  return tr;
154
154
  }
155
- var _getInlineNodePos3 = getInlineNodePos(tr, start, nodeSize),
155
+ var _getInlineNodePos3 = getInlineNodePos(tr.doc, start, nodeSize),
156
156
  inlineNodeEndPos = _getInlineNodePos3.inlineNodeEndPos;
157
157
  selection = new TextSelection(tr.doc.resolve(inlineNodeEndPos));
158
158
  tr.setSelection(selection);
@@ -214,4 +214,30 @@ export var rootTaskListDepth = function rootTaskListDepth(taskListPos) {
214
214
  }
215
215
  }
216
216
  return depth;
217
+ };
218
+
219
+ /**
220
+ * Collapses the given $from and $to resolved positions to the nearest valid selection range.
221
+ *
222
+ * Will retract the from and to positions to nearest inline positions at node boundaries only if needed.
223
+ *
224
+ * @param $from the resolved start position
225
+ * @param $to the resolved end position
226
+ * @returns An object containing the collapsed $from and $to resolved positions
227
+ */
228
+ export var collapseToSelectionRange = function collapseToSelectionRange($from, $to) {
229
+ var _$to$nodeBefore$nodeS, _$to$nodeBefore;
230
+ // Get the selections that would be made for the first and last node in the range
231
+ // We re-use the getSelection logic as it already handles various node types and edge cases
232
+ // always pass true for selectionEmpty to emulate a cursor selection within the node
233
+ var firstNodeSelection = newGetSelection($from.doc, true, $from.pos);
234
+ var lastNodeSize = (_$to$nodeBefore$nodeS = (_$to$nodeBefore = $to.nodeBefore) === null || _$to$nodeBefore === void 0 ? void 0 : _$to$nodeBefore.nodeSize) !== null && _$to$nodeBefore$nodeS !== void 0 ? _$to$nodeBefore$nodeS : 0;
235
+ var lastNodeStartPos = $to.pos - lastNodeSize;
236
+ var lastNodeSelection = newGetSelection($from.doc, true, lastNodeStartPos);
237
+
238
+ // Return a selection spanning from the start of the first node selection to the end of the last node selection
239
+ return {
240
+ $from: $from.doc.resolve(firstNodeSelection ? firstNodeSelection.from : $from.pos),
241
+ $to: $to.doc.resolve(lastNodeSelection ? lastNodeSelection.to : $to.pos)
242
+ };
217
243
  };
@@ -41,7 +41,7 @@ import { key } from '../pm-plugins/main';
41
41
  import { selectionPreservationPluginKey } from '../pm-plugins/selection-preservation/plugin-key';
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
- import { isHandleCorrelatedToSelection, selectNode } from '../pm-plugins/utils/getSelection';
44
+ import { collapseToSelectionRange, isHandleCorrelatedToSelection, selectNode } from '../pm-plugins/utils/getSelection';
45
45
  import { 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, topPositionAdjustment } from './consts';
47
47
  import { dragPreview } from './drag-preview';
@@ -434,7 +434,10 @@ export var DragHandle = function DragHandle(_ref2) {
434
434
 
435
435
  // Set selection to expanded selection range if it encompases the clicked drag handle
436
436
  if (range && isPosWithinRange(startPos, range) && isMultiNodeRange(range)) {
437
- tr.setSelection(TextSelection.create(tr.doc, Math.min(selection.from, range.start), Math.max(selection.to, range.end)));
437
+ var collapsed = collapseToSelectionRange(tr.doc.resolve(range.start), tr.doc.resolve(range.end));
438
+
439
+ // Then create a selection from the start of the first node to the end of the last node
440
+ 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)));
438
441
  } else {
439
442
  // Select the clicked drag handle's node only
440
443
  tr = selectNode(tr, startPos, nodeType, api);
@@ -162,6 +162,7 @@ export type BlockControlsMeta = {
162
162
  editorBlurred: boolean;
163
163
  editorHeight: number;
164
164
  nodeMoved: boolean;
165
+ nodeMovedOffset: number;
165
166
  type: string;
166
167
  };
167
168
  export type MoveNodeMethod = INPUT_METHOD.DRAG_AND_DROP | INPUT_METHOD.SHORTCUT | INPUT_METHOD.BLOCK_MENU;
@@ -5,7 +5,7 @@ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
5
5
  import type { EditorState, ReadonlyTransaction } from '@atlaskit/editor-prosemirror/state';
6
6
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
7
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
8
- import type { ActiveDropTargetNode, BlockControlsPlugin, PluginState } from '../blockControlsPluginType';
8
+ import type { ActiveDropTargetNode, BlockControlsMeta, BlockControlsPlugin, PluginState } from '../blockControlsPluginType';
9
9
  import { AnchorRectCache } from './utils/anchor-utils';
10
10
  export declare const key: PluginKey<PluginState>;
11
11
  export interface FlagType {
@@ -61,3 +61,4 @@ export declare const createPlugin: (api: ExtractInjectionAPI<BlockControlsPlugin
61
61
  lastDragCancelled: any;
62
62
  isSelectedViaDragHandle: any;
63
63
  }>;
64
+ export declare const getBlockControlsMeta: (tr: ReadonlyTransaction) => BlockControlsMeta | undefined;
@@ -1,8 +1,8 @@
1
1
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
- import { type ResolvedPos } from '@atlaskit/editor-prosemirror/model';
3
- import { type EditorState, NodeSelection, TextSelection, type Transaction, type Selection } from '@atlaskit/editor-prosemirror/state';
2
+ import { type Node as PMNode, type ResolvedPos } from '@atlaskit/editor-prosemirror/model';
3
+ import { type EditorState, NodeSelection, type Selection, TextSelection, type Transaction } from '@atlaskit/editor-prosemirror/state';
4
4
  import type { BlockControlsPlugin } from '../../blockControlsPluginType';
5
- export declare const getInlineNodePos: (tr: Transaction, start: number, nodeSize: number) => {
5
+ export declare const getInlineNodePos: (doc: PMNode, start: number, nodeSize: number) => {
6
6
  inlineNodeEndPos: number;
7
7
  inlineNodePos: number;
8
8
  };
@@ -20,3 +20,16 @@ export declare const setCursorPositionAtMovedNode: (tr: Transaction, start: numb
20
20
  export declare const isHandleCorrelatedToSelection: (state: EditorState, selection: Selection, handlePos: number) => boolean;
21
21
  export declare const rootListDepth: (itemPos: ResolvedPos) => number | undefined;
22
22
  export declare const rootTaskListDepth: (taskListPos: ResolvedPos) => number | undefined;
23
+ /**
24
+ * Collapses the given $from and $to resolved positions to the nearest valid selection range.
25
+ *
26
+ * Will retract the from and to positions to nearest inline positions at node boundaries only if needed.
27
+ *
28
+ * @param $from the resolved start position
29
+ * @param $to the resolved end position
30
+ * @returns An object containing the collapsed $from and $to resolved positions
31
+ */
32
+ export declare const collapseToSelectionRange: ($from: ResolvedPos, $to: ResolvedPos) => {
33
+ $from: ResolvedPos;
34
+ $to: ResolvedPos;
35
+ };
@@ -162,6 +162,7 @@ export type BlockControlsMeta = {
162
162
  editorBlurred: boolean;
163
163
  editorHeight: number;
164
164
  nodeMoved: boolean;
165
+ nodeMovedOffset: number;
165
166
  type: string;
166
167
  };
167
168
  export type MoveNodeMethod = INPUT_METHOD.DRAG_AND_DROP | INPUT_METHOD.SHORTCUT | INPUT_METHOD.BLOCK_MENU;
@@ -5,7 +5,7 @@ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
5
5
  import type { EditorState, ReadonlyTransaction } from '@atlaskit/editor-prosemirror/state';
6
6
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
7
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
8
- import type { ActiveDropTargetNode, BlockControlsPlugin, PluginState } from '../blockControlsPluginType';
8
+ import type { ActiveDropTargetNode, BlockControlsMeta, BlockControlsPlugin, PluginState } from '../blockControlsPluginType';
9
9
  import { AnchorRectCache } from './utils/anchor-utils';
10
10
  export declare const key: PluginKey<PluginState>;
11
11
  export interface FlagType {
@@ -61,3 +61,4 @@ export declare const createPlugin: (api: ExtractInjectionAPI<BlockControlsPlugin
61
61
  lastDragCancelled: any;
62
62
  isSelectedViaDragHandle: any;
63
63
  }>;
64
+ export declare const getBlockControlsMeta: (tr: ReadonlyTransaction) => BlockControlsMeta | undefined;
@@ -1,8 +1,8 @@
1
1
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
- import { type ResolvedPos } from '@atlaskit/editor-prosemirror/model';
3
- import { type EditorState, NodeSelection, TextSelection, type Transaction, type Selection } from '@atlaskit/editor-prosemirror/state';
2
+ import { type Node as PMNode, type ResolvedPos } from '@atlaskit/editor-prosemirror/model';
3
+ import { type EditorState, NodeSelection, type Selection, TextSelection, type Transaction } from '@atlaskit/editor-prosemirror/state';
4
4
  import type { BlockControlsPlugin } from '../../blockControlsPluginType';
5
- export declare const getInlineNodePos: (tr: Transaction, start: number, nodeSize: number) => {
5
+ export declare const getInlineNodePos: (doc: PMNode, start: number, nodeSize: number) => {
6
6
  inlineNodeEndPos: number;
7
7
  inlineNodePos: number;
8
8
  };
@@ -20,3 +20,16 @@ export declare const setCursorPositionAtMovedNode: (tr: Transaction, start: numb
20
20
  export declare const isHandleCorrelatedToSelection: (state: EditorState, selection: Selection, handlePos: number) => boolean;
21
21
  export declare const rootListDepth: (itemPos: ResolvedPos) => number | undefined;
22
22
  export declare const rootTaskListDepth: (taskListPos: ResolvedPos) => number | undefined;
23
+ /**
24
+ * Collapses the given $from and $to resolved positions to the nearest valid selection range.
25
+ *
26
+ * Will retract the from and to positions to nearest inline positions at node boundaries only if needed.
27
+ *
28
+ * @param $from the resolved start position
29
+ * @param $to the resolved end position
30
+ * @returns An object containing the collapsed $from and $to resolved positions
31
+ */
32
+ export declare const collapseToSelectionRange: ($from: ResolvedPos, $to: ResolvedPos) => {
33
+ $from: ResolvedPos;
34
+ $to: ResolvedPos;
35
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-controls",
3
- "version": "7.11.1",
3
+ "version": "7.11.3",
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.4.0",
31
+ "@atlaskit/adf-schema": "^51.5.1",
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",
@@ -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": "^14.4.0",
57
+ "@atlaskit/tmp-editor-statsig": "^14.5.0",
58
58
  "@atlaskit/tokens": "^8.4.0",
59
59
  "@atlaskit/tooltip": "^20.10.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.36.0",
69
+ "@atlaskit/editor-common": "^110.38.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"