feat: enter key to submit dialog

This commit is contained in:
trafficlunar 2025-01-29 20:31:13 +00:00
parent 9cd8c0f18d
commit 88b43829bc
8 changed files with 41 additions and 18 deletions

View file

@ -10,7 +10,7 @@ import { Input } from "@/components/ui/input";
import { useTextures } from "@/hooks/useTextures"; import { useTextures } from "@/hooks/useTextures";
function SaveImage({ close }: DialogProps) { function SaveImage({ close, registerSubmit, dialogKeyHandler }: DialogProps) {
const { blocks, canvasSize, version } = useContext(CanvasContext); const { blocks, canvasSize, version } = useContext(CanvasContext);
const { missingTexture } = useContext(TexturesContext); const { missingTexture } = useContext(TexturesContext);
@ -59,15 +59,17 @@ function SaveImage({ close }: DialogProps) {
close(); close();
}; };
registerSubmit(onSubmit);
return ( return (
<DialogContent> <DialogContent onKeyDown={dialogKeyHandler}>
<DialogHeader> <DialogHeader>
<DialogTitle>Save as image (.png)</DialogTitle> <DialogTitle>Save as image (.png)</DialogTitle>
<DialogDescription>Save your canvas as a full size image</DialogDescription> <DialogDescription>Save your canvas as a full size image</DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Input value={fileName} onChange={(e) => setFileName(e.target.value)} /> <Input value={fileName} onChange={(e) => setFileName(e.target.value)} autoFocus />
<span>.png</span> <span>.png</span>
</div> </div>

View file

@ -19,7 +19,7 @@ type BlockStatePalette = {
Properties?: Record<string, string>; Properties?: Record<string, string>;
}[]; }[];
function SaveLitematic({ close }: DialogProps) { function SaveLitematic({ close, registerSubmit, dialogKeyHandler }: DialogProps) {
const { canvasSize, blocks, version } = useContext(CanvasContext); const { canvasSize, blocks, version } = useContext(CanvasContext);
const { setLoading } = useContext(LoadingContext); const { setLoading } = useContext(LoadingContext);
@ -164,15 +164,17 @@ function SaveLitematic({ close }: DialogProps) {
close(); close();
}; };
registerSubmit(onSubmit);
return ( return (
<DialogContent> <DialogContent onKeyDown={dialogKeyHandler}>
<DialogHeader> <DialogHeader>
<DialogTitle>Save as .litematic</DialogTitle> <DialogTitle>Save as .litematic</DialogTitle>
<DialogDescription>Save your image as a litematic</DialogDescription> <DialogDescription>Save your image as a litematic</DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Input value={fileName} onChange={(e) => setFileName(e.target.value)} /> <Input value={fileName} onChange={(e) => setFileName(e.target.value)} autoFocus />
<span>.litematic</span> <span>.litematic</span>
</div> </div>

View file

@ -43,7 +43,7 @@ const blockEntitiesWhitelist = [
"vault", "vault",
]; ];
function SaveSchem({ close }: DialogProps) { function SaveSchem({ close, registerSubmit, dialogKeyHandler }: DialogProps) {
const { canvasSize, blocks, version } = useContext(CanvasContext); const { canvasSize, blocks, version } = useContext(CanvasContext);
const { setLoading } = useContext(LoadingContext); const { setLoading } = useContext(LoadingContext);
@ -166,15 +166,17 @@ function SaveSchem({ close }: DialogProps) {
close(); close();
}; };
registerSubmit(onSubmit);
return ( return (
<DialogContent> <DialogContent onKeyDown={dialogKeyHandler}>
<DialogHeader> <DialogHeader>
<DialogTitle>Save as .schem</DialogTitle> <DialogTitle>Save as .schem</DialogTitle>
<DialogDescription>Save your image as a .schem (Sponge Version 3)</DialogDescription> <DialogDescription>Save your image as a .schem (Sponge Version 3)</DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Input value={fileName} onChange={(e) => setFileName(e.target.value)} /> <Input value={fileName} onChange={(e) => setFileName(e.target.value)} autoFocus />
<span>.schem</span> <span>.schem</span>
</div> </div>

View file

@ -7,7 +7,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
function SetCoords({ close }: DialogProps) { function SetCoords({ close, registerSubmit, dialogKeyHandler }: DialogProps) {
const { stageSize, coords, scale, setCoords } = useContext(CanvasContext); const { stageSize, coords, scale, setCoords } = useContext(CanvasContext);
const [newTilemapCoords, setNewTilemapCoords] = useState(coords); const [newTilemapCoords, setNewTilemapCoords] = useState(coords);
@ -30,6 +30,8 @@ function SetCoords({ close }: DialogProps) {
close(); close();
}; };
registerSubmit(onSubmit);
// Sets the current coordinates when the dialog is first opened // Sets the current coordinates when the dialog is first opened
useEffect(() => { useEffect(() => {
// Get center of screen // Get center of screen
@ -47,7 +49,7 @@ function SetCoords({ close }: DialogProps) {
}, []); }, []);
return ( return (
<DialogContent> <DialogContent onKeyDown={dialogKeyHandler}>
<DialogHeader> <DialogHeader>
<DialogTitle>Set Coordinates</DialogTitle> <DialogTitle>Set Coordinates</DialogTitle>
<DialogDescription>Set your coordinates to a particular position</DialogDescription> <DialogDescription>Set your coordinates to a particular position</DialogDescription>
@ -62,6 +64,7 @@ function SetCoords({ close }: DialogProps) {
placeholder="X" placeholder="X"
value={newTilemapCoords.x} value={newTilemapCoords.x}
onChange={(e) => setNewTilemapCoords((prev) => ({ ...prev, x: parseInt(e.target.value) }))} onChange={(e) => setNewTilemapCoords((prev) => ({ ...prev, x: parseInt(e.target.value) }))}
autoFocus
/> />
</div> </div>

View file

@ -6,7 +6,7 @@ import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTit
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
function SetScale({ close }: DialogProps) { function SetScale({ close, registerSubmit, dialogKeyHandler }: DialogProps) {
const { scale, setScale } = useContext(CanvasContext); const { scale, setScale } = useContext(CanvasContext);
const [newScale, setNewScale] = useState(scale * 100); const [newScale, setNewScale] = useState(scale * 100);
@ -16,14 +16,16 @@ function SetScale({ close }: DialogProps) {
close(); close();
}; };
registerSubmit(onSubmit);
return ( return (
<DialogContent> <DialogContent onKeyDown={dialogKeyHandler}>
<DialogHeader> <DialogHeader>
<DialogTitle>Set Scale</DialogTitle> <DialogTitle>Set Scale</DialogTitle>
<DialogDescription>Set your scale to a particular percentage</DialogDescription> <DialogDescription>Set your scale to a particular percentage</DialogDescription>
</DialogHeader> </DialogHeader>
<Input type="number" value={newScale} onChange={(e) => setNewScale(parseInt(e.target.value))} /> <Input type="number" value={newScale} onChange={(e) => setNewScale(parseInt(e.target.value))} autoFocus />
<DialogFooter> <DialogFooter>
<Button variant="outline" onClick={close}> <Button variant="outline" onClick={close}>

View file

@ -29,7 +29,7 @@ function SetVersion({ close }: DialogProps) {
<Button variant="outline" onClick={close}> <Button variant="outline" onClick={close}>
Cancel Cancel
</Button> </Button>
<Button type="submit" onClick={onSubmit}> <Button type="submit" onClick={onSubmit} autoFocus>
Set Set
</Button> </Button>
</DialogFooter> </DialogFooter>

View file

@ -1,5 +1,5 @@
import { Dialog } from "@/components/ui/dialog"; import { Dialog } from "@/components/ui/dialog";
import { createContext, lazy, ReactNode, Suspense, useState } from "react"; import { createContext, lazy, ReactNode, Suspense, useRef, useState } from "react";
type Context = (id: string) => void; type Context = (id: string) => void;
@ -13,19 +13,29 @@ export const DialogProvider = ({ children }: Props) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [id, setId] = useState(""); const [id, setId] = useState("");
const onSubmitRef = useRef<(() => void) | null>(null);
const openDialog = (id: string) => { const openDialog = (id: string) => {
setId(id); setId(id);
setOpen(true); setOpen(true);
}; };
const LazyDialogContent = id ? lazy(() => import(`@/components/dialogs/${id}.tsx`)) : null; const dialogKeyHandler = (e: React.KeyboardEvent) => {
if (!onSubmitRef.current) return;
if (e.key !== "Enter") return;
onSubmitRef.current();
close();
};
const LazyDialogContent = id ? lazy<React.ComponentType<DialogProps>>(() => import(`@/components/dialogs/${id}.tsx`)) : null;
return ( return (
<DialogContext.Provider value={openDialog}> <DialogContext.Provider value={openDialog}>
<Dialog open={open} onOpenChange={(value) => setOpen(value)}> <Dialog open={open} onOpenChange={(value) => setOpen(value)}>
{LazyDialogContent && ( {LazyDialogContent && (
<Suspense fallback={<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">Loading dialog...</div>}> <Suspense fallback={<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">Loading dialog...</div>}>
<LazyDialogContent close={() => setOpen(false)} /> <LazyDialogContent close={() => setOpen(false)} registerSubmit={(fn) => (onSubmitRef.current = fn)} dialogKeyHandler={dialogKeyHandler} />
</Suspense> </Suspense>
)} )}
</Dialog> </Dialog>

2
src/types.d.ts vendored
View file

@ -36,6 +36,8 @@ interface Settings {
interface DialogProps { interface DialogProps {
close: () => void; close: () => void;
registerSubmit: (fn: () => void) => void;
dialogKeyHandler: (e: React.KeyboardEvent) => void;
} }
type BlockData = Record< type BlockData = Record<