@ckeditor/ckeditor5-enter 35.2.1 → 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 +20 -13
- package/src/enter.js +26 -32
- package/src/entercommand.js +54 -64
- package/src/enterobserver.js +34 -45
- package/src/index.js +0 -2
- package/src/shiftenter.js +44 -52
- package/src/shiftentercommand.js +83 -102
- package/src/utils.js +6 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-enter",
|
|
3
|
-
"version": "35.
|
|
3
|
+
"version": "35.3.0",
|
|
4
4
|
"description": "Enter feature for CKEditor 5.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ckeditor",
|
|
@@ -12,18 +12,20 @@
|
|
|
12
12
|
],
|
|
13
13
|
"main": "src/index.js",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@ckeditor/ckeditor5-core": "^35.
|
|
16
|
-
"@ckeditor/ckeditor5-engine": "^35.
|
|
17
|
-
"@ckeditor/ckeditor5-utils": "^35.2.1"
|
|
15
|
+
"@ckeditor/ckeditor5-core": "^35.3.0",
|
|
16
|
+
"@ckeditor/ckeditor5-engine": "^35.3.0"
|
|
18
17
|
},
|
|
19
18
|
"devDependencies": {
|
|
20
|
-
"@ckeditor/ckeditor5-basic-styles": "^35.
|
|
21
|
-
"@ckeditor/ckeditor5-editor-classic": "^35.
|
|
22
|
-
"@ckeditor/ckeditor5-heading": "^35.
|
|
23
|
-
"@ckeditor/ckeditor5-link": "^35.
|
|
24
|
-
"@ckeditor/ckeditor5-paragraph": "^35.
|
|
25
|
-
"@ckeditor/ckeditor5-typing": "^35.
|
|
26
|
-
"@ckeditor/ckeditor5-undo": "^35.
|
|
19
|
+
"@ckeditor/ckeditor5-basic-styles": "^35.3.0",
|
|
20
|
+
"@ckeditor/ckeditor5-editor-classic": "^35.3.0",
|
|
21
|
+
"@ckeditor/ckeditor5-heading": "^35.3.0",
|
|
22
|
+
"@ckeditor/ckeditor5-link": "^35.3.0",
|
|
23
|
+
"@ckeditor/ckeditor5-paragraph": "^35.3.0",
|
|
24
|
+
"@ckeditor/ckeditor5-typing": "^35.3.0",
|
|
25
|
+
"@ckeditor/ckeditor5-undo": "^35.3.0",
|
|
26
|
+
"typescript": "^4.8.4",
|
|
27
|
+
"webpack": "^5.58.1",
|
|
28
|
+
"webpack-cli": "^4.9.0"
|
|
27
29
|
},
|
|
28
30
|
"engines": {
|
|
29
31
|
"node": ">=14.0.0",
|
|
@@ -40,9 +42,14 @@
|
|
|
40
42
|
},
|
|
41
43
|
"files": [
|
|
42
44
|
"lang",
|
|
43
|
-
"src",
|
|
45
|
+
"src/**/*.js",
|
|
46
|
+
"src/**/*.d.ts",
|
|
44
47
|
"theme",
|
|
45
48
|
"ckeditor5-metadata.json",
|
|
46
49
|
"CHANGELOG.md"
|
|
47
|
-
]
|
|
50
|
+
],
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsc -p ./tsconfig.release.json",
|
|
53
|
+
"postversion": "npm run build"
|
|
54
|
+
}
|
|
48
55
|
}
|
package/src/enter.js
CHANGED
|
@@ -2,15 +2,12 @@
|
|
|
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
|
/**
|
|
7
6
|
* @module enter/enter
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
8
|
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
|
|
11
9
|
import EnterCommand from './entercommand';
|
|
12
10
|
import EnterObserver from './enterobserver';
|
|
13
|
-
|
|
14
11
|
/**
|
|
15
12
|
* This plugin handles the <kbd>Enter</kbd> key (hard line break) in the editor.
|
|
16
13
|
*
|
|
@@ -21,33 +18,30 @@ import EnterObserver from './enterobserver';
|
|
|
21
18
|
* @extends module:core/plugin~Plugin
|
|
22
19
|
*/
|
|
23
20
|
export default class Enter extends Plugin {
|
|
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
|
-
view.scrollToTheSelection();
|
|
51
|
-
}, { priority: 'low' } );
|
|
52
|
-
}
|
|
21
|
+
/**
|
|
22
|
+
* @inheritDoc
|
|
23
|
+
*/
|
|
24
|
+
static get pluginName() {
|
|
25
|
+
return 'Enter';
|
|
26
|
+
}
|
|
27
|
+
init() {
|
|
28
|
+
const editor = this.editor;
|
|
29
|
+
const view = editor.editing.view;
|
|
30
|
+
const viewDocument = view.document;
|
|
31
|
+
view.addObserver(EnterObserver);
|
|
32
|
+
editor.commands.add('enter', new EnterCommand(editor));
|
|
33
|
+
this.listenTo(viewDocument, 'enter', (evt, data) => {
|
|
34
|
+
// When not in composition, we handle the action, so prevent the default one.
|
|
35
|
+
// When in composition, it's the browser who modify the DOM (renderer is disabled).
|
|
36
|
+
if (!viewDocument.isComposing) {
|
|
37
|
+
data.preventDefault();
|
|
38
|
+
}
|
|
39
|
+
// The soft enter key is handled by the ShiftEnter plugin.
|
|
40
|
+
if (data.isSoft) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
editor.execute('enter');
|
|
44
|
+
view.scrollToTheSelection();
|
|
45
|
+
}, { priority: 'low' });
|
|
46
|
+
}
|
|
53
47
|
}
|
package/src/entercommand.js
CHANGED
|
@@ -2,34 +2,29 @@
|
|
|
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
|
/**
|
|
7
6
|
* @module enter/entercommand
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
8
|
import Command from '@ckeditor/ckeditor5-core/src/command';
|
|
11
9
|
import { getCopyOnEnterAttributes } from './utils';
|
|
12
|
-
|
|
13
10
|
/**
|
|
14
11
|
* Enter command. It is used by the {@link module:enter/enter~Enter Enter feature} to handle the <kbd>Enter</kbd> key.
|
|
15
12
|
*
|
|
16
13
|
* @extends module:core/command~Command
|
|
17
14
|
*/
|
|
18
15
|
export default class EnterCommand extends Command {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
16
|
+
/**
|
|
17
|
+
* @inheritDoc
|
|
18
|
+
*/
|
|
19
|
+
execute() {
|
|
20
|
+
const model = this.editor.model;
|
|
21
|
+
const doc = model.document;
|
|
22
|
+
model.change(writer => {
|
|
23
|
+
enterBlock(this.editor.model, writer, doc.selection, model.schema);
|
|
24
|
+
this.fire('afterExecute', { writer });
|
|
25
|
+
});
|
|
26
|
+
}
|
|
31
27
|
}
|
|
32
|
-
|
|
33
28
|
// Creates a new block in the way that the <kbd>Enter</kbd> key is expected to work.
|
|
34
29
|
//
|
|
35
30
|
// @param {module:engine/model~Model} model
|
|
@@ -37,53 +32,48 @@ export default class EnterCommand extends Command {
|
|
|
37
32
|
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
|
|
38
33
|
// Selection on which the action should be performed.
|
|
39
34
|
// @param {module:engine/model/schema~Schema} schema
|
|
40
|
-
function enterBlock(
|
|
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
|
-
writer.setSelection( endElement, 0 );
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
35
|
+
function enterBlock(model, writer, selection, schema) {
|
|
36
|
+
const isSelectionEmpty = selection.isCollapsed;
|
|
37
|
+
const range = selection.getFirstRange();
|
|
38
|
+
const startElement = range.start.parent;
|
|
39
|
+
const endElement = range.end.parent;
|
|
40
|
+
// Don't touch the roots and other limit elements.
|
|
41
|
+
if (schema.isLimit(startElement) || schema.isLimit(endElement)) {
|
|
42
|
+
// Delete the selected content but only if inside a single limit element.
|
|
43
|
+
// Abort, when crossing limit elements boundary (e.g. <limit1>x[x</limit1>donttouchme<limit2>y]y</limit2>).
|
|
44
|
+
// This is an edge case and it's hard to tell what should actually happen because such a selection
|
|
45
|
+
// is not entirely valid.
|
|
46
|
+
if (!isSelectionEmpty && startElement == endElement) {
|
|
47
|
+
model.deleteContent(selection);
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (isSelectionEmpty) {
|
|
52
|
+
const attributesToCopy = getCopyOnEnterAttributes(writer.model.schema, selection.getAttributes());
|
|
53
|
+
splitBlock(writer, range.start);
|
|
54
|
+
writer.setSelectionAttribute(attributesToCopy);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const leaveUnmerged = !(range.start.isAtStart && range.end.isAtEnd);
|
|
58
|
+
const isContainedWithinOneElement = (startElement == endElement);
|
|
59
|
+
model.deleteContent(selection, { leaveUnmerged });
|
|
60
|
+
if (leaveUnmerged) {
|
|
61
|
+
// Partially selected elements.
|
|
62
|
+
//
|
|
63
|
+
// <h>x[xx]x</h> -> <h>x^x</h> -> <h>x</h><h>^x</h>
|
|
64
|
+
if (isContainedWithinOneElement) {
|
|
65
|
+
splitBlock(writer, selection.focus);
|
|
66
|
+
}
|
|
67
|
+
// Selection over multiple elements.
|
|
68
|
+
//
|
|
69
|
+
// <h>x[x</h><p>y]y<p> -> <h>x^</h><p>y</p> -> <h>x</h><p>^y</p>
|
|
70
|
+
else {
|
|
71
|
+
writer.setSelection(endElement, 0);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
84
75
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
writer.setSelection( splitPos.parent.nextSibling, 0 );
|
|
76
|
+
function splitBlock(writer, splitPos) {
|
|
77
|
+
writer.split(splitPos);
|
|
78
|
+
writer.setSelection(splitPos.parent.nextSibling, 0);
|
|
89
79
|
}
|
package/src/enterobserver.js
CHANGED
|
@@ -2,61 +2,50 @@
|
|
|
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
|
/**
|
|
7
6
|
* @module enter/enterobserver
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
8
|
import Observer from '@ckeditor/ckeditor5-engine/src/view/observer/observer';
|
|
11
9
|
import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
|
|
12
10
|
import BubblingEventInfo from '@ckeditor/ckeditor5-engine/src/view/observer/bubblingeventinfo';
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const ENTER_EVENT_TYPES = {
|
|
12
|
+
insertParagraph: { isSoft: false },
|
|
13
|
+
insertLineBreak: { isSoft: true }
|
|
14
|
+
};
|
|
15
15
|
/**
|
|
16
16
|
* Enter observer introduces the {@link module:engine/view/document~Document#event:enter `Document#enter`} event.
|
|
17
17
|
*
|
|
18
18
|
* @extends module:engine/view/observer/observer~Observer
|
|
19
19
|
*/
|
|
20
20
|
export default class EnterObserver extends Observer {
|
|
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
|
-
|
|
21
|
+
/**
|
|
22
|
+
* @inheritDoc
|
|
23
|
+
*/
|
|
24
|
+
constructor(view) {
|
|
25
|
+
super(view);
|
|
26
|
+
const doc = this.document;
|
|
27
|
+
doc.on('beforeinput', (evt, data) => {
|
|
28
|
+
if (!this.isEnabled) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const domEvent = data.domEvent;
|
|
32
|
+
const enterEventSpec = ENTER_EVENT_TYPES[data.inputType];
|
|
33
|
+
if (!enterEventSpec) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const event = new BubblingEventInfo(doc, 'enter', data.targetRanges[0]);
|
|
37
|
+
doc.fire(event, new DomEventData(view, domEvent, {
|
|
38
|
+
isSoft: enterEventSpec.isSoft
|
|
39
|
+
}));
|
|
40
|
+
// Stop `beforeinput` event if `enter` event was stopped.
|
|
41
|
+
// https://github.com/ckeditor/ckeditor5/issues/753
|
|
42
|
+
if (event.stop.called) {
|
|
43
|
+
evt.stop();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @inheritDoc
|
|
49
|
+
*/
|
|
50
|
+
observe() { }
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Event fired when the user presses the <kbd>Enter</kbd> key.
|
|
54
|
-
*
|
|
55
|
-
* Note: This event is fired by the {@link module:enter/enterobserver~EnterObserver observer}
|
|
56
|
-
* (usually registered by the {@link module:enter/enter~Enter Enter feature} and
|
|
57
|
-
* {@link module:enter/shiftenter~ShiftEnter ShiftEnter feature}).
|
|
58
|
-
*
|
|
59
|
-
* @event module:engine/view/document~Document#event:enter
|
|
60
|
-
* @param {module:engine/view/observer/domeventdata~DomEventData} data
|
|
61
|
-
* @param {Boolean} data.isSoft Whether it's a soft enter (<kbd>Shift</kbd>+<kbd>Enter</kbd>) or hard enter (<kbd>Enter</kbd>).
|
|
62
|
-
*/
|
package/src/index.js
CHANGED
|
@@ -2,10 +2,8 @@
|
|
|
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
|
/**
|
|
7
6
|
* @module enter
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
8
|
export { default as Enter } from './enter';
|
|
11
9
|
export { default as ShiftEnter } from './shiftenter';
|
package/src/shiftenter.js
CHANGED
|
@@ -2,15 +2,12 @@
|
|
|
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
|
/**
|
|
7
6
|
* @module enter/shiftenter
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
8
|
import ShiftEnterCommand from './shiftentercommand';
|
|
11
9
|
import EnterObserver from './enterobserver';
|
|
12
10
|
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
|
|
13
|
-
|
|
14
11
|
/**
|
|
15
12
|
* This plugin handles the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke (soft line break) in the editor.
|
|
16
13
|
*
|
|
@@ -21,53 +18,48 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
|
|
|
21
18
|
* @extends module:core/plugin~Plugin
|
|
22
19
|
*/
|
|
23
20
|
export default class ShiftEnter extends Plugin {
|
|
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
|
-
editor.execute( 'shiftEnter' );
|
|
70
|
-
view.scrollToTheSelection();
|
|
71
|
-
}, { priority: 'low' } );
|
|
72
|
-
}
|
|
21
|
+
/**
|
|
22
|
+
* @inheritDoc
|
|
23
|
+
*/
|
|
24
|
+
static get pluginName() {
|
|
25
|
+
return 'ShiftEnter';
|
|
26
|
+
}
|
|
27
|
+
init() {
|
|
28
|
+
const editor = this.editor;
|
|
29
|
+
const schema = editor.model.schema;
|
|
30
|
+
const conversion = editor.conversion;
|
|
31
|
+
const view = editor.editing.view;
|
|
32
|
+
const viewDocument = view.document;
|
|
33
|
+
// Configure the schema.
|
|
34
|
+
schema.register('softBreak', {
|
|
35
|
+
allowWhere: '$text',
|
|
36
|
+
isInline: true
|
|
37
|
+
});
|
|
38
|
+
// Configure converters.
|
|
39
|
+
conversion.for('upcast')
|
|
40
|
+
.elementToElement({
|
|
41
|
+
model: 'softBreak',
|
|
42
|
+
view: 'br'
|
|
43
|
+
});
|
|
44
|
+
conversion.for('downcast')
|
|
45
|
+
.elementToElement({
|
|
46
|
+
model: 'softBreak',
|
|
47
|
+
view: (modelElement, { writer }) => writer.createEmptyElement('br')
|
|
48
|
+
});
|
|
49
|
+
view.addObserver(EnterObserver);
|
|
50
|
+
editor.commands.add('shiftEnter', new ShiftEnterCommand(editor));
|
|
51
|
+
this.listenTo(viewDocument, 'enter', (evt, data) => {
|
|
52
|
+
// When not in composition, we handle the action, so prevent the default one.
|
|
53
|
+
// When in composition, it's the browser who modify the DOM (renderer is disabled).
|
|
54
|
+
if (!viewDocument.isComposing) {
|
|
55
|
+
data.preventDefault();
|
|
56
|
+
}
|
|
57
|
+
// The hard enter key is handled by the Enter plugin.
|
|
58
|
+
if (!data.isSoft) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
editor.execute('shiftEnter');
|
|
62
|
+
view.scrollToTheSelection();
|
|
63
|
+
}, { priority: 'low' });
|
|
64
|
+
}
|
|
73
65
|
}
|
package/src/shiftentercommand.js
CHANGED
|
@@ -2,14 +2,11 @@
|
|
|
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
|
/**
|
|
7
6
|
* @module enter/shiftentercommand
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
8
|
import Command from '@ckeditor/ckeditor5-core/src/command';
|
|
11
9
|
import { getCopyOnEnterAttributes } from './utils';
|
|
12
|
-
|
|
13
10
|
/**
|
|
14
11
|
* ShiftEnter command. It is used by the {@link module:enter/shiftenter~ShiftEnter ShiftEnter feature} to handle
|
|
15
12
|
* the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke.
|
|
@@ -17,112 +14,97 @@ import { getCopyOnEnterAttributes } from './utils';
|
|
|
17
14
|
* @extends module:core/command~Command
|
|
18
15
|
*/
|
|
19
16
|
export default class ShiftEnterCommand extends Command {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.isEnabled = isEnabled( model.schema, doc.selection );
|
|
38
|
-
}
|
|
17
|
+
/**
|
|
18
|
+
* @inheritDoc
|
|
19
|
+
*/
|
|
20
|
+
execute() {
|
|
21
|
+
const model = this.editor.model;
|
|
22
|
+
const doc = model.document;
|
|
23
|
+
model.change(writer => {
|
|
24
|
+
softBreakAction(model, writer, doc.selection);
|
|
25
|
+
this.fire('afterExecute', { writer });
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
refresh() {
|
|
29
|
+
const model = this.editor.model;
|
|
30
|
+
const doc = model.document;
|
|
31
|
+
this.isEnabled = isEnabled(model.schema, doc.selection);
|
|
32
|
+
}
|
|
39
33
|
}
|
|
40
|
-
|
|
41
34
|
// Checks whether the ShiftEnter command should be enabled in the specified selection.
|
|
42
35
|
//
|
|
43
36
|
// @param {module:engine/model/schema~Schema} schema
|
|
44
37
|
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
|
|
45
|
-
function isEnabled(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if ( ( isInsideLimitElement( startElement, schema ) || isInsideLimitElement( endElement, schema ) ) && startElement !== endElement ) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return true;
|
|
38
|
+
function isEnabled(schema, selection) {
|
|
39
|
+
// At this moment it is okay to support single range selections only.
|
|
40
|
+
// But in the future we may need to change that.
|
|
41
|
+
if (selection.rangeCount > 1) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const anchorPos = selection.anchor;
|
|
45
|
+
// Check whether the break element can be inserted in the current selection anchor.
|
|
46
|
+
if (!anchorPos || !schema.checkChild(anchorPos, 'softBreak')) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const range = selection.getFirstRange();
|
|
50
|
+
const startElement = range.start.parent;
|
|
51
|
+
const endElement = range.end.parent;
|
|
52
|
+
// Do not modify the content if selection is cross-limit elements.
|
|
53
|
+
if ((isInsideLimitElement(startElement, schema) || isInsideLimitElement(endElement, schema)) && startElement !== endElement) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
69
57
|
}
|
|
70
|
-
|
|
71
58
|
// Creates a break in the way that the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke is expected to work.
|
|
72
59
|
//
|
|
73
60
|
// @param {module:engine/model~Model} model
|
|
74
61
|
// @param {module:engine/model/writer~Writer} writer
|
|
75
62
|
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
|
|
76
63
|
// Selection on which the action should be performed.
|
|
77
|
-
function softBreakAction(
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
}
|
|
64
|
+
function softBreakAction(model, writer, selection) {
|
|
65
|
+
const isSelectionEmpty = selection.isCollapsed;
|
|
66
|
+
const range = selection.getFirstRange();
|
|
67
|
+
const startElement = range.start.parent;
|
|
68
|
+
const endElement = range.end.parent;
|
|
69
|
+
const isContainedWithinOneElement = (startElement == endElement);
|
|
70
|
+
if (isSelectionEmpty) {
|
|
71
|
+
const attributesToCopy = getCopyOnEnterAttributes(model.schema, selection.getAttributes());
|
|
72
|
+
insertBreak(model, writer, range.end);
|
|
73
|
+
writer.removeSelectionAttribute(selection.getAttributeKeys());
|
|
74
|
+
writer.setSelectionAttribute(attributesToCopy);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const leaveUnmerged = !(range.start.isAtStart && range.end.isAtEnd);
|
|
78
|
+
model.deleteContent(selection, { leaveUnmerged });
|
|
79
|
+
// Selection within one element:
|
|
80
|
+
//
|
|
81
|
+
// <h>x[xx]x</h> -> <h>x^x</h> -> <h>x<br>^x</h>
|
|
82
|
+
if (isContainedWithinOneElement) {
|
|
83
|
+
insertBreak(model, writer, selection.focus);
|
|
84
|
+
}
|
|
85
|
+
// Selection over multiple elements.
|
|
86
|
+
//
|
|
87
|
+
// <h>x[x</h><p>y]y<p> -> <h>x^</h><p>y</p> -> <h>x</h><p>^y</p>
|
|
88
|
+
//
|
|
89
|
+
// We chose not to insert a line break in this case because:
|
|
90
|
+
//
|
|
91
|
+
// * it's not a very common scenario,
|
|
92
|
+
// * it actually surprised me when I saw the "expected behavior" in real life.
|
|
93
|
+
//
|
|
94
|
+
// It's ok if the user will need to be more specific where they want the <br> to be inserted.
|
|
95
|
+
else {
|
|
96
|
+
// Move the selection to the 2nd element (last step of the example above).
|
|
97
|
+
if (leaveUnmerged) {
|
|
98
|
+
writer.setSelection(endElement, 0);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
117
102
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
model.insertContent( breakLineElement, position );
|
|
123
|
-
writer.setSelection( breakLineElement, 'after' );
|
|
103
|
+
function insertBreak(model, writer, position) {
|
|
104
|
+
const breakLineElement = writer.createElement('softBreak');
|
|
105
|
+
model.insertContent(breakLineElement, position);
|
|
106
|
+
writer.setSelection(breakLineElement, 'after');
|
|
124
107
|
}
|
|
125
|
-
|
|
126
108
|
// Checks whether the specified `element` is a child of the limit element.
|
|
127
109
|
//
|
|
128
110
|
// Checking whether the `<p>` element is inside a limit element:
|
|
@@ -132,11 +114,10 @@ function insertBreak( model, writer, position ) {
|
|
|
132
114
|
// @param {module:engine/model/element~Element} element
|
|
133
115
|
// @param {module:engine/schema~Schema} schema
|
|
134
116
|
// @returns {Boolean}
|
|
135
|
-
function isInsideLimitElement(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return schema.isLimit( element ) || isInsideLimitElement( element.parent, schema );
|
|
117
|
+
function isInsideLimitElement(element, schema) {
|
|
118
|
+
// `$root` is a limit element but in this case is an invalid element.
|
|
119
|
+
if (element.is('rootElement')) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return schema.isLimit(element) || isInsideLimitElement(element.parent, schema);
|
|
142
123
|
}
|
package/src/utils.js
CHANGED
|
@@ -2,11 +2,6 @@
|
|
|
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
|
-
/**
|
|
7
|
-
* @module enter/utils
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
5
|
/**
|
|
11
6
|
* Returns attributes that should be preserved on the enter key.
|
|
12
7
|
*
|
|
@@ -17,10 +12,10 @@
|
|
|
17
12
|
* @param {Iterable.<*>} allAttributes attributes to filter.
|
|
18
13
|
* @returns {Iterable.<*>}
|
|
19
14
|
*/
|
|
20
|
-
export function* getCopyOnEnterAttributes(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
export function* getCopyOnEnterAttributes(schema, allAttributes) {
|
|
16
|
+
for (const attribute of allAttributes) {
|
|
17
|
+
if (attribute && schema.getAttributeProperties(attribute[0]).copyOnEnter) {
|
|
18
|
+
yield attribute;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
26
21
|
}
|