diff --git a/packages/web/src/app/bookmarks/-dialogs/add-bookmark-dialog.tsx b/packages/web/src/app/bookmarks/-dialogs/add-bookmark-dialog.tsx index 1d29f9d..4fb6794 100644 --- a/packages/web/src/app/bookmarks/-dialogs/add-bookmark-dialog.tsx +++ b/packages/web/src/app/bookmarks/-dialogs/add-bookmark-dialog.tsx @@ -33,6 +33,9 @@ function AddBookmarkDialog() { { ignore: () => false }, ) + // when using autoFocus, it also captures the "a" mnemonic + // which appends the "a" to the input + // this is to prevent the "a" mnemonic to be captured as input to the text box useEffect(() => { setTimeout(() => { if (linkInputRef.current) { diff --git a/packages/web/src/app/bookmarks/-dialogs/edit-bookmark-dialog.tsx b/packages/web/src/app/bookmarks/-dialogs/edit-bookmark-dialog.tsx index a159e91..1cd763e 100644 --- a/packages/web/src/app/bookmarks/-dialogs/edit-bookmark-dialog.tsx +++ b/packages/web/src/app/bookmarks/-dialogs/edit-bookmark-dialog.tsx @@ -1,5 +1,5 @@ import type { Bookmark, Tag } from "@markone/core" -import { useId, useRef } from "react" +import { useEffect, useId, useImperativeHandle, useRef } from "react" import { useBookmarkTags, useUpdateBookmark } from "~/bookmark/api" import { Button } from "~/components/button" import { Dialog, DialogActionRow, DialogBody, DialogTitle } from "~/components/dialog" @@ -7,12 +7,47 @@ import { FormField } from "~/components/form-field" import { LoadingSpinner } from "~/components/loading-spinner" import { Message, MessageVariant } from "~/components/message.tsx" import { TagsInput, type TagsInputRef } from "~/components/tags-input" +import { useMnemonics } from "~/hooks/use-mnemonics.ts" import { useBookmarkPageStore } from "../-store" +interface EditFormRef { + form: HTMLFormElement | null + titleInput: HTMLInputElement | null + tagsInput: TagsInputRef | null +} + function EditBookmarkDialog({ bookmark }: { bookmark: Bookmark }) { const closeDialog = useBookmarkPageStore((state) => state.closeDialog) const { data: tags, status } = useBookmarkTags(bookmark) const editFormId = useId() + const editFormRef = useRef(null) + + useMnemonics( + { + s: () => { + if ( + editFormRef.current && + editFormRef.current.titleInput !== document.activeElement && + editFormRef.current.tagsInput?.input !== document.activeElement + ) { + editFormRef.current.form?.requestSubmit() + } + }, + c: () => { + if ( + editFormRef.current?.titleInput !== document.activeElement && + editFormRef.current?.tagsInput?.input !== document.activeElement + ) { + closeDialog() + } + }, + Escape: () => { + editFormRef.current?.titleInput?.blur() + editFormRef.current?.tagsInput?.input?.blur() + }, + }, + { ignore: () => false }, + ) function content() { switch (status) { @@ -23,7 +58,7 @@ function EditBookmarkDialog({ bookmark }: { bookmark: Bookmark }) {

) case "success": - return + return case "error": return null } @@ -45,11 +80,33 @@ function EditBookmarkDialog({ bookmark }: { bookmark: Bookmark }) { ) } -function EditForm({ formId, bookmark, tags }: { formId: string; bookmark: Bookmark; tags: Tag[] }) { +function EditForm({ + ref, + formId, + bookmark, + tags, +}: { ref: React.Ref; formId: string; bookmark: Bookmark; tags: Tag[] }) { + const formRef = useRef(null) + const titleInputRef = useRef(null) const tagsInputRef = useRef(null) const updateBookmarkMutation = useUpdateBookmark(bookmark) const closeDialog = useBookmarkPageStore((state) => state.closeDialog) + useImperativeHandle(ref, () => ({ + form: formRef.current, + titleInput: titleInputRef.current, + tagsInput: tagsInputRef.current, + })) + + // when using autoFocus, it also captures the "e" mnemonic + // which appends the "e" to the input + // this is to prevent the "e" mnemonic to be captured as input to the text box + useEffect(() => { + setTimeout(() => { + titleInputRef.current?.focus() + }, 0) + }, []) + async function onSubmit(event: React.FormEvent) { if (tagsInputRef.current) { event.preventDefault() @@ -86,8 +143,9 @@ function EditForm({ formId, bookmark, tags }: { formId: string; bookmark: Bookma return ( <> {message()} -
+