@ckeditor/ckeditor5-enter 48.2.0 → 48.3.0-alpha.1
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/dist/augmentation.d.ts +13 -13
- package/dist/enter.d.ts +21 -21
- package/dist/entercommand.d.ts +44 -44
- package/dist/enterobserver.d.ts +34 -34
- package/dist/index-content.css +1 -0
- package/dist/index-editor.css +1 -0
- package/dist/index.css +0 -2
- package/dist/index.d.ts +12 -12
- package/dist/index.js +345 -393
- package/dist/index.js.map +1 -1
- package/dist/shiftenter.d.ts +19 -19
- package/dist/shiftentercommand.d.ts +26 -26
- package/dist/utils.d.ts +15 -15
- package/package.json +4 -4
- package/dist/index.css.map +0 -1
package/dist/index.js
CHANGED
|
@@ -2,427 +2,379 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
4
|
*/
|
|
5
|
-
import { Command, Plugin } from
|
|
6
|
-
import {
|
|
7
|
-
import { env } from
|
|
5
|
+
import { Command, Plugin } from "@ckeditor/ckeditor5-core";
|
|
6
|
+
import { BubblingEventInfo, Observer, ViewDocumentDomEventData } from "@ckeditor/ckeditor5-engine";
|
|
7
|
+
import { env } from "@ckeditor/ckeditor5-utils";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (attribute && schema.getAttributeProperties(attribute[0]).copyOnEnter) {
|
|
26
|
-
yield attribute;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
10
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
11
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Returns attributes that should be preserved on the enter keystroke.
|
|
15
|
+
*
|
|
16
|
+
* Filtering is realized based on `copyOnEnter` attribute property. Read more about attribute properties
|
|
17
|
+
* {@link module:engine/model/schema~ModelSchema#setAttributeProperties here}.
|
|
18
|
+
*
|
|
19
|
+
* @param schema Model's schema.
|
|
20
|
+
* @param allAttributes Attributes to filter.
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
function* getCopyOnEnterAttributes(schema, allAttributes) {
|
|
24
|
+
for (const attribute of allAttributes) if (attribute && schema.getAttributeProperties(attribute[0]).copyOnEnter) yield attribute;
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
/**
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
} else {
|
|
103
|
-
writer.setSelection(endElement, 0);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
28
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
29
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* @module enter/entercommand
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Enter command used by the {@link module:enter/enter~Enter Enter feature} to handle the <kbd>Enter</kbd> keystroke.
|
|
36
|
+
*/
|
|
37
|
+
var EnterCommand = class extends Command {
|
|
38
|
+
/**
|
|
39
|
+
* @inheritDoc
|
|
40
|
+
*/
|
|
41
|
+
execute() {
|
|
42
|
+
this.editor.model.change((writer) => {
|
|
43
|
+
this.enterBlock(writer);
|
|
44
|
+
this.fire("afterExecute", { writer });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Splits a block where the document selection is placed, in the way how the <kbd>Enter</kbd> key is expected to work:
|
|
49
|
+
*
|
|
50
|
+
* ```
|
|
51
|
+
* <p>Foo[]bar</p> -> <p>Foo</p><p>[]bar</p>
|
|
52
|
+
* <p>Foobar[]</p> -> <p>Foobar</p><p>[]</p>
|
|
53
|
+
* <p>Fo[ob]ar</p> -> <p>Fo</p><p>[]ar</p>
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* In some cases, the split will not happen:
|
|
57
|
+
*
|
|
58
|
+
* ```
|
|
59
|
+
* // The selection parent is a limit element:
|
|
60
|
+
* <figcaption>A[bc]d</figcaption> -> <figcaption>A[]d</figcaption>
|
|
61
|
+
*
|
|
62
|
+
* // The selection spans over multiple elements:
|
|
63
|
+
* <h>x[x</h><p>y]y<p> -> <h>x</h><p>[]y</p>
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @param writer Writer to use when performing the enter action.
|
|
67
|
+
* @returns Boolean indicating if the block was split.
|
|
68
|
+
*/
|
|
69
|
+
enterBlock(writer) {
|
|
70
|
+
const model = this.editor.model;
|
|
71
|
+
const selection = model.document.selection;
|
|
72
|
+
const schema = model.schema;
|
|
73
|
+
const isSelectionEmpty = selection.isCollapsed;
|
|
74
|
+
const range = selection.getFirstRange();
|
|
75
|
+
const startElement = range.start.parent;
|
|
76
|
+
const endElement = range.end.parent;
|
|
77
|
+
if (schema.isLimit(startElement) || schema.isLimit(endElement)) {
|
|
78
|
+
if (!isSelectionEmpty && startElement == endElement) model.deleteContent(selection);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if (isSelectionEmpty) {
|
|
82
|
+
const attributesToCopy = getCopyOnEnterAttributes(writer.model.schema, selection.getAttributes());
|
|
83
|
+
splitBlock(writer, range.start);
|
|
84
|
+
writer.setSelectionAttribute(attributesToCopy);
|
|
85
|
+
return true;
|
|
86
|
+
} else {
|
|
87
|
+
const leaveUnmerged = !(range.start.isAtStart && range.end.isAtEnd);
|
|
88
|
+
const isContainedWithinOneElement = startElement == endElement;
|
|
89
|
+
model.deleteContent(selection, { leaveUnmerged });
|
|
90
|
+
if (leaveUnmerged) if (isContainedWithinOneElement) {
|
|
91
|
+
splitBlock(writer, selection.focus);
|
|
92
|
+
return true;
|
|
93
|
+
} else writer.setSelection(endElement, 0);
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
110
98
|
function splitBlock(writer, splitPos) {
|
|
111
|
-
|
|
112
|
-
|
|
99
|
+
writer.split(splitPos);
|
|
100
|
+
writer.setSelection(splitPos.parent.nextSibling, 0);
|
|
113
101
|
}
|
|
114
102
|
|
|
103
|
+
/**
|
|
104
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
105
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
106
|
+
*/
|
|
107
|
+
/**
|
|
108
|
+
* @module enter/enterobserver
|
|
109
|
+
*/
|
|
115
110
|
const ENTER_EVENT_TYPES = {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
},
|
|
119
|
-
insertLineBreak: {
|
|
120
|
-
isSoft: true
|
|
121
|
-
}
|
|
111
|
+
insertParagraph: { isSoft: false },
|
|
112
|
+
insertLineBreak: { isSoft: true }
|
|
122
113
|
};
|
|
123
114
|
/**
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* @inheritDoc
|
|
162
|
-
*/ observe() {}
|
|
163
|
-
/**
|
|
164
|
-
* @inheritDoc
|
|
165
|
-
*/ stopObserving() {}
|
|
166
|
-
}
|
|
115
|
+
* Enter observer introduces the {@link module:engine/view/document~ViewDocument#event:enter `Document#enter`} event.
|
|
116
|
+
*/
|
|
117
|
+
var EnterObserver = class extends Observer {
|
|
118
|
+
/**
|
|
119
|
+
* @inheritDoc
|
|
120
|
+
*/
|
|
121
|
+
constructor(view) {
|
|
122
|
+
super(view);
|
|
123
|
+
const doc = this.document;
|
|
124
|
+
let shiftPressed = false;
|
|
125
|
+
doc.on("keydown", (evt, data) => {
|
|
126
|
+
shiftPressed = data.shiftKey;
|
|
127
|
+
});
|
|
128
|
+
doc.on("beforeinput", (evt, data) => {
|
|
129
|
+
if (!this.isEnabled) return;
|
|
130
|
+
let inputType = data.inputType;
|
|
131
|
+
if (env.isSafari && shiftPressed && inputType == "insertParagraph") inputType = "insertLineBreak";
|
|
132
|
+
const domEvent = data.domEvent;
|
|
133
|
+
const enterEventSpec = ENTER_EVENT_TYPES[inputType];
|
|
134
|
+
if (!enterEventSpec) return;
|
|
135
|
+
const event = new BubblingEventInfo(doc, "enter", data.targetRanges[0]);
|
|
136
|
+
doc.fire(event, new ViewDocumentDomEventData(view, domEvent, { isSoft: enterEventSpec.isSoft }));
|
|
137
|
+
if (event.stop.called) evt.stop();
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* @inheritDoc
|
|
142
|
+
*/
|
|
143
|
+
observe() {}
|
|
144
|
+
/**
|
|
145
|
+
* @inheritDoc
|
|
146
|
+
*/
|
|
147
|
+
stopObserving() {}
|
|
148
|
+
};
|
|
167
149
|
|
|
168
150
|
/**
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
]
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
151
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
152
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
153
|
+
*/
|
|
154
|
+
/**
|
|
155
|
+
* @module enter/enter
|
|
156
|
+
*/
|
|
157
|
+
/**
|
|
158
|
+
* This plugin handles the <kbd>Enter</kbd> keystroke (hard line break) in the editor.
|
|
159
|
+
*
|
|
160
|
+
* See also the {@link module:enter/shiftenter~ShiftEnter} plugin.
|
|
161
|
+
*
|
|
162
|
+
* For more information about this feature see the {@glink api/enter package page}.
|
|
163
|
+
*/
|
|
164
|
+
var Enter = class extends Plugin {
|
|
165
|
+
/**
|
|
166
|
+
* @inheritDoc
|
|
167
|
+
*/
|
|
168
|
+
static get pluginName() {
|
|
169
|
+
return "Enter";
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* @inheritDoc
|
|
173
|
+
*/
|
|
174
|
+
static get isOfficialPlugin() {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
init() {
|
|
178
|
+
const editor = this.editor;
|
|
179
|
+
const view = editor.editing.view;
|
|
180
|
+
const viewDocument = view.document;
|
|
181
|
+
const t = this.editor.t;
|
|
182
|
+
view.addObserver(EnterObserver);
|
|
183
|
+
editor.commands.add("enter", new EnterCommand(editor));
|
|
184
|
+
this.listenTo(viewDocument, "enter", (evt, data) => {
|
|
185
|
+
if (!viewDocument.isComposing) data.preventDefault();
|
|
186
|
+
if (data.isSoft) return;
|
|
187
|
+
editor.execute("enter");
|
|
188
|
+
view.scrollToTheSelection();
|
|
189
|
+
}, { priority: "low" });
|
|
190
|
+
editor.accessibility.addKeystrokeInfos({ keystrokes: [{
|
|
191
|
+
label: t("Insert a hard break (a new paragraph)"),
|
|
192
|
+
keystroke: "Enter"
|
|
193
|
+
}] });
|
|
194
|
+
}
|
|
195
|
+
};
|
|
218
196
|
|
|
219
197
|
/**
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
198
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
199
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
200
|
+
*/
|
|
201
|
+
/**
|
|
202
|
+
* @module enter/shiftentercommand
|
|
203
|
+
*/
|
|
204
|
+
/**
|
|
205
|
+
* ShiftEnter command. It is used by the {@link module:enter/shiftenter~ShiftEnter ShiftEnter feature} to handle
|
|
206
|
+
* the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke.
|
|
207
|
+
*/
|
|
208
|
+
var ShiftEnterCommand = class extends Command {
|
|
209
|
+
/**
|
|
210
|
+
* @inheritDoc
|
|
211
|
+
*/
|
|
212
|
+
execute() {
|
|
213
|
+
const model = this.editor.model;
|
|
214
|
+
const doc = model.document;
|
|
215
|
+
model.change((writer) => {
|
|
216
|
+
softBreakAction(model, writer, doc.selection);
|
|
217
|
+
this.fire("afterExecute", { writer });
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* @inheritDoc
|
|
222
|
+
*/
|
|
223
|
+
refresh() {
|
|
224
|
+
const model = this.editor.model;
|
|
225
|
+
const doc = model.document;
|
|
226
|
+
this.isEnabled = isEnabled(model.schema, doc.selection);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
243
229
|
/**
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
const range = selection.getFirstRange();
|
|
257
|
-
const startElement = range.start.parent;
|
|
258
|
-
const endElement = range.end.parent;
|
|
259
|
-
// Do not modify the content if selection is cross-limit elements.
|
|
260
|
-
if ((isInsideLimitElement(startElement, schema) || isInsideLimitElement(endElement, schema)) && startElement !== endElement) {
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
|
-
return true;
|
|
230
|
+
* Checks whether the ShiftEnter command should be enabled in the specified selection.
|
|
231
|
+
*/
|
|
232
|
+
function isEnabled(schema, selection) {
|
|
233
|
+
if (selection.rangeCount > 1) return false;
|
|
234
|
+
const anchorPos = selection.anchor;
|
|
235
|
+
if (!anchorPos || !schema.checkChild(anchorPos, "softBreak")) return false;
|
|
236
|
+
const range = selection.getFirstRange();
|
|
237
|
+
const startElement = range.start.parent;
|
|
238
|
+
const endElement = range.end.parent;
|
|
239
|
+
if ((isInsideLimitElement(startElement, schema) || isInsideLimitElement(endElement, schema)) && startElement !== endElement) return false;
|
|
240
|
+
return true;
|
|
264
241
|
}
|
|
265
242
|
/**
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
//
|
|
282
|
-
// <h>x[xx]x</h> -> <h>x^x</h> -> <h>x<br>^x</h>
|
|
283
|
-
if (isContainedWithinOneElement) {
|
|
284
|
-
insertBreak(model, writer, selection.focus, selection.getAttributes());
|
|
285
|
-
} else {
|
|
286
|
-
// Move the selection to the 2nd element (last step of the example above).
|
|
287
|
-
if (leaveUnmerged) {
|
|
288
|
-
writer.setSelection(endElement, 0);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
243
|
+
* Creates a break in the way that the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke is expected to work.
|
|
244
|
+
*/
|
|
245
|
+
function softBreakAction(model, writer, selection) {
|
|
246
|
+
const isSelectionEmpty = selection.isCollapsed;
|
|
247
|
+
const range = selection.getFirstRange();
|
|
248
|
+
const startElement = range.start.parent;
|
|
249
|
+
const endElement = range.end.parent;
|
|
250
|
+
const isContainedWithinOneElement = startElement == endElement;
|
|
251
|
+
if (isSelectionEmpty) insertBreak(model, writer, range.end, selection.getAttributes());
|
|
252
|
+
else {
|
|
253
|
+
const leaveUnmerged = !(range.start.isAtStart && range.end.isAtEnd);
|
|
254
|
+
model.deleteContent(selection, { leaveUnmerged });
|
|
255
|
+
if (isContainedWithinOneElement) insertBreak(model, writer, selection.focus, selection.getAttributes());
|
|
256
|
+
else if (leaveUnmerged) writer.setSelection(endElement, 0);
|
|
257
|
+
}
|
|
292
258
|
}
|
|
293
259
|
/**
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
260
|
+
* Inserts a softBreak with applicable attributes.
|
|
261
|
+
*/
|
|
262
|
+
function insertBreak(model, writer, position, selectionAttributes) {
|
|
263
|
+
const attributes = Array.from(getCopyOnEnterAttributes(model.schema, selectionAttributes));
|
|
264
|
+
const breakLineElement = writer.createElement("softBreak", attributes);
|
|
265
|
+
model.insertContent(breakLineElement, position);
|
|
266
|
+
writer.setSelection(breakLineElement, "after");
|
|
300
267
|
}
|
|
301
268
|
/**
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
return schema.isLimit(element) || isInsideLimitElement(element.parent, schema);
|
|
269
|
+
* Checks whether the specified `element` is a child of the limit element.
|
|
270
|
+
*
|
|
271
|
+
* Checking whether the `<p>` element is inside a limit element:
|
|
272
|
+
* - `<$root><p>Text.</p></$root> => false`
|
|
273
|
+
* - `<$root><limitElement><p>Text</p></limitElement></$root> => true`
|
|
274
|
+
*/
|
|
275
|
+
function isInsideLimitElement(element, schema) {
|
|
276
|
+
if (element.is("rootElement")) return false;
|
|
277
|
+
return schema.isLimit(element) || isInsideLimitElement(element.parent, schema);
|
|
313
278
|
}
|
|
314
279
|
|
|
315
280
|
/**
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
keystroke: 'Shift+Enter'
|
|
378
|
-
}
|
|
379
|
-
]
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
}
|
|
281
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
282
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
283
|
+
*/
|
|
284
|
+
/**
|
|
285
|
+
* @module enter/shiftenter
|
|
286
|
+
*/
|
|
287
|
+
/**
|
|
288
|
+
* This plugin handles the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke (soft line break) in the editor.
|
|
289
|
+
*
|
|
290
|
+
* See also the {@link module:enter/enter~Enter} plugin.
|
|
291
|
+
*
|
|
292
|
+
* For more information about this feature see the {@glink api/enter package page}.
|
|
293
|
+
*/
|
|
294
|
+
var ShiftEnter = class extends Plugin {
|
|
295
|
+
/**
|
|
296
|
+
* @inheritDoc
|
|
297
|
+
*/
|
|
298
|
+
static get pluginName() {
|
|
299
|
+
return "ShiftEnter";
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* @inheritDoc
|
|
303
|
+
*/
|
|
304
|
+
static get isOfficialPlugin() {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
init() {
|
|
308
|
+
const editor = this.editor;
|
|
309
|
+
const schema = editor.model.schema;
|
|
310
|
+
const conversion = editor.conversion;
|
|
311
|
+
const view = editor.editing.view;
|
|
312
|
+
const viewDocument = view.document;
|
|
313
|
+
const t = this.editor.t;
|
|
314
|
+
schema.register("softBreak", {
|
|
315
|
+
allowWhere: "$text",
|
|
316
|
+
allowAttributesOf: "$text",
|
|
317
|
+
isInline: true
|
|
318
|
+
});
|
|
319
|
+
conversion.for("upcast").elementToElement({
|
|
320
|
+
model: "softBreak",
|
|
321
|
+
view: "br"
|
|
322
|
+
});
|
|
323
|
+
conversion.for("downcast").elementToElement({
|
|
324
|
+
model: "softBreak",
|
|
325
|
+
view: (modelElement, { writer }) => writer.createEmptyElement("br")
|
|
326
|
+
});
|
|
327
|
+
view.addObserver(EnterObserver);
|
|
328
|
+
editor.commands.add("shiftEnter", new ShiftEnterCommand(editor));
|
|
329
|
+
editor.model.document.registerPostFixer((writer) => removeStaleSoftBreakAttributes(writer));
|
|
330
|
+
this.listenTo(viewDocument, "enter", (evt, data) => {
|
|
331
|
+
if (!viewDocument.isComposing) data.preventDefault();
|
|
332
|
+
if (!data.isSoft) return;
|
|
333
|
+
editor.execute("shiftEnter");
|
|
334
|
+
view.scrollToTheSelection();
|
|
335
|
+
}, { priority: "low" });
|
|
336
|
+
editor.accessibility.addKeystrokeInfos({ keystrokes: [{
|
|
337
|
+
label: t("Insert a soft break (a <code><br></code> element)"),
|
|
338
|
+
keystroke: "Shift+Enter"
|
|
339
|
+
}] });
|
|
340
|
+
}
|
|
341
|
+
};
|
|
383
342
|
/**
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
// Do not remove attributes when softBreak is the last element or there is some element after it.
|
|
408
|
-
if (!nextSibling || nextSibling.is('element')) {
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
// Remove the attribute if the next sibling does not have the same attribute.
|
|
412
|
-
const attributesToRemove = Array.from(child.getAttributes()).filter(([key, value])=>!hasSameAttribute(nextSibling, key, value));
|
|
413
|
-
for (const [key] of attributesToRemove){
|
|
414
|
-
writer.removeAttribute(key, child);
|
|
415
|
-
wasChanged = true;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
return wasChanged;
|
|
343
|
+
* The post-fixer that removes attributes from stale soft-break elements.
|
|
344
|
+
*/
|
|
345
|
+
function removeStaleSoftBreakAttributes(writer) {
|
|
346
|
+
const parentsToCheck = /* @__PURE__ */ new Set();
|
|
347
|
+
for (const change of writer.model.document.differ.getChanges()) if (change.type == "insert" || change.type == "remove") {
|
|
348
|
+
/* v8 ignore else -- @preserve */
|
|
349
|
+
if (change.position.parent.is("element")) parentsToCheck.add(change.position.parent);
|
|
350
|
+
} else if (change.type == "attribute") {
|
|
351
|
+
/* v8 ignore else -- @preserve */
|
|
352
|
+
if (change.range.start.parent.is("element")) parentsToCheck.add(change.range.start.parent);
|
|
353
|
+
}
|
|
354
|
+
let wasChanged = false;
|
|
355
|
+
for (const parent of parentsToCheck) for (const child of parent.getChildren()) {
|
|
356
|
+
if (!child.is("element", "softBreak")) continue;
|
|
357
|
+
const nextSibling = child.nextSibling;
|
|
358
|
+
if (!nextSibling || nextSibling.is("element")) continue;
|
|
359
|
+
const attributesToRemove = Array.from(child.getAttributes()).filter(([key, value]) => !hasSameAttribute(nextSibling, key, value));
|
|
360
|
+
for (const [key] of attributesToRemove) {
|
|
361
|
+
writer.removeAttribute(key, child);
|
|
362
|
+
wasChanged = true;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return wasChanged;
|
|
420
366
|
}
|
|
421
367
|
/**
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
368
|
+
* Returns `true` if the given node has the same attribute as the one provided in the parameters.
|
|
369
|
+
*/
|
|
370
|
+
function hasSameAttribute(node, key, value) {
|
|
371
|
+
return node?.getAttribute(key) === value;
|
|
425
372
|
}
|
|
426
373
|
|
|
374
|
+
/**
|
|
375
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
376
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
377
|
+
*/
|
|
378
|
+
|
|
427
379
|
export { Enter, EnterCommand, EnterObserver, ShiftEnter, ShiftEnterCommand, getCopyOnEnterAttributes as _getCopyOnEnterAttributes };
|
|
428
|
-
//# sourceMappingURL=index.js.map
|
|
380
|
+
//# sourceMappingURL=index.js.map
|