feat: implements adding node to scene

This commit is contained in:
Mark Mankarious
2023-07-05 19:27:20 +01:00
parent af2d96d77b
commit 99c8060243
15 changed files with 117 additions and 62 deletions

View File

@@ -8,7 +8,7 @@ interface Props {
}
const COLOR = "grey.900";
const SIZE = 14;
const SIZE = 11;
const ANIMATIONS = {
in: keyframes`
0% {

View File

@@ -1,5 +1,4 @@
import React from "react";
import { observer } from "mobx-react";
import { useGlobalState } from "../../hooks/useGlobalState";
import { ContextMenu } from "./ContextMenu";
import { ContextMenuItem } from "./ContextMenuItem";

View File

@@ -0,0 +1,31 @@
import React from "react";
import { useGlobalState } from "../../hooks/useGlobalState";
import { ContextMenu } from "./ContextMenu";
import { ContextMenuItem } from "./ContextMenuItem";
import { Coords } from "../../renderer/elements/Coords";
import { Add } from "@mui/icons-material";
interface Props {
tile: Coords;
}
export const TileContextMenu = ({ tile }: Props) => {
const renderer = useGlobalState((state) => state.renderer);
const icons = useGlobalState((state) => state.initialScene.icons);
const position = renderer.getTileScreenPosition(tile.x, tile.y);
return (
<ContextMenu position={position}>
<ContextMenuItem
onClick={() =>
renderer.sceneElements.nodes.addNode({
position: tile,
iconId: icons[0].id,
})
}
icon={<Add />}
label="Add node"
/>
</ContextMenu>
);
};

View File

@@ -1,13 +1,24 @@
import React from "react";
import { useGlobalState } from "../../hooks/useGlobalState";
import { NodeContextMenu } from "./NodeContextMenu";
import { TileContextMenu } from "./TileContextMenu";
import { Node } from "../../renderer/elements/Node";
import { Coords } from "../../renderer/elements/Coords";
export const ContextMenu = () => {
const targetElement = useGlobalState((state) => state.showContextMenuAt);
const targetElement = useGlobalState((state) => state.showContextMenuFor);
if (!targetElement) {
return null;
}
return <NodeContextMenu node={targetElement} />;
if (targetElement instanceof Node) {
return <NodeContextMenu node={targetElement} />;
}
if (targetElement instanceof Coords) {
return <TileContextMenu tile={targetElement} />;
}
return null;
};

View File

@@ -1,11 +1,12 @@
import { create } from "zustand";
import { SceneI } from "../validation/SceneSchema";
import { Node } from "../renderer/elements/Node";
import { Coords } from "../renderer/elements/Coords";
import { Renderer } from "../renderer/Renderer";
import { OnSceneChange, SceneEventI } from "../types";
interface GlobalState {
showContextMenuAt: Node | null;
showContextMenuFor: Node | Coords | null;
onSceneChange: OnSceneChange;
setOnSceneChange: (onSceneChange: OnSceneChange) => void;
initialScene: SceneI;
@@ -20,7 +21,7 @@ interface GlobalState {
}
export const useGlobalState = create<GlobalState>((set, get) => ({
showContextMenuAt: null,
showContextMenuFor: null,
selectedElements: [],
selectedSideNavItem: null,
onSceneChange: () => {},
@@ -45,8 +46,8 @@ export const useGlobalState = create<GlobalState>((set, get) => ({
const { renderer } = get();
switch (event.type) {
case "GRID_SELECTED":
set({ showContextMenuAt: null, selectedElements: [] });
case "TILE_SELECTED":
set({ showContextMenuFor: event.data.tile, selectedElements: [] });
break;
case "NODES_SELECTED":
set({ selectedElements: event.data.nodes });
@@ -55,21 +56,21 @@ export const useGlobalState = create<GlobalState>((set, get) => ({
const node = renderer.sceneElements.nodes.getNodeById(
event.data.nodes[0]
);
set({ showContextMenuAt: node });
set({ showContextMenuFor: node });
}
break;
case "NODE_REMOVED":
set({
showContextMenuAt: null,
showContextMenuFor: null,
selectedElements: [],
selectedSideNavItem: null,
});
break;
case "NODE_MOVED":
set({ showContextMenuAt: null, selectedElements: [] });
set({ showContextMenuFor: null, selectedElements: [] });
break;
case "ZOOM_CHANGED":
set({ showContextMenuAt: null, selectedElements: [] });
set({ showContextMenuFor: null, selectedElements: [] });
break;
default:
break;

View File

@@ -1,13 +1,9 @@
import { useEffect, useState, useCallback } from "react";
interface MouseCoords {
x: number;
y: number;
}
import { Coords } from "../renderer/elements/Coords";
interface MousePosition {
position: MouseCoords;
delta: MouseCoords | null;
position: Coords;
delta: Coords | null;
}
type _MouseEvent = (e: MousePosition) => void;
@@ -52,26 +48,23 @@ export const useMouseInput = () => {
(e: MouseEvent) => {
const offset = getOffset(domEl);
return {
x: e.clientX - offset.left + window.scrollX,
y: e.clientY - offset.top + window.scrollY,
};
return new Coords(
e.clientX - offset.left + window.scrollX,
e.clientY - offset.top + window.scrollY
);
},
[domEl]
);
const parseMousePosition = useCallback(
(e: MouseEvent, mousedown: MouseCoords | null) => {
(e: MouseEvent, mousedown: Coords | null) => {
const current = mouseEventToCoords(e);
const delta = mousedown
? {
x: current.x - mousedown.x,
y: current.y - mousedown.y,
}
? new Coords(current.x - mousedown.x, current.y - mousedown.y)
: null;
return {
position: { x: current.x, y: current.y },
position: new Coords(current.x, current.y),
delta,
};
},
@@ -81,7 +74,7 @@ export const useMouseInput = () => {
useEffect(() => {
if (!callbacks || !domEl) return;
let lastPosition: MouseCoords | null = null;
let lastPosition: Coords | null = null;
const onMouseDown = (e: MouseEvent) => {
callbacks.onMouseDown(parseMousePosition(e, lastPosition));

View File

@@ -1,5 +1,6 @@
import { makeAutoObservable } from "mobx";
import { Renderer } from "../renderer/Renderer";
import { Coords } from "../renderer/elements/Coords";
import { ModeBase } from "./ModeBase";
import type { Mouse, OnSceneChange } from "../types";
@@ -12,7 +13,7 @@ export class ModeManager {
} = undefined;
lastMode?: typeof ModeBase = undefined;
mouse: Mouse = {
position: { x: 0, y: 0 },
position: new Coords(0, 0),
delta: null,
};
emitEvent?: OnSceneChange;

View File

@@ -3,6 +3,7 @@ import { Mouse } from "../types";
import { getTargetFromSelection, isMouseOverNewTile } from "./utils";
import { SelectNode } from "./SelectNode";
import { Node } from "../renderer/elements/Node";
import { Coords } from "../renderer/elements/Coords";
export class Select extends ModeBase {
entry(mouse: Mouse) {
@@ -43,7 +44,10 @@ export class Select extends ModeBase {
return;
}
this.ctx.emitEvent({ type: "GRID_SELECTED" });
this.ctx.emitEvent({
type: "TILE_SELECTED",
data: { tile: new Coords(x, y) },
});
}
MOUSE_MOVE(mouse: Mouse) {

View File

@@ -0,0 +1,14 @@
export class Coords {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
set(x: number, y: number) {
this.x = x;
this.y = y;
}
}

View File

@@ -7,7 +7,8 @@ import {
getBoundingBox,
getTileBounds,
} from "../utils/gridHelpers";
import type { Context, Coords } from "../../types";
import type { Context } from "../../types";
import { Coords } from "./Coords";
import { SceneElement } from "../SceneElement";
import { tweenPosition } from "../../utils";

View File

@@ -1,6 +1,7 @@
import { makeAutoObservable } from "mobx";
import { Group } from "paper";
import { Coords, Context } from "../../types";
import { Context } from "../../types";
import { Coords } from "./Coords";
import { theme } from "../../theme";
import { NodeTile } from "./NodeTile";
import { NodeIcon } from "./NodeIcon";

View File

@@ -1,6 +1,6 @@
import { Group, Raster } from "paper";
import { PROJECTED_TILE_WIDTH, PIXEL_UNIT } from "../constants";
import { Coords, Context } from "../../types";
import { Context } from "../../types";
const NODE_IMG_PADDING = 0 * PIXEL_UNIT;

View File

@@ -3,6 +3,7 @@ import autobind from "auto-bind";
import { makeAutoObservable, toJS } from "mobx";
import { Context } from "../../types";
import { Node, NodeOptions } from "./Node";
import { Coords } from "./Coords";
import cuid from "cuid";
import { tweenPosition } from "../../utils";
@@ -19,11 +20,19 @@ export class Nodes {
this.ctx = ctx;
}
addNode(options: NodeOptions) {
addNode(
options: Omit<NodeOptions, "position" | "id"> & {
id?: string;
position: { x: number; y: number };
}
) {
const position = new Coords(options.position.x, options.position.y);
const node = new Node(
this.ctx,
{
...options,
position,
id: options.id ?? cuid(),
},
{
@@ -42,14 +51,11 @@ export class Nodes {
}
onMove(x: number, y: number, node: Node, opts?: { skipAnimation: boolean }) {
const from = node.position;
const to = { x, y };
const from = new Coords(node.position.x, node.position.y);
const to = new Coords(x, y);
const tile = this.ctx.getTileBounds(x, y);
node.position = {
x,
y,
};
node.position = new Coords(x, y);
tweenPosition(node.container, {
...tile.bottom,

View File

@@ -1,5 +1,5 @@
import { PROJECTED_TILE_HEIGHT, PROJECTED_TILE_WIDTH } from "../constants";
import { Coords } from "../../types";
import { Coords } from "../elements/Coords";
// Iterates over every item in a 2 dimensional array
// const tileIterator = (w, h, cb) => {
@@ -45,15 +45,15 @@ export const sortByPosition = (items: Coords[]) => {
export const getBoundingBox = (
tiles: Coords[],
offset: Coords = { x: 0, y: 0 }
offset: Coords = new Coords(0, 0)
) => {
const { lowX, lowY, highX, highY } = sortByPosition(tiles);
return [
{ x: lowX - offset.x, y: lowY - offset.y },
{ x: highX + offset.x, y: lowY - offset.y },
{ x: highX + offset.x, y: highY + offset.y },
{ x: lowX - offset.x, y: highY + offset.y },
new Coords(lowX - offset.x, lowY - offset.y),
new Coords(highX + offset.x, lowY - offset.y),
new Coords(highX + offset.x, highY + offset.y),
new Coords(lowX - offset.x, highY + offset.y),
];
};

View File

@@ -1,6 +1,6 @@
import { Renderer } from "./renderer/Renderer";
import type { ModeManager } from "./modes/ModeManager";
import type { Node } from "./renderer/elements/Node";
import { Coords } from "./renderer/elements/Coords";
export interface Mode {
initial: string;
@@ -8,14 +8,9 @@ export interface Mode {
destroy?: () => void;
}
export interface MouseCoords {
x: number;
y: number;
}
export interface Mouse {
position: MouseCoords;
delta: MouseCoords | null;
position: Coords;
delta: Coords | null;
}
export interface ModeContext {
@@ -24,11 +19,6 @@ export interface ModeContext {
emitEvent: OnSceneChange;
}
export interface Coords {
x: number;
y: number;
}
export type GeneralEventI = {
type: "SCENE_LOAD";
data: {};
@@ -37,7 +27,10 @@ export type GeneralEventI = {
export type NodeEventI =
// Grid Events
| {
type: "GRID_SELECTED";
type: "TILE_SELECTED";
data: {
tile: Coords;
};
}
// Node Events
| {