From 67f9111d03f259a6fff5b5e5b3f29d34903a4b79 Mon Sep 17 00:00:00 2001 From: wass08 Date: Wed, 21 Jan 2026 11:50:56 +0900 Subject: [PATCH] fix typing --- .../components/editor/selection-manager.tsx | 195 ++++++++++-------- 1 file changed, 106 insertions(+), 89 deletions(-) diff --git a/apps/editor/components/editor/selection-manager.tsx b/apps/editor/components/editor/selection-manager.tsx index 2ec5c7ed..870967e6 100644 --- a/apps/editor/components/editor/selection-manager.tsx +++ b/apps/editor/components/editor/selection-manager.tsx @@ -1,165 +1,182 @@ -import { emitter, sceneRegistry } from "@pascal-app/core"; +import { + type AnyNode, + type BuildingNode, + emitter, + type ItemNode, + sceneRegistry, +} from '@pascal-app/core' -import { useViewer } from "@pascal-app/viewer"; -import { useEffect } from "react"; -import useEditor from "@/store/use-editor"; +import { useViewer } from '@pascal-app/viewer' +import { useEffect } from 'react' +import useEditor from '@/store/use-editor' -const SELECTION_STRATEGIES = { +type SelectableNodeType = 'wall' | 'item' | 'building' + +interface SelectionStrategy { + types: SelectableNodeType[] + handleSelect: (node: AnyNode, isShift: boolean) => void + handleDeselect: () => void + isValid: (node: AnyNode) => boolean +} + +const SELECTION_STRATEGIES: Record = { site: { - types: ["building"], - handleSelect: (node: any, isShift: boolean) => { - useViewer.getState().setSelection({ buildingId: node.id }); + types: ['building'], + handleSelect: (node) => { + useViewer + .getState() + .setSelection({ buildingId: (node as BuildingNode).id }) }, handleDeselect: () => { - useViewer.getState().setSelection({ buildingId: null }); + useViewer.getState().setSelection({ buildingId: null }) }, - isValid: (node: any) => node.type === "building", + isValid: (node) => node.type === 'building', }, structure: { - types: ["wall", "item"], - handleSelect: (node: any, isShift: boolean) => { - const { selection, setSelection } = useViewer.getState(); + types: ['wall', 'item'], + handleSelect: (node, isShift) => { + const { selection, setSelection } = useViewer.getState() const nextIds = isShift ? selection.selectedIds.includes(node.id) ? selection.selectedIds.filter((id) => id !== node.id) : [...selection.selectedIds, node.id] - : [node.id]; - setSelection({ selectedIds: nextIds }); + : [node.id] + setSelection({ selectedIds: nextIds }) }, handleDeselect: () => { - useViewer.getState().setSelection({ selectedIds: [] }); + useViewer.getState().setSelection({ selectedIds: [] }) }, - isValid: (node: any) => { - if (node.type === "wall") return true; - if (node.type === "item") { - return node.category === "door" || node.category === "window"; + isValid: (node) => { + if (node.type === 'wall') return true + if (node.type === 'item') { + return ( + (node as ItemNode).asset.category === 'door' || + (node as ItemNode).asset.category === 'window' + ) } - return false; + return false }, }, furnish: { - types: ["item"], - handleSelect: (node: any, isShift: boolean) => { - const { selection, setSelection } = useViewer.getState(); + types: ['item'], + handleSelect: (node, isShift) => { + const { selection, setSelection } = useViewer.getState() const nextIds = isShift ? selection.selectedIds.includes(node.id) ? selection.selectedIds.filter((id) => id !== node.id) : [...selection.selectedIds, node.id] - : [node.id]; - setSelection({ selectedIds: nextIds }); + : [node.id] + setSelection({ selectedIds: nextIds }) }, handleDeselect: () => { - useViewer.getState().setSelection({ selectedIds: [] }); + useViewer.getState().setSelection({ selectedIds: [] }) }, - isValid: (node: any) => { - return ( - node.type === "item" && - node.category !== "door" && - node.category !== "window" - ); + isValid: (node) => { + if (node.type !== 'item') return false + const item = node as ItemNode + return item.asset.category !== 'door' && item.asset.category !== 'window' }, }, -}; +} export const SelectionManager = () => { - const phase = useEditor((s) => s.phase); - const mode = useEditor((s) => s.mode); + const phase = useEditor((s) => s.phase) + const mode = useEditor((s) => s.mode) useEffect(() => { - if (mode !== "select") return; + if (mode !== 'select') return - const strategy = SELECTION_STRATEGIES[phase]; - if (!strategy) return; + const strategy = SELECTION_STRATEGIES[phase] + if (!strategy) return const onEnter = (event: any) => { if (strategy.isValid(event.node)) { - useViewer.setState({ hoveredId: event.node.id }); + useViewer.setState({ hoveredId: event.node.id }) } - }; + } - const onLeave = () => useViewer.setState({ hoveredId: null }); + const onLeave = () => useViewer.setState({ hoveredId: null }) const onClick = (event: any) => { - if (!strategy.isValid(event.node)) return; + if (!strategy.isValid(event.node)) return - event.stopPropagation(); - const isShift = event.nativeEvent?.shiftKey; - strategy.handleSelect(event.node, isShift); - }; + event.stopPropagation() + const isShift = event.nativeEvent?.shiftKey + strategy.handleSelect(event.node, isShift) + } // Bind listeners for all potential types this strategy might care about strategy.types.forEach((type) => { - emitter.on(`${type}:enter`, onEnter); - emitter.on(`${type}:leave`, onLeave); - emitter.on(`${type}:click`, onClick); - }); + emitter.on(`${type}:enter`, onEnter) + emitter.on(`${type}:leave`, onLeave) + emitter.on(`${type}:click`, onClick) + }) - const onGridClick = () => strategy.handleDeselect(); - emitter.on("grid:click", onGridClick); + const onGridClick = () => strategy.handleDeselect() + emitter.on('grid:click', onGridClick) return () => { strategy.types.forEach((type) => { - emitter.off(`${type}:enter`, onEnter); - emitter.off(`${type}:leave`, onLeave); - emitter.off(`${type}:click`, onClick); - }); - emitter.off("grid:click", onGridClick); - }; - }, [phase, mode]); + emitter.off(`${type}:enter`, onEnter) + emitter.off(`${type}:leave`, onLeave) + emitter.off(`${type}:click`, onClick) + }) + emitter.off('grid:click', onGridClick) + } + }, [phase, mode]) - return ; -}; + return +} const EditorOutlinerSync = () => { - const phase = useEditor((s) => s.phase); - const selection = useViewer((s) => s.selection); - const hoveredId = useViewer((s) => s.hoveredId); - const outliner = useViewer((s) => s.outliner); + const phase = useEditor((s) => s.phase) + const selection = useViewer((s) => s.selection) + const hoveredId = useViewer((s) => s.hoveredId) + const outliner = useViewer((s) => s.outliner) useEffect(() => { - let idsToHighlight: string[] = []; + let idsToHighlight: string[] = [] // 1. Determine what should be highlighted based on Phase switch (phase) { - case "site": + case 'site': // Only highlight the building if one is selected - if (selection.buildingId) idsToHighlight = [selection.buildingId]; - break; + if (selection.buildingId) idsToHighlight = [selection.buildingId] + break - case "structure": + case 'structure': // Highlight selected items (walls/slabs) // We IGNORE buildingId even if it's set in the store - idsToHighlight = selection.selectedIds; - break; + idsToHighlight = selection.selectedIds + break - case "furnish": + case 'furnish': // Highlight selected furniture/items - idsToHighlight = selection.selectedIds; - break; + idsToHighlight = selection.selectedIds + break default: // Pure Viewer mode: Highlight based on the "deepest" selection - if (selection.selectedIds.length > 0) - idsToHighlight = selection.selectedIds; - else if (selection.levelId) idsToHighlight = [selection.levelId]; - else if (selection.buildingId) idsToHighlight = [selection.buildingId]; + if (selection.selectedIds.length > 0) idsToHighlight = selection.selectedIds + else if (selection.levelId) idsToHighlight = [selection.levelId] + else if (selection.buildingId) idsToHighlight = [selection.buildingId] } // 2. Sync with the imperative outliner arrays (mutate in place to keep references) - outliner.selectedObjects.length = 0; + outliner.selectedObjects.length = 0 for (const id of idsToHighlight) { - const obj = sceneRegistry.nodes.get(id); - if (obj) outliner.selectedObjects.push(obj); + const obj = sceneRegistry.nodes.get(id) + if (obj) outliner.selectedObjects.push(obj) } - outliner.hoveredObjects.length = 0; + outliner.hoveredObjects.length = 0 if (hoveredId) { - const obj = sceneRegistry.nodes.get(hoveredId); - if (obj) outliner.hoveredObjects.push(obj); + const obj = sceneRegistry.nodes.get(hoveredId) + if (obj) outliner.hoveredObjects.push(obj) } - }, [phase, selection, hoveredId, outliner]); + }, [phase, selection, hoveredId, outliner]) - return null; -}; + return null +}