ckeditor5-elements-core 0.0.0-stage → 49.0.0-alpha.2
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/LICENSE +21 -0
- package/README.md +36 -2
- package/dist/augmentation.d.ts +7 -0
- package/dist/augmentation.d.ts.map +1 -0
- package/dist/augmentation.js +2 -0
- package/dist/augmentation.js.map +1 -0
- package/dist/elementButtonView.d.ts +14 -0
- package/dist/elementButtonView.d.ts.map +1 -0
- package/dist/elementButtonView.js +14 -0
- package/dist/elementButtonView.js.map +1 -0
- package/dist/elementCommand.d.ts +69 -0
- package/dist/elementCommand.d.ts.map +1 -0
- package/dist/elementCommand.js +133 -0
- package/dist/elementCommand.js.map +1 -0
- package/dist/elementMenu.d.ts +93 -0
- package/dist/elementMenu.d.ts.map +1 -0
- package/dist/elementMenu.js +163 -0
- package/dist/elementMenu.js.map +1 -0
- package/dist/elementMenuItemButtonView.d.ts +24 -0
- package/dist/elementMenuItemButtonView.d.ts.map +1 -0
- package/dist/elementMenuItemButtonView.js +29 -0
- package/dist/elementMenuItemButtonView.js.map +1 -0
- package/dist/elementMenuView.d.ts +35 -0
- package/dist/elementMenuView.d.ts.map +1 -0
- package/dist/elementMenuView.js +118 -0
- package/dist/elementMenuView.js.map +1 -0
- package/dist/elementOrPositionButtonView.d.ts +51 -0
- package/dist/elementOrPositionButtonView.d.ts.map +1 -0
- package/dist/elementOrPositionButtonView.js +89 -0
- package/dist/elementOrPositionButtonView.js.map +1 -0
- package/dist/elementOrPositionCommand.d.ts +113 -0
- package/dist/elementOrPositionCommand.d.ts.map +1 -0
- package/dist/elementOrPositionCommand.js +138 -0
- package/dist/elementOrPositionCommand.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/matching.d.ts +24 -0
- package/dist/matching.d.ts.map +1 -0
- package/dist/matching.js +27 -0
- package/dist/matching.js.map +1 -0
- package/dist/menuSeparatorView.d.ts +8 -0
- package/dist/menuSeparatorView.d.ts.map +1 -0
- package/dist/menuSeparatorView.js +16 -0
- package/dist/menuSeparatorView.js.map +1 -0
- package/dist/singleCopyGuard.d.ts +2 -0
- package/dist/singleCopyGuard.d.ts.map +1 -0
- package/dist/singleCopyGuard.js +16 -0
- package/dist/singleCopyGuard.js.map +1 -0
- package/dist/submenuView.d.ts +68 -0
- package/dist/submenuView.d.ts.map +1 -0
- package/dist/submenuView.js +159 -0
- package/dist/submenuView.js.map +1 -0
- package/dist/utils.d.ts +23 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +47 -0
- package/dist/utils.js.map +1 -0
- package/package.json +43 -3
- package/src/augmentation.ts +7 -0
- package/src/elementButtonView.ts +21 -0
- package/src/elementCommand.ts +177 -0
- package/src/elementMenu.ts +275 -0
- package/src/elementMenuView.ts +164 -0
- package/src/elementOrPositionButtonView.ts +138 -0
- package/src/elementOrPositionCommand.ts +194 -0
- package/src/index.ts +38 -0
- package/src/matching.ts +45 -0
- package/src/singleCopyGuard.ts +20 -0
- package/src/utils.ts +59 -0
- package/theme/elementMenu.css +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ivan Kildyushov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ckeditor5-elements-core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The base of CKEditor 5 Elements: commands that act on a position or an element (`ElementOrPositionCommand`,
|
|
4
|
+
`ElementCommand`), their buttons (`ElementOrPositionButtonView`, `ElementButtonView`), and the `ElementMenu` plugin, which
|
|
5
|
+
shows the menus of the context menu, the breadcrumbs and quick insert.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
This package is part of `ckeditor5-elements`. Install the whole package to use it:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install ckeditor5 ckeditor5-elements
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { ElementButtonView, ElementCommand } from 'ckeditor5-elements';
|
|
19
|
+
|
|
20
|
+
class CalloutCommand extends ElementCommand {
|
|
21
|
+
constructor(editor) {
|
|
22
|
+
super(editor);
|
|
23
|
+
|
|
24
|
+
this.elementName = 'callout';
|
|
25
|
+
this.set({ title: 'Callout', icon: calloutIcon });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
editor.commands.add('callout', new CalloutCommand(editor));
|
|
30
|
+
editor.ui.componentFactory.add('callout', locale => new ElementButtonView(locale, editor.commands.get('callout')));
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
See the [Commands guide](https://ivankild.github.io/ckeditor5-elements/guide/commands) of the documentation.
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"augmentation.d.ts","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,QAAQ,WAAW,CAAC;IAC1B,UAAU,UAAU;QACnB,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC;KACtC;CACD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"augmentation.js","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Locale } from 'ckeditor5';
|
|
2
|
+
import type ElementCommand from './elementCommand.js';
|
|
3
|
+
import ElementOrPositionButtonView, { type ElementOrPositionButtonViewOptions } from './elementOrPositionButtonView.js';
|
|
4
|
+
/**
|
|
5
|
+
* A button that reflects an {@link ElementCommand}, including whether it is "on", and executes it when clicked.
|
|
6
|
+
*/
|
|
7
|
+
export default class ElementButtonView extends ElementOrPositionButtonView {
|
|
8
|
+
/**
|
|
9
|
+
* The command this button reflects.
|
|
10
|
+
*/
|
|
11
|
+
readonly command: ElementCommand;
|
|
12
|
+
constructor(locale: Locale | undefined, command: ElementCommand, options?: ElementOrPositionButtonViewOptions);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=elementButtonView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementButtonView.d.ts","sourceRoot":"","sources":["../src/elementButtonView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,2BAA2B,EAAE,EAAE,KAAK,kCAAkC,EAAE,MAAM,kCAAkC,CAAC;AAExH;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,2BAA2B;IACzE;;OAEG;IACH,SAAwB,OAAO,EAAE,cAAc,CAAC;gBAEpC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,GAAE,kCAAuC;CAOjH"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {} from 'ckeditor5';
|
|
2
|
+
import ElementOrPositionButtonView, {} from './elementOrPositionButtonView.js';
|
|
3
|
+
/**
|
|
4
|
+
* A button that reflects an {@link ElementCommand}, including whether it is "on", and executes it when clicked.
|
|
5
|
+
*/
|
|
6
|
+
export default class ElementButtonView extends ElementOrPositionButtonView {
|
|
7
|
+
constructor(locale, command, options = {}) {
|
|
8
|
+
super(locale, command, options);
|
|
9
|
+
if (!options.target) {
|
|
10
|
+
this.bind('isOn').to(command, 'isOn');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=elementButtonView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementButtonView.js","sourceRoot":"","sources":["../src/elementButtonView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,MAAM,WAAW,CAAC;AAExC,OAAO,2BAA2B,EAAE,EAA2C,MAAM,kCAAkC,CAAC;AAExH;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,2BAA2B;IAMzE,YAAY,MAA0B,EAAE,OAAuB,EAAE,UAA8C,EAAE;QAChH,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAEhC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;IACF,CAAC;CACD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type Editor, type ModelDocumentFragment, type ModelElement, type ModelNode, type ModelWriter } from 'ckeditor5';
|
|
2
|
+
import ElementOrPositionCommand, { type ElementOrPosition } from './elementOrPositionCommand.js';
|
|
3
|
+
/**
|
|
4
|
+
* A command that toggles an element.
|
|
5
|
+
*
|
|
6
|
+
* Both {@link refresh} and {@link execute} accept an optional position or element. When it is
|
|
7
|
+
* absent, it is taken from the current selection (see {@link ElementOrPositionCommand}).
|
|
8
|
+
*
|
|
9
|
+
* The command toggles: it unwraps the closest matching element, or wraps the target with a new one.
|
|
10
|
+
* It is enabled only when the toggle is allowed by the schema and the target is editable.
|
|
11
|
+
*/
|
|
12
|
+
export default class ElementCommand extends ElementOrPositionCommand {
|
|
13
|
+
/**
|
|
14
|
+
* Whether the command is "on" (e.g. the element is active at the given position).
|
|
15
|
+
*
|
|
16
|
+
* @observable
|
|
17
|
+
*/
|
|
18
|
+
isOn: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The name of the model element this command matches, wraps with and unwraps.
|
|
21
|
+
*/
|
|
22
|
+
elementName: string;
|
|
23
|
+
/**
|
|
24
|
+
* The attributes the model element must have to be matched. Also set on newly created elements.
|
|
25
|
+
*/
|
|
26
|
+
elementAttributes: Record<string, unknown>;
|
|
27
|
+
constructor(editor: Editor);
|
|
28
|
+
refresh(elementOrPosition?: ElementOrPosition): void;
|
|
29
|
+
/**
|
|
30
|
+
* Checks whether the given node is an element handled by this command: it is named {@link elementName}
|
|
31
|
+
* and has all {@link elementAttributes} with the same values.
|
|
32
|
+
*/
|
|
33
|
+
isElement(node: ModelNode | ModelDocumentFragment): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Unwraps the closest element that satisfies {@link isElement}. If there is none,
|
|
36
|
+
* wraps the given element (or the position's parent) with a new element.
|
|
37
|
+
*/
|
|
38
|
+
execute(elementOrPosition?: ElementOrPosition): void;
|
|
39
|
+
/**
|
|
40
|
+
* Creates the element used for wrapping.
|
|
41
|
+
*/
|
|
42
|
+
protected _createElement(writer: ModelWriter): ModelElement;
|
|
43
|
+
/**
|
|
44
|
+
* Enabled when the target can be edited and the toggle is allowed by the schema.
|
|
45
|
+
*/
|
|
46
|
+
protected _isEnabledFor(elementOrPosition?: ElementOrPosition): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* "On" when the target is (in) a matching element.
|
|
49
|
+
*/
|
|
50
|
+
protected _isOnFor(elementOrPosition?: ElementOrPosition): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Returns the element that would be wrapped: the given element, or the position's parent. Never a root.
|
|
53
|
+
*/
|
|
54
|
+
private _getNodeToWrap;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the schema allows the new element in place of the node to wrap, and the node inside the new element.
|
|
57
|
+
*/
|
|
58
|
+
private _canWrap;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the schema allows all children of the element in its parent.
|
|
61
|
+
*/
|
|
62
|
+
private _canUnwrap;
|
|
63
|
+
/**
|
|
64
|
+
* Returns the closest element that satisfies {@link isElement}: the element itself or its ancestor,
|
|
65
|
+
* or the position's parent or its ancestor. Root elements are skipped, as they cannot be unwrapped.
|
|
66
|
+
*/
|
|
67
|
+
private _findElement;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=elementCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementCommand.d.ts","sourceRoot":"","sources":["../src/elementCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,MAAM,EACX,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,MAAM,WAAW,CAAC;AACnB,OAAO,wBAAwB,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEjG;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,wBAAwB;IACnE;;;;OAIG;IACY,IAAI,EAAE,OAAO,CAAC;IAE7B;;OAEG;IACI,WAAW,SAAa;IAE/B;;OAEG;IACI,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;gBAE3C,MAAM,EAAE,MAAM;IAOV,OAAO,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAKpE;;;OAGG;IACI,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,qBAAqB,GAAG,OAAO;IAQlE;;;OAGG;IACa,OAAO,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAsBpE;;OAEG;IACH,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY;IAI3D;;OAEG;cACgB,aAAa,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO;IAWhF;;OAEG;cACgB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO;IAM3E;;OAEG;IACH,OAAO,CAAC,cAAc;IAMtB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAgBhB;;OAEG;IACH,OAAO,CAAC,UAAU;IAYlB;;;OAGG;IACH,OAAO,CAAC,YAAY;CAapB"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {} from 'ckeditor5';
|
|
2
|
+
import ElementOrPositionCommand, {} from './elementOrPositionCommand.js';
|
|
3
|
+
/**
|
|
4
|
+
* A command that toggles an element.
|
|
5
|
+
*
|
|
6
|
+
* Both {@link refresh} and {@link execute} accept an optional position or element. When it is
|
|
7
|
+
* absent, it is taken from the current selection (see {@link ElementOrPositionCommand}).
|
|
8
|
+
*
|
|
9
|
+
* The command toggles: it unwraps the closest matching element, or wraps the target with a new one.
|
|
10
|
+
* It is enabled only when the toggle is allowed by the schema and the target is editable.
|
|
11
|
+
*/
|
|
12
|
+
export default class ElementCommand extends ElementOrPositionCommand {
|
|
13
|
+
/**
|
|
14
|
+
* The name of the model element this command matches, wraps with and unwraps.
|
|
15
|
+
*/
|
|
16
|
+
elementName = 'element';
|
|
17
|
+
/**
|
|
18
|
+
* The attributes the model element must have to be matched. Also set on newly created elements.
|
|
19
|
+
*/
|
|
20
|
+
elementAttributes = {};
|
|
21
|
+
constructor(editor) {
|
|
22
|
+
super(editor);
|
|
23
|
+
this.set('isOn', false);
|
|
24
|
+
this.isToggleable = true;
|
|
25
|
+
}
|
|
26
|
+
refresh(elementOrPosition) {
|
|
27
|
+
this.isOn = this._isOnFor(elementOrPosition);
|
|
28
|
+
super.refresh(elementOrPosition);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Checks whether the given node is an element handled by this command: it is named {@link elementName}
|
|
32
|
+
* and has all {@link elementAttributes} with the same values.
|
|
33
|
+
*/
|
|
34
|
+
isElement(node) {
|
|
35
|
+
if (!node.is('element', this.elementName)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return Object.entries(this.elementAttributes).every(([key, value]) => node.getAttribute(key) === value);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Unwraps the closest element that satisfies {@link isElement}. If there is none,
|
|
42
|
+
* wraps the given element (or the position's parent) with a new element.
|
|
43
|
+
*/
|
|
44
|
+
execute(elementOrPosition) {
|
|
45
|
+
const model = this.editor.model;
|
|
46
|
+
const target = this._getElementOrPosition(elementOrPosition);
|
|
47
|
+
if (!target || !model.canEditAt(target)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const element = this._findElement(target);
|
|
51
|
+
const nodeToWrap = this._getNodeToWrap(target);
|
|
52
|
+
model.change(writer => {
|
|
53
|
+
if (element) {
|
|
54
|
+
if (this._canUnwrap(element)) {
|
|
55
|
+
writer.unwrap(element);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (nodeToWrap && this._canWrap(target)) {
|
|
59
|
+
writer.wrap(writer.createRangeOn(nodeToWrap), this._createElement(writer));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates the element used for wrapping.
|
|
65
|
+
*/
|
|
66
|
+
_createElement(writer) {
|
|
67
|
+
return writer.createElement(this.elementName, this.elementAttributes);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Enabled when the target can be edited and the toggle is allowed by the schema.
|
|
71
|
+
*/
|
|
72
|
+
_isEnabledFor(elementOrPosition) {
|
|
73
|
+
const target = this._getElementOrPosition(elementOrPosition);
|
|
74
|
+
const element = target && this._findElement(target);
|
|
75
|
+
return (!!target &&
|
|
76
|
+
this.editor.model.canEditAt(target) &&
|
|
77
|
+
(element ? this._canUnwrap(element) : this._canWrap(target)));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* "On" when the target is (in) a matching element.
|
|
81
|
+
*/
|
|
82
|
+
_isOnFor(elementOrPosition) {
|
|
83
|
+
const target = this._getElementOrPosition(elementOrPosition);
|
|
84
|
+
return !!target && !!this._findElement(target);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Returns the element that would be wrapped: the given element, or the position's parent. Never a root.
|
|
88
|
+
*/
|
|
89
|
+
_getNodeToWrap(elementOrPosition) {
|
|
90
|
+
const node = elementOrPosition.is('position') ? elementOrPosition.parent : elementOrPosition;
|
|
91
|
+
return node.is('element') && !node.is('rootElement') ? node : null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Whether the schema allows the new element in place of the node to wrap, and the node inside the new element.
|
|
95
|
+
*/
|
|
96
|
+
_canWrap(elementOrPosition) {
|
|
97
|
+
const node = this._getNodeToWrap(elementOrPosition);
|
|
98
|
+
if (!node) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
const model = this.editor.model;
|
|
102
|
+
const ancestors = node.getAncestors().filter(ancestor => ancestor.is('element'));
|
|
103
|
+
return (model.schema.checkChild(model.createPositionBefore(node), this.elementName) &&
|
|
104
|
+
model.schema.checkChild([...ancestors, this.elementName], node));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Whether the schema allows all children of the element in its parent.
|
|
108
|
+
*/
|
|
109
|
+
_canUnwrap(element) {
|
|
110
|
+
const model = this.editor.model;
|
|
111
|
+
if (!element.parent) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const position = model.createPositionBefore(element);
|
|
115
|
+
return Array.from(element.getChildren()).every(child => model.schema.checkChild(position, child));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Returns the closest element that satisfies {@link isElement}: the element itself or its ancestor,
|
|
119
|
+
* or the position's parent or its ancestor. Root elements are skipped, as they cannot be unwrapped.
|
|
120
|
+
*/
|
|
121
|
+
_findElement(elementOrPosition) {
|
|
122
|
+
const ancestors = elementOrPosition.is('position')
|
|
123
|
+
? elementOrPosition.getAncestors()
|
|
124
|
+
: elementOrPosition.getAncestors({ includeSelf: true });
|
|
125
|
+
for (const node of ancestors.reverse()) {
|
|
126
|
+
if (node.is('element') && !node.is('rootElement') && this.isElement(node)) {
|
|
127
|
+
return node;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=elementCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementCommand.js","sourceRoot":"","sources":["../src/elementCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { ContextualBalloon, Notification, Plugin, type Editor, type FocusableView, type ModelElement, type RectSource } from 'ckeditor5';
|
|
2
|
+
import ElementOrPositionCommand from './elementOrPositionCommand.js';
|
|
3
|
+
/**
|
|
4
|
+
* A submenu of an element menu. Missing items are skipped, as in the menu.
|
|
5
|
+
*/
|
|
6
|
+
export interface ElementSubmenu {
|
|
7
|
+
title: string;
|
|
8
|
+
/**
|
|
9
|
+
* The icon (SVG string) shown next to the title.
|
|
10
|
+
*/
|
|
11
|
+
icon?: string;
|
|
12
|
+
items: (ElementMenuItem | null | undefined)[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* An item of an element menu:
|
|
16
|
+
*
|
|
17
|
+
* * a command, shown as a button with its `title` and `icon`, enabled and "on" according to its
|
|
18
|
+
* {@link ElementOrPositionCommand#getState state} for the element,
|
|
19
|
+
* * a submenu,
|
|
20
|
+
* * `'|'`, a separator. Separators at the start or the end of a menu, and repeated ones, are not shown, so a group of
|
|
21
|
+
* items can start with one.
|
|
22
|
+
*/
|
|
23
|
+
export type ElementMenuItem = ElementOrPositionCommand | ElementSubmenu | '|';
|
|
24
|
+
/**
|
|
25
|
+
* Returns the items of the menu of the given element, e.g. as the `elementContextMenu` or `breadcrumbs.menu` editor
|
|
26
|
+
* config option. Missing items (e.g. commands that are not registered) are skipped. No items means no menu.
|
|
27
|
+
*/
|
|
28
|
+
export type ElementMenuConfig = (element: ModelElement, editor: Editor) => (ElementMenuItem | null | undefined)[];
|
|
29
|
+
/**
|
|
30
|
+
* The options of {@link ElementMenu#open} and {@link ElementMenu#show}.
|
|
31
|
+
*/
|
|
32
|
+
export interface ElementMenuShowOptions {
|
|
33
|
+
/**
|
|
34
|
+
* The accessible name of the menu of an element (see {@link ElementMenu#open}). Defaults to "Element menu".
|
|
35
|
+
*/
|
|
36
|
+
label?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Called when the document data changes while the menu is open. When it returns `true`, the menu closes, e.g.
|
|
39
|
+
* because the element it is for was removed.
|
|
40
|
+
*/
|
|
41
|
+
isObsolete?: () => boolean;
|
|
42
|
+
/**
|
|
43
|
+
* What gets the focus when the menu is closed with Esc, e.g. the button that opened the menu. Defaults to the editor.
|
|
44
|
+
*/
|
|
45
|
+
returnFocusTo?: {
|
|
46
|
+
focus(): void;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Called when the menu closes, for any reason: Esc, a click outside, an executed item, or another menu replacing it.
|
|
50
|
+
*/
|
|
51
|
+
onHide?: () => void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Shows menus in a balloon: the menu of an element, built from commands, submenus and separators (see {@link open}),
|
|
55
|
+
* or any view, e.g. the quick insert search (see {@link show}).
|
|
56
|
+
*
|
|
57
|
+
* The element context menu, the breadcrumbs and quick insert show their menus with it. The menu of an element is
|
|
58
|
+
* CKEditor's dropdown menu (see {@link ElementMenuView}).
|
|
59
|
+
*
|
|
60
|
+
* Executing an item closes the menu. Esc and clicking outside close it too; Esc returns the focus to the editor, or to
|
|
61
|
+
* {@link ElementMenuShowOptions.returnFocusTo}.
|
|
62
|
+
*/
|
|
63
|
+
export default class ElementMenu extends Plugin {
|
|
64
|
+
/**
|
|
65
|
+
* The view in the open menu, if any.
|
|
66
|
+
*/
|
|
67
|
+
view: FocusableView | null;
|
|
68
|
+
private _keystrokes;
|
|
69
|
+
private _isObsolete;
|
|
70
|
+
private _onHide;
|
|
71
|
+
static get requires(): readonly [typeof ContextualBalloon, typeof Notification];
|
|
72
|
+
static get pluginName(): 'ElementMenu';
|
|
73
|
+
init(): void;
|
|
74
|
+
/**
|
|
75
|
+
* Opens the menu of the element next to the target (e.g. a button, or a `DOMRect` in viewport coordinates), with
|
|
76
|
+
* the items from the config callback. The menu closes when the element is removed.
|
|
77
|
+
*
|
|
78
|
+
* Returns `false` when there are no items.
|
|
79
|
+
*/
|
|
80
|
+
open(element: ModelElement, target: RectSource, config: ElementMenuConfig, { label, isObsolete, ...options }?: ElementMenuShowOptions): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Shows the view (e.g. a menu) next to the target (e.g. a button, or a `DOMRect` in viewport coordinates), and
|
|
83
|
+
* focuses it. The menu closes when the view, or an item of CKEditor's dropdown menu in it, fires `execute`.
|
|
84
|
+
*/
|
|
85
|
+
show(view: FocusableView, target: RectSource, { isObsolete, returnFocusTo, onHide }?: ElementMenuShowOptions): void;
|
|
86
|
+
/**
|
|
87
|
+
* Hides the open menu, if any.
|
|
88
|
+
*/
|
|
89
|
+
hide(): void;
|
|
90
|
+
destroy(): void;
|
|
91
|
+
private get _balloon();
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=elementMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementMenu.d.ts","sourceRoot":"","sources":["../src/elementMenu.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EAGjB,YAAY,EACZ,MAAM,EAEN,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,MAAM,WAAW,CAAC;AAEnB,OAAO,wBAAwB,MAAM,+BAA+B,CAAC;AAGrE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,KAAK,EAAE,CAAC,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,wBAAwB,GAAG,cAAc,GAAG,GAAG,CAAC;AAE9E;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;AAElH;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACtC;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;IAE3B;;OAEG;IACH,aAAa,CAAC,EAAE;QAAE,KAAK,IAAI,IAAI,CAAA;KAAE,CAAC;IAElC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,MAAM;IAC9C;;OAEG;IACI,IAAI,EAAE,aAAa,GAAG,IAAI,CAAQ;IAEzC,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,OAAO,CAA6B;IAE5C,WAAkB,QAAQ,IAAI,SAAS,CAAC,OAAO,iBAAiB,EAAE,OAAO,YAAY,CAAC,CAGrF;IAED,WAAkB,UAAU,IAAI,aAAa,CAE5C;IAEM,IAAI,IAAI,IAAI;IAQnB;;;;;OAKG;IACI,IAAI,CACV,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,iBAAiB,EACzB,EAAE,KAAqC,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,GAAE,sBAA2B,GAC5F,OAAO;IAmBV;;;OAGG;IACI,IAAI,CACV,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,UAAU,EAClB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,GAAE,sBAA2B,GAChE,IAAI;IAqDP;;OAEG;IACI,IAAI,IAAI,IAAI;IAuBH,OAAO,IAAI,IAAI;IAK/B,OAAO,KAAK,QAAQ,GAEnB;CACD"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { ContextualBalloon, DropdownMenuRootListView, KeystrokeHandler, Notification, Plugin, clickOutsideHandler } from 'ckeditor5';
|
|
2
|
+
import ElementMenuView from './elementMenuView.js';
|
|
3
|
+
import ElementOrPositionCommand from './elementOrPositionCommand.js';
|
|
4
|
+
import { isInDocument } from './utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Shows menus in a balloon: the menu of an element, built from commands, submenus and separators (see {@link open}),
|
|
7
|
+
* or any view, e.g. the quick insert search (see {@link show}).
|
|
8
|
+
*
|
|
9
|
+
* The element context menu, the breadcrumbs and quick insert show their menus with it. The menu of an element is
|
|
10
|
+
* CKEditor's dropdown menu (see {@link ElementMenuView}).
|
|
11
|
+
*
|
|
12
|
+
* Executing an item closes the menu. Esc and clicking outside close it too; Esc returns the focus to the editor, or to
|
|
13
|
+
* {@link ElementMenuShowOptions.returnFocusTo}.
|
|
14
|
+
*/
|
|
15
|
+
export default class ElementMenu extends Plugin {
|
|
16
|
+
/**
|
|
17
|
+
* The view in the open menu, if any.
|
|
18
|
+
*/
|
|
19
|
+
view = null;
|
|
20
|
+
_keystrokes = null;
|
|
21
|
+
_isObsolete = null;
|
|
22
|
+
_onHide = null;
|
|
23
|
+
static get requires() {
|
|
24
|
+
// The buttons of the menu report failed commands with notifications.
|
|
25
|
+
return [ContextualBalloon, Notification];
|
|
26
|
+
}
|
|
27
|
+
static get pluginName() {
|
|
28
|
+
return 'ElementMenu';
|
|
29
|
+
}
|
|
30
|
+
init() {
|
|
31
|
+
this.listenTo(this.editor.model.document, 'change:data', () => {
|
|
32
|
+
if (this._isObsolete?.()) {
|
|
33
|
+
this.hide();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Opens the menu of the element next to the target (e.g. a button, or a `DOMRect` in viewport coordinates), with
|
|
39
|
+
* the items from the config callback. The menu closes when the element is removed.
|
|
40
|
+
*
|
|
41
|
+
* Returns `false` when there are no items.
|
|
42
|
+
*/
|
|
43
|
+
open(element, target, config, { label = this.editor.t('Element menu'), isObsolete, ...options } = {}) {
|
|
44
|
+
const items = cleanItems(config(element, this.editor));
|
|
45
|
+
if (!items.length) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const view = new ElementMenuView(this.editor.locale, this.editor.ui.view.body, items, element);
|
|
49
|
+
view.ariaLabel = label;
|
|
50
|
+
this.show(view, target, {
|
|
51
|
+
...options,
|
|
52
|
+
isObsolete: () => !isInDocument(element) || !!isObsolete?.()
|
|
53
|
+
});
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Shows the view (e.g. a menu) next to the target (e.g. a button, or a `DOMRect` in viewport coordinates), and
|
|
58
|
+
* focuses it. The menu closes when the view, or an item of CKEditor's dropdown menu in it, fires `execute`.
|
|
59
|
+
*/
|
|
60
|
+
show(view, target, { isObsolete, returnFocusTo, onHide } = {}) {
|
|
61
|
+
this.hide();
|
|
62
|
+
const close = () => {
|
|
63
|
+
// The item may have opened another menu already (e.g. "Insert before" opens quick insert).
|
|
64
|
+
if (this.view === view) {
|
|
65
|
+
this.hide();
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
this.listenTo(view, 'execute', close);
|
|
69
|
+
this.listenTo(view, 'menu:execute', close);
|
|
70
|
+
clickOutsideHandler({
|
|
71
|
+
emitter: view,
|
|
72
|
+
activator: () => this._balloon.visibleView === view,
|
|
73
|
+
// The open nested menus are in panels outside the balloon.
|
|
74
|
+
contextElements: () => [this._balloon.view.element, ...getOpenPanels(view)].filter(element => !!element),
|
|
75
|
+
callback: () => {
|
|
76
|
+
this.hide();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
this._balloon.add({
|
|
80
|
+
view,
|
|
81
|
+
position: { target },
|
|
82
|
+
withArrow: false,
|
|
83
|
+
singleViewMode: true,
|
|
84
|
+
// Views that other plugins add to the balloon meanwhile (e.g. an element toolbar, when the selection moves
|
|
85
|
+
// into its element) go to their own stack, so they do not cover the menu.
|
|
86
|
+
stackId: 'elementMenu'
|
|
87
|
+
});
|
|
88
|
+
const keystrokes = new KeystrokeHandler();
|
|
89
|
+
// Esc in a nested menu closes only that menu.
|
|
90
|
+
keystrokes.set('Esc', (data, cancel) => {
|
|
91
|
+
this.hide();
|
|
92
|
+
(returnFocusTo ?? this.editor.editing.view).focus();
|
|
93
|
+
cancel();
|
|
94
|
+
});
|
|
95
|
+
if (view.element) {
|
|
96
|
+
keystrokes.listenTo(view.element);
|
|
97
|
+
}
|
|
98
|
+
this.view = view;
|
|
99
|
+
this._keystrokes = keystrokes;
|
|
100
|
+
this._isObsolete = isObsolete ?? null;
|
|
101
|
+
this._onHide = onHide ?? null;
|
|
102
|
+
view.focus();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Hides the open menu, if any.
|
|
106
|
+
*/
|
|
107
|
+
hide() {
|
|
108
|
+
const view = this.view;
|
|
109
|
+
const onHide = this._onHide;
|
|
110
|
+
if (!view) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
this._keystrokes?.destroy();
|
|
114
|
+
this.view = null;
|
|
115
|
+
this._keystrokes = null;
|
|
116
|
+
this._isObsolete = null;
|
|
117
|
+
this._onHide = null;
|
|
118
|
+
this.stopListening(view);
|
|
119
|
+
if (this._balloon.hasView(view)) {
|
|
120
|
+
this._balloon.remove(view);
|
|
121
|
+
}
|
|
122
|
+
view.destroy();
|
|
123
|
+
onHide?.();
|
|
124
|
+
}
|
|
125
|
+
destroy() {
|
|
126
|
+
this.hide();
|
|
127
|
+
super.destroy();
|
|
128
|
+
}
|
|
129
|
+
get _balloon() {
|
|
130
|
+
return this.editor.plugins.get(ContextualBalloon);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Returns the items without missing ones, submenus without items, and separators at the start or the end, or
|
|
135
|
+
* repeated ones.
|
|
136
|
+
*/
|
|
137
|
+
function cleanItems(items) {
|
|
138
|
+
const result = [];
|
|
139
|
+
for (const item of items) {
|
|
140
|
+
const cleaned = item === '|' || item instanceof ElementOrPositionCommand || !item ? item : cleanSubmenu(item);
|
|
141
|
+
if (!cleaned || (cleaned === '|' && (!result.length || result.at(-1) === '|'))) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
result.push(cleaned);
|
|
145
|
+
}
|
|
146
|
+
if (result.at(-1) === '|') {
|
|
147
|
+
result.pop();
|
|
148
|
+
}
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
function cleanSubmenu(submenu) {
|
|
152
|
+
const items = cleanItems(submenu.items);
|
|
153
|
+
return items.length ? { ...submenu, items } : null;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Returns the panels of the open nested menus of CKEditor's dropdown menu.
|
|
157
|
+
*/
|
|
158
|
+
function getOpenPanels(view) {
|
|
159
|
+
return view instanceof DropdownMenuRootListView
|
|
160
|
+
? view.menus.filter(menu => menu.isOpen).map(menu => menu.panelView.element)
|
|
161
|
+
: [];
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=elementMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementMenu.js","sourceRoot":"","sources":["../src/elementMenu.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ListItemButtonView, type Locale } from 'ckeditor5';
|
|
2
|
+
import type ElementOrPositionCommand from './elementOrPositionCommand.js';
|
|
3
|
+
import { type ElementOrPosition } from './elementOrPositionCommand.js';
|
|
4
|
+
/**
|
|
5
|
+
* A menu item for an {@link ElementOrPositionCommand}: CKEditor's list item button, labelled with the command title and
|
|
6
|
+
* icon, that reflects and executes the command like {@link ElementOrPositionButtonView}.
|
|
7
|
+
*
|
|
8
|
+
* For a {@link ElementOrPositionCommand#isToggleable toggleable} command, it is a `menuitemcheckbox` with a check mark
|
|
9
|
+
* when the command is "on"; otherwise, a `menuitem`.
|
|
10
|
+
*/
|
|
11
|
+
export default class ElementMenuItemButtonView extends ListItemButtonView {
|
|
12
|
+
/**
|
|
13
|
+
* The command this item reflects.
|
|
14
|
+
*/
|
|
15
|
+
readonly command: ElementOrPositionCommand;
|
|
16
|
+
/**
|
|
17
|
+
* The position or element the item acts on, if any.
|
|
18
|
+
*/
|
|
19
|
+
readonly target: ElementOrPosition | undefined;
|
|
20
|
+
constructor(locale: Locale | undefined, command: ElementOrPositionCommand, { target }?: {
|
|
21
|
+
target?: ElementOrPosition;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=elementMenuItemButtonView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementMenuItemButtonView.d.ts","sourceRoot":"","sources":["../src/elementMenuItemButtonView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAE5D,OAAO,KAAK,wBAAwB,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,yBAA0B,SAAQ,kBAAkB;IACxE;;OAEG;IACH,SAAgB,OAAO,EAAE,wBAAwB,CAAC;IAElD;;OAEG;IACH,SAAgB,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC;gBAGrD,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,wBAAwB,EACjC,EAAE,MAAM,EAAE,GAAE;QAAE,MAAM,CAAC,EAAE,iBAAiB,CAAA;KAAO;CAYhD"}
|