ckeditor5-elements-menu 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +50 -2
- package/dist/augmentation.d.ts +30 -0
- package/dist/augmentation.d.ts.map +1 -0
- package/dist/augmentation.js +2 -0
- package/dist/augmentation.js.map +1 -0
- package/dist/breadcrumbs/breadcrumbItemView.d.ts +47 -0
- package/dist/breadcrumbs/breadcrumbItemView.d.ts.map +1 -0
- package/dist/breadcrumbs/breadcrumbItemView.js +30 -0
- package/dist/breadcrumbs/breadcrumbItemView.js.map +1 -0
- package/dist/breadcrumbs/breadcrumbLabelView.d.ts +29 -0
- package/dist/breadcrumbs/breadcrumbLabelView.d.ts.map +1 -0
- package/dist/breadcrumbs/breadcrumbLabelView.js +39 -0
- package/dist/breadcrumbs/breadcrumbLabelView.js.map +1 -0
- package/dist/breadcrumbs/breadcrumbs.d.ts +72 -0
- package/dist/breadcrumbs/breadcrumbs.d.ts.map +1 -0
- package/dist/breadcrumbs/breadcrumbs.js +224 -0
- package/dist/breadcrumbs/breadcrumbs.js.map +1 -0
- package/dist/breadcrumbs/breadcrumbsView.d.ts +68 -0
- package/dist/breadcrumbs/breadcrumbsView.d.ts.map +1 -0
- package/dist/breadcrumbs/breadcrumbsView.js +134 -0
- package/dist/breadcrumbs/breadcrumbsView.js.map +1 -0
- package/dist/contextMenu/contextMenuObserver.d.ts +19 -0
- package/dist/contextMenu/contextMenuObserver.d.ts.map +1 -0
- package/dist/contextMenu/contextMenuObserver.js +18 -0
- package/dist/contextMenu/contextMenuObserver.js.map +1 -0
- package/dist/contextMenu/elementContextMenu.d.ts +34 -0
- package/dist/contextMenu/elementContextMenu.d.ts.map +1 -0
- package/dist/contextMenu/elementContextMenu.js +98 -0
- package/dist/contextMenu/elementContextMenu.js.map +1 -0
- package/dist/elementMenu.d.ts +93 -0
- package/dist/elementMenu.d.ts.map +1 -0
- package/dist/elementMenu.js +162 -0
- package/dist/elementMenu.js.map +1 -0
- package/dist/elementMenuView.d.ts +35 -0
- package/dist/elementMenuView.d.ts.map +1 -0
- package/dist/elementMenuView.js +117 -0
- package/dist/elementMenuView.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/quickInsert/icons.d.ts +16 -0
- package/dist/quickInsert/icons.d.ts.map +1 -0
- package/dist/quickInsert/icons.js +21 -0
- package/dist/quickInsert/icons.js.map +1 -0
- package/dist/quickInsert/quickInsert.d.ts +57 -0
- package/dist/quickInsert/quickInsert.d.ts.map +1 -0
- package/dist/quickInsert/quickInsert.js +106 -0
- package/dist/quickInsert/quickInsert.js.map +1 -0
- package/dist/quickInsert/quickInsertCommand.d.ts +38 -0
- package/dist/quickInsert/quickInsertCommand.d.ts.map +1 -0
- package/dist/quickInsert/quickInsertCommand.js +70 -0
- package/dist/quickInsert/quickInsertCommand.js.map +1 -0
- package/dist/quickInsert/quickInsertListView.d.ts +29 -0
- package/dist/quickInsert/quickInsertListView.d.ts.map +1 -0
- package/dist/quickInsert/quickInsertListView.js +44 -0
- package/dist/quickInsert/quickInsertListView.js.map +1 -0
- package/dist/quickInsert/quickInsertPositionCommand.d.ts +28 -0
- package/dist/quickInsert/quickInsertPositionCommand.d.ts.map +1 -0
- package/dist/quickInsert/quickInsertPositionCommand.js +54 -0
- package/dist/quickInsert/quickInsertPositionCommand.js.map +1 -0
- package/dist/quickInsert/quickInsertView.d.ts +20 -0
- package/dist/quickInsert/quickInsertView.d.ts.map +1 -0
- package/dist/quickInsert/quickInsertView.js +52 -0
- package/dist/quickInsert/quickInsertView.js.map +1 -0
- package/package.json +49 -3
- package/src/augmentation.ts +44 -0
- package/src/breadcrumbs/breadcrumbItemView.ts +74 -0
- package/src/breadcrumbs/breadcrumbLabelView.ts +55 -0
- package/src/breadcrumbs/breadcrumbs.ts +322 -0
- package/src/breadcrumbs/breadcrumbsView.ts +176 -0
- package/src/contextMenu/contextMenuObserver.ts +27 -0
- package/src/contextMenu/elementContextMenu.ts +116 -0
- package/src/elementMenu.ts +274 -0
- package/src/elementMenuView.ts +163 -0
- package/src/index.ts +54 -0
- package/src/quickInsert/icons.ts +26 -0
- package/src/quickInsert/quickInsert.ts +149 -0
- package/src/quickInsert/quickInsertCommand.ts +85 -0
- package/src/quickInsert/quickInsertListView.ts +71 -0
- package/src/quickInsert/quickInsertPositionCommand.ts +72 -0
- package/src/quickInsert/quickInsertView.ts +64 -0
- package/theme/menu.css +131 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { KeystrokeHandler, View, ViewCollection, type Locale } from 'ckeditor5';
|
|
2
|
+
import BreadcrumbItemView, { type BreadcrumbItem } from './breadcrumbItemView.js';
|
|
3
|
+
import BreadcrumbLabelView from './breadcrumbLabelView.js';
|
|
4
|
+
/**
|
|
5
|
+
* The options of {@link BreadcrumbsView}.
|
|
6
|
+
*/
|
|
7
|
+
export interface BreadcrumbsViewOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Whether the items are buttons that can be clicked, or labels. Defaults to `false`.
|
|
10
|
+
*/
|
|
11
|
+
isClickable?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Shows a path of elements, from the outermost to the innermost one, as a list (`<ol>`) in a `<nav>` landmark: buttons
|
|
15
|
+
* when the items can be clicked, labels otherwise. Items without a model element (list items) are labels too. The
|
|
16
|
+
* innermost item is marked with `aria-current="location"`.
|
|
17
|
+
*
|
|
18
|
+
* The left and right arrows move the focus between the items; past the first or the last one, they are left to the
|
|
19
|
+
* toolbar the view is in. Fires `execute` with the item when one of the buttons is clicked.
|
|
20
|
+
*/
|
|
21
|
+
export default class BreadcrumbsView extends View {
|
|
22
|
+
/**
|
|
23
|
+
* The breadcrumb items: the buttons or labels, from the outermost to the innermost one.
|
|
24
|
+
*/
|
|
25
|
+
readonly items: ViewCollection<BreadcrumbItemView | BreadcrumbLabelView>;
|
|
26
|
+
/**
|
|
27
|
+
* Whether the items are buttons that can be clicked, or labels.
|
|
28
|
+
*/
|
|
29
|
+
readonly isClickable: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Handles the keystrokes within the breadcrumbs.
|
|
32
|
+
*/
|
|
33
|
+
readonly keystrokes: KeystrokeHandler;
|
|
34
|
+
/**
|
|
35
|
+
* The list items (`<li>`) that hold the {@link items}.
|
|
36
|
+
*/
|
|
37
|
+
private readonly _listItems;
|
|
38
|
+
constructor(locale?: Locale, { isClickable }?: BreadcrumbsViewOptions);
|
|
39
|
+
/**
|
|
40
|
+
* Replaces the items.
|
|
41
|
+
*/
|
|
42
|
+
setItems(items: readonly BreadcrumbItem[]): void;
|
|
43
|
+
/**
|
|
44
|
+
* Renders the view and starts handling the keystrokes.
|
|
45
|
+
*/
|
|
46
|
+
render(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Focuses an item: the first one when the focus moves forward (`1`, e.g. from the previous toolbar item), the
|
|
49
|
+
* innermost one otherwise.
|
|
50
|
+
*/
|
|
51
|
+
focus(direction?: 1 | -1): void;
|
|
52
|
+
/**
|
|
53
|
+
* Destroys the view and its keystroke handler.
|
|
54
|
+
*/
|
|
55
|
+
destroy(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Focuses the item before (`-1`) or after (`1`) the focused one. Returns `false` when there is none.
|
|
58
|
+
*/
|
|
59
|
+
private _focusSibling;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Fired by {@link BreadcrumbsView} when one of its buttons is clicked.
|
|
63
|
+
*/
|
|
64
|
+
export interface BreadcrumbsViewExecuteEvent {
|
|
65
|
+
name: 'execute';
|
|
66
|
+
args: [item: BreadcrumbItemView];
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=breadcrumbsView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"breadcrumbsView.d.ts","sourceRoot":"","sources":["../../src/breadcrumbs/breadcrumbsView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,kBAAkB,EAAE,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,mBAAmB,MAAM,0BAA0B,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACtC;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,IAAI;IAChD;;OAEG;IACH,SAAgB,KAAK,2DAAkE;IAEvF;;OAEG;IACH,SAAgB,WAAW,EAAE,OAAO,CAAC;IAErC;;OAEG;IACH,SAAgB,UAAU,mBAA0B;IAEpD;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;gBAEzD,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,WAAmB,EAAE,GAAE,sBAA2B;IAsCjF;;OAEG;IACI,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI;IA0BvD;;OAEG;IACa,MAAM,IAAI,IAAI;IAQ9B;;;OAGG;IACI,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI;IAItC;;OAEG;IACa,OAAO,IAAI,IAAI;IAK/B;;OAEG;IACH,OAAO,CAAC,aAAa;CASrB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC3C,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;CACjC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { KeystrokeHandler, View, ViewCollection } from 'ckeditor5';
|
|
2
|
+
import BreadcrumbItemView, {} from './breadcrumbItemView.js';
|
|
3
|
+
import BreadcrumbLabelView from './breadcrumbLabelView.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shows a path of elements, from the outermost to the innermost one, as a list (`<ol>`) in a `<nav>` landmark: buttons
|
|
6
|
+
* when the items can be clicked, labels otherwise. Items without a model element (list items) are labels too. The
|
|
7
|
+
* innermost item is marked with `aria-current="location"`.
|
|
8
|
+
*
|
|
9
|
+
* The left and right arrows move the focus between the items; past the first or the last one, they are left to the
|
|
10
|
+
* toolbar the view is in. Fires `execute` with the item when one of the buttons is clicked.
|
|
11
|
+
*/
|
|
12
|
+
export default class BreadcrumbsView extends View {
|
|
13
|
+
/**
|
|
14
|
+
* The breadcrumb items: the buttons or labels, from the outermost to the innermost one.
|
|
15
|
+
*/
|
|
16
|
+
items = new ViewCollection();
|
|
17
|
+
/**
|
|
18
|
+
* Whether the items are buttons that can be clicked, or labels.
|
|
19
|
+
*/
|
|
20
|
+
isClickable;
|
|
21
|
+
/**
|
|
22
|
+
* Handles the keystrokes within the breadcrumbs.
|
|
23
|
+
*/
|
|
24
|
+
keystrokes = new KeystrokeHandler();
|
|
25
|
+
/**
|
|
26
|
+
* The list items (`<li>`) that hold the {@link items}.
|
|
27
|
+
*/
|
|
28
|
+
_listItems;
|
|
29
|
+
constructor(locale, { isClickable = false } = {}) {
|
|
30
|
+
super(locale);
|
|
31
|
+
this._listItems = this.createCollection();
|
|
32
|
+
this.isClickable = isClickable;
|
|
33
|
+
const isRtl = locale?.uiLanguageDirection === 'rtl';
|
|
34
|
+
this.keystrokes.set(isRtl ? 'arrowright' : 'arrowleft', (data, cancel) => {
|
|
35
|
+
if (this._focusSibling(-1)) {
|
|
36
|
+
cancel();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
this.keystrokes.set(isRtl ? 'arrowleft' : 'arrowright', (data, cancel) => {
|
|
40
|
+
if (this._focusSibling(1)) {
|
|
41
|
+
cancel();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
this.setTemplate({
|
|
45
|
+
tag: 'nav',
|
|
46
|
+
attributes: {
|
|
47
|
+
class: ['ck', 'ck-elements-breadcrumbs'],
|
|
48
|
+
'aria-label': locale?.t('Element path') ?? 'Element path'
|
|
49
|
+
},
|
|
50
|
+
children: [
|
|
51
|
+
{
|
|
52
|
+
tag: 'ol',
|
|
53
|
+
attributes: {
|
|
54
|
+
class: ['ck', 'ck-elements-breadcrumbs__list']
|
|
55
|
+
},
|
|
56
|
+
children: this._listItems
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Replaces the items.
|
|
63
|
+
*/
|
|
64
|
+
setItems(items) {
|
|
65
|
+
for (const listItem of this._listItems) {
|
|
66
|
+
listItem.destroy();
|
|
67
|
+
}
|
|
68
|
+
this._listItems.clear();
|
|
69
|
+
this.items.clear();
|
|
70
|
+
items.forEach((item, index) => {
|
|
71
|
+
const isCurrent = index === items.length - 1;
|
|
72
|
+
const view = this.isClickable && item.modelElement
|
|
73
|
+
? new BreadcrumbItemView(this.locale, item, { isCurrent })
|
|
74
|
+
: new BreadcrumbLabelView(this.locale, item, { isCurrent });
|
|
75
|
+
if (view instanceof BreadcrumbItemView) {
|
|
76
|
+
view.on('execute', () => {
|
|
77
|
+
this.fire('execute', view);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
this.items.add(view);
|
|
81
|
+
this._listItems.add(new BreadcrumbsListItemView(this.locale, view));
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Renders the view and starts handling the keystrokes.
|
|
86
|
+
*/
|
|
87
|
+
render() {
|
|
88
|
+
super.render();
|
|
89
|
+
if (this.element) {
|
|
90
|
+
this.keystrokes.listenTo(this.element);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Focuses an item: the first one when the focus moves forward (`1`, e.g. from the previous toolbar item), the
|
|
95
|
+
* innermost one otherwise.
|
|
96
|
+
*/
|
|
97
|
+
focus(direction) {
|
|
98
|
+
(direction === 1 ? this.items.first : this.items.last)?.focus();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Destroys the view and its keystroke handler.
|
|
102
|
+
*/
|
|
103
|
+
destroy() {
|
|
104
|
+
super.destroy();
|
|
105
|
+
this.keystrokes.destroy();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Focuses the item before (`-1`) or after (`1`) the focused one. Returns `false` when there is none.
|
|
109
|
+
*/
|
|
110
|
+
_focusSibling(step) {
|
|
111
|
+
const items = Array.from(this.items);
|
|
112
|
+
const index = items.findIndex(item => item.element === document.activeElement);
|
|
113
|
+
const sibling = index === -1 ? undefined : items[index + step];
|
|
114
|
+
sibling?.focus();
|
|
115
|
+
return !!sibling;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* A list item (`<li>`) of the breadcrumbs, holding a button or a label.
|
|
120
|
+
*/
|
|
121
|
+
class BreadcrumbsListItemView extends View {
|
|
122
|
+
constructor(locale, item) {
|
|
123
|
+
super(locale);
|
|
124
|
+
// Rendering registers the item as a child, so it is destroyed with the list item.
|
|
125
|
+
this.setTemplate({
|
|
126
|
+
tag: 'li',
|
|
127
|
+
attributes: {
|
|
128
|
+
class: ['ck', 'ck-elements-breadcrumbs__list-item']
|
|
129
|
+
},
|
|
130
|
+
children: [item]
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=breadcrumbsView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"breadcrumbsView.js","sourceRoot":"","sources":["../../src/breadcrumbs/breadcrumbsView.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DomEventObserver, type ViewDocumentDomEventData } from 'ckeditor5';
|
|
2
|
+
/**
|
|
3
|
+
* Fires the `contextmenu` event on the view document when the user opens the native context menu in an editable.
|
|
4
|
+
*/
|
|
5
|
+
export default class ContextMenuObserver extends DomEventObserver<'contextmenu'> {
|
|
6
|
+
get domEventType(): 'contextmenu';
|
|
7
|
+
/**
|
|
8
|
+
* Fires `contextmenu` on the view document for the DOM event.
|
|
9
|
+
*/
|
|
10
|
+
onDomEvent(domEvent: MouseEvent): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Fired on the view document by the {@link ContextMenuObserver}.
|
|
14
|
+
*/
|
|
15
|
+
export interface ViewDocumentContextMenuEvent {
|
|
16
|
+
name: 'contextmenu';
|
|
17
|
+
args: [data: ViewDocumentDomEventData<MouseEvent>];
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=contextMenuObserver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextMenuObserver.d.ts","sourceRoot":"","sources":["../../src/contextMenu/contextMenuObserver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAE5E;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,gBAAgB,CAAC,aAAa,CAAC;IAG/E,IAAW,YAAY,IAAI,aAAa,CAEvC;IAED;;OAEG;IACI,UAAU,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI;CAG7C;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,CAAC,IAAI,EAAE,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAAC;CACnD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { DomEventObserver } from 'ckeditor5';
|
|
2
|
+
/**
|
|
3
|
+
* Fires the `contextmenu` event on the view document when the user opens the native context menu in an editable.
|
|
4
|
+
*/
|
|
5
|
+
export default class ContextMenuObserver extends DomEventObserver {
|
|
6
|
+
// The base class declares it as an accessor, so it cannot be a field here.
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/class-literal-property-style
|
|
8
|
+
get domEventType() {
|
|
9
|
+
return 'contextmenu';
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Fires `contextmenu` on the view document for the DOM event.
|
|
13
|
+
*/
|
|
14
|
+
onDomEvent(domEvent) {
|
|
15
|
+
this.fire(domEvent.type, domEvent);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=contextMenuObserver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextMenuObserver.js","sourceRoot":"","sources":["../../src/contextMenu/contextMenuObserver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAiC,MAAM,WAAW,CAAC;AAE5E;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,gBAA+B;IAC/E,2EAA2E;IAC3E,2EAA2E;IAC3E,IAAW,YAAY;QACtB,OAAO,aAAa,CAAC;IACtB,CAAC;IAED;;OAEG;IACI,UAAU,CAAC,QAAoB;QACrC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;CACD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Plugin, type ModelElement, type RectSource, type ViewElement } from 'ckeditor5';
|
|
2
|
+
import ElementMenu from '../elementMenu.js';
|
|
3
|
+
/**
|
|
4
|
+
* Shows the menu of an element when the user right-clicks it, or opens the context menu with the keyboard (the menu
|
|
5
|
+
* key, or Shift+F10) while the cursor is in it.
|
|
6
|
+
*
|
|
7
|
+
* The items come from the `elementContextMenu` editor config option only (see `ElementMenuConfig`): commands, submenus
|
|
8
|
+
* and `'|'` separators. Without it, or when it returns no items for an
|
|
9
|
+
* element, the native context menu shows. The menu itself is shown by the `ElementMenu` plugin.
|
|
10
|
+
*/
|
|
11
|
+
export default class ElementContextMenu extends Plugin {
|
|
12
|
+
static get requires(): readonly [typeof ElementMenu];
|
|
13
|
+
static get pluginName(): 'ElementContextMenu';
|
|
14
|
+
init(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Opens the menu of the element next to the target (e.g. a button, or a `DOMRect` in viewport coordinates), with
|
|
17
|
+
* the items from the `elementContextMenu` config option.
|
|
18
|
+
*
|
|
19
|
+
* Returns `false` when there are no items, so the caller can let the native context menu show instead.
|
|
20
|
+
*/
|
|
21
|
+
open(element: ModelElement, target: RectSource): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Opens the menu of the element at the selection (the selected element, or the element with the cursor), next to
|
|
24
|
+
* the selection.
|
|
25
|
+
*
|
|
26
|
+
* Returns `false` when there is no such element or no items.
|
|
27
|
+
*/
|
|
28
|
+
openAtSelection(): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Returns the closest model element mapped to the given view element or its ancestors, skipping the root.
|
|
31
|
+
*/
|
|
32
|
+
getModelElement(viewElement: ViewElement): ModelElement | null;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=elementContextMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementContextMenu.d.ts","sourceRoot":"","sources":["../../src/contextMenu/elementContextMenu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAEzF,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAG5C;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,MAAM;IACrD,WAAkB,QAAQ,IAAI,SAAS,CAAC,OAAO,WAAW,CAAC,CAE1D;IAED,WAAkB,UAAU,IAAI,oBAAoB,CAEnD;IAEM,IAAI,IAAI,IAAI;IAmCnB;;;;;OAKG;IACI,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO;IAY/D;;;;;OAKG;IACI,eAAe,IAAI,OAAO;IAkBjC;;OAEG;IACI,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,YAAY,GAAG,IAAI;CAarE"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Plugin } from 'ckeditor5';
|
|
2
|
+
import { getElementAtSelection, isInDocument } from 'ckeditor5-elements-core';
|
|
3
|
+
import ElementMenu from '../elementMenu.js';
|
|
4
|
+
import ContextMenuObserver, {} from './contextMenuObserver.js';
|
|
5
|
+
/**
|
|
6
|
+
* Shows the menu of an element when the user right-clicks it, or opens the context menu with the keyboard (the menu
|
|
7
|
+
* key, or Shift+F10) while the cursor is in it.
|
|
8
|
+
*
|
|
9
|
+
* The items come from the `elementContextMenu` editor config option only (see `ElementMenuConfig`): commands, submenus
|
|
10
|
+
* and `'|'` separators. Without it, or when it returns no items for an
|
|
11
|
+
* element, the native context menu shows. The menu itself is shown by the `ElementMenu` plugin.
|
|
12
|
+
*/
|
|
13
|
+
export default class ElementContextMenu extends Plugin {
|
|
14
|
+
static get requires() {
|
|
15
|
+
return [ElementMenu];
|
|
16
|
+
}
|
|
17
|
+
static get pluginName() {
|
|
18
|
+
return 'ElementContextMenu';
|
|
19
|
+
}
|
|
20
|
+
init() {
|
|
21
|
+
const editor = this.editor;
|
|
22
|
+
const editingView = editor.editing.view;
|
|
23
|
+
editingView.addObserver(ContextMenuObserver);
|
|
24
|
+
this.listenTo(editingView.document, 'contextmenu', (evt, data) => {
|
|
25
|
+
const { domEvent } = data;
|
|
26
|
+
const element = this.getModelElement(data.target);
|
|
27
|
+
// A context menu opened with the keyboard (the menu key, or Shift+F10 in some browsers) targets the editable
|
|
28
|
+
// itself, not the element at the cursor. A right-click outside any element keeps the native menu.
|
|
29
|
+
const isOpen = element
|
|
30
|
+
? this.open(element, new DOMRect(domEvent.clientX, domEvent.clientY, 0, 0))
|
|
31
|
+
: domEvent.button !== 2 && this.openAtSelection();
|
|
32
|
+
if (isOpen) {
|
|
33
|
+
data.preventDefault();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// Not every browser opens the context menu on Shift+F10 (e.g. on macOS), so handle it in all of them.
|
|
37
|
+
editor.keystrokes.set('Shift+F10', (data, cancel) => {
|
|
38
|
+
if (this.openAtSelection()) {
|
|
39
|
+
cancel();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (typeof editor.config.get('elementContextMenu') === 'function') {
|
|
43
|
+
editor.accessibility.addKeystrokeInfos({
|
|
44
|
+
keystrokes: [{ label: editor.t('Open the menu of the element at the cursor'), keystroke: 'Shift+F10' }]
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Opens the menu of the element next to the target (e.g. a button, or a `DOMRect` in viewport coordinates), with
|
|
50
|
+
* the items from the `elementContextMenu` config option.
|
|
51
|
+
*
|
|
52
|
+
* Returns `false` when there are no items, so the caller can let the native context menu show instead.
|
|
53
|
+
*/
|
|
54
|
+
open(element, target) {
|
|
55
|
+
const config = this.editor.config.get('elementContextMenu');
|
|
56
|
+
if (typeof config !== 'function') {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return this.editor.plugins
|
|
60
|
+
.get(ElementMenu)
|
|
61
|
+
.open(element, target, config, { label: this.editor.t('Element menu') });
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Opens the menu of the element at the selection (the selected element, or the element with the cursor), next to
|
|
65
|
+
* the selection.
|
|
66
|
+
*
|
|
67
|
+
* Returns `false` when there is no such element or no items.
|
|
68
|
+
*/
|
|
69
|
+
openAtSelection() {
|
|
70
|
+
const editing = this.editor.editing;
|
|
71
|
+
const selection = this.editor.model.document.selection;
|
|
72
|
+
const selectedElement = selection.getSelectedElement();
|
|
73
|
+
const element = getElementAtSelection(selection);
|
|
74
|
+
const viewElement = selectedElement && editing.mapper.toViewElement(selectedElement);
|
|
75
|
+
const viewRange = editing.view.document.selection.getFirstRange();
|
|
76
|
+
const target = viewElement
|
|
77
|
+
? editing.view.domConverter.mapViewToDom(viewElement)
|
|
78
|
+
: viewRange && editing.view.domConverter.viewRangeToDom(viewRange);
|
|
79
|
+
if (!element || !isInDocument(element) || !(target instanceof HTMLElement || target instanceof Range)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return this.open(element, target);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Returns the closest model element mapped to the given view element or its ancestors, skipping the root.
|
|
86
|
+
*/
|
|
87
|
+
getModelElement(viewElement) {
|
|
88
|
+
const mapper = this.editor.editing.mapper;
|
|
89
|
+
for (let node = viewElement; node; node = node.parent?.is('element') ? node.parent : null) {
|
|
90
|
+
const element = mapper.toModelElement(node);
|
|
91
|
+
if (element) {
|
|
92
|
+
return element.is('rootElement') ? null : element;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=elementContextMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementContextMenu.js","sourceRoot":"","sources":["../../src/contextMenu/elementContextMenu.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 'ckeditor5-elements-core';
|
|
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;AACnB,OAAO,EAAE,wBAAwB,EAAgB,MAAM,yBAAyB,CAAC;AAGjF;;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,162 @@
|
|
|
1
|
+
import { ContextualBalloon, DropdownMenuRootListView, KeystrokeHandler, Notification, Plugin, clickOutsideHandler } from 'ckeditor5';
|
|
2
|
+
import { ElementOrPositionCommand, isInDocument } from 'ckeditor5-elements-core';
|
|
3
|
+
import ElementMenuView from './elementMenuView.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shows menus in a balloon: the menu of an element, built from commands, submenus and separators (see {@link open}),
|
|
6
|
+
* or any view, e.g. the quick insert search (see {@link show}).
|
|
7
|
+
*
|
|
8
|
+
* The element context menu, the breadcrumbs and quick insert show their menus with it. The menu of an element is
|
|
9
|
+
* CKEditor's dropdown menu (see {@link ElementMenuView}).
|
|
10
|
+
*
|
|
11
|
+
* Executing an item closes the menu. Esc and clicking outside close it too; Esc returns the focus to the editor, or to
|
|
12
|
+
* {@link ElementMenuShowOptions.returnFocusTo}.
|
|
13
|
+
*/
|
|
14
|
+
export default class ElementMenu extends Plugin {
|
|
15
|
+
/**
|
|
16
|
+
* The view in the open menu, if any.
|
|
17
|
+
*/
|
|
18
|
+
view = null;
|
|
19
|
+
_keystrokes = null;
|
|
20
|
+
_isObsolete = null;
|
|
21
|
+
_onHide = null;
|
|
22
|
+
static get requires() {
|
|
23
|
+
// The buttons of the menu report failed commands with notifications.
|
|
24
|
+
return [ContextualBalloon, Notification];
|
|
25
|
+
}
|
|
26
|
+
static get pluginName() {
|
|
27
|
+
return 'ElementMenu';
|
|
28
|
+
}
|
|
29
|
+
init() {
|
|
30
|
+
this.listenTo(this.editor.model.document, 'change:data', () => {
|
|
31
|
+
if (this._isObsolete?.()) {
|
|
32
|
+
this.hide();
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Opens the menu of the element next to the target (e.g. a button, or a `DOMRect` in viewport coordinates), with
|
|
38
|
+
* the items from the config callback. The menu closes when the element is removed.
|
|
39
|
+
*
|
|
40
|
+
* Returns `false` when there are no items.
|
|
41
|
+
*/
|
|
42
|
+
open(element, target, config, { label = this.editor.t('Element menu'), isObsolete, ...options } = {}) {
|
|
43
|
+
const items = cleanItems(config(element, this.editor));
|
|
44
|
+
if (!items.length) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const view = new ElementMenuView(this.editor.locale, this.editor.ui.view.body, items, element);
|
|
48
|
+
view.ariaLabel = label;
|
|
49
|
+
this.show(view, target, {
|
|
50
|
+
...options,
|
|
51
|
+
isObsolete: () => !isInDocument(element) || !!isObsolete?.()
|
|
52
|
+
});
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Shows the view (e.g. a menu) next to the target (e.g. a button, or a `DOMRect` in viewport coordinates), and
|
|
57
|
+
* focuses it. The menu closes when the view, or an item of CKEditor's dropdown menu in it, fires `execute`.
|
|
58
|
+
*/
|
|
59
|
+
show(view, target, { isObsolete, returnFocusTo, onHide } = {}) {
|
|
60
|
+
this.hide();
|
|
61
|
+
const close = () => {
|
|
62
|
+
// The item may have opened another menu already (e.g. "Insert before" opens quick insert).
|
|
63
|
+
if (this.view === view) {
|
|
64
|
+
this.hide();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
this.listenTo(view, 'execute', close);
|
|
68
|
+
this.listenTo(view, 'menu:execute', close);
|
|
69
|
+
clickOutsideHandler({
|
|
70
|
+
emitter: view,
|
|
71
|
+
activator: () => this._balloon.visibleView === view,
|
|
72
|
+
// The open nested menus are in panels outside the balloon.
|
|
73
|
+
contextElements: () => [this._balloon.view.element, ...getOpenPanels(view)].filter(element => !!element),
|
|
74
|
+
callback: () => {
|
|
75
|
+
this.hide();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
this._balloon.add({
|
|
79
|
+
view,
|
|
80
|
+
position: { target },
|
|
81
|
+
withArrow: false,
|
|
82
|
+
singleViewMode: true,
|
|
83
|
+
// Views that other plugins add to the balloon meanwhile (e.g. an element toolbar, when the selection moves
|
|
84
|
+
// into its element) go to their own stack, so they do not cover the menu.
|
|
85
|
+
stackId: 'elementMenu'
|
|
86
|
+
});
|
|
87
|
+
const keystrokes = new KeystrokeHandler();
|
|
88
|
+
// Esc in a nested menu closes only that menu.
|
|
89
|
+
keystrokes.set('Esc', (data, cancel) => {
|
|
90
|
+
this.hide();
|
|
91
|
+
(returnFocusTo ?? this.editor.editing.view).focus();
|
|
92
|
+
cancel();
|
|
93
|
+
});
|
|
94
|
+
if (view.element) {
|
|
95
|
+
keystrokes.listenTo(view.element);
|
|
96
|
+
}
|
|
97
|
+
this.view = view;
|
|
98
|
+
this._keystrokes = keystrokes;
|
|
99
|
+
this._isObsolete = isObsolete ?? null;
|
|
100
|
+
this._onHide = onHide ?? null;
|
|
101
|
+
view.focus();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Hides the open menu, if any.
|
|
105
|
+
*/
|
|
106
|
+
hide() {
|
|
107
|
+
const view = this.view;
|
|
108
|
+
const onHide = this._onHide;
|
|
109
|
+
if (!view) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this._keystrokes?.destroy();
|
|
113
|
+
this.view = null;
|
|
114
|
+
this._keystrokes = null;
|
|
115
|
+
this._isObsolete = null;
|
|
116
|
+
this._onHide = null;
|
|
117
|
+
this.stopListening(view);
|
|
118
|
+
if (this._balloon.hasView(view)) {
|
|
119
|
+
this._balloon.remove(view);
|
|
120
|
+
}
|
|
121
|
+
view.destroy();
|
|
122
|
+
onHide?.();
|
|
123
|
+
}
|
|
124
|
+
destroy() {
|
|
125
|
+
this.hide();
|
|
126
|
+
super.destroy();
|
|
127
|
+
}
|
|
128
|
+
get _balloon() {
|
|
129
|
+
return this.editor.plugins.get(ContextualBalloon);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Returns the items without missing ones, submenus without items, and separators at the start or the end, or
|
|
134
|
+
* repeated ones.
|
|
135
|
+
*/
|
|
136
|
+
function cleanItems(items) {
|
|
137
|
+
const result = [];
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
const cleaned = item === '|' || item instanceof ElementOrPositionCommand || !item ? item : cleanSubmenu(item);
|
|
140
|
+
if (!cleaned || (cleaned === '|' && (!result.length || result.at(-1) === '|'))) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
result.push(cleaned);
|
|
144
|
+
}
|
|
145
|
+
if (result.at(-1) === '|') {
|
|
146
|
+
result.pop();
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
function cleanSubmenu(submenu) {
|
|
151
|
+
const items = cleanItems(submenu.items);
|
|
152
|
+
return items.length ? { ...submenu, items } : null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Returns the panels of the open nested menus of CKEditor's dropdown menu.
|
|
156
|
+
*/
|
|
157
|
+
function getOpenPanels(view) {
|
|
158
|
+
return view instanceof DropdownMenuRootListView
|
|
159
|
+
? view.menus.filter(menu => menu.isOpen).map(menu => menu.panelView.element)
|
|
160
|
+
: [];
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=elementMenu.js.map
|