diff --git a/src/core/features/editor/components/rich-text-editor/core-editor-rich-text-editor.html b/src/core/features/editor/components/rich-text-editor/core-editor-rich-text-editor.html
index 40a6d2aa6..acddfb540 100644
--- a/src/core/features/editor/components/rich-text-editor/core-editor-rich-text-editor.html
+++ b/src/core/features/editor/components/rich-text-editor/core-editor-rich-text-editor.html
@@ -1,7 +1,8 @@
+ (blur)="hideToolbar($event)" (keydown)="onKeyDown($event)" (keyup)="onChange()" (change)="onChange()" (paste)="onChange()"
+ (input)="onChange()">
;
protected buttonsDomPromise?: CoreCancellablePromise;
+ protected shortcutCommands?: Record;
rteEnabled = false;
isPhone = false;
@@ -171,10 +172,6 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
this.setContent(this.control?.value);
this.originalContent = this.control?.value;
this.lastDraft = this.control?.value;
- this.editorElement.onchange = () => this.onChange();
- this.editorElement.onkeyup = () => this.onChange();
- this.editorElement.onpaste = () => this.onChange();
- this.editorElement.oninput = () => this.onChange();
// Use paragraph on enter.
document.execCommand('DefaultParagraphSeparator', false, 'p');
@@ -273,6 +270,26 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
});
}
+ /**
+ * Handle keydown events in the editor.
+ *
+ * @param event Event
+ */
+ onKeyDown(event: KeyboardEvent): void {
+ this.onChange();
+
+ const shortcutId = this.getShortcutId(event);
+ const commands = this.getShortcutCommands();
+ const command = commands[shortcutId];
+
+ if (!command) {
+ return;
+ }
+
+ this.stopBubble(event);
+ this.executeCommand(command);
+ }
+
/**
* Resize editor to maximize the space occupied.
*/
@@ -583,7 +600,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
}
/**
- * Execute an action over the selected text.
+ * Execute an action over the selected text when a button is activated.
* API docs: https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
*
* @param event Event data
@@ -602,6 +619,18 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
return;
}
+ this.executeCommand({ name: command, parameters });
+ }
+
+ /**
+ * Execute an action over the selected text.
+ * API docs: https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
+ *
+ * @param command Editor command.
+ * @param command.name Command name.
+ * @param command.parameters Command parameters.
+ */
+ protected executeCommand({ name: command, parameters }: EditorCommand): void {
if (parameters == 'block') {
document.execCommand('formatBlock', false, '<' + command + '>');
@@ -1059,4 +1088,121 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
this.buttonsDomPromise?.cancel();
}
+ /**
+ * Get commands triggered by keyboard shortcuts.
+ *
+ * @returns Commands dictionary indexed by their corresponding keyboard shortcut id.
+ */
+ getShortcutCommands(): Record {
+ if (!this.shortcutCommands) {
+ const isIOS = CorePlatform.isIOS();
+ const metaKey = isIOS ? 'metaKey' : 'ctrlKey';
+ const shiftKey = isIOS ? 'ctrlKey' : 'shiftKey';
+
+ // Same shortcuts as TinyMCE:
+ // @see https://www.tiny.cloud/docs/advanced/keyboard-shortcuts/
+ const shortcuts: { code: string; modifiers: (keyof KeyboardShortcut)[]; command: EditorCommand }[] = [
+ {
+ code: 'KeyB',
+ modifiers: [metaKey],
+ command: {
+ name: 'bold',
+ parameters: 'strong',
+ },
+ },
+ {
+ code: 'KeyI',
+ modifiers: [metaKey],
+ command: {
+ name: 'italic',
+ parameters: 'em',
+ },
+ },
+ {
+ code: 'KeyU',
+ modifiers: [metaKey],
+ command: {
+ name: 'underline',
+ parameters: 'u',
+ },
+ },
+ {
+ code: 'Digit3',
+ modifiers: ['altKey', shiftKey],
+ command: {
+ name: 'h3',
+ parameters: 'block',
+ },
+ },
+ {
+ code: 'Digit4',
+ modifiers: ['altKey', shiftKey],
+ command: {
+ name: 'h4',
+ parameters: 'block',
+ },
+ },
+ {
+ code: 'Digit5',
+ modifiers: ['altKey', shiftKey],
+ command: {
+ name: 'h5',
+ parameters: 'block',
+ },
+ },
+ {
+ code: 'Digit7',
+ modifiers: ['altKey', shiftKey],
+ command: {
+ name: 'p',
+ parameters: 'block',
+ },
+ },
+ ];
+
+ this.shortcutCommands = shortcuts.reduce((shortcuts, { code, modifiers, command }) => {
+ const id = this.getShortcutId({
+ code: code,
+ altKey: modifiers.includes('altKey'),
+ metaKey: modifiers.includes('metaKey'),
+ shiftKey: modifiers.includes('shiftKey'),
+ ctrlKey: modifiers.includes('ctrlKey'),
+ });
+
+ shortcuts[id] = command;
+
+ return shortcuts;
+ }, {} as Record);
+ }
+
+ return this.shortcutCommands;
+ }
+
+ /**
+ * Get a unique identifier for a given keyboard shortcut.
+ *
+ * @param shortcut Shortcut.
+ * @returns Identifier.
+ */
+ protected getShortcutId(shortcut: KeyboardShortcut): string {
+ return (shortcut.altKey ? '1' : '0')
+ + (shortcut.metaKey ? '1' : '0')
+ + (shortcut.shiftKey ? '1' : '0')
+ + (shortcut.ctrlKey ? '1' : '0')
+ + shortcut.code;
+ }
+
+}
+
+/**
+ * Combination
+ */
+type KeyboardShortcut = Pick;
+
+/**
+ * Editor command.
+ */
+interface EditorCommand {
+ name: string;
+ parameters?: string;
}