mirror of https://github.com/usememos/memos.git
Fixing not working keyboard shortcuts
This commit is contained in:
parent
d1b2c0308b
commit
5c2010195e
|
|
@ -151,6 +151,12 @@ const MemoEditor = observer((props: Props) => {
|
||||||
const isMetaKey = event.ctrlKey || event.metaKey;
|
const isMetaKey = event.ctrlKey || event.metaKey;
|
||||||
if (isMetaKey) {
|
if (isMetaKey) {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
handleSaveBtnClick();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key.toLowerCase() === "s") {
|
||||||
|
event.preventDefault();
|
||||||
handleSaveBtnClick();
|
handleSaveBtnClick();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { BookmarkIcon, EyeOffIcon, MessageCircleMoreIcon } from "lucide-react";
|
import { BookmarkIcon, EyeOffIcon, MessageCircleMoreIcon } from "lucide-react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { memo, useCallback, useState } from "react";
|
import { memo, useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||||
|
|
@ -50,6 +51,8 @@ const MemoView: React.FC<Props> = observer((props: Props) => {
|
||||||
urls: [],
|
urls: [],
|
||||||
index: 0,
|
index: 0,
|
||||||
});
|
});
|
||||||
|
const [shortcutActive, setShortcutActive] = useState(false);
|
||||||
|
const cardRef = useRef<HTMLDivElement>(null);
|
||||||
const instanceMemoRelatedSetting = instanceStore.state.memoRelatedSetting;
|
const instanceMemoRelatedSetting = instanceStore.state.memoRelatedSetting;
|
||||||
const referencedMemos = memo.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE);
|
const referencedMemos = memo.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE);
|
||||||
const commentAmount = memo.relations.filter(
|
const commentAmount = memo.relations.filter(
|
||||||
|
|
@ -124,6 +127,89 @@ const MemoView: React.FC<Props> = observer((props: Props) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const archiveMemo = useCallback(async () => {
|
||||||
|
if (isArchived) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await memoStore.updateMemo(
|
||||||
|
{
|
||||||
|
name: memo.name,
|
||||||
|
state: State.ARCHIVED,
|
||||||
|
},
|
||||||
|
["state"],
|
||||||
|
);
|
||||||
|
toast.success(t("message.archived-successfully"));
|
||||||
|
userStore.setStatsStateId();
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(error);
|
||||||
|
toast.error(error?.details);
|
||||||
|
}
|
||||||
|
}, [isArchived, memo.name, t]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!shortcutActive || readonly || showEditor || !cardRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardEl = cardRef.current;
|
||||||
|
const isTextInputElement = (element: HTMLElement | null) => {
|
||||||
|
if (!element) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (element.isContentEditable) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (element instanceof HTMLTextAreaElement) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element instanceof HTMLInputElement) {
|
||||||
|
const textTypes = ["text", "search", "email", "password", "url", "tel", "number"];
|
||||||
|
return textTypes.includes(element.type || "text");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
const target = event.target as HTMLElement | null;
|
||||||
|
if (!cardEl.contains(target) || isTextInputElement(target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.metaKey || event.ctrlKey || event.altKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = event.key.toLowerCase();
|
||||||
|
if (key === "e") {
|
||||||
|
event.preventDefault();
|
||||||
|
setShowEditor(true);
|
||||||
|
} else if (key === "a" && !isArchived) {
|
||||||
|
event.preventDefault();
|
||||||
|
archiveMemo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cardEl.addEventListener("keydown", handleKeyDown);
|
||||||
|
return () => cardEl.removeEventListener("keydown", handleKeyDown);
|
||||||
|
}, [shortcutActive, readonly, showEditor, isArchived, archiveMemo]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (showEditor || readonly) {
|
||||||
|
setShortcutActive(false);
|
||||||
|
}
|
||||||
|
}, [showEditor, readonly]);
|
||||||
|
|
||||||
|
const handleShortcutActivation = (active: boolean) => {
|
||||||
|
if (readonly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setShortcutActive(active);
|
||||||
|
};
|
||||||
|
|
||||||
const displayTime = isArchived ? (
|
const displayTime = isArchived ? (
|
||||||
memo.displayTime?.toLocaleString()
|
memo.displayTime?.toLocaleString()
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -142,9 +228,13 @@ const MemoView: React.FC<Props> = observer((props: Props) => {
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative group flex flex-col justify-start items-start bg-card w-full px-4 py-3 mb-2 gap-2 text-card-foreground rounded-lg border border-border transition-colors",
|
"relative group flex flex-col justify-start items-start bg-card w-full px-4 py-3 mb-2 gap-2 text-card-foreground rounded-lg border border-border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
ref={cardRef}
|
||||||
|
tabIndex={readonly ? -1 : 0}
|
||||||
|
onFocus={() => handleShortcutActivation(true)}
|
||||||
|
onBlur={() => handleShortcutActivation(false)}
|
||||||
>
|
>
|
||||||
<div className="w-full flex flex-row justify-between items-center gap-2">
|
<div className="w-full flex flex-row justify-between items-center gap-2">
|
||||||
<div className="w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center">
|
<div className="w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { SearchIcon } from "lucide-react";
|
import { SearchIcon } from "lucide-react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { memoFilterStore } from "@/store";
|
import { memoFilterStore } from "@/store";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
@ -9,6 +9,19 @@ import MemoDisplaySettingMenu from "./MemoDisplaySettingMenu";
|
||||||
const SearchBar = observer(() => {
|
const SearchBar = observer(() => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const [queryText, setQueryText] = useState("");
|
const [queryText, setQueryText] = useState("");
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleGlobalShortcut = (event: KeyboardEvent) => {
|
||||||
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
||||||
|
event.preventDefault();
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleGlobalShortcut);
|
||||||
|
return () => window.removeEventListener("keydown", handleGlobalShortcut);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const onTextChange = (event: React.FormEvent<HTMLInputElement>) => {
|
const onTextChange = (event: React.FormEvent<HTMLInputElement>) => {
|
||||||
setQueryText(event.currentTarget.value);
|
setQueryText(event.currentTarget.value);
|
||||||
|
|
@ -40,6 +53,7 @@ const SearchBar = observer(() => {
|
||||||
value={queryText}
|
value={queryText}
|
||||||
onChange={onTextChange}
|
onChange={onTextChange}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
|
ref={inputRef}
|
||||||
/>
|
/>
|
||||||
<MemoDisplaySettingMenu className="absolute right-2 top-2 text-sidebar-foreground" />
|
<MemoDisplaySettingMenu className="absolute right-2 top-2 text-sidebar-foreground" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue