ckeditor5-elements-schema 49.0.0-alpha.2 → 49.0.0-alpha.4
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/README.md +28 -2
- package/dist/augmentation.d.ts +7 -1
- package/dist/augmentation.d.ts.map +1 -1
- package/dist/commands/elementBlockCommand.d.ts +46 -0
- package/dist/commands/elementBlockCommand.d.ts.map +1 -0
- package/dist/commands/elementBlockCommand.js +71 -0
- package/dist/commands/elementBlockCommand.js.map +1 -0
- package/dist/commands/elementContainerCommand.d.ts +36 -0
- package/dist/commands/elementContainerCommand.d.ts.map +1 -0
- package/dist/commands/elementContainerCommand.js +67 -0
- package/dist/commands/elementContainerCommand.js.map +1 -0
- package/dist/commands/elementInlineCommand.d.ts +87 -0
- package/dist/commands/elementInlineCommand.d.ts.map +1 -0
- package/dist/commands/elementInlineCommand.js +253 -0
- package/dist/commands/elementInlineCommand.js.map +1 -0
- package/dist/commands/elementInsertCommand.d.ts +33 -0
- package/dist/commands/elementInsertCommand.d.ts.map +1 -0
- package/dist/commands/elementInsertCommand.js +61 -0
- package/dist/commands/elementInsertCommand.js.map +1 -0
- package/dist/commands/elementSchemaCommand.d.ts +62 -0
- package/dist/commands/elementSchemaCommand.d.ts.map +1 -0
- package/dist/commands/elementSchemaCommand.js +107 -0
- package/dist/commands/elementSchemaCommand.js.map +1 -0
- package/dist/{convertElementSchema.d.ts → conversion/convertElementSchema.d.ts} +3 -2
- package/dist/conversion/convertElementSchema.d.ts.map +1 -0
- package/dist/{convertElementSchema.js → conversion/convertElementSchema.js} +16 -4
- package/dist/conversion/convertElementSchema.js.map +1 -0
- package/dist/conversion/inlineTextElement.d.ts +24 -0
- package/dist/conversion/inlineTextElement.d.ts.map +1 -0
- package/dist/conversion/inlineTextElement.js +28 -0
- package/dist/conversion/inlineTextElement.js.map +1 -0
- package/dist/definition/canHold.d.ts.map +1 -0
- package/dist/definition/canHold.js.map +1 -0
- package/dist/definition/compileElementSchema.d.ts.map +1 -0
- package/dist/definition/compileElementSchema.js.map +1 -0
- package/dist/definition/elementSchemaError.d.ts.map +1 -0
- package/dist/definition/elementSchemaError.js.map +1 -0
- package/dist/definition/readDefinition.d.ts.map +1 -0
- package/dist/definition/readDefinition.js.map +1 -0
- package/dist/definition/suggest.d.ts.map +1 -0
- package/dist/definition/suggest.js.map +1 -0
- package/dist/definition/types.d.ts.map +1 -0
- package/dist/definition/types.js.map +1 -0
- package/dist/definition/validateElementSchema.d.ts.map +1 -0
- package/dist/definition/validateElementSchema.js.map +1 -0
- package/dist/elementSchema.js +4 -4
- package/dist/elementSchema.js.map +1 -1
- package/dist/elementSchemaCommands.d.ts +83 -0
- package/dist/elementSchemaCommands.d.ts.map +1 -0
- package/dist/elementSchemaCommands.js +137 -0
- package/dist/elementSchemaCommands.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/typing/twoStepCaretMovement.d.ts +11 -0
- package/dist/typing/twoStepCaretMovement.d.ts.map +1 -0
- package/dist/typing/twoStepCaretMovement.js +76 -0
- package/dist/typing/twoStepCaretMovement.js.map +1 -0
- package/dist/validate.d.ts +4 -4
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +3 -3
- package/dist/validate.js.map +1 -1
- package/package.json +2 -2
- package/src/augmentation.ts +8 -1
- package/src/commands/elementBlockCommand.ts +87 -0
- package/src/commands/elementContainerCommand.ts +83 -0
- package/src/commands/elementInlineCommand.ts +311 -0
- package/src/commands/elementInsertCommand.ts +74 -0
- package/src/commands/elementSchemaCommand.ts +140 -0
- package/src/{convertElementSchema.ts → conversion/convertElementSchema.ts} +19 -5
- package/src/conversion/inlineTextElement.ts +31 -0
- package/src/elementSchema.ts +5 -5
- package/src/elementSchemaCommands.ts +202 -0
- package/src/index.ts +17 -1
- package/src/typing/twoStepCaretMovement.ts +119 -0
- package/src/validate.ts +4 -4
- package/dist/canHold.d.ts.map +0 -1
- package/dist/canHold.js.map +0 -1
- package/dist/compileElementSchema.d.ts.map +0 -1
- package/dist/compileElementSchema.js.map +0 -1
- package/dist/convertElementSchema.d.ts.map +0 -1
- package/dist/convertElementSchema.js.map +0 -1
- package/dist/elementSchemaError.d.ts.map +0 -1
- package/dist/elementSchemaError.js.map +0 -1
- package/dist/readDefinition.d.ts.map +0 -1
- package/dist/readDefinition.js.map +0 -1
- package/dist/suggest.d.ts.map +0 -1
- package/dist/suggest.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/validateElementSchema.d.ts.map +0 -1
- package/dist/validateElementSchema.js.map +0 -1
- /package/dist/{canHold.d.ts → definition/canHold.d.ts} +0 -0
- /package/dist/{canHold.js → definition/canHold.js} +0 -0
- /package/dist/{compileElementSchema.d.ts → definition/compileElementSchema.d.ts} +0 -0
- /package/dist/{compileElementSchema.js → definition/compileElementSchema.js} +0 -0
- /package/dist/{elementSchemaError.d.ts → definition/elementSchemaError.d.ts} +0 -0
- /package/dist/{elementSchemaError.js → definition/elementSchemaError.js} +0 -0
- /package/dist/{readDefinition.d.ts → definition/readDefinition.d.ts} +0 -0
- /package/dist/{readDefinition.js → definition/readDefinition.js} +0 -0
- /package/dist/{suggest.d.ts → definition/suggest.d.ts} +0 -0
- /package/dist/{suggest.js → definition/suggest.js} +0 -0
- /package/dist/{types.d.ts → definition/types.d.ts} +0 -0
- /package/dist/{types.js → definition/types.js} +0 -0
- /package/dist/{validateElementSchema.d.ts → definition/validateElementSchema.d.ts} +0 -0
- /package/dist/{validateElementSchema.js → definition/validateElementSchema.js} +0 -0
- /package/src/{canHold.ts → definition/canHold.ts} +0 -0
- /package/src/{compileElementSchema.ts → definition/compileElementSchema.ts} +0 -0
- /package/src/{elementSchemaError.ts → definition/elementSchemaError.ts} +0 -0
- /package/src/{readDefinition.ts → definition/readDefinition.ts} +0 -0
- /package/src/{suggest.ts → definition/suggest.ts} +0 -0
- /package/src/{types.ts → definition/types.ts} +0 -0
- /package/src/{validateElementSchema.ts → definition/validateElementSchema.ts} +0 -0
package/README.md
CHANGED
|
@@ -33,12 +33,38 @@ elementSchemaConverters: true // Optional: converts the elements one to one, as
|
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
The `empty` and `container` elements can be selected as a whole, by a click and by the arrow keys, when `ElementSelectable`
|
|
36
|
-
(from `ckeditor5-elements-
|
|
36
|
+
(from `ckeditor5-elements-interaction`) is loaded too.
|
|
37
|
+
|
|
38
|
+
`ElementSchemaCommands` gives elements of the schema a command, a toolbar button and a keystroke, by their kind: an
|
|
39
|
+
inline `block` wraps text, as CKEditor's bold; a `block` renames blocks, as a heading; a `container` wraps blocks, as a
|
|
40
|
+
block quote; an `empty` element and an inline `container` are inserted, as a horizontal line. At the ends of inline
|
|
41
|
+
elements with text, the arrow keys move in two steps, as CKEditor's two-step caret movement does.
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{ "bold": { "type": "block", "isInline": true }, "divider": { "type": "empty" } }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { IconBold, IconHorizontalLine } from 'ckeditor5';
|
|
49
|
+
import { ElementSchema, ElementSchemaCommands } from 'ckeditor5-elements';
|
|
50
|
+
|
|
51
|
+
// In the editor config:
|
|
52
|
+
plugins: [..., ElementSchema, ElementSchemaCommands],
|
|
53
|
+
toolbar: ['bold', 'divider'],
|
|
54
|
+
elementSchema: schema,
|
|
55
|
+
elementSchemaCommands: {
|
|
56
|
+
bold: { title: 'Bold', icon: IconBold, keystroke: 'Ctrl+B', view: 'strong' },
|
|
57
|
+
divider: { title: 'Divider', icon: IconHorizontalLine, view: 'hr' }
|
|
58
|
+
}
|
|
59
|
+
```
|
|
37
60
|
|
|
38
61
|
`validateElementSchema(schema)` checks a definition without an editor, e.g. in a test or in CI. In Node.js, import it from
|
|
39
62
|
`ckeditor5-elements/validate-schema`: the main entry loads CKEditor, which needs a browser.
|
|
40
63
|
|
|
41
|
-
See the [Element schema
|
|
64
|
+
See the guide of the documentation: [Element schema](https://ivankild.github.io/ckeditor5-elements/guide/schema),
|
|
65
|
+
[Converters](https://ivankild.github.io/ckeditor5-elements/guide/schema-converters),
|
|
66
|
+
[Commands and buttons](https://ivankild.github.io/ckeditor5-elements/guide/schema-commands) and
|
|
67
|
+
[Examples](https://ivankild.github.io/ckeditor5-elements/guide/schema-examples).
|
|
42
68
|
|
|
43
69
|
## License
|
|
44
70
|
|
package/dist/augmentation.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { ElementSchema, ElementSchemaInput } from './index.js';
|
|
1
|
+
import type { ElementSchema, ElementSchemaCommands, ElementSchemaCommandsConfig, ElementSchemaInput } from './index.js';
|
|
2
2
|
declare module 'ckeditor5' {
|
|
3
3
|
interface PluginsMap {
|
|
4
4
|
[ElementSchema.pluginName]: ElementSchema;
|
|
5
|
+
[ElementSchemaCommands.pluginName]: ElementSchemaCommands;
|
|
5
6
|
}
|
|
6
7
|
interface EditorConfig {
|
|
7
8
|
/**
|
|
@@ -14,6 +15,11 @@ declare module 'ckeditor5' {
|
|
|
14
15
|
* element sets another tag for the editing view. See {@link ElementSchema}. Default: `false`.
|
|
15
16
|
*/
|
|
16
17
|
elementSchemaConverters?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* The commands and the buttons of elements of the schema, as CKEditor's bold, heading, block quote or horizontal
|
|
20
|
+
* line, by the name of the command. See {@link ElementSchemaCommands}.
|
|
21
|
+
*/
|
|
22
|
+
elementSchemaCommands?: ElementSchemaCommandsConfig;
|
|
17
23
|
}
|
|
18
24
|
}
|
|
19
25
|
//# sourceMappingURL=augmentation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"augmentation.d.ts","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"augmentation.d.ts","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAExH,OAAO,QAAQ,WAAW,CAAC;IAC1B,UAAU,UAAU;QACnB,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,aAAa,CAAC;QAC1C,CAAC,qBAAqB,CAAC,UAAU,CAAC,EAAE,qBAAqB,CAAC;KAC1D;IAED,UAAU,YAAY;QACrB;;WAEG;QACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;QAEnC;;;;WAIG;QACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;QAElC;;;WAGG;QACH,qBAAqB,CAAC,EAAE,2BAA2B,CAAC;KACpD;CACD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ElementOrPosition } from 'ckeditor5-elements-core';
|
|
2
|
+
import ElementSchemaCommand from './elementSchemaCommand.js';
|
|
3
|
+
/**
|
|
4
|
+
* Turns blocks into the element, as CKEditor's heading command does, e.g. a `title` that is a `block` of the element
|
|
5
|
+
* schema: the selected blocks, or the block of the target, are renamed, and keep their content.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* editor.execute('title'); // The blocks of the selection.
|
|
9
|
+
* editor.commands.get('title').executeFor(paragraph);
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* When all the blocks are the element already, it turns them back into paragraphs (the {@link defaultElement}). The
|
|
13
|
+
* blocks that cannot be renamed, e.g. where the element is not allowed, are left as they are.
|
|
14
|
+
*
|
|
15
|
+
* It is "on" when all the blocks are the element.
|
|
16
|
+
*/
|
|
17
|
+
export default class ElementBlockCommand extends ElementSchemaCommand {
|
|
18
|
+
isToggleable: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The element the blocks are turned back into.
|
|
21
|
+
*/
|
|
22
|
+
defaultElement: string;
|
|
23
|
+
/**
|
|
24
|
+
* Renames the blocks of the target: to the element, or back to the {@link defaultElement} when they are all the
|
|
25
|
+
* element.
|
|
26
|
+
*/
|
|
27
|
+
execute(elementOrPosition?: ElementOrPosition): void;
|
|
28
|
+
/**
|
|
29
|
+
* Enabled when the target can be edited, and one of its blocks can be renamed.
|
|
30
|
+
*/
|
|
31
|
+
protected _isEnabledFor(elementOrPosition?: ElementOrPosition): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* "On" when all the blocks of the target are the element.
|
|
34
|
+
*/
|
|
35
|
+
protected _isOnFor(elementOrPosition?: ElementOrPosition): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Returns the name the blocks get: the element, or the {@link defaultElement} when they are the element already.
|
|
38
|
+
*/
|
|
39
|
+
private _getNewName;
|
|
40
|
+
/**
|
|
41
|
+
* Whether a block can be renamed: it is not an object, nor the name already, and the schema allows it with its
|
|
42
|
+
* content.
|
|
43
|
+
*/
|
|
44
|
+
private _canRename;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=elementBlockCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementBlockCommand.d.ts","sourceRoot":"","sources":["../../src/commands/elementBlockCommand.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,oBAAoB,MAAM,2BAA2B,CAAC;AAE7D;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,oBAAoB;IACpD,YAAY,UAAQ;IAEpC;;OAEG;IACI,cAAc,SAAe;IAEpC;;;OAGG;IACa,OAAO,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAcpE;;OAEG;cACgB,aAAa,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO;IAShF;;OAEG;cACgB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO;IAM3E;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;;OAGG;IACH,OAAO,CAAC,UAAU;CAUlB"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import ElementSchemaCommand from './elementSchemaCommand.js';
|
|
2
|
+
/**
|
|
3
|
+
* Turns blocks into the element, as CKEditor's heading command does, e.g. a `title` that is a `block` of the element
|
|
4
|
+
* schema: the selected blocks, or the block of the target, are renamed, and keep their content.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* editor.execute('title'); // The blocks of the selection.
|
|
8
|
+
* editor.commands.get('title').executeFor(paragraph);
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* When all the blocks are the element already, it turns them back into paragraphs (the {@link defaultElement}). The
|
|
12
|
+
* blocks that cannot be renamed, e.g. where the element is not allowed, are left as they are.
|
|
13
|
+
*
|
|
14
|
+
* It is "on" when all the blocks are the element.
|
|
15
|
+
*/
|
|
16
|
+
export default class ElementBlockCommand extends ElementSchemaCommand {
|
|
17
|
+
isToggleable = true;
|
|
18
|
+
/**
|
|
19
|
+
* The element the blocks are turned back into.
|
|
20
|
+
*/
|
|
21
|
+
defaultElement = 'paragraph';
|
|
22
|
+
/**
|
|
23
|
+
* Renames the blocks of the target: to the element, or back to the {@link defaultElement} when they are all the
|
|
24
|
+
* element.
|
|
25
|
+
*/
|
|
26
|
+
execute(elementOrPosition) {
|
|
27
|
+
const name = this._getNewName(elementOrPosition);
|
|
28
|
+
const schema = this.editor.model.schema;
|
|
29
|
+
this.editor.model.change(writer => {
|
|
30
|
+
for (const block of this._getBlocks(elementOrPosition)) {
|
|
31
|
+
if (this._canRename(block, name)) {
|
|
32
|
+
writer.rename(block, name);
|
|
33
|
+
schema.removeDisallowedAttributes([block], writer);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Enabled when the target can be edited, and one of its blocks can be renamed.
|
|
40
|
+
*/
|
|
41
|
+
_isEnabledFor(elementOrPosition) {
|
|
42
|
+
const name = this._getNewName(elementOrPosition);
|
|
43
|
+
return (super._isEnabledFor(elementOrPosition) &&
|
|
44
|
+
this._getBlocks(elementOrPosition).some(block => this._canRename(block, name)));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* "On" when all the blocks of the target are the element.
|
|
48
|
+
*/
|
|
49
|
+
_isOnFor(elementOrPosition) {
|
|
50
|
+
const blocks = this._getBlocks(elementOrPosition);
|
|
51
|
+
return blocks.length > 0 && blocks.every(block => this.isElement(block));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Returns the name the blocks get: the element, or the {@link defaultElement} when they are the element already.
|
|
55
|
+
*/
|
|
56
|
+
_getNewName(elementOrPosition) {
|
|
57
|
+
return this._isOnFor(elementOrPosition) ? this.defaultElement : this.elementName;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether a block can be renamed: it is not an object, nor the name already, and the schema allows it with its
|
|
61
|
+
* content.
|
|
62
|
+
*/
|
|
63
|
+
_canRename(block, name) {
|
|
64
|
+
const schema = this.editor.model.schema;
|
|
65
|
+
return (block.name !== name &&
|
|
66
|
+
!schema.isObject(block) &&
|
|
67
|
+
schema.isRegistered(name) &&
|
|
68
|
+
this._canHold(this.editor.model.createPositionBefore(block), block.getChildren(), name));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=elementBlockCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementBlockCommand.js","sourceRoot":"","sources":["../../src/commands/elementBlockCommand.ts"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,MAAM,2BAA2B,CAAC;AAE7D;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,oBAAoB;IACpD,YAAY,GAAG,IAAI,CAAC;IAEpC;;OAEG;IACI,cAAc,GAAG,WAAW,CAAC;IAEpC;;;OAGG;IACa,OAAO,CAAC,iBAAqC;QAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAExC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;YACjC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACxD,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;oBAClC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;oBAC3B,MAAM,CAAC,0BAA0B,CAAC,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;gBACpD,CAAC;YACF,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;OAEG;IACgB,aAAa,CAAC,iBAAqC;QACrE,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;QAEjD,OAAO,CACN,KAAK,CAAC,aAAa,CAAC,iBAAiB,CAAC;YACtC,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAC9E,CAAC;IACH,CAAC;IAED;;OAEG;IACgB,QAAQ,CAAC,iBAAqC;QAChE,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QAElD,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED;;OAEG;IACK,WAAW,CAAC,iBAAqC;QACxD,OAAO,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;IAClF,CAAC;IAED;;;OAGG;IACK,UAAU,CAAC,KAAmB,EAAE,IAAY;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAExC,OAAO,CACN,KAAK,CAAC,IAAI,KAAK,IAAI;YACnB,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YACvB,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CACvF,CAAC;IACH,CAAC;CACD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ElementOrPosition } from 'ckeditor5-elements-core';
|
|
2
|
+
import ElementSchemaCommand from './elementSchemaCommand.js';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps blocks with the element, as CKEditor's block quote command does, e.g. a `callout` that is a `container` of the
|
|
5
|
+
* element schema: the selected blocks, or the block-level element of the target, go in a new element.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* editor.execute('callout'); // The blocks of the selection.
|
|
9
|
+
* editor.commands.get('callout').executeFor(paragraph);
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* In the element, it unwraps it: the whole element, not only the selected blocks.
|
|
13
|
+
*
|
|
14
|
+
* It is "on" when the target is in the element.
|
|
15
|
+
*/
|
|
16
|
+
export default class ElementContainerCommand extends ElementSchemaCommand {
|
|
17
|
+
isToggleable: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Unwraps the element the target is in, or wraps the blocks of the target with a new one.
|
|
20
|
+
*/
|
|
21
|
+
execute(elementOrPosition?: ElementOrPosition): void;
|
|
22
|
+
/**
|
|
23
|
+
* Enabled when the target can be edited, and the element it is in can be unwrapped, or its blocks can be wrapped.
|
|
24
|
+
*/
|
|
25
|
+
protected _isEnabledFor(elementOrPosition?: ElementOrPosition): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* "On" when the target is in the element.
|
|
28
|
+
*/
|
|
29
|
+
protected _isOnFor(elementOrPosition?: ElementOrPosition): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Returns the range to wrap: from the first block of the target to the last, as siblings: the blocks themselves, or
|
|
32
|
+
* their ancestors in the parent they share. `null` when the schema does not allow the element there.
|
|
33
|
+
*/
|
|
34
|
+
private _getWrapRange;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=elementContainerCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementContainerCommand.d.ts","sourceRoot":"","sources":["../../src/commands/elementContainerCommand.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,oBAAoB,MAAM,2BAA2B,CAAC;AAE7D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,OAAO,OAAO,uBAAwB,SAAQ,oBAAoB;IACxD,YAAY,UAAQ;IAEpC;;OAEG;IACa,OAAO,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAapE;;OAEG;cACgB,aAAa,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO;IAShF;;OAEG;cACgB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO;IAI3E;;;OAGG;IACH,OAAO,CAAC,aAAa;CAuBrB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import ElementSchemaCommand from './elementSchemaCommand.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps blocks with the element, as CKEditor's block quote command does, e.g. a `callout` that is a `container` of the
|
|
4
|
+
* element schema: the selected blocks, or the block-level element of the target, go in a new element.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* editor.execute('callout'); // The blocks of the selection.
|
|
8
|
+
* editor.commands.get('callout').executeFor(paragraph);
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* In the element, it unwraps it: the whole element, not only the selected blocks.
|
|
12
|
+
*
|
|
13
|
+
* It is "on" when the target is in the element.
|
|
14
|
+
*/
|
|
15
|
+
export default class ElementContainerCommand extends ElementSchemaCommand {
|
|
16
|
+
isToggleable = true;
|
|
17
|
+
/**
|
|
18
|
+
* Unwraps the element the target is in, or wraps the blocks of the target with a new one.
|
|
19
|
+
*/
|
|
20
|
+
execute(elementOrPosition) {
|
|
21
|
+
const element = this._findElement(elementOrPosition);
|
|
22
|
+
const range = element ? null : this._getWrapRange(elementOrPosition);
|
|
23
|
+
this.editor.model.change(writer => {
|
|
24
|
+
if (element) {
|
|
25
|
+
writer.unwrap(element);
|
|
26
|
+
}
|
|
27
|
+
else if (range) {
|
|
28
|
+
writer.wrap(range, this.elementName);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Enabled when the target can be edited, and the element it is in can be unwrapped, or its blocks can be wrapped.
|
|
34
|
+
*/
|
|
35
|
+
_isEnabledFor(elementOrPosition) {
|
|
36
|
+
const element = this._findElement(elementOrPosition);
|
|
37
|
+
return (super._isEnabledFor(elementOrPosition) &&
|
|
38
|
+
(element ? this._canUnwrap(element) : !!this._getWrapRange(elementOrPosition)));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* "On" when the target is in the element.
|
|
42
|
+
*/
|
|
43
|
+
_isOnFor(elementOrPosition) {
|
|
44
|
+
return !!this._findElement(elementOrPosition);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns the range to wrap: from the first block of the target to the last, as siblings: the blocks themselves, or
|
|
48
|
+
* their ancestors in the parent they share. `null` when the schema does not allow the element there.
|
|
49
|
+
*/
|
|
50
|
+
_getWrapRange(elementOrPosition) {
|
|
51
|
+
const model = this.editor.model;
|
|
52
|
+
const blocks = this._getBlocks(elementOrPosition);
|
|
53
|
+
const first = blocks[0];
|
|
54
|
+
const last = blocks.at(-1);
|
|
55
|
+
if (!first || !last) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const parent = first === last ? first.parent : first.getCommonAncestor(last);
|
|
59
|
+
const [start, end] = [first, last].map(block => block.getAncestors({ includeSelf: true }).find((node) => node.parent === parent));
|
|
60
|
+
if (!start || !end) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const range = model.createRange(model.createPositionBefore(start), model.createPositionAfter(end));
|
|
64
|
+
return this._canHold(range.start, range.getItems({ shallow: true })) ? range : null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=elementContainerCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementContainerCommand.js","sourceRoot":"","sources":["../../src/commands/elementContainerCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type ModelElement } from 'ckeditor5';
|
|
2
|
+
import type { ElementOrPosition } from 'ckeditor5-elements-core';
|
|
3
|
+
import ElementSchemaCommand from './elementSchemaCommand.js';
|
|
4
|
+
/**
|
|
5
|
+
* Toggles an inline element with text, as CKEditor's `bold` toggles an attribute of text, but with an element, e.g. a
|
|
6
|
+
* `bold` that is an inline `block` of the element schema.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* editor.execute('bold'); // The selection.
|
|
10
|
+
* editor.commands.get('bold').executeFor(element); // An element, e.g. of a context menu.
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* * On selected text, it wraps the text with the element, in each block, and merges it with the elements next to it.
|
|
14
|
+
* When all the text is in the element already, it unwraps the elements: the whole ones, not only the selected part.
|
|
15
|
+
* * On the cursor, it inserts an empty element with the cursor in it. In the element, it moves the cursor out of it, at
|
|
16
|
+
* its start or end, or it unwraps it.
|
|
17
|
+
* * On an element (selected, or given), it unwraps the element it is in, or wraps it.
|
|
18
|
+
*
|
|
19
|
+
* It is "on" ({@link isOn}) when the target is in the element: for selected text, when all of it is.
|
|
20
|
+
*/
|
|
21
|
+
export default class ElementInlineCommand extends ElementSchemaCommand {
|
|
22
|
+
isToggleable: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Puts the target in the element, or out of it: by default, the selection, also selected text.
|
|
25
|
+
*/
|
|
26
|
+
execute(elementOrPosition?: ElementOrPosition): void;
|
|
27
|
+
/**
|
|
28
|
+
* Enabled when the target can be edited, and is in the element, or the element can be there.
|
|
29
|
+
*/
|
|
30
|
+
protected _isEnabledFor(elementOrPosition?: ElementOrPosition): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* "On" when the target is in the element: for selected text, when all of it is.
|
|
33
|
+
*/
|
|
34
|
+
protected _isOnFor(elementOrPosition?: ElementOrPosition): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Whether all the text of the ranges is in the element: there are elements, and nothing to wrap.
|
|
37
|
+
*/
|
|
38
|
+
private _isOnRanges;
|
|
39
|
+
/**
|
|
40
|
+
* Returns the elements of the command that a node is, or is in, in its line of text: the node and its ancestors, up to
|
|
41
|
+
* the first that is not inline, e.g. the paragraph. The inner first.
|
|
42
|
+
*/
|
|
43
|
+
private _getElementsAround;
|
|
44
|
+
/**
|
|
45
|
+
* Returns the closest element of the command that the target is, or is in, in its line of text.
|
|
46
|
+
*/
|
|
47
|
+
protected _findElement(elementOrPosition?: ElementOrPosition): ModelElement | null;
|
|
48
|
+
/**
|
|
49
|
+
* Returns the elements of the command in ranges, and around their ends.
|
|
50
|
+
*/
|
|
51
|
+
private _getElementsIn;
|
|
52
|
+
/**
|
|
53
|
+
* Returns the ranges to wrap with the element to put the text of ranges in it: in each parent the element is allowed
|
|
54
|
+
* in, the runs of nodes it can hold, which are not in the element already. The other elements are looked into, e.g.
|
|
55
|
+
* the paragraphs of a selection across blocks.
|
|
56
|
+
*/
|
|
57
|
+
private _getWrapRanges;
|
|
58
|
+
/**
|
|
59
|
+
* Wraps the text of ranges with new elements, merged with the ones next to them, and keeps the selection on the text.
|
|
60
|
+
*/
|
|
61
|
+
private _wrapRanges;
|
|
62
|
+
/**
|
|
63
|
+
* Unwraps the elements in ranges and around their ends, and keeps the selection on the text.
|
|
64
|
+
*/
|
|
65
|
+
private _unwrapRanges;
|
|
66
|
+
/**
|
|
67
|
+
* Changes the content of the selected ranges, and selects the same text then: the ends of the ranges stick to the
|
|
68
|
+
* text, so they follow it into the elements and out of them.
|
|
69
|
+
*/
|
|
70
|
+
private _keepSelection;
|
|
71
|
+
/**
|
|
72
|
+
* Merges a new element with the elements of the command next to it that have no attributes, as it has none.
|
|
73
|
+
*/
|
|
74
|
+
private _mergeWithSiblings;
|
|
75
|
+
/**
|
|
76
|
+
* Puts the target out of the element it is in: a cursor at the start or the end of the element goes before or after
|
|
77
|
+
* it, so that what is typed next is out of it; otherwise the element is unwrapped. A selected element that is
|
|
78
|
+
* unwrapped leaves its content selected.
|
|
79
|
+
*/
|
|
80
|
+
private _leave;
|
|
81
|
+
/**
|
|
82
|
+
* Puts the target in a new element: at a position, an empty element with the cursor in it; an element, e.g. a
|
|
83
|
+
* selected inline object, is wrapped, and selected.
|
|
84
|
+
*/
|
|
85
|
+
private _insert;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=elementInlineCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementInlineCommand.d.ts","sourceRoot":"","sources":["../../src/commands/elementInlineCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,KAAK,YAAY,EAIjB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,oBAAoB,MAAM,2BAA2B,CAAC;AAE7D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,oBAAoB;IACrD,YAAY,UAAQ;IAEpC;;OAEG;IACa,OAAO,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAuBpE;;OAEG;cACgB,aAAa,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO;IAqBhF;;OAEG;cACgB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO;IAO3E;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;cACgB,YAAY,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,YAAY,GAAG,IAAI;IAM3F;;OAEG;IACH,OAAO,CAAC,cAAc;IAqBtB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IA8CtB;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,OAAO,CAAC,aAAa;IAUrB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAmBtB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;;;OAIG;IACH,OAAO,CAAC,MAAM;IAqBd;;;OAGG;IACH,OAAO,CAAC,OAAO;CAWf"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { ModelLivePosition, ModelLiveRange } from 'ckeditor5';
|
|
2
|
+
import ElementSchemaCommand from './elementSchemaCommand.js';
|
|
3
|
+
/**
|
|
4
|
+
* Toggles an inline element with text, as CKEditor's `bold` toggles an attribute of text, but with an element, e.g. a
|
|
5
|
+
* `bold` that is an inline `block` of the element schema.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* editor.execute('bold'); // The selection.
|
|
9
|
+
* editor.commands.get('bold').executeFor(element); // An element, e.g. of a context menu.
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* * On selected text, it wraps the text with the element, in each block, and merges it with the elements next to it.
|
|
13
|
+
* When all the text is in the element already, it unwraps the elements: the whole ones, not only the selected part.
|
|
14
|
+
* * On the cursor, it inserts an empty element with the cursor in it. In the element, it moves the cursor out of it, at
|
|
15
|
+
* its start or end, or it unwraps it.
|
|
16
|
+
* * On an element (selected, or given), it unwraps the element it is in, or wraps it.
|
|
17
|
+
*
|
|
18
|
+
* It is "on" ({@link isOn}) when the target is in the element: for selected text, when all of it is.
|
|
19
|
+
*/
|
|
20
|
+
export default class ElementInlineCommand extends ElementSchemaCommand {
|
|
21
|
+
isToggleable = true;
|
|
22
|
+
/**
|
|
23
|
+
* Puts the target in the element, or out of it: by default, the selection, also selected text.
|
|
24
|
+
*/
|
|
25
|
+
execute(elementOrPosition) {
|
|
26
|
+
const ranges = this._getSelectedRanges(elementOrPosition);
|
|
27
|
+
const target = this._getElementOrPosition(elementOrPosition);
|
|
28
|
+
this.editor.model.change(writer => {
|
|
29
|
+
if (ranges) {
|
|
30
|
+
if (this._isOnRanges(ranges)) {
|
|
31
|
+
this._unwrapRanges(writer, ranges);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this._wrapRanges(writer, ranges);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else if (target) {
|
|
38
|
+
const element = this._findElement(target);
|
|
39
|
+
if (element) {
|
|
40
|
+
this._leave(writer, element, target);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
this._insert(writer, target);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Enabled when the target can be edited, and is in the element, or the element can be there.
|
|
50
|
+
*/
|
|
51
|
+
_isEnabledFor(elementOrPosition) {
|
|
52
|
+
const ranges = this._getSelectedRanges(elementOrPosition);
|
|
53
|
+
const target = this._getElementOrPosition(elementOrPosition);
|
|
54
|
+
if (!target || !super._isEnabledFor(target)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (ranges) {
|
|
58
|
+
return this._isOnRanges(ranges) || this._getWrapRanges(ranges).length > 0;
|
|
59
|
+
}
|
|
60
|
+
if (this._findElement(target)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return target.is('position')
|
|
64
|
+
? this.editor.model.schema.checkChild(target, this.elementName)
|
|
65
|
+
: this._canHold(this.editor.model.createPositionBefore(target), [target]);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* "On" when the target is in the element: for selected text, when all of it is.
|
|
69
|
+
*/
|
|
70
|
+
_isOnFor(elementOrPosition) {
|
|
71
|
+
const ranges = this._getSelectedRanges(elementOrPosition);
|
|
72
|
+
const target = this._getElementOrPosition(elementOrPosition);
|
|
73
|
+
return ranges ? this._isOnRanges(ranges) : !!target && !!this._findElement(target);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Whether all the text of the ranges is in the element: there are elements, and nothing to wrap.
|
|
77
|
+
*/
|
|
78
|
+
_isOnRanges(ranges) {
|
|
79
|
+
return this._getElementsIn(ranges).length > 0 && !this._getWrapRanges(ranges).length;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Returns the elements of the command that a node is, or is in, in its line of text: the node and its ancestors, up to
|
|
83
|
+
* the first that is not inline, e.g. the paragraph. The inner first.
|
|
84
|
+
*/
|
|
85
|
+
_getElementsAround(node) {
|
|
86
|
+
const schema = this.editor.model.schema;
|
|
87
|
+
const elements = [];
|
|
88
|
+
for (let element = node; element?.is('element') && schema.isInline(element); element = element.parent) {
|
|
89
|
+
if (this.isElement(element)) {
|
|
90
|
+
elements.push(element);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return elements;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Returns the closest element of the command that the target is, or is in, in its line of text.
|
|
97
|
+
*/
|
|
98
|
+
_findElement(elementOrPosition) {
|
|
99
|
+
const target = this._getElementOrPosition(elementOrPosition);
|
|
100
|
+
return target ? (this._getElementsAround(target.is('position') ? target.parent : target)[0] ?? null) : null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Returns the elements of the command in ranges, and around their ends.
|
|
104
|
+
*/
|
|
105
|
+
_getElementsIn(ranges) {
|
|
106
|
+
const elements = new Set();
|
|
107
|
+
for (const range of ranges) {
|
|
108
|
+
for (const element of [
|
|
109
|
+
...this._getElementsAround(range.start.parent),
|
|
110
|
+
...this._getElementsAround(range.end.parent)
|
|
111
|
+
]) {
|
|
112
|
+
elements.add(element);
|
|
113
|
+
}
|
|
114
|
+
for (const item of range.getItems()) {
|
|
115
|
+
if (this.isElement(item)) {
|
|
116
|
+
elements.add(item);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return Array.from(elements);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Returns the ranges to wrap with the element to put the text of ranges in it: in each parent the element is allowed
|
|
124
|
+
* in, the runs of nodes it can hold, which are not in the element already. The other elements are looked into, e.g.
|
|
125
|
+
* the paragraphs of a selection across blocks.
|
|
126
|
+
*/
|
|
127
|
+
_getWrapRanges(ranges) {
|
|
128
|
+
const model = this.editor.model;
|
|
129
|
+
const wrapRanges = [];
|
|
130
|
+
const collect = (flatRange) => {
|
|
131
|
+
const parent = flatRange.start.parent;
|
|
132
|
+
if (this._getElementsAround(parent).length) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
let run = null;
|
|
136
|
+
const close = () => {
|
|
137
|
+
if (run) {
|
|
138
|
+
wrapRanges.push(run);
|
|
139
|
+
run = null;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
for (const item of flatRange.getItems({ shallow: true })) {
|
|
143
|
+
if (this.isElement(item)) {
|
|
144
|
+
close();
|
|
145
|
+
}
|
|
146
|
+
else if (this._canHold(flatRange.start, [item])) {
|
|
147
|
+
run = model.createRange(run?.start ?? model.createPositionBefore(item), model.createPositionAfter(item));
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
close();
|
|
151
|
+
if (item.is('element')) {
|
|
152
|
+
collect(model.createRangeIn(item));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
close();
|
|
157
|
+
};
|
|
158
|
+
for (const range of ranges) {
|
|
159
|
+
range.getMinimalFlatRanges().forEach(collect);
|
|
160
|
+
}
|
|
161
|
+
return wrapRanges;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Wraps the text of ranges with new elements, merged with the ones next to them, and keeps the selection on the text.
|
|
165
|
+
*/
|
|
166
|
+
_wrapRanges(writer, ranges) {
|
|
167
|
+
// The last first: wrapping one does not move the ones before it.
|
|
168
|
+
const wrapRanges = this._getWrapRanges(ranges).reverse();
|
|
169
|
+
this._keepSelection(writer, ranges, () => {
|
|
170
|
+
for (const range of wrapRanges) {
|
|
171
|
+
const element = writer.createElement(this.elementName);
|
|
172
|
+
writer.wrap(range, element);
|
|
173
|
+
this._mergeWithSiblings(writer, element);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Unwraps the elements in ranges and around their ends, and keeps the selection on the text.
|
|
179
|
+
*/
|
|
180
|
+
_unwrapRanges(writer, ranges) {
|
|
181
|
+
const elements = this._getElementsIn(ranges);
|
|
182
|
+
this._keepSelection(writer, ranges, () => {
|
|
183
|
+
for (const element of elements) {
|
|
184
|
+
writer.unwrap(element);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Changes the content of the selected ranges, and selects the same text then: the ends of the ranges stick to the
|
|
190
|
+
* text, so they follow it into the elements and out of them.
|
|
191
|
+
*/
|
|
192
|
+
_keepSelection(writer, ranges, change) {
|
|
193
|
+
const backward = this.editor.model.document.selection.isBackward;
|
|
194
|
+
const ends = ranges.map(range => ({
|
|
195
|
+
start: ModelLivePosition.fromPosition(range.start, 'toNext'),
|
|
196
|
+
end: ModelLivePosition.fromPosition(range.end, 'toPrevious')
|
|
197
|
+
}));
|
|
198
|
+
change();
|
|
199
|
+
writer.setSelection(ends.map(({ start, end }) => writer.createRange(start, end)), { backward });
|
|
200
|
+
for (const { start, end } of ends) {
|
|
201
|
+
start.detach();
|
|
202
|
+
end.detach();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Merges a new element with the elements of the command next to it that have no attributes, as it has none.
|
|
207
|
+
*/
|
|
208
|
+
_mergeWithSiblings(writer, element) {
|
|
209
|
+
const isPlain = (node) => this.isElement(node) && node.getAttributeKeys().next().done === true;
|
|
210
|
+
if (isPlain(element.nextSibling)) {
|
|
211
|
+
writer.merge(writer.createPositionAfter(element));
|
|
212
|
+
}
|
|
213
|
+
if (isPlain(element.previousSibling)) {
|
|
214
|
+
writer.merge(writer.createPositionBefore(element));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Puts the target out of the element it is in: a cursor at the start or the end of the element goes before or after
|
|
219
|
+
* it, so that what is typed next is out of it; otherwise the element is unwrapped. A selected element that is
|
|
220
|
+
* unwrapped leaves its content selected.
|
|
221
|
+
*/
|
|
222
|
+
_leave(writer, element, target) {
|
|
223
|
+
if (target.is('position') && target.parent === element && !element.isEmpty) {
|
|
224
|
+
if (target.isAtEnd || target.isAtStart) {
|
|
225
|
+
writer.setSelection(element, target.isAtEnd ? 'after' : 'before');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const isSelected = this.editor.model.document.selection.getSelectedElement() === element;
|
|
230
|
+
const content = ModelLiveRange.fromRange(writer.createRangeIn(element));
|
|
231
|
+
writer.unwrap(element);
|
|
232
|
+
if (isSelected) {
|
|
233
|
+
writer.setSelection(content.toRange());
|
|
234
|
+
}
|
|
235
|
+
content.detach();
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Puts the target in a new element: at a position, an empty element with the cursor in it; an element, e.g. a
|
|
239
|
+
* selected inline object, is wrapped, and selected.
|
|
240
|
+
*/
|
|
241
|
+
_insert(writer, target) {
|
|
242
|
+
if (target.is('position')) {
|
|
243
|
+
const element = writer.createElement(this.elementName);
|
|
244
|
+
writer.insert(element, target);
|
|
245
|
+
writer.setSelection(element, 0);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
writer.wrap(writer.createRangeOn(target), this.elementName);
|
|
249
|
+
writer.setSelection(target, 'on');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=elementInlineCommand.js.map
|