jodit 4.15.10 → 4.15.13
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/CHANGELOG.md +10 -0
- package/README.md +7 -0
- package/es2015/jodit.css +21 -1
- package/es2015/jodit.fat.min.css +1 -1
- package/es2015/jodit.fat.min.js +7 -7
- package/es2015/jodit.js +228 -8
- package/es2015/jodit.min.css +1 -1
- package/es2015/jodit.min.js +5 -5
- package/es2015/plugins/debug/debug.css +1 -1
- package/es2015/plugins/debug/debug.js +1 -1
- package/es2015/plugins/debug/debug.min.js +1 -1
- package/es2015/plugins/speech-recognize/speech-recognize.css +1 -1
- package/es2015/plugins/speech-recognize/speech-recognize.js +1 -1
- package/es2015/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es2018/jodit.fat.min.css +1 -1
- package/es2018/jodit.fat.min.js +5 -5
- package/es2018/jodit.min.css +1 -1
- package/es2018/jodit.min.js +5 -5
- package/es2018/plugins/debug/debug.min.js +1 -1
- package/es2018/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es2021/jodit.css +21 -1
- package/es2021/jodit.fat.min.css +1 -1
- package/es2021/jodit.fat.min.js +5 -5
- package/es2021/jodit.js +227 -8
- package/es2021/jodit.min.css +1 -1
- package/es2021/jodit.min.js +5 -5
- package/es2021/plugins/debug/debug.css +1 -1
- package/es2021/plugins/debug/debug.js +1 -1
- package/es2021/plugins/debug/debug.min.js +1 -1
- package/es2021/plugins/speech-recognize/speech-recognize.css +1 -1
- package/es2021/plugins/speech-recognize/speech-recognize.js +1 -1
- package/es2021/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es2021.en/jodit.css +21 -1
- package/es2021.en/jodit.fat.min.css +1 -1
- package/es2021.en/jodit.fat.min.js +10 -10
- package/es2021.en/jodit.js +227 -8
- package/es2021.en/jodit.min.css +1 -1
- package/es2021.en/jodit.min.js +9 -9
- package/es2021.en/plugins/debug/debug.css +1 -1
- package/es2021.en/plugins/debug/debug.js +1 -1
- package/es2021.en/plugins/debug/debug.min.js +1 -1
- package/es2021.en/plugins/speech-recognize/speech-recognize.css +1 -1
- package/es2021.en/plugins/speech-recognize/speech-recognize.js +1 -1
- package/es2021.en/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es5/jodit.css +22 -2
- package/es5/jodit.fat.min.css +1 -1
- package/es5/jodit.fat.min.js +2 -2
- package/es5/jodit.js +282 -8
- package/es5/jodit.min.css +3 -3
- package/es5/jodit.min.js +2 -2
- package/es5/plugins/debug/debug.css +1 -1
- package/es5/plugins/debug/debug.js +1 -1
- package/es5/plugins/debug/debug.min.js +1 -1
- package/es5/plugins/speech-recognize/speech-recognize.css +1 -1
- package/es5/plugins/speech-recognize/speech-recognize.js +1 -1
- package/es5/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es5/polyfills.fat.min.js +1 -1
- package/es5/polyfills.js +1 -1
- package/es5/polyfills.min.js +1 -1
- package/esm/core/constants.d.ts +2 -0
- package/esm/core/constants.js +3 -1
- package/esm/modules/toolbar/button/button.d.ts +14 -0
- package/esm/modules/toolbar/button/button.js +102 -4
- package/esm/plugins/all.d.ts +1 -0
- package/esm/plugins/all.js +1 -0
- package/esm/plugins/index.d.ts +1 -0
- package/esm/plugins/index.js +1 -0
- package/esm/plugins/toolbar-navigation/toolbar-navigation.d.ts +42 -0
- package/esm/plugins/toolbar-navigation/toolbar-navigation.js +113 -0
- package/package.json +1 -1
- package/types/core/constants.d.ts +2 -0
- package/types/modules/toolbar/button/button.d.ts +14 -0
- package/types/plugins/index.d.ts +1 -0
- package/types/plugins/toolbar-navigation/toolbar-navigation.d.ts +42 -0
package/es5/polyfills.fat.min.js
CHANGED
package/es5/polyfills.js
CHANGED
package/es5/polyfills.min.js
CHANGED
package/esm/core/constants.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export declare const KEY_TAB = "Tab";
|
|
|
45
45
|
export declare const KEY_ENTER = "Enter";
|
|
46
46
|
export declare const KEY_ESC = "Escape";
|
|
47
47
|
export declare const KEY_ALT = "Alt";
|
|
48
|
+
export declare const KEY_HOME = "Home";
|
|
49
|
+
export declare const KEY_END = "End";
|
|
48
50
|
export declare const KEY_LEFT = "ArrowLeft";
|
|
49
51
|
export declare const KEY_UP = "ArrowUp";
|
|
50
52
|
export declare const KEY_RIGHT = "ArrowRight";
|
package/esm/core/constants.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
4
|
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
5
|
*/
|
|
6
|
-
export const APP_VERSION = "4.15.
|
|
6
|
+
export const APP_VERSION = "4.15.13";
|
|
7
7
|
// prettier-ignore
|
|
8
8
|
export const ES = "es2020";
|
|
9
9
|
export const IS_ES_MODERN = true;
|
|
@@ -53,6 +53,8 @@ export const KEY_TAB = 'Tab';
|
|
|
53
53
|
export const KEY_ENTER = 'Enter';
|
|
54
54
|
export const KEY_ESC = 'Escape';
|
|
55
55
|
export const KEY_ALT = 'Alt';
|
|
56
|
+
export const KEY_HOME = 'Home';
|
|
57
|
+
export const KEY_END = 'End';
|
|
56
58
|
export const KEY_LEFT = 'ArrowLeft';
|
|
57
59
|
export const KEY_UP = 'ArrowUp';
|
|
58
60
|
export const KEY_RIGHT = 'ArrowRight';
|
|
@@ -67,6 +67,20 @@ export declare class ToolbarButton<T extends IViewBased = IViewBased> extends UI
|
|
|
67
67
|
*/
|
|
68
68
|
protected onTriggerClick(e: MouseEvent): void;
|
|
69
69
|
protected onTriggerKeyDown(e: KeyboardEvent): void;
|
|
70
|
+
/**
|
|
71
|
+
* `ArrowDown` on a button with a trigger opens its dropdown and moves the
|
|
72
|
+
* focus into it, like the WAI-ARIA menu button pattern
|
|
73
|
+
*/
|
|
74
|
+
protected onButtonKeyDown(e: KeyboardEvent): void;
|
|
75
|
+
/**
|
|
76
|
+
* Inside the dropdown: `ArrowDown`/`ArrowUp` walk the items, `Escape` and
|
|
77
|
+
* `ArrowUp` on the first item close it and give the focus back to the button
|
|
78
|
+
*/
|
|
79
|
+
private __onPopupKeyDown;
|
|
80
|
+
/**
|
|
81
|
+
* Visible controls of the opened dropdown that can take the focus
|
|
82
|
+
*/
|
|
83
|
+
private __popupFocusables;
|
|
70
84
|
private openedPopup;
|
|
71
85
|
/**
|
|
72
86
|
* Create an open popup list
|
|
@@ -14,7 +14,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
14
14
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
15
15
|
};
|
|
16
16
|
import { STATUSES } from "../../../core/component/statuses.js";
|
|
17
|
-
import { KEY_ENTER, KEY_SPACE } from "../../../core/constants.js";
|
|
17
|
+
import { KEY_DOWN, KEY_ENTER, KEY_ESC, KEY_SPACE, KEY_UP } from "../../../core/constants.js";
|
|
18
18
|
import { autobind } from "../../../core/decorators/autobind/autobind.js";
|
|
19
19
|
import { cacheHTML } from "../../../core/decorators/cache/cache.js";
|
|
20
20
|
import { component, getComponentClass } from "../../../core/decorators/component/component.js";
|
|
@@ -35,6 +35,15 @@ import { findControlType } from "../../../core/ui/helpers/get-control-type.js";
|
|
|
35
35
|
import { Icon } from "../../../core/ui/icon.js";
|
|
36
36
|
import { Popup } from "../../../core/ui/popup/popup.js";
|
|
37
37
|
import { makeCollection } from "../factory.js";
|
|
38
|
+
const INPUT_TAGS = new Set(['input', 'textarea', 'select']);
|
|
39
|
+
const FOCUSABLE_SELECTOR = [
|
|
40
|
+
'button:not([disabled])',
|
|
41
|
+
'a[href]',
|
|
42
|
+
'input:not([disabled]):not([type="hidden"])',
|
|
43
|
+
'select:not([disabled])',
|
|
44
|
+
'textarea:not([disabled])',
|
|
45
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
46
|
+
].join(',');
|
|
38
47
|
let ToolbarButton = class ToolbarButton extends UIButton {
|
|
39
48
|
className() {
|
|
40
49
|
return 'ToolbarButton';
|
|
@@ -290,13 +299,88 @@ let ToolbarButton = class ToolbarButton extends UIButton {
|
|
|
290
299
|
}
|
|
291
300
|
}
|
|
292
301
|
onTriggerKeyDown(e) {
|
|
293
|
-
if (this.state.disabled
|
|
294
|
-
|
|
302
|
+
if (this.state.disabled) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (e.key === KEY_DOWN) {
|
|
306
|
+
this.onButtonKeyDown(e);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (![KEY_ENTER, KEY_SPACE, ' '].includes(e.key)) {
|
|
295
310
|
return;
|
|
296
311
|
}
|
|
297
312
|
e.preventDefault();
|
|
298
313
|
this.trigger.click();
|
|
299
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* `ArrowDown` on a button with a trigger opens its dropdown and moves the
|
|
317
|
+
* focus into it, like the WAI-ARIA menu button pattern
|
|
318
|
+
*/
|
|
319
|
+
onButtonKeyDown(e) {
|
|
320
|
+
var _a;
|
|
321
|
+
if (e.key !== KEY_DOWN ||
|
|
322
|
+
this.state.disabled ||
|
|
323
|
+
!this.state.hasTrigger ||
|
|
324
|
+
e.altKey ||
|
|
325
|
+
e.ctrlKey ||
|
|
326
|
+
e.metaKey) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
e.preventDefault();
|
|
330
|
+
e.stopPropagation();
|
|
331
|
+
if (!this.openedPopup) {
|
|
332
|
+
this.trigger.click();
|
|
333
|
+
}
|
|
334
|
+
(_a = this.__popupFocusables()[0]) === null || _a === void 0 ? void 0 : _a.focus();
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Inside the dropdown: `ArrowDown`/`ArrowUp` walk the items, `Escape` and
|
|
338
|
+
* `ArrowUp` on the first item close it and give the focus back to the button
|
|
339
|
+
*/
|
|
340
|
+
__onPopupKeyDown(e) {
|
|
341
|
+
if (!this.openedPopup || e.altKey || e.ctrlKey || e.metaKey) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const target = e.target;
|
|
345
|
+
// A text field keeps the arrow keys for itself
|
|
346
|
+
if (Dom.isTag(target, INPUT_TAGS) && e.key !== KEY_ESC) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const items = this.__popupFocusables(), index = items.indexOf(target);
|
|
350
|
+
switch (e.key) {
|
|
351
|
+
case KEY_ESC:
|
|
352
|
+
this.__closePopup();
|
|
353
|
+
break;
|
|
354
|
+
case KEY_UP:
|
|
355
|
+
if (index <= 0) {
|
|
356
|
+
this.__closePopup();
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
items[index - 1].focus();
|
|
360
|
+
}
|
|
361
|
+
break;
|
|
362
|
+
case KEY_DOWN:
|
|
363
|
+
if (!items.length) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
items[(index + 1) % items.length].focus();
|
|
367
|
+
break;
|
|
368
|
+
default:
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
e.preventDefault();
|
|
372
|
+
e.stopPropagation();
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Visible controls of the opened dropdown that can take the focus
|
|
376
|
+
*/
|
|
377
|
+
__popupFocusables() {
|
|
378
|
+
const popup = this.openedPopup;
|
|
379
|
+
if (!popup) {
|
|
380
|
+
return [];
|
|
381
|
+
}
|
|
382
|
+
return Array.from(popup.container.querySelectorAll(FOCUSABLE_SELECTOR)).filter(elm => Boolean(elm.offsetParent));
|
|
383
|
+
}
|
|
300
384
|
/**
|
|
301
385
|
* Create an open popup list
|
|
302
386
|
*/
|
|
@@ -377,7 +461,8 @@ let ToolbarButton = class ToolbarButton extends UIButton {
|
|
|
377
461
|
this.openedPopup = new Popup(this.j, false);
|
|
378
462
|
this.j.e
|
|
379
463
|
.on(this.ow, 'mousedown touchstart', this.onOutsideClick)
|
|
380
|
-
.on('escape closeAllPopups', this.onOutsideClick)
|
|
464
|
+
.on('escape closeAllPopups', this.onOutsideClick)
|
|
465
|
+
.on(this.openedPopup.container, 'keydown', this.__onPopupKeyDown);
|
|
381
466
|
return this.openedPopup;
|
|
382
467
|
}
|
|
383
468
|
__closePopup() {
|
|
@@ -395,12 +480,19 @@ let ToolbarButton = class ToolbarButton extends UIButton {
|
|
|
395
480
|
this.j.e
|
|
396
481
|
.off(this.ow, 'mousedown touchstart', this.onOutsideClick)
|
|
397
482
|
.off('escape closeAllPopups', this.onOutsideClick);
|
|
483
|
+
// The keyboard brought the focus into the dropdown: give it back to
|
|
484
|
+
// the button, otherwise it would fall out to the document
|
|
485
|
+
const { activeElement } = this.od, hadFocus = activeElement &&
|
|
486
|
+
Dom.isOrContains(popup.container, activeElement);
|
|
398
487
|
this.state.activated = false;
|
|
399
488
|
popup.close();
|
|
400
489
|
popup.destruct();
|
|
401
490
|
if (this.trigger) {
|
|
402
491
|
attr(this.trigger, 'aria-expanded', false);
|
|
403
492
|
}
|
|
493
|
+
if (hadFocus) {
|
|
494
|
+
this.focus();
|
|
495
|
+
}
|
|
404
496
|
}
|
|
405
497
|
/**
|
|
406
498
|
* Click handler
|
|
@@ -460,6 +552,12 @@ __decorate([
|
|
|
460
552
|
__decorate([
|
|
461
553
|
watch('trigger:keydown')
|
|
462
554
|
], ToolbarButton.prototype, "onTriggerKeyDown", null);
|
|
555
|
+
__decorate([
|
|
556
|
+
watch('button:keydown')
|
|
557
|
+
], ToolbarButton.prototype, "onButtonKeyDown", null);
|
|
558
|
+
__decorate([
|
|
559
|
+
autobind
|
|
560
|
+
], ToolbarButton.prototype, "__onPopupKeyDown", null);
|
|
463
561
|
__decorate([
|
|
464
562
|
autobind
|
|
465
563
|
], ToolbarButton.prototype, "onOutsideClick", null);
|
package/esm/plugins/all.d.ts
CHANGED
|
@@ -66,6 +66,7 @@ import "./ai-assistant/ai-assistant";
|
|
|
66
66
|
import "./tab/tab";
|
|
67
67
|
import "./table/table";
|
|
68
68
|
import "./table-keyboard-navigation/table-keyboard-navigation";
|
|
69
|
+
import "./toolbar-navigation/toolbar-navigation";
|
|
69
70
|
import "./video/video";
|
|
70
71
|
import "./wrap-nodes/wrap-nodes";
|
|
71
72
|
import "./dtd/dtd";
|
package/esm/plugins/all.js
CHANGED
|
@@ -67,6 +67,7 @@ import "./ai-assistant/ai-assistant.js";
|
|
|
67
67
|
import "./tab/tab.js";
|
|
68
68
|
import "./table/table.js";
|
|
69
69
|
import "./table-keyboard-navigation/table-keyboard-navigation.js";
|
|
70
|
+
import "./toolbar-navigation/toolbar-navigation.js";
|
|
70
71
|
import "./video/video.js";
|
|
71
72
|
import "./wrap-nodes/wrap-nodes.js";
|
|
72
73
|
import "./dtd/dtd.js";
|
package/esm/plugins/index.d.ts
CHANGED
|
@@ -66,6 +66,7 @@ import "./ai-assistant/ai-assistant";
|
|
|
66
66
|
import "./tab/tab";
|
|
67
67
|
import "./table/table";
|
|
68
68
|
import "./table-keyboard-navigation/table-keyboard-navigation";
|
|
69
|
+
import "./toolbar-navigation/toolbar-navigation";
|
|
69
70
|
import "./video/video";
|
|
70
71
|
import "./wrap-nodes/wrap-nodes";
|
|
71
72
|
import "./dtd/dtd";
|
package/esm/plugins/index.js
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Jodit Editor (https://xdsoft.net/jodit/)
|
|
3
|
+
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
|
+
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* [[include:plugins/toolbar-navigation/README.md]]
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
* @module plugins/toolbar-navigation
|
|
10
|
+
*/
|
|
11
|
+
import type { IJodit } from "../../types/index";
|
|
12
|
+
import { Plugin } from "../../core/plugin/index";
|
|
13
|
+
/**
|
|
14
|
+
* Makes the toolbar reachable from the keyboard, as described by the WAI-ARIA
|
|
15
|
+
* authoring practices for a `role="toolbar"` widget.
|
|
16
|
+
*
|
|
17
|
+
* The focus is moved programmatically, so this works whatever
|
|
18
|
+
* {@link Config.allowTabNavigation} does with the tab order — including the
|
|
19
|
+
* default, where the buttons take no part in the page tab order.
|
|
20
|
+
*/
|
|
21
|
+
export declare class toolbarNavigation extends Plugin {
|
|
22
|
+
/** @override */
|
|
23
|
+
protected afterInit(jodit: IJodit): void;
|
|
24
|
+
/** @override */
|
|
25
|
+
protected beforeDestruct(): void;
|
|
26
|
+
/**
|
|
27
|
+
* Moves the focus to the first button of the toolbar.
|
|
28
|
+
*
|
|
29
|
+
* Bound to `Alt+F10` by default; remap it like any other command:
|
|
30
|
+
* `commandToHotkeys: { focusToolbar: 'alt+0' }`
|
|
31
|
+
*/
|
|
32
|
+
focusToolbar(): false;
|
|
33
|
+
/**
|
|
34
|
+
* Arrow keys / `Home` / `End` walk the buttons, `Escape` leaves the toolbar
|
|
35
|
+
*/
|
|
36
|
+
protected onKeyDown(e: KeyboardEvent): boolean | void;
|
|
37
|
+
/**
|
|
38
|
+
* Toolbar buttons that can take the focus right now
|
|
39
|
+
*/
|
|
40
|
+
private __buttons;
|
|
41
|
+
private __isRtl;
|
|
42
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Jodit Editor (https://xdsoft.net/jodit/)
|
|
3
|
+
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
|
+
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
|
+
*/
|
|
6
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
7
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
9
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
10
|
+
else
|
|
11
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
12
|
+
if (d = decorators[i])
|
|
13
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
14
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
15
|
+
};
|
|
16
|
+
import { KEY_END, KEY_ESC, KEY_HOME, KEY_LEFT, KEY_RIGHT } from "../../core/constants.js";
|
|
17
|
+
import { autobind } from "../../core/decorators/autobind/autobind.js";
|
|
18
|
+
import { watch } from "../../core/decorators/watch/watch.js";
|
|
19
|
+
import { Dom } from "../../core/dom/dom.js";
|
|
20
|
+
import { pluginSystem } from "../../core/global.js";
|
|
21
|
+
import { attr } from "../../core/helpers/utils/attr.js";
|
|
22
|
+
import { Plugin } from "../../core/plugin/index.js";
|
|
23
|
+
const INPUT_TAGS = new Set(['input', 'textarea', 'select']);
|
|
24
|
+
/**
|
|
25
|
+
* Makes the toolbar reachable from the keyboard, as described by the WAI-ARIA
|
|
26
|
+
* authoring practices for a `role="toolbar"` widget.
|
|
27
|
+
*
|
|
28
|
+
* The focus is moved programmatically, so this works whatever
|
|
29
|
+
* {@link Config.allowTabNavigation} does with the tab order — including the
|
|
30
|
+
* default, where the buttons take no part in the page tab order.
|
|
31
|
+
*/
|
|
32
|
+
export class toolbarNavigation extends Plugin {
|
|
33
|
+
/** @override */
|
|
34
|
+
afterInit(jodit) {
|
|
35
|
+
jodit.registerCommand('focusToolbar', {
|
|
36
|
+
hotkeys: 'alt+f10',
|
|
37
|
+
exec: this.focusToolbar
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** @override */
|
|
41
|
+
beforeDestruct() { }
|
|
42
|
+
/**
|
|
43
|
+
* Moves the focus to the first button of the toolbar.
|
|
44
|
+
*
|
|
45
|
+
* Bound to `Alt+F10` by default; remap it like any other command:
|
|
46
|
+
* `commandToHotkeys: { focusToolbar: 'alt+0' }`
|
|
47
|
+
*/
|
|
48
|
+
focusToolbar() {
|
|
49
|
+
const [first] = this.__buttons();
|
|
50
|
+
first === null || first === void 0 ? void 0 : first.focus();
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Arrow keys / `Home` / `End` walk the buttons, `Escape` leaves the toolbar
|
|
55
|
+
*/
|
|
56
|
+
onKeyDown(e) {
|
|
57
|
+
if (e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const target = e.target;
|
|
61
|
+
// A control that needs the arrow keys for itself
|
|
62
|
+
if (Dom.isTag(target, INPUT_TAGS) ||
|
|
63
|
+
(Dom.isHTMLElement(target) && target.isContentEditable)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const buttons = this.__buttons();
|
|
67
|
+
if (!buttons.length) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
switch (e.key) {
|
|
71
|
+
case KEY_ESC:
|
|
72
|
+
this.j.s.focus();
|
|
73
|
+
break;
|
|
74
|
+
case KEY_HOME:
|
|
75
|
+
buttons[0].focus();
|
|
76
|
+
break;
|
|
77
|
+
case KEY_END:
|
|
78
|
+
buttons[buttons.length - 1].focus();
|
|
79
|
+
break;
|
|
80
|
+
case KEY_LEFT:
|
|
81
|
+
case KEY_RIGHT: {
|
|
82
|
+
const index = buttons.findIndex(button => button.container.contains(target));
|
|
83
|
+
if (index === -1) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const forward = (e.key === KEY_RIGHT) !== this.__isRtl(), next = index + (forward ? 1 : -1);
|
|
87
|
+
buttons[(next + buttons.length) % buttons.length].focus();
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
default:
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
e.stopPropagation();
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Toolbar buttons that can take the focus right now
|
|
99
|
+
*/
|
|
100
|
+
__buttons() {
|
|
101
|
+
return this.j.toolbar.buttons.filter(button => !button.state.disabled && button.container.isConnected);
|
|
102
|
+
}
|
|
103
|
+
__isRtl() {
|
|
104
|
+
return attr(this.j.toolbar.container, 'dir') === 'rtl';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
__decorate([
|
|
108
|
+
autobind
|
|
109
|
+
], toolbarNavigation.prototype, "focusToolbar", null);
|
|
110
|
+
__decorate([
|
|
111
|
+
watch('j.toolbar.container:keydown.toolbar-navigation')
|
|
112
|
+
], toolbarNavigation.prototype, "onKeyDown", null);
|
|
113
|
+
pluginSystem.add('toolbarNavigation', toolbarNavigation);
|
package/package.json
CHANGED
|
@@ -45,6 +45,8 @@ export declare const KEY_TAB = "Tab";
|
|
|
45
45
|
export declare const KEY_ENTER = "Enter";
|
|
46
46
|
export declare const KEY_ESC = "Escape";
|
|
47
47
|
export declare const KEY_ALT = "Alt";
|
|
48
|
+
export declare const KEY_HOME = "Home";
|
|
49
|
+
export declare const KEY_END = "End";
|
|
48
50
|
export declare const KEY_LEFT = "ArrowLeft";
|
|
49
51
|
export declare const KEY_UP = "ArrowUp";
|
|
50
52
|
export declare const KEY_RIGHT = "ArrowRight";
|
|
@@ -67,6 +67,20 @@ export declare class ToolbarButton<T extends IViewBased = IViewBased> extends UI
|
|
|
67
67
|
*/
|
|
68
68
|
protected onTriggerClick(e: MouseEvent): void;
|
|
69
69
|
protected onTriggerKeyDown(e: KeyboardEvent): void;
|
|
70
|
+
/**
|
|
71
|
+
* `ArrowDown` on a button with a trigger opens its dropdown and moves the
|
|
72
|
+
* focus into it, like the WAI-ARIA menu button pattern
|
|
73
|
+
*/
|
|
74
|
+
protected onButtonKeyDown(e: KeyboardEvent): void;
|
|
75
|
+
/**
|
|
76
|
+
* Inside the dropdown: `ArrowDown`/`ArrowUp` walk the items, `Escape` and
|
|
77
|
+
* `ArrowUp` on the first item close it and give the focus back to the button
|
|
78
|
+
*/
|
|
79
|
+
private __onPopupKeyDown;
|
|
80
|
+
/**
|
|
81
|
+
* Visible controls of the opened dropdown that can take the focus
|
|
82
|
+
*/
|
|
83
|
+
private __popupFocusables;
|
|
70
84
|
private openedPopup;
|
|
71
85
|
/**
|
|
72
86
|
* Create an open popup list
|
package/types/plugins/index.d.ts
CHANGED
|
@@ -66,6 +66,7 @@ import "./ai-assistant/ai-assistant";
|
|
|
66
66
|
import "./tab/tab";
|
|
67
67
|
import "./table/table";
|
|
68
68
|
import "./table-keyboard-navigation/table-keyboard-navigation";
|
|
69
|
+
import "./toolbar-navigation/toolbar-navigation";
|
|
69
70
|
import "./video/video";
|
|
70
71
|
import "./wrap-nodes/wrap-nodes";
|
|
71
72
|
import "./dtd/dtd";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Jodit Editor (https://xdsoft.net/jodit/)
|
|
3
|
+
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
|
+
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* [[include:plugins/toolbar-navigation/README.md]]
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
* @module plugins/toolbar-navigation
|
|
10
|
+
*/
|
|
11
|
+
import type { IJodit } from "../../types/index";
|
|
12
|
+
import { Plugin } from "../../core/plugin/index";
|
|
13
|
+
/**
|
|
14
|
+
* Makes the toolbar reachable from the keyboard, as described by the WAI-ARIA
|
|
15
|
+
* authoring practices for a `role="toolbar"` widget.
|
|
16
|
+
*
|
|
17
|
+
* The focus is moved programmatically, so this works whatever
|
|
18
|
+
* {@link Config.allowTabNavigation} does with the tab order — including the
|
|
19
|
+
* default, where the buttons take no part in the page tab order.
|
|
20
|
+
*/
|
|
21
|
+
export declare class toolbarNavigation extends Plugin {
|
|
22
|
+
/** @override */
|
|
23
|
+
protected afterInit(jodit: IJodit): void;
|
|
24
|
+
/** @override */
|
|
25
|
+
protected beforeDestruct(): void;
|
|
26
|
+
/**
|
|
27
|
+
* Moves the focus to the first button of the toolbar.
|
|
28
|
+
*
|
|
29
|
+
* Bound to `Alt+F10` by default; remap it like any other command:
|
|
30
|
+
* `commandToHotkeys: { focusToolbar: 'alt+0' }`
|
|
31
|
+
*/
|
|
32
|
+
focusToolbar(): false;
|
|
33
|
+
/**
|
|
34
|
+
* Arrow keys / `Home` / `End` walk the buttons, `Escape` leaves the toolbar
|
|
35
|
+
*/
|
|
36
|
+
protected onKeyDown(e: KeyboardEvent): boolean | void;
|
|
37
|
+
/**
|
|
38
|
+
* Toolbar buttons that can take the focus right now
|
|
39
|
+
*/
|
|
40
|
+
private __buttons;
|
|
41
|
+
private __isRtl;
|
|
42
|
+
}
|