@struq/skill 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -100
- package/package.json +5 -3
- package/skills/struq/SKILL.md +168 -165
- package/skills/struq/blocks/CATALOG.md +49 -46
- package/skills/struq/blocks/backgrounds/background-aurora.tsx +2 -1
- package/skills/struq/blocks/backgrounds/background-facets.tsx +5 -3
- package/skills/struq/blocks/backgrounds/background-silk.tsx +6 -2
- package/skills/struq/blocks/catalog.json +50 -5
- package/skills/struq/blocks/film.json +11 -7
- package/skills/struq/blocks/footers/footer-contact.tsx +8 -78
- package/skills/struq/blocks/headers/header-bar.tsx +144 -29
- package/skills/struq/blocks/headers/menu-curtain.tsx +36 -19
- package/skills/struq/blocks/heroes/hero-poster.tsx +36 -10
- package/skills/struq/blocks/kit/HeroCanvas.tsx +6 -2
- package/skills/struq/blocks/kit/SplitHeadline.tsx +2 -1
- package/skills/struq/blocks/kit/accent.ts +29 -3
- package/skills/struq/blocks/kit/blocks.css +530 -6
- package/skills/struq/blocks/kit/feel.ts +183 -0
- package/skills/struq/blocks/kit/gsap.ts +9 -6
- package/skills/struq/blocks/kit/middle/PlaceHours.tsx +254 -0
- package/skills/struq/blocks/kit/middle/ProcessStrip.tsx +428 -0
- package/skills/struq/blocks/kit/middle/ServicesList.tsx +496 -0
- package/skills/struq/blocks/kit/middle/entrance.ts +98 -0
- package/skills/struq/blocks/kit/middle/hours.ts +154 -0
- package/skills/struq/blocks/kit/middle/index.ts +14 -0
- package/skills/struq/blocks/kit/motion/HoverLink.tsx +33 -0
- package/skills/struq/blocks/kit/motion/ImageCurtain.tsx +79 -0
- package/skills/struq/blocks/kit/motion/MaskWipe.tsx +91 -0
- package/skills/struq/blocks/kit/motion/Pressable.tsx +58 -0
- package/skills/struq/blocks/kit/motion/RevealText.tsx +103 -0
- package/skills/struq/blocks/kit/motion/Rise.tsx +77 -0
- package/skills/struq/blocks/kit/motion/ScrubLine.tsx +108 -0
- package/skills/struq/blocks/kit/motion/index.ts +11 -0
- package/skills/struq/blocks/kit/quiet/Close.tsx +178 -52
- package/skills/struq/blocks/kit/quiet/Statement.tsx +3 -2
- package/skills/struq/blocks/kit/subject/AppScreen.tsx +388 -0
- package/skills/struq/blocks/kit/subject/Document.tsx +742 -0
- package/skills/struq/blocks/kit/subject/Mark.tsx +44 -0
- package/skills/struq/blocks/kit/subject/PatternPaper.tsx +410 -0
- package/skills/struq/blocks/kit/subject/SectionDrawing.tsx +349 -0
- package/skills/struq/blocks/kit/subject/Ticket.tsx +354 -0
- package/skills/struq/blocks/kit/subject/build.ts +66 -0
- package/skills/struq/blocks/kit/subject/draw.ts +196 -0
- package/skills/struq/blocks/kit/subject/drawings.ts +373 -0
- package/skills/struq/blocks/kit/subject/format.ts +122 -0
- package/skills/struq/blocks/kit/subject/index.ts +11 -0
- package/skills/struq/blocks/kit/subject/patterns.ts +204 -0
- package/skills/struq/blocks/preloaders/preloader-stack.tsx +75 -22
- package/skills/struq/blocks/showpieces/hero-opener.tsx +55 -7
- package/skills/struq/blocks/showpieces/image-to-fullbleed.tsx +64 -6
- package/skills/struq/blocks/showpieces/manifesto-scrub.tsx +56 -3
- package/skills/struq/blocks/showpieces/panel-wipe.tsx +93 -11
- package/skills/struq/blocks/showpieces/wordmark-finale.tsx +38 -5
- package/skills/struq/canon/canon.json +1 -1
- package/skills/struq/references/art-direction.md +51 -0
- package/skills/struq/scripts/blocks.mjs +56 -20
- package/skills/struq/scripts/canon.mjs +36 -7
- package/skills/struq/scripts/check.mjs +417 -11
- package/skills/struq/scripts/draw.mjs +49 -0
- package/skills/struq/scripts/film-check.mjs +28 -4
- package/skills/struq/scripts/gate.mjs +13 -3
- package/skills/struq/scripts/hand.mjs +222 -0
- package/skills/struq/scripts/images.mjs +12 -3
- package/skills/struq/scripts/look.mjs +1184 -0
- package/skills/struq/scripts/stamp.mjs +47 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// components/struq/kit/subject/SectionDrawing.tsx (Struq recipe)
|
|
3
|
+
// A technical section drawing for trades and makers, drawn to the conventions a draughtsman would sign: every cut
|
|
4
|
+
// face hatched in its material's standard pattern (steel ANSI32, brass and copper ANSI33, brick ANSI31, ceramic
|
|
5
|
+
// crossed, timber end grain crossed, finished wood in grain, concrete stippled with aggregate, plaster dotted, mineral
|
|
6
|
+
// wool as the batt coil, thin sections solid), touching parts hatched at different angles, outlines heavy and
|
|
7
|
+
// everything else thin, the centre line in dash and dot, dimension lines with arrowheads and values, numbered
|
|
8
|
+
// callouts on leaders, and a title block with the names, the title and the caller's fields. Line weights and hatch
|
|
9
|
+
// spacing stay the same on a phone and a wide screen.
|
|
10
|
+
//
|
|
11
|
+
// <SectionDrawing template="compression-fitting" title="Knelkoppeling 15 mm" locale="nl-NL"
|
|
12
|
+
// names={{ pipe: "Koperen buis 15 mm", olive: "Knelring", nut: "Wartel", body: "Koppelstuk" }}
|
|
13
|
+
// fields={[{ label: "Schaal", value: "2:1" }, { label: "Tekening", value: "KR-015" }]} />
|
|
14
|
+
//
|
|
15
|
+
// Props
|
|
16
|
+
// - template: "compression-fitting", "mug", "bowl", "door-jamb" or "wall" (drawings.ts), or
|
|
17
|
+
// - drawing: your own Drawing ({ view, parts, lines?, dims?, callouts?, names?, title?, unit? }; parts are SVG paths
|
|
18
|
+
// in drawing units, y down, each with a material). One of the two.
|
|
19
|
+
// - names: the callouts' names by key, in the client's words (the template's English ones otherwise).
|
|
20
|
+
// - title: the title block's title (the template's otherwise). subtitle: a line under it.
|
|
21
|
+
// - fields: { label, value }[] for the title block (scale, drawing number, date, maker), only what the caller has.
|
|
22
|
+
// - labels: { unit? }: the units line ("Dimensions in mm"), English or Dutch by locale otherwise.
|
|
23
|
+
// - paper: "scene" (default) the drawing in the scene's ink on its ground, so an inverse scene reads as a dark sheet;
|
|
24
|
+
// "print" a printed sheet in the palette's light colour.
|
|
25
|
+
// - locale (default "en-GB"), caption (a line under the sheet, in the scene's ink), build ("scroll" default, or
|
|
26
|
+
// "static"), className (on the figure).
|
|
27
|
+
//
|
|
28
|
+
// Mounting: inside any section, in six or more of twelve columns on a wide screen, where the title block stands
|
|
29
|
+
// beside the drawing; under 48rem it stands under it. The root is a <figure data-subject="section">.
|
|
30
|
+
//
|
|
31
|
+
// Motion: tied to the scroll. The outlines draw themselves as the sheet comes up through the view, the hatching
|
|
32
|
+
// settles into the cut faces, then the centre line, the dimensions and the numbered callouts set in. Reduced motion,
|
|
33
|
+
// build="static" or no scripts: the finished drawing.
|
|
34
|
+
import { useId, useRef, type ReactNode } from "react";
|
|
35
|
+
import { useScrollBuild } from "./build";
|
|
36
|
+
import { usePen, type Pt } from "./draw";
|
|
37
|
+
import { DRAWINGS, type Drawing, type DrawingDim, type DrawingName, type Material } from "./drawings";
|
|
38
|
+
import { FIGURES, languageOf, number as numberText } from "./format";
|
|
39
|
+
|
|
40
|
+
type Props = {
|
|
41
|
+
template?: DrawingName;
|
|
42
|
+
drawing?: Drawing;
|
|
43
|
+
names?: Record<string, string>;
|
|
44
|
+
title?: string;
|
|
45
|
+
subtitle?: string;
|
|
46
|
+
fields?: { label: string; value: string }[];
|
|
47
|
+
labels?: { unit?: string };
|
|
48
|
+
paper?: "scene" | "print";
|
|
49
|
+
locale?: string;
|
|
50
|
+
caption?: string;
|
|
51
|
+
build?: "scroll" | "static";
|
|
52
|
+
className?: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const UNIT_LINE: Record<string, string> = { en: "Dimensions in {unit}", nl: "Maten in {unit}" };
|
|
56
|
+
const SMALL = "text-[0.75rem] leading-[1.45]";
|
|
57
|
+
|
|
58
|
+
/** The hatch tiles, drawn in screen pixels through `u` and turned by the part's angle. */
|
|
59
|
+
function Hatch({ id, material, angle, u }: { id: string; material: Material; angle: number; u: (px: number) => number }) {
|
|
60
|
+
const s = u(6.5);
|
|
61
|
+
const line = { stroke: "var(--ink)", strokeWidth: u(0.6), fill: "none" } as const;
|
|
62
|
+
const dot = { fill: "var(--ink)" } as const;
|
|
63
|
+
const common = { id, patternUnits: "userSpaceOnUse" as const, patternTransform: `rotate(${angle})` };
|
|
64
|
+
switch (material) {
|
|
65
|
+
case "brick":
|
|
66
|
+
return (
|
|
67
|
+
<pattern {...common} width={s} height={s}>
|
|
68
|
+
<path d={`M0 ${s / 2} H${s}`} style={line} />
|
|
69
|
+
</pattern>
|
|
70
|
+
);
|
|
71
|
+
case "steel":
|
|
72
|
+
return (
|
|
73
|
+
<pattern {...common} width={s} height={s * 2.2}>
|
|
74
|
+
<path d={`M0 ${s * 0.6} H${s} M0 ${s * 1.05} H${s}`} style={line} />
|
|
75
|
+
</pattern>
|
|
76
|
+
);
|
|
77
|
+
case "brass":
|
|
78
|
+
case "copper":
|
|
79
|
+
return (
|
|
80
|
+
<pattern {...common} width={s * 1.8} height={s * 2}>
|
|
81
|
+
<path d={`M0 ${s * 0.5} H${s * 1.8}`} style={line} />
|
|
82
|
+
<path d={`M0 ${s * 1.5} H${s * 1.8}`} style={{ ...line, strokeDasharray: `${s * 1.1} ${s * 0.7}` }} />
|
|
83
|
+
</pattern>
|
|
84
|
+
);
|
|
85
|
+
case "ceramic":
|
|
86
|
+
return (
|
|
87
|
+
<pattern {...common} width={s * 1.6} height={s * 1.6}>
|
|
88
|
+
<path d={`M0 ${s * 0.8} H${s * 1.6} M${s * 0.8} 0 V${s * 1.6}`} style={line} />
|
|
89
|
+
</pattern>
|
|
90
|
+
);
|
|
91
|
+
case "wood":
|
|
92
|
+
return (
|
|
93
|
+
<pattern {...common} width={s * 8} height={s * 1.7}>
|
|
94
|
+
<path d={`M0 ${s * 0.85} Q${s * 2} ${s * 0.25} ${s * 4} ${s * 0.85} T${s * 8} ${s * 0.85}`} style={line} />
|
|
95
|
+
</pattern>
|
|
96
|
+
);
|
|
97
|
+
case "concrete":
|
|
98
|
+
case "block":
|
|
99
|
+
return (
|
|
100
|
+
<pattern id={id} patternUnits="userSpaceOnUse" width={s * 7} height={s * 7}>
|
|
101
|
+
{[
|
|
102
|
+
[0.8, 1.1], [3.4, 0.6], [5.6, 2.2], [1.9, 3.4], [4.6, 4.6], [0.6, 5.6], [6.3, 6.1], [3, 6.4],
|
|
103
|
+
].map(([x, y]) => (
|
|
104
|
+
<circle key={`${x}-${y}`} cx={x * s} cy={y * s} r={u(0.75)} style={dot} />
|
|
105
|
+
))}
|
|
106
|
+
<path d={`M${2.6 * s} ${2.0 * s} l${s * 0.7} ${s * 0.15} l${-s * 0.45} ${s * 0.55} Z`} style={line} />
|
|
107
|
+
<path d={`M${5.2 * s} ${5.0 * s} l${-s * 0.6} ${s * 0.45} l${s * 0.7} ${s * 0.25} Z`} style={line} />
|
|
108
|
+
<path d={`M${0.9 * s} ${4.3 * s} l${s * 0.55} ${-s * 0.4} l${s * 0.1} ${s * 0.6} Z`} style={line} />
|
|
109
|
+
{material === "block" ? <path d={`M0 ${s * 7} L${s * 7} 0`} style={line} /> : null}
|
|
110
|
+
</pattern>
|
|
111
|
+
);
|
|
112
|
+
case "plaster":
|
|
113
|
+
return (
|
|
114
|
+
<pattern id={id} patternUnits="userSpaceOnUse" width={s * 1.4} height={s * 1.4}>
|
|
115
|
+
<circle cx={s * 0.35} cy={s * 0.35} r={u(0.6)} style={dot} />
|
|
116
|
+
<circle cx={s * 1.05} cy={s * 1.05} r={u(0.6)} style={dot} />
|
|
117
|
+
</pattern>
|
|
118
|
+
);
|
|
119
|
+
case "insulation":
|
|
120
|
+
return (
|
|
121
|
+
<pattern {...common} width={s * 3} height={s * 3}>
|
|
122
|
+
<path d={`M0 0 L${s * 1.5} ${s * 3} L${s * 3} 0`} style={line} />
|
|
123
|
+
</pattern>
|
|
124
|
+
);
|
|
125
|
+
default:
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** A filled arrowhead with its tip at `tip`, pointing along `dir` (a unit vector). */
|
|
131
|
+
function head(tip: Pt, dir: Pt, u: (px: number) => number) {
|
|
132
|
+
const l = u(8);
|
|
133
|
+
const w = u(2.6);
|
|
134
|
+
const [dx, dy] = dir;
|
|
135
|
+
return `M${tip[0]} ${tip[1]} L${tip[0] - dx * l - dy * w} ${tip[1] - dy * l + dx * w} L${tip[0] - dx * l + dy * w} ${tip[1] - dy * l - dx * w} Z`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function SectionDrawing({
|
|
139
|
+
template,
|
|
140
|
+
drawing: own,
|
|
141
|
+
names,
|
|
142
|
+
title,
|
|
143
|
+
subtitle,
|
|
144
|
+
fields = [],
|
|
145
|
+
labels,
|
|
146
|
+
paper = "scene",
|
|
147
|
+
locale = "en-GB",
|
|
148
|
+
caption,
|
|
149
|
+
build = "scroll",
|
|
150
|
+
className = "",
|
|
151
|
+
}: Props) {
|
|
152
|
+
const root = useRef<HTMLElement>(null);
|
|
153
|
+
const svg = useRef<SVGSVGElement>(null);
|
|
154
|
+
const raw = useId().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
155
|
+
const drawing: Drawing = own ?? DRAWINGS[template ?? "compression-fitting"]();
|
|
156
|
+
const [vx, vy, vw, vh] = drawing.view;
|
|
157
|
+
const px = usePen(svg, vw, 640);
|
|
158
|
+
const u = (pixels: number) => pixels / px;
|
|
159
|
+
const at = (p: Pt) => ({ left: `${((p[0] - vx) / vw) * 100}%`, top: `${((p[1] - vy) / vh) * 100}%` });
|
|
160
|
+
const nameOf = (key: string) => names?.[key] ?? drawing.names?.[key] ?? key;
|
|
161
|
+
const unit = drawing.unit ?? "mm";
|
|
162
|
+
const unitLine = (labels?.unit ?? UNIT_LINE[languageOf(locale)] ?? UNIT_LINE.en).replace("{unit}", unit);
|
|
163
|
+
|
|
164
|
+
useScrollBuild(
|
|
165
|
+
root,
|
|
166
|
+
build === "scroll",
|
|
167
|
+
(el, tl) => {
|
|
168
|
+
tl.fromTo(el.querySelectorAll("[data-section-edge]"), { strokeDasharray: 1, strokeDashoffset: 1 }, { strokeDashoffset: 0, duration: 1.6, stagger: 0.04 });
|
|
169
|
+
tl.from(el.querySelectorAll("[data-section-face]"), { autoAlpha: 0, duration: 0.7, stagger: 0.03 }, "-=0.6");
|
|
170
|
+
tl.from(el.querySelectorAll("[data-section-thin]"), { autoAlpha: 0, duration: 0.5 }, "-=0.3");
|
|
171
|
+
tl.from(el.querySelectorAll("[data-section-note]"), { autoAlpha: 0, duration: 0.4, stagger: 0.05 }, "-=0.2");
|
|
172
|
+
},
|
|
173
|
+
[template, own],
|
|
174
|
+
{ start: "top 80%", end: "bottom 80%" },
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const hatchId = (i: number) => `${raw}-h${i}`;
|
|
178
|
+
const weights = { edge: u(1.7), thin: u(0.7), center: u(0.7) };
|
|
179
|
+
|
|
180
|
+
// The dimensions: line, extension lines, arrowheads, and where the value stands.
|
|
181
|
+
const dims = (drawing.dims ?? []).map((dim: DrawingDim, i) => {
|
|
182
|
+
const horizontal = dim.axis === "x";
|
|
183
|
+
const a: Pt = horizontal ? [dim.a[0], dim.at] : [dim.at, dim.a[1]];
|
|
184
|
+
const b: Pt = horizontal ? [dim.b[0], dim.at] : [dim.at, dim.b[1]];
|
|
185
|
+
const value = Math.abs(horizontal ? dim.b[0] - dim.a[0] : dim.b[1] - dim.a[1]);
|
|
186
|
+
const text = dim.text ?? `${dim.prefix ?? ""}${numberText(value, locale, 1)}`;
|
|
187
|
+
const ext = (from: Pt, to: Pt) => {
|
|
188
|
+
const dx = to[0] - from[0];
|
|
189
|
+
const dy = to[1] - from[1];
|
|
190
|
+
const len = Math.hypot(dx, dy);
|
|
191
|
+
if (len < u(4)) return "";
|
|
192
|
+
const ux = dx / len;
|
|
193
|
+
const uy = dy / len;
|
|
194
|
+
return `M${from[0] + ux * u(3)} ${from[1] + uy * u(3)} L${to[0] + ux * u(5)} ${to[1] + uy * u(5)}`;
|
|
195
|
+
};
|
|
196
|
+
const span = Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
197
|
+
const dir: Pt = [(b[0] - a[0]) / (span || 1), (b[1] - a[1]) / (span || 1)];
|
|
198
|
+
// A value wider than its span stands beyond the end, outside the arrowheads.
|
|
199
|
+
const roomy = span * px > text.length * 7 + 18;
|
|
200
|
+
const mid: Pt = roomy ? [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2] : [b[0] + dir[0] * u(6 + text.length * 3.6), b[1] + dir[1] * u(6 + text.length * 3.6)];
|
|
201
|
+
return { i, a, b, dir, text, horizontal, mid, paths: `${ext(dim.a, a)} ${ext(dim.b, b)} M${a[0]} ${a[1]} L${b[0]} ${b[1]}` };
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const callouts = drawing.callouts ?? [];
|
|
205
|
+
|
|
206
|
+
const legend: ReactNode = callouts.length ? (
|
|
207
|
+
<ol className="m-0 grid list-none gap-y-1.5 p-0">
|
|
208
|
+
{callouts.map((callout, i) => (
|
|
209
|
+
<li key={callout.key} className={`grid grid-cols-[1.5rem_1fr] items-baseline ${SMALL}`}>
|
|
210
|
+
<span className={`${FIGURES} font-medium`}>{i + 1}</span>
|
|
211
|
+
<span>{nameOf(callout.key)}</span>
|
|
212
|
+
</li>
|
|
213
|
+
))}
|
|
214
|
+
</ol>
|
|
215
|
+
) : null;
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<figure ref={root} data-subject="section" className={`@container m-0 ${className}`}>
|
|
219
|
+
<div data-paper={paper} className={`${paper === "print" ? "subject-paper" : ""} grid border border-[color:var(--ink)] text-ink @3xl:grid-cols-[minmax(0,1fr)_16rem]`}>
|
|
220
|
+
<div className="relative min-w-0 p-[3cqi] @3xl:p-[2.5cqi]">
|
|
221
|
+
<svg ref={svg} viewBox={`${vx} ${vy} ${vw} ${vh}`} className="block h-auto w-full overflow-visible" role="img" aria-label={title ?? drawing.title ?? ""}>
|
|
222
|
+
<defs>
|
|
223
|
+
{drawing.parts.map((part, i) => (
|
|
224
|
+
<Hatch key={i} id={hatchId(i)} material={part.material} angle={part.angle ?? 45} u={u} />
|
|
225
|
+
))}
|
|
226
|
+
</defs>
|
|
227
|
+
{drawing.parts.map((part, i) => (
|
|
228
|
+
<g key={i}>
|
|
229
|
+
{part.material !== "void" ? (
|
|
230
|
+
<path
|
|
231
|
+
data-section-face=""
|
|
232
|
+
d={part.d}
|
|
233
|
+
style={{ fill: part.material === "solid" ? "var(--ink)" : part.material === "timber" ? "none" : `url(#${hatchId(i)})`, stroke: "none" }}
|
|
234
|
+
/>
|
|
235
|
+
) : null}
|
|
236
|
+
{part.material === "timber" && part.box ? (
|
|
237
|
+
<path
|
|
238
|
+
data-section-face=""
|
|
239
|
+
d={`M${part.box[0]} ${part.box[1]} L${part.box[0] + part.box[2]} ${part.box[1] + part.box[3]} M${part.box[0] + part.box[2]} ${part.box[1]} L${part.box[0]} ${part.box[1] + part.box[3]}`}
|
|
240
|
+
style={{ stroke: "var(--ink)", strokeWidth: weights.thin, fill: "none" }}
|
|
241
|
+
/>
|
|
242
|
+
) : null}
|
|
243
|
+
</g>
|
|
244
|
+
))}
|
|
245
|
+
{drawing.parts.map((part, i) =>
|
|
246
|
+
part.stroke === "" ? null : (
|
|
247
|
+
<path
|
|
248
|
+
key={`e${i}`}
|
|
249
|
+
data-section-edge=""
|
|
250
|
+
pathLength={1}
|
|
251
|
+
d={part.stroke ?? part.d}
|
|
252
|
+
style={{ fill: "none", stroke: "var(--ink)", strokeWidth: weights.edge, strokeLinejoin: "round", strokeLinecap: "round" }}
|
|
253
|
+
/>
|
|
254
|
+
),
|
|
255
|
+
)}
|
|
256
|
+
{(drawing.lines ?? []).map((line, i) =>
|
|
257
|
+
line.kind === "visible" ? (
|
|
258
|
+
<path
|
|
259
|
+
key={`l${i}`}
|
|
260
|
+
data-section-edge=""
|
|
261
|
+
pathLength={1}
|
|
262
|
+
d={line.d}
|
|
263
|
+
style={{ fill: "none", stroke: "var(--ink)", strokeWidth: weights.edge, strokeLinejoin: "round", strokeLinecap: "round" }}
|
|
264
|
+
/>
|
|
265
|
+
) : (
|
|
266
|
+
<path
|
|
267
|
+
key={`l${i}`}
|
|
268
|
+
data-section-thin=""
|
|
269
|
+
d={line.d}
|
|
270
|
+
style={{
|
|
271
|
+
fill: "none",
|
|
272
|
+
stroke: "var(--ink)",
|
|
273
|
+
strokeWidth: line.kind === "center" ? weights.center : weights.thin,
|
|
274
|
+
strokeDasharray: line.kind === "center" ? `${u(16)} ${u(3.5)} ${u(2)} ${u(3.5)}` : line.kind === "hidden" ? `${u(5)} ${u(3)}` : undefined,
|
|
275
|
+
strokeLinejoin: "round",
|
|
276
|
+
}}
|
|
277
|
+
/>
|
|
278
|
+
),
|
|
279
|
+
)}
|
|
280
|
+
{dims.map((dim) => (
|
|
281
|
+
<g key={`d${dim.i}`} data-section-note="">
|
|
282
|
+
<path d={dim.paths} style={{ fill: "none", stroke: "var(--ink)", strokeWidth: weights.thin }} />
|
|
283
|
+
<path d={`${head(dim.a, [-dim.dir[0], -dim.dir[1]], u)} ${head(dim.b, dim.dir, u)}`} style={{ fill: "var(--ink)" }} />
|
|
284
|
+
</g>
|
|
285
|
+
))}
|
|
286
|
+
{callouts.map((callout) => (
|
|
287
|
+
<g key={callout.key} data-section-note="">
|
|
288
|
+
<path d={`M${callout.at[0]} ${callout.at[1]} L${callout.to[0]} ${callout.to[1]}`} style={{ stroke: "var(--ink)", strokeWidth: weights.thin }} />
|
|
289
|
+
<circle cx={callout.at[0]} cy={callout.at[1]} r={u(2.4)} style={{ fill: "var(--ink)" }} />
|
|
290
|
+
</g>
|
|
291
|
+
))}
|
|
292
|
+
</svg>
|
|
293
|
+
|
|
294
|
+
<div className="pointer-events-none absolute inset-[3cqi] @3xl:inset-[2.5cqi]">
|
|
295
|
+
{dims.map((dim) => (
|
|
296
|
+
<span
|
|
297
|
+
key={`t${dim.i}`}
|
|
298
|
+
data-section-note=""
|
|
299
|
+
className={`absolute ${SMALL} ${FIGURES}`}
|
|
300
|
+
style={{
|
|
301
|
+
...at(dim.mid),
|
|
302
|
+
transform: dim.horizontal
|
|
303
|
+
? "translate(-50%, calc(-100% - 0.15rem))"
|
|
304
|
+
: "translate(-50%, -50%) rotate(-90deg) translateY(calc(-50% - 0.2rem))",
|
|
305
|
+
}}
|
|
306
|
+
>
|
|
307
|
+
{dim.text}
|
|
308
|
+
</span>
|
|
309
|
+
))}
|
|
310
|
+
{callouts.map((callout, i) => (
|
|
311
|
+
<span
|
|
312
|
+
key={callout.key}
|
|
313
|
+
data-section-note=""
|
|
314
|
+
className={`absolute grid size-[1.25rem] -translate-x-1/2 @3xl:size-[1.5rem] -translate-y-1/2 place-items-center rounded-full border border-[color:var(--ink)] bg-surface-0 ${SMALL} font-medium ${FIGURES}`}
|
|
315
|
+
style={at(callout.to)}
|
|
316
|
+
>
|
|
317
|
+
{i + 1}
|
|
318
|
+
</span>
|
|
319
|
+
))}
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<div className="grid content-end gap-5 border-t border-[color:var(--ink)] p-5 @3xl:border-l @3xl:border-t-0">
|
|
324
|
+
{legend}
|
|
325
|
+
<div className="grid gap-3 border-t border-[color:var(--ink)] pt-4">
|
|
326
|
+
<div>
|
|
327
|
+
<p className="m-0 font-display text-[1.25rem] leading-[1.15] tracking-[-0.01em]">{title ?? drawing.title}</p>
|
|
328
|
+
{subtitle ? <p className={`m-0 mt-1 ${SMALL} text-ink-muted`}>{subtitle}</p> : null}
|
|
329
|
+
</div>
|
|
330
|
+
<div className="border-t border-line pt-3">
|
|
331
|
+
{fields.length ? (
|
|
332
|
+
<dl className="m-0 mb-2 grid grid-cols-2 gap-x-4 gap-y-2">
|
|
333
|
+
{fields.map((field) => (
|
|
334
|
+
<div key={field.label}>
|
|
335
|
+
<dt className={`${SMALL} text-ink-muted`}>{field.label}</dt>
|
|
336
|
+
<dd className={`m-0 ${SMALL} ${FIGURES}`}>{field.value}</dd>
|
|
337
|
+
</div>
|
|
338
|
+
))}
|
|
339
|
+
</dl>
|
|
340
|
+
) : null}
|
|
341
|
+
<p className={`m-0 ${SMALL} text-ink-muted`}>{unitLine}</p>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
{caption ? <figcaption className="mt-4 font-body text-sheet-label text-ink-muted">{caption}</figcaption> : null}
|
|
347
|
+
</figure>
|
|
348
|
+
);
|
|
349
|
+
}
|