@atlaskit/editor-plugin-block-controls 7.18.3 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/cjs/blockControlsPlugin.js +2 -3
- package/dist/cjs/editor-commands/handle-key-down-with-preserved-selection.js +87 -0
- package/dist/cjs/pm-plugins/decorations-drag-handle.js +5 -50
- package/dist/cjs/pm-plugins/selection-preservation/pm-plugin.js +7 -20
- package/dist/cjs/pm-plugins/selection-preservation/utils.js +13 -1
- package/dist/es2019/blockControlsPlugin.js +3 -6
- package/dist/es2019/editor-commands/handle-key-down-with-preserved-selection.js +79 -0
- package/dist/es2019/pm-plugins/decorations-drag-handle.js +3 -41
- package/dist/es2019/pm-plugins/selection-preservation/pm-plugin.js +8 -22
- package/dist/es2019/pm-plugins/selection-preservation/utils.js +13 -0
- package/dist/esm/blockControlsPlugin.js +2 -3
- package/dist/esm/editor-commands/handle-key-down-with-preserved-selection.js +81 -0
- package/dist/esm/pm-plugins/decorations-drag-handle.js +5 -50
- package/dist/esm/pm-plugins/selection-preservation/pm-plugin.js +8 -22
- package/dist/esm/pm-plugins/selection-preservation/utils.js +12 -0
- package/dist/types/blockControlsPluginType.d.ts +6 -5
- package/dist/types/editor-commands/handle-key-down-with-preserved-selection.d.ts +17 -0
- package/dist/types/pm-plugins/selection-preservation/pm-plugin.d.ts +1 -1
- package/dist/types/pm-plugins/selection-preservation/utils.d.ts +8 -1
- package/dist/types-ts4.5/blockControlsPluginType.d.ts +6 -5
- package/dist/types-ts4.5/editor-commands/handle-key-down-with-preserved-selection.d.ts +17 -0
- package/dist/types-ts4.5/pm-plugins/selection-preservation/pm-plugin.d.ts +1 -1
- package/dist/types-ts4.5/pm-plugins/selection-preservation/utils.d.ts +8 -1
- package/package.json +15 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-block-controls
|
|
2
2
|
|
|
3
|
+
## 8.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`4da819b186eaf`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/4da819b186eaf) -
|
|
8
|
+
EDITOR-3911 selection preservation key handling
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
|
|
11
|
+
## 7.19.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [`cae218eb0956b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/cae218eb0956b) -
|
|
16
|
+
ED-29725 fix table controls when native anchor enabled
|
|
17
|
+
|
|
3
18
|
## 7.18.3
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
|
@@ -13,6 +13,7 @@ var _state = require("@atlaskit/editor-prosemirror/state");
|
|
|
13
13
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
14
14
|
var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
|
|
15
15
|
var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
|
|
16
|
+
var _handleKeyDownWithPreservedSelection = require("./editor-commands/handle-key-down-with-preserved-selection");
|
|
16
17
|
var _moveNode = require("./editor-commands/move-node");
|
|
17
18
|
var _moveNodeWithBlockMenu2 = require("./editor-commands/move-node-with-block-menu");
|
|
18
19
|
var _moveToLayout = require("./editor-commands/move-to-layout");
|
|
@@ -102,9 +103,6 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
|
|
|
102
103
|
tr: tr
|
|
103
104
|
});
|
|
104
105
|
}
|
|
105
|
-
(0, _editorCommands.stopPreservingSelection)({
|
|
106
|
-
tr: tr
|
|
107
|
-
});
|
|
108
106
|
return tr;
|
|
109
107
|
}
|
|
110
108
|
|
|
@@ -249,6 +247,7 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
|
|
|
249
247
|
moveNodeWithBlockMenu: function moveNodeWithBlockMenu(direction) {
|
|
250
248
|
return (0, _moveNodeWithBlockMenu2.moveNodeWithBlockMenu)(api, direction);
|
|
251
249
|
},
|
|
250
|
+
handleKeyDownWithPreservedSelection: (0, _handleKeyDownWithPreservedSelection.handleKeyDownWithPreservedSelection)(api),
|
|
252
251
|
startPreservingSelection: function startPreservingSelection() {
|
|
253
252
|
return _editorCommands.startPreservingSelection;
|
|
254
253
|
},
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.handleKeyDownWithPreservedSelection = void 0;
|
|
7
|
+
var _selection = require("@atlaskit/editor-common/selection");
|
|
8
|
+
var _editorCommands = require("../pm-plugins/selection-preservation/editor-commands");
|
|
9
|
+
var getKeyboardEventInfo = function getKeyboardEventInfo(event) {
|
|
10
|
+
var key = event.key.toLowerCase();
|
|
11
|
+
var isMetaCtrl = event.metaKey || event.ctrlKey;
|
|
12
|
+
var isDelete = ['backspace', 'delete'].includes(key);
|
|
13
|
+
var isCut = isMetaCtrl && key === 'x';
|
|
14
|
+
var isPaste = isMetaCtrl && key === 'v';
|
|
15
|
+
var isDestructive = isDelete || isCut || isPaste;
|
|
16
|
+
var isModifierOnly = ['control', 'meta', 'alt', 'shift'].includes(key) && !isMetaCtrl;
|
|
17
|
+
var isCopy = isMetaCtrl && key === 'c';
|
|
18
|
+
var isInert = isModifierOnly || isCopy;
|
|
19
|
+
return {
|
|
20
|
+
isDelete: isDelete,
|
|
21
|
+
isDestructive: isDestructive,
|
|
22
|
+
isInert: isInert
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Handles key presses when a selection is being preserved, when the block menu is open or closed.
|
|
28
|
+
*
|
|
29
|
+
* Based on the key pressed and whether the block menu is open or closed:
|
|
30
|
+
* 1. Handles key presses with custom logic (e.g. delete/backspace)
|
|
31
|
+
* 2. Closes the block menu
|
|
32
|
+
* 3. Stops preserving the selection
|
|
33
|
+
*
|
|
34
|
+
* This is used in two places:
|
|
35
|
+
* 1. selection preservation plugin when selection is being preserved, and focus is in the editor.
|
|
36
|
+
* 2. block menu UI component when focus is in the block menu.
|
|
37
|
+
*
|
|
38
|
+
* Ensures consistent behaviour for key presses in both scenarios.
|
|
39
|
+
*/
|
|
40
|
+
var handleKeyDownWithPreservedSelection = exports.handleKeyDownWithPreservedSelection = function handleKeyDownWithPreservedSelection(api) {
|
|
41
|
+
return function (event) {
|
|
42
|
+
return function (_ref) {
|
|
43
|
+
var _api$userIntent;
|
|
44
|
+
var tr = _ref.tr;
|
|
45
|
+
if (!api) {
|
|
46
|
+
return tr;
|
|
47
|
+
}
|
|
48
|
+
var _getKeyboardEventInfo = getKeyboardEventInfo(event),
|
|
49
|
+
isDelete = _getKeyboardEventInfo.isDelete,
|
|
50
|
+
isDestructive = _getKeyboardEventInfo.isDestructive,
|
|
51
|
+
isInert = _getKeyboardEventInfo.isInert;
|
|
52
|
+
|
|
53
|
+
// Handle delete/backspace key presses with custom logic to ensure preserved selection is used
|
|
54
|
+
if (isDelete) {
|
|
55
|
+
var _api$blockControls;
|
|
56
|
+
tr = (0, _selection.deleteSelectedRange)(tr, (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || (_api$blockControls = _api$blockControls.sharedState.currentState()) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.preservedSelection);
|
|
57
|
+
}
|
|
58
|
+
var isBlockMenuOpen = ((_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent) === 'blockMenuOpen';
|
|
59
|
+
|
|
60
|
+
// When selected content is being removed and the block menu is open
|
|
61
|
+
// close the block menu and refocus the editor
|
|
62
|
+
var shouldCloseBlockMenu = isDestructive && isBlockMenuOpen;
|
|
63
|
+
if (shouldCloseBlockMenu) {
|
|
64
|
+
var _api$blockControls2;
|
|
65
|
+
(_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || _api$blockControls2.commands.toggleBlockMenu({
|
|
66
|
+
closeMenu: true
|
|
67
|
+
})({
|
|
68
|
+
tr: tr
|
|
69
|
+
});
|
|
70
|
+
api.core.actions.focus({
|
|
71
|
+
scrollIntoView: false
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Stop preserving when:
|
|
76
|
+
// 1. Content is being removed (delete/cut/paste) OR
|
|
77
|
+
// 2. Menu is closed AND user pressed a non-inert key (i.e. action which modifies selection or content)
|
|
78
|
+
var shouldStopPreservingSelection = isDestructive || !isBlockMenuOpen && !isInert;
|
|
79
|
+
if (shouldStopPreservingSelection) {
|
|
80
|
+
(0, _editorCommands.stopPreservingSelection)({
|
|
81
|
+
tr: tr
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return tr;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
};
|
|
@@ -33,50 +33,6 @@ var findHandleDec = exports.findHandleDec = function findHandleDec(decorations,
|
|
|
33
33
|
return spec.type === _decorationsCommon.TYPE_HANDLE_DEC;
|
|
34
34
|
});
|
|
35
35
|
};
|
|
36
|
-
/**
|
|
37
|
-
* Fix for widget positioning to ensure it is not placed into a previous mark's DOM structure.
|
|
38
|
-
* A ProseMirror widget can appear in the wrong DOM position, specifically being added
|
|
39
|
-
* to a previous mark instead of its intended location, which leads to various rendering issues.
|
|
40
|
-
* For example, when nodeBefore has an alignment mark but the current node doesn't, ProseMirror may
|
|
41
|
-
* incorrectly render the widget inside the previous node's alignment mark wrapper instead.
|
|
42
|
-
*/
|
|
43
|
-
var fixWidgetSide = function fixWidgetSide($pos) {
|
|
44
|
-
var defaultSide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
|
|
45
|
-
var alignmentMark = $pos.doc.type.schema.marks.alignment;
|
|
46
|
-
var indentationMark = $pos.doc.type.schema.marks.indentation;
|
|
47
|
-
|
|
48
|
-
// Only apply fix for alignment and indent marks
|
|
49
|
-
if (!alignmentMark && !indentationMark) {
|
|
50
|
-
return defaultSide;
|
|
51
|
-
}
|
|
52
|
-
if ($pos.nodeBefore && $pos.nodeAfter) {
|
|
53
|
-
var _$pos$nodeBefore, _$pos$nodeAfter;
|
|
54
|
-
var beforeMarks = ((_$pos$nodeBefore = $pos.nodeBefore) === null || _$pos$nodeBefore === void 0 ? void 0 : _$pos$nodeBefore.marks.filter(function (mark) {
|
|
55
|
-
return mark.type === alignmentMark || mark.type === indentationMark;
|
|
56
|
-
})) || [];
|
|
57
|
-
var afterMarks = ((_$pos$nodeAfter = $pos.nodeAfter) === null || _$pos$nodeAfter === void 0 ? void 0 : _$pos$nodeAfter.marks.filter(function (mark) {
|
|
58
|
-
return mark.type === alignmentMark || mark.type === indentationMark;
|
|
59
|
-
})) || [];
|
|
60
|
-
if (beforeMarks.length === 0) {
|
|
61
|
-
return defaultSide;
|
|
62
|
-
} else if (afterMarks.length === 0) {
|
|
63
|
-
return 0;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Check if previous node has marks that current node doesn't have
|
|
67
|
-
var hasMissingMark = beforeMarks.some(function (mark) {
|
|
68
|
-
return !afterMarks.some(function (nextMark) {
|
|
69
|
-
return nextMark.eq(mark);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// if we have missing mark, we set side to 0 to render widget outside previous mark DOM
|
|
74
|
-
if (hasMissingMark) {
|
|
75
|
-
return 0;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return defaultSide;
|
|
79
|
-
};
|
|
80
36
|
var dragHandleDecoration = exports.dragHandleDecoration = function dragHandleDecoration(_ref) {
|
|
81
37
|
var api = _ref.api,
|
|
82
38
|
formatMessage = _ref.formatMessage,
|
|
@@ -95,10 +51,8 @@ var dragHandleDecoration = exports.dragHandleDecoration = function dragHandleDec
|
|
|
95
51
|
var unbind;
|
|
96
52
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
97
53
|
var key = (0, _uuid.default)();
|
|
98
|
-
var $pos = editorState.doc.resolve(pos);
|
|
99
|
-
var side = (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && (0, _platformFeatureFlags.fg)('platform_editor_native_anchor_patch_1') ? fixWidgetSide($pos) : -1;
|
|
100
54
|
var widgetSpec = (0, _experiments.editorExperiment)('platform_editor_breakout_resizing', true) ? {
|
|
101
|
-
side:
|
|
55
|
+
side: -1,
|
|
102
56
|
type: _decorationsCommon.TYPE_HANDLE_DEC,
|
|
103
57
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
104
58
|
testid: "".concat(_decorationsCommon.TYPE_HANDLE_DEC, "-").concat((0, _uuid.default)()),
|
|
@@ -115,10 +69,11 @@ var dragHandleDecoration = exports.dragHandleDecoration = function dragHandleDec
|
|
|
115
69
|
}
|
|
116
70
|
}
|
|
117
71
|
} : {
|
|
118
|
-
side:
|
|
72
|
+
side: -1,
|
|
119
73
|
type: _decorationsCommon.TYPE_HANDLE_DEC,
|
|
120
74
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
121
75
|
testid: "".concat(_decorationsCommon.TYPE_HANDLE_DEC, "-").concat((0, _uuid.default)()),
|
|
76
|
+
marks: (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && (0, _platformFeatureFlags.fg)('platform_editor_native_anchor_patch_1') ? (0, _marks.getActiveBlockMarks)(editorState, pos) : undefined,
|
|
122
77
|
destroy: function destroy(node) {
|
|
123
78
|
unbind && unbind();
|
|
124
79
|
if ((0, _experiments.editorExperiment)('platform_editor_block_control_optimise_render', true) && node instanceof HTMLElement) {
|
|
@@ -144,8 +99,8 @@ var dragHandleDecoration = exports.dragHandleDecoration = function dragHandleDec
|
|
|
144
99
|
};
|
|
145
100
|
var newPos = getPos();
|
|
146
101
|
if (typeof newPos === 'number') {
|
|
147
|
-
var
|
|
148
|
-
isTopLevelNode = (
|
|
102
|
+
var $pos = view.state.doc.resolve(newPos);
|
|
103
|
+
isTopLevelNode = ($pos === null || $pos === void 0 ? void 0 : $pos.parent.type.name) === 'doc';
|
|
149
104
|
}
|
|
150
105
|
/*
|
|
151
106
|
* We disable mouseover event to fix flickering issue on hover
|
|
@@ -75,7 +75,9 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
|
|
|
75
75
|
if (!savedSel) {
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
|
-
|
|
78
|
+
|
|
79
|
+
// Auto-stop if user explicitly changes selection or selection is set within a code block
|
|
80
|
+
if ((0, _utils.hasUserSelectionChange)(transactions) || (0, _utils.isSelectionWithinCodeBlock)(newState.selection)) {
|
|
79
81
|
// Auto-stop if user explicitly changes selection
|
|
80
82
|
return (0, _editorCommands.stopPreservingSelection)({
|
|
81
83
|
tr: newState.tr
|
|
@@ -121,7 +123,7 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
|
|
|
121
123
|
return false;
|
|
122
124
|
},
|
|
123
125
|
handleKeyDown: function handleKeyDown(view, event) {
|
|
124
|
-
var _api$
|
|
126
|
+
var _api$core, _api$blockControls;
|
|
125
127
|
var _ref2 = _pluginKey.selectionPreservationPluginKey.getState(view.state) || {},
|
|
126
128
|
preservedSelection = _ref2.preservedSelection;
|
|
127
129
|
|
|
@@ -129,26 +131,11 @@ var createSelectionPreservationPlugin = exports.createSelectionPreservationPlugi
|
|
|
129
131
|
if (!preservedSelection) {
|
|
130
132
|
return false;
|
|
131
133
|
}
|
|
134
|
+
api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || (_api$blockControls = _api$blockControls.commands) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.handleKeyDownWithPreservedSelection(event));
|
|
132
135
|
|
|
133
136
|
// While preserving selection, if user presses delete/backspace, prevent event from being
|
|
134
|
-
// handled by ProseMirror natively so that we can apply
|
|
135
|
-
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
var blockMenuOpen = (api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent) === 'blockMenuOpen';
|
|
139
|
-
|
|
140
|
-
// When block menu is open, prevent all key events to avoid changing selection or editing content
|
|
141
|
-
if (blockMenuOpen) {
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// When block menu isn't open and user presses any key, stop preserving selection
|
|
146
|
-
var tr = view.state.tr;
|
|
147
|
-
(0, _editorCommands.stopPreservingSelection)({
|
|
148
|
-
tr: tr
|
|
149
|
-
});
|
|
150
|
-
view.dispatch(tr);
|
|
151
|
-
return false;
|
|
137
|
+
// handled by ProseMirror natively so that we can apply logic using the preserved selection.
|
|
138
|
+
return ['backspace', 'delete'].includes(event.key.toLowerCase());
|
|
152
139
|
}
|
|
153
140
|
}
|
|
154
141
|
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.hasUserSelectionChange = exports.getSelectionPreservationMeta = void 0;
|
|
6
|
+
exports.isSelectionWithinCodeBlock = exports.hasUserSelectionChange = exports.getSelectionPreservationMeta = void 0;
|
|
7
7
|
var _pluginKey = require("./plugin-key");
|
|
8
8
|
/**
|
|
9
9
|
* Detects if any of the transactions include user-driven selection changes.
|
|
@@ -18,4 +18,16 @@ var hasUserSelectionChange = exports.hasUserSelectionChange = function hasUserSe
|
|
|
18
18
|
};
|
|
19
19
|
var getSelectionPreservationMeta = exports.getSelectionPreservationMeta = function getSelectionPreservationMeta(tr) {
|
|
20
20
|
return tr.getMeta(_pluginKey.selectionPreservationPluginKey);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Checks if the current selection is within a code block.
|
|
25
|
+
*
|
|
26
|
+
* @param selection The current selection to check.
|
|
27
|
+
* @returns True if the selection is within a code block, otherwise false.
|
|
28
|
+
*/
|
|
29
|
+
var isSelectionWithinCodeBlock = exports.isSelectionWithinCodeBlock = function isSelectionWithinCodeBlock(_ref) {
|
|
30
|
+
var $from = _ref.$from,
|
|
31
|
+
$to = _ref.$to;
|
|
32
|
+
return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
|
|
21
33
|
};
|
|
@@ -5,6 +5,7 @@ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
|
5
5
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
6
6
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
7
7
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
8
|
+
import { handleKeyDownWithPreservedSelection } from './editor-commands/handle-key-down-with-preserved-selection';
|
|
8
9
|
import { moveNode } from './editor-commands/move-node';
|
|
9
10
|
import { moveNodeWithBlockMenu } from './editor-commands/move-node-with-block-menu';
|
|
10
11
|
import { moveToLayout } from './editor-commands/move-to-layout';
|
|
@@ -92,9 +93,6 @@ export const blockControlsPlugin = ({
|
|
|
92
93
|
tr
|
|
93
94
|
});
|
|
94
95
|
}
|
|
95
|
-
stopPreservingSelection({
|
|
96
|
-
tr
|
|
97
|
-
});
|
|
98
96
|
return tr;
|
|
99
97
|
}
|
|
100
98
|
|
|
@@ -240,9 +238,8 @@ export const blockControlsPlugin = ({
|
|
|
240
238
|
isSelectedViaDragHandle
|
|
241
239
|
});
|
|
242
240
|
},
|
|
243
|
-
moveNodeWithBlockMenu: direction =>
|
|
244
|
-
|
|
245
|
-
},
|
|
241
|
+
moveNodeWithBlockMenu: direction => moveNodeWithBlockMenu(api, direction),
|
|
242
|
+
handleKeyDownWithPreservedSelection: handleKeyDownWithPreservedSelection(api),
|
|
246
243
|
startPreservingSelection: () => startPreservingSelection,
|
|
247
244
|
stopPreservingSelection: () => stopPreservingSelection
|
|
248
245
|
},
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { deleteSelectedRange } from '@atlaskit/editor-common/selection';
|
|
2
|
+
import { stopPreservingSelection } from '../pm-plugins/selection-preservation/editor-commands';
|
|
3
|
+
const getKeyboardEventInfo = event => {
|
|
4
|
+
const key = event.key.toLowerCase();
|
|
5
|
+
const isMetaCtrl = event.metaKey || event.ctrlKey;
|
|
6
|
+
const isDelete = ['backspace', 'delete'].includes(key);
|
|
7
|
+
const isCut = isMetaCtrl && key === 'x';
|
|
8
|
+
const isPaste = isMetaCtrl && key === 'v';
|
|
9
|
+
const isDestructive = isDelete || isCut || isPaste;
|
|
10
|
+
const isModifierOnly = ['control', 'meta', 'alt', 'shift'].includes(key) && !isMetaCtrl;
|
|
11
|
+
const isCopy = isMetaCtrl && key === 'c';
|
|
12
|
+
const isInert = isModifierOnly || isCopy;
|
|
13
|
+
return {
|
|
14
|
+
isDelete,
|
|
15
|
+
isDestructive,
|
|
16
|
+
isInert
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Handles key presses when a selection is being preserved, when the block menu is open or closed.
|
|
22
|
+
*
|
|
23
|
+
* Based on the key pressed and whether the block menu is open or closed:
|
|
24
|
+
* 1. Handles key presses with custom logic (e.g. delete/backspace)
|
|
25
|
+
* 2. Closes the block menu
|
|
26
|
+
* 3. Stops preserving the selection
|
|
27
|
+
*
|
|
28
|
+
* This is used in two places:
|
|
29
|
+
* 1. selection preservation plugin when selection is being preserved, and focus is in the editor.
|
|
30
|
+
* 2. block menu UI component when focus is in the block menu.
|
|
31
|
+
*
|
|
32
|
+
* Ensures consistent behaviour for key presses in both scenarios.
|
|
33
|
+
*/
|
|
34
|
+
export const handleKeyDownWithPreservedSelection = api => event => ({
|
|
35
|
+
tr
|
|
36
|
+
}) => {
|
|
37
|
+
var _api$userIntent, _api$userIntent$share;
|
|
38
|
+
if (!api) {
|
|
39
|
+
return tr;
|
|
40
|
+
}
|
|
41
|
+
const {
|
|
42
|
+
isDelete,
|
|
43
|
+
isDestructive,
|
|
44
|
+
isInert
|
|
45
|
+
} = getKeyboardEventInfo(event);
|
|
46
|
+
|
|
47
|
+
// Handle delete/backspace key presses with custom logic to ensure preserved selection is used
|
|
48
|
+
if (isDelete) {
|
|
49
|
+
var _api$blockControls, _api$blockControls$sh;
|
|
50
|
+
tr = deleteSelectedRange(tr, (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : (_api$blockControls$sh = _api$blockControls.sharedState.currentState()) === null || _api$blockControls$sh === void 0 ? void 0 : _api$blockControls$sh.preservedSelection);
|
|
51
|
+
}
|
|
52
|
+
const isBlockMenuOpen = ((_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : (_api$userIntent$share = _api$userIntent.sharedState.currentState()) === null || _api$userIntent$share === void 0 ? void 0 : _api$userIntent$share.currentUserIntent) === 'blockMenuOpen';
|
|
53
|
+
|
|
54
|
+
// When selected content is being removed and the block menu is open
|
|
55
|
+
// close the block menu and refocus the editor
|
|
56
|
+
const shouldCloseBlockMenu = isDestructive && isBlockMenuOpen;
|
|
57
|
+
if (shouldCloseBlockMenu) {
|
|
58
|
+
var _api$blockControls2;
|
|
59
|
+
(_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.commands.toggleBlockMenu({
|
|
60
|
+
closeMenu: true
|
|
61
|
+
})({
|
|
62
|
+
tr
|
|
63
|
+
});
|
|
64
|
+
api.core.actions.focus({
|
|
65
|
+
scrollIntoView: false
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Stop preserving when:
|
|
70
|
+
// 1. Content is being removed (delete/cut/paste) OR
|
|
71
|
+
// 2. Menu is closed AND user pressed a non-inert key (i.e. action which modifies selection or content)
|
|
72
|
+
const shouldStopPreservingSelection = isDestructive || !isBlockMenuOpen && !isInert;
|
|
73
|
+
if (shouldStopPreservingSelection) {
|
|
74
|
+
stopPreservingSelection({
|
|
75
|
+
tr
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return tr;
|
|
79
|
+
};
|
|
@@ -23,43 +23,6 @@ export const emptyParagraphNodeDecorations = () => {
|
|
|
23
23
|
export const findHandleDec = (decorations, from, to) => {
|
|
24
24
|
return decorations.find(from, to, spec => spec.type === TYPE_HANDLE_DEC);
|
|
25
25
|
};
|
|
26
|
-
/**
|
|
27
|
-
* Fix for widget positioning to ensure it is not placed into a previous mark's DOM structure.
|
|
28
|
-
* A ProseMirror widget can appear in the wrong DOM position, specifically being added
|
|
29
|
-
* to a previous mark instead of its intended location, which leads to various rendering issues.
|
|
30
|
-
* For example, when nodeBefore has an alignment mark but the current node doesn't, ProseMirror may
|
|
31
|
-
* incorrectly render the widget inside the previous node's alignment mark wrapper instead.
|
|
32
|
-
*/
|
|
33
|
-
const fixWidgetSide = ($pos, defaultSide = -1) => {
|
|
34
|
-
const alignmentMark = $pos.doc.type.schema.marks.alignment;
|
|
35
|
-
const indentationMark = $pos.doc.type.schema.marks.indentation;
|
|
36
|
-
|
|
37
|
-
// Only apply fix for alignment and indent marks
|
|
38
|
-
if (!alignmentMark && !indentationMark) {
|
|
39
|
-
return defaultSide;
|
|
40
|
-
}
|
|
41
|
-
if ($pos.nodeBefore && $pos.nodeAfter) {
|
|
42
|
-
var _$pos$nodeBefore, _$pos$nodeAfter;
|
|
43
|
-
const beforeMarks = ((_$pos$nodeBefore = $pos.nodeBefore) === null || _$pos$nodeBefore === void 0 ? void 0 : _$pos$nodeBefore.marks.filter(mark => mark.type === alignmentMark || mark.type === indentationMark)) || [];
|
|
44
|
-
const afterMarks = ((_$pos$nodeAfter = $pos.nodeAfter) === null || _$pos$nodeAfter === void 0 ? void 0 : _$pos$nodeAfter.marks.filter(mark => mark.type === alignmentMark || mark.type === indentationMark)) || [];
|
|
45
|
-
if (beforeMarks.length === 0) {
|
|
46
|
-
return defaultSide;
|
|
47
|
-
} else if (afterMarks.length === 0) {
|
|
48
|
-
return 0;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Check if previous node has marks that current node doesn't have
|
|
52
|
-
const hasMissingMark = beforeMarks.some(mark => {
|
|
53
|
-
return !afterMarks.some(nextMark => nextMark.eq(mark));
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// if we have missing mark, we set side to 0 to render widget outside previous mark DOM
|
|
57
|
-
if (hasMissingMark) {
|
|
58
|
-
return 0;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return defaultSide;
|
|
62
|
-
};
|
|
63
26
|
export const dragHandleDecoration = ({
|
|
64
27
|
api,
|
|
65
28
|
formatMessage,
|
|
@@ -79,10 +42,8 @@ export const dragHandleDecoration = ({
|
|
|
79
42
|
let unbind;
|
|
80
43
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
81
44
|
const key = uuid();
|
|
82
|
-
const $pos = editorState.doc.resolve(pos);
|
|
83
|
-
const side = expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && fg('platform_editor_native_anchor_patch_1') ? fixWidgetSide($pos) : -1;
|
|
84
45
|
const widgetSpec = editorExperiment('platform_editor_breakout_resizing', true) ? {
|
|
85
|
-
side,
|
|
46
|
+
side: -1,
|
|
86
47
|
type: TYPE_HANDLE_DEC,
|
|
87
48
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
88
49
|
testid: `${TYPE_HANDLE_DEC}-${uuid()}`,
|
|
@@ -99,10 +60,11 @@ export const dragHandleDecoration = ({
|
|
|
99
60
|
}
|
|
100
61
|
}
|
|
101
62
|
} : {
|
|
102
|
-
side,
|
|
63
|
+
side: -1,
|
|
103
64
|
type: TYPE_HANDLE_DEC,
|
|
104
65
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
105
66
|
testid: `${TYPE_HANDLE_DEC}-${uuid()}`,
|
|
67
|
+
marks: expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && fg('platform_editor_native_anchor_patch_1') ? getActiveBlockMarks(editorState, pos) : undefined,
|
|
106
68
|
destroy: node => {
|
|
107
69
|
unbind && unbind();
|
|
108
70
|
if (editorExperiment('platform_editor_block_control_optimise_render', true) && node instanceof HTMLElement) {
|
|
@@ -4,8 +4,7 @@ import { DRAG_HANDLE_SELECTOR } from '@atlaskit/editor-common/styles';
|
|
|
4
4
|
import { mapPreservedSelection } from '../utils/selection';
|
|
5
5
|
import { stopPreservingSelection } from './editor-commands';
|
|
6
6
|
import { selectionPreservationPluginKey } from './plugin-key';
|
|
7
|
-
import { getSelectionPreservationMeta, hasUserSelectionChange } from './utils';
|
|
8
|
-
|
|
7
|
+
import { getSelectionPreservationMeta, hasUserSelectionChange, isSelectionWithinCodeBlock } from './utils';
|
|
9
8
|
/**
|
|
10
9
|
* Selection Preservation Plugin
|
|
11
10
|
*
|
|
@@ -67,7 +66,9 @@ export const createSelectionPreservationPlugin = api => () => {
|
|
|
67
66
|
if (!savedSel) {
|
|
68
67
|
return null;
|
|
69
68
|
}
|
|
70
|
-
|
|
69
|
+
|
|
70
|
+
// Auto-stop if user explicitly changes selection or selection is set within a code block
|
|
71
|
+
if (hasUserSelectionChange(transactions) || isSelectionWithinCodeBlock(newState.selection)) {
|
|
71
72
|
// Auto-stop if user explicitly changes selection
|
|
72
73
|
return stopPreservingSelection({
|
|
73
74
|
tr: newState.tr
|
|
@@ -114,7 +115,7 @@ export const createSelectionPreservationPlugin = api => () => {
|
|
|
114
115
|
return false;
|
|
115
116
|
},
|
|
116
117
|
handleKeyDown: (view, event) => {
|
|
117
|
-
var _api$
|
|
118
|
+
var _api$core, _api$blockControls, _api$blockControls$co;
|
|
118
119
|
const {
|
|
119
120
|
preservedSelection
|
|
120
121
|
} = selectionPreservationPluginKey.getState(view.state) || {};
|
|
@@ -123,26 +124,11 @@ export const createSelectionPreservationPlugin = api => () => {
|
|
|
123
124
|
if (!preservedSelection) {
|
|
124
125
|
return false;
|
|
125
126
|
}
|
|
127
|
+
api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : (_api$blockControls$co = _api$blockControls.commands) === null || _api$blockControls$co === void 0 ? void 0 : _api$blockControls$co.handleKeyDownWithPreservedSelection(event));
|
|
126
128
|
|
|
127
129
|
// While preserving selection, if user presses delete/backspace, prevent event from being
|
|
128
|
-
// handled by ProseMirror natively so that we can apply
|
|
129
|
-
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
const blockMenuOpen = (api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : (_api$userIntent$share = _api$userIntent.sharedState.currentState()) === null || _api$userIntent$share === void 0 ? void 0 : _api$userIntent$share.currentUserIntent) === 'blockMenuOpen';
|
|
133
|
-
|
|
134
|
-
// When block menu is open, prevent all key events to avoid changing selection or editing content
|
|
135
|
-
if (blockMenuOpen) {
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// When block menu isn't open and user presses any key, stop preserving selection
|
|
140
|
-
const tr = view.state.tr;
|
|
141
|
-
stopPreservingSelection({
|
|
142
|
-
tr
|
|
143
|
-
});
|
|
144
|
-
view.dispatch(tr);
|
|
145
|
-
return false;
|
|
130
|
+
// handled by ProseMirror natively so that we can apply logic using the preserved selection.
|
|
131
|
+
return ['backspace', 'delete'].includes(event.key.toLowerCase());
|
|
146
132
|
}
|
|
147
133
|
}
|
|
148
134
|
});
|
|
@@ -10,4 +10,17 @@ export const hasUserSelectionChange = transactions => {
|
|
|
10
10
|
};
|
|
11
11
|
export const getSelectionPreservationMeta = tr => {
|
|
12
12
|
return tr.getMeta(selectionPreservationPluginKey);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Checks if the current selection is within a code block.
|
|
17
|
+
*
|
|
18
|
+
* @param selection The current selection to check.
|
|
19
|
+
* @returns True if the selection is within a code block, otherwise false.
|
|
20
|
+
*/
|
|
21
|
+
export const isSelectionWithinCodeBlock = ({
|
|
22
|
+
$from,
|
|
23
|
+
$to
|
|
24
|
+
}) => {
|
|
25
|
+
return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
|
|
13
26
|
};
|
|
@@ -8,6 +8,7 @@ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
|
8
8
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
9
9
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
10
10
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
11
|
+
import { handleKeyDownWithPreservedSelection } from './editor-commands/handle-key-down-with-preserved-selection';
|
|
11
12
|
import { moveNode } from './editor-commands/move-node';
|
|
12
13
|
import { moveNodeWithBlockMenu as _moveNodeWithBlockMenu } from './editor-commands/move-node-with-block-menu';
|
|
13
14
|
import { moveToLayout } from './editor-commands/move-to-layout';
|
|
@@ -95,9 +96,6 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
|
|
|
95
96
|
tr: tr
|
|
96
97
|
});
|
|
97
98
|
}
|
|
98
|
-
_stopPreservingSelection({
|
|
99
|
-
tr: tr
|
|
100
|
-
});
|
|
101
99
|
return tr;
|
|
102
100
|
}
|
|
103
101
|
|
|
@@ -242,6 +240,7 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
|
|
|
242
240
|
moveNodeWithBlockMenu: function moveNodeWithBlockMenu(direction) {
|
|
243
241
|
return _moveNodeWithBlockMenu(api, direction);
|
|
244
242
|
},
|
|
243
|
+
handleKeyDownWithPreservedSelection: handleKeyDownWithPreservedSelection(api),
|
|
245
244
|
startPreservingSelection: function startPreservingSelection() {
|
|
246
245
|
return _startPreservingSelection;
|
|
247
246
|
},
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { deleteSelectedRange } from '@atlaskit/editor-common/selection';
|
|
2
|
+
import { stopPreservingSelection } from '../pm-plugins/selection-preservation/editor-commands';
|
|
3
|
+
var getKeyboardEventInfo = function getKeyboardEventInfo(event) {
|
|
4
|
+
var key = event.key.toLowerCase();
|
|
5
|
+
var isMetaCtrl = event.metaKey || event.ctrlKey;
|
|
6
|
+
var isDelete = ['backspace', 'delete'].includes(key);
|
|
7
|
+
var isCut = isMetaCtrl && key === 'x';
|
|
8
|
+
var isPaste = isMetaCtrl && key === 'v';
|
|
9
|
+
var isDestructive = isDelete || isCut || isPaste;
|
|
10
|
+
var isModifierOnly = ['control', 'meta', 'alt', 'shift'].includes(key) && !isMetaCtrl;
|
|
11
|
+
var isCopy = isMetaCtrl && key === 'c';
|
|
12
|
+
var isInert = isModifierOnly || isCopy;
|
|
13
|
+
return {
|
|
14
|
+
isDelete: isDelete,
|
|
15
|
+
isDestructive: isDestructive,
|
|
16
|
+
isInert: isInert
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Handles key presses when a selection is being preserved, when the block menu is open or closed.
|
|
22
|
+
*
|
|
23
|
+
* Based on the key pressed and whether the block menu is open or closed:
|
|
24
|
+
* 1. Handles key presses with custom logic (e.g. delete/backspace)
|
|
25
|
+
* 2. Closes the block menu
|
|
26
|
+
* 3. Stops preserving the selection
|
|
27
|
+
*
|
|
28
|
+
* This is used in two places:
|
|
29
|
+
* 1. selection preservation plugin when selection is being preserved, and focus is in the editor.
|
|
30
|
+
* 2. block menu UI component when focus is in the block menu.
|
|
31
|
+
*
|
|
32
|
+
* Ensures consistent behaviour for key presses in both scenarios.
|
|
33
|
+
*/
|
|
34
|
+
export var handleKeyDownWithPreservedSelection = function handleKeyDownWithPreservedSelection(api) {
|
|
35
|
+
return function (event) {
|
|
36
|
+
return function (_ref) {
|
|
37
|
+
var _api$userIntent;
|
|
38
|
+
var tr = _ref.tr;
|
|
39
|
+
if (!api) {
|
|
40
|
+
return tr;
|
|
41
|
+
}
|
|
42
|
+
var _getKeyboardEventInfo = getKeyboardEventInfo(event),
|
|
43
|
+
isDelete = _getKeyboardEventInfo.isDelete,
|
|
44
|
+
isDestructive = _getKeyboardEventInfo.isDestructive,
|
|
45
|
+
isInert = _getKeyboardEventInfo.isInert;
|
|
46
|
+
|
|
47
|
+
// Handle delete/backspace key presses with custom logic to ensure preserved selection is used
|
|
48
|
+
if (isDelete) {
|
|
49
|
+
var _api$blockControls;
|
|
50
|
+
tr = deleteSelectedRange(tr, (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || (_api$blockControls = _api$blockControls.sharedState.currentState()) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.preservedSelection);
|
|
51
|
+
}
|
|
52
|
+
var isBlockMenuOpen = ((_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent) === 'blockMenuOpen';
|
|
53
|
+
|
|
54
|
+
// When selected content is being removed and the block menu is open
|
|
55
|
+
// close the block menu and refocus the editor
|
|
56
|
+
var shouldCloseBlockMenu = isDestructive && isBlockMenuOpen;
|
|
57
|
+
if (shouldCloseBlockMenu) {
|
|
58
|
+
var _api$blockControls2;
|
|
59
|
+
(_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || _api$blockControls2.commands.toggleBlockMenu({
|
|
60
|
+
closeMenu: true
|
|
61
|
+
})({
|
|
62
|
+
tr: tr
|
|
63
|
+
});
|
|
64
|
+
api.core.actions.focus({
|
|
65
|
+
scrollIntoView: false
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Stop preserving when:
|
|
70
|
+
// 1. Content is being removed (delete/cut/paste) OR
|
|
71
|
+
// 2. Menu is closed AND user pressed a non-inert key (i.e. action which modifies selection or content)
|
|
72
|
+
var shouldStopPreservingSelection = isDestructive || !isBlockMenuOpen && !isInert;
|
|
73
|
+
if (shouldStopPreservingSelection) {
|
|
74
|
+
stopPreservingSelection({
|
|
75
|
+
tr: tr
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return tr;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
};
|
|
@@ -25,50 +25,6 @@ export var findHandleDec = function findHandleDec(decorations, from, to) {
|
|
|
25
25
|
return spec.type === TYPE_HANDLE_DEC;
|
|
26
26
|
});
|
|
27
27
|
};
|
|
28
|
-
/**
|
|
29
|
-
* Fix for widget positioning to ensure it is not placed into a previous mark's DOM structure.
|
|
30
|
-
* A ProseMirror widget can appear in the wrong DOM position, specifically being added
|
|
31
|
-
* to a previous mark instead of its intended location, which leads to various rendering issues.
|
|
32
|
-
* For example, when nodeBefore has an alignment mark but the current node doesn't, ProseMirror may
|
|
33
|
-
* incorrectly render the widget inside the previous node's alignment mark wrapper instead.
|
|
34
|
-
*/
|
|
35
|
-
var fixWidgetSide = function fixWidgetSide($pos) {
|
|
36
|
-
var defaultSide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
|
|
37
|
-
var alignmentMark = $pos.doc.type.schema.marks.alignment;
|
|
38
|
-
var indentationMark = $pos.doc.type.schema.marks.indentation;
|
|
39
|
-
|
|
40
|
-
// Only apply fix for alignment and indent marks
|
|
41
|
-
if (!alignmentMark && !indentationMark) {
|
|
42
|
-
return defaultSide;
|
|
43
|
-
}
|
|
44
|
-
if ($pos.nodeBefore && $pos.nodeAfter) {
|
|
45
|
-
var _$pos$nodeBefore, _$pos$nodeAfter;
|
|
46
|
-
var beforeMarks = ((_$pos$nodeBefore = $pos.nodeBefore) === null || _$pos$nodeBefore === void 0 ? void 0 : _$pos$nodeBefore.marks.filter(function (mark) {
|
|
47
|
-
return mark.type === alignmentMark || mark.type === indentationMark;
|
|
48
|
-
})) || [];
|
|
49
|
-
var afterMarks = ((_$pos$nodeAfter = $pos.nodeAfter) === null || _$pos$nodeAfter === void 0 ? void 0 : _$pos$nodeAfter.marks.filter(function (mark) {
|
|
50
|
-
return mark.type === alignmentMark || mark.type === indentationMark;
|
|
51
|
-
})) || [];
|
|
52
|
-
if (beforeMarks.length === 0) {
|
|
53
|
-
return defaultSide;
|
|
54
|
-
} else if (afterMarks.length === 0) {
|
|
55
|
-
return 0;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Check if previous node has marks that current node doesn't have
|
|
59
|
-
var hasMissingMark = beforeMarks.some(function (mark) {
|
|
60
|
-
return !afterMarks.some(function (nextMark) {
|
|
61
|
-
return nextMark.eq(mark);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// if we have missing mark, we set side to 0 to render widget outside previous mark DOM
|
|
66
|
-
if (hasMissingMark) {
|
|
67
|
-
return 0;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return defaultSide;
|
|
71
|
-
};
|
|
72
28
|
export var dragHandleDecoration = function dragHandleDecoration(_ref) {
|
|
73
29
|
var api = _ref.api,
|
|
74
30
|
formatMessage = _ref.formatMessage,
|
|
@@ -87,10 +43,8 @@ export var dragHandleDecoration = function dragHandleDecoration(_ref) {
|
|
|
87
43
|
var unbind;
|
|
88
44
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
89
45
|
var key = uuid();
|
|
90
|
-
var $pos = editorState.doc.resolve(pos);
|
|
91
|
-
var side = expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && fg('platform_editor_native_anchor_patch_1') ? fixWidgetSide($pos) : -1;
|
|
92
46
|
var widgetSpec = editorExperiment('platform_editor_breakout_resizing', true) ? {
|
|
93
|
-
side:
|
|
47
|
+
side: -1,
|
|
94
48
|
type: TYPE_HANDLE_DEC,
|
|
95
49
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
96
50
|
testid: "".concat(TYPE_HANDLE_DEC, "-").concat(uuid()),
|
|
@@ -107,10 +61,11 @@ export var dragHandleDecoration = function dragHandleDecoration(_ref) {
|
|
|
107
61
|
}
|
|
108
62
|
}
|
|
109
63
|
} : {
|
|
110
|
-
side:
|
|
64
|
+
side: -1,
|
|
111
65
|
type: TYPE_HANDLE_DEC,
|
|
112
66
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
113
67
|
testid: "".concat(TYPE_HANDLE_DEC, "-").concat(uuid()),
|
|
68
|
+
marks: expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && fg('platform_editor_native_anchor_patch_1') ? getActiveBlockMarks(editorState, pos) : undefined,
|
|
114
69
|
destroy: function destroy(node) {
|
|
115
70
|
unbind && unbind();
|
|
116
71
|
if (editorExperiment('platform_editor_block_control_optimise_render', true) && node instanceof HTMLElement) {
|
|
@@ -136,8 +91,8 @@ export var dragHandleDecoration = function dragHandleDecoration(_ref) {
|
|
|
136
91
|
};
|
|
137
92
|
var newPos = getPos();
|
|
138
93
|
if (typeof newPos === 'number') {
|
|
139
|
-
var
|
|
140
|
-
isTopLevelNode = (
|
|
94
|
+
var $pos = view.state.doc.resolve(newPos);
|
|
95
|
+
isTopLevelNode = ($pos === null || $pos === void 0 ? void 0 : $pos.parent.type.name) === 'doc';
|
|
141
96
|
}
|
|
142
97
|
/*
|
|
143
98
|
* We disable mouseover event to fix flickering issue on hover
|
|
@@ -7,8 +7,7 @@ import { DRAG_HANDLE_SELECTOR } from '@atlaskit/editor-common/styles';
|
|
|
7
7
|
import { mapPreservedSelection } from '../utils/selection';
|
|
8
8
|
import { stopPreservingSelection } from './editor-commands';
|
|
9
9
|
import { selectionPreservationPluginKey } from './plugin-key';
|
|
10
|
-
import { getSelectionPreservationMeta, hasUserSelectionChange } from './utils';
|
|
11
|
-
|
|
10
|
+
import { getSelectionPreservationMeta, hasUserSelectionChange, isSelectionWithinCodeBlock } from './utils';
|
|
12
11
|
/**
|
|
13
12
|
* Selection Preservation Plugin
|
|
14
13
|
*
|
|
@@ -69,7 +68,9 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
|
|
|
69
68
|
if (!savedSel) {
|
|
70
69
|
return null;
|
|
71
70
|
}
|
|
72
|
-
|
|
71
|
+
|
|
72
|
+
// Auto-stop if user explicitly changes selection or selection is set within a code block
|
|
73
|
+
if (hasUserSelectionChange(transactions) || isSelectionWithinCodeBlock(newState.selection)) {
|
|
73
74
|
// Auto-stop if user explicitly changes selection
|
|
74
75
|
return stopPreservingSelection({
|
|
75
76
|
tr: newState.tr
|
|
@@ -115,7 +116,7 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
|
|
|
115
116
|
return false;
|
|
116
117
|
},
|
|
117
118
|
handleKeyDown: function handleKeyDown(view, event) {
|
|
118
|
-
var _api$
|
|
119
|
+
var _api$core, _api$blockControls;
|
|
119
120
|
var _ref2 = selectionPreservationPluginKey.getState(view.state) || {},
|
|
120
121
|
preservedSelection = _ref2.preservedSelection;
|
|
121
122
|
|
|
@@ -123,26 +124,11 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
|
|
|
123
124
|
if (!preservedSelection) {
|
|
124
125
|
return false;
|
|
125
126
|
}
|
|
127
|
+
api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || (_api$blockControls = _api$blockControls.commands) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.handleKeyDownWithPreservedSelection(event));
|
|
126
128
|
|
|
127
129
|
// While preserving selection, if user presses delete/backspace, prevent event from being
|
|
128
|
-
// handled by ProseMirror natively so that we can apply
|
|
129
|
-
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
var blockMenuOpen = (api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent) === 'blockMenuOpen';
|
|
133
|
-
|
|
134
|
-
// When block menu is open, prevent all key events to avoid changing selection or editing content
|
|
135
|
-
if (blockMenuOpen) {
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// When block menu isn't open and user presses any key, stop preserving selection
|
|
140
|
-
var tr = view.state.tr;
|
|
141
|
-
stopPreservingSelection({
|
|
142
|
-
tr: tr
|
|
143
|
-
});
|
|
144
|
-
view.dispatch(tr);
|
|
145
|
-
return false;
|
|
130
|
+
// handled by ProseMirror natively so that we can apply logic using the preserved selection.
|
|
131
|
+
return ['backspace', 'delete'].includes(event.key.toLowerCase());
|
|
146
132
|
}
|
|
147
133
|
}
|
|
148
134
|
});
|
|
@@ -12,4 +12,16 @@ export var hasUserSelectionChange = function hasUserSelectionChange(transactions
|
|
|
12
12
|
};
|
|
13
13
|
export var getSelectionPreservationMeta = function getSelectionPreservationMeta(tr) {
|
|
14
14
|
return tr.getMeta(selectionPreservationPluginKey);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Checks if the current selection is within a code block.
|
|
19
|
+
*
|
|
20
|
+
* @param selection The current selection to check.
|
|
21
|
+
* @returns True if the selection is within a code block, otherwise false.
|
|
22
|
+
*/
|
|
23
|
+
export var isSelectionWithinCodeBlock = function isSelectionWithinCodeBlock(_ref) {
|
|
24
|
+
var $from = _ref.$from,
|
|
25
|
+
$to = _ref.$to;
|
|
26
|
+
return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
|
|
15
27
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { IntlShape } from 'react-intl-next';
|
|
2
|
+
import type { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
3
3
|
import type { DIRECTION, EditorCommand, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
|
|
4
4
|
import type { AccessibilityUtilsPlugin } from '@atlaskit/editor-plugin-accessibility-utils';
|
|
5
|
-
import {
|
|
5
|
+
import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
|
|
6
6
|
import type { EditorDisabledPlugin } from '@atlaskit/editor-plugin-editor-disabled';
|
|
7
7
|
import type { FeatureFlagsPlugin } from '@atlaskit/editor-plugin-feature-flags';
|
|
8
8
|
import type { InteractionPlugin } from '@atlaskit/editor-plugin-interaction';
|
|
@@ -15,8 +15,8 @@ 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
17
|
import type { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import type { Mapping } from '@atlaskit/editor-prosemirror/transform';
|
|
19
|
+
import type { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
20
20
|
export type ActiveNode = {
|
|
21
21
|
anchorName: string;
|
|
22
22
|
handleOptions?: HandleOptions;
|
|
@@ -118,6 +118,7 @@ export type BlockControlsPluginDependencies = [
|
|
|
118
118
|
];
|
|
119
119
|
export type BlockControlsPlugin = NextEditorPlugin<'blockControls', {
|
|
120
120
|
commands: {
|
|
121
|
+
handleKeyDownWithPreservedSelection: (event: KeyboardEvent) => EditorCommand;
|
|
121
122
|
moveNode: MoveNode;
|
|
122
123
|
moveNodeWithBlockMenu: (direction: DIRECTION.UP | DIRECTION.DOWN) => EditorCommand;
|
|
123
124
|
/**
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EditorCommand, ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
2
|
+
import type { BlockControlsPlugin } from '../blockControlsPluginType';
|
|
3
|
+
/**
|
|
4
|
+
* Handles key presses when a selection is being preserved, when the block menu is open or closed.
|
|
5
|
+
*
|
|
6
|
+
* Based on the key pressed and whether the block menu is open or closed:
|
|
7
|
+
* 1. Handles key presses with custom logic (e.g. delete/backspace)
|
|
8
|
+
* 2. Closes the block menu
|
|
9
|
+
* 3. Stops preserving the selection
|
|
10
|
+
*
|
|
11
|
+
* This is used in two places:
|
|
12
|
+
* 1. selection preservation plugin when selection is being preserved, and focus is in the editor.
|
|
13
|
+
* 2. block menu UI component when focus is in the block menu.
|
|
14
|
+
*
|
|
15
|
+
* Ensures consistent behaviour for key presses in both scenarios.
|
|
16
|
+
*/
|
|
17
|
+
export declare const handleKeyDownWithPreservedSelection: (api?: ExtractInjectionAPI<BlockControlsPlugin>) => (event: KeyboardEvent) => EditorCommand;
|
|
@@ -32,4 +32,4 @@ import type { SelectionPreservationPluginState } from './types';
|
|
|
32
32
|
*
|
|
33
33
|
* https://hello.atlassian.net/wiki/spaces/egcuc/pages/6170822503/Block+Menu+Solution+for+multi-select+and+selection+preservation
|
|
34
34
|
*/
|
|
35
|
-
export declare const createSelectionPreservationPlugin: (api
|
|
35
|
+
export declare const createSelectionPreservationPlugin: (api?: ExtractInjectionAPI<BlockControlsPlugin>) => () => SafePlugin<SelectionPreservationPluginState>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReadonlyTransaction, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
1
|
+
import type { 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.
|
|
@@ -8,3 +8,10 @@ import type { SelectionPreservationMeta } from './types';
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const hasUserSelectionChange: (transactions: readonly Transaction[]) => boolean;
|
|
10
10
|
export declare const getSelectionPreservationMeta: (tr: Transaction | ReadonlyTransaction) => SelectionPreservationMeta | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Checks if the current selection is within a code block.
|
|
13
|
+
*
|
|
14
|
+
* @param selection The current selection to check.
|
|
15
|
+
* @returns True if the selection is within a code block, otherwise false.
|
|
16
|
+
*/
|
|
17
|
+
export declare const isSelectionWithinCodeBlock: ({ $from, $to }: Selection) => boolean;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { IntlShape } from 'react-intl-next';
|
|
2
|
+
import type { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
3
3
|
import type { DIRECTION, EditorCommand, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
|
|
4
4
|
import type { AccessibilityUtilsPlugin } from '@atlaskit/editor-plugin-accessibility-utils';
|
|
5
|
-
import {
|
|
5
|
+
import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
|
|
6
6
|
import type { EditorDisabledPlugin } from '@atlaskit/editor-plugin-editor-disabled';
|
|
7
7
|
import type { FeatureFlagsPlugin } from '@atlaskit/editor-plugin-feature-flags';
|
|
8
8
|
import type { InteractionPlugin } from '@atlaskit/editor-plugin-interaction';
|
|
@@ -15,8 +15,8 @@ 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
17
|
import type { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import type { Mapping } from '@atlaskit/editor-prosemirror/transform';
|
|
19
|
+
import type { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
20
20
|
export type ActiveNode = {
|
|
21
21
|
anchorName: string;
|
|
22
22
|
handleOptions?: HandleOptions;
|
|
@@ -118,6 +118,7 @@ export type BlockControlsPluginDependencies = [
|
|
|
118
118
|
];
|
|
119
119
|
export type BlockControlsPlugin = NextEditorPlugin<'blockControls', {
|
|
120
120
|
commands: {
|
|
121
|
+
handleKeyDownWithPreservedSelection: (event: KeyboardEvent) => EditorCommand;
|
|
121
122
|
moveNode: MoveNode;
|
|
122
123
|
moveNodeWithBlockMenu: (direction: DIRECTION.UP | DIRECTION.DOWN) => EditorCommand;
|
|
123
124
|
/**
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EditorCommand, ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
2
|
+
import type { BlockControlsPlugin } from '../blockControlsPluginType';
|
|
3
|
+
/**
|
|
4
|
+
* Handles key presses when a selection is being preserved, when the block menu is open or closed.
|
|
5
|
+
*
|
|
6
|
+
* Based on the key pressed and whether the block menu is open or closed:
|
|
7
|
+
* 1. Handles key presses with custom logic (e.g. delete/backspace)
|
|
8
|
+
* 2. Closes the block menu
|
|
9
|
+
* 3. Stops preserving the selection
|
|
10
|
+
*
|
|
11
|
+
* This is used in two places:
|
|
12
|
+
* 1. selection preservation plugin when selection is being preserved, and focus is in the editor.
|
|
13
|
+
* 2. block menu UI component when focus is in the block menu.
|
|
14
|
+
*
|
|
15
|
+
* Ensures consistent behaviour for key presses in both scenarios.
|
|
16
|
+
*/
|
|
17
|
+
export declare const handleKeyDownWithPreservedSelection: (api?: ExtractInjectionAPI<BlockControlsPlugin>) => (event: KeyboardEvent) => EditorCommand;
|
|
@@ -32,4 +32,4 @@ import type { SelectionPreservationPluginState } from './types';
|
|
|
32
32
|
*
|
|
33
33
|
* https://hello.atlassian.net/wiki/spaces/egcuc/pages/6170822503/Block+Menu+Solution+for+multi-select+and+selection+preservation
|
|
34
34
|
*/
|
|
35
|
-
export declare const createSelectionPreservationPlugin: (api
|
|
35
|
+
export declare const createSelectionPreservationPlugin: (api?: ExtractInjectionAPI<BlockControlsPlugin>) => () => SafePlugin<SelectionPreservationPluginState>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReadonlyTransaction, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
1
|
+
import type { 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.
|
|
@@ -8,3 +8,10 @@ import type { SelectionPreservationMeta } from './types';
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const hasUserSelectionChange: (transactions: readonly Transaction[]) => boolean;
|
|
10
10
|
export declare const getSelectionPreservationMeta: (tr: Transaction | ReadonlyTransaction) => SelectionPreservationMeta | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Checks if the current selection is within a code block.
|
|
13
|
+
*
|
|
14
|
+
* @param selection The current selection to check.
|
|
15
|
+
* @returns True if the selection is within a code block, otherwise false.
|
|
16
|
+
*/
|
|
17
|
+
export declare const isSelectionWithinCodeBlock: ({ $from, $to }: Selection) => boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-block-controls",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"description": "Block controls plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -30,19 +30,19 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@atlaskit/adf-schema": "^51.5.0",
|
|
32
32
|
"@atlaskit/browser-apis": "^0.0.1",
|
|
33
|
-
"@atlaskit/editor-plugin-accessibility-utils": "^
|
|
34
|
-
"@atlaskit/editor-plugin-analytics": "^
|
|
35
|
-
"@atlaskit/editor-plugin-editor-disabled": "^
|
|
36
|
-
"@atlaskit/editor-plugin-feature-flags": "^
|
|
37
|
-
"@atlaskit/editor-plugin-interaction": "^
|
|
38
|
-
"@atlaskit/editor-plugin-limited-mode": "^
|
|
39
|
-
"@atlaskit/editor-plugin-metrics": "^
|
|
40
|
-
"@atlaskit/editor-plugin-quick-insert": "^
|
|
41
|
-
"@atlaskit/editor-plugin-selection": "^
|
|
42
|
-
"@atlaskit/editor-plugin-toolbar": "^
|
|
43
|
-
"@atlaskit/editor-plugin-type-ahead": "^
|
|
44
|
-
"@atlaskit/editor-plugin-user-intent": "^
|
|
45
|
-
"@atlaskit/editor-plugin-width": "^
|
|
33
|
+
"@atlaskit/editor-plugin-accessibility-utils": "^7.0.0",
|
|
34
|
+
"@atlaskit/editor-plugin-analytics": "^7.0.0",
|
|
35
|
+
"@atlaskit/editor-plugin-editor-disabled": "^7.0.0",
|
|
36
|
+
"@atlaskit/editor-plugin-feature-flags": "^6.0.0",
|
|
37
|
+
"@atlaskit/editor-plugin-interaction": "^12.0.0",
|
|
38
|
+
"@atlaskit/editor-plugin-limited-mode": "^4.0.0",
|
|
39
|
+
"@atlaskit/editor-plugin-metrics": "^8.0.0",
|
|
40
|
+
"@atlaskit/editor-plugin-quick-insert": "^7.0.0",
|
|
41
|
+
"@atlaskit/editor-plugin-selection": "^7.0.0",
|
|
42
|
+
"@atlaskit/editor-plugin-toolbar": "^4.0.0",
|
|
43
|
+
"@atlaskit/editor-plugin-type-ahead": "^7.0.0",
|
|
44
|
+
"@atlaskit/editor-plugin-user-intent": "^5.0.0",
|
|
45
|
+
"@atlaskit/editor-plugin-width": "^8.0.0",
|
|
46
46
|
"@atlaskit/editor-prosemirror": "^7.2.0",
|
|
47
47
|
"@atlaskit/editor-shared-styles": "^3.10.0",
|
|
48
48
|
"@atlaskit/editor-tables": "^2.9.0",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"uuid": "^3.1.0"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@atlaskit/editor-common": "^
|
|
69
|
+
"@atlaskit/editor-common": "^111.0.0",
|
|
70
70
|
"react": "^18.2.0",
|
|
71
71
|
"react-dom": "^18.2.0",
|
|
72
72
|
"react-intl-next": "npm:react-intl@^5.18.1"
|