@ckeditor/ckeditor5-widget 35.2.0 → 35.3.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/package.json +31 -23
- package/src/highlightstack.js +105 -139
- package/src/index.js +0 -1
- package/src/utils.js +127 -181
- package/src/verticalnavigation.js +144 -187
- package/src/widget.js +359 -435
- package/src/widgetresize/resizer.js +412 -505
- package/src/widgetresize/resizerstate.js +154 -176
- package/src/widgetresize/sizeview.js +79 -98
- package/src/widgetresize.js +199 -297
- package/src/widgettoolbarrepository.js +244 -296
- package/src/widgettypearound/utils.js +11 -20
- package/src/widgettypearound/widgettypearound.js +748 -876
@@ -2,87 +2,72 @@
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
4
|
*/
|
5
|
-
|
6
5
|
import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
|
7
6
|
import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
|
8
|
-
|
9
7
|
/**
|
10
8
|
* @module widget/verticalnavigationhandler
|
11
9
|
*/
|
12
|
-
|
13
10
|
/**
|
14
11
|
* Returns 'keydown' handler for up/down arrow keys that modifies the caret movement if it's in a text line next to an object.
|
15
12
|
*
|
16
13
|
* @param {module:engine/controller/editingcontroller~EditingController} editing The editing controller.
|
17
14
|
* @returns {Function}
|
18
15
|
*/
|
19
|
-
export default function verticalNavigationHandler(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
writer.setSelection( newSelection );
|
74
|
-
} else {
|
75
|
-
writer.setSelection( newPosition );
|
76
|
-
}
|
77
|
-
} );
|
78
|
-
|
79
|
-
evt.stop();
|
80
|
-
data.preventDefault();
|
81
|
-
data.stopPropagation();
|
82
|
-
}
|
83
|
-
};
|
16
|
+
export default function verticalNavigationHandler(editing) {
|
17
|
+
const model = editing.model;
|
18
|
+
return (evt, data) => {
|
19
|
+
const arrowUpPressed = data.keyCode == keyCodes.arrowup;
|
20
|
+
const arrowDownPressed = data.keyCode == keyCodes.arrowdown;
|
21
|
+
const expandSelection = data.shiftKey;
|
22
|
+
const selection = model.document.selection;
|
23
|
+
if (!arrowUpPressed && !arrowDownPressed) {
|
24
|
+
return;
|
25
|
+
}
|
26
|
+
const isForward = arrowDownPressed;
|
27
|
+
// Navigation is in the opposite direction than the selection direction so this is shrinking of the selection.
|
28
|
+
// Selection for sure will not approach any object.
|
29
|
+
if (expandSelection && selectionWillShrink(selection, isForward)) {
|
30
|
+
return;
|
31
|
+
}
|
32
|
+
// Find a range between selection and closest limit element.
|
33
|
+
const range = findTextRangeFromSelection(editing, selection, isForward);
|
34
|
+
// There is no selection position inside the limit element.
|
35
|
+
if (!range) {
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
// If already at the edge of a limit element.
|
39
|
+
if (range.isCollapsed) {
|
40
|
+
// A collapsed selection at limit edge - nothing more to do.
|
41
|
+
if (selection.isCollapsed) {
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
// A non collapsed selection is at the limit edge while expanding the selection - let others do their stuff.
|
45
|
+
else if (expandSelection) {
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
// If the range is a single line (there is no word wrapping) then move the selection to the position closest to the limit element.
|
50
|
+
//
|
51
|
+
// We can't move the selection directly to the isObject element (eg. table cell) because of dual position at the end/beginning
|
52
|
+
// of wrapped line (it's at the same time at the end of one line and at the start of the next line).
|
53
|
+
if (range.isCollapsed || isSingleLineRange(editing, range, isForward)) {
|
54
|
+
model.change(writer => {
|
55
|
+
const newPosition = isForward ? range.end : range.start;
|
56
|
+
if (expandSelection) {
|
57
|
+
const newSelection = model.createSelection(selection.anchor);
|
58
|
+
newSelection.setFocus(newPosition);
|
59
|
+
writer.setSelection(newSelection);
|
60
|
+
}
|
61
|
+
else {
|
62
|
+
writer.setSelection(newPosition);
|
63
|
+
}
|
64
|
+
});
|
65
|
+
evt.stop();
|
66
|
+
data.preventDefault();
|
67
|
+
data.stopPropagation();
|
68
|
+
}
|
69
|
+
};
|
84
70
|
}
|
85
|
-
|
86
71
|
// Finds the range between selection and closest limit element (in the direction of navigation).
|
87
72
|
// The position next to limit element is adjusted to the closest allowed `$text` position.
|
88
73
|
//
|
@@ -93,46 +78,37 @@ export default function verticalNavigationHandler( editing ) {
|
|
93
78
|
// @param {Boolean} isForward The expected navigation direction.
|
94
79
|
// @returns {module:engine/model/range~Range|null}
|
95
80
|
//
|
96
|
-
function findTextRangeFromSelection(
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
const firstRangePosition = getNearestTextPosition( model.schema, range, 'forward' );
|
127
|
-
|
128
|
-
if ( firstRangePosition ) {
|
129
|
-
return model.createRange( firstRangePosition, endPosition );
|
130
|
-
}
|
131
|
-
|
132
|
-
return null;
|
133
|
-
}
|
81
|
+
function findTextRangeFromSelection(editing, selection, isForward) {
|
82
|
+
const model = editing.model;
|
83
|
+
if (isForward) {
|
84
|
+
const startPosition = selection.isCollapsed ? selection.focus : selection.getLastPosition();
|
85
|
+
const endPosition = getNearestNonInlineLimit(model, startPosition, 'forward');
|
86
|
+
// There is no limit element, browser should handle this.
|
87
|
+
if (!endPosition) {
|
88
|
+
return null;
|
89
|
+
}
|
90
|
+
const range = model.createRange(startPosition, endPosition);
|
91
|
+
const lastRangePosition = getNearestTextPosition(model.schema, range, 'backward');
|
92
|
+
if (lastRangePosition) {
|
93
|
+
return model.createRange(startPosition, lastRangePosition);
|
94
|
+
}
|
95
|
+
return null;
|
96
|
+
}
|
97
|
+
else {
|
98
|
+
const endPosition = selection.isCollapsed ? selection.focus : selection.getFirstPosition();
|
99
|
+
const startPosition = getNearestNonInlineLimit(model, endPosition, 'backward');
|
100
|
+
// There is no limit element, browser should handle this.
|
101
|
+
if (!startPosition) {
|
102
|
+
return null;
|
103
|
+
}
|
104
|
+
const range = model.createRange(startPosition, endPosition);
|
105
|
+
const firstRangePosition = getNearestTextPosition(model.schema, range, 'forward');
|
106
|
+
if (firstRangePosition) {
|
107
|
+
return model.createRange(firstRangePosition, endPosition);
|
108
|
+
}
|
109
|
+
return null;
|
110
|
+
}
|
134
111
|
}
|
135
|
-
|
136
112
|
// Finds the limit element position that is closest to startPosition.
|
137
113
|
//
|
138
114
|
// @param {module:engine/model/model~Model} model
|
@@ -140,26 +116,21 @@ function findTextRangeFromSelection( editing, selection, isForward ) {
|
|
140
116
|
// @param {'forward'|'backward'} direction Search direction.
|
141
117
|
// @returns {<module:engine/model/position~Position>|null}
|
142
118
|
//
|
143
|
-
function getNearestNonInlineLimit(
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
}
|
158
|
-
}
|
159
|
-
|
160
|
-
return null;
|
119
|
+
function getNearestNonInlineLimit(model, startPosition, direction) {
|
120
|
+
const schema = model.schema;
|
121
|
+
const range = model.createRangeIn(startPosition.root);
|
122
|
+
const walkerValueType = direction == 'forward' ? 'elementStart' : 'elementEnd';
|
123
|
+
for (const { previousPosition, item, type } of range.getWalker({ startPosition, direction })) {
|
124
|
+
if (schema.isLimit(item) && !schema.isInline(item)) {
|
125
|
+
return previousPosition;
|
126
|
+
}
|
127
|
+
// Stop looking for isLimit element if the next element is a block element (it is for sure not single line).
|
128
|
+
if (type == walkerValueType && schema.isBlock(item)) {
|
129
|
+
return null;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
return null;
|
161
133
|
}
|
162
|
-
|
163
134
|
// Basing on the provided range, finds the first or last (depending on `direction`) position inside the range
|
164
135
|
// that can contain `$text` (according to schema).
|
165
136
|
//
|
@@ -168,22 +139,18 @@ function getNearestNonInlineLimit( model, startPosition, direction ) {
|
|
168
139
|
// @param {'forward'|'backward'} direction Search direction.
|
169
140
|
// @returns {module:engine/model/position~Position|null} The nearest selection position.
|
170
141
|
//
|
171
|
-
function getNearestTextPosition(
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
}
|
183
|
-
|
184
|
-
return null;
|
142
|
+
function getNearestTextPosition(schema, range, direction) {
|
143
|
+
const position = direction == 'backward' ? range.end : range.start;
|
144
|
+
if (schema.checkChild(position, '$text')) {
|
145
|
+
return position;
|
146
|
+
}
|
147
|
+
for (const { nextPosition } of range.getWalker({ direction })) {
|
148
|
+
if (schema.checkChild(nextPosition, '$text')) {
|
149
|
+
return nextPosition;
|
150
|
+
}
|
151
|
+
}
|
152
|
+
return null;
|
185
153
|
}
|
186
|
-
|
187
154
|
// Checks if the DOM range corresponding to the provided model range renders as a single line by analyzing DOMRects
|
188
155
|
// (verifying if they visually wrap content to the next line).
|
189
156
|
//
|
@@ -192,50 +159,40 @@ function getNearestTextPosition( schema, range, direction ) {
|
|
192
159
|
// @param {Boolean} isForward The expected navigation direction.
|
193
160
|
// @returns {Boolean}
|
194
161
|
//
|
195
|
-
function isSingleLineRange(
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
// Let's check if this rect is in new line.
|
229
|
-
if ( Math.round( rect.top ) >= boundaryVerticalPosition ) {
|
230
|
-
return false;
|
231
|
-
}
|
232
|
-
|
233
|
-
boundaryVerticalPosition = Math.max( boundaryVerticalPosition, Math.round( rect.bottom ) );
|
234
|
-
}
|
235
|
-
|
236
|
-
return true;
|
162
|
+
function isSingleLineRange(editing, modelRange, isForward) {
|
163
|
+
const model = editing.model;
|
164
|
+
const domConverter = editing.view.domConverter;
|
165
|
+
// Wrapped lines contain exactly the same position at the end of current line
|
166
|
+
// and at the beginning of next line. That position's client rect is at the end
|
167
|
+
// of current line. In case of caret at first position of the last line that 'dual'
|
168
|
+
// position would be detected as it's not the last line.
|
169
|
+
if (isForward) {
|
170
|
+
const probe = model.createSelection(modelRange.start);
|
171
|
+
model.modifySelection(probe);
|
172
|
+
// If the new position is at the end of the container then we can't use this position
|
173
|
+
// because it would provide incorrect result for eg caption of image and selection
|
174
|
+
// just before end of it. Also in this case there is no "dual" position.
|
175
|
+
if (!probe.focus.isAtEnd && !modelRange.start.isEqual(probe.focus)) {
|
176
|
+
modelRange = model.createRange(probe.focus, modelRange.end);
|
177
|
+
}
|
178
|
+
}
|
179
|
+
const viewRange = editing.mapper.toViewRange(modelRange);
|
180
|
+
const domRange = domConverter.viewRangeToDom(viewRange);
|
181
|
+
const rects = Rect.getDomRangeRects(domRange);
|
182
|
+
let boundaryVerticalPosition;
|
183
|
+
for (const rect of rects) {
|
184
|
+
if (boundaryVerticalPosition === undefined) {
|
185
|
+
boundaryVerticalPosition = Math.round(rect.bottom);
|
186
|
+
continue;
|
187
|
+
}
|
188
|
+
// Let's check if this rect is in new line.
|
189
|
+
if (Math.round(rect.top) >= boundaryVerticalPosition) {
|
190
|
+
return false;
|
191
|
+
}
|
192
|
+
boundaryVerticalPosition = Math.max(boundaryVerticalPosition, Math.round(rect.bottom));
|
193
|
+
}
|
194
|
+
return true;
|
237
195
|
}
|
238
|
-
|
239
|
-
|
240
|
-
return !selection.isCollapsed && selection.isBackward == isForward;
|
196
|
+
function selectionWillShrink(selection, isForward) {
|
197
|
+
return !selection.isCollapsed && selection.isBackward == isForward;
|
241
198
|
}
|