handle deselect
This commit is contained in:
@@ -4,71 +4,110 @@ import { useViewer } from "@pascal-app/viewer";
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import useEditor from "@/store/use-editor";
|
import useEditor from "@/store/use-editor";
|
||||||
|
|
||||||
const PHASE_CONFIG = {
|
const SELECTION_STRATEGIES = {
|
||||||
site: ["building"],
|
site: {
|
||||||
structure: ["wall", "slab", "door", "window", "column", "stair"],
|
types: ["building"],
|
||||||
furnish: ["item"], // furniture, decor, etc.
|
handleSelect: (node: any, isShift: boolean) => {
|
||||||
} as const;
|
useViewer.getState().setSelection({ buildingId: node.id });
|
||||||
|
},
|
||||||
|
handleDeselect: () => {
|
||||||
|
useViewer.getState().setSelection({ buildingId: null });
|
||||||
|
},
|
||||||
|
isValid: (node: any) => node.type === "building",
|
||||||
|
},
|
||||||
|
|
||||||
type PhaseType = keyof typeof PHASE_CONFIG;
|
structure: {
|
||||||
|
types: ["wall", "item"],
|
||||||
|
handleSelect: (node: any, isShift: boolean) => {
|
||||||
|
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 });
|
||||||
|
},
|
||||||
|
handleDeselect: () => {
|
||||||
|
useViewer.getState().setSelection({ selectedIds: [] });
|
||||||
|
},
|
||||||
|
isValid: (node: any) => {
|
||||||
|
if (node.type === "wall") return true;
|
||||||
|
if (node.type === "item") {
|
||||||
|
return node.category === "door" || node.category === "window";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
furnish: {
|
||||||
|
types: ["item"],
|
||||||
|
handleSelect: (node: any, isShift: boolean) => {
|
||||||
|
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 });
|
||||||
|
},
|
||||||
|
handleDeselect: () => {
|
||||||
|
useViewer.getState().setSelection({ selectedIds: [] });
|
||||||
|
},
|
||||||
|
isValid: (node: any) => {
|
||||||
|
return (
|
||||||
|
node.type === "item" &&
|
||||||
|
node.category !== "door" &&
|
||||||
|
node.category !== "window"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const SelectionManager = () => {
|
export const SelectionManager = () => {
|
||||||
const phase = useEditor((s) => s.phase);
|
const phase = useEditor((s) => s.phase);
|
||||||
const mode = useEditor((s) => s.mode);
|
const mode = useEditor((s) => s.mode);
|
||||||
const { setSelection, selection } = useViewer();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mode !== "select") return;
|
if (mode !== "select") return;
|
||||||
|
|
||||||
const allowedTypes = PHASE_CONFIG[phase] || [];
|
const strategy = SELECTION_STRATEGIES[phase];
|
||||||
|
if (!strategy) return;
|
||||||
|
|
||||||
const onEnter = (event: any) => {
|
const onEnter = (event: any) => {
|
||||||
// Only hover if the node type is allowed in this phase
|
if (strategy.isValid(event.node)) {
|
||||||
if (allowedTypes.includes(event.node.type)) {
|
useViewer.setState({ hoveredId: event.node.id });
|
||||||
useViewer.getState().setHoveredId(event.node.id);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onLeave = () => useViewer.getState().setHoveredId(null);
|
const onLeave = () => useViewer.setState({ hoveredId: null });
|
||||||
|
|
||||||
const onClick = (event: any) => {
|
const onClick = (event: any) => {
|
||||||
|
if (!strategy.isValid(event.node)) return;
|
||||||
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const { id, type } = event.node;
|
|
||||||
|
|
||||||
if (!allowedTypes.includes(type)) return;
|
|
||||||
|
|
||||||
const isShift = event.nativeEvent?.shiftKey;
|
const isShift = event.nativeEvent?.shiftKey;
|
||||||
|
strategy.handleSelect(event.node, isShift);
|
||||||
if (isShift) {
|
|
||||||
const isSelected = selection.selectedIds.includes(id);
|
|
||||||
const nextIds = isSelected
|
|
||||||
? selection.selectedIds.filter((i) => i !== id)
|
|
||||||
: [...selection.selectedIds, id];
|
|
||||||
setSelection({ selectedIds: nextIds });
|
|
||||||
} else {
|
|
||||||
setSelection({ selectedIds: [id] });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// BINDING: Loop through all allowed types for this phase
|
// Bind listeners for all potential types this strategy might care about
|
||||||
allowedTypes.forEach((type) => {
|
strategy.types.forEach((type) => {
|
||||||
emitter.on(`${type}:enter`, onEnter);
|
emitter.on(`${type}:enter`, onEnter);
|
||||||
emitter.on(`${type}:leave`, onLeave);
|
emitter.on(`${type}:leave`, onLeave);
|
||||||
emitter.on(`${type}:click`, onClick);
|
emitter.on(`${type}:click`, onClick);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onGridClick = () => setSelection({ selectedIds: [] });
|
const onGridClick = () => strategy.handleDeselect();
|
||||||
emitter.on("grid:click", onGridClick);
|
emitter.on("grid:click", onGridClick);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
allowedTypes.forEach((type) => {
|
strategy.types.forEach((type) => {
|
||||||
emitter.off(`${type}:enter`, onEnter);
|
emitter.off(`${type}:enter`, onEnter);
|
||||||
emitter.off(`${type}:leave`, onLeave);
|
emitter.off(`${type}:leave`, onLeave);
|
||||||
emitter.off(`${type}:click`, onClick);
|
emitter.off(`${type}:click`, onClick);
|
||||||
});
|
});
|
||||||
emitter.off("grid:click", onGridClick);
|
emitter.off("grid:click", onGridClick);
|
||||||
};
|
};
|
||||||
}, [phase, mode, selection.selectedIds, setSelection]);
|
}, [phase, mode]);
|
||||||
|
|
||||||
return <EditorOutlinerSync />;
|
return <EditorOutlinerSync />;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user