mirror of
https://github.com/zadam/trilium.git
synced 2025-11-01 19:05:59 +01:00
chore(react/type_widget): hot-pluggable keyboard shortcuts
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import server from "./server.js";
|
||||
import appContext, { type CommandNames } from "../components/app_context.js";
|
||||
import shortcutService from "./shortcuts.js";
|
||||
import appContext from "../components/app_context.js";
|
||||
import shortcutService, { ShortcutBinding } from "./shortcuts.js";
|
||||
import type Component from "../components/component.js";
|
||||
import type { ActionKeyboardShortcut } from "@triliumnext/commons";
|
||||
|
||||
@@ -30,12 +30,18 @@ async function getActionsForScope(scope: string) {
|
||||
|
||||
async function setupActionsForElement(scope: string, $el: JQuery<HTMLElement>, component: Component) {
|
||||
const actions = await getActionsForScope(scope);
|
||||
const bindings: ShortcutBinding[] = [];
|
||||
|
||||
for (const action of actions) {
|
||||
for (const shortcut of action.effectiveShortcuts ?? []) {
|
||||
shortcutService.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, { ntxId: appContext.tabManager.activeNtxId }));
|
||||
const binding = shortcutService.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, { ntxId: appContext.tabManager.activeNtxId }));
|
||||
if (binding) {
|
||||
bindings.push(binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bindings;
|
||||
}
|
||||
|
||||
getActionsForScope("window").then((actions) => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import utils from "./utils.js";
|
||||
type ElementType = HTMLElement | Document;
|
||||
type Handler = (e: KeyboardEvent) => void;
|
||||
|
||||
interface ShortcutBinding {
|
||||
export interface ShortcutBinding {
|
||||
element: HTMLElement | Document;
|
||||
shortcut: string;
|
||||
handler: Handler;
|
||||
@@ -51,7 +51,7 @@ export function isIMEComposing(e: KeyboardEvent): boolean {
|
||||
if (!e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Standard check for composition state
|
||||
// e.isComposing is true when IME is actively composing
|
||||
// e.keyCode === 229 is a fallback for older browsers where 229 indicates IME processing
|
||||
@@ -86,13 +86,13 @@ function bindElShortcut($el: JQuery<ElementType | Element>, keyboardShortcut: st
|
||||
}
|
||||
|
||||
const e = evt as KeyboardEvent;
|
||||
|
||||
|
||||
// Skip processing if IME is composing to prevent shortcuts from
|
||||
// interfering with text input in CJK languages
|
||||
if (isIMEComposing(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (matchesShortcut(e, keyboardShortcut)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -117,10 +117,20 @@ function bindElShortcut($el: JQuery<ElementType | Element>, keyboardShortcut: st
|
||||
activeBindings.set(key, []);
|
||||
}
|
||||
activeBindings.get(key)!.push(binding);
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function removeIndividualBinding(binding: ShortcutBinding) {
|
||||
const key = binding.namespace ?? "global";
|
||||
const activeBindingsInNamespace = activeBindings.get(key);
|
||||
if (activeBindingsInNamespace) {
|
||||
activeBindings.set(key, activeBindingsInNamespace.filter(aBinding => aBinding.handler === binding.handler));
|
||||
}
|
||||
binding.element.removeEventListener("keydown", binding.listener);
|
||||
}
|
||||
|
||||
function removeNamespaceBindings(namespace: string) {
|
||||
const bindings = activeBindings.get(namespace);
|
||||
if (bindings) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Inputs, MutableRef, useCallback, useContext, useDebugValue, useEffect, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||
import { CommandListenerData, EventData, EventNames } from "../../components/app_context";
|
||||
import { ParentComponent } from "./react_utils";
|
||||
import { MutableRef, useCallback, useContext, useDebugValue, useEffect, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||
import { EventData, EventNames } from "../../components/app_context";
|
||||
import { ParentComponent, refToJQuerySelector } from "./react_utils";
|
||||
import SpacedUpdate from "../../services/spaced_update";
|
||||
import { FilterLabelsByType, KeyboardActionNames, OptionNames, RelationNames } from "@triliumnext/commons";
|
||||
import options, { type OptionValue } from "../../services/options";
|
||||
@@ -21,6 +21,7 @@ import Component from "../../components/component";
|
||||
import toast, { ToastOptions } from "../../services/toast";
|
||||
import protected_session_holder from "../../services/protected_session_holder";
|
||||
import server from "../../services/server";
|
||||
import { removeIndividualBinding } from "../../services/shortcuts";
|
||||
|
||||
export function useTriliumEvent<T extends EventNames>(eventName: T, handler: (data: EventData<T>) => void) {
|
||||
const parentComponent = useContext(ParentComponent);
|
||||
@@ -721,3 +722,17 @@ export function useResizeObserver(ref: RefObject<HTMLElement>, callback: () => v
|
||||
return () => observer.disconnect();
|
||||
}, [ callback, ref ]);
|
||||
}
|
||||
|
||||
export function useKeyboardShortcuts(scope: "code-detail" | "text-detail", containerRef: RefObject<HTMLElement>, parentComponent: Component | undefined) {
|
||||
useEffect(() => {
|
||||
if (!parentComponent) return;
|
||||
const $container = refToJQuerySelector(containerRef);
|
||||
const bindingPromise = keyboard_actions.setupActionsForElement(scope, $container, parentComponent);
|
||||
return async () => {
|
||||
const bindings = await bindingPromise;
|
||||
for (const binding of bindings) {
|
||||
removeIndividualBinding(binding);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@ import { TypeWidgetProps } from "../type_widget";
|
||||
import "./code.css";
|
||||
import CodeMirror, { CodeMirrorProps } from "./CodeMirror";
|
||||
import utils from "../../../services/utils";
|
||||
import { useEditorSpacedUpdate, useNoteBlob, useSyncedRef, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
|
||||
import { useEditorSpacedUpdate, useKeyboardShortcuts, useNoteBlob, useSyncedRef, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
|
||||
import { t } from "../../../services/i18n";
|
||||
import appContext from "../../../components/app_context";
|
||||
import TouchBar, { TouchBarButton } from "../../react/TouchBar";
|
||||
import keyboard_actions from "../../../services/keyboard_actions";
|
||||
import { refToJQuerySelector } from "../../react/react_utils";
|
||||
import { CODE_THEME_DEFAULT_PREFIX as DEFAULT_PREFIX } from "../constants";
|
||||
|
||||
@@ -69,11 +68,7 @@ export function EditableCode({ note, ntxId, debounceUpdate, parentComponent, upd
|
||||
updateInterval
|
||||
});
|
||||
|
||||
// Set up keyboard shortcuts.
|
||||
useEffect(() => {
|
||||
if (!parentComponent) return;
|
||||
keyboard_actions.setupActionsForElement("code-detail", refToJQuerySelector(containerRef), parentComponent);
|
||||
}, []);
|
||||
useKeyboardShortcuts("code-detail", containerRef, parentComponent);
|
||||
|
||||
return (
|
||||
<div className="note-detail-code note-detail-printable">
|
||||
|
||||
Reference in New Issue
Block a user