@atlaskit/editor-plugin-block-controls 8.0.6 → 8.0.8
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 +16 -0
- package/dist/cjs/pm-plugins/selection-preservation/pm-plugin.js +25 -18
- package/dist/cjs/pm-plugins/selection-preservation/utils.js +49 -1
- package/dist/cjs/pm-plugins/utils/getSelection.js +11 -2
- package/dist/cjs/pm-plugins/utils/selection.js +45 -30
- package/dist/es2019/pm-plugins/selection-preservation/pm-plugin.js +27 -20
- package/dist/es2019/pm-plugins/selection-preservation/utils.js +48 -0
- package/dist/es2019/pm-plugins/utils/getSelection.js +10 -1
- package/dist/es2019/pm-plugins/utils/selection.js +47 -32
- package/dist/esm/pm-plugins/selection-preservation/pm-plugin.js +27 -20
- package/dist/esm/pm-plugins/selection-preservation/utils.js +48 -0
- package/dist/esm/pm-plugins/utils/getSelection.js +10 -1
- package/dist/esm/pm-plugins/utils/selection.js +45 -30
- package/dist/types/pm-plugins/selection-preservation/utils.d.ts +29 -1
- package/dist/types/pm-plugins/utils/getSelection.d.ts +9 -0
- package/dist/types/pm-plugins/utils/selection.d.ts +15 -1
- package/dist/types-ts4.5/pm-plugins/selection-preservation/utils.d.ts +29 -1
- package/dist/types-ts4.5/pm-plugins/utils/getSelection.d.ts +9 -0
- package/dist/types-ts4.5/pm-plugins/utils/selection.d.ts +15 -1
- package/package.json +4 -4
- package/dist/cjs/ui/drag-handle-menu.js +0 -21
- package/dist/es2019/ui/drag-handle-menu.js +0 -16
- package/dist/esm/ui/drag-handle-menu.js +0 -14
- package/dist/types/ui/drag-handle-menu.d.ts +0 -6
- package/dist/types-ts4.5/ui/drag-handle-menu.d.ts +0 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-block-controls
|
|
2
2
|
|
|
3
|
+
## 8.0.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`eb7609ee331ab`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/eb7609ee331ab) -
|
|
8
|
+
[ux] EDITOR-4264 Fix preserved selection mapping
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
|
|
11
|
+
## 8.0.7
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [`81c5041650743`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/81c5041650743) -
|
|
16
|
+
EDITOR-4284 Fix move down visual selection persisting
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
|
|
3
19
|
## 8.0.6
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.createSelectionPreservationPlugin = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
-
var _monitoring = require("@atlaskit/editor-common/monitoring");
|
|
10
9
|
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
|
|
11
10
|
var _styles = require("@atlaskit/editor-common/styles");
|
|
12
11
|
var _selection = require("../utils/selection");
|
|
@@ -59,14 +58,13 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
|
|
|
59
58
|
var meta = (0, _utils.getSelectionPreservationMeta)(tr);
|
|
60
59
|
var newState = _objectSpread({}, pluginState);
|
|
61
60
|
if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
|
|
62
|
-
newState.preservedSelection = (0, _selection.
|
|
61
|
+
newState.preservedSelection = (0, _selection.createPreservedSelection)(tr.doc.resolve(tr.selection.from), tr.doc.resolve(tr.selection.to));
|
|
63
62
|
} else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
|
|
64
63
|
newState.preservedSelection = undefined;
|
|
65
|
-
}
|
|
66
|
-
if (newState.preservedSelection && tr.docChanged) {
|
|
64
|
+
} else if (newState.preservedSelection && tr.docChanged) {
|
|
67
65
|
newState.preservedSelection = (0, _selection.mapPreservedSelection)(newState.preservedSelection, tr);
|
|
68
66
|
}
|
|
69
|
-
if (newState.preservedSelection
|
|
67
|
+
if (!(0, _utils.compareSelections)(newState.preservedSelection, pluginState.preservedSelection)) {
|
|
70
68
|
if (newState !== null && newState !== void 0 && newState.preservedSelection) {
|
|
71
69
|
var _api$selection;
|
|
72
70
|
api === null || api === void 0 || api.core.actions.execute(api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || (_api$selection = _api$selection.commands) === null || _api$selection === void 0 ? void 0 : _api$selection.setBlockSelection(newState.preservedSelection));
|
|
@@ -80,32 +78,41 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
|
|
|
80
78
|
},
|
|
81
79
|
appendTransaction: function appendTransaction(transactions, _oldState, newState) {
|
|
82
80
|
var pluginState = _pluginKey.selectionPreservationPluginKey.getState(newState);
|
|
83
|
-
var
|
|
84
|
-
|
|
81
|
+
var preservedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
|
|
82
|
+
var stateSel = newState.selection;
|
|
83
|
+
if (!preservedSel) {
|
|
85
84
|
return null;
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
// Auto-stop if user explicitly changes selection or selection is set within a code block
|
|
89
|
-
if ((0, _utils.hasUserSelectionChange)(transactions) || (0, _utils.isSelectionWithinCodeBlock)(
|
|
90
|
-
// Auto-stop if user explicitly changes selection
|
|
88
|
+
if ((0, _utils.hasUserSelectionChange)(transactions) || (0, _utils.isSelectionWithinCodeBlock)(stateSel)) {
|
|
91
89
|
return (0, _editorCommands.stopPreservingSelection)({
|
|
92
90
|
tr: newState.tr
|
|
93
91
|
});
|
|
94
92
|
}
|
|
95
|
-
var
|
|
96
|
-
var
|
|
97
|
-
var selectionInvalid = savedSel.from < 0 || savedSel.to > newState.doc.content.size;
|
|
93
|
+
var selectionUnchanged = stateSel.from === preservedSel.from && stateSel.to === preservedSel.to;
|
|
94
|
+
var selectionInvalid = preservedSel.from < 0 || preservedSel.to > newState.doc.content.size;
|
|
98
95
|
if (selectionUnchanged || selectionInvalid) {
|
|
99
96
|
return null;
|
|
100
97
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
var newSelection = (0, _selection.createPreservedSelection)(newState.doc.resolve(preservedSel.from), newState.doc.resolve(preservedSel.to));
|
|
99
|
+
|
|
100
|
+
// If selection becomes invalid, stop preserving
|
|
101
|
+
if (!newSelection) {
|
|
102
|
+
return (0, _editorCommands.stopPreservingSelection)({
|
|
103
|
+
tr: newState.tr
|
|
106
104
|
});
|
|
107
105
|
}
|
|
108
|
-
return
|
|
106
|
+
return newState.tr.setSelection(newSelection);
|
|
107
|
+
},
|
|
108
|
+
view: function view() {
|
|
109
|
+
return {
|
|
110
|
+
update: function update(view, prevState) {
|
|
111
|
+
if ((0, _utils.isPreservedSelectionChanged)(view.state, prevState)) {
|
|
112
|
+
(0, _utils.syncDOMSelection)(view.state.selection);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
109
116
|
},
|
|
110
117
|
props: {
|
|
111
118
|
handleClick: function handleClick(view, pos, event) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.isSelectionWithinCodeBlock = exports.hasUserSelectionChange = exports.getSelectionPreservationMeta = void 0;
|
|
6
|
+
exports.syncDOMSelection = exports.isSelectionWithinCodeBlock = exports.isPreservedSelectionChanged = exports.hasUserSelectionChange = exports.getSelectionPreservationMeta = exports.compareSelections = void 0;
|
|
7
7
|
var _pluginKey = require("./plugin-key");
|
|
8
8
|
/**
|
|
9
9
|
* Detects if any of the transactions include user-driven selection changes.
|
|
@@ -30,4 +30,52 @@ var isSelectionWithinCodeBlock = exports.isSelectionWithinCodeBlock = function i
|
|
|
30
30
|
var $from = _ref.$from,
|
|
31
31
|
$to = _ref.$to;
|
|
32
32
|
return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Compares two selections for equality based on their from and to positions.
|
|
37
|
+
*
|
|
38
|
+
* @param a The first selection to compare.
|
|
39
|
+
* @param b The second selection to compare.
|
|
40
|
+
* @returns True if both selections are equal, otherwise false.
|
|
41
|
+
*/
|
|
42
|
+
var compareSelections = exports.compareSelections = function compareSelections(a, b) {
|
|
43
|
+
return (a === null || a === void 0 ? void 0 : a.from) === (b === null || b === void 0 ? void 0 : b.from) && (a === null || a === void 0 ? void 0 : a.to) === (b === null || b === void 0 ? void 0 : b.to);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns true/false indicating whether the preserved selection
|
|
48
|
+
* has changed between the old and new editor states.
|
|
49
|
+
*
|
|
50
|
+
* @param newState The new editor state.
|
|
51
|
+
* @param oldState The old editor state.
|
|
52
|
+
* @returns True if the preserved selection has changed, otherwise false.
|
|
53
|
+
*/
|
|
54
|
+
var isPreservedSelectionChanged = exports.isPreservedSelectionChanged = function isPreservedSelectionChanged(newState, oldState) {
|
|
55
|
+
var _selectionPreservatio, _selectionPreservatio2;
|
|
56
|
+
var prev = (_selectionPreservatio = _pluginKey.selectionPreservationPluginKey.getState(oldState)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection;
|
|
57
|
+
var curr = (_selectionPreservatio2 = _pluginKey.selectionPreservationPluginKey.getState(newState)) === null || _selectionPreservatio2 === void 0 ? void 0 : _selectionPreservatio2.preservedSelection;
|
|
58
|
+
return !!prev && !!curr && !compareSelections(prev, curr);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Triggers a DOM selection sync by resetting the current native selection range
|
|
63
|
+
* only if it is out of sync with the provided ProseMirror selection state.
|
|
64
|
+
*
|
|
65
|
+
* This is a necessary workaround to ensure the browser's native selection state
|
|
66
|
+
* stays in sync with the preserved selection, particularly after transactions
|
|
67
|
+
* that shift document content.
|
|
68
|
+
*
|
|
69
|
+
* @param selection The current ProseMirror selection state to compare against.
|
|
70
|
+
*/
|
|
71
|
+
var syncDOMSelection = exports.syncDOMSelection = function syncDOMSelection(selection) {
|
|
72
|
+
var domSelection = window.getSelection();
|
|
73
|
+
var domRange = domSelection && domSelection.rangeCount === 1 && domSelection.getRangeAt(0).cloneRange();
|
|
74
|
+
var isOutOfSync = domRange && (selection.from !== domRange.startOffset || selection.to !== domRange.endOffset);
|
|
75
|
+
if (isOutOfSync) {
|
|
76
|
+
// Force the DOM selection to refresh, setting it to the same range
|
|
77
|
+
// This will trigger ProseMirror to re-apply its selection logic based on the current state
|
|
78
|
+
domSelection.removeAllRanges();
|
|
79
|
+
domSelection.addRange(domRange);
|
|
80
|
+
}
|
|
33
81
|
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.setCursorPositionAtMovedNode = exports.selectNode = exports.rootTaskListDepth = exports.rootListDepth = exports.isNodeWithCodeBlock = exports.isHandleCorrelatedToSelection = exports.getSelection = exports.getInlineNodePos = void 0;
|
|
6
|
+
exports.setCursorPositionAtMovedNode = exports.selectNode = exports.rootTaskListDepth = exports.rootListDepth = exports.newGetSelection = exports.isNodeWithCodeBlock = exports.isHandleCorrelatedToSelection = exports.getSelection = exports.getInlineNodePos = void 0;
|
|
7
7
|
var _selection2 = require("@atlaskit/editor-common/selection");
|
|
8
8
|
var _toolbarFlagCheck = require("@atlaskit/editor-common/toolbar-flag-check");
|
|
9
9
|
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
@@ -81,7 +81,16 @@ var oldGetSelection = function oldGetSelection(tr, start) {
|
|
|
81
81
|
return new _state.TextSelection(tr.doc.resolve(inlineNodePos), tr.doc.resolve(inlineNodeEndPos));
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
|
-
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Gets the appropriate selection for the node at the given start position.
|
|
87
|
+
*
|
|
88
|
+
* @param doc The ProseMirror document.
|
|
89
|
+
* @param selectionEmpty Indicates if the current selection is empty.
|
|
90
|
+
* @param start The start position of the node.
|
|
91
|
+
* @returns The appropriate selection for the node.
|
|
92
|
+
*/
|
|
93
|
+
var newGetSelection = exports.newGetSelection = function newGetSelection(doc, selectionEmpty, start) {
|
|
85
94
|
var node = doc.nodeAt(start);
|
|
86
95
|
var isNodeSelection = node && _state.NodeSelection.isSelectable(node);
|
|
87
96
|
var nodeSize = node ? node.nodeSize : 1;
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.mapPreservedSelection = exports.getSelectedSlicePosition = exports.getMultiSelectionIfPosInside = exports.expandSelectionHeadToNodeAtPos = exports.alignAnchorHeadInDirectionOfPos = void 0;
|
|
6
|
+
exports.mapPreservedSelection = exports.getSelectedSlicePosition = exports.getMultiSelectionIfPosInside = exports.expandSelectionHeadToNodeAtPos = exports.createPreservedSelection = exports.alignAnchorHeadInDirectionOfPos = void 0;
|
|
7
7
|
var _selection = require("@atlaskit/editor-common/selection");
|
|
8
8
|
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
9
|
+
var _utils = require("@atlaskit/editor-tables/utils");
|
|
9
10
|
var _main = require("../main");
|
|
11
|
+
var _getSelection = require("./getSelection");
|
|
10
12
|
var getMultiSelectionIfPosInside = exports.getMultiSelectionIfPosInside = function getMultiSelectionIfPosInside(api, pos, tr) {
|
|
11
13
|
var _api$blockControls, _pluginState$multiSel, _tr$getMeta;
|
|
12
14
|
var pluginState = api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState();
|
|
@@ -91,38 +93,51 @@ var mapPreservedSelection = exports.mapPreservedSelection = function mapPreserve
|
|
|
91
93
|
var _ref = (0, _main.getBlockControlsMeta)(tr) || {},
|
|
92
94
|
preservedSelectionMapping = _ref.preservedSelectionMapping;
|
|
93
95
|
var mapping = preservedSelectionMapping || tr.mapping;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// selection always includes whole nodes
|
|
105
|
-
var expanded = (0, _selection.expandToBlockRange)(tr.doc.resolve(from), tr.doc.resolve(to));
|
|
96
|
+
var from = mapping.map(selection.from);
|
|
97
|
+
var to = mapping.map(selection.to);
|
|
98
|
+
var isSelectionEmpty = from === to;
|
|
99
|
+
var wasSelectionEmpty = selection.from === selection.to;
|
|
100
|
+
if (isSelectionEmpty && !wasSelectionEmpty) {
|
|
101
|
+
// If selection has become empty i.e. content has been deleted, stop preserving
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
return createPreservedSelection(tr.doc.resolve(from), tr.doc.resolve(to));
|
|
105
|
+
};
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Creates a preserved selection which is expanded to block boundaries.
|
|
109
|
+
*
|
|
110
|
+
* Will return the correct type of selection based on the nodes contained within the
|
|
111
|
+
* expanded selection range.
|
|
112
|
+
*
|
|
113
|
+
* If the selection becomes empty or invalid, it returns undefined.
|
|
114
|
+
*
|
|
115
|
+
* @param $from The resolved position of the start of the selection
|
|
116
|
+
* @param $to The resolved position of the end of the selection
|
|
117
|
+
* @returns A Selection or undefined if selection is invalid
|
|
118
|
+
*/
|
|
119
|
+
var createPreservedSelection = exports.createPreservedSelection = function createPreservedSelection($from, $to) {
|
|
120
|
+
var _doc$nodeAt;
|
|
121
|
+
var doc = $from.doc;
|
|
122
|
+
|
|
123
|
+
// expand the selection range to block boundaries, so selection always includes whole nodes
|
|
124
|
+
var expanded = (0, _selection.expandToBlockRange)($from, $to);
|
|
125
|
+
|
|
126
|
+
// stop preserving if selection becomes invalid
|
|
127
|
+
if (expanded.$from.pos < 0 || expanded.$to.pos > doc.content.size || expanded.$from.pos >= expanded.$to.pos) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
116
130
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
return new _state.TextSelection($from, $to);
|
|
131
|
+
// If multiple blocks selected, create TextSelection from start of first node to end of last node
|
|
132
|
+
if (expanded.range && (0, _selection.isMultiBlockRange)(expanded.range)) {
|
|
133
|
+
return new _state.TextSelection(expanded.$from, expanded.$to);
|
|
122
134
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
} catch (_unused) {
|
|
135
|
+
var nodeType = (_doc$nodeAt = doc.nodeAt(expanded.$from.pos)) === null || _doc$nodeAt === void 0 || (_doc$nodeAt = _doc$nodeAt.type) === null || _doc$nodeAt === void 0 ? void 0 : _doc$nodeAt.name;
|
|
136
|
+
if (!nodeType) {
|
|
126
137
|
return undefined;
|
|
127
138
|
}
|
|
139
|
+
if (nodeType === 'table') {
|
|
140
|
+
return (0, _utils.getTableSelectionClosesToPos)($from);
|
|
141
|
+
}
|
|
142
|
+
return (0, _getSelection.newGetSelection)(doc, false, expanded.$from.pos) || undefined;
|
|
128
143
|
};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
2
1
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
3
2
|
import { DRAG_HANDLE_SELECTOR } from '@atlaskit/editor-common/styles';
|
|
4
|
-
import { mapPreservedSelection } from '../utils/selection';
|
|
3
|
+
import { createPreservedSelection, mapPreservedSelection } from '../utils/selection';
|
|
5
4
|
import { stopPreservingSelection } from './editor-commands';
|
|
6
5
|
import { selectionPreservationPluginKey } from './plugin-key';
|
|
7
|
-
import { getSelectionPreservationMeta, hasUserSelectionChange, isSelectionWithinCodeBlock } from './utils';
|
|
6
|
+
import { compareSelections, getSelectionPreservationMeta, hasUserSelectionChange, isPreservedSelectionChanged, isSelectionWithinCodeBlock, syncDOMSelection } from './utils';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Selection Preservation Plugin
|
|
@@ -51,14 +50,13 @@ export const createSelectionPreservationPlugin = api => () => {
|
|
|
51
50
|
...pluginState
|
|
52
51
|
};
|
|
53
52
|
if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
|
|
54
|
-
newState.preservedSelection =
|
|
53
|
+
newState.preservedSelection = createPreservedSelection(tr.doc.resolve(tr.selection.from), tr.doc.resolve(tr.selection.to));
|
|
55
54
|
} else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
|
|
56
55
|
newState.preservedSelection = undefined;
|
|
57
|
-
}
|
|
58
|
-
if (newState.preservedSelection && tr.docChanged) {
|
|
56
|
+
} else if (newState.preservedSelection && tr.docChanged) {
|
|
59
57
|
newState.preservedSelection = mapPreservedSelection(newState.preservedSelection, tr);
|
|
60
58
|
}
|
|
61
|
-
if (newState.preservedSelection
|
|
59
|
+
if (!compareSelections(newState.preservedSelection, pluginState.preservedSelection)) {
|
|
62
60
|
if (newState !== null && newState !== void 0 && newState.preservedSelection) {
|
|
63
61
|
var _api$selection, _api$selection$comman;
|
|
64
62
|
api === null || api === void 0 ? void 0 : api.core.actions.execute(api === null || api === void 0 ? void 0 : (_api$selection = api.selection) === null || _api$selection === void 0 ? void 0 : (_api$selection$comman = _api$selection.commands) === null || _api$selection$comman === void 0 ? void 0 : _api$selection$comman.setBlockSelection(newState.preservedSelection));
|
|
@@ -72,32 +70,41 @@ export const createSelectionPreservationPlugin = api => () => {
|
|
|
72
70
|
},
|
|
73
71
|
appendTransaction(transactions, _oldState, newState) {
|
|
74
72
|
const pluginState = selectionPreservationPluginKey.getState(newState);
|
|
75
|
-
const
|
|
76
|
-
|
|
73
|
+
const preservedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
|
|
74
|
+
const stateSel = newState.selection;
|
|
75
|
+
if (!preservedSel) {
|
|
77
76
|
return null;
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
// Auto-stop if user explicitly changes selection or selection is set within a code block
|
|
81
|
-
if (hasUserSelectionChange(transactions) || isSelectionWithinCodeBlock(
|
|
82
|
-
// Auto-stop if user explicitly changes selection
|
|
80
|
+
if (hasUserSelectionChange(transactions) || isSelectionWithinCodeBlock(stateSel)) {
|
|
83
81
|
return stopPreservingSelection({
|
|
84
82
|
tr: newState.tr
|
|
85
83
|
});
|
|
86
84
|
}
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const selectionInvalid = savedSel.from < 0 || savedSel.to > newState.doc.content.size;
|
|
85
|
+
const selectionUnchanged = stateSel.from === preservedSel.from && stateSel.to === preservedSel.to;
|
|
86
|
+
const selectionInvalid = preservedSel.from < 0 || preservedSel.to > newState.doc.content.size;
|
|
90
87
|
if (selectionUnchanged || selectionInvalid) {
|
|
91
88
|
return null;
|
|
92
89
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
const newSelection = createPreservedSelection(newState.doc.resolve(preservedSel.from), newState.doc.resolve(preservedSel.to));
|
|
91
|
+
|
|
92
|
+
// If selection becomes invalid, stop preserving
|
|
93
|
+
if (!newSelection) {
|
|
94
|
+
return stopPreservingSelection({
|
|
95
|
+
tr: newState.tr
|
|
98
96
|
});
|
|
99
97
|
}
|
|
100
|
-
return
|
|
98
|
+
return newState.tr.setSelection(newSelection);
|
|
99
|
+
},
|
|
100
|
+
view() {
|
|
101
|
+
return {
|
|
102
|
+
update(view, prevState) {
|
|
103
|
+
if (isPreservedSelectionChanged(view.state, prevState)) {
|
|
104
|
+
syncDOMSelection(view.state.selection);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
101
108
|
},
|
|
102
109
|
props: {
|
|
103
110
|
handleClick: (view, pos, event) => {
|
|
@@ -23,4 +23,52 @@ export const isSelectionWithinCodeBlock = ({
|
|
|
23
23
|
$to
|
|
24
24
|
}) => {
|
|
25
25
|
return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Compares two selections for equality based on their from and to positions.
|
|
30
|
+
*
|
|
31
|
+
* @param a The first selection to compare.
|
|
32
|
+
* @param b The second selection to compare.
|
|
33
|
+
* @returns True if both selections are equal, otherwise false.
|
|
34
|
+
*/
|
|
35
|
+
export const compareSelections = (a, b) => {
|
|
36
|
+
return (a === null || a === void 0 ? void 0 : a.from) === (b === null || b === void 0 ? void 0 : b.from) && (a === null || a === void 0 ? void 0 : a.to) === (b === null || b === void 0 ? void 0 : b.to);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Returns true/false indicating whether the preserved selection
|
|
41
|
+
* has changed between the old and new editor states.
|
|
42
|
+
*
|
|
43
|
+
* @param newState The new editor state.
|
|
44
|
+
* @param oldState The old editor state.
|
|
45
|
+
* @returns True if the preserved selection has changed, otherwise false.
|
|
46
|
+
*/
|
|
47
|
+
export const isPreservedSelectionChanged = (newState, oldState) => {
|
|
48
|
+
var _selectionPreservatio, _selectionPreservatio2;
|
|
49
|
+
const prev = (_selectionPreservatio = selectionPreservationPluginKey.getState(oldState)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection;
|
|
50
|
+
const curr = (_selectionPreservatio2 = selectionPreservationPluginKey.getState(newState)) === null || _selectionPreservatio2 === void 0 ? void 0 : _selectionPreservatio2.preservedSelection;
|
|
51
|
+
return !!prev && !!curr && !compareSelections(prev, curr);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Triggers a DOM selection sync by resetting the current native selection range
|
|
56
|
+
* only if it is out of sync with the provided ProseMirror selection state.
|
|
57
|
+
*
|
|
58
|
+
* This is a necessary workaround to ensure the browser's native selection state
|
|
59
|
+
* stays in sync with the preserved selection, particularly after transactions
|
|
60
|
+
* that shift document content.
|
|
61
|
+
*
|
|
62
|
+
* @param selection The current ProseMirror selection state to compare against.
|
|
63
|
+
*/
|
|
64
|
+
export const syncDOMSelection = selection => {
|
|
65
|
+
const domSelection = window.getSelection();
|
|
66
|
+
const domRange = domSelection && domSelection.rangeCount === 1 && domSelection.getRangeAt(0).cloneRange();
|
|
67
|
+
const isOutOfSync = domRange && (selection.from !== domRange.startOffset || selection.to !== domRange.endOffset);
|
|
68
|
+
if (isOutOfSync) {
|
|
69
|
+
// Force the DOM selection to refresh, setting it to the same range
|
|
70
|
+
// This will trigger ProseMirror to re-apply its selection logic based on the current state
|
|
71
|
+
domSelection.removeAllRanges();
|
|
72
|
+
domSelection.addRange(domRange);
|
|
73
|
+
}
|
|
26
74
|
};
|
|
@@ -76,7 +76,16 @@ const oldGetSelection = (tr, start) => {
|
|
|
76
76
|
return new TextSelection(tr.doc.resolve(inlineNodePos), tr.doc.resolve(inlineNodeEndPos));
|
|
77
77
|
}
|
|
78
78
|
};
|
|
79
|
-
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Gets the appropriate selection for the node at the given start position.
|
|
82
|
+
*
|
|
83
|
+
* @param doc The ProseMirror document.
|
|
84
|
+
* @param selectionEmpty Indicates if the current selection is empty.
|
|
85
|
+
* @param start The start position of the node.
|
|
86
|
+
* @returns The appropriate selection for the node.
|
|
87
|
+
*/
|
|
88
|
+
export const newGetSelection = (doc, selectionEmpty, start) => {
|
|
80
89
|
const node = doc.nodeAt(start);
|
|
81
90
|
const isNodeSelection = node && NodeSelection.isSelectable(node);
|
|
82
91
|
const nodeSize = node ? node.nodeSize : 1;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { expandToBlockRange } from '@atlaskit/editor-common/selection';
|
|
1
|
+
import { expandToBlockRange, isMultiBlockRange } from '@atlaskit/editor-common/selection';
|
|
2
2
|
import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
import { getTableSelectionClosesToPos } from '@atlaskit/editor-tables/utils';
|
|
3
4
|
import { getBlockControlsMeta, key } from '../main';
|
|
5
|
+
import { newGetSelection } from './getSelection';
|
|
4
6
|
export const getMultiSelectionIfPosInside = (api, pos, tr) => {
|
|
5
7
|
var _api$blockControls, _pluginState$multiSel, _tr$getMeta;
|
|
6
8
|
const pluginState = api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState();
|
|
@@ -87,40 +89,53 @@ export const mapPreservedSelection = (selection, tr) => {
|
|
|
87
89
|
preservedSelectionMapping
|
|
88
90
|
} = getBlockControlsMeta(tr) || {};
|
|
89
91
|
const mapping = preservedSelectionMapping || tr.mapping;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
const from = mapping.map(selection.from);
|
|
93
|
+
const to = mapping.map(selection.to);
|
|
94
|
+
const isSelectionEmpty = from === to;
|
|
95
|
+
const wasSelectionEmpty = selection.from === selection.to;
|
|
96
|
+
if (isSelectionEmpty && !wasSelectionEmpty) {
|
|
97
|
+
// If selection has become empty i.e. content has been deleted, stop preserving
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
return createPreservedSelection(tr.doc.resolve(from), tr.doc.resolve(to));
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates a preserved selection which is expanded to block boundaries.
|
|
105
|
+
*
|
|
106
|
+
* Will return the correct type of selection based on the nodes contained within the
|
|
107
|
+
* expanded selection range.
|
|
108
|
+
*
|
|
109
|
+
* If the selection becomes empty or invalid, it returns undefined.
|
|
110
|
+
*
|
|
111
|
+
* @param $from The resolved position of the start of the selection
|
|
112
|
+
* @param $to The resolved position of the end of the selection
|
|
113
|
+
* @returns A Selection or undefined if selection is invalid
|
|
114
|
+
*/
|
|
115
|
+
export const createPreservedSelection = ($from, $to) => {
|
|
116
|
+
var _doc$nodeAt, _doc$nodeAt$type;
|
|
117
|
+
const {
|
|
118
|
+
doc
|
|
119
|
+
} = $from;
|
|
102
120
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const nodeBefore = expanded.$to.nodeBefore;
|
|
106
|
-
const expandedSelectionEmpty = nodeAfter === nodeBefore && (nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.content.size) === 0;
|
|
107
|
-
if (isSelectionEmpty && expandedSelectionEmpty) {
|
|
108
|
-
return TextSelection.create(tr.doc, from);
|
|
109
|
-
}
|
|
110
|
-
const {
|
|
111
|
-
$from,
|
|
112
|
-
$to
|
|
113
|
-
} = expanded;
|
|
121
|
+
// expand the selection range to block boundaries, so selection always includes whole nodes
|
|
122
|
+
const expanded = expandToBlockRange($from, $to);
|
|
114
123
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
return new TextSelection($from, $to);
|
|
124
|
+
// stop preserving if selection becomes invalid
|
|
125
|
+
if (expanded.$from.pos < 0 || expanded.$to.pos > doc.content.size || expanded.$from.pos >= expanded.$to.pos) {
|
|
126
|
+
return undefined;
|
|
120
127
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
128
|
+
|
|
129
|
+
// If multiple blocks selected, create TextSelection from start of first node to end of last node
|
|
130
|
+
if (expanded.range && isMultiBlockRange(expanded.range)) {
|
|
131
|
+
return new TextSelection(expanded.$from, expanded.$to);
|
|
132
|
+
}
|
|
133
|
+
const nodeType = (_doc$nodeAt = doc.nodeAt(expanded.$from.pos)) === null || _doc$nodeAt === void 0 ? void 0 : (_doc$nodeAt$type = _doc$nodeAt.type) === null || _doc$nodeAt$type === void 0 ? void 0 : _doc$nodeAt$type.name;
|
|
134
|
+
if (!nodeType) {
|
|
124
135
|
return undefined;
|
|
125
136
|
}
|
|
137
|
+
if (nodeType === 'table') {
|
|
138
|
+
return getTableSelectionClosesToPos($from);
|
|
139
|
+
}
|
|
140
|
+
return newGetSelection(doc, false, expanded.$from.pos) || undefined;
|
|
126
141
|
};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
3
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
-
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
5
4
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
6
5
|
import { DRAG_HANDLE_SELECTOR } from '@atlaskit/editor-common/styles';
|
|
7
|
-
import { mapPreservedSelection } from '../utils/selection';
|
|
6
|
+
import { createPreservedSelection, mapPreservedSelection } from '../utils/selection';
|
|
8
7
|
import { stopPreservingSelection } from './editor-commands';
|
|
9
8
|
import { selectionPreservationPluginKey } from './plugin-key';
|
|
10
|
-
import { getSelectionPreservationMeta, hasUserSelectionChange, isSelectionWithinCodeBlock } from './utils';
|
|
9
|
+
import { compareSelections, getSelectionPreservationMeta, hasUserSelectionChange, isPreservedSelectionChanged, isSelectionWithinCodeBlock, syncDOMSelection } from './utils';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Selection Preservation Plugin
|
|
@@ -53,14 +52,13 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
|
|
|
53
52
|
var meta = getSelectionPreservationMeta(tr);
|
|
54
53
|
var newState = _objectSpread({}, pluginState);
|
|
55
54
|
if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
|
|
56
|
-
newState.preservedSelection =
|
|
55
|
+
newState.preservedSelection = createPreservedSelection(tr.doc.resolve(tr.selection.from), tr.doc.resolve(tr.selection.to));
|
|
57
56
|
} else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
|
|
58
57
|
newState.preservedSelection = undefined;
|
|
59
|
-
}
|
|
60
|
-
if (newState.preservedSelection && tr.docChanged) {
|
|
58
|
+
} else if (newState.preservedSelection && tr.docChanged) {
|
|
61
59
|
newState.preservedSelection = mapPreservedSelection(newState.preservedSelection, tr);
|
|
62
60
|
}
|
|
63
|
-
if (newState.preservedSelection
|
|
61
|
+
if (!compareSelections(newState.preservedSelection, pluginState.preservedSelection)) {
|
|
64
62
|
if (newState !== null && newState !== void 0 && newState.preservedSelection) {
|
|
65
63
|
var _api$selection;
|
|
66
64
|
api === null || api === void 0 || api.core.actions.execute(api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || (_api$selection = _api$selection.commands) === null || _api$selection === void 0 ? void 0 : _api$selection.setBlockSelection(newState.preservedSelection));
|
|
@@ -74,32 +72,41 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
|
|
|
74
72
|
},
|
|
75
73
|
appendTransaction: function appendTransaction(transactions, _oldState, newState) {
|
|
76
74
|
var pluginState = selectionPreservationPluginKey.getState(newState);
|
|
77
|
-
var
|
|
78
|
-
|
|
75
|
+
var preservedSel = pluginState === null || pluginState === void 0 ? void 0 : pluginState.preservedSelection;
|
|
76
|
+
var stateSel = newState.selection;
|
|
77
|
+
if (!preservedSel) {
|
|
79
78
|
return null;
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
// Auto-stop if user explicitly changes selection or selection is set within a code block
|
|
83
|
-
if (hasUserSelectionChange(transactions) || isSelectionWithinCodeBlock(
|
|
84
|
-
// Auto-stop if user explicitly changes selection
|
|
82
|
+
if (hasUserSelectionChange(transactions) || isSelectionWithinCodeBlock(stateSel)) {
|
|
85
83
|
return stopPreservingSelection({
|
|
86
84
|
tr: newState.tr
|
|
87
85
|
});
|
|
88
86
|
}
|
|
89
|
-
var
|
|
90
|
-
var
|
|
91
|
-
var selectionInvalid = savedSel.from < 0 || savedSel.to > newState.doc.content.size;
|
|
87
|
+
var selectionUnchanged = stateSel.from === preservedSel.from && stateSel.to === preservedSel.to;
|
|
88
|
+
var selectionInvalid = preservedSel.from < 0 || preservedSel.to > newState.doc.content.size;
|
|
92
89
|
if (selectionUnchanged || selectionInvalid) {
|
|
93
90
|
return null;
|
|
94
91
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
var newSelection = createPreservedSelection(newState.doc.resolve(preservedSel.from), newState.doc.resolve(preservedSel.to));
|
|
93
|
+
|
|
94
|
+
// If selection becomes invalid, stop preserving
|
|
95
|
+
if (!newSelection) {
|
|
96
|
+
return stopPreservingSelection({
|
|
97
|
+
tr: newState.tr
|
|
100
98
|
});
|
|
101
99
|
}
|
|
102
|
-
return
|
|
100
|
+
return newState.tr.setSelection(newSelection);
|
|
101
|
+
},
|
|
102
|
+
view: function view() {
|
|
103
|
+
return {
|
|
104
|
+
update: function update(view, prevState) {
|
|
105
|
+
if (isPreservedSelectionChanged(view.state, prevState)) {
|
|
106
|
+
syncDOMSelection(view.state.selection);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
103
110
|
},
|
|
104
111
|
props: {
|
|
105
112
|
handleClick: function handleClick(view, pos, event) {
|
|
@@ -24,4 +24,52 @@ export var isSelectionWithinCodeBlock = function isSelectionWithinCodeBlock(_ref
|
|
|
24
24
|
var $from = _ref.$from,
|
|
25
25
|
$to = _ref.$to;
|
|
26
26
|
return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Compares two selections for equality based on their from and to positions.
|
|
31
|
+
*
|
|
32
|
+
* @param a The first selection to compare.
|
|
33
|
+
* @param b The second selection to compare.
|
|
34
|
+
* @returns True if both selections are equal, otherwise false.
|
|
35
|
+
*/
|
|
36
|
+
export var compareSelections = function compareSelections(a, b) {
|
|
37
|
+
return (a === null || a === void 0 ? void 0 : a.from) === (b === null || b === void 0 ? void 0 : b.from) && (a === null || a === void 0 ? void 0 : a.to) === (b === null || b === void 0 ? void 0 : b.to);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Returns true/false indicating whether the preserved selection
|
|
42
|
+
* has changed between the old and new editor states.
|
|
43
|
+
*
|
|
44
|
+
* @param newState The new editor state.
|
|
45
|
+
* @param oldState The old editor state.
|
|
46
|
+
* @returns True if the preserved selection has changed, otherwise false.
|
|
47
|
+
*/
|
|
48
|
+
export var isPreservedSelectionChanged = function isPreservedSelectionChanged(newState, oldState) {
|
|
49
|
+
var _selectionPreservatio, _selectionPreservatio2;
|
|
50
|
+
var prev = (_selectionPreservatio = selectionPreservationPluginKey.getState(oldState)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection;
|
|
51
|
+
var curr = (_selectionPreservatio2 = selectionPreservationPluginKey.getState(newState)) === null || _selectionPreservatio2 === void 0 ? void 0 : _selectionPreservatio2.preservedSelection;
|
|
52
|
+
return !!prev && !!curr && !compareSelections(prev, curr);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Triggers a DOM selection sync by resetting the current native selection range
|
|
57
|
+
* only if it is out of sync with the provided ProseMirror selection state.
|
|
58
|
+
*
|
|
59
|
+
* This is a necessary workaround to ensure the browser's native selection state
|
|
60
|
+
* stays in sync with the preserved selection, particularly after transactions
|
|
61
|
+
* that shift document content.
|
|
62
|
+
*
|
|
63
|
+
* @param selection The current ProseMirror selection state to compare against.
|
|
64
|
+
*/
|
|
65
|
+
export var syncDOMSelection = function syncDOMSelection(selection) {
|
|
66
|
+
var domSelection = window.getSelection();
|
|
67
|
+
var domRange = domSelection && domSelection.rangeCount === 1 && domSelection.getRangeAt(0).cloneRange();
|
|
68
|
+
var isOutOfSync = domRange && (selection.from !== domRange.startOffset || selection.to !== domRange.endOffset);
|
|
69
|
+
if (isOutOfSync) {
|
|
70
|
+
// Force the DOM selection to refresh, setting it to the same range
|
|
71
|
+
// This will trigger ProseMirror to re-apply its selection logic based on the current state
|
|
72
|
+
domSelection.removeAllRanges();
|
|
73
|
+
domSelection.addRange(domRange);
|
|
74
|
+
}
|
|
27
75
|
};
|
|
@@ -75,7 +75,16 @@ var oldGetSelection = function oldGetSelection(tr, start) {
|
|
|
75
75
|
return new TextSelection(tr.doc.resolve(inlineNodePos), tr.doc.resolve(inlineNodeEndPos));
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
|
-
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Gets the appropriate selection for the node at the given start position.
|
|
81
|
+
*
|
|
82
|
+
* @param doc The ProseMirror document.
|
|
83
|
+
* @param selectionEmpty Indicates if the current selection is empty.
|
|
84
|
+
* @param start The start position of the node.
|
|
85
|
+
* @returns The appropriate selection for the node.
|
|
86
|
+
*/
|
|
87
|
+
export var newGetSelection = function newGetSelection(doc, selectionEmpty, start) {
|
|
79
88
|
var node = doc.nodeAt(start);
|
|
80
89
|
var isNodeSelection = node && NodeSelection.isSelectable(node);
|
|
81
90
|
var nodeSize = node ? node.nodeSize : 1;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { expandToBlockRange } from '@atlaskit/editor-common/selection';
|
|
1
|
+
import { expandToBlockRange, isMultiBlockRange } from '@atlaskit/editor-common/selection';
|
|
2
2
|
import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
import { getTableSelectionClosesToPos } from '@atlaskit/editor-tables/utils';
|
|
3
4
|
import { getBlockControlsMeta, key } from '../main';
|
|
5
|
+
import { newGetSelection } from './getSelection';
|
|
4
6
|
export var getMultiSelectionIfPosInside = function getMultiSelectionIfPosInside(api, pos, tr) {
|
|
5
7
|
var _api$blockControls, _pluginState$multiSel, _tr$getMeta;
|
|
6
8
|
var pluginState = api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState();
|
|
@@ -85,38 +87,51 @@ export var mapPreservedSelection = function mapPreservedSelection(selection, tr)
|
|
|
85
87
|
var _ref = getBlockControlsMeta(tr) || {},
|
|
86
88
|
preservedSelectionMapping = _ref.preservedSelectionMapping;
|
|
87
89
|
var mapping = preservedSelectionMapping || tr.mapping;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// selection always includes whole nodes
|
|
99
|
-
var expanded = expandToBlockRange(tr.doc.resolve(from), tr.doc.resolve(to));
|
|
90
|
+
var from = mapping.map(selection.from);
|
|
91
|
+
var to = mapping.map(selection.to);
|
|
92
|
+
var isSelectionEmpty = from === to;
|
|
93
|
+
var wasSelectionEmpty = selection.from === selection.to;
|
|
94
|
+
if (isSelectionEmpty && !wasSelectionEmpty) {
|
|
95
|
+
// If selection has become empty i.e. content has been deleted, stop preserving
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
return createPreservedSelection(tr.doc.resolve(from), tr.doc.resolve(to));
|
|
99
|
+
};
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Creates a preserved selection which is expanded to block boundaries.
|
|
103
|
+
*
|
|
104
|
+
* Will return the correct type of selection based on the nodes contained within the
|
|
105
|
+
* expanded selection range.
|
|
106
|
+
*
|
|
107
|
+
* If the selection becomes empty or invalid, it returns undefined.
|
|
108
|
+
*
|
|
109
|
+
* @param $from The resolved position of the start of the selection
|
|
110
|
+
* @param $to The resolved position of the end of the selection
|
|
111
|
+
* @returns A Selection or undefined if selection is invalid
|
|
112
|
+
*/
|
|
113
|
+
export var createPreservedSelection = function createPreservedSelection($from, $to) {
|
|
114
|
+
var _doc$nodeAt;
|
|
115
|
+
var doc = $from.doc;
|
|
116
|
+
|
|
117
|
+
// expand the selection range to block boundaries, so selection always includes whole nodes
|
|
118
|
+
var expanded = expandToBlockRange($from, $to);
|
|
119
|
+
|
|
120
|
+
// stop preserving if selection becomes invalid
|
|
121
|
+
if (expanded.$from.pos < 0 || expanded.$to.pos > doc.content.size || expanded.$from.pos >= expanded.$to.pos) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
110
124
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
return new TextSelection($from, $to);
|
|
125
|
+
// If multiple blocks selected, create TextSelection from start of first node to end of last node
|
|
126
|
+
if (expanded.range && isMultiBlockRange(expanded.range)) {
|
|
127
|
+
return new TextSelection(expanded.$from, expanded.$to);
|
|
116
128
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
} catch (_unused) {
|
|
129
|
+
var nodeType = (_doc$nodeAt = doc.nodeAt(expanded.$from.pos)) === null || _doc$nodeAt === void 0 || (_doc$nodeAt = _doc$nodeAt.type) === null || _doc$nodeAt === void 0 ? void 0 : _doc$nodeAt.name;
|
|
130
|
+
if (!nodeType) {
|
|
120
131
|
return undefined;
|
|
121
132
|
}
|
|
133
|
+
if (nodeType === 'table') {
|
|
134
|
+
return getTableSelectionClosesToPos($from);
|
|
135
|
+
}
|
|
136
|
+
return newGetSelection(doc, false, expanded.$from.pos) || undefined;
|
|
122
137
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReadonlyTransaction, Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
1
|
+
import type { EditorState, ReadonlyTransaction, Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
2
2
|
import type { SelectionPreservationMeta } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Detects if any of the transactions include user-driven selection changes.
|
|
@@ -15,3 +15,31 @@ export declare const getSelectionPreservationMeta: (tr: Transaction | ReadonlyTr
|
|
|
15
15
|
* @returns True if the selection is within a code block, otherwise false.
|
|
16
16
|
*/
|
|
17
17
|
export declare const isSelectionWithinCodeBlock: ({ $from, $to }: Selection) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Compares two selections for equality based on their from and to positions.
|
|
20
|
+
*
|
|
21
|
+
* @param a The first selection to compare.
|
|
22
|
+
* @param b The second selection to compare.
|
|
23
|
+
* @returns True if both selections are equal, otherwise false.
|
|
24
|
+
*/
|
|
25
|
+
export declare const compareSelections: (a?: Selection, b?: Selection) => boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Returns true/false indicating whether the preserved selection
|
|
28
|
+
* has changed between the old and new editor states.
|
|
29
|
+
*
|
|
30
|
+
* @param newState The new editor state.
|
|
31
|
+
* @param oldState The old editor state.
|
|
32
|
+
* @returns True if the preserved selection has changed, otherwise false.
|
|
33
|
+
*/
|
|
34
|
+
export declare const isPreservedSelectionChanged: (newState: EditorState, oldState: EditorState) => boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Triggers a DOM selection sync by resetting the current native selection range
|
|
37
|
+
* only if it is out of sync with the provided ProseMirror selection state.
|
|
38
|
+
*
|
|
39
|
+
* This is a necessary workaround to ensure the browser's native selection state
|
|
40
|
+
* stays in sync with the preserved selection, particularly after transactions
|
|
41
|
+
* that shift document content.
|
|
42
|
+
*
|
|
43
|
+
* @param selection The current ProseMirror selection state to compare against.
|
|
44
|
+
*/
|
|
45
|
+
export declare const syncDOMSelection: (selection: Selection) => void;
|
|
@@ -7,6 +7,15 @@ export declare const getInlineNodePos: (doc: PMNode, start: number, nodeSize: nu
|
|
|
7
7
|
inlineNodePos: number;
|
|
8
8
|
};
|
|
9
9
|
export declare const isNodeWithCodeBlock: (tr: Transaction, start: number, nodeSize: number) => boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Gets the appropriate selection for the node at the given start position.
|
|
12
|
+
*
|
|
13
|
+
* @param doc The ProseMirror document.
|
|
14
|
+
* @param selectionEmpty Indicates if the current selection is empty.
|
|
15
|
+
* @param start The start position of the node.
|
|
16
|
+
* @returns The appropriate selection for the node.
|
|
17
|
+
*/
|
|
18
|
+
export declare const newGetSelection: (doc: PMNode, selectionEmpty: boolean, start: number) => false | NodeSelection | TextSelection;
|
|
10
19
|
export declare const getSelection: (tr: Transaction, start: number, api?: ExtractInjectionAPI<BlockControlsPlugin>) => false | NodeSelection | TextSelection;
|
|
11
20
|
export declare const selectNode: (tr: Transaction, start: number, nodeType: string, api?: ExtractInjectionAPI<BlockControlsPlugin>) => Transaction;
|
|
12
21
|
export declare const setCursorPositionAtMovedNode: (tr: Transaction, start: number, api?: ExtractInjectionAPI<BlockControlsPlugin>) => Transaction;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
2
|
-
import
|
|
2
|
+
import type { ResolvedPos } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import { NodeSelection, TextSelection, type ReadonlyTransaction, type Selection, type Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
3
4
|
import type { BlockControlsPlugin } from '../../blockControlsPluginType';
|
|
4
5
|
export declare const getMultiSelectionIfPosInside: (api: ExtractInjectionAPI<BlockControlsPlugin>, pos: number, tr?: Transaction) => {
|
|
5
6
|
anchor?: number;
|
|
@@ -38,3 +39,16 @@ export declare const alignAnchorHeadInDirectionOfPos: (selection: Selection, pos
|
|
|
38
39
|
* @returns The mapped selection or undefined if mapping is not possible
|
|
39
40
|
*/
|
|
40
41
|
export declare const mapPreservedSelection: (selection: Selection, tr: ReadonlyTransaction | Transaction) => Selection | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Creates a preserved selection which is expanded to block boundaries.
|
|
44
|
+
*
|
|
45
|
+
* Will return the correct type of selection based on the nodes contained within the
|
|
46
|
+
* expanded selection range.
|
|
47
|
+
*
|
|
48
|
+
* If the selection becomes empty or invalid, it returns undefined.
|
|
49
|
+
*
|
|
50
|
+
* @param $from The resolved position of the start of the selection
|
|
51
|
+
* @param $to The resolved position of the end of the selection
|
|
52
|
+
* @returns A Selection or undefined if selection is invalid
|
|
53
|
+
*/
|
|
54
|
+
export declare const createPreservedSelection: ($from: ResolvedPos, $to: ResolvedPos) => NodeSelection | TextSelection | import("@atlaskit/editor-tables").CellSelection | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReadonlyTransaction, Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
1
|
+
import type { EditorState, ReadonlyTransaction, Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
2
2
|
import type { SelectionPreservationMeta } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Detects if any of the transactions include user-driven selection changes.
|
|
@@ -15,3 +15,31 @@ export declare const getSelectionPreservationMeta: (tr: Transaction | ReadonlyTr
|
|
|
15
15
|
* @returns True if the selection is within a code block, otherwise false.
|
|
16
16
|
*/
|
|
17
17
|
export declare const isSelectionWithinCodeBlock: ({ $from, $to }: Selection) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Compares two selections for equality based on their from and to positions.
|
|
20
|
+
*
|
|
21
|
+
* @param a The first selection to compare.
|
|
22
|
+
* @param b The second selection to compare.
|
|
23
|
+
* @returns True if both selections are equal, otherwise false.
|
|
24
|
+
*/
|
|
25
|
+
export declare const compareSelections: (a?: Selection, b?: Selection) => boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Returns true/false indicating whether the preserved selection
|
|
28
|
+
* has changed between the old and new editor states.
|
|
29
|
+
*
|
|
30
|
+
* @param newState The new editor state.
|
|
31
|
+
* @param oldState The old editor state.
|
|
32
|
+
* @returns True if the preserved selection has changed, otherwise false.
|
|
33
|
+
*/
|
|
34
|
+
export declare const isPreservedSelectionChanged: (newState: EditorState, oldState: EditorState) => boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Triggers a DOM selection sync by resetting the current native selection range
|
|
37
|
+
* only if it is out of sync with the provided ProseMirror selection state.
|
|
38
|
+
*
|
|
39
|
+
* This is a necessary workaround to ensure the browser's native selection state
|
|
40
|
+
* stays in sync with the preserved selection, particularly after transactions
|
|
41
|
+
* that shift document content.
|
|
42
|
+
*
|
|
43
|
+
* @param selection The current ProseMirror selection state to compare against.
|
|
44
|
+
*/
|
|
45
|
+
export declare const syncDOMSelection: (selection: Selection) => void;
|
|
@@ -7,6 +7,15 @@ export declare const getInlineNodePos: (doc: PMNode, start: number, nodeSize: nu
|
|
|
7
7
|
inlineNodePos: number;
|
|
8
8
|
};
|
|
9
9
|
export declare const isNodeWithCodeBlock: (tr: Transaction, start: number, nodeSize: number) => boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Gets the appropriate selection for the node at the given start position.
|
|
12
|
+
*
|
|
13
|
+
* @param doc The ProseMirror document.
|
|
14
|
+
* @param selectionEmpty Indicates if the current selection is empty.
|
|
15
|
+
* @param start The start position of the node.
|
|
16
|
+
* @returns The appropriate selection for the node.
|
|
17
|
+
*/
|
|
18
|
+
export declare const newGetSelection: (doc: PMNode, selectionEmpty: boolean, start: number) => false | NodeSelection | TextSelection;
|
|
10
19
|
export declare const getSelection: (tr: Transaction, start: number, api?: ExtractInjectionAPI<BlockControlsPlugin>) => false | NodeSelection | TextSelection;
|
|
11
20
|
export declare const selectNode: (tr: Transaction, start: number, nodeType: string, api?: ExtractInjectionAPI<BlockControlsPlugin>) => Transaction;
|
|
12
21
|
export declare const setCursorPositionAtMovedNode: (tr: Transaction, start: number, api?: ExtractInjectionAPI<BlockControlsPlugin>) => Transaction;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
2
|
-
import
|
|
2
|
+
import type { ResolvedPos } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import { NodeSelection, TextSelection, type ReadonlyTransaction, type Selection, type Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
3
4
|
import type { BlockControlsPlugin } from '../../blockControlsPluginType';
|
|
4
5
|
export declare const getMultiSelectionIfPosInside: (api: ExtractInjectionAPI<BlockControlsPlugin>, pos: number, tr?: Transaction) => {
|
|
5
6
|
anchor?: number;
|
|
@@ -38,3 +39,16 @@ export declare const alignAnchorHeadInDirectionOfPos: (selection: Selection, pos
|
|
|
38
39
|
* @returns The mapped selection or undefined if mapping is not possible
|
|
39
40
|
*/
|
|
40
41
|
export declare const mapPreservedSelection: (selection: Selection, tr: ReadonlyTransaction | Transaction) => Selection | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Creates a preserved selection which is expanded to block boundaries.
|
|
44
|
+
*
|
|
45
|
+
* Will return the correct type of selection based on the nodes contained within the
|
|
46
|
+
* expanded selection range.
|
|
47
|
+
*
|
|
48
|
+
* If the selection becomes empty or invalid, it returns undefined.
|
|
49
|
+
*
|
|
50
|
+
* @param $from The resolved position of the start of the selection
|
|
51
|
+
* @param $to The resolved position of the end of the selection
|
|
52
|
+
* @returns A Selection or undefined if selection is invalid
|
|
53
|
+
*/
|
|
54
|
+
export declare const createPreservedSelection: ($from: ResolvedPos, $to: ResolvedPos) => NodeSelection | TextSelection | import("@atlaskit/editor-tables").CellSelection | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-block-controls",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.8",
|
|
4
4
|
"description": "Block controls plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@atlaskit/editor-plugin-interaction": "^12.0.0",
|
|
38
38
|
"@atlaskit/editor-plugin-limited-mode": "^4.0.0",
|
|
39
39
|
"@atlaskit/editor-plugin-metrics": "^8.0.0",
|
|
40
|
-
"@atlaskit/editor-plugin-quick-insert": "^7.
|
|
40
|
+
"@atlaskit/editor-plugin-quick-insert": "^7.1.0",
|
|
41
41
|
"@atlaskit/editor-plugin-selection": "^7.0.0",
|
|
42
42
|
"@atlaskit/editor-plugin-toolbar": "^4.0.0",
|
|
43
43
|
"@atlaskit/editor-plugin-type-ahead": "^7.0.0",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "^3.2.0",
|
|
55
55
|
"@atlaskit/primitives": "^17.0.0",
|
|
56
56
|
"@atlaskit/theme": "^21.0.0",
|
|
57
|
-
"@atlaskit/tmp-editor-statsig": "^16.
|
|
57
|
+
"@atlaskit/tmp-editor-statsig": "^16.14.0",
|
|
58
58
|
"@atlaskit/tokens": "^9.1.0",
|
|
59
|
-
"@atlaskit/tooltip": "^20.
|
|
59
|
+
"@atlaskit/tooltip": "^20.13.0",
|
|
60
60
|
"@babel/runtime": "^7.0.0",
|
|
61
61
|
"@emotion/react": "^11.7.1",
|
|
62
62
|
"@popperjs/core": "^2.11.8",
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
Object.defineProperty(exports, "__esModule", {
|
|
5
|
-
value: true
|
|
6
|
-
});
|
|
7
|
-
exports.DragHandleMenu = void 0;
|
|
8
|
-
var _react = _interopRequireDefault(require("react"));
|
|
9
|
-
var _hooks = require("@atlaskit/editor-common/hooks");
|
|
10
|
-
var DragHandleMenu = exports.DragHandleMenu = function DragHandleMenu(_ref) {
|
|
11
|
-
var api = _ref.api;
|
|
12
|
-
var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['blockControls'], function (states) {
|
|
13
|
-
var _states$blockControls;
|
|
14
|
-
return {
|
|
15
|
-
isMenuOpen: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isMenuOpen
|
|
16
|
-
};
|
|
17
|
-
}),
|
|
18
|
-
isMenuOpen = _useSharedPluginState.isMenuOpen;
|
|
19
|
-
// eslint-disable-next-line @atlassian/i18n/no-literal-string-in-jsx
|
|
20
|
-
return isMenuOpen ? /*#__PURE__*/_react.default.createElement("div", null, "menu") : null;
|
|
21
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
3
|
-
export const DragHandleMenu = ({
|
|
4
|
-
api
|
|
5
|
-
}) => {
|
|
6
|
-
const {
|
|
7
|
-
isMenuOpen
|
|
8
|
-
} = useSharedPluginStateWithSelector(api, ['blockControls'], states => {
|
|
9
|
-
var _states$blockControls;
|
|
10
|
-
return {
|
|
11
|
-
isMenuOpen: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isMenuOpen
|
|
12
|
-
};
|
|
13
|
-
});
|
|
14
|
-
// eslint-disable-next-line @atlassian/i18n/no-literal-string-in-jsx
|
|
15
|
-
return isMenuOpen ? /*#__PURE__*/React.createElement("div", null, "menu") : null;
|
|
16
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
3
|
-
export var DragHandleMenu = function DragHandleMenu(_ref) {
|
|
4
|
-
var api = _ref.api;
|
|
5
|
-
var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['blockControls'], function (states) {
|
|
6
|
-
var _states$blockControls;
|
|
7
|
-
return {
|
|
8
|
-
isMenuOpen: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isMenuOpen
|
|
9
|
-
};
|
|
10
|
-
}),
|
|
11
|
-
isMenuOpen = _useSharedPluginState.isMenuOpen;
|
|
12
|
-
// eslint-disable-next-line @atlassian/i18n/no-literal-string-in-jsx
|
|
13
|
-
return isMenuOpen ? /*#__PURE__*/React.createElement("div", null, "menu") : null;
|
|
14
|
-
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
3
|
-
import type { BlockControlsPlugin } from '../blockControlsPluginType';
|
|
4
|
-
export declare const DragHandleMenu: ({ api, }: {
|
|
5
|
-
api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
|
|
6
|
-
}) => React.JSX.Element | null;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
3
|
-
import type { BlockControlsPlugin } from '../blockControlsPluginType';
|
|
4
|
-
export declare const DragHandleMenu: ({ api, }: {
|
|
5
|
-
api: ExtractInjectionAPI<BlockControlsPlugin> | undefined;
|
|
6
|
-
}) => React.JSX.Element | null;
|