Make entrance a draggable/resizable square with selection and size controls

This commit is contained in:
2026-10-17 20:02:57 -04:00
parent ced33b672f
commit 752a87cc0f
5 changed files with 238 additions and 132 deletions
+46 -24
View File
@@ -14,11 +14,8 @@ interface DragState {
offsetX: number;
offsetY: number;
resizing?: boolean;
edge?: 'se' | 'e' | 's' | 'nw' | 'ne' | 'sw' | 'n' | 'w';
}
const MIN_SIZE = 10;
export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect }: RoomCanvasProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const [drag, setDrag] = useState<DragState | null>(null);
@@ -42,9 +39,20 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect }:
ctx.strokeRect(0, 0, state.room.width, state.room.height);
if (state.entrance) {
const e = state.entrance;
const w = e.width ?? 20;
const h = e.height ?? 20;
ctx.fillStyle = '#4caf50';
ctx.fillRect(state.entrance.x - 4, state.entrance.y - 4, 8, 8);
ctx.fillText('ENTRANCE', state.entrance.x + 6, state.entrance.y + 4);
ctx.fillRect(e.x, e.y, w, h);
ctx.strokeStyle = selectedId === '__entrance__' ? '#ff5722' : '#333';
ctx.lineWidth = selectedId === '__entrance__' ? 3 : 1;
ctx.strokeRect(e.x, e.y, w, h);
ctx.fillStyle = '#000';
ctx.font = '10px sans-serif';
ctx.fillText('ENTRANCE', e.x + 2, e.y + 12);
// resize handle (SE)
ctx.fillStyle = selectedId === '__entrance__' ? '#ff5722' : '#666';
ctx.fillRect(e.x + w - 4, e.y + h - 4, 8, 8);
}
for (const f of state.furniture) {
@@ -75,15 +83,21 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect }:
const handleMouseDown = (e: React.MouseEvent<HTMLCanvasElement>) => {
const pos = getMousePos(e);
if (selectedId) {
const f = state.furniture.find((x) => x.id === selectedId);
if (f) {
const seX = f.shape.x + f.shape.width;
const seY = f.shape.y + f.shape.height;
if (Math.abs(pos.x - seX) < 8 && Math.abs(pos.y - seY) < 8) {
setDrag({ id: selectedId, offsetX: 0, offsetY: 0, resizing: true, edge: 'se' });
return;
}
if (state.entrance) {
const en = state.entrance;
const w = en.width ?? 20;
const h = en.height ?? 20;
const handleX = en.x + w - 4;
const handleY = en.y + h - 4;
if (Math.abs(pos.x - handleX) <= 6 && Math.abs(pos.y - handleY) <= 6) {
onSelect('__entrance__');
setDrag({ id: '__entrance__', offsetX: 0, offsetY: 0, resizing: true });
return;
}
if (pointInRect(pos, { x: en.x, y: en.y, w, h })) {
onSelect('__entrance__');
setDrag({ id: '__entrance__', offsetX: pos.x - en.x, offsetY: pos.y - en.y });
return;
}
}
for (let i = state.furniture.length - 1; i >= 0; i--) {
@@ -101,18 +115,22 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect }:
const handleMouseMove = (e: React.MouseEvent<HTMLCanvasElement>) => {
if (!drag) return;
const pos = getMousePos(e);
if (drag.id === '__entrance__' && state.entrance) {
const en = state.entrance;
if (drag.resizing) {
const newW = Math.max(10, pos.x - en.x);
const newH = Math.max(10, pos.y - en.y);
onStateChange({ ...state, entrance: { ...en, width: newW, height: newH } });
} else {
onStateChange({
...state,
entrance: { ...en, x: pos.x - drag.offsetX, y: pos.y - drag.offsetY },
});
}
return;
}
const updated = state.furniture.map((f) => {
if (f.id !== drag.id) return f;
if (drag.resizing && drag.edge === 'se') {
return {
...f,
shape: {
...f.shape,
width: Math.max(MIN_SIZE, pos.x - f.shape.x),
height: Math.max(MIN_SIZE, pos.y - f.shape.y),
},
};
}
return {
...f,
shape: {
@@ -126,6 +144,10 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect }:
};
const handleMouseUp = () => {
if (drag && drag.id !== '__entrance__') {
const updated = state.furniture.map((f) => (f.id === drag.id ? { ...f, placed: true } : f));
onStateChange({ ...state, furniture: updated });
}
setDrag(null);
};