af8b9fe00f
Full-featured house/apartment floor plan editor with: - Turborepo monorepo (React/Vite client, Fastify/Prisma server, shared Zod schemas) - 2D room editor with walls, doors, windows, furniture, electrical elements - 3D room preview with Three.js (auto-hide nearest walls, bird's eye default) - Wall projection views with interactive drag (elevation, position) - Apartment floor plan view with room positioning - Copy/paste, alignment tools, measurement tool, annotations - Item-attached annotations with leader lines (visible on projections) - Door open direction (LEFT/RIGHT/INWARD/OUTWARD) with swing arc - Floor type textures (wood, tile, concrete, laminate, herringbone) - Wall color picker for 3D view - Furniture: bed, desk, wardrobe, sofa, table, chair, shelf, nightstand, dresser, bookcase, TV (with stand toggle), AC unit - Furniture elevation support (wall-mounted items) - Auto-save with dirty state tracking, batch save API - Rotation-aware collision detection (SAT/OBB) with 3D elevation check - Rotation-aware hit testing - i18n (English/Russian) with locale-aware number formatting - Dark mode with system preference detection - Undo/redo, keyboard shortcuts, scale bar - PDF/PNG/JSON export and JSON import - Focus trap modal, toast notifications, tooltips - Responsive layout with overlay palettes
66 lines
1.6 KiB
TypeScript
66 lines
1.6 KiB
TypeScript
import { Group, Rect, Text } from 'react-konva';
|
|
import type { ProjectedFurniture } from '../utils/projectionMapping';
|
|
import { projectionToPixel } from '../utils/projectionMapping';
|
|
|
|
interface ProjectionFurnitureProps {
|
|
readonly projected: ProjectedFurniture;
|
|
readonly wallHeight: number;
|
|
readonly scale: number;
|
|
readonly padding: number;
|
|
readonly isSelected: boolean;
|
|
readonly onClick: () => void;
|
|
}
|
|
|
|
const TYPE_COLORS: Record<string, string> = {
|
|
SHELF: '#d4a574',
|
|
BOOKCASE: '#b8860b',
|
|
WARDROBE: '#8b7355',
|
|
DRESSER: '#a0845c',
|
|
DESK: '#c9a96e',
|
|
TABLE: '#deb887',
|
|
};
|
|
|
|
/** Render a furniture item in wall elevation view. */
|
|
export function ProjectionFurniture({
|
|
projected,
|
|
wallHeight,
|
|
scale,
|
|
padding,
|
|
isSelected,
|
|
onClick,
|
|
}: ProjectionFurnitureProps) {
|
|
const { rect, item } = projected;
|
|
|
|
const topLeft = projectionToPixel(rect.x, rect.y + rect.height, wallHeight, scale, padding);
|
|
const pxWidth = rect.width * scale;
|
|
const pxHeight = rect.height * scale;
|
|
|
|
const color = TYPE_COLORS[item.type] ?? '#a0845c';
|
|
|
|
return (
|
|
<Group onClick={onClick}>
|
|
<Rect
|
|
x={topLeft.x}
|
|
y={topLeft.y}
|
|
width={pxWidth}
|
|
height={pxHeight}
|
|
fill={isSelected ? '#dbeafe' : color}
|
|
stroke={isSelected ? '#2563eb' : '#6b5b3a'}
|
|
strokeWidth={isSelected ? 2 : 1}
|
|
opacity={0.7}
|
|
/>
|
|
{/* Furniture label */}
|
|
<Text
|
|
x={topLeft.x}
|
|
y={topLeft.y + pxHeight / 2 - 5}
|
|
width={pxWidth}
|
|
text={item.label ?? item.type}
|
|
align="center"
|
|
fontSize={9}
|
|
fill={isSelected ? '#1e40af' : '#3b2f1e'}
|
|
ellipsis
|
|
/>
|
|
</Group>
|
|
);
|
|
}
|