..
This commit is contained in:
parent
9689bfcc18
commit
62a385e4c9
1 changed files with 2649 additions and 809 deletions
|
|
@ -1,10 +1,10 @@
|
|||
import { useId } from 'react';
|
||||
import type { FlooringPlan, Geometry, Point, Room, SkirtingPlan, WallEdge } from '../lib/types';
|
||||
import { area, mm } from '../lib/format';
|
||||
import { area, cutMm, mm } from '../lib/format';
|
||||
import { signedArea } from '../lib/geometry';
|
||||
import { isSkirtingCut } from '../lib/cut-list';
|
||||
import { skirtingPolygon } from '../lib/skirting-polygon';
|
||||
import { dimensionLabels } from '../lib/dimension-labels';
|
||||
import { dimensionLabels, wallDimensionBox } from '../lib/dimension-labels';
|
||||
import type { DimensionLabel, LabelBox } from '../lib/dimension-labels';
|
||||
import { ClosedBadge } from './Fields';
|
||||
|
||||
|
|
@ -113,6 +113,9 @@ interface Props {
|
|||
selectedPiece: string | null;
|
||||
select?: (id: string) => void;
|
||||
interactive?: boolean;
|
||||
completedBoards?: string[];
|
||||
highlightedPieces?: string[];
|
||||
searchActive?: boolean;
|
||||
}
|
||||
|
||||
export function FloorPlan({
|
||||
|
|
@ -123,6 +126,9 @@ export function FloorPlan({
|
|||
selectedPiece,
|
||||
select,
|
||||
interactive = true,
|
||||
completedBoards = [],
|
||||
highlightedPieces = [],
|
||||
searchActive = false,
|
||||
}: Props) {
|
||||
const hatch = useId().replaceAll(':', '');
|
||||
const allPoints = geometry.edges.flatMap((edge) => [edge.start, edge.end]);
|
||||
|
|
@ -145,14 +151,17 @@ export function FloorPlan({
|
|||
const y = center.y - ((orientation * (piece.end.x - piece.start.x)) / length) * span * 0.032;
|
||||
skirtLabels.set(piece.id, { x, y });
|
||||
reserved.push({
|
||||
minX: x - font * 1.2,
|
||||
maxX: x + font * 1.2,
|
||||
minX: x - font * 1.4,
|
||||
maxX: x + font * 1.4,
|
||||
minY: y - font * 0.7,
|
||||
maxY: y + font * 0.7,
|
||||
});
|
||||
}
|
||||
const labels = dimensionLabels(geometry.edges, span, orientation, reserved);
|
||||
const boxes = [...reserved, ...labels.map((label) => label.box)];
|
||||
const boxes = [
|
||||
...reserved,
|
||||
...labels.map((label, i) => wallDimensionBox(geometry.edges[i], label, span)),
|
||||
];
|
||||
const left = Math.min(minX, ...boxes.map((b) => b.minX)) - span * 0.035;
|
||||
const top = Math.min(minY, ...boxes.map((b) => b.minY)) - span * 0.035;
|
||||
const width = Math.max(maxX, ...boxes.map((b) => b.maxX)) + span * 0.035 - left;
|
||||
|
|
@ -161,7 +170,10 @@ export function FloorPlan({
|
|||
<section className="drawing-card" aria-label="Floor plan preview">
|
||||
<div className="drawing-header">
|
||||
<h2>
|
||||
Floor plan <small>{area(geometry.area)} m²</small>
|
||||
Floor plan{' '}
|
||||
<small>
|
||||
{room.name} · {area(geometry.area)} m²
|
||||
</small>
|
||||
</h2>
|
||||
<ClosedBadge closed={geometry.closed && geometry.valid} />
|
||||
</div>
|
||||
|
|
@ -212,6 +224,8 @@ export function FloorPlan({
|
|||
{geometry.closed && <polygon points={points(geometry.vertices)} fill="#fff" />}
|
||||
{floorPieces.map((piece) => {
|
||||
const cut = piece.cut || piece.reused;
|
||||
const done = completedBoards.includes(`floor:${piece.stockId}`);
|
||||
const highlighted = highlightedPieces.includes(piece.id);
|
||||
const x =
|
||||
(Math.min(...piece.polygon.map((p) => p.x)) +
|
||||
Math.max(...piece.polygon.map((p) => p.x))) /
|
||||
|
|
@ -223,8 +237,11 @@ export function FloorPlan({
|
|||
return (
|
||||
<g
|
||||
key={piece.id}
|
||||
className={`floor-piece ${cut ? 'svg-cut floor-cut' : 'uncut'}`}
|
||||
className={`floor-piece ${cut ? 'svg-cut floor-cut' : 'uncut'} ${done ? 'is-done' : ''} ${highlighted ? 'is-highlighted' : searchActive ? 'search-muted' : ''}`}
|
||||
data-material="flooring"
|
||||
data-board-key={`floor:${piece.stockId}`}
|
||||
data-done={done}
|
||||
data-highlighted={highlighted}
|
||||
data-piece-id={piece.id}
|
||||
role={cut && interactive ? 'button' : undefined}
|
||||
tabIndex={cut && interactive ? 0 : undefined}
|
||||
|
|
@ -246,11 +263,30 @@ export function FloorPlan({
|
|||
<polygon
|
||||
points={points(piece.polygon)}
|
||||
fill={
|
||||
cut ? (selectedPiece === piece.id ? '#ceddf9' : `url(#${hatch})`) : '#f5f6f8'
|
||||
done
|
||||
? '#dbe9e7'
|
||||
: cut
|
||||
? selectedPiece === piece.id
|
||||
? '#ceddf9'
|
||||
: `url(#${hatch})`
|
||||
: '#f5f6f8'
|
||||
}
|
||||
stroke={cut ? '#1647ab' : '#9baabd'}
|
||||
strokeWidth={span * (selectedPiece === piece.id ? 0.003 : 0.0012)}
|
||||
/>
|
||||
{done && !cut && (
|
||||
<text
|
||||
className="completion-mark"
|
||||
x={x}
|
||||
y={y}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
fontSize={font}
|
||||
fill="#20594f"
|
||||
>
|
||||
✓
|
||||
</text>
|
||||
)}
|
||||
{cut && (
|
||||
<>
|
||||
<g
|
||||
|
|
@ -258,11 +294,11 @@ export function FloorPlan({
|
|||
pointerEvents="none"
|
||||
>
|
||||
<rect
|
||||
x={-font * 1.1}
|
||||
x={-font * 1.3}
|
||||
y={-font * 0.55}
|
||||
width={font * 2.2}
|
||||
width={font * 2.6}
|
||||
height={font * 1.1}
|
||||
fill="#1647ab"
|
||||
fill={done ? '#27655d' : '#1647ab'}
|
||||
/>
|
||||
<text
|
||||
textAnchor="middle"
|
||||
|
|
@ -272,10 +308,11 @@ export function FloorPlan({
|
|||
fontWeight="600"
|
||||
>
|
||||
{piece.id}
|
||||
{done ? ' ✓' : ''}
|
||||
</text>
|
||||
</g>
|
||||
<title>
|
||||
{piece.id}: {mm(piece.length)} × {mm(piece.width)} mm
|
||||
{piece.id}: {cutMm(piece.length)} × {cutMm(piece.width)} mm
|
||||
</title>
|
||||
</>
|
||||
)}
|
||||
|
|
@ -287,6 +324,8 @@ export function FloorPlan({
|
|||
))}
|
||||
{skirtPieces.map((piece) => {
|
||||
const cut = isSkirtingCut(piece, skirting);
|
||||
const done = completedBoards.includes(`skirting:${piece.stockId}`);
|
||||
const highlighted = highlightedPieces.includes(piece.id);
|
||||
const label = skirtLabels.get(piece.id);
|
||||
// Each template uses its true wall-facing and room-facing edges, including mitres.
|
||||
const thickness = piece.template ? Math.max(...piece.template.map((p) => p.y)) : 0;
|
||||
|
|
@ -294,8 +333,11 @@ export function FloorPlan({
|
|||
return (
|
||||
<g
|
||||
key={piece.id}
|
||||
className={`skirt-piece ${cut ? 'svg-cut skirt-cut' : 'uncut'}`}
|
||||
className={`skirt-piece ${cut ? 'svg-cut skirt-cut' : 'uncut'} ${done ? 'is-done' : ''} ${highlighted ? 'is-highlighted' : searchActive ? 'search-muted' : ''}`}
|
||||
data-material="skirting"
|
||||
data-board-key={`skirting:${piece.stockId}`}
|
||||
data-done={done}
|
||||
data-highlighted={highlighted}
|
||||
data-piece-id={piece.id}
|
||||
role={cut && interactive ? 'button' : undefined}
|
||||
tabIndex={cut && interactive ? 0 : undefined}
|
||||
|
|
@ -316,7 +358,15 @@ export function FloorPlan({
|
|||
>
|
||||
<polygon
|
||||
points={points(polygon)}
|
||||
fill={cut ? (selectedPiece === piece.id ? '#17263f' : '#b74900') : '#d8d5cd'}
|
||||
fill={
|
||||
done
|
||||
? '#27655d'
|
||||
: cut
|
||||
? selectedPiece === piece.id
|
||||
? '#17263f'
|
||||
: '#b74900'
|
||||
: '#d8d5cd'
|
||||
}
|
||||
stroke={cut ? '#8d3800' : '#79756b'}
|
||||
strokeWidth={span * 0.001}
|
||||
/>
|
||||
|
|
@ -330,14 +380,27 @@ export function FloorPlan({
|
|||
strokeWidth={span * 0.025}
|
||||
/>
|
||||
)}
|
||||
{done && !cut && (
|
||||
<text
|
||||
className="completion-mark"
|
||||
x={(piece.start.x + piece.end.x) / 2}
|
||||
y={(piece.start.y + piece.end.y) / 2}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
fontSize={font}
|
||||
fill="#20594f"
|
||||
>
|
||||
✓
|
||||
</text>
|
||||
)}
|
||||
{label && (
|
||||
<g className="skirting-code" transform={`translate(${label.x} ${label.y})`}>
|
||||
<rect
|
||||
x={-font * 1.1}
|
||||
x={-font * 1.3}
|
||||
y={-font * 0.58}
|
||||
width={font * 2.2}
|
||||
width={font * 2.6}
|
||||
height={font * 1.16}
|
||||
fill="#b74900"
|
||||
fill={done ? '#27655d' : '#b74900'}
|
||||
stroke="#8d3800"
|
||||
strokeWidth={span * 0.0012}
|
||||
/>
|
||||
|
|
@ -349,12 +412,13 @@ export function FloorPlan({
|
|||
fontWeight="700"
|
||||
>
|
||||
{piece.id}
|
||||
{done ? ' ✓' : ''}
|
||||
</text>
|
||||
</g>
|
||||
)}
|
||||
{cut && (
|
||||
<title>
|
||||
{piece.id}: {mm(piece.length)} mm · {mm(piece.leftMitre)}° /{' '}
|
||||
{piece.id}: {cutMm(piece.length)} mm · {mm(piece.leftMitre)}° /{' '}
|
||||
{mm(piece.rightMitre)}°
|
||||
</title>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue