@ckeditor/ckeditor5-enter 48.2.0 → 48.3.0-alpha.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/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 '@ckeditor/ckeditor5-core/dist/index.js';
6
- import { Observer, BubblingEventInfo, ViewDocumentDomEventData } from '@ckeditor/ckeditor5-engine/dist/index.js';
7
- import { env } from '@ckeditor/ckeditor5-utils/dist/index.js';
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
- * @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
- * @module enter/utils
14
- */ /**
15
- * Returns attributes that should be preserved on the enter keystroke.
16
- *
17
- * Filtering is realized based on `copyOnEnter` attribute property. Read more about attribute properties
18
- * {@link module:engine/model/schema~ModelSchema#setAttributeProperties here}.
19
- *
20
- * @param schema Model's schema.
21
- * @param allAttributes Attributes to filter.
22
- * @internal
23
- */ function* getCopyOnEnterAttributes(schema, allAttributes) {
24
- for (const attribute of allAttributes){
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
- * Enter command used by the {@link module:enter/enter~Enter Enter feature} to handle the <kbd>Enter</kbd> keystroke.
33
- */ class EnterCommand extends Command {
34
- /**
35
- * @inheritDoc
36
- */ execute() {
37
- this.editor.model.change((writer)=>{
38
- this.enterBlock(writer);
39
- this.fire('afterExecute', {
40
- writer
41
- });
42
- });
43
- }
44
- /**
45
- * Splits a block where the document selection is placed, in the way how the <kbd>Enter</kbd> key is expected to work:
46
- *
47
- * ```
48
- * <p>Foo[]bar</p> -> <p>Foo</p><p>[]bar</p>
49
- * <p>Foobar[]</p> -> <p>Foobar</p><p>[]</p>
50
- * <p>Fo[ob]ar</p> -> <p>Fo</p><p>[]ar</p>
51
- * ```
52
- *
53
- * In some cases, the split will not happen:
54
- *
55
- * ```
56
- * // The selection parent is a limit element:
57
- * <figcaption>A[bc]d</figcaption> -> <figcaption>A[]d</figcaption>
58
- *
59
- * // The selection spans over multiple elements:
60
- * <h>x[x</h><p>y]y<p> -> <h>x</h><p>[]y</p>
61
- * ```
62
- *
63
- * @param writer Writer to use when performing the enter action.
64
- * @returns Boolean indicating if the block was split.
65
- */ enterBlock(writer) {
66
- const model = this.editor.model;
67
- const selection = model.document.selection;
68
- const schema = model.schema;
69
- const isSelectionEmpty = selection.isCollapsed;
70
- const range = selection.getFirstRange();
71
- const startElement = range.start.parent;
72
- const endElement = range.end.parent;
73
- // Don't touch the roots and other limit elements.
74
- if (schema.isLimit(startElement) || schema.isLimit(endElement)) {
75
- // Delete the selected content but only if inside a single limit element.
76
- // Abort, when crossing limit elements boundary (e.g. <limit1>x[x</limit1>donttouchme<limit2>y]y</limit2>).
77
- // This is an edge case and it's hard to tell what should actually happen because such a selection
78
- // is not entirely valid.
79
- if (!isSelectionEmpty && startElement == endElement) {
80
- model.deleteContent(selection);
81
- }
82
- return false;
83
- }
84
- if (isSelectionEmpty) {
85
- const attributesToCopy = getCopyOnEnterAttributes(writer.model.schema, selection.getAttributes());
86
- splitBlock(writer, range.start);
87
- writer.setSelectionAttribute(attributesToCopy);
88
- return true;
89
- } else {
90
- const leaveUnmerged = !(range.start.isAtStart && range.end.isAtEnd);
91
- const isContainedWithinOneElement = startElement == endElement;
92
- model.deleteContent(selection, {
93
- leaveUnmerged
94
- });
95
- if (leaveUnmerged) {
96
- // Partially selected elements.
97
- //
98
- // <h>x[xx]x</h> -> <h>x^x</h> -> <h>x</h><h>^x</h>
99
- if (isContainedWithinOneElement) {
100
- splitBlock(writer, selection.focus);
101
- return true;
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
- writer.split(splitPos);
112
- writer.setSelection(splitPos.parent.nextSibling, 0);
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
- insertParagraph: {
117
- isSoft: false
118
- },
119
- insertLineBreak: {
120
- isSoft: true
121
- }
111
+ insertParagraph: { isSoft: false },
112
+ insertLineBreak: { isSoft: true }
122
113
  };
123
114
  /**
124
- * Enter observer introduces the {@link module:engine/view/document~ViewDocument#event:enter `Document#enter`} event.
125
- */ class EnterObserver extends Observer {
126
- /**
127
- * @inheritDoc
128
- */ constructor(view){
129
- super(view);
130
- const doc = this.document;
131
- let shiftPressed = false;
132
- doc.on('keydown', (evt, data)=>{
133
- shiftPressed = data.shiftKey;
134
- });
135
- doc.on('beforeinput', (evt, data)=>{
136
- if (!this.isEnabled) {
137
- return;
138
- }
139
- let inputType = data.inputType;
140
- // See https://github.com/ckeditor/ckeditor5/issues/13321.
141
- if (env.isSafari && shiftPressed && inputType == 'insertParagraph') {
142
- inputType = 'insertLineBreak';
143
- }
144
- const domEvent = data.domEvent;
145
- const enterEventSpec = ENTER_EVENT_TYPES[inputType];
146
- if (!enterEventSpec) {
147
- return;
148
- }
149
- const event = new BubblingEventInfo(doc, 'enter', data.targetRanges[0]);
150
- doc.fire(event, new ViewDocumentDomEventData(view, domEvent, {
151
- isSoft: enterEventSpec.isSoft
152
- }));
153
- // Stop `beforeinput` event if `enter` event was stopped.
154
- // https://github.com/ckeditor/ckeditor5/issues/753
155
- if (event.stop.called) {
156
- evt.stop();
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
- * This plugin handles the <kbd>Enter</kbd> keystroke (hard line break) in the editor.
170
- *
171
- * See also the {@link module:enter/shiftenter~ShiftEnter} plugin.
172
- *
173
- * For more information about this feature see the {@glink api/enter package page}.
174
- */ class Enter extends Plugin {
175
- /**
176
- * @inheritDoc
177
- */ static get pluginName() {
178
- return 'Enter';
179
- }
180
- /**
181
- * @inheritDoc
182
- */ static get isOfficialPlugin() {
183
- return true;
184
- }
185
- init() {
186
- const editor = this.editor;
187
- const view = editor.editing.view;
188
- const viewDocument = view.document;
189
- const t = this.editor.t;
190
- view.addObserver(EnterObserver);
191
- editor.commands.add('enter', new EnterCommand(editor));
192
- this.listenTo(viewDocument, 'enter', (evt, data)=>{
193
- // When not in composition, we handle the action, so prevent the default one.
194
- // When in composition, it's the browser who modify the DOM (renderer is disabled).
195
- if (!viewDocument.isComposing) {
196
- data.preventDefault();
197
- }
198
- // The soft enter key is handled by the ShiftEnter plugin.
199
- if (data.isSoft) {
200
- return;
201
- }
202
- editor.execute('enter');
203
- view.scrollToTheSelection();
204
- }, {
205
- priority: 'low'
206
- });
207
- // Add the information about the keystroke to the accessibility database.
208
- editor.accessibility.addKeystrokeInfos({
209
- keystrokes: [
210
- {
211
- label: t('Insert a hard break (a new paragraph)'),
212
- keystroke: 'Enter'
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
- * ShiftEnter command. It is used by the {@link module:enter/shiftenter~ShiftEnter ShiftEnter feature} to handle
221
- * the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke.
222
- */ class ShiftEnterCommand extends Command {
223
- /**
224
- * @inheritDoc
225
- */ execute() {
226
- const model = this.editor.model;
227
- const doc = model.document;
228
- model.change((writer)=>{
229
- softBreakAction(model, writer, doc.selection);
230
- this.fire('afterExecute', {
231
- writer
232
- });
233
- });
234
- }
235
- /**
236
- * @inheritDoc
237
- */ refresh() {
238
- const model = this.editor.model;
239
- const doc = model.document;
240
- this.isEnabled = isEnabled(model.schema, doc.selection);
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
- * Checks whether the ShiftEnter command should be enabled in the specified selection.
245
- */ function isEnabled(schema, selection) {
246
- // At this moment it is okay to support single range selections only.
247
- // But in the future we may need to change that.
248
- if (selection.rangeCount > 1) {
249
- return false;
250
- }
251
- const anchorPos = selection.anchor;
252
- // Check whether the break element can be inserted in the current selection anchor.
253
- if (!anchorPos || !schema.checkChild(anchorPos, 'softBreak')) {
254
- return false;
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
- * Creates a break in the way that the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke is expected to work.
267
- */ function softBreakAction(model, writer, selection) {
268
- const isSelectionEmpty = selection.isCollapsed;
269
- const range = selection.getFirstRange();
270
- const startElement = range.start.parent;
271
- const endElement = range.end.parent;
272
- const isContainedWithinOneElement = startElement == endElement;
273
- if (isSelectionEmpty) {
274
- insertBreak(model, writer, range.end, selection.getAttributes());
275
- } else {
276
- const leaveUnmerged = !(range.start.isAtStart && range.end.isAtEnd);
277
- model.deleteContent(selection, {
278
- leaveUnmerged
279
- });
280
- // Selection within one element:
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
- * Inserts a softBreak with applicable attributes.
295
- */ function insertBreak(model, writer, position, selectionAttributes) {
296
- const attributes = Array.from(getCopyOnEnterAttributes(model.schema, selectionAttributes));
297
- const breakLineElement = writer.createElement('softBreak', attributes);
298
- model.insertContent(breakLineElement, position);
299
- writer.setSelection(breakLineElement, 'after');
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
- * Checks whether the specified `element` is a child of the limit element.
303
- *
304
- * Checking whether the `<p>` element is inside a limit element:
305
- * - `<$root><p>Text.</p></$root> => false`
306
- * - `<$root><limitElement><p>Text</p></limitElement></$root> => true`
307
- */ function isInsideLimitElement(element, schema) {
308
- // `$root` is a limit element but in this case is an invalid element.
309
- if (element.is('rootElement')) {
310
- return false;
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
- * This plugin handles the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke (soft line break) in the editor.
317
- *
318
- * See also the {@link module:enter/enter~Enter} plugin.
319
- *
320
- * For more information about this feature see the {@glink api/enter package page}.
321
- */ class ShiftEnter extends Plugin {
322
- /**
323
- * @inheritDoc
324
- */ static get pluginName() {
325
- return 'ShiftEnter';
326
- }
327
- /**
328
- * @inheritDoc
329
- */ static get isOfficialPlugin() {
330
- return true;
331
- }
332
- init() {
333
- const editor = this.editor;
334
- const schema = editor.model.schema;
335
- const conversion = editor.conversion;
336
- const view = editor.editing.view;
337
- const viewDocument = view.document;
338
- const t = this.editor.t;
339
- // Configure the schema.
340
- schema.register('softBreak', {
341
- allowWhere: '$text',
342
- allowAttributesOf: '$text',
343
- isInline: true
344
- });
345
- // Configure converters.
346
- conversion.for('upcast').elementToElement({
347
- model: 'softBreak',
348
- view: 'br'
349
- });
350
- conversion.for('downcast').elementToElement({
351
- model: 'softBreak',
352
- view: (modelElement, { writer })=>writer.createEmptyElement('br')
353
- });
354
- view.addObserver(EnterObserver);
355
- editor.commands.add('shiftEnter', new ShiftEnterCommand(editor));
356
- editor.model.document.registerPostFixer((writer)=>removeStaleSoftBreakAttributes(writer));
357
- this.listenTo(viewDocument, 'enter', (evt, data)=>{
358
- // When not in composition, we handle the action, so prevent the default one.
359
- // When in composition, it's the browser who modify the DOM (renderer is disabled).
360
- if (!viewDocument.isComposing) {
361
- data.preventDefault();
362
- }
363
- // The hard enter key is handled by the Enter plugin.
364
- if (!data.isSoft) {
365
- return;
366
- }
367
- editor.execute('shiftEnter');
368
- view.scrollToTheSelection();
369
- }, {
370
- priority: 'low'
371
- });
372
- // Add the information about the keystroke to the accessibility database.
373
- editor.accessibility.addKeystrokeInfos({
374
- keystrokes: [
375
- {
376
- label: t('Insert a soft break (a <code>&lt;br&gt;</code> element)'),
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>&lt;br&gt;</code> element)"),
338
+ keystroke: "Shift+Enter"
339
+ }] });
340
+ }
341
+ };
383
342
  /**
384
- * The post-fixer that removes attributes from stale soft-break elements.
385
- */ function removeStaleSoftBreakAttributes(writer) {
386
- const parentsToCheck = new Set();
387
- for (const change of writer.model.document.differ.getChanges()){
388
- if (change.type == 'insert' || change.type == 'remove') {
389
- if (change.position.parent.is('element')) {
390
- parentsToCheck.add(change.position.parent);
391
- }
392
- } else if (change.type == 'attribute') {
393
- if (change.range.start.parent.is('element')) {
394
- parentsToCheck.add(change.range.start.parent);
395
- }
396
- }
397
- }
398
- let wasChanged = false;
399
- for (const parent of parentsToCheck){
400
- // Just a sibling nodes check. We do not need to check attributes of nested elements.
401
- for (const child of parent.getChildren()){
402
- // Find a softBreak element.
403
- if (!child.is('element', 'softBreak')) {
404
- continue;
405
- }
406
- const nextSibling = child.nextSibling;
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
- * Returns `true` if the given node has the same attribute as the one provided in the parameters.
423
- */ function hasSameAttribute(node, key, value) {
424
- return node?.getAttribute(key) === value;
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