fix keyboard shortcut not working in add dialog

This commit is contained in:
2025-05-21 15:47:07 +01:00
parent 2d03f1f31b
commit 8b82dd1408
3 changed files with 98 additions and 91 deletions

View File

@@ -1,11 +1,7 @@
import { Outlet, createFileRoute } from "@tanstack/react-router"
import { useEffect } from "react"
import { useDeleteBookmark } from "~/bookmark/api"
import { Button } from "~/components/button"
import { Dialog, DialogActionRow, DialogBody, DialogTitle } from "~/components/dialog"
import { LoadingSpinner } from "~/components/loading-spinner"
import { useMnemonics } from "~/hooks/use-mnemonics"
import { AddBookmarkDialog } from "./bookmarks/-dialogs/add-bookmark-dialog"
import { DeleteBookmarkDialog } from "./bookmarks/-dialogs/delete-bookmark-dialog"
import { ActiveDialog, LayoutMode, useBookmarkPageStore } from "./bookmarks/-store"
export const Route = createFileRoute("/bookmarks")({
@@ -53,84 +49,3 @@ function PageDialog() {
return <DeleteBookmarkDialog />
}
}
function DeleteBookmarkDialog() {
// biome-ignore lint/style/noNonNullAssertion: this cannot be null when delete bookmark dialog is visible
const bookmark = useBookmarkPageStore((state) => state.bookmarkToBeDeleted!)
const setActiveDialog = useBookmarkPageStore((state) => state.setActiveDialog)
const deleteBookmarkMutation = useDeleteBookmark()
useMnemonics(
{
y: proceed,
n: cancel,
},
{ ignore: () => false },
)
async function proceed() {
try {
await deleteBookmarkMutation.mutateAsync({ bookmark })
setActiveDialog(ActiveDialog.None)
} catch (error) {
console.error(error)
}
}
function cancel() {
setActiveDialog(ActiveDialog.None)
}
function body() {
switch (deleteBookmarkMutation.status) {
case "pending":
return (
<p>
Deleting <LoadingSpinner />
</p>
)
case "idle":
return (
<p>
The bookmark titled:
<br />
<br />
<strong>
<em>"{bookmark.title}"</em>
</strong>
<br />
<br />
will be deleted. Proceed?
</p>
)
case "error":
return <p className="text-red-500">Failed to delete the bookmark!</p>
}
}
function title() {
switch (deleteBookmarkMutation.status) {
case "pending":
return "PLEASE WAIT"
case "idle":
return "CONFIRM"
case "error":
return "ERROR OCCURRED"
}
}
return (
<Dialog>
<DialogTitle>{title()}</DialogTitle>
<DialogBody>{body()}</DialogBody>
<DialogActionRow>
<Button disabled={deleteBookmarkMutation.isPending} onClick={proceed}>
{deleteBookmarkMutation.isError ? "Retry" : "Proceed"} (y)
</Button>
<Button disabled={deleteBookmarkMutation.isPending} onClick={cancel}>
Cancel (n)
</Button>
</DialogActionRow>
</Dialog>
)
}

View File

@@ -3,7 +3,7 @@ import type { BookmarkTag } from "@markone/core/bookmark"
import clsx from "clsx"
import { atom, useAtom } from "jotai"
import { useAtomCallback } from "jotai/utils"
import { useCallback, useEffect, useId, useRef, useState } from "react"
import { useCallback, useEffect, useId, useImperativeHandle, useRef, useState } from "react"
import { ApiErrorCode, BadRequestError } from "~/api"
import { useCreateBookmark, useTags } from "~/bookmark/api"
import { Button } from "~/components/button"
@@ -36,6 +36,7 @@ function AddBookmarkDialog() {
const setActiveDialog = useBookmarkPageStore((state) => state.setActiveDialog)
const formId = useId()
const linkInputRef = useRef<HTMLInputElement | null>(null)
const tagsInputRef = useRef<HTMLInputElement | null>(null)
const getTags = useAtomCallback(
useCallback((get) => {
const value = get(tagsInputValueAtom)
@@ -46,7 +47,7 @@ function AddBookmarkDialog() {
useMnemonics(
{
c: () => {
if (!document.activeElement) {
if (linkInputRef.current !== document.activeElement && tagsInputRef.current !== document.activeElement) {
cancel()
}
},
@@ -127,7 +128,7 @@ function AddBookmarkDialog() {
className="w-full"
labelClassName="bg-stone-300 dark:bg-stone-800"
/>
<TagsInput />
<TagsInput ref={tagsInputRef} />
</form>
</DialogBody>
<DialogActionRow>
@@ -142,11 +143,11 @@ function AddBookmarkDialog() {
)
}
function TagsInput() {
function TagsInput({ ref }: { ref: React.Ref<HTMLInputElement | null> }) {
const [value, setValue] = useAtom(tagsInputValueAtom)
const [isInputFocused, setIsInputFocused] = useState(false)
const [lastTag] = useAtom(lastTagAtom)
const { refs, floatingStyles } = useFloating({
const { refs, floatingStyles } = useFloating<HTMLInputElement>({
whileElementsMounted: autoUpdate,
middleware: [
size({
@@ -160,6 +161,8 @@ function TagsInput() {
open: isInputFocused && lastTag !== "",
})
useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(ref, () => refs.reference.current)
return (
<>
<FormField

View File

@@ -0,0 +1,89 @@
import { useDeleteBookmark } from "~/bookmark/api"
import { Button } from "~/components/button"
import { Dialog, DialogTitle, DialogBody, DialogActionRow } from "~/components/dialog"
import { LoadingSpinner } from "~/components/loading-spinner"
import { useMnemonics } from "~/hooks/use-mnemonics"
import { useBookmarkPageStore, ActiveDialog } from "../-store"
function DeleteBookmarkDialog() {
// biome-ignore lint/style/noNonNullAssertion: this cannot be null when delete bookmark dialog is visible
const bookmark = useBookmarkPageStore((state) => state.bookmarkToBeDeleted!)
const setActiveDialog = useBookmarkPageStore((state) => state.setActiveDialog)
const deleteBookmarkMutation = useDeleteBookmark()
useMnemonics(
{
y: proceed,
n: cancel,
},
{ ignore: () => false },
)
async function proceed() {
try {
await deleteBookmarkMutation.mutateAsync({ bookmark })
setActiveDialog(ActiveDialog.None)
} catch (error) {
console.error(error)
}
}
function cancel() {
setActiveDialog(ActiveDialog.None)
}
function body() {
switch (deleteBookmarkMutation.status) {
case "pending":
return (
<p>
Deleting <LoadingSpinner />
</p>
)
case "idle":
return (
<p>
The bookmark titled:
<br />
<br />
<strong>
<em>"{bookmark.title}"</em>
</strong>
<br />
<br />
will be deleted. Proceed?
</p>
)
case "error":
return <p className="text-red-500">Failed to delete the bookmark!</p>
}
}
function title() {
switch (deleteBookmarkMutation.status) {
case "pending":
return "PLEASE WAIT"
case "idle":
return "CONFIRM"
case "error":
return "ERROR OCCURRED"
}
}
return (
<Dialog>
<DialogTitle>{title()}</DialogTitle>
<DialogBody>{body()}</DialogBody>
<DialogActionRow>
<Button disabled={deleteBookmarkMutation.isPending} onClick={proceed}>
{deleteBookmarkMutation.isError ? "Retry" : "Proceed"} (y)
</Button>
<Button disabled={deleteBookmarkMutation.isPending} onClick={cancel}>
Cancel (n)
</Button>
</DialogActionRow>
</Dialog>
)
}
export { DeleteBookmarkDialog }