WYSIWYG: Added inline code support to minimal editor

Used for comments and descriptions.
Also updated shortcut handling that we're not registering shortcuts for
edits which can't use the related formatting types.

For #6003
This commit is contained in:
Dan Brown
2026-04-16 11:11:06 +01:00
parent 0760e677b2
commit 18364d1e6e
5 changed files with 40 additions and 31 deletions

View File

@@ -27,6 +27,7 @@ class HtmlDescriptionFilter
'span' => [],
'em' => [],
'br' => [],
'code' => [],
];
public static function filterFromString(string $html): string

View File

@@ -59,7 +59,7 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
mergeRegister(
registerRichText(editor),
registerHistory(editor, createEmptyHistoryState(), 300),
registerShortcuts(context),
registerShortcuts(context, true),
registerKeyboardHandling(context),
registerMouseHandling(context),
registerSelectionHandling(context),
@@ -123,7 +123,7 @@ export function createBasicEditorInstance(container: HTMLElement, htmlContent: s
const editorTeardown = mergeRegister(
registerRichText(editor),
registerHistory(editor, createEmptyHistoryState(), 300),
registerShortcuts(context),
registerShortcuts(context, false),
registerAutoLinks(editor),
);
@@ -157,7 +157,7 @@ export function createCommentEditorInstance(container: HTMLElement, htmlContent:
const editorTeardown = mergeRegister(
registerRichText(editor),
registerHistory(editor, createEmptyHistoryState(), 300),
registerShortcuts(context),
registerShortcuts(context, false),
registerAutoLinks(editor),
registerMentions(context),
);

View File

@@ -38,29 +38,9 @@ type ShortcutAction = (editor: LexicalEditor, context: EditorUiContext) => boole
* List of action functions by their shortcut combo.
* We use "meta" as an abstraction for ctrl/cmd depending on platform.
*/
const actionsByKeys: Record<string, ShortcutAction> = {
'meta+s': () => {
window.$events.emit('editor-save-draft');
return true;
},
'meta+enter': () => {
window.$events.emit('editor-save-page');
return true;
},
'meta+1': (editor, context) => headerHandler(context, 'h2'),
'meta+2': (editor, context) => headerHandler(context, 'h3'),
'meta+3': (editor, context) => headerHandler(context, 'h4'),
'meta+4': (editor, context) => headerHandler(context, 'h5'),
'meta+5': wrapFormatAction(toggleSelectionAsParagraph),
'meta+d': wrapFormatAction(toggleSelectionAsParagraph),
'meta+6': wrapFormatAction(toggleSelectionAsBlockquote),
'meta+q': wrapFormatAction(toggleSelectionAsBlockquote),
'meta+7': wrapFormatAction(formatCodeBlock),
'meta+e': wrapFormatAction(formatCodeBlock),
const baseActionsByKeys: Record<string, ShortcutAction> = {
'meta+8': toggleInlineCode,
'meta+shift+e': toggleInlineCode,
'meta+9': wrapFormatAction(cycleSelectionCalloutFormats),
'meta+o': wrapFormatAction((e) => toggleSelectionAsList(e, 'number')),
'meta+p': wrapFormatAction((e) => toggleSelectionAsList(e, 'bullet')),
'meta+k': (editor, context) => {
@@ -87,12 +67,39 @@ const actionsByKeys: Record<string, ShortcutAction> = {
},
};
function createKeyDownListener(context: EditorUiContext): (e: KeyboardEvent) => void {
/**
* An extended set of the above, used for fuller-featured editors with heavier block-level formatting.
*/
const extendedActionsByKeys: Record<string, ShortcutAction> = {
...baseActionsByKeys,
'meta+s': () => {
window.$events.emit('editor-save-draft');
return true;
},
'meta+enter': () => {
window.$events.emit('editor-save-page');
return true;
},
'meta+1': (editor, context) => headerHandler(context, 'h2'),
'meta+2': (editor, context) => headerHandler(context, 'h3'),
'meta+3': (editor, context) => headerHandler(context, 'h4'),
'meta+4': (editor, context) => headerHandler(context, 'h5'),
'meta+5': wrapFormatAction(toggleSelectionAsParagraph),
'meta+d': wrapFormatAction(toggleSelectionAsParagraph),
'meta+6': wrapFormatAction(toggleSelectionAsBlockquote),
'meta+7': wrapFormatAction(formatCodeBlock),
'meta+e': wrapFormatAction(formatCodeBlock),
'meta+q': wrapFormatAction(toggleSelectionAsBlockquote),
'meta+9': wrapFormatAction(cycleSelectionCalloutFormats),
};
function createKeyDownListener(context: EditorUiContext, useExtended: boolean): (e: KeyboardEvent) => void {
const keySetToUse = useExtended ? extendedActionsByKeys : baseActionsByKeys;
return (event: KeyboardEvent) => {
const combo = keyboardEventToKeyComboString(event);
// console.log(`pressed: ${combo}`);
if (actionsByKeys[combo]) {
const handled = actionsByKeys[combo](context.editor, context);
if (keySetToUse[combo]) {
const handled = keySetToUse[combo](context.editor, context);
if (handled) {
event.stopPropagation();
event.preventDefault();
@@ -127,8 +134,8 @@ function overrideDefaultCommands(editor: LexicalEditor) {
}, COMMAND_PRIORITY_HIGH);
}
export function registerShortcuts(context: EditorUiContext) {
const listener = createKeyDownListener(context);
export function registerShortcuts(context: EditorUiContext, useExtended: boolean) {
const listener = createKeyDownListener(context, useExtended);
overrideDefaultCommands(context.editor);
return context.editor.registerRootListener((rootElement: null | HTMLElement, prevRootElement: null | HTMLElement) => {

View File

@@ -227,6 +227,7 @@ export function getBasicEditorToolbar(context: EditorUiContext): EditorContainer
new EditorButton(bold),
new EditorButton(italic),
new EditorButton(link),
new EditorButton(code),
new EditorButton(bulletList),
new EditorButton(numberList),
])

View File

@@ -256,8 +256,8 @@ class BookTest extends TestCase
{
$book = $this->entities->book();
$input = '<h1>Test</h1><p id="abc" href="beans">Content<a href="#cat" target="_blank" data-a="b">a</a><section>Hello</section></p>';
$expected = '<p>Content<a href="#cat" target="_blank">a</a></p>';
$input = '<h1>Test</h1><p id="abc" href="beans">Content<a href="#cat" target="_blank" data-a="b">a</a><section>Hello</section><code id="abc">code</code></p>';
$expected = '<p>Content<a href="#cat" target="_blank">a</a><code>code</code></p>';
$this->asEditor()->put($book->getUrl(), [
'name' => $book->name,