ckeditor5-elements-toolbar 0.0.0-stage → 49.0.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/LICENSE +21 -0
- package/README.md +30 -2
- package/dist/augmentation.d.ts +13 -0
- package/dist/augmentation.d.ts.map +1 -0
- package/dist/augmentation.js +2 -0
- package/dist/augmentation.js.map +1 -0
- package/dist/elementToolbar.d.ts +54 -0
- package/dist/elementToolbar.d.ts.map +1 -0
- package/dist/elementToolbar.js +71 -0
- package/dist/elementToolbar.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -3
- package/src/augmentation.ts +14 -0
- package/src/elementToolbar.ts +123 -0
- package/src/index.ts +11 -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,31 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ckeditor5-elements-toolbar
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Element toolbars for CKEditor 5 Elements: `ElementToolbar` shows a floating toolbar next to the element with the cursor,
|
|
4
|
+
for the elements that match a model name or a filter function. Its command buttons act on that element.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
This package is part of `ckeditor5-elements`. Install the whole package to use it:
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install ckeditor5 ckeditor5-elements
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { ElementActions, ElementToolbar } from 'ckeditor5-elements';
|
|
18
|
+
|
|
19
|
+
// In the editor config:
|
|
20
|
+
plugins: [..., ElementActions, ElementToolbar],
|
|
21
|
+
elementToolbar: [
|
|
22
|
+
{ element: 'note', items: ['moveElementUp', 'moveElementDown', '|', 'duplicateElement', 'deleteElement'] },
|
|
23
|
+
{ element: element => element.name.startsWith('heading'), items: ['heading', '|', 'deleteElement'] }
|
|
24
|
+
]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
See the [Element toolbar guide](https://ivankild.github.io/ckeditor5-elements/guide/toolbar) of the documentation.
|
|
28
|
+
|
|
29
|
+
## License
|
|
30
|
+
|
|
31
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ElementToolbar, ElementToolbarDefinition } from './index.js';
|
|
2
|
+
declare module 'ckeditor5' {
|
|
3
|
+
interface PluginsMap {
|
|
4
|
+
[ElementToolbar.pluginName]: ElementToolbar;
|
|
5
|
+
}
|
|
6
|
+
interface EditorConfig {
|
|
7
|
+
/**
|
|
8
|
+
* The floating toolbars of elements. See {@link ElementToolbar}.
|
|
9
|
+
*/
|
|
10
|
+
elementToolbar?: ElementToolbarDefinition[];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=augmentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"augmentation.d.ts","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3E,OAAO,QAAQ,WAAW,CAAC;IAC1B,UAAU,UAAU;QACnB,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,cAAc,CAAC;KAC5C;IAED,UAAU,YAAY;QACrB;;WAEG;QACH,cAAc,CAAC,EAAE,wBAAwB,EAAE,CAAC;KAC5C;CACD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"augmentation.js","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Plugin, WidgetToolbarRepository, type ModelElement, type ToolbarConfigItem } from 'ckeditor5';
|
|
2
|
+
import { type ElementMatcher } from 'ckeditor5-elements-core';
|
|
3
|
+
/**
|
|
4
|
+
* A floating toolbar of the elements that match `element`.
|
|
5
|
+
*/
|
|
6
|
+
export interface ElementToolbarDefinition {
|
|
7
|
+
/**
|
|
8
|
+
* The elements the toolbar is for: the name of a model element (e.g. `'note'`), or a function that returns whether it
|
|
9
|
+
* is for the given element (e.g. `element => element.name.startsWith('heading')`).
|
|
10
|
+
*/
|
|
11
|
+
element: ElementMatcher;
|
|
12
|
+
/**
|
|
13
|
+
* The toolbar items, as in CKEditor's toolbar config:
|
|
14
|
+
*
|
|
15
|
+
* * the name of a command that acts on an element (an `ElementOrPositionCommand`, e.g. `'moveElementUp'`): a button
|
|
16
|
+
* that acts on the element of the toolbar,
|
|
17
|
+
* * the name of another UI component (e.g. `'bold'`), as in the main toolbar,
|
|
18
|
+
* * `'|'`, a separator.
|
|
19
|
+
*/
|
|
20
|
+
items: ToolbarConfigItem[];
|
|
21
|
+
/**
|
|
22
|
+
* The accessible name of the toolbar. Defaults to "Element toolbar".
|
|
23
|
+
*/
|
|
24
|
+
ariaLabel?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Shows floating toolbars for elements, configured by the `elementToolbar` editor config option (see
|
|
28
|
+
* {@link ElementToolbarDefinition}): the toolbar of the closest element at the selection that matches, next to it,
|
|
29
|
+
* while the editor is focused. When toolbars match nested elements, the toolbar of the innermost one shows.
|
|
30
|
+
*
|
|
31
|
+
* The toolbars are CKEditor's widget toolbars (`WidgetToolbarRepository`), as the image and table toolbars: they are
|
|
32
|
+
* placed and hidden by CKEditor, and Alt+F10 focuses them. Unlike widget toolbars, they show for any element, also when
|
|
33
|
+
* the selection is inside it, and their command buttons act on that element: "Move up" in the toolbar of a note moves the
|
|
34
|
+
* note, not the paragraph with the cursor.
|
|
35
|
+
*/
|
|
36
|
+
export default class ElementToolbar extends Plugin {
|
|
37
|
+
static get requires(): readonly [typeof WidgetToolbarRepository];
|
|
38
|
+
static get pluginName(): 'ElementToolbar';
|
|
39
|
+
/**
|
|
40
|
+
* Registers the toolbars, after the other plugins registered their commands and components.
|
|
41
|
+
*/
|
|
42
|
+
afterInit(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Returns the closest element at the selection that matches: the selected element, or the innermost element with the
|
|
45
|
+
* selection, or one of their ancestors. Never a root.
|
|
46
|
+
*/
|
|
47
|
+
getElement(element: ElementToolbarDefinition['element']): ModelElement | null;
|
|
48
|
+
/**
|
|
49
|
+
* Returns the toolbar item for the config item: for a command that acts on an element, the name of a new component,
|
|
50
|
+
* a button that acts on the element of the toolbar; otherwise, the config item itself.
|
|
51
|
+
*/
|
|
52
|
+
private _createItem;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=elementToolbar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementToolbar.d.ts","sourceRoot":"","sources":["../src/elementToolbar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACvG,OAAO,EAMN,KAAK,cAAc,EACnB,MAAM,yBAAyB,CAAC;AAEjC;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC;;;OAGG;IACH,OAAO,EAAE,cAAc,CAAC;IAExB;;;;;;;OAOG;IACH,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAE3B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,MAAM;IACjD,WAAkB,QAAQ,IAAI,SAAS,CAAC,OAAO,uBAAuB,CAAC,CAEtE;IAED,WAAkB,UAAU,IAAI,gBAAgB,CAE/C;IAED;;OAEG;IACI,SAAS,IAAI,IAAI;IAoBxB;;;OAGG;IACI,UAAU,CAAC,OAAO,EAAE,wBAAwB,CAAC,SAAS,CAAC,GAAG,YAAY,GAAG,IAAI;IAUpF;;;OAGG;IACH,OAAO,CAAC,WAAW;CA0BnB"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Plugin, WidgetToolbarRepository } from 'ckeditor5';
|
|
2
|
+
import { ElementOrPositionButtonView, ElementOrPositionCommand, getAncestorElements, getElementAtSelection, matchesElement } from 'ckeditor5-elements-core';
|
|
3
|
+
/**
|
|
4
|
+
* Shows floating toolbars for elements, configured by the `elementToolbar` editor config option (see
|
|
5
|
+
* {@link ElementToolbarDefinition}): the toolbar of the closest element at the selection that matches, next to it,
|
|
6
|
+
* while the editor is focused. When toolbars match nested elements, the toolbar of the innermost one shows.
|
|
7
|
+
*
|
|
8
|
+
* The toolbars are CKEditor's widget toolbars (`WidgetToolbarRepository`), as the image and table toolbars: they are
|
|
9
|
+
* placed and hidden by CKEditor, and Alt+F10 focuses them. Unlike widget toolbars, they show for any element, also when
|
|
10
|
+
* the selection is inside it, and their command buttons act on that element: "Move up" in the toolbar of a note moves the
|
|
11
|
+
* note, not the paragraph with the cursor.
|
|
12
|
+
*/
|
|
13
|
+
export default class ElementToolbar extends Plugin {
|
|
14
|
+
static get requires() {
|
|
15
|
+
return [WidgetToolbarRepository];
|
|
16
|
+
}
|
|
17
|
+
static get pluginName() {
|
|
18
|
+
return 'ElementToolbar';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Registers the toolbars, after the other plugins registered their commands and components.
|
|
22
|
+
*/
|
|
23
|
+
afterInit() {
|
|
24
|
+
const editor = this.editor;
|
|
25
|
+
const repository = editor.plugins.get(WidgetToolbarRepository);
|
|
26
|
+
(editor.config.get('elementToolbar') ?? []).forEach((definition, index) => {
|
|
27
|
+
const toolbarId = `elementToolbar${String(index)}`;
|
|
28
|
+
const getElement = () => this.getElement(definition.element);
|
|
29
|
+
repository.register(toolbarId, {
|
|
30
|
+
ariaLabel: definition.ariaLabel ?? editor.t('Element toolbar'),
|
|
31
|
+
items: definition.items.map(item => this._createItem(item, toolbarId, getElement)),
|
|
32
|
+
getRelatedElement: () => {
|
|
33
|
+
const element = getElement();
|
|
34
|
+
return (element && editor.editing.mapper.toViewElement(element)) ?? null;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns the closest element at the selection that matches: the selected element, or the innermost element with the
|
|
41
|
+
* selection, or one of their ancestors. Never a root.
|
|
42
|
+
*/
|
|
43
|
+
getElement(element) {
|
|
44
|
+
for (const node of getAncestorElements(getElementAtSelection(this.editor.model.document.selection))) {
|
|
45
|
+
if (matchesElement(element, node)) {
|
|
46
|
+
return node;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns the toolbar item for the config item: for a command that acts on an element, the name of a new component,
|
|
53
|
+
* a button that acts on the element of the toolbar; otherwise, the config item itself.
|
|
54
|
+
*/
|
|
55
|
+
_createItem(item, toolbarId, getElement) {
|
|
56
|
+
if (typeof item !== 'string') {
|
|
57
|
+
return { ...item, items: item.items.map(child => this._createItem(child, toolbarId, getElement)) };
|
|
58
|
+
}
|
|
59
|
+
const editor = this.editor;
|
|
60
|
+
const command = editor.commands.get(item);
|
|
61
|
+
const name = `${toolbarId}:${item}`;
|
|
62
|
+
if (!(command instanceof ElementOrPositionCommand)) {
|
|
63
|
+
return item;
|
|
64
|
+
}
|
|
65
|
+
if (!editor.ui.componentFactory.has(name)) {
|
|
66
|
+
editor.ui.componentFactory.add(name, locale => new ElementOrPositionButtonView(locale, command, { target: getElement }));
|
|
67
|
+
}
|
|
68
|
+
return name;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=elementToolbar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementToolbar.js","sourceRoot":"","sources":["../src/elementToolbar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAA6C,MAAM,WAAW,CAAC;AACvG,OAAO,EACN,2BAA2B,EAC3B,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,EAEd,MAAM,yBAAyB,CAAC;AA4BjC;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,MAAM;IAC1C,MAAM,KAAK,QAAQ;QACzB,OAAO,CAAC,uBAAuB,CAAU,CAAC;IAC3C,CAAC;IAEM,MAAM,KAAK,UAAU;QAC3B,OAAO,gBAAyB,CAAC;IAClC,CAAC;IAED;;OAEG;IACI,SAAS;QACf,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QAE/D,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE;YACzE,MAAM,SAAS,GAAG,iBAAiB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACnD,MAAM,UAAU,GAAG,GAAwB,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAElF,UAAU,CAAC,QAAQ,CAAC,SAAS,EAAE;gBAC9B,SAAS,EAAE,UAAU,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC;gBAC9D,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;gBAClF,iBAAiB,EAAE,GAAG,EAAE;oBACvB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;oBAE7B,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC;gBAC1E,CAAC;aACD,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACI,UAAU,CAAC,OAA4C;QAC7D,KAAK,MAAM,IAAI,IAAI,mBAAmB,CAAC,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YACrG,IAAI,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;gBACnC,OAAO,IAAI,CAAC;YACb,CAAC;QACF,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;OAGG;IACK,WAAW,CAClB,IAAuB,EACvB,SAAiB,EACjB,UAAqC;QAErC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;QACpG,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,GAAG,SAAS,IAAI,IAAI,EAAE,CAAC;QAEpC,IAAI,CAAC,CAAC,OAAO,YAAY,wBAAwB,CAAC,EAAE,CAAC;YACpD,OAAO,IAAI,CAAC;QACb,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAC7B,IAAI,EACJ,MAAM,CAAC,EAAE,CAAC,IAAI,2BAA2B,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAClF,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;CACD"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element toolbars: floating toolbars for the elements that match a name or a filter, with buttons that act on the
|
|
3
|
+
* element.
|
|
4
|
+
*
|
|
5
|
+
* @module ckeditor5-elements-toolbar
|
|
6
|
+
*/
|
|
7
|
+
export { default as ElementToolbar, type ElementToolbarDefinition } from './elementToolbar.js';
|
|
8
|
+
import './augmentation.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/F,OAAO,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element toolbars: floating toolbars for the elements that match a name or a filter, with buttons that act on the
|
|
3
|
+
* element.
|
|
4
|
+
*
|
|
5
|
+
* @module ckeditor5-elements-toolbar
|
|
6
|
+
*/
|
|
7
|
+
// Plugins.
|
|
8
|
+
export { default as ElementToolbar } from './elementToolbar.js';
|
|
9
|
+
import './augmentation.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,WAAW;AACX,OAAO,EAAE,OAAO,IAAI,cAAc,EAAiC,MAAM,qBAAqB,CAAC;AAE/F,OAAO,mBAAmB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ckeditor5-elements-toolbar",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "49.0.0",
|
|
4
|
+
"description": "Element toolbars for CKEditor 5: floating toolbars for the elements that match a name or a filter, with buttons that act on the element.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ckeditor",
|
|
7
|
+
"ckeditor5",
|
|
8
|
+
"ckeditor 5",
|
|
9
|
+
"ckeditor5-feature",
|
|
10
|
+
"ckeditor5-plugin"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Ivan Kildyushov",
|
|
14
|
+
"homepage": "https://ivankild.github.io/ckeditor5-elements/guide/toolbar",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/ivankild/ckeditor5-elements.git",
|
|
18
|
+
"directory": "packages/ckeditor5-elements-toolbar"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/ivankild/ckeditor5-elements/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"src",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"sideEffects": false,
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"ckeditor5-elements-core": "49.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"ckeditor5-elements-actions": "49.0.0",
|
|
44
|
+
"ckeditor5-elements-menu": "49.0.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"ckeditor5": "^49.0.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc --build"
|
|
51
|
+
}
|
|
6
52
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ElementToolbar, ElementToolbarDefinition } from './index.js';
|
|
2
|
+
|
|
3
|
+
declare module 'ckeditor5' {
|
|
4
|
+
interface PluginsMap {
|
|
5
|
+
[ElementToolbar.pluginName]: ElementToolbar;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface EditorConfig {
|
|
9
|
+
/**
|
|
10
|
+
* The floating toolbars of elements. See {@link ElementToolbar}.
|
|
11
|
+
*/
|
|
12
|
+
elementToolbar?: ElementToolbarDefinition[];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Plugin, WidgetToolbarRepository, type ModelElement, type ToolbarConfigItem } from 'ckeditor5';
|
|
2
|
+
import {
|
|
3
|
+
ElementOrPositionButtonView,
|
|
4
|
+
ElementOrPositionCommand,
|
|
5
|
+
getAncestorElements,
|
|
6
|
+
getElementAtSelection,
|
|
7
|
+
matchesElement,
|
|
8
|
+
type ElementMatcher
|
|
9
|
+
} from 'ckeditor5-elements-core';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A floating toolbar of the elements that match `element`.
|
|
13
|
+
*/
|
|
14
|
+
export interface ElementToolbarDefinition {
|
|
15
|
+
/**
|
|
16
|
+
* The elements the toolbar is for: the name of a model element (e.g. `'note'`), or a function that returns whether it
|
|
17
|
+
* is for the given element (e.g. `element => element.name.startsWith('heading')`).
|
|
18
|
+
*/
|
|
19
|
+
element: ElementMatcher;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The toolbar items, as in CKEditor's toolbar config:
|
|
23
|
+
*
|
|
24
|
+
* * the name of a command that acts on an element (an `ElementOrPositionCommand`, e.g. `'moveElementUp'`): a button
|
|
25
|
+
* that acts on the element of the toolbar,
|
|
26
|
+
* * the name of another UI component (e.g. `'bold'`), as in the main toolbar,
|
|
27
|
+
* * `'|'`, a separator.
|
|
28
|
+
*/
|
|
29
|
+
items: ToolbarConfigItem[];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The accessible name of the toolbar. Defaults to "Element toolbar".
|
|
33
|
+
*/
|
|
34
|
+
ariaLabel?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Shows floating toolbars for elements, configured by the `elementToolbar` editor config option (see
|
|
39
|
+
* {@link ElementToolbarDefinition}): the toolbar of the closest element at the selection that matches, next to it,
|
|
40
|
+
* while the editor is focused. When toolbars match nested elements, the toolbar of the innermost one shows.
|
|
41
|
+
*
|
|
42
|
+
* The toolbars are CKEditor's widget toolbars (`WidgetToolbarRepository`), as the image and table toolbars: they are
|
|
43
|
+
* placed and hidden by CKEditor, and Alt+F10 focuses them. Unlike widget toolbars, they show for any element, also when
|
|
44
|
+
* the selection is inside it, and their command buttons act on that element: "Move up" in the toolbar of a note moves the
|
|
45
|
+
* note, not the paragraph with the cursor.
|
|
46
|
+
*/
|
|
47
|
+
export default class ElementToolbar extends Plugin {
|
|
48
|
+
public static get requires(): readonly [typeof WidgetToolbarRepository] {
|
|
49
|
+
return [WidgetToolbarRepository] as const;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public static get pluginName(): 'ElementToolbar' {
|
|
53
|
+
return 'ElementToolbar' as const;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Registers the toolbars, after the other plugins registered their commands and components.
|
|
58
|
+
*/
|
|
59
|
+
public afterInit(): void {
|
|
60
|
+
const editor = this.editor;
|
|
61
|
+
const repository = editor.plugins.get(WidgetToolbarRepository);
|
|
62
|
+
|
|
63
|
+
(editor.config.get('elementToolbar') ?? []).forEach((definition, index) => {
|
|
64
|
+
const toolbarId = `elementToolbar${String(index)}`;
|
|
65
|
+
const getElement = (): ModelElement | null => this.getElement(definition.element);
|
|
66
|
+
|
|
67
|
+
repository.register(toolbarId, {
|
|
68
|
+
ariaLabel: definition.ariaLabel ?? editor.t('Element toolbar'),
|
|
69
|
+
items: definition.items.map(item => this._createItem(item, toolbarId, getElement)),
|
|
70
|
+
getRelatedElement: () => {
|
|
71
|
+
const element = getElement();
|
|
72
|
+
|
|
73
|
+
return (element && editor.editing.mapper.toViewElement(element)) ?? null;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Returns the closest element at the selection that matches: the selected element, or the innermost element with the
|
|
81
|
+
* selection, or one of their ancestors. Never a root.
|
|
82
|
+
*/
|
|
83
|
+
public getElement(element: ElementToolbarDefinition['element']): ModelElement | null {
|
|
84
|
+
for (const node of getAncestorElements(getElementAtSelection(this.editor.model.document.selection))) {
|
|
85
|
+
if (matchesElement(element, node)) {
|
|
86
|
+
return node;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Returns the toolbar item for the config item: for a command that acts on an element, the name of a new component,
|
|
95
|
+
* a button that acts on the element of the toolbar; otherwise, the config item itself.
|
|
96
|
+
*/
|
|
97
|
+
private _createItem(
|
|
98
|
+
item: ToolbarConfigItem,
|
|
99
|
+
toolbarId: string,
|
|
100
|
+
getElement: () => ModelElement | null
|
|
101
|
+
): ToolbarConfigItem {
|
|
102
|
+
if (typeof item !== 'string') {
|
|
103
|
+
return { ...item, items: item.items.map(child => this._createItem(child, toolbarId, getElement)) };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const editor = this.editor;
|
|
107
|
+
const command = editor.commands.get(item);
|
|
108
|
+
const name = `${toolbarId}:${item}`;
|
|
109
|
+
|
|
110
|
+
if (!(command instanceof ElementOrPositionCommand)) {
|
|
111
|
+
return item;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!editor.ui.componentFactory.has(name)) {
|
|
115
|
+
editor.ui.componentFactory.add(
|
|
116
|
+
name,
|
|
117
|
+
locale => new ElementOrPositionButtonView(locale, command, { target: getElement })
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return name;
|
|
122
|
+
}
|
|
123
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element toolbars: floating toolbars for the elements that match a name or a filter, with buttons that act on the
|
|
3
|
+
* element.
|
|
4
|
+
*
|
|
5
|
+
* @module ckeditor5-elements-toolbar
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Plugins.
|
|
9
|
+
export { default as ElementToolbar, type ElementToolbarDefinition } from './elementToolbar.js';
|
|
10
|
+
|
|
11
|
+
import './augmentation.js';
|