Working on layout, tilegrid

This commit is contained in:
Benjamin Sherriff
2023-10-31 08:48:21 -04:00
parent c5a6b762f0
commit 046bf51697
9 changed files with 155 additions and 76 deletions

View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page() {
return <></>;
}

View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page({ params }: { params: { id: string } }) {
return <>{params.id}</>;
}

View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page() {
return <h1>Create new Character</h1>;
}

View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page() {
return <></>;
}

View File

@@ -4,6 +4,16 @@ import React from 'react';
// Home page for siren // Home page for siren
export default function Page() { export default function Page() {
return ( return (
// <div>
// <p>Siren is a Dungeon Master's best friend.</p>
// <h2>Features:</h2>
// <ul>
// <li>Manage your campaign and players</li>
// <li>Create battlemaps on the fly and track initiative</li>
// <li>Connect the Discord Bot to play online with friends</li>
// <li>Reference Races, Classes, Items, Spells, and more</li>
// </ul>
// </div>
<div style={{ overflow: 'hidden' }}> <div style={{ overflow: 'hidden' }}>
<TileGrid /> <TileGrid />
</div> </div>

View File

@@ -1,36 +1,49 @@
export interface HeaderItem { export interface HeaderItem {
name: string; label: string;
link: string; link?: string;
role?: string; links?: HeaderItem[];
} }
export const headerItems: HeaderItem[] = [ export const headerItems: HeaderItem[] = [
{ {
name: 'Races', label: 'Campaigns',
link: '/campaigns'
},
{
label: 'Characters',
link: '/characters'
},
{
label: 'Resources',
links: [
{
label: 'Races',
link: '/races' link: '/races'
}, },
{ {
name: 'Classes', label: 'Classes',
link: '/classes' link: '/classes'
}, },
{ {
name: 'Feats', label: 'Feats',
link: '/feats' link: '/feats'
}, },
{ {
name: 'Options & Features', label: 'Options & Features',
link: '/options' link: '/options'
}, },
{ {
name: 'Backgrounds', label: 'Backgrounds',
link: '/backgrounds' link: '/backgrounds'
}, },
{ {
name: 'Items', label: 'Items',
link: '/items' link: '/items'
}, },
{ {
name: 'Spells', label: 'Spells',
link: '/spells' link: '/spells'
} }
]
}
]; ];

View File

@@ -1,9 +1,9 @@
'use client'; 'use client';
import Link from 'next/link'; import Link from 'next/link';
import { usePathname } from 'next/navigation'; import { usePathname, useRouter } from 'next/navigation';
import './header.css'; import './header.css';
import { Avatar, Button, Card, FileButton, Grid, Group, Menu, Text, UnstyledButton } from '@mantine/core'; import { Avatar, Button, Card, Center, FileButton, Grid, Group, Menu, Text, UnstyledButton } from '@mantine/core';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { logout, refresh, refreshLoggedIn } from '@/api/auth'; import { logout, refresh, refreshLoggedIn } from '@/api/auth';
@@ -13,14 +13,16 @@ import { HeaderItem, headerItems } from './headerItems';
import { userState } from '@/state/auth'; import { userState } from '@/state/auth';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { getPicture, setPicture } from '@/api/users'; import { getPicture, setPicture } from '@/api/users';
import { BsChevronDown } from 'react-icons/bs';
export default function Header() { export default function Header() {
const pathName = usePathname(); const pathName = usePathname();
const [modalType, toggle] = useToggle([undefined, 'login', 'register', 'reset']); const [modalType, toggle] = useToggle([undefined, 'login', 'register', 'reset']);
const [headers, setHeaders] = useState<HeaderItem[]>([]); const [headers] = useState<HeaderItem[]>(headerItems);
const [user, setUser] = useRecoilState(userState); const [user, setUser] = useRecoilState(userState);
const [refreshId, setRefreshId] = useState<NodeJS.Timeout | undefined>(undefined); const [refreshId, setRefreshId] = useState<NodeJS.Timeout | undefined>(undefined);
const [profilePicture, setProfilePicture] = useState<File | null>(null); const [profilePicture, setProfilePicture] = useState<File | null>(null);
const router = useRouter();
useEffect(() => { useEffect(() => {
if (!user || !Cookies.get('logged_in')) { if (!user || !Cookies.get('logged_in')) {
@@ -40,16 +42,6 @@ export default function Header() {
} }
}, [user]); }, [user]);
useEffect(() => {
const h: HeaderItem[] = [];
headerItems.forEach((item) => {
if (item.role == undefined || user?.role == item.role) {
h.push(item);
}
setHeaders(h);
});
}, [user]);
return ( return (
<> <>
<nav className='navbar'> <nav className='navbar'>
@@ -58,11 +50,41 @@ export default function Header() {
Siren Siren
</Link> </Link>
<div className='header-items'> <div className='header-items'>
{headers.map((item) => ( {headers.map((item) => {
<Link className={`header-item ${pathName == item.link && 'active'}`} href={item.link} key={item.name}> const menuItems = item.links?.map((subItem) => (
{item.name} <Menu.Item
color={pathName == subItem.link ? 'blue' : undefined}
onClick={() => router.push(subItem.link ?? '#')}
key={subItem.label}
>
{subItem.label}
</Menu.Item>
));
if (menuItems) {
return (
<Menu trigger='hover' transitionProps={{ exitDuration: 0 }} withinPortal key={item.label}>
<Menu.Target>
<Link className={`header-item ${pathName == item.link && 'active'}`} href={item.link ?? '#'}>
<Center>
{item.label}
<BsChevronDown />
</Center>
</Link> </Link>
))} </Menu.Target>
<Menu.Dropdown>{menuItems}</Menu.Dropdown>
</Menu>
);
}
return (
<Link
className={`header-item ${pathName == item.link && 'active'}`}
href={item.link ?? '#'}
key={item.label}
>
{item.label}
</Link>
);
})}
</div> </div>
</div> </div>
<div className='user-section'> <div className='user-section'>

View File

@@ -1,10 +1,11 @@
'use client'; 'use client';
import { Graphics, Stage } from '@pixi/react'; import { Graphics, Stage, Text } from '@pixi/react';
import { Graphics as PixiGraphics } from '@pixi/graphics'; import { Graphics as PixiGraphics } from '@pixi/graphics';
import { MouseEvent, WheelEvent, useCallback, useEffect, useState } from 'react'; import { MouseEvent, WheelEvent, useCallback, useEffect, useState } from 'react';
import TileControls, { EditTool, Tool, defaultColors } from './TileControls'; import TileControls, { EditTool, Tool, defaultColors } from './TileControls';
import { Box } from '@mantine/core'; import { Box } from '@mantine/core';
import { TextStyle } from 'pixi.js';
interface SquareEdit { interface SquareEdit {
x: number; x: number;
@@ -31,21 +32,21 @@ export default function TileGrid() {
useEffect(() => { useEffect(() => {
if (window) { if (window) {
window.addEventListener( // window.addEventListener(
'wheel', // 'wheel',
(event) => { // (event) => {
event.preventDefault(); // event.preventDefault();
}, // },
{ passive: false } // { passive: false }
); // );
// Disable right click context menu // // Disable right click context menu
window.addEventListener( // window.addEventListener(
'contextmenu', // 'contextmenu',
(event) => { // (event) => {
event.preventDefault(); // event.preventDefault();
}, // },
{ passive: false } // { passive: false }
); // );
} }
}, []); }, []);
@@ -73,20 +74,39 @@ export default function TileGrid() {
g.endFill(); g.endFill();
}); });
}, },
[edits] [edits, zoom]
); );
function clickEvent(e: MouseEvent) { function clickEvent(e: MouseEvent) {
setMouseDown(true); setMouseDown(true);
setLastPosition({ x: e.clientX, y: e.clientY }); setLastPosition({ x: e.clientX, y: e.clientY });
if (tool == Tool.ZOOM) { if (tool == Tool.ZOOM) {
handleZoom(e.button === 0 ? -100 : 100); handleZoom(e.button === 0 ? -100 : 100, e.clientX, e.clientY);
} else if (tool == Tool.EDIT) {
if (editTool === EditTool.SQUARE) {
drawSquare(e.button, e.clientX, e.clientY);
} else if (editTool === EditTool.CIRCLE) {
// handle circle
}
}
}
function drawSquare(button: number, clientX: number, clientY: number) {
// Calculate tile coordinates
const x = Math.floor((clientX - position.x) / (32 * zoom));
const y = Math.floor((clientY - position.y) / (32 * zoom));
if (button === 1) {
// Add new edit if left mouse button is pressed
setEdits([...edits, { x, y, color: colors[selectedColor] }]);
} else if (button == 2) {
// Remove edit if right mouse button is pressed
setEdits(edits.filter((edit) => edit.x !== x || edit.y !== y));
} }
} }
function moveEvent(e: MouseEvent) { function moveEvent(e: MouseEvent) {
if (mouseDown) { if (mouseDown) {
if (tool == Tool.HAND) { if (tool == Tool.HAND || e.buttons == 4) {
let dx = position.x + e.clientX - lastPosition.x; let dx = position.x + e.clientX - lastPosition.x;
let dy = position.y + e.clientY - lastPosition.y; let dy = position.y + e.clientY - lastPosition.y;
// Prevent coordinates from going out of bounds // Prevent coordinates from going out of bounds
@@ -98,18 +118,7 @@ export default function TileGrid() {
setLastPosition({ x: e.clientX, y: e.clientY }); setLastPosition({ x: e.clientX, y: e.clientY });
} else if (tool === Tool.EDIT) { } else if (tool === Tool.EDIT) {
if (editTool === EditTool.SQUARE) { if (editTool === EditTool.SQUARE) {
// Calculate tile coordinates drawSquare(e.buttons, e.clientX, e.clientY);
const x = Math.floor((e.clientX - position.x) / (32 * zoom));
const y = Math.floor((e.clientY - position.y - 64) / (32 * zoom));
// Check if tile is already edited, and remove it
const index = edits.findIndex((edit) => edit.x === x && edit.y === y);
if (index !== -1) {
setEdits([...edits.slice(0, index), ...edits.slice(index + 1)]);
}
// Add new edit if left mouse button is pressed
if (e.buttons === 1) {
setEdits([...edits, { x, y, color: colors[selectedColor] }]);
}
} else if (editTool === EditTool.CIRCLE) { } else if (editTool === EditTool.CIRCLE) {
// handle circle // handle circle
} }
@@ -120,10 +129,10 @@ export default function TileGrid() {
} }
function zoomEvent(e: WheelEvent) { function zoomEvent(e: WheelEvent) {
handleZoom(e.deltaY); handleZoom(e.deltaY, e.clientX, e.clientY);
} }
function handleZoom(delta: number) { function handleZoom(delta: number, clientX: number, clientY: number) {
let newZoom = zoom; let newZoom = zoom;
if (delta > 0) { if (delta > 0) {
newZoom = zoom / 1.1; newZoom = zoom / 1.1;
@@ -132,7 +141,12 @@ export default function TileGrid() {
} }
newZoom = Math.min(newZoom, 3); newZoom = Math.min(newZoom, 3);
newZoom = Math.max(newZoom, 0.6); newZoom = Math.max(newZoom, 0.6);
console.log(newZoom);
setZoom(newZoom); setZoom(newZoom);
// Adjust position to zoom in on mouse position
const dx = (position.x - clientX) * (newZoom / zoom) + clientX;
const dy = (position.y - clientY) * (newZoom / zoom) + clientY;
setPosition({ x: dx, y: dy });
} }
return ( return (