zone editor

This commit is contained in:
wass08
2026-01-22 12:24:55 +09:00
parent 17363a9c11
commit eb0ccac30f
6 changed files with 346 additions and 8 deletions
+13
View File
@@ -2,6 +2,7 @@ import type { ThreeEvent } from "@react-three/fiber";
import mitt from "mitt";
import type { BuildingNode, ItemNode, WallNode } from "../schema";
import type { AnyNode } from "../schema/types";
import type { Zone } from "../schema/zone";
// Base event interfaces
export interface GridEvent {
@@ -18,6 +19,13 @@ export interface NodeEvent<T extends AnyNode = AnyNode> {
nativeEvent: ThreeEvent<PointerEvent>;
}
export interface ZoneEvent {
zone: Zone;
position: [number, number, number];
stopPropagation: () => void;
nativeEvent: ThreeEvent<PointerEvent>;
}
export type WallEvent = NodeEvent<WallNode>;
export type ItemEvent = NodeEvent<ItemNode>;
export type BuildingEvent = NodeEvent<BuildingNode>;
@@ -52,10 +60,15 @@ type CameraControlEvents = {
"camera-controls:view": CameraControlEvent;
"camera-controls:capture": CameraControlEvent;
};
type ZoneEvents = {
[K in `zone:${EventSuffix}`]: ZoneEvent;
};
type EditorEvents = GridEvents &
NodeEvents<"wall", WallEvent> &
NodeEvents<"item", ItemEvent> &
NodeEvents<"building", BuildingEvent> &
ZoneEvents &
CameraControlEvents;
export const emitter = mitt<EditorEvents>();
+1
View File
@@ -7,6 +7,7 @@ export type {
ItemEvent,
NodeEvent,
WallEvent,
ZoneEvent,
} from './events/bus'
// Events
export { emitter, eventSuffixes } from './events/bus'
@@ -1,6 +1,7 @@
import { useRegistry, useScene, type Zone } from "@pascal-app/core";
import { emitter, useRegistry, useScene, type Zone } from "@pascal-app/core";
import { useMemo, useRef } from "react";
import type { ThreeEvent } from "@react-three/fiber";
import { useCallback, useMemo, useRef } from "react";
import {
BufferGeometry,
Color,
@@ -162,8 +163,26 @@ export const ZoneRenderer = ({ zoneId }: { zoneId: Zone["id"] }) => {
return null;
}
const emitZoneEvent = useCallback(
(suffix: string, e: ThreeEvent<PointerEvent>) => {
const eventKey = `zone:${suffix}` as `zone:${typeof suffix}`;
emitter.emit(eventKey, {
zone,
position: [e.point.x, e.point.y, e.point.z],
stopPropagation: () => e.stopPropagation(),
nativeEvent: e,
});
},
[zone]
);
return (
<group ref={ref}>
<group
ref={ref}
onClick={(e) => emitZoneEvent("click", e)}
onPointerEnter={(e) => emitZoneEvent("enter", e)}
onPointerLeave={(e) => emitZoneEvent("leave", e)}
>
{/* Floor fill */}
<mesh
position={[0, Y_OFFSET, 0]}
+2 -2
View File
@@ -25,8 +25,8 @@ type Outliner = {
type ViewerState = {
selection: SelectionPath;
hoveredId: AnyNode["id"] | null;
setHoveredId: (id: AnyNode["id"] | null) => void;
hoveredId: AnyNode["id"] | Zone["id"] | null;
setHoveredId: (id: AnyNode["id"] | Zone["id"] | null) => void;
cameraMode: "perspective" | "orthographic";
setCameraMode: (mode: "perspective" | "orthographic") => void;