implement edit dialog mnemonics

This commit is contained in:
2025-05-27 16:47:46 +01:00
parent 7bddab0619
commit 347451dbbc
2 changed files with 65 additions and 4 deletions

View File

@@ -33,6 +33,9 @@ function AddBookmarkDialog() {
{ ignore: () => false }, { 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(() => { useEffect(() => {
setTimeout(() => { setTimeout(() => {
if (linkInputRef.current) { if (linkInputRef.current) {

View File

@@ -1,5 +1,5 @@
import type { Bookmark, Tag } from "@markone/core" 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 { useBookmarkTags, useUpdateBookmark } from "~/bookmark/api"
import { Button } from "~/components/button" import { Button } from "~/components/button"
import { Dialog, DialogActionRow, DialogBody, DialogTitle } from "~/components/dialog" 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 { LoadingSpinner } from "~/components/loading-spinner"
import { Message, MessageVariant } from "~/components/message.tsx" import { Message, MessageVariant } from "~/components/message.tsx"
import { TagsInput, type TagsInputRef } from "~/components/tags-input" import { TagsInput, type TagsInputRef } from "~/components/tags-input"
import { useMnemonics } from "~/hooks/use-mnemonics.ts"
import { useBookmarkPageStore } from "../-store" import { useBookmarkPageStore } from "../-store"
interface EditFormRef {
form: HTMLFormElement | null
titleInput: HTMLInputElement | null
tagsInput: TagsInputRef | null
}
function EditBookmarkDialog({ bookmark }: { bookmark: Bookmark }) { function EditBookmarkDialog({ bookmark }: { bookmark: Bookmark }) {
const closeDialog = useBookmarkPageStore((state) => state.closeDialog) const closeDialog = useBookmarkPageStore((state) => state.closeDialog)
const { data: tags, status } = useBookmarkTags(bookmark) const { data: tags, status } = useBookmarkTags(bookmark)
const editFormId = useId() const editFormId = useId()
const editFormRef = useRef<EditFormRef | null>(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() { function content() {
switch (status) { switch (status) {
@@ -23,7 +58,7 @@ function EditBookmarkDialog({ bookmark }: { bookmark: Bookmark }) {
</p> </p>
) )
case "success": case "success":
return <EditForm formId={editFormId} bookmark={bookmark} tags={tags} /> return <EditForm ref={editFormRef} formId={editFormId} bookmark={bookmark} tags={tags} />
case "error": case "error":
return null 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<EditFormRef>; formId: string; bookmark: Bookmark; tags: Tag[] }) {
const formRef = useRef<HTMLFormElement | null>(null)
const titleInputRef = useRef<HTMLInputElement | null>(null)
const tagsInputRef = useRef<TagsInputRef>(null) const tagsInputRef = useRef<TagsInputRef>(null)
const updateBookmarkMutation = useUpdateBookmark(bookmark) const updateBookmarkMutation = useUpdateBookmark(bookmark)
const closeDialog = useBookmarkPageStore((state) => state.closeDialog) 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<HTMLFormElement>) { async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
if (tagsInputRef.current) { if (tagsInputRef.current) {
event.preventDefault() event.preventDefault()
@@ -86,8 +143,9 @@ function EditForm({ formId, bookmark, tags }: { formId: string; bookmark: Bookma
return ( return (
<> <>
{message()} {message()}
<form id={formId} onSubmit={onSubmit}> <form ref={formRef} id={formId} onSubmit={onSubmit}>
<FormField <FormField
ref={titleInputRef}
type="text" type="text"
name="title" name="title"
label="TITLE" label="TITLE"