完善桌面端交互体验与发布检查
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
export const DESKTOP_REFRESH_CURRENT_VIEW_EVENT = "ctms:desktop-refresh-current-view";
|
||||
|
||||
export interface DesktopRefreshEventDetail {
|
||||
handled: boolean;
|
||||
}
|
||||
|
||||
export const dispatchDesktopRefreshCurrentView = (): boolean => {
|
||||
if (typeof window === "undefined") return false;
|
||||
const detail: DesktopRefreshEventDetail = { handled: false };
|
||||
window.dispatchEvent(new CustomEvent<DesktopRefreshEventDetail>(DESKTOP_REFRESH_CURRENT_VIEW_EVENT, { detail }));
|
||||
return detail.handled;
|
||||
};
|
||||
|
||||
export const onDesktopRefreshCurrentView = (handler: () => void | Promise<void>): (() => void) => {
|
||||
if (typeof window === "undefined") return () => {};
|
||||
const listener = (event: Event) => {
|
||||
const detail = (event as CustomEvent<DesktopRefreshEventDetail>).detail;
|
||||
if (detail) detail.handled = true;
|
||||
void handler();
|
||||
};
|
||||
window.addEventListener(DESKTOP_REFRESH_CURRENT_VIEW_EVENT, listener);
|
||||
return () => window.removeEventListener(DESKTOP_REFRESH_CURRENT_VIEW_EVENT, listener);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isEditableShortcutTarget } from "./useDesktopShortcuts";
|
||||
|
||||
describe("desktop shortcut target detection", () => {
|
||||
it("does not hijack form controls", () => {
|
||||
const input = document.createElement("input");
|
||||
const textarea = document.createElement("textarea");
|
||||
const editable = document.createElement("div");
|
||||
editable.setAttribute("contenteditable", "true");
|
||||
|
||||
expect(isEditableShortcutTarget(input)).toBe(true);
|
||||
expect(isEditableShortcutTarget(textarea)).toBe(true);
|
||||
expect(isEditableShortcutTarget(editable)).toBe(true);
|
||||
});
|
||||
|
||||
it("allows shortcuts outside editable areas", () => {
|
||||
const button = document.createElement("button");
|
||||
|
||||
expect(isEditableShortcutTarget(button)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
import { onBeforeUnmount, onMounted } from "vue";
|
||||
|
||||
export interface DesktopShortcutHandlers {
|
||||
openCommandPalette: () => void;
|
||||
closeActiveLayer?: () => boolean;
|
||||
navigateBack?: () => void;
|
||||
navigateForward?: () => void;
|
||||
refreshCurrentView?: () => void;
|
||||
}
|
||||
|
||||
export const isEditableShortcutTarget = (target: EventTarget | null): boolean => {
|
||||
if (!(target instanceof Element)) return false;
|
||||
const tag = target.tagName.toLowerCase();
|
||||
if (["input", "textarea", "select"].includes(tag)) return true;
|
||||
if ((target as HTMLElement).isContentEditable) return true;
|
||||
if (target.getAttribute("contenteditable") === "true") return true;
|
||||
return Boolean(target.closest('[contenteditable="true"], .el-input, .el-textarea, .el-select'));
|
||||
};
|
||||
|
||||
export const useDesktopShortcuts = (enabled: () => boolean, handlers: DesktopShortcutHandlers) => {
|
||||
const onKeydown = (event: KeyboardEvent) => {
|
||||
if (!enabled() || isEditableShortcutTarget(event.target)) return;
|
||||
const modifier = event.metaKey || event.ctrlKey;
|
||||
const key = event.key.toLowerCase();
|
||||
|
||||
if (!modifier && key === "escape") {
|
||||
if (handlers.closeActiveLayer?.()) event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!modifier) return;
|
||||
|
||||
if (key === "k") {
|
||||
event.preventDefault();
|
||||
handlers.openCommandPalette();
|
||||
return;
|
||||
}
|
||||
if (key === "[") {
|
||||
event.preventDefault();
|
||||
handlers.navigateBack?.();
|
||||
return;
|
||||
}
|
||||
if (key === "]") {
|
||||
event.preventDefault();
|
||||
handlers.navigateForward?.();
|
||||
return;
|
||||
}
|
||||
if (key === "r") {
|
||||
event.preventDefault();
|
||||
handlers.refreshCurrentView?.();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => window.addEventListener("keydown", onKeydown));
|
||||
onBeforeUnmount(() => window.removeEventListener("keydown", onKeydown));
|
||||
|
||||
return { onKeydown };
|
||||
};
|
||||
Reference in New Issue
Block a user