@atlaskit/editor-plugin-block-controls 7.7.4 → 7.7.6
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 +18 -0
- package/dist/cjs/blockControlsPlugin.js +5 -0
- package/dist/cjs/editor-commands/move-node-with-block-menu.js +4 -1
- package/dist/cjs/pm-plugins/selection-preservation/pm-plugin.js +38 -20
- package/dist/cjs/ui/drag-handle.js +2 -2
- package/dist/es2019/blockControlsPlugin.js +5 -0
- package/dist/es2019/editor-commands/move-node-with-block-menu.js +4 -1
- package/dist/es2019/pm-plugins/selection-preservation/pm-plugin.js +39 -21
- package/dist/es2019/ui/drag-handle.js +2 -2
- package/dist/esm/blockControlsPlugin.js +5 -0
- package/dist/esm/editor-commands/move-node-with-block-menu.js +4 -1
- package/dist/esm/pm-plugins/selection-preservation/pm-plugin.js +38 -20
- package/dist/esm/ui/drag-handle.js +2 -2
- package/dist/types/blockControlsPluginType.d.ts +3 -0
- package/dist/types/pm-plugins/selection-preservation/pm-plugin.d.ts +11 -6
- package/dist/types-ts4.5/blockControlsPluginType.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/selection-preservation/pm-plugin.d.ts +11 -6
- package/package.json +3 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-block-controls
|
|
2
2
|
|
|
3
|
+
## 7.7.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`253460bc61db3`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/253460bc61db3) -
|
|
8
|
+
[ux] EDITOR-3380 use preserved selection for block menu move action
|
|
9
|
+
- [`1b3603981c776`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1b3603981c776) -
|
|
10
|
+
EDITOR-3380 ensure we preserve the same selection type
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
|
|
13
|
+
## 7.7.5
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [`6f765533c791b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6f765533c791b) -
|
|
18
|
+
FG platform_editor_block_menu_patch_1 clean up.
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
|
|
3
21
|
## 7.7.4
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -21,6 +21,7 @@ var _firstNodeDecPlugin = require("./pm-plugins/first-node-dec-plugin");
|
|
|
21
21
|
var _pmPlugin = require("./pm-plugins/interaction-tracking/pm-plugin");
|
|
22
22
|
var _main = require("./pm-plugins/main");
|
|
23
23
|
var _editorCommands = require("./pm-plugins/selection-preservation/editor-commands");
|
|
24
|
+
var _pluginKey = require("./pm-plugins/selection-preservation/plugin-key");
|
|
24
25
|
var _pmPlugin2 = require("./pm-plugins/selection-preservation/pm-plugin");
|
|
25
26
|
var _getSelection = require("./pm-plugins/utils/getSelection");
|
|
26
27
|
var _blockMenu = _interopRequireDefault(require("./ui/block-menu"));
|
|
@@ -279,6 +280,10 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
|
|
|
279
280
|
var _interactionTrackingP2, _interactionTrackingP3;
|
|
280
281
|
sharedState.isMouseOut = (_interactionTrackingP2 = (_interactionTrackingP3 = _pmPlugin.interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP3 === void 0 ? void 0 : _interactionTrackingP3.isMouseOut) !== null && _interactionTrackingP2 !== void 0 ? _interactionTrackingP2 : false;
|
|
281
282
|
}
|
|
283
|
+
if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true)) {
|
|
284
|
+
var _selectionPreservatio;
|
|
285
|
+
sharedState.preservedSelection = (_selectionPreservatio = _pluginKey.selectionPreservationPluginKey.getState(editorState)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection;
|
|
286
|
+
}
|
|
282
287
|
return sharedState;
|
|
283
288
|
},
|
|
284
289
|
contentComponent: function contentComponent(_ref8) {
|
|
@@ -12,14 +12,17 @@ var _moveNode = require("./move-node");
|
|
|
12
12
|
var _moveNodeUtils = require("./utils/move-node-utils");
|
|
13
13
|
var moveNodeWithBlockMenu = exports.moveNodeWithBlockMenu = function moveNodeWithBlockMenu(api, direction) {
|
|
14
14
|
return function (_ref) {
|
|
15
|
+
var _api$blockControls$sh;
|
|
15
16
|
var tr = _ref.tr;
|
|
16
17
|
if (!(0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true)) {
|
|
17
18
|
return tr;
|
|
18
19
|
}
|
|
20
|
+
var preservedSelection = api === null || api === void 0 || (_api$blockControls$sh = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh === void 0 ? void 0 : _api$blockControls$sh.preservedSelection;
|
|
21
|
+
var selection = preservedSelection !== null && preservedSelection !== void 0 ? preservedSelection : tr.selection;
|
|
19
22
|
|
|
20
23
|
// Nodes like lists nest within themselves, we need to find the top most position
|
|
21
24
|
var currentNodePos = (0, _moveNodeUtils.getCurrentNodePosFromDragHandleSelection)({
|
|
22
|
-
selection:
|
|
25
|
+
selection: selection,
|
|
23
26
|
schema: tr.doc.type.schema,
|
|
24
27
|
resolve: tr.doc.resolve.bind(tr.doc)
|
|
25
28
|
});
|
|
@@ -15,13 +15,18 @@ var _utils = require("./utils");
|
|
|
15
15
|
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; }
|
|
16
16
|
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) { (0, _defineProperty2.default)(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; }
|
|
17
17
|
/**
|
|
18
|
-
* Selection Preservation Plugin
|
|
18
|
+
* Selection Preservation Plugin
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
20
|
+
* Used to ensure the selection remains stable across selected nodes during specific UI operations,
|
|
21
|
+
* such as when block menus are open or during drag-and-drop actions.
|
|
22
|
+
*
|
|
23
|
+
* We use a TextSelection to span multi-node selections, however there is a ProseMirror limitation
|
|
24
|
+
* where TextSelection cannot include non inline positions at node boundaries (like media/images).
|
|
25
|
+
*
|
|
26
|
+
* When a selection spans text + media nodes, subsequent transactions cause ProseMirror to collapse
|
|
27
|
+
* the selection to the nearest inline position, excluding the media node. This is problematic for
|
|
28
|
+
* features like block menus and drag-and-drop that need stable multi-node selections while performing
|
|
29
|
+
* operations.
|
|
25
30
|
*
|
|
26
31
|
* The plugin works in three phases:
|
|
27
32
|
* (1) Explicitly save a selection via startPreservingSelection() when opening block menus or starting drag operations.
|
|
@@ -50,23 +55,12 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
|
|
|
50
55
|
var meta = (0, _utils.getSelectionPreservationMeta)(tr);
|
|
51
56
|
var newState = _objectSpread({}, pluginState);
|
|
52
57
|
if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
|
|
53
|
-
newState.preservedSelection =
|
|
58
|
+
newState.preservedSelection = tr.selection;
|
|
54
59
|
} else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
|
|
55
60
|
newState.preservedSelection = undefined;
|
|
56
61
|
}
|
|
57
62
|
if (newState.preservedSelection && tr.docChanged) {
|
|
58
|
-
|
|
59
|
-
var to = tr.mapping.map(newState.preservedSelection.to);
|
|
60
|
-
if (from < 0 || to > tr.doc.content.size || from >= to) {
|
|
61
|
-
// stop preserving if preserved selection becomes invalid or collapsed to a cursor
|
|
62
|
-
// e.g. after deleting the selection
|
|
63
|
-
newState.preservedSelection = undefined;
|
|
64
|
-
} else {
|
|
65
|
-
var _expandToBlockRange = expandToBlockRange(tr.doc.resolve(from), tr.doc.resolve(to)),
|
|
66
|
-
$from = _expandToBlockRange.$from,
|
|
67
|
-
$to = _expandToBlockRange.$to;
|
|
68
|
-
newState.preservedSelection = new _state.TextSelection($from, $to);
|
|
69
|
-
}
|
|
63
|
+
newState.preservedSelection = mapSelection(newState.preservedSelection, tr);
|
|
70
64
|
}
|
|
71
65
|
return newState;
|
|
72
66
|
}
|
|
@@ -91,7 +85,7 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
|
|
|
91
85
|
return null;
|
|
92
86
|
}
|
|
93
87
|
try {
|
|
94
|
-
return newState.tr.setSelection(
|
|
88
|
+
return newState.tr.setSelection(savedSel);
|
|
95
89
|
} catch (error) {
|
|
96
90
|
(0, _monitoring.logException)(error, {
|
|
97
91
|
location: 'editor-plugin-block-controls/SelectionPreservationPlugin'
|
|
@@ -101,6 +95,30 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
|
|
|
101
95
|
}
|
|
102
96
|
});
|
|
103
97
|
};
|
|
98
|
+
var mapSelection = function mapSelection(selection, tr) {
|
|
99
|
+
if (selection instanceof _state.TextSelection) {
|
|
100
|
+
var from = tr.mapping.map(selection.from);
|
|
101
|
+
var to = tr.mapping.map(selection.to);
|
|
102
|
+
|
|
103
|
+
// expand the text selection range to block boundaries, so as document changes occur the
|
|
104
|
+
// selection always includes whole nodes
|
|
105
|
+
var _expandToBlockRange = expandToBlockRange(tr.doc.resolve(from), tr.doc.resolve(to)),
|
|
106
|
+
$from = _expandToBlockRange.$from,
|
|
107
|
+
$to = _expandToBlockRange.$to;
|
|
108
|
+
|
|
109
|
+
// stop preserving if preserved selection becomes invalid or collapsed to a cursor
|
|
110
|
+
// e.g. after deleting the selection
|
|
111
|
+
if ($from.pos < 0 || $to.pos > tr.doc.content.size || $from.pos >= $to.pos) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
return new _state.TextSelection($from, $to);
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
return selection.map(tr.doc, tr.mapping);
|
|
118
|
+
} catch (_unused) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
104
122
|
var expandToBlockRange = function expandToBlockRange($from, $to) {
|
|
105
123
|
var range = $from.blockRange($to);
|
|
106
124
|
if (!range) {
|
|
@@ -986,12 +986,12 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref2) {
|
|
|
986
986
|
setDragHandleDisabled(false);
|
|
987
987
|
}
|
|
988
988
|
}, [api === null || api === void 0 ? void 0 : api.blockControls.sharedState, isMultiSelect, isShiftDown, isTopLevelNode, view]);
|
|
989
|
-
var dragHandleMessage = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true)
|
|
989
|
+
var dragHandleMessage = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true) ? formatMessage(_messages.blockControlsMessages.dragToMoveClickToOpen, {
|
|
990
990
|
br: (0, _react2.jsx)("br", null)
|
|
991
991
|
}) : formatMessage(_messages.blockControlsMessages.dragToMove);
|
|
992
992
|
|
|
993
993
|
// Create a string version for aria-label
|
|
994
|
-
var dragHandleAriaLabel = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true)
|
|
994
|
+
var dragHandleAriaLabel = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true) ? formatMessage(_messages.blockControlsMessages.dragToMoveClickToOpen, {
|
|
995
995
|
br: ' '
|
|
996
996
|
}) : formatMessage(_messages.blockControlsMessages.dragToMove);
|
|
997
997
|
var helpDescriptors = isTopLevelNode ? [{
|
|
@@ -13,6 +13,7 @@ import { firstNodeDecPlugin } from './pm-plugins/first-node-dec-plugin';
|
|
|
13
13
|
import { createInteractionTrackingPlugin, interactionTrackingPluginKey } from './pm-plugins/interaction-tracking/pm-plugin';
|
|
14
14
|
import { createPlugin, key } from './pm-plugins/main';
|
|
15
15
|
import { startPreservingSelection, stopPreservingSelection } from './pm-plugins/selection-preservation/editor-commands';
|
|
16
|
+
import { selectionPreservationPluginKey } from './pm-plugins/selection-preservation/plugin-key';
|
|
16
17
|
import { createSelectionPreservationPlugin } from './pm-plugins/selection-preservation/pm-plugin';
|
|
17
18
|
import { selectNode } from './pm-plugins/utils/getSelection';
|
|
18
19
|
import BlockMenu from './ui/block-menu';
|
|
@@ -269,6 +270,10 @@ export const blockControlsPlugin = ({
|
|
|
269
270
|
var _interactionTrackingP2, _interactionTrackingP3;
|
|
270
271
|
sharedState.isMouseOut = (_interactionTrackingP2 = (_interactionTrackingP3 = interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP3 === void 0 ? void 0 : _interactionTrackingP3.isMouseOut) !== null && _interactionTrackingP2 !== void 0 ? _interactionTrackingP2 : false;
|
|
271
272
|
}
|
|
273
|
+
if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
|
|
274
|
+
var _selectionPreservatio;
|
|
275
|
+
sharedState.preservedSelection = (_selectionPreservatio = selectionPreservationPluginKey.getState(editorState)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection;
|
|
276
|
+
}
|
|
272
277
|
return sharedState;
|
|
273
278
|
},
|
|
274
279
|
contentComponent({
|
|
@@ -8,13 +8,16 @@ export const moveNodeWithBlockMenu = (api, direction) => {
|
|
|
8
8
|
return ({
|
|
9
9
|
tr
|
|
10
10
|
}) => {
|
|
11
|
+
var _api$blockControls$sh;
|
|
11
12
|
if (!expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
|
|
12
13
|
return tr;
|
|
13
14
|
}
|
|
15
|
+
const preservedSelection = api === null || api === void 0 ? void 0 : (_api$blockControls$sh = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh === void 0 ? void 0 : _api$blockControls$sh.preservedSelection;
|
|
16
|
+
const selection = preservedSelection !== null && preservedSelection !== void 0 ? preservedSelection : tr.selection;
|
|
14
17
|
|
|
15
18
|
// Nodes like lists nest within themselves, we need to find the top most position
|
|
16
19
|
const currentNodePos = getCurrentNodePosFromDragHandleSelection({
|
|
17
|
-
selection
|
|
20
|
+
selection,
|
|
18
21
|
schema: tr.doc.type.schema,
|
|
19
22
|
resolve: tr.doc.resolve.bind(tr.doc)
|
|
20
23
|
});
|
|
@@ -6,13 +6,18 @@ import { selectionPreservationPluginKey } from './plugin-key';
|
|
|
6
6
|
import { getSelectionPreservationMeta, hasUserSelectionChange } from './utils';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Selection Preservation Plugin
|
|
9
|
+
* Selection Preservation Plugin
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* Used to ensure the selection remains stable across selected nodes during specific UI operations,
|
|
12
|
+
* such as when block menus are open or during drag-and-drop actions.
|
|
13
|
+
*
|
|
14
|
+
* We use a TextSelection to span multi-node selections, however there is a ProseMirror limitation
|
|
15
|
+
* where TextSelection cannot include non inline positions at node boundaries (like media/images).
|
|
16
|
+
*
|
|
17
|
+
* When a selection spans text + media nodes, subsequent transactions cause ProseMirror to collapse
|
|
18
|
+
* the selection to the nearest inline position, excluding the media node. This is problematic for
|
|
19
|
+
* features like block menus and drag-and-drop that need stable multi-node selections while performing
|
|
20
|
+
* operations.
|
|
16
21
|
*
|
|
17
22
|
* The plugin works in three phases:
|
|
18
23
|
* (1) Explicitly save a selection via startPreservingSelection() when opening block menus or starting drag operations.
|
|
@@ -43,24 +48,12 @@ export const createSelectionPreservationPlugin = () => {
|
|
|
43
48
|
...pluginState
|
|
44
49
|
};
|
|
45
50
|
if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
|
|
46
|
-
newState.preservedSelection =
|
|
51
|
+
newState.preservedSelection = tr.selection;
|
|
47
52
|
} else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
|
|
48
53
|
newState.preservedSelection = undefined;
|
|
49
54
|
}
|
|
50
55
|
if (newState.preservedSelection && tr.docChanged) {
|
|
51
|
-
|
|
52
|
-
const to = tr.mapping.map(newState.preservedSelection.to);
|
|
53
|
-
if (from < 0 || to > tr.doc.content.size || from >= to) {
|
|
54
|
-
// stop preserving if preserved selection becomes invalid or collapsed to a cursor
|
|
55
|
-
// e.g. after deleting the selection
|
|
56
|
-
newState.preservedSelection = undefined;
|
|
57
|
-
} else {
|
|
58
|
-
const {
|
|
59
|
-
$from,
|
|
60
|
-
$to
|
|
61
|
-
} = expandToBlockRange(tr.doc.resolve(from), tr.doc.resolve(to));
|
|
62
|
-
newState.preservedSelection = new TextSelection($from, $to);
|
|
63
|
-
}
|
|
56
|
+
newState.preservedSelection = mapSelection(newState.preservedSelection, tr);
|
|
64
57
|
}
|
|
65
58
|
return newState;
|
|
66
59
|
}
|
|
@@ -85,7 +78,7 @@ export const createSelectionPreservationPlugin = () => {
|
|
|
85
78
|
return null;
|
|
86
79
|
}
|
|
87
80
|
try {
|
|
88
|
-
return newState.tr.setSelection(
|
|
81
|
+
return newState.tr.setSelection(savedSel);
|
|
89
82
|
} catch (error) {
|
|
90
83
|
logException(error, {
|
|
91
84
|
location: 'editor-plugin-block-controls/SelectionPreservationPlugin'
|
|
@@ -95,6 +88,31 @@ export const createSelectionPreservationPlugin = () => {
|
|
|
95
88
|
}
|
|
96
89
|
});
|
|
97
90
|
};
|
|
91
|
+
const mapSelection = (selection, tr) => {
|
|
92
|
+
if (selection instanceof TextSelection) {
|
|
93
|
+
const from = tr.mapping.map(selection.from);
|
|
94
|
+
const to = tr.mapping.map(selection.to);
|
|
95
|
+
|
|
96
|
+
// expand the text selection range to block boundaries, so as document changes occur the
|
|
97
|
+
// selection always includes whole nodes
|
|
98
|
+
const {
|
|
99
|
+
$from,
|
|
100
|
+
$to
|
|
101
|
+
} = expandToBlockRange(tr.doc.resolve(from), tr.doc.resolve(to));
|
|
102
|
+
|
|
103
|
+
// stop preserving if preserved selection becomes invalid or collapsed to a cursor
|
|
104
|
+
// e.g. after deleting the selection
|
|
105
|
+
if ($from.pos < 0 || $to.pos > tr.doc.content.size || $from.pos >= $to.pos) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
return new TextSelection($from, $to);
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
return selection.map(tr.doc, tr.mapping);
|
|
112
|
+
} catch {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
98
116
|
const expandToBlockRange = ($from, $to) => {
|
|
99
117
|
const range = $from.blockRange($to);
|
|
100
118
|
if (!range) {
|
|
@@ -975,12 +975,12 @@ export const DragHandle = ({
|
|
|
975
975
|
setDragHandleDisabled(false);
|
|
976
976
|
}
|
|
977
977
|
}, [api === null || api === void 0 ? void 0 : api.blockControls.sharedState, isMultiSelect, isShiftDown, isTopLevelNode, view]);
|
|
978
|
-
const dragHandleMessage = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)
|
|
978
|
+
const dragHandleMessage = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? formatMessage(blockControlsMessages.dragToMoveClickToOpen, {
|
|
979
979
|
br: jsx("br", null)
|
|
980
980
|
}) : formatMessage(blockControlsMessages.dragToMove);
|
|
981
981
|
|
|
982
982
|
// Create a string version for aria-label
|
|
983
|
-
const dragHandleAriaLabel = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)
|
|
983
|
+
const dragHandleAriaLabel = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? formatMessage(blockControlsMessages.dragToMoveClickToOpen, {
|
|
984
984
|
br: ' '
|
|
985
985
|
}) : formatMessage(blockControlsMessages.dragToMove);
|
|
986
986
|
let helpDescriptors = isTopLevelNode ? [{
|
|
@@ -16,6 +16,7 @@ import { firstNodeDecPlugin } from './pm-plugins/first-node-dec-plugin';
|
|
|
16
16
|
import { createInteractionTrackingPlugin, interactionTrackingPluginKey } from './pm-plugins/interaction-tracking/pm-plugin';
|
|
17
17
|
import { createPlugin, key } from './pm-plugins/main';
|
|
18
18
|
import { startPreservingSelection as _startPreservingSelection, stopPreservingSelection as _stopPreservingSelection } from './pm-plugins/selection-preservation/editor-commands';
|
|
19
|
+
import { selectionPreservationPluginKey } from './pm-plugins/selection-preservation/plugin-key';
|
|
19
20
|
import { createSelectionPreservationPlugin } from './pm-plugins/selection-preservation/pm-plugin';
|
|
20
21
|
import { selectNode } from './pm-plugins/utils/getSelection';
|
|
21
22
|
import BlockMenu from './ui/block-menu';
|
|
@@ -272,6 +273,10 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
|
|
|
272
273
|
var _interactionTrackingP2, _interactionTrackingP3;
|
|
273
274
|
sharedState.isMouseOut = (_interactionTrackingP2 = (_interactionTrackingP3 = interactionTrackingPluginKey.getState(editorState)) === null || _interactionTrackingP3 === void 0 ? void 0 : _interactionTrackingP3.isMouseOut) !== null && _interactionTrackingP2 !== void 0 ? _interactionTrackingP2 : false;
|
|
274
275
|
}
|
|
276
|
+
if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
|
|
277
|
+
var _selectionPreservatio;
|
|
278
|
+
sharedState.preservedSelection = (_selectionPreservatio = selectionPreservationPluginKey.getState(editorState)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection;
|
|
279
|
+
}
|
|
275
280
|
return sharedState;
|
|
276
281
|
},
|
|
277
282
|
contentComponent: function contentComponent(_ref8) {
|
|
@@ -6,14 +6,17 @@ import { moveNode } from './move-node';
|
|
|
6
6
|
import { getCurrentNodePosFromDragHandleSelection, getPosWhenMoveNodeDown, getPosWhenMoveNodeUp, getShouldMoveNode } from './utils/move-node-utils';
|
|
7
7
|
export var moveNodeWithBlockMenu = function moveNodeWithBlockMenu(api, direction) {
|
|
8
8
|
return function (_ref) {
|
|
9
|
+
var _api$blockControls$sh;
|
|
9
10
|
var tr = _ref.tr;
|
|
10
11
|
if (!expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
|
|
11
12
|
return tr;
|
|
12
13
|
}
|
|
14
|
+
var preservedSelection = api === null || api === void 0 || (_api$blockControls$sh = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh === void 0 ? void 0 : _api$blockControls$sh.preservedSelection;
|
|
15
|
+
var selection = preservedSelection !== null && preservedSelection !== void 0 ? preservedSelection : tr.selection;
|
|
13
16
|
|
|
14
17
|
// Nodes like lists nest within themselves, we need to find the top most position
|
|
15
18
|
var currentNodePos = getCurrentNodePosFromDragHandleSelection({
|
|
16
|
-
selection:
|
|
19
|
+
selection: selection,
|
|
17
20
|
schema: tr.doc.type.schema,
|
|
18
21
|
resolve: tr.doc.resolve.bind(tr.doc)
|
|
19
22
|
});
|
|
@@ -9,13 +9,18 @@ import { selectionPreservationPluginKey } from './plugin-key';
|
|
|
9
9
|
import { getSelectionPreservationMeta, hasUserSelectionChange } from './utils';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Selection Preservation Plugin
|
|
12
|
+
* Selection Preservation Plugin
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* Used to ensure the selection remains stable across selected nodes during specific UI operations,
|
|
15
|
+
* such as when block menus are open or during drag-and-drop actions.
|
|
16
|
+
*
|
|
17
|
+
* We use a TextSelection to span multi-node selections, however there is a ProseMirror limitation
|
|
18
|
+
* where TextSelection cannot include non inline positions at node boundaries (like media/images).
|
|
19
|
+
*
|
|
20
|
+
* When a selection spans text + media nodes, subsequent transactions cause ProseMirror to collapse
|
|
21
|
+
* the selection to the nearest inline position, excluding the media node. This is problematic for
|
|
22
|
+
* features like block menus and drag-and-drop that need stable multi-node selections while performing
|
|
23
|
+
* operations.
|
|
19
24
|
*
|
|
20
25
|
* The plugin works in three phases:
|
|
21
26
|
* (1) Explicitly save a selection via startPreservingSelection() when opening block menus or starting drag operations.
|
|
@@ -44,23 +49,12 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
|
|
|
44
49
|
var meta = getSelectionPreservationMeta(tr);
|
|
45
50
|
var newState = _objectSpread({}, pluginState);
|
|
46
51
|
if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
|
|
47
|
-
newState.preservedSelection =
|
|
52
|
+
newState.preservedSelection = tr.selection;
|
|
48
53
|
} else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
|
|
49
54
|
newState.preservedSelection = undefined;
|
|
50
55
|
}
|
|
51
56
|
if (newState.preservedSelection && tr.docChanged) {
|
|
52
|
-
|
|
53
|
-
var to = tr.mapping.map(newState.preservedSelection.to);
|
|
54
|
-
if (from < 0 || to > tr.doc.content.size || from >= to) {
|
|
55
|
-
// stop preserving if preserved selection becomes invalid or collapsed to a cursor
|
|
56
|
-
// e.g. after deleting the selection
|
|
57
|
-
newState.preservedSelection = undefined;
|
|
58
|
-
} else {
|
|
59
|
-
var _expandToBlockRange = expandToBlockRange(tr.doc.resolve(from), tr.doc.resolve(to)),
|
|
60
|
-
$from = _expandToBlockRange.$from,
|
|
61
|
-
$to = _expandToBlockRange.$to;
|
|
62
|
-
newState.preservedSelection = new TextSelection($from, $to);
|
|
63
|
-
}
|
|
57
|
+
newState.preservedSelection = mapSelection(newState.preservedSelection, tr);
|
|
64
58
|
}
|
|
65
59
|
return newState;
|
|
66
60
|
}
|
|
@@ -85,7 +79,7 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
|
|
|
85
79
|
return null;
|
|
86
80
|
}
|
|
87
81
|
try {
|
|
88
|
-
return newState.tr.setSelection(
|
|
82
|
+
return newState.tr.setSelection(savedSel);
|
|
89
83
|
} catch (error) {
|
|
90
84
|
logException(error, {
|
|
91
85
|
location: 'editor-plugin-block-controls/SelectionPreservationPlugin'
|
|
@@ -95,6 +89,30 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
|
|
|
95
89
|
}
|
|
96
90
|
});
|
|
97
91
|
};
|
|
92
|
+
var mapSelection = function mapSelection(selection, tr) {
|
|
93
|
+
if (selection instanceof TextSelection) {
|
|
94
|
+
var from = tr.mapping.map(selection.from);
|
|
95
|
+
var to = tr.mapping.map(selection.to);
|
|
96
|
+
|
|
97
|
+
// expand the text selection range to block boundaries, so as document changes occur the
|
|
98
|
+
// selection always includes whole nodes
|
|
99
|
+
var _expandToBlockRange = expandToBlockRange(tr.doc.resolve(from), tr.doc.resolve(to)),
|
|
100
|
+
$from = _expandToBlockRange.$from,
|
|
101
|
+
$to = _expandToBlockRange.$to;
|
|
102
|
+
|
|
103
|
+
// stop preserving if preserved selection becomes invalid or collapsed to a cursor
|
|
104
|
+
// e.g. after deleting the selection
|
|
105
|
+
if ($from.pos < 0 || $to.pos > tr.doc.content.size || $from.pos >= $to.pos) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
return new TextSelection($from, $to);
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
return selection.map(tr.doc, tr.mapping);
|
|
112
|
+
} catch (_unused) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
98
116
|
var expandToBlockRange = function expandToBlockRange($from, $to) {
|
|
99
117
|
var range = $from.blockRange($to);
|
|
100
118
|
if (!range) {
|
|
@@ -983,12 +983,12 @@ export var DragHandle = function DragHandle(_ref2) {
|
|
|
983
983
|
setDragHandleDisabled(false);
|
|
984
984
|
}
|
|
985
985
|
}, [api === null || api === void 0 ? void 0 : api.blockControls.sharedState, isMultiSelect, isShiftDown, isTopLevelNode, view]);
|
|
986
|
-
var dragHandleMessage = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)
|
|
986
|
+
var dragHandleMessage = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? formatMessage(blockControlsMessages.dragToMoveClickToOpen, {
|
|
987
987
|
br: jsx("br", null)
|
|
988
988
|
}) : formatMessage(blockControlsMessages.dragToMove);
|
|
989
989
|
|
|
990
990
|
// Create a string version for aria-label
|
|
991
|
-
var dragHandleAriaLabel = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)
|
|
991
|
+
var dragHandleAriaLabel = expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) ? formatMessage(blockControlsMessages.dragToMoveClickToOpen, {
|
|
992
992
|
br: ' '
|
|
993
993
|
}) : formatMessage(blockControlsMessages.dragToMove);
|
|
994
994
|
var helpDescriptors = isTopLevelNode ? [{
|
|
@@ -14,6 +14,7 @@ import type { ToolbarPlugin } from '@atlaskit/editor-plugin-toolbar';
|
|
|
14
14
|
import type { TypeAheadPlugin } from '@atlaskit/editor-plugin-type-ahead';
|
|
15
15
|
import type { UserIntentPlugin } from '@atlaskit/editor-plugin-user-intent';
|
|
16
16
|
import type { WidthPlugin } from '@atlaskit/editor-plugin-width';
|
|
17
|
+
import type { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
17
18
|
import { type DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
18
19
|
export type ActiveNode = {
|
|
19
20
|
anchorName: string;
|
|
@@ -71,6 +72,7 @@ export interface PluginState {
|
|
|
71
72
|
menuTriggerBy?: string;
|
|
72
73
|
menuTriggerByNode?: TriggerByNode;
|
|
73
74
|
multiSelectDnD?: MultiSelectDnD;
|
|
75
|
+
preservedSelection?: Selection;
|
|
74
76
|
}
|
|
75
77
|
export type ReleaseHiddenDecoration = () => boolean | undefined;
|
|
76
78
|
export type BlockControlsSharedState = {
|
|
@@ -92,6 +94,7 @@ export type BlockControlsSharedState = {
|
|
|
92
94
|
menuTriggerBy?: string;
|
|
93
95
|
menuTriggerByNode?: TriggerByNode;
|
|
94
96
|
multiSelectDnD?: MultiSelectDnD;
|
|
97
|
+
preservedSelection?: Selection;
|
|
95
98
|
} | undefined;
|
|
96
99
|
export type HandleOptions = {
|
|
97
100
|
isFocused: boolean;
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
2
|
import type { SelectionPreservationPluginState } from './types';
|
|
3
3
|
/**
|
|
4
|
-
* Selection Preservation Plugin
|
|
4
|
+
* Selection Preservation Plugin
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Used to ensure the selection remains stable across selected nodes during specific UI operations,
|
|
7
|
+
* such as when block menus are open or during drag-and-drop actions.
|
|
8
|
+
*
|
|
9
|
+
* We use a TextSelection to span multi-node selections, however there is a ProseMirror limitation
|
|
10
|
+
* where TextSelection cannot include non inline positions at node boundaries (like media/images).
|
|
11
|
+
*
|
|
12
|
+
* When a selection spans text + media nodes, subsequent transactions cause ProseMirror to collapse
|
|
13
|
+
* the selection to the nearest inline position, excluding the media node. This is problematic for
|
|
14
|
+
* features like block menus and drag-and-drop that need stable multi-node selections while performing
|
|
15
|
+
* operations.
|
|
11
16
|
*
|
|
12
17
|
* The plugin works in three phases:
|
|
13
18
|
* (1) Explicitly save a selection via startPreservingSelection() when opening block menus or starting drag operations.
|
|
@@ -14,6 +14,7 @@ import type { ToolbarPlugin } from '@atlaskit/editor-plugin-toolbar';
|
|
|
14
14
|
import type { TypeAheadPlugin } from '@atlaskit/editor-plugin-type-ahead';
|
|
15
15
|
import type { UserIntentPlugin } from '@atlaskit/editor-plugin-user-intent';
|
|
16
16
|
import type { WidthPlugin } from '@atlaskit/editor-plugin-width';
|
|
17
|
+
import type { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
17
18
|
import { type DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
18
19
|
export type ActiveNode = {
|
|
19
20
|
anchorName: string;
|
|
@@ -71,6 +72,7 @@ export interface PluginState {
|
|
|
71
72
|
menuTriggerBy?: string;
|
|
72
73
|
menuTriggerByNode?: TriggerByNode;
|
|
73
74
|
multiSelectDnD?: MultiSelectDnD;
|
|
75
|
+
preservedSelection?: Selection;
|
|
74
76
|
}
|
|
75
77
|
export type ReleaseHiddenDecoration = () => boolean | undefined;
|
|
76
78
|
export type BlockControlsSharedState = {
|
|
@@ -92,6 +94,7 @@ export type BlockControlsSharedState = {
|
|
|
92
94
|
menuTriggerBy?: string;
|
|
93
95
|
menuTriggerByNode?: TriggerByNode;
|
|
94
96
|
multiSelectDnD?: MultiSelectDnD;
|
|
97
|
+
preservedSelection?: Selection;
|
|
95
98
|
} | undefined;
|
|
96
99
|
export type HandleOptions = {
|
|
97
100
|
isFocused: boolean;
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
2
|
import type { SelectionPreservationPluginState } from './types';
|
|
3
3
|
/**
|
|
4
|
-
* Selection Preservation Plugin
|
|
4
|
+
* Selection Preservation Plugin
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Used to ensure the selection remains stable across selected nodes during specific UI operations,
|
|
7
|
+
* such as when block menus are open or during drag-and-drop actions.
|
|
8
|
+
*
|
|
9
|
+
* We use a TextSelection to span multi-node selections, however there is a ProseMirror limitation
|
|
10
|
+
* where TextSelection cannot include non inline positions at node boundaries (like media/images).
|
|
11
|
+
*
|
|
12
|
+
* When a selection spans text + media nodes, subsequent transactions cause ProseMirror to collapse
|
|
13
|
+
* the selection to the nearest inline position, excluding the media node. This is problematic for
|
|
14
|
+
* features like block menus and drag-and-drop that need stable multi-node selections while performing
|
|
15
|
+
* operations.
|
|
11
16
|
*
|
|
12
17
|
* The plugin works in three phases:
|
|
13
18
|
* (1) Explicitly save a selection via startPreservingSelection() when opening block menus or starting drag operations.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-block-controls",
|
|
3
|
-
"version": "7.7.
|
|
3
|
+
"version": "7.7.6",
|
|
4
4
|
"description": "Block controls plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "^3.2.0",
|
|
55
55
|
"@atlaskit/primitives": "^16.2.0",
|
|
56
56
|
"@atlaskit/theme": "^21.0.0",
|
|
57
|
-
"@atlaskit/tmp-editor-statsig": "^13.
|
|
58
|
-
"@atlaskit/tokens": "^8.
|
|
57
|
+
"@atlaskit/tmp-editor-statsig": "^13.40.0",
|
|
58
|
+
"@atlaskit/tokens": "^8.2.0",
|
|
59
59
|
"@atlaskit/tooltip": "^20.10.0",
|
|
60
60
|
"@babel/runtime": "^7.0.0",
|
|
61
61
|
"@emotion/react": "^11.7.1",
|
|
@@ -143,9 +143,6 @@
|
|
|
143
143
|
"dst-a11y__replace-anchor-with-link__editor-jenga": {
|
|
144
144
|
"type": "boolean"
|
|
145
145
|
},
|
|
146
|
-
"platform_editor_block_menu_patch_1": {
|
|
147
|
-
"type": "boolean"
|
|
148
|
-
},
|
|
149
146
|
"editor_native_anchor_remove_decoration_in_apply": {
|
|
150
147
|
"type": "boolean"
|
|
151
148
|
},
|