From 75fd00fb7f52958a68e3f1182a2faa6483da43d5 Mon Sep 17 00:00:00 2001 From: Benjamin Sherriff Date: Tue, 24 Oct 2023 16:02:48 -0400 Subject: [PATCH] Added tile controls --- ui/src/app/page.tsx | 2 +- ui/src/components/Header/header.css | 1 + ui/src/components/TileGrid/TileControls.tsx | 168 ++++++++++++++++++++ ui/src/components/TileGrid/index.tsx | 109 +++++++++---- 4 files changed, 246 insertions(+), 34 deletions(-) create mode 100644 ui/src/components/TileGrid/TileControls.tsx diff --git a/ui/src/app/page.tsx b/ui/src/app/page.tsx index 78da4b0..d4fc22c 100644 --- a/ui/src/app/page.tsx +++ b/ui/src/app/page.tsx @@ -4,7 +4,7 @@ import React from 'react'; // Home page for siren export default function Page() { return ( -
+
); diff --git a/ui/src/components/Header/header.css b/ui/src/components/Header/header.css index 4ba9397..dabaca1 100644 --- a/ui/src/components/Header/header.css +++ b/ui/src/components/Header/header.css @@ -4,6 +4,7 @@ color: black; border-bottom: 1px solid #e6e6e6; max-height: 70px; + user-select: none; } .navbar .left { diff --git a/ui/src/components/TileGrid/TileControls.tsx b/ui/src/components/TileGrid/TileControls.tsx new file mode 100644 index 0000000..516b572 --- /dev/null +++ b/ui/src/components/TileGrid/TileControls.tsx @@ -0,0 +1,168 @@ +import { ActionIcon, Box, ColorPicker, Menu } from '@mantine/core'; +import { FaSquare, FaCircle, FaHandPaper, FaRegCircle } from 'react-icons/fa'; +import { FaMagnifyingGlass, FaPencil } from 'react-icons/fa6'; + +export enum Tool { + HAND, + ZOOM, + EDIT, + TOKEN +} + +export enum EditTool { + SQUARE, + CIRCLE +} + +export const defaultColors = [ + '#000000', + '#1D2B53', + '#7E2553', + '#008751', + '#AB5236', + '#5F574F', + '#C2C3C7', + '#FFF1E8', + '#FF004D' +]; + +interface TileControlsProps { + tool: Tool; + setTool: (tool: Tool) => void; + editTool: EditTool; + setEditTool: (editTool: EditTool) => void; + colors: string[]; + setColors: (colors: string[]) => void; + selectedColor: number; + setSelectedColor: (selectedColor: number) => void; +} + +export default function TileControls({ + tool, + setTool, + editTool, + setEditTool, + colors, + setColors, + selectedColor, + setSelectedColor +}: TileControlsProps) { + window.addEventListener( + 'keydown', + (e) => { + if (e.key === ' ') { + setTool(Tool.HAND); + } else if (e.key === 'z') { + setTool(Tool.ZOOM); + } else if (e.key === 'e') { + setTool(Tool.EDIT); + } else if (e.key === 't') { + setTool(Tool.TOKEN); + } else if (e.key === '1') { + setSelectedColor(0); + } else if (e.key === '2') { + setSelectedColor(1); + } else if (e.key === '3') { + setSelectedColor(2); + } else if (e.key === '4') { + setSelectedColor(3); + } else if (e.key === '5') { + setSelectedColor(4); + } else if (e.key === '6') { + setSelectedColor(5); + } else if (e.key === '7') { + setSelectedColor(6); + } else if (e.key === '8') { + setSelectedColor(7); + } else if (e.key === '9') { + setSelectedColor(8); + } + }, + { passive: false } + ); + + function checkIfColorIsDark(color: string) { + // If the color is dark, return white, otherwise return black + const r = parseInt(color.slice(1, 3), 16); + const g = parseInt(color.slice(3, 5), 16); + const b = parseInt(color.slice(5, 7), 16); + const brightness = (r * 299 + g * 587 + b * 114) / 1000; + return brightness < 128 ? '#ffffff' : '#000000'; + } + + return ( + + {tool === Tool.EDIT && ( + + setEditTool(EditTool.SQUARE)} + > + + + setEditTool(EditTool.CIRCLE)} + > + + + + )} + + setTool(Tool.HAND)}> + + + setTool(Tool.ZOOM)}> + + + setTool(Tool.EDIT)}> + + + setTool(Tool.TOKEN)}> + + + + + {colors.map((color, index) => ( + + + setSelectedColor(index)} + > + + {index + 1} + + + + + { + const newColors = [...colors]; + newColors[index] = v; + setColors(newColors); + }} + /> + + + ))} + + + ); +} diff --git a/ui/src/components/TileGrid/index.tsx b/ui/src/components/TileGrid/index.tsx index c186348..5823da1 100644 --- a/ui/src/components/TileGrid/index.tsx +++ b/ui/src/components/TileGrid/index.tsx @@ -1,25 +1,45 @@ 'use client'; -import { Graphics, Stage } from '@pixi/react'; +import { Container, Graphics, Stage } from '@pixi/react'; import { Graphics as PixiGraphics } from '@pixi/graphics'; -import { MouseEvent, WheelEvent, useCallback, useState } from 'react'; - -// export default function TileGrid({ width, height }: TileGridProps) { -export default function TIleGrid() { - const [mouseDown, setMouseDown] = useState(false); - const [lastPosition, setLastPosition] = useState({ x: 0, y: 0 }); - const [position, setPosition] = useState({ x: 0, y: 0 }); +import { MouseEvent, WheelEvent, useCallback, useEffect, useState } from 'react'; +import TileControls, { EditTool, Tool, defaultColors } from './TileControls'; +export default function TileGrid() { // Offset height of navbar from window height - const height = window.innerHeight - 75; + const height = window ? window.innerHeight - 75 : 0; // Offset width of layout padding from window width - const width = window.innerWidth; + const width = window ? window.innerWidth : 0; + + const [scale, setScale] = useState(1); + const [zoom, setZoom] = useState(1); + const [size, setSize] = useState({ width: width * 2, height: height * 2 }); + const [boundaries, setBoundaries] = useState({ top: 0, bottom: 0, left: 0, right: 0 }); + const [mouseDown, setMouseDown] = useState(false); + const [lastPosition, setLastPosition] = useState({ x: -width / 2, y: -height / 2 }); + const [position, setPosition] = useState({ x: -width / 2, y: -height / 2 }); + const [tool, setTool] = useState(Tool.HAND); + const [editTool, setEditTool] = useState(EditTool.SQUARE); + const [colors, setColors] = useState(defaultColors); + const [selectedColor, setSelectedColor] = useState(0); + + useEffect(() => { + if (window) { + window.addEventListener( + 'wheel', + (event) => { + event.preventDefault(); + }, + { passive: false } + ); + } + }, []); const draw = useCallback((g: PixiGraphics) => { g.clear(); // Draw dot in the corner of each tile - for (let x = 0; x < width; x += 32) { - for (let y = 0; y < height; y += 32) { + for (let x = 0; x < size.width; x += 32 * scale) { + for (let y = 0; y < size.height; y += 32 * scale) { g.beginFill(0xffffff, 0.5); g.drawCircle(x, y, 1); g.endFill(); @@ -27,37 +47,60 @@ export default function TIleGrid() { } }, []); - function pan(e: MouseEvent) { + function clickEvent(e: MouseEvent) { + if (tool === Tool.HAND) { + setMouseDown(true); + setLastPosition({ x: e.clientX, y: e.clientY }); + } + } + + function moveEvent(e: MouseEvent) { if (mouseDown) { const dx = position.x + e.clientX - lastPosition.x; const dy = position.y + e.clientY - lastPosition.y; + // TODO: Boundaries setPosition({ x: dx, y: dy }); setLastPosition({ x: e.clientX, y: e.clientY }); } } - function zoom(e: WheelEvent) { - console.log('zoom', e); + function zoomEvent(e: WheelEvent) { + const delta = e.deltaY; + if (delta > 0) { + setZoom(zoom / 1.1); + } else { + setZoom(zoom * 1.1); + } + setScale(scale * zoom); } return ( - { - setMouseDown(true); - setLastPosition({ x: e.clientX, y: e.clientY }); - }} - onMouseUp={() => setMouseDown(false)} - onMouseMove={(e) => pan(e)} - onWheel={(e) => zoom(e)} - onClick={(e) => console.log(e)} - > - - + <> + clickEvent(e)} + onMouseUp={() => setMouseDown(false)} + onMouseMove={(e) => moveEvent(e)} + onWheel={(e) => zoomEvent(e)} + > + + + + + ); }