Skip to content

Commit cf74939

Browse files
authored
fix: guard null selected item during item tool placement (#229)
1 parent 3afa1ce commit cf74939

2 files changed

Lines changed: 8 additions & 5 deletions

File tree

packages/editor/src/components/tools/item/item-tool.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ export const ItemTool: React.FC = () => {
88
const draftNode = useDraftNode()
99

1010
const cursor = usePlacementCoordinator({
11-
asset: selectedItem!,
11+
asset: selectedItem,
1212
draftNode,
1313
initDraft: (gridPosition) => {
14-
if (!selectedItem?.attachTo) {
15-
draftNode.create(gridPosition, selectedItem!)
14+
if (selectedItem && !selectedItem.attachTo) {
15+
draftNode.create(gridPosition, selectedItem)
1616
}
1717
},
1818
onCommitted: () => {

packages/editor/src/components/tools/item/use-placement-coordinator.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { distance, smoothstep, uv, vec2 } from 'three/tsl'
3333
import { LineBasicNodeMaterial, MeshBasicNodeMaterial } from 'three/webgpu'
3434
import { EDITOR_LAYER } from '../../../lib/constants'
3535
import { sfxEmitter } from '../../../lib/sfx-bus'
36+
import { snapToGrid } from './placement-math'
3637
import {
3738
ceilingStrategy,
3839
checkCanPlace,
@@ -41,7 +42,6 @@ import {
4142
wallStrategy,
4243
} from './placement-strategies'
4344
import type { PlacementState, TransitionResult } from './placement-types'
44-
import { snapToGrid } from './placement-math'
4545
import type { DraftNodeHandle } from './use-draft-node'
4646

4747
const DEFAULT_DIMENSIONS: [number, number, number] = [1, 1, 1]
@@ -69,7 +69,7 @@ const radialOpacity = smoothstep(0, 0.7, dist).mul(0.6)
6969
basePlaneMaterial.opacityNode = radialOpacity
7070

7171
export interface PlacementCoordinatorConfig {
72-
asset: AssetInput
72+
asset: AssetInput | null
7373
draftNode: DraftNodeHandle
7474
initDraft: (gridPosition: Vector3) => void
7575
onCommitted: () => boolean
@@ -98,6 +98,7 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
9898
const { asset, draftNode } = config
9999

100100
useEffect(() => {
101+
if (!asset) return
101102
useScene.temporal.getState().pause()
102103

103104
const validators = { canPlaceOnFloor, canPlaceOnWall, canPlaceOnCeiling }
@@ -873,6 +874,7 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
873874
// Wall/ceiling items are managed by their own surface entry events (ensureDraft / reparent).
874875
const viewerLevelId = useViewer((s) => s.selection.levelId)
875876
useEffect(() => {
877+
if (!asset) return
876878
const draft = draftNode.current
877879
if (!(draft && viewerLevelId) || asset.attachTo) return
878880
if (draft.parentId === viewerLevelId) return
@@ -881,6 +883,7 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
881883
}, [viewerLevelId, draftNode, asset])
882884

883885
useFrame((_, delta) => {
886+
if (!asset) return
884887
if (!draftNode.current) return
885888
const mesh = sceneRegistry.nodes.get(draftNode.current.id)
886889
if (!mesh) return

0 commit comments

Comments
 (0)