feat: enter key to submit dialog
This commit is contained in:
parent
9cd8c0f18d
commit
88b43829bc
8 changed files with 41 additions and 18 deletions
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
2
src/types.d.ts
vendored
|
|
@ -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<
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue