feat: add toolbar

This commit is contained in:
trafficlunar 2024-12-05 21:17:46 +00:00
parent c836c26a05
commit f0445c208b
8 changed files with 199 additions and 18 deletions

View file

@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from "react";
import { Layer, Stage } from "react-konva";
import { Hand } from "lucide-react";
import {
Menubar,
@ -12,6 +13,7 @@ import {
MenubarSubTrigger,
MenubarTrigger,
} from "@/components/ui/menubar";
import { ToggleGroup, ToggleGroupItem } from "./components/ui/toggle-group";
import ThemeChanger from "./components/menubar/theme-changer";
import Blocks from "./components/blocks";
@ -31,6 +33,7 @@ function App() {
const [stageCoords, setStageCoords] = useState<Position>({ x: 0, y: 0 });
const [mousePosition, setMousePosition] = useState<Position>({ x: 0, y: 0 });
const [tool, setTool] = useState<Tool>("hand");
const [blocks, setBlocks] = useState<Block[]>([]);
const onMouseMove = (e) => {
@ -68,10 +71,12 @@ function App() {
}, []);
return (
<main className="h-screen grid grid-rows-[2.5rem_1fr]">
<Menubar className="rounded-none border-t-0 border-x-0">
<main className="h-screen grid grid-rows-[2.5rem_1fr] grid-cols-[2.5rem_1fr]">
<Menubar className="rounded-none border-t-0 border-x-0 col-span-2">
<MenubarMenu>
<a href="https://github.com/trafficlunar/blockmatic" className="ml-4 mr-2">blockmatic</a>
<a href="https://github.com/trafficlunar/blockmatic" className="ml-4 mr-2">
blockmatic
</a>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
@ -99,6 +104,12 @@ function App() {
</MenubarMenu>
</Menubar>
<ToggleGroup type="single" size={"sm"} value={tool} className="flex flex-col justify-start py-0.5 border-r border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-950">
<ToggleGroupItem value="hand">
<Hand />
</ToggleGroupItem>
</ToggleGroup>
<div ref={stageContainerRef} className="relative w-full h-full">
<Stage
width={stageSize.width}

View file

@ -3,7 +3,7 @@ import { Image as KonvaImage } from "react-konva";
import blocksData from "@/lib/blocks/programmer-art/average_colors.json";
function Blocks({ blocks, setBlocks }: { blocks: Block[], setBlocks: React.Dispatch<React.SetStateAction<Block[]>> }) {
function Blocks({ blocks, setBlocks }: { blocks: Block[]; setBlocks: React.Dispatch<React.SetStateAction<Block[]>> }) {
const [images, setImages] = useState<{ [key: string]: HTMLImageElement }>({});
const findClosestBlock = (r: number, g: number, b: number) => {
@ -52,12 +52,12 @@ function Blocks({ blocks, setBlocks }: { blocks: Block[], setBlocks: React.Dispa
const block = findClosestBlock(imageData.data[i], imageData.data[i + 1], imageData.data[i + 2]);
const x = Math.floor((i / 4) % imageData.width);
const y = Math.floor((i / 4) / imageData.width);
const y = Math.floor(i / 4 / imageData.width);
newBlocks.push({
name: block,
x,
y
y,
});
}

View file

@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
function CursorInformation({ mousePosition, blocks }: { mousePosition: Position, blocks: Block[] }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
function CursorInformation({ mousePosition, blocks }: { mousePosition: Position; blocks: Block[] }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [block, setBlock] = useState<Block>();
useEffect(() => {
@ -14,18 +14,22 @@ function CursorInformation({ mousePosition, blocks }: { mousePosition: Position,
y: snappedY,
});
setBlock(blocks.find(b => b.x === snappedX && b.y === snappedY));
setBlock(blocks.find((b) => b.x === snappedX && b.y === snappedY));
}
}, [mousePosition]);
return <div className="absolute left-4 bottom-4 flex flex-col gap-1">
<div className="bg-zinc-900 px-2 py-1 rounded shadow-xl w-fit">{block?.name ?? "air"}</div>
return (
<div className="absolute left-4 bottom-4 flex flex-col gap-1">
<div className="bg-white dark:bg-zinc-950 px-2 py-1 rounded shadow-xl w-fit border border-zinc-200 dark:border-zinc-800">
{block?.name ?? "air"}
</div>
<div className="flex gap-4 bg-zinc-900 px-2 py-1 rounded shadow-xl w-fit">
<span>X: {position.x}</span>
<span>Y: {position.y}</span>
</div>
</div>
<div className="flex gap-4 bg-white dark:bg-zinc-950 px-2 py-1 rounded shadow-xl w-fit border border-zinc-200 dark:border-zinc-800">
<span>X: {position.x}</span>
<span>Y: {position.y}</span>
</div>
</div>
);
}
export default CursorInformation
export default CursorInformation;

View file

@ -0,0 +1,59 @@
import * as React from "react"
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
import { type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
import { toggleVariants } from "@/components/ui/toggle"
const ToggleGroupContext = React.createContext<
VariantProps<typeof toggleVariants>
>({
size: "default",
variant: "default",
})
const ToggleGroup = React.forwardRef<
React.ElementRef<typeof ToggleGroupPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
VariantProps<typeof toggleVariants>
>(({ className, variant, size, children, ...props }, ref) => (
<ToggleGroupPrimitive.Root
ref={ref}
className={cn("flex items-center justify-center gap-1", className)}
{...props}
>
<ToggleGroupContext.Provider value={{ variant, size }}>
{children}
</ToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root>
))
ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
const ToggleGroupItem = React.forwardRef<
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
VariantProps<typeof toggleVariants>
>(({ className, children, variant, size, ...props }, ref) => {
const context = React.useContext(ToggleGroupContext)
return (
<ToggleGroupPrimitive.Item
ref={ref}
className={cn(
toggleVariants({
variant: context.variant || variant,
size: context.size || size,
}),
className
)}
{...props}
>
{children}
</ToggleGroupPrimitive.Item>
)
})
ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName
export { ToggleGroup, ToggleGroupItem }

View file

@ -0,0 +1,45 @@
"use client"
import * as React from "react"
import * as TogglePrimitive from "@radix-ui/react-toggle"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-white transition-colors hover:bg-zinc-100 hover:text-zinc-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-zinc-100 data-[state=on]:text-zinc-900 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2 dark:ring-offset-zinc-950 dark:hover:bg-zinc-800 dark:hover:text-zinc-400 dark:focus-visible:ring-zinc-300 dark:data-[state=on]:bg-zinc-800 dark:data-[state=on]:text-zinc-50",
{
variants: {
variant: {
default: "bg-transparent",
outline:
"border border-zinc-200 bg-transparent hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-50",
},
size: {
default: "h-10 px-3 min-w-10",
sm: "h-9 px-2.5 min-w-9",
lg: "h-11 px-5 min-w-11",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
const Toggle = React.forwardRef<
React.ElementRef<typeof TogglePrimitive.Root>,
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
VariantProps<typeof toggleVariants>
>(({ className, variant, size, ...props }, ref) => (
<TogglePrimitive.Root
ref={ref}
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
))
Toggle.displayName = TogglePrimitive.Root.displayName
export { Toggle, toggleVariants }

4
src/types.d.ts vendored
View file

@ -5,4 +5,6 @@ interface Position {
interface Block extends Position {
name: string;
}
}
type Tool = "hand";