feat: resizable tool settings window
This commit is contained in:
parent
c8cccd8bf7
commit
acfd638d2c
4 changed files with 77 additions and 16 deletions
|
|
@ -267,24 +267,30 @@ function Canvas() {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const container = stageContainerRef.current;
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
const resizeCanvas = () => {
|
const resizeCanvas = () => {
|
||||||
if (stageContainerRef.current) {
|
|
||||||
setStageSize({
|
setStageSize({
|
||||||
width: stageContainerRef.current.offsetWidth,
|
width: container.offsetWidth,
|
||||||
height: stageContainerRef.current.offsetHeight,
|
height: container.offsetHeight,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver(resizeCanvas);
|
||||||
|
resizeObserver.observe(container);
|
||||||
|
|
||||||
resizeCanvas();
|
resizeCanvas();
|
||||||
|
return () => resizeObserver.disconnect();
|
||||||
|
}, [stageContainerRef]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
setBlocks(welcomeBlocksData);
|
setBlocks(welcomeBlocksData);
|
||||||
|
|
||||||
window.addEventListener("resize", resizeCanvas);
|
|
||||||
window.addEventListener("keydown", onKeyDown);
|
window.addEventListener("keydown", onKeyDown);
|
||||||
window.addEventListener("keyup", onKeyUp);
|
window.addEventListener("keyup", onKeyUp);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("resize", resizeCanvas);
|
|
||||||
window.removeEventListener("keydown", onKeyDown);
|
window.removeEventListener("keydown", onKeyDown);
|
||||||
window.removeEventListener("keyup", onKeyUp);
|
window.removeEventListener("keyup", onKeyUp);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ function BlockSelector({ stageWidth, searchInput }: Props) {
|
||||||
return (
|
return (
|
||||||
<Stage
|
<Stage
|
||||||
width={stageWidth}
|
width={stageWidth}
|
||||||
height={Math.ceil(Object.keys(blockData).length / blocksPerColumn) * (32 + 2)}
|
height={Math.ceil(Object.keys(blockData).length / blocksPerColumn) * (32 + 2) + 8}
|
||||||
options={{ backgroundAlpha: 0 }}
|
options={{ backgroundAlpha: 0 }}
|
||||||
onMouseLeave={() => setHoverPosition(null)}
|
onMouseLeave={() => setHoverPosition(null)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -4,29 +4,84 @@ import { SettingsContext } from "@/context/Settings";
|
||||||
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
|
||||||
import ColorPicker from "./ColorPicker";
|
import ColorPicker from "./ColorPicker";
|
||||||
import Replace from "./Replace";
|
import Replace from "./Replace";
|
||||||
import Radius from "./Radius";
|
import Radius from "./Radius";
|
||||||
import BlockSelector from "./BlockSelector";
|
import BlockSelector from "./BlockSelector";
|
||||||
import { ScrollArea } from "../ui/scroll-area";
|
import { GripVerticalIcon } from "lucide-react";
|
||||||
|
|
||||||
function ToolSettings() {
|
function ToolSettings() {
|
||||||
const { settings } = useContext(SettingsContext);
|
const { settings } = useContext(SettingsContext);
|
||||||
|
|
||||||
|
const [width, setWidth] = useState(288);
|
||||||
|
const [resizing, setResizing] = useState(false);
|
||||||
|
|
||||||
const divRef = useRef<HTMLDivElement>(null);
|
const divRef = useRef<HTMLDivElement>(null);
|
||||||
const [stageWidth, setStageWidth] = useState(0);
|
const [stageWidth, setStageWidth] = useState(0);
|
||||||
const [searchInput, setSearchInput] = useState("");
|
const [searchInput, setSearchInput] = useState("");
|
||||||
|
|
||||||
|
const onMouseDown = () => {
|
||||||
|
setResizing(true);
|
||||||
|
document.body.style.cursor = "ew-resize";
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
setResizing(false);
|
||||||
|
document.body.style.cursor = "auto";
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove = (e: MouseEvent) => {
|
||||||
|
if (!resizing) return;
|
||||||
|
setWidth(() => Math.min(Math.max(window.innerWidth - e.clientX, 200), 1000));
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!divRef.current) return;
|
if (resizing) {
|
||||||
setStageWidth(divRef.current.clientWidth);
|
document.addEventListener("mousemove", onMouseMove);
|
||||||
}, []);
|
document.addEventListener("mouseup", onMouseUp);
|
||||||
|
} else {
|
||||||
|
document.removeEventListener("mousemove", onMouseMove);
|
||||||
|
document.removeEventListener("mouseup", onMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousemove", onMouseMove);
|
||||||
|
document.removeEventListener("mouseup", onMouseUp);
|
||||||
|
};
|
||||||
|
}, [resizing]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const div = divRef.current;
|
||||||
|
if (!div) return;
|
||||||
|
|
||||||
|
const set = () => setStageWidth(div.clientWidth);
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver(set);
|
||||||
|
resizeObserver.observe(div);
|
||||||
|
|
||||||
|
set();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
};
|
||||||
|
}, [divRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{(settings.colorPicker || settings.blockReplacer || settings.radiusChanger || settings.blockSelector) && (
|
{(settings.colorPicker || settings.blockReplacer || settings.radiusChanger || settings.blockSelector) && (
|
||||||
<div className="w-72 border-l border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-2 pb-0 flex flex-col h-full gap-2">
|
<div
|
||||||
|
style={{ width: `${width}px` }}
|
||||||
|
className="w-72 border-l border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-2 pb-0 flex flex-col h-full gap-2 relative"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
onMouseDown={onMouseDown}
|
||||||
|
className="absolute top-0 -left-2 h-full w-4 bg-zinc-300 dark:bg-zinc-800 flex justify-center items-center cursor-e-resize opacity-0 transition-opacity duration-300 delay-100 hover:opacity-100"
|
||||||
|
>
|
||||||
|
<GripVerticalIcon />
|
||||||
|
</div>
|
||||||
|
|
||||||
{settings.colorPicker && (
|
{settings.colorPicker && (
|
||||||
<>
|
<>
|
||||||
<ColorPicker />
|
<ColorPicker />
|
||||||
|
|
@ -51,7 +106,7 @@ function ToolSettings() {
|
||||||
{settings.blockSelector && (
|
{settings.blockSelector && (
|
||||||
<>
|
<>
|
||||||
<Input placeholder="Search for blocks..." value={searchInput} onChange={(e) => setSearchInput(e.target.value)} />
|
<Input placeholder="Search for blocks..." value={searchInput} onChange={(e) => setSearchInput(e.target.value)} />
|
||||||
<ScrollArea ref={divRef} className="w-full flex-1 pb-2">
|
<ScrollArea ref={divRef} className="w-full flex-1 pb-0">
|
||||||
<BlockSelector stageWidth={stageWidth} searchInput={searchInput} />
|
<BlockSelector stageWidth={stageWidth} searchInput={searchInput} />
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ function AppPage() {
|
||||||
<SettingsProvider>
|
<SettingsProvider>
|
||||||
<TexturesProvider>
|
<TexturesProvider>
|
||||||
<ToolProvider>
|
<ToolProvider>
|
||||||
<main className="h-screen grid grid-rows-[2.5rem_minmax(0,1fr)] grid-cols-[2.5rem_1fr_auto]">
|
<main className="h-screen grid grid-rows-[2.5rem_minmax(0,1fr)] grid-cols-[2.5rem_minmax(0,1fr)_auto]">
|
||||||
<Menubar />
|
<Menubar />
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
<Canvas />
|
<Canvas />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue