Files
editor/packages/viewer/src/hooks/use-node-events.ts
T
2026-01-27 09:25:40 +09:00

66 lines
2.1 KiB
TypeScript

import {
type BuildingEvent,
type BuildingNode,
type CeilingEvent,
type CeilingNode,
type EventSuffix,
emitter,
type ItemEvent,
type ItemNode,
type SlabEvent,
type SlabNode,
type WallEvent,
type WallNode,
type ZoneEvent,
type ZoneNode,
} from '@pascal-app/core'
import type { ThreeEvent } from '@react-three/fiber'
type NodeConfig = {
item: { node: ItemNode; event: ItemEvent }
wall: { node: WallNode; event: WallEvent }
building: { node: BuildingNode; event: BuildingEvent }
zone: { node: ZoneNode; event: ZoneEvent }
slab: { node: SlabNode; event: SlabEvent }
ceiling: { node: CeilingNode; event: CeilingEvent }
}
type NodeType = keyof NodeConfig
export function useNodeEvents<T extends NodeType>(node: NodeConfig[T]['node'], type: T) {
const emit = (suffix: EventSuffix, e: ThreeEvent<PointerEvent>) => {
const eventKey = `${type}:${suffix}` as `${T}:${EventSuffix}`
const localPoint = e.object.worldToLocal(e.point.clone())
const payload = {
node,
position: [e.point.x, e.point.y, e.point.z],
localPosition: [localPoint.x, localPoint.y, localPoint.z],
normal: e.face ? [e.face.normal.x, e.face.normal.y, e.face.normal.z] : undefined,
stopPropagation: () => e.stopPropagation(),
nativeEvent: e,
} as NodeConfig[T]['event']
emitter.emit(eventKey, payload)
}
return {
onPointerDown: (e: ThreeEvent<PointerEvent>) => {
if (e.button !== 0) return
emit('pointerdown', e)
},
onPointerUp: (e: ThreeEvent<PointerEvent>) => {
if (e.button !== 0) return
emit('pointerup', e)
},
onClick: (e: ThreeEvent<PointerEvent>) => {
if (e.button !== 0) return
emit('click', e)
},
onPointerEnter: (e: ThreeEvent<PointerEvent>) => emit('enter', e),
onPointerLeave: (e: ThreeEvent<PointerEvent>) => emit('leave', e),
onPointerMove: (e: ThreeEvent<PointerEvent>) => emit('move', e),
onDoubleClick: (e: ThreeEvent<PointerEvent>) => emit('double-click', e),
onContextMenu: (e: ThreeEvent<PointerEvent>) => emit('context-menu', e),
}
}