@atlaskit/editor-plugin-selection 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/LICENSE.md +13 -0
- package/README.md +30 -0
- package/dist/cjs/actions.js +11 -0
- package/dist/cjs/commands.js +257 -0
- package/dist/cjs/gap-cursor/actions.js +255 -0
- package/dist/cjs/gap-cursor/direction.js +23 -0
- package/dist/cjs/gap-cursor/selection.js +30 -0
- package/dist/cjs/gap-cursor/utils/is-ignored.js +12 -0
- package/dist/cjs/gap-cursor/utils/is-valid-target-node.js +12 -0
- package/dist/cjs/gap-cursor/utils/place-gap-cursor.js +103 -0
- package/dist/cjs/gap-cursor/utils.js +137 -0
- package/dist/cjs/gap-cursor-selection.js +37 -0
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/plugin-factory.js +49 -0
- package/dist/cjs/plugin.js +75 -0
- package/dist/cjs/pm-plugins/events/create-selection-between.js +92 -0
- package/dist/cjs/pm-plugins/events/keydown.js +115 -0
- package/dist/cjs/pm-plugins/gap-cursor-keymap.js +46 -0
- package/dist/cjs/pm-plugins/gap-cursor-main.js +159 -0
- package/dist/cjs/pm-plugins/gap-cursor-plugin-key.js +8 -0
- package/dist/cjs/pm-plugins/keymap.js +16 -0
- package/dist/cjs/pm-plugins/selection-main.js +104 -0
- package/dist/cjs/reducer.js +26 -0
- package/dist/cjs/types.js +20 -0
- package/dist/cjs/utils.js +280 -0
- package/dist/es2019/actions.js +5 -0
- package/dist/es2019/commands.js +250 -0
- package/dist/es2019/gap-cursor/actions.js +256 -0
- package/dist/es2019/gap-cursor/direction.js +15 -0
- package/dist/es2019/gap-cursor/selection.js +1 -0
- package/dist/es2019/gap-cursor/utils/is-ignored.js +1 -0
- package/dist/es2019/gap-cursor/utils/is-valid-target-node.js +1 -0
- package/dist/es2019/gap-cursor/utils/place-gap-cursor.js +94 -0
- package/dist/es2019/gap-cursor/utils.js +124 -0
- package/dist/es2019/gap-cursor-selection.js +2 -0
- package/dist/es2019/index.js +1 -0
- package/dist/es2019/plugin-factory.js +43 -0
- package/dist/es2019/plugin.js +60 -0
- package/dist/es2019/pm-plugins/events/create-selection-between.js +89 -0
- package/dist/es2019/pm-plugins/events/keydown.js +111 -0
- package/dist/es2019/pm-plugins/gap-cursor-keymap.js +40 -0
- package/dist/es2019/pm-plugins/gap-cursor-main.js +157 -0
- package/dist/es2019/pm-plugins/gap-cursor-plugin-key.js +2 -0
- package/dist/es2019/pm-plugins/keymap.js +10 -0
- package/dist/es2019/pm-plugins/selection-main.js +97 -0
- package/dist/es2019/reducer.js +18 -0
- package/dist/es2019/types.js +9 -0
- package/dist/es2019/utils.js +233 -0
- package/dist/esm/actions.js +5 -0
- package/dist/esm/commands.js +251 -0
- package/dist/esm/gap-cursor/actions.js +249 -0
- package/dist/esm/gap-cursor/direction.js +15 -0
- package/dist/esm/gap-cursor/selection.js +1 -0
- package/dist/esm/gap-cursor/utils/is-ignored.js +1 -0
- package/dist/esm/gap-cursor/utils/is-valid-target-node.js +1 -0
- package/dist/esm/gap-cursor/utils/place-gap-cursor.js +97 -0
- package/dist/esm/gap-cursor/utils.js +128 -0
- package/dist/esm/gap-cursor-selection.js +2 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/plugin-factory.js +43 -0
- package/dist/esm/plugin.js +68 -0
- package/dist/esm/pm-plugins/events/create-selection-between.js +86 -0
- package/dist/esm/pm-plugins/events/keydown.js +109 -0
- package/dist/esm/pm-plugins/gap-cursor-keymap.js +40 -0
- package/dist/esm/pm-plugins/gap-cursor-main.js +153 -0
- package/dist/esm/pm-plugins/gap-cursor-plugin-key.js +2 -0
- package/dist/esm/pm-plugins/keymap.js +10 -0
- package/dist/esm/pm-plugins/selection-main.js +98 -0
- package/dist/esm/reducer.js +19 -0
- package/dist/esm/types.js +9 -0
- package/dist/esm/utils.js +241 -0
- package/dist/types/actions.d.ts +17 -0
- package/dist/types/commands.d.ts +9 -0
- package/dist/types/gap-cursor/actions.d.ts +23 -0
- package/dist/types/gap-cursor/direction.d.ts +10 -0
- package/dist/types/gap-cursor/selection.d.ts +1 -0
- package/dist/types/gap-cursor/utils/is-ignored.d.ts +1 -0
- package/dist/types/gap-cursor/utils/is-valid-target-node.d.ts +1 -0
- package/dist/types/gap-cursor/utils/place-gap-cursor.d.ts +2 -0
- package/dist/types/gap-cursor/utils.d.ts +8 -0
- package/dist/types/gap-cursor-selection.d.ts +2 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/plugin-factory.d.ts +2 -0
- package/dist/types/plugin.d.ts +13 -0
- package/dist/types/pm-plugins/events/create-selection-between.d.ts +4 -0
- package/dist/types/pm-plugins/events/keydown.d.ts +2 -0
- package/dist/types/pm-plugins/gap-cursor-keymap.d.ts +2 -0
- package/dist/types/pm-plugins/gap-cursor-main.d.ts +6 -0
- package/dist/types/pm-plugins/gap-cursor-plugin-key.d.ts +2 -0
- package/dist/types/pm-plugins/keymap.d.ts +3 -0
- package/dist/types/pm-plugins/selection-main.d.ts +7 -0
- package/dist/types/reducer.d.ts +3 -0
- package/dist/types/types.d.ts +20 -0
- package/dist/types/utils.d.ts +58 -0
- package/package.json +93 -0
- package/types/package.json +15 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { atTheBeginningOfDoc, atTheEndOfDoc } from '@atlaskit/editor-common/selection';
|
|
2
|
+
import { isMediaNode, isNodeBeforeMediaNode, isPositionNearTableRow, ZERO_WIDTH_SPACE } from '@atlaskit/editor-common/utils';
|
|
3
|
+
import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
4
|
+
import { findDomRefAtPos, findPositionOfNodeBefore, removeNodeBefore } from '@atlaskit/editor-prosemirror/utils';
|
|
5
|
+
import { gapCursorPluginKey } from '../pm-plugins/gap-cursor-plugin-key';
|
|
6
|
+
import { Direction, isBackward, isForward } from './direction';
|
|
7
|
+
import { GapCursorSelection, Side } from './selection';
|
|
8
|
+
import { isTextBlockNearPos } from './utils';
|
|
9
|
+
import { isValidTargetNode } from './utils/is-valid-target-node';
|
|
10
|
+
export var shouldSkipGapCursor = function shouldSkipGapCursor(direction, state, $pos) {
|
|
11
|
+
var _$pos$nodeBefore;
|
|
12
|
+
var doc = state.doc,
|
|
13
|
+
schema = state.schema;
|
|
14
|
+
switch (direction) {
|
|
15
|
+
case Direction.UP:
|
|
16
|
+
if (atTheBeginningOfDoc(state)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return isPositionNearTableRow($pos, schema, 'before') || isTextBlockNearPos(doc, schema, $pos, -1) || isNodeBeforeMediaNode($pos, state);
|
|
20
|
+
case Direction.DOWN:
|
|
21
|
+
return atTheEndOfDoc(state) || isTextBlockNearPos(doc, schema, $pos, 1) || isPositionNearTableRow($pos, schema, 'after') || ((_$pos$nodeBefore = $pos.nodeBefore) === null || _$pos$nodeBefore === void 0 ? void 0 : _$pos$nodeBefore.type.name) === 'text' && !$pos.nodeAfter // end of a paragraph
|
|
22
|
+
;
|
|
23
|
+
|
|
24
|
+
default:
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// These cases should be handled using the handleMediaGapCursor function
|
|
30
|
+
function shouldHandleMediaGapCursor(dir, state) {
|
|
31
|
+
var _selection$$from$node;
|
|
32
|
+
var selection = state.selection;
|
|
33
|
+
var upArrowFromGapCursorIntoMedia = selection instanceof GapCursorSelection && dir === Direction.UP && selection.$from.nodeBefore && isMediaNode(selection.$from.nodeBefore);
|
|
34
|
+
var downArrowFromGapCursorIntoMediaGroup = selection instanceof GapCursorSelection && dir === Direction.DOWN && ((_selection$$from$node = selection.$from.nodeAfter) === null || _selection$$from$node === void 0 ? void 0 : _selection$$from$node.type.name) === 'mediaGroup';
|
|
35
|
+
return upArrowFromGapCursorIntoMedia || downArrowFromGapCursorIntoMediaGroup;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Handle media gap cursor for up/down arrow into media nodes
|
|
39
|
+
// Should check this case by using shouldHandleMediaGapCursor first
|
|
40
|
+
function handleMediaGapCursor(dir, state) {
|
|
41
|
+
var selection = state.selection,
|
|
42
|
+
tr = state.tr;
|
|
43
|
+
var $pos = isBackward(dir) ? selection.$from : selection.$to;
|
|
44
|
+
if (dir === Direction.UP && selection.$from.nodeBefore && isMediaNode(selection.$from.nodeBefore)) {
|
|
45
|
+
var _tr$doc$nodeAt;
|
|
46
|
+
var nodeBeforePos = findPositionOfNodeBefore(tr.selection);
|
|
47
|
+
if (nodeBeforePos && selection.side === 'right' && ((_tr$doc$nodeAt = tr.doc.nodeAt(nodeBeforePos)) === null || _tr$doc$nodeAt === void 0 ? void 0 : _tr$doc$nodeAt.type.name) === 'mediaSingle') {
|
|
48
|
+
tr.setSelection(new NodeSelection(tr.doc.resolve(nodeBeforePos))).scrollIntoView();
|
|
49
|
+
} else if (nodeBeforePos || nodeBeforePos === 0) {
|
|
50
|
+
tr.setSelection(new GapCursorSelection(tr.doc.resolve(nodeBeforePos), Side.LEFT)).scrollIntoView();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (dir === Direction.DOWN && selection.$from.nodeAfter) {
|
|
54
|
+
var nodeAfterPos = selection.side === 'right' ? $pos.pos : $pos.pos + selection.$from.nodeAfter.nodeSize;
|
|
55
|
+
if (nodeAfterPos) {
|
|
56
|
+
tr.setSelection(new GapCursorSelection(tr.doc.resolve(nodeAfterPos), Side.LEFT)).scrollIntoView();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return tr;
|
|
60
|
+
}
|
|
61
|
+
export var arrow = function arrow(dir, endOfTextblock) {
|
|
62
|
+
return function (state, dispatch, view) {
|
|
63
|
+
var doc = state.doc,
|
|
64
|
+
selection = state.selection,
|
|
65
|
+
tr = state.tr;
|
|
66
|
+
var $pos = isBackward(dir) ? selection.$from : selection.$to;
|
|
67
|
+
var mustMove = selection.empty;
|
|
68
|
+
|
|
69
|
+
// start from text selection
|
|
70
|
+
if (selection instanceof TextSelection) {
|
|
71
|
+
// if cursor is in the middle of a text node, do nothing
|
|
72
|
+
if (!endOfTextblock || !endOfTextblock(dir.toString())) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// UP/DOWN jumps to the nearest texblock skipping gapcursor whenever possible
|
|
77
|
+
if (shouldSkipGapCursor(dir, state, $pos)) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// otherwise resolve previous/next position
|
|
82
|
+
$pos = doc.resolve(isBackward(dir) ? $pos.before() : $pos.after());
|
|
83
|
+
mustMove = false;
|
|
84
|
+
}
|
|
85
|
+
if (selection instanceof NodeSelection) {
|
|
86
|
+
if (selection.node.isInline) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (dir === Direction.UP && !atTheBeginningOfDoc(state) && !isNodeBeforeMediaNode($pos, state) || dir === Direction.DOWN) {
|
|
90
|
+
// We dont add gap cursor on node selections going up and down
|
|
91
|
+
// Except we do if we're going up for a block node which is the
|
|
92
|
+
// first node in the document OR the node before is a media node
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Handle media gap cursor for up/down arrow into media nodes
|
|
98
|
+
if (shouldHandleMediaGapCursor(dir, state)) {
|
|
99
|
+
var updatedTr = handleMediaGapCursor(dir, state);
|
|
100
|
+
if (dispatch) {
|
|
101
|
+
dispatch(updatedTr);
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// when jumping between block nodes at the same depth, we need to reverse cursor without changing ProseMirror position
|
|
107
|
+
if (selection instanceof GapCursorSelection &&
|
|
108
|
+
// next node allow gap cursor position
|
|
109
|
+
isValidTargetNode(isBackward(dir) ? $pos.nodeBefore : $pos.nodeAfter) && (
|
|
110
|
+
// gap cursor changes block node
|
|
111
|
+
isBackward(dir) && selection.side === Side.LEFT || isForward(dir) && selection.side === Side.RIGHT)) {
|
|
112
|
+
// reverse cursor position
|
|
113
|
+
if (dispatch) {
|
|
114
|
+
dispatch(tr.setSelection(new GapCursorSelection($pos, selection.side === Side.RIGHT ? Side.LEFT : Side.RIGHT)).scrollIntoView());
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (view) {
|
|
119
|
+
var domAtPos = view.domAtPos.bind(view);
|
|
120
|
+
var target = findDomRefAtPos($pos.pos, domAtPos);
|
|
121
|
+
if (target && target.textContent === ZERO_WIDTH_SPACE) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
var nextSelection = GapCursorSelection.findFrom($pos, isBackward(dir) ? -1 : 1, mustMove);
|
|
126
|
+
if (!nextSelection) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
if (!isValidTargetNode(isForward(dir) ? nextSelection.$from.nodeBefore : nextSelection.$from.nodeAfter)) {
|
|
130
|
+
// reverse cursor position
|
|
131
|
+
if (dispatch) {
|
|
132
|
+
dispatch(tr.setSelection(new GapCursorSelection(nextSelection.$from, isForward(dir) ? Side.LEFT : Side.RIGHT)).scrollIntoView());
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
if (dispatch) {
|
|
137
|
+
dispatch(tr.setSelection(nextSelection).scrollIntoView());
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
export var deleteNode = function deleteNode(dir) {
|
|
143
|
+
return function (state, dispatch) {
|
|
144
|
+
if (state.selection instanceof GapCursorSelection) {
|
|
145
|
+
var _state$selection = state.selection,
|
|
146
|
+
$from = _state$selection.$from,
|
|
147
|
+
$anchor = _state$selection.$anchor;
|
|
148
|
+
var tr = state.tr;
|
|
149
|
+
if (isBackward(dir)) {
|
|
150
|
+
if (state.selection.side === 'left') {
|
|
151
|
+
tr.setSelection(new GapCursorSelection($anchor, Side.RIGHT));
|
|
152
|
+
if (dispatch) {
|
|
153
|
+
dispatch(tr);
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
tr = removeNodeBefore(state.tr);
|
|
158
|
+
} else if ($from.nodeAfter) {
|
|
159
|
+
tr = tr.delete($from.pos, $from.pos + $from.nodeAfter.nodeSize);
|
|
160
|
+
}
|
|
161
|
+
if (dispatch) {
|
|
162
|
+
dispatch(tr.setSelection(Selection.near(tr.doc.resolve(tr.mapping.map(state.selection.$from.pos)))).scrollIntoView());
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// This function captures clicks outside of the ProseMirror contentEditable area
|
|
171
|
+
// see also description of "handleClick" in gap-cursor pm-plugin
|
|
172
|
+
var captureCursorCoords = function captureCursorCoords(event, editorRef, posAtCoords, tr) {
|
|
173
|
+
var rect = editorRef.getBoundingClientRect();
|
|
174
|
+
|
|
175
|
+
// capture clicks before the first block element
|
|
176
|
+
if (event.clientY < rect.top) {
|
|
177
|
+
return {
|
|
178
|
+
position: 0,
|
|
179
|
+
side: Side.LEFT
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (rect.left > 0) {
|
|
183
|
+
// calculate start position of a node that is vertically at the same level
|
|
184
|
+
var _coords = posAtCoords({
|
|
185
|
+
left: rect.left,
|
|
186
|
+
top: event.clientY
|
|
187
|
+
});
|
|
188
|
+
if (_coords && _coords.inside > -1) {
|
|
189
|
+
var $from = tr.doc.resolve(_coords.inside);
|
|
190
|
+
var start = $from.before(1);
|
|
191
|
+
var side = event.clientX < rect.left ? Side.LEFT : Side.RIGHT;
|
|
192
|
+
var position;
|
|
193
|
+
if (side === Side.LEFT) {
|
|
194
|
+
position = start;
|
|
195
|
+
} else {
|
|
196
|
+
var node = tr.doc.nodeAt(start);
|
|
197
|
+
if (node) {
|
|
198
|
+
position = start + node.nodeSize;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
position: position,
|
|
203
|
+
side: side
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
};
|
|
209
|
+
export var setSelectionTopLevelBlocks = function setSelectionTopLevelBlocks(tr, event, editorRef, posAtCoords, editorFocused) {
|
|
210
|
+
var cursorCoords = captureCursorCoords(event, editorRef, posAtCoords, tr);
|
|
211
|
+
if (!cursorCoords) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
var $pos = cursorCoords.position !== undefined ? tr.doc.resolve(cursorCoords.position) : null;
|
|
215
|
+
if ($pos === null) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
var isGapCursorAllowed = cursorCoords.side === Side.LEFT ? isValidTargetNode($pos.nodeAfter) : isValidTargetNode($pos.nodeBefore);
|
|
219
|
+
if (isGapCursorAllowed && GapCursorSelection.valid($pos)) {
|
|
220
|
+
// this forces PM to re-render the decoration node if we change the side of the gap cursor, it doesn't do it by default
|
|
221
|
+
if (tr.selection instanceof GapCursorSelection) {
|
|
222
|
+
tr.setSelection(Selection.near($pos));
|
|
223
|
+
} else {
|
|
224
|
+
tr.setSelection(new GapCursorSelection($pos, cursorCoords.side));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// try to set text selection if the editor isnt focused
|
|
228
|
+
// if the editor is focused, we are most likely dragging a selection outside.
|
|
229
|
+
else if (editorFocused === false) {
|
|
230
|
+
var selectionTemp = Selection.findFrom($pos, cursorCoords.side === Side.LEFT ? 1 : -1, true);
|
|
231
|
+
if (selectionTemp) {
|
|
232
|
+
tr.setSelection(selectionTemp);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
export var setCursorForTopLevelBlocks = function setCursorForTopLevelBlocks(event, editorRef, posAtCoords, editorFocused) {
|
|
237
|
+
return function (state, dispatch) {
|
|
238
|
+
var tr = state.tr;
|
|
239
|
+
setSelectionTopLevelBlocks(tr, event, editorRef, posAtCoords, editorFocused);
|
|
240
|
+
if (tr.selectionSet && dispatch) {
|
|
241
|
+
dispatch(tr);
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
export var hasGapCursorPlugin = function hasGapCursorPlugin(state) {
|
|
248
|
+
return Boolean(gapCursorPluginKey.get(state));
|
|
249
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export var Direction = /*#__PURE__*/function (Direction) {
|
|
2
|
+
Direction["UP"] = "up";
|
|
3
|
+
Direction["RIGHT"] = "right";
|
|
4
|
+
Direction["DOWN"] = "down";
|
|
5
|
+
Direction["LEFT"] = "left";
|
|
6
|
+
Direction["BACKWARD"] = "backward";
|
|
7
|
+
Direction["FORWARD"] = "forward";
|
|
8
|
+
return Direction;
|
|
9
|
+
}({});
|
|
10
|
+
export function isBackward(dir) {
|
|
11
|
+
return [Direction.UP, Direction.LEFT, Direction.BACKWARD].indexOf(dir) !== -1;
|
|
12
|
+
}
|
|
13
|
+
export function isForward(dir) {
|
|
14
|
+
return [Direction.RIGHT, Direction.DOWN, Direction.FORWARD].indexOf(dir) !== -1;
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { GapCursorSelection, JSON_ID, Side, GapBookmark } from '@atlaskit/editor-common/selection';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { isIgnored } from '@atlaskit/editor-common/selection';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { isValidTargetNode } from '@atlaskit/editor-common/selection';
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import _toArray from "@babel/runtime/helpers/toArray";
|
|
2
|
+
import { Side } from '@atlaskit/editor-common/selection';
|
|
3
|
+
import { getComputedStyleForLayoutMode, getLayoutModeFromTargetNode, isLeftCursor } from '../utils';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* We have a couple of nodes that require us to compute style
|
|
7
|
+
* on different elements, ideally all nodes should be able to
|
|
8
|
+
* compute the appropriate styles based on their wrapper.
|
|
9
|
+
*/
|
|
10
|
+
var nestedCases = {
|
|
11
|
+
'tableView-content-wrap': 'table',
|
|
12
|
+
'mediaSingleView-content-wrap': '.rich-media-item',
|
|
13
|
+
'bodiedExtensionView-content-wrap': '.extension-container',
|
|
14
|
+
'embedCardView-content-wrap': '.rich-media-item',
|
|
15
|
+
'datasourceView-content-wrap': '.datasourceView-content-inner-wrap'
|
|
16
|
+
};
|
|
17
|
+
var computeNestedStyle = function computeNestedStyle(dom) {
|
|
18
|
+
var foundKey = Object.keys(nestedCases).find(function (className) {
|
|
19
|
+
return dom.classList.contains(className);
|
|
20
|
+
});
|
|
21
|
+
var nestedSelector = foundKey && nestedCases[foundKey];
|
|
22
|
+
if (nestedSelector) {
|
|
23
|
+
var nestedElement = dom.querySelector(nestedSelector);
|
|
24
|
+
if (nestedElement) {
|
|
25
|
+
return window.getComputedStyle(nestedElement);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var measureHeight = function measureHeight(style) {
|
|
30
|
+
return measureValue(style, ['height', 'padding-top', 'padding-bottom', 'border-top-width', 'border-bottom-width']);
|
|
31
|
+
};
|
|
32
|
+
var measureWidth = function measureWidth(style) {
|
|
33
|
+
return measureValue(style, ['width', 'padding-left', 'padding-right', 'border-left-width', 'border-right-width']);
|
|
34
|
+
};
|
|
35
|
+
var measureValue = function measureValue(style, measureValues) {
|
|
36
|
+
var _measureValues = _toArray(measureValues),
|
|
37
|
+
base = _measureValues[0],
|
|
38
|
+
contentBoxValues = _measureValues.slice(1);
|
|
39
|
+
var measures = [style.getPropertyValue(base)];
|
|
40
|
+
var boxSizing = style.getPropertyValue('box-sizing');
|
|
41
|
+
if (boxSizing === 'content-box') {
|
|
42
|
+
contentBoxValues.forEach(function (value) {
|
|
43
|
+
measures.push(style.getPropertyValue(value));
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
var result = 0;
|
|
47
|
+
for (var i = 0; i < measures.length; i++) {
|
|
48
|
+
result += parseFloat(measures[i]);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
var mutateElementStyle = function mutateElementStyle(element, style, side) {
|
|
53
|
+
element.style.transform = style.getPropertyValue('transform');
|
|
54
|
+
if (isLeftCursor(side)) {
|
|
55
|
+
element.style.width = style.getPropertyValue('width');
|
|
56
|
+
element.style.marginLeft = style.getPropertyValue('margin-left');
|
|
57
|
+
} else {
|
|
58
|
+
var marginRight = parseFloat(style.getPropertyValue('margin-right'));
|
|
59
|
+
if (marginRight > 0) {
|
|
60
|
+
element.style.marginLeft = "-".concat(Math.abs(marginRight), "px");
|
|
61
|
+
} else {
|
|
62
|
+
element.style.paddingRight = "".concat(Math.abs(marginRight), "px");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
export var toDOM = function toDOM(view, getPos) {
|
|
67
|
+
var selection = view.state.selection;
|
|
68
|
+
var $from = selection.$from,
|
|
69
|
+
side = selection.side;
|
|
70
|
+
var isRightCursor = side === Side.RIGHT;
|
|
71
|
+
var node = isRightCursor ? $from.nodeBefore : $from.nodeAfter;
|
|
72
|
+
var nodeStart = getPos();
|
|
73
|
+
// @ts-ignore - [unblock prosemirror bump] nodeStart can be undefined
|
|
74
|
+
var dom = view.nodeDOM(nodeStart);
|
|
75
|
+
var element = document.createElement('span');
|
|
76
|
+
element.className = "ProseMirror-gapcursor ".concat(isRightCursor ? '-right' : '-left');
|
|
77
|
+
element.appendChild(document.createElement('span'));
|
|
78
|
+
if (dom instanceof HTMLElement && element.firstChild) {
|
|
79
|
+
var style = computeNestedStyle(dom) || window.getComputedStyle(dom);
|
|
80
|
+
var gapCursor = element.firstChild;
|
|
81
|
+
gapCursor.style.height = "".concat(measureHeight(style), "px");
|
|
82
|
+
var layoutMode = node && getLayoutModeFromTargetNode(node);
|
|
83
|
+
|
|
84
|
+
// TODO remove this table specific piece. need to figure out margin collapsing logic
|
|
85
|
+
if (nodeStart !== 0 || layoutMode || (node === null || node === void 0 ? void 0 : node.type.name) === 'table') {
|
|
86
|
+
gapCursor.style.marginTop = style.getPropertyValue('margin-top');
|
|
87
|
+
}
|
|
88
|
+
if (layoutMode) {
|
|
89
|
+
gapCursor.setAttribute('layout', layoutMode);
|
|
90
|
+
var breakoutModeStyle = getComputedStyleForLayoutMode(dom, node, style);
|
|
91
|
+
gapCursor.style.width = "".concat(measureWidth(breakoutModeStyle), "px");
|
|
92
|
+
} else {
|
|
93
|
+
mutateElementStyle(gapCursor, style, selection.side);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return element;
|
|
97
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { CAPTION_PLACEHOLDER_ID } from '@atlaskit/editor-common/media-single';
|
|
2
|
+
import { TableSharedCssClassName, UnsupportedSharedCssClassName } from '@atlaskit/editor-common/styles';
|
|
3
|
+
import { Side } from './selection';
|
|
4
|
+
export var isLeftCursor = function isLeftCursor(side) {
|
|
5
|
+
return side === Side.LEFT;
|
|
6
|
+
};
|
|
7
|
+
export function getMediaNearPos(doc, $pos, schema) {
|
|
8
|
+
var dir = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : -1;
|
|
9
|
+
var $currentPos = $pos;
|
|
10
|
+
var currentNode = null;
|
|
11
|
+
var _schema$nodes = schema.nodes,
|
|
12
|
+
mediaSingle = _schema$nodes.mediaSingle,
|
|
13
|
+
media = _schema$nodes.media,
|
|
14
|
+
mediaGroup = _schema$nodes.mediaGroup;
|
|
15
|
+
do {
|
|
16
|
+
$currentPos = doc.resolve(dir === -1 ? $currentPos.before() : $currentPos.after());
|
|
17
|
+
if (!$currentPos) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
currentNode = (dir === -1 ? $currentPos.nodeBefore : $currentPos.nodeAfter) || $currentPos.parent;
|
|
21
|
+
if (!currentNode || currentNode.type === schema.nodes.doc) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (currentNode.type === mediaSingle || currentNode.type === media || currentNode.type === mediaGroup) {
|
|
25
|
+
return currentNode;
|
|
26
|
+
}
|
|
27
|
+
} while ($currentPos.depth > 0);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
export var isTextBlockNearPos = function isTextBlockNearPos(doc, schema, $pos, dir) {
|
|
31
|
+
var $currentPos = $pos;
|
|
32
|
+
var currentNode = dir === -1 ? $currentPos.nodeBefore : $currentPos.nodeAfter;
|
|
33
|
+
|
|
34
|
+
// If next node is a text or a text block bail out early.
|
|
35
|
+
if (currentNode && (currentNode.isTextblock || currentNode.isText)) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
while ($currentPos.depth > 0) {
|
|
39
|
+
$currentPos = doc.resolve(dir === -1 ? $currentPos.before() : $currentPos.after());
|
|
40
|
+
if (!$currentPos) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
currentNode = (dir === -1 ? $currentPos.nodeBefore : $currentPos.nodeAfter) || $currentPos.parent;
|
|
44
|
+
if (!currentNode || currentNode.type === schema.nodes.doc) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (currentNode.isTextblock) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
var childNode = currentNode;
|
|
52
|
+
while (childNode && childNode.firstChild) {
|
|
53
|
+
childNode = childNode.firstChild;
|
|
54
|
+
if (childNode && (childNode.isTextblock || childNode.isText)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
};
|
|
60
|
+
export function getLayoutModeFromTargetNode(node) {
|
|
61
|
+
var layout;
|
|
62
|
+
if (node.attrs.layout) {
|
|
63
|
+
layout = node.attrs.layout;
|
|
64
|
+
}
|
|
65
|
+
if (node.marks && node.marks.length) {
|
|
66
|
+
layout = (node.marks.find(function (mark) {
|
|
67
|
+
return mark.type.name === 'breakout';
|
|
68
|
+
}) || {
|
|
69
|
+
attrs: {
|
|
70
|
+
mode: ''
|
|
71
|
+
}
|
|
72
|
+
}).attrs.mode;
|
|
73
|
+
}
|
|
74
|
+
if (node.type.name === 'table' && node.attrs.width) {
|
|
75
|
+
layout = 'fixed-width';
|
|
76
|
+
}
|
|
77
|
+
if (['wide', 'full-width', 'fixed-width'].indexOf(layout) === -1) {
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
80
|
+
return layout;
|
|
81
|
+
}
|
|
82
|
+
export var isIgnoredClick = function isIgnoredClick(elem) {
|
|
83
|
+
if (elem.nodeName === 'BUTTON' || elem.closest('button')) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// check if we're clicking an image caption placeholder
|
|
88
|
+
if (elem.closest("[data-id=\"".concat(CAPTION_PLACEHOLDER_ID, "\"]"))) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// check if target node has a parent table node
|
|
93
|
+
var tableWrap;
|
|
94
|
+
var node = elem;
|
|
95
|
+
while (node) {
|
|
96
|
+
if (node.className && (node.getAttribute('class') || '').indexOf(TableSharedCssClassName.TABLE_CONTAINER) > -1) {
|
|
97
|
+
tableWrap = node;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
node = node.parentNode;
|
|
101
|
+
}
|
|
102
|
+
if (tableWrap) {
|
|
103
|
+
var rowControls = tableWrap.querySelector(".".concat(TableSharedCssClassName.TABLE_ROW_CONTROLS_WRAPPER));
|
|
104
|
+
var isColumnControlsDecoration = elem && elem.classList && elem.classList.contains(TableSharedCssClassName.TABLE_COLUMN_CONTROLS_DECORATIONS);
|
|
105
|
+
return rowControls && rowControls.contains(elem) || isColumnControlsDecoration;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check if unsupported node selection
|
|
109
|
+
// (without this, selection requires double clicking in FF due to posAtCoords differences)
|
|
110
|
+
if (elem.closest(".".concat(UnsupportedSharedCssClassName.BLOCK_CONTAINER))) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/*
|
|
117
|
+
Calculates custom style for breakout mode
|
|
118
|
+
Mainly to handle table width to include the numbered column width as well
|
|
119
|
+
*/
|
|
120
|
+
export var getComputedStyleForLayoutMode = function getComputedStyleForLayoutMode(dom, node, style) {
|
|
121
|
+
if (node && node.type.name === 'table') {
|
|
122
|
+
var tableContainer = dom.querySelector('.pm-table-container');
|
|
123
|
+
if (tableContainer) {
|
|
124
|
+
return window.getComputedStyle(tableContainer);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return style;
|
|
128
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { selectionPlugin } from './plugin';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
import { pluginFactory } from '@atlaskit/editor-common/utils';
|
|
5
|
+
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
|
|
6
|
+
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
7
|
+
import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
|
|
8
|
+
import { reducer } from './reducer';
|
|
9
|
+
import { selectionPluginKey } from './types';
|
|
10
|
+
import { getDecorations, isSelectableContainerNode } from './utils';
|
|
11
|
+
var handleDocChanged = function handleDocChanged(tr, pluginState) {
|
|
12
|
+
// in some collab edge cases mapping decorations could throw an error
|
|
13
|
+
try {
|
|
14
|
+
if (pluginState.decorationSet.find().length === 0 && (!tr.selectionSet || getDecorations(tr).find().length === 0)) {
|
|
15
|
+
return pluginState;
|
|
16
|
+
}
|
|
17
|
+
var decorationSet = pluginState.decorationSet.map(tr.mapping, tr.doc);
|
|
18
|
+
return _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
19
|
+
decorationSet: decorationSet
|
|
20
|
+
});
|
|
21
|
+
} catch (error) {
|
|
22
|
+
return _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
23
|
+
decorationSet: DecorationSet.empty
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var handleSelectionChanged = function handleSelectionChanged(tr, pluginState) {
|
|
28
|
+
// Reset relative selection pos when user clicks to select a node
|
|
29
|
+
if ((tr.selection instanceof NodeSelection && isSelectableContainerNode(tr.selection.node) || tr.selection instanceof CellSelection) && !tr.getMeta(selectionPluginKey)) {
|
|
30
|
+
return _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
31
|
+
selectionRelativeToNode: undefined
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return pluginState;
|
|
35
|
+
};
|
|
36
|
+
var _pluginFactory = pluginFactory(selectionPluginKey, reducer, {
|
|
37
|
+
onDocChanged: handleDocChanged,
|
|
38
|
+
onSelectionChanged: handleSelectionChanged
|
|
39
|
+
}),
|
|
40
|
+
createCommand = _pluginFactory.createCommand,
|
|
41
|
+
getPluginState = _pluginFactory.getPluginState,
|
|
42
|
+
createPluginState = _pluginFactory.createPluginState;
|
|
43
|
+
export { createCommand, getPluginState, createPluginState };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { selectNearNode as _selectNearNode } from './commands';
|
|
2
|
+
import gapCursorKeymapPlugin from './pm-plugins/gap-cursor-keymap';
|
|
3
|
+
import gapCursorPlugin from './pm-plugins/gap-cursor-main';
|
|
4
|
+
import { gapCursorPluginKey } from './pm-plugins/gap-cursor-plugin-key';
|
|
5
|
+
import selectionKeymapPlugin from './pm-plugins/keymap';
|
|
6
|
+
import { createPlugin } from './pm-plugins/selection-main';
|
|
7
|
+
import { selectionPluginKey } from './types';
|
|
8
|
+
var displayGapCursor = function displayGapCursor(toggle) {
|
|
9
|
+
return function (_ref) {
|
|
10
|
+
var tr = _ref.tr;
|
|
11
|
+
return tr.setMeta(gapCursorPluginKey, {
|
|
12
|
+
displayGapCursor: toggle
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export var selectionPlugin = function selectionPlugin(_ref2) {
|
|
17
|
+
var options = _ref2.config;
|
|
18
|
+
return {
|
|
19
|
+
name: 'selection',
|
|
20
|
+
commands: {
|
|
21
|
+
displayGapCursor: displayGapCursor
|
|
22
|
+
},
|
|
23
|
+
actions: {
|
|
24
|
+
selectNearNode: function selectNearNode(_ref3) {
|
|
25
|
+
var selectionRelativeToNode = _ref3.selectionRelativeToNode,
|
|
26
|
+
selection = _ref3.selection;
|
|
27
|
+
return function (state) {
|
|
28
|
+
return _selectNearNode(selectionRelativeToNode, selection)({
|
|
29
|
+
tr: state.tr
|
|
30
|
+
}) || state.tr;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
getSharedState: function getSharedState(editorState) {
|
|
35
|
+
if (!editorState) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
var pluginState = selectionPluginKey.getState(editorState);
|
|
39
|
+
return {
|
|
40
|
+
selectionRelativeToNode: pluginState === null || pluginState === void 0 ? void 0 : pluginState.selectionRelativeToNode
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
pmPlugins: function pmPlugins() {
|
|
44
|
+
return [{
|
|
45
|
+
name: 'selection',
|
|
46
|
+
plugin: function plugin(_ref4) {
|
|
47
|
+
var dispatch = _ref4.dispatch,
|
|
48
|
+
dispatchAnalyticsEvent = _ref4.dispatchAnalyticsEvent;
|
|
49
|
+
return createPlugin(dispatch, dispatchAnalyticsEvent, options);
|
|
50
|
+
}
|
|
51
|
+
}, {
|
|
52
|
+
name: 'selectionKeymap',
|
|
53
|
+
plugin: selectionKeymapPlugin
|
|
54
|
+
}, {
|
|
55
|
+
name: 'gapCursorKeymap',
|
|
56
|
+
plugin: function plugin() {
|
|
57
|
+
return gapCursorKeymapPlugin();
|
|
58
|
+
}
|
|
59
|
+
}, {
|
|
60
|
+
name: 'gapCursor',
|
|
61
|
+
plugin: function plugin() {
|
|
62
|
+
return gapCursorPlugin;
|
|
63
|
+
}
|
|
64
|
+
}];
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
export default selectionPlugin;
|