wip
This commit is contained in:
commit
d8f2407ddf
44 changed files with 10377 additions and 0 deletions
166
src/components/CutPreview.tsx
Normal file
166
src/components/CutPreview.tsx
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
import { Fragment } from 'react';
|
||||
import type { FlooringPiece, SkirtingPiece } from '../lib/types';
|
||||
import {
|
||||
exactMm,
|
||||
flooringTemplate,
|
||||
fullPreviewBox,
|
||||
skirtingEndBoxes,
|
||||
skirtingTemplate,
|
||||
viewBox,
|
||||
} from '../lib/cut-preview';
|
||||
|
||||
export function FlooringPreview({
|
||||
piece,
|
||||
compact = false,
|
||||
}: {
|
||||
piece: FlooringPiece;
|
||||
compact?: boolean;
|
||||
}) {
|
||||
const template = flooringTemplate(piece);
|
||||
const Wrapper = compact ? 'span' : 'figure';
|
||||
return (
|
||||
<Wrapper className={`flooring-preview-figure ${compact ? 'compact-preview' : 'exact-preview'}`}>
|
||||
<svg
|
||||
className="flooring-preview"
|
||||
viewBox={viewBox(fullPreviewBox(piece.length, piece.width))}
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
aria-hidden={compact || undefined}
|
||||
role={compact ? undefined : 'img'}
|
||||
aria-label={compact ? undefined : `Flooring shape ${piece.id}`}
|
||||
>
|
||||
<polygon
|
||||
points={template.map((point) => `${point.x},${point.y}`).join(' ')}
|
||||
fill="#dbeafe"
|
||||
stroke="#2563eb"
|
||||
strokeWidth={compact ? 1 : 1.5}
|
||||
vectorEffect="non-scaling-stroke"
|
||||
/>
|
||||
{!compact &&
|
||||
piece.shaped &&
|
||||
template.map((point, index) => (
|
||||
<text
|
||||
key={index}
|
||||
x={point.x}
|
||||
y={point.y - Math.max(piece.length, piece.width) * 0.015}
|
||||
textAnchor="middle"
|
||||
fontSize={Math.max(piece.length, piece.width) * 0.025}
|
||||
fill="#1e40af"
|
||||
>
|
||||
{index + 1}
|
||||
</text>
|
||||
))}
|
||||
</svg>
|
||||
{!compact && (
|
||||
<figcaption>
|
||||
Length {exactMm(piece.length)} mm × width {exactMm(piece.width)} mm · top view, to scale
|
||||
</figcaption>
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export function SkirtingPreview({
|
||||
piece,
|
||||
thickness,
|
||||
compact = false,
|
||||
}: {
|
||||
piece: SkirtingPiece;
|
||||
thickness: number;
|
||||
compact?: boolean;
|
||||
}) {
|
||||
const template = skirtingTemplate(piece, thickness);
|
||||
const Wrapper = compact ? 'span' : 'div';
|
||||
const EndWrapper = compact ? 'span' : 'figure';
|
||||
const Grid = compact ? 'span' : 'div';
|
||||
if (template.length !== 4) return <span className="helper">Shape template unavailable</span>;
|
||||
const boxes = skirtingEndBoxes(template, thickness);
|
||||
const points = template.map((point) => `${point.x},${point.y}`).join(' ');
|
||||
const ends = [
|
||||
{ side: 'left', back: template[0], front: template[3], box: boxes.left },
|
||||
{ side: 'right', back: template[1], front: template[2], box: boxes.right },
|
||||
] as const;
|
||||
return (
|
||||
<Wrapper className={`skirting-preview ${compact ? 'compact-preview' : 'exact-preview'}`}>
|
||||
{!compact && (
|
||||
<figure className="skirting-full-preview">
|
||||
<svg
|
||||
viewBox={viewBox(fullPreviewBox(piece.length, thickness))}
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
role="img"
|
||||
aria-label={`Full skirting shape ${piece.id}`}
|
||||
>
|
||||
<polygon
|
||||
points={points}
|
||||
fill="#ffedd5"
|
||||
stroke="#c2410c"
|
||||
strokeWidth={1}
|
||||
vectorEffect="non-scaling-stroke"
|
||||
/>
|
||||
</svg>
|
||||
<figcaption>
|
||||
Full length {exactMm(piece.length)} mm × thickness {exactMm(thickness)} mm · top view,
|
||||
to scale
|
||||
</figcaption>
|
||||
</figure>
|
||||
)}
|
||||
<Grid className="skirting-end-grid">
|
||||
{ends.map((end, index) => (
|
||||
<Fragment key={end.side}>
|
||||
<EndWrapper className={`skirting-end ${end.side}-end`}>
|
||||
<svg
|
||||
className="skirting-end-preview"
|
||||
data-end={end.side}
|
||||
viewBox={viewBox(end.box)}
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
overflow="hidden"
|
||||
aria-hidden={compact || undefined}
|
||||
role={compact ? undefined : 'img'}
|
||||
aria-label={
|
||||
compact
|
||||
? undefined
|
||||
: `${end.side === 'left' ? 'Left' : 'Right'} mitre close-up ${piece.id}`
|
||||
}
|
||||
>
|
||||
<polygon
|
||||
points={points}
|
||||
fill="#ffedd5"
|
||||
stroke="#c2410c"
|
||||
strokeWidth={1}
|
||||
vectorEffect="non-scaling-stroke"
|
||||
/>
|
||||
<line
|
||||
className="skirting-cut-edge"
|
||||
data-end={end.side}
|
||||
x1={end.back.x}
|
||||
y1={end.back.y}
|
||||
x2={end.front.x}
|
||||
y2={end.front.y}
|
||||
stroke="#9a3412"
|
||||
strokeWidth={compact ? 1.5 : 2.5}
|
||||
vectorEffect="non-scaling-stroke"
|
||||
/>
|
||||
</svg>
|
||||
{!compact && (
|
||||
<figcaption>
|
||||
{index === 0 ? 'Left' : 'Right'} end ·{' '}
|
||||
{exactMm(Math.abs(index === 0 ? piece.leftMitre : piece.rightMitre))}°
|
||||
</figcaption>
|
||||
)}
|
||||
</EndWrapper>
|
||||
{index === 0 && (
|
||||
<span className="board-break" aria-hidden="true">
|
||||
//
|
||||
</span>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</Grid>
|
||||
{!compact && (
|
||||
<p className="helper">
|
||||
End close-ups; the middle is omitted. The back edge faces the wall, the front edge faces
|
||||
the room. Both axes use the same scale.
|
||||
</p>
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue