@lamatemaga/sterling 0.1.0

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/dist/index.mjs ADDED
@@ -0,0 +1,1912 @@
1
+ "use client";
2
+ "use client";
3
+
4
+ // src/SterlingFigure.tsx
5
+ import { isValidElement } from "react";
6
+
7
+ // src/palette.ts
8
+ function defineSterlingPalette(palette) {
9
+ return palette;
10
+ }
11
+ function sterlingPaletteStyle(palette) {
12
+ const variables = {};
13
+ if (!palette) return variables;
14
+ const surfaceTokens = {
15
+ paper: "paper",
16
+ plot: "surface",
17
+ plotAlt: "surface-2",
18
+ text: "text",
19
+ muted: "muted",
20
+ grid: "grid",
21
+ edge: "edge",
22
+ period: "period"
23
+ };
24
+ for (const [key, value] of Object.entries(palette.surface ?? {})) {
25
+ if (value) variables[`--sterling-${surfaceTokens[key]}`] = value;
26
+ }
27
+ const assignSeries = (name, values) => {
28
+ values?.forEach((value, index) => {
29
+ if (value) variables[`--sterling-${name}-${index + 1}`] = value;
30
+ });
31
+ };
32
+ assignSeries("cat", palette.categorical);
33
+ assignSeries("legend", palette.legend);
34
+ assignSeries("seq", palette.sequential);
35
+ assignSeries("div", palette.divergent);
36
+ assignSeries("heat", palette.heat);
37
+ palette.ramps?.forEach((ramp, familyIndex) => {
38
+ ramp.forEach((value, stopIndex) => {
39
+ if (value) variables[`--sterling-ramp-${familyIndex + 1}-${stopIndex + 1}`] = value;
40
+ });
41
+ });
42
+ return variables;
43
+ }
44
+ var sterlingCategorical = {
45
+ light: [
46
+ "#9A79E7",
47
+ "#25A08D",
48
+ "#D45AC7",
49
+ "#E4A43A",
50
+ "#5A83D7",
51
+ "#E87864",
52
+ "#96AB51",
53
+ "#536B78"
54
+ ],
55
+ dark: [
56
+ "#B69AF2",
57
+ "#5EC9AE",
58
+ "#E88BDD",
59
+ "#F2C46D",
60
+ "#86A8E8",
61
+ "#F29A88",
62
+ "#B7C974",
63
+ "#B7C8D1"
64
+ ],
65
+ print: [
66
+ "#8E68D8",
67
+ "#218C7C",
68
+ "#C653BC",
69
+ "#D39A32",
70
+ "#4F73C6",
71
+ "#DC6A55",
72
+ "#879347",
73
+ "#607986"
74
+ ]
75
+ };
76
+ var sterlingColorNames = [
77
+ "Violet",
78
+ "Teal",
79
+ "Orchid",
80
+ "Amber",
81
+ "Blue",
82
+ "Coral",
83
+ "Moss",
84
+ "Payne"
85
+ ];
86
+ var sterlingChartColors = sterlingColorNames.map(
87
+ (_, index) => `var(--sterling-cat-${index + 1})`
88
+ );
89
+ var sterlingLegendColors = sterlingColorNames.map(
90
+ (_, index) => `var(--sterling-legend-${index + 1})`
91
+ );
92
+ var sterlingDivergentColors = Array.from(
93
+ { length: 11 },
94
+ (_, index) => `var(--sterling-div-${index + 1})`
95
+ );
96
+ var sterlingSequentialColors = Array.from(
97
+ { length: 10 },
98
+ (_, index) => `var(--sterling-seq-${index + 1})`
99
+ );
100
+ var sterlingHeatColors = Array.from(
101
+ { length: 11 },
102
+ (_, index) => `var(--sterling-heat-${index + 1})`
103
+ );
104
+ var sterlingRampStops = 7;
105
+ function sterlingRamp(family, stop) {
106
+ const familyIndex = (family % sterlingColorNames.length + sterlingColorNames.length) % sterlingColorNames.length;
107
+ const clamped = Math.max(1, Math.min(sterlingRampStops, Math.round(stop)));
108
+ return `var(--sterling-ramp-${familyIndex + 1}-${clamped})`;
109
+ }
110
+
111
+ // src/SterlingLegend.tsx
112
+ import { jsx, jsxs } from "react/jsx-runtime";
113
+ function LegendMark({ shape = "circle" }) {
114
+ if (shape === "square") {
115
+ return /* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "8", height: "8", rx: "1" });
116
+ }
117
+ if (shape === "triangle") {
118
+ return /* @__PURE__ */ jsx("path", { d: "M 6 1 L 11 10 L 1 10 Z" });
119
+ }
120
+ if (shape === "line") {
121
+ return /* @__PURE__ */ jsx("path", { d: "M 1 6 H 11", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" });
122
+ }
123
+ return /* @__PURE__ */ jsx("circle", { cx: "6", cy: "6", r: "4.5" });
124
+ }
125
+ function SterlingInlineLegend({
126
+ items,
127
+ locale
128
+ }) {
129
+ const parts = new Intl.ListFormat(locale, {
130
+ style: "long",
131
+ type: "conjunction"
132
+ }).formatToParts(items.map((item) => item.label));
133
+ let itemIndex = 0;
134
+ return /* @__PURE__ */ jsx("span", { className: "sterling-inline-legend", children: parts.map((part, partIndex) => {
135
+ if (part.type !== "element") {
136
+ return /* @__PURE__ */ jsx("span", { children: part.value }, `literal-${partIndex}`);
137
+ }
138
+ const item = items[itemIndex++];
139
+ const colorIndex = Math.max(0, Math.min(sterlingChartColors.length - 1, item.colorIndex ?? 0));
140
+ const style = {
141
+ "--sterling-legend-mark": item.color ?? sterlingChartColors[colorIndex],
142
+ "--sterling-legend-text": item.textColor ?? sterlingLegendColors[colorIndex]
143
+ };
144
+ return /* @__PURE__ */ jsxs("span", { className: "sterling-inline-legend__item", style, children: [
145
+ /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx(LegendMark, { shape: item.shape }) }),
146
+ /* @__PURE__ */ jsx("span", { children: item.label })
147
+ ] }, `${item.label}-${partIndex}`);
148
+ }) });
149
+ }
150
+
151
+ // src/SterlingFigureActions.tsx
152
+ import { Check, ClipboardCopy, Download, LoaderCircle, MoreHorizontal, Share2 } from "lucide-react";
153
+ import { toPng } from "html-to-image";
154
+ import { useEffect, useRef, useState } from "react";
155
+
156
+ // src/dataExport.ts
157
+ function csvCell(value) {
158
+ if (value === null || value === void 0) return "";
159
+ if (value instanceof Date) return value.toISOString();
160
+ if (typeof value === "object") return JSON.stringify(value);
161
+ return String(value);
162
+ }
163
+ function escapeCsvCell(value) {
164
+ const cell = csvCell(value);
165
+ return /[\",\n\r]/.test(cell) ? `"${cell.replace(/"/g, '""')}"` : cell;
166
+ }
167
+ function sterlingRowsToCsv(rows) {
168
+ const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];
169
+ return [
170
+ columns.map(escapeCsvCell).join(","),
171
+ ...rows.map((row) => columns.map((column) => escapeCsvCell(row[column])).join(","))
172
+ ].join("\r\n");
173
+ }
174
+ function rowsFromArray(values) {
175
+ return values.map((value) => {
176
+ if (Array.isArray(value)) {
177
+ return Object.fromEntries(value.map((cell, index) => [`value_${index + 1}`, cell]));
178
+ }
179
+ if (value && typeof value === "object") return value;
180
+ return { value };
181
+ });
182
+ }
183
+ function inferSterlingDataExport(props) {
184
+ if (Array.isArray(props.data)) return { rows: rowsFromArray(props.data) };
185
+ if (Array.isArray(props.rows)) return { rows: rowsFromArray(props.rows) };
186
+ if (Array.isArray(props.links)) return { rows: rowsFromArray(props.links) };
187
+ if (Array.isArray(props.points)) return { rows: rowsFromArray(props.points) };
188
+ if (Array.isArray(props.groups)) {
189
+ const groups = props.groups;
190
+ if (groups.every((group) => Array.isArray(group.values))) {
191
+ return {
192
+ rows: groups.flatMap((group) => group.values.map((value) => ({ group: group.label, value })))
193
+ };
194
+ }
195
+ }
196
+ if (Array.isArray(props.values)) {
197
+ const values = props.values;
198
+ if (values.every((value) => typeof value === "number")) return { rows: values.map((value) => ({ value })) };
199
+ if (values.every(Array.isArray)) {
200
+ const rowLabels = Array.isArray(props.rowLabels) ? props.rowLabels : [];
201
+ const columnLabels = Array.isArray(props.columnLabels) ? props.columnLabels : [];
202
+ return {
203
+ rows: values.flatMap((row, rowIndex) => row.map((value, columnIndex) => ({
204
+ row: rowLabels[rowIndex] ?? rowIndex + 1,
205
+ column: columnLabels[columnIndex] ?? columnIndex + 1,
206
+ value
207
+ })))
208
+ };
209
+ }
210
+ }
211
+ if (Array.isArray(props.series) && Array.isArray(props.labels)) {
212
+ const labels = props.labels;
213
+ const series = props.series;
214
+ if (series.every((item) => Array.isArray(item.values))) {
215
+ return {
216
+ rows: labels.map((label, index) => Object.fromEntries([
217
+ ["label", label],
218
+ ...series.map((item) => [String(item.label ?? "value"), item.values[index]])
219
+ ]))
220
+ };
221
+ }
222
+ }
223
+ if (Array.isArray(props.nodes)) return { rows: rowsFromArray(props.nodes) };
224
+ return void 0;
225
+ }
226
+
227
+ // src/SterlingFigureActions.tsx
228
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
229
+ var copy = {
230
+ es: {
231
+ copy: "Copiar imagen",
232
+ copied: "Imagen copiada",
233
+ more: "Exportar o compartir",
234
+ downloadImage: "Descargar imagen PNG",
235
+ downloadData: "Descargar datos de la figura (.csv)",
236
+ share: "Compartir figura",
237
+ shared: "Compartida",
238
+ error: "No se pudo completar la acci\xF3n"
239
+ },
240
+ en: {
241
+ copy: "Copy image",
242
+ copied: "Image copied",
243
+ more: "Export or share",
244
+ downloadImage: "Download PNG image",
245
+ downloadData: "Download figure data (.csv)",
246
+ share: "Share figure",
247
+ shared: "Shared",
248
+ error: "The action could not be completed"
249
+ }
250
+ };
251
+ function fileName(title, extension) {
252
+ const slug = title.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
253
+ return `${slug || "sterling-figure"}.${extension}`;
254
+ }
255
+ function resolveSvgColors(root) {
256
+ const snapshots = [];
257
+ const properties = ["color", "fill", "stroke", "stop-color", "flood-color"];
258
+ root.querySelectorAll("svg, svg *").forEach((element) => {
259
+ snapshots.push({ element, style: element.getAttribute("style") });
260
+ const computed = getComputedStyle(element);
261
+ for (const property of properties) {
262
+ const value = computed.getPropertyValue(property).trim();
263
+ if (value && value !== "none" && !value.startsWith("url(")) {
264
+ element.style.setProperty(property, value);
265
+ }
266
+ }
267
+ });
268
+ return () => {
269
+ for (const { element, style } of snapshots) {
270
+ if (style === null) element.removeAttribute("style");
271
+ else element.setAttribute("style", style);
272
+ }
273
+ };
274
+ }
275
+ function triggerDownload(href, name) {
276
+ const link = document.createElement("a");
277
+ link.download = name;
278
+ link.href = href;
279
+ document.body.append(link);
280
+ link.click();
281
+ link.remove();
282
+ }
283
+ function SterlingFigureActions({
284
+ locale,
285
+ title,
286
+ dataExport
287
+ }) {
288
+ const actionsRef = useRef(null);
289
+ const menuRef = useRef(null);
290
+ const resetTimer = useRef(null);
291
+ const [state, setState] = useState("idle");
292
+ const t = copy[locale];
293
+ const busy = ["copying", "downloading-image", "downloading-data", "sharing"].includes(state);
294
+ useEffect(() => () => {
295
+ if (resetTimer.current) clearTimeout(resetTimer.current);
296
+ }, []);
297
+ async function renderCard() {
298
+ const figure = actionsRef.current?.closest(".sterling-figure");
299
+ const card = figure?.querySelector(".sterling-figure__inner");
300
+ if (!card) throw new Error("Sterling figure not found");
301
+ await document.fonts.ready;
302
+ const actions = card.querySelector(".sterling-figure__actions");
303
+ const actionsVisibility = actions?.style.visibility ?? "";
304
+ if (actions) actions.style.visibility = "hidden";
305
+ const bounds = card.getBoundingClientRect();
306
+ const backgroundColor = getComputedStyle(card).backgroundColor;
307
+ const restoreSvgColors = resolveSvgColors(card);
308
+ try {
309
+ return await toPng(card, {
310
+ backgroundColor,
311
+ cacheBust: true,
312
+ width: Math.ceil(bounds.width),
313
+ height: Math.ceil(bounds.height),
314
+ pixelRatio: 2
315
+ });
316
+ } finally {
317
+ restoreSvgColors();
318
+ if (actions) actions.style.visibility = actionsVisibility;
319
+ }
320
+ }
321
+ function closeMenu() {
322
+ if (menuRef.current) menuRef.current.open = false;
323
+ }
324
+ function resetSoon(next) {
325
+ setState(next);
326
+ if (resetTimer.current) clearTimeout(resetTimer.current);
327
+ resetTimer.current = setTimeout(() => setState("idle"), 1800);
328
+ }
329
+ async function copyImage() {
330
+ try {
331
+ setState("copying");
332
+ const dataUrl = await renderCard();
333
+ const blob = await fetch(dataUrl).then((response) => response.blob());
334
+ if (!navigator.clipboard?.write || typeof ClipboardItem === "undefined") {
335
+ throw new Error("Image clipboard is not available");
336
+ }
337
+ await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]);
338
+ resetSoon("copied");
339
+ } catch {
340
+ resetSoon("error");
341
+ }
342
+ }
343
+ async function downloadImage() {
344
+ try {
345
+ setState("downloading-image");
346
+ const dataUrl = await renderCard();
347
+ triggerDownload(dataUrl, fileName(title, "png"));
348
+ closeMenu();
349
+ resetSoon("idle");
350
+ } catch {
351
+ resetSoon("error");
352
+ }
353
+ }
354
+ function downloadData() {
355
+ if (!dataExport) return;
356
+ try {
357
+ setState("downloading-data");
358
+ const csv = sterlingRowsToCsv(dataExport.rows);
359
+ const blob = new Blob(["\uFEFF", csv], { type: "text/csv;charset=utf-8" });
360
+ const url = URL.createObjectURL(blob);
361
+ triggerDownload(url, dataExport.fileName ? `${dataExport.fileName}.csv` : fileName(title, "csv"));
362
+ window.setTimeout(() => URL.revokeObjectURL(url), 0);
363
+ closeMenu();
364
+ resetSoon("idle");
365
+ } catch {
366
+ resetSoon("error");
367
+ }
368
+ }
369
+ async function shareFigure() {
370
+ try {
371
+ setState("sharing");
372
+ const dataUrl = await renderCard();
373
+ const blob = await fetch(dataUrl).then((response) => response.blob());
374
+ const image = new File([blob], fileName(title, "png"), { type: "image/png" });
375
+ const shareData = { title, text: title, url: window.location.href, files: [image] };
376
+ if (navigator.share && (!navigator.canShare || navigator.canShare(shareData))) {
377
+ closeMenu();
378
+ await navigator.share(shareData);
379
+ resetSoon("shared");
380
+ return;
381
+ }
382
+ if (!navigator.clipboard?.writeText) throw new Error("Sharing is not available");
383
+ await navigator.clipboard.writeText(window.location.href);
384
+ closeMenu();
385
+ resetSoon("shared");
386
+ } catch (error) {
387
+ if (error instanceof DOMException && error.name === "AbortError") {
388
+ setState("idle");
389
+ return;
390
+ }
391
+ resetSoon("error");
392
+ }
393
+ }
394
+ const copyLabel = state === "copied" ? t.copied : state === "error" ? t.error : t.copy;
395
+ const moreLabel = state === "shared" ? t.shared : state === "error" ? t.error : t.more;
396
+ return /* @__PURE__ */ jsxs2("div", { ref: actionsRef, className: "sterling-figure__actions", children: [
397
+ /* @__PURE__ */ jsx2(
398
+ "button",
399
+ {
400
+ type: "button",
401
+ onClick: copyImage,
402
+ disabled: busy,
403
+ "data-state": state,
404
+ "aria-label": copyLabel,
405
+ title: copyLabel,
406
+ children: state === "copying" ? /* @__PURE__ */ jsx2(LoaderCircle, { "aria-hidden": "true" }) : state === "copied" ? /* @__PURE__ */ jsx2(Check, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx2(ClipboardCopy, { "aria-hidden": "true" })
407
+ }
408
+ ),
409
+ /* @__PURE__ */ jsxs2("details", { ref: menuRef, className: "sterling-figure__action-menu", children: [
410
+ /* @__PURE__ */ jsx2("summary", { "aria-label": moreLabel, title: moreLabel, children: busy && state !== "copying" ? /* @__PURE__ */ jsx2(LoaderCircle, { "aria-hidden": "true", "data-state": state }) : state === "shared" ? /* @__PURE__ */ jsx2(Check, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx2(MoreHorizontal, { "aria-hidden": "true" }) }),
411
+ /* @__PURE__ */ jsxs2("div", { role: "menu", className: "sterling-figure__action-popover", children: [
412
+ /* @__PURE__ */ jsxs2("button", { type: "button", role: "menuitem", onClick: downloadImage, disabled: busy, children: [
413
+ /* @__PURE__ */ jsx2(Download, { "aria-hidden": "true" }),
414
+ " ",
415
+ /* @__PURE__ */ jsx2("span", { children: t.downloadImage })
416
+ ] }),
417
+ dataExport ? /* @__PURE__ */ jsxs2("button", { type: "button", role: "menuitem", onClick: downloadData, disabled: busy, children: [
418
+ /* @__PURE__ */ jsx2(Download, { "aria-hidden": "true" }),
419
+ " ",
420
+ /* @__PURE__ */ jsx2("span", { children: t.downloadData })
421
+ ] }) : null,
422
+ /* @__PURE__ */ jsxs2("button", { type: "button", role: "menuitem", onClick: shareFigure, disabled: busy, children: [
423
+ /* @__PURE__ */ jsx2(Share2, { "aria-hidden": "true" }),
424
+ " ",
425
+ /* @__PURE__ */ jsx2("span", { children: t.share })
426
+ ] })
427
+ ] })
428
+ ] })
429
+ ] });
430
+ }
431
+
432
+ // src/editorial.tsx
433
+ import { Fragment, jsxs as jsxs3 } from "react/jsx-runtime";
434
+ function sterlingCredit({
435
+ author,
436
+ productName = "Sterling",
437
+ linkToSterling = true,
438
+ sterlingUrl = "https://www.lamatemaga.com/sterling"
439
+ }, locale = "en") {
440
+ const product = linkToSterling ? /* @__PURE__ */ jsxs3("a", { className: "sterling-figure__credit-link", href: sterlingUrl, children: [
441
+ productName,
442
+ " \u2726"
443
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
444
+ productName,
445
+ " \u2726"
446
+ ] });
447
+ if (locale === "es") {
448
+ return author ? /* @__PURE__ */ jsxs3(Fragment, { children: [
449
+ "Hecho por ",
450
+ author,
451
+ " con ",
452
+ product
453
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
454
+ "Hecho con ",
455
+ product
456
+ ] });
457
+ }
458
+ return author ? /* @__PURE__ */ jsxs3(Fragment, { children: [
459
+ "Made by ",
460
+ author,
461
+ " with ",
462
+ product
463
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
464
+ "Made with ",
465
+ product
466
+ ] });
467
+ }
468
+
469
+ // src/SterlingFigure.tsx
470
+ import { jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
471
+ function SterlingFigure({
472
+ locale = "en",
473
+ labelPrimary,
474
+ labelSecondary,
475
+ title,
476
+ subtitle,
477
+ legend,
478
+ source,
479
+ children,
480
+ size = "medium",
481
+ className,
482
+ style,
483
+ palette,
484
+ signature,
485
+ sourceLabel,
486
+ titleMarkHref,
487
+ editorial,
488
+ dataExport
489
+ }) {
490
+ const defaults = { titleMark: ".", signature: sterlingCredit({}, locale) };
491
+ const titleMark = editorial?.titleMark === void 0 ? defaults.titleMark : editorial.titleMark;
492
+ const resolvedTitleMarkHref = titleMarkHref ?? editorial?.titleMarkHref;
493
+ const resolvedSourceLabel = sourceLabel ?? editorial?.sourceLabel ?? (locale === "es" ? "Fuente" : "Source");
494
+ const resolvedSignature = signature ?? editorial?.signature ?? defaults.signature;
495
+ const inferredDataExport = isValidElement(children) ? inferSterlingDataExport(children.props) : void 0;
496
+ return /* @__PURE__ */ jsx3(
497
+ "figure",
498
+ {
499
+ className: ["sterling-figure", className].filter(Boolean).join(" "),
500
+ "data-size": size,
501
+ style: { ...sterlingPaletteStyle(palette), ...style },
502
+ children: /* @__PURE__ */ jsxs4("div", { className: "sterling-figure__inner", children: [
503
+ /* @__PURE__ */ jsxs4("header", { className: "sterling-figure__header", children: [
504
+ /* @__PURE__ */ jsxs4("div", { className: "sterling-figure__topline", children: [
505
+ /* @__PURE__ */ jsxs4("div", { className: "sterling-figure__labels", "aria-label": `${labelPrimary}, ${labelSecondary}`, children: [
506
+ /* @__PURE__ */ jsx3("span", { children: labelPrimary }),
507
+ /* @__PURE__ */ jsx3("span", { children: labelSecondary })
508
+ ] }),
509
+ /* @__PURE__ */ jsx3(SterlingFigureActions, { locale, title, dataExport: dataExport ?? inferredDataExport })
510
+ ] }),
511
+ /* @__PURE__ */ jsxs4("h3", { className: "sterling-figure__title", children: [
512
+ title,
513
+ titleMark === false ? null : resolvedTitleMarkHref ? /* @__PURE__ */ jsx3(
514
+ "a",
515
+ {
516
+ className: "sterling-figure__period",
517
+ href: resolvedTitleMarkHref,
518
+ "aria-label": editorial?.titleMarkLabel ?? (locale === "es" ? "Ir al inicio" : "Go home"),
519
+ children: titleMark
520
+ }
521
+ ) : /* @__PURE__ */ jsx3("span", { className: "sterling-figure__period", "aria-hidden": "true", children: titleMark })
522
+ ] }),
523
+ /* @__PURE__ */ jsxs4("p", { className: "sterling-figure__subtitle", children: [
524
+ subtitle,
525
+ legend?.length ? /* @__PURE__ */ jsx3(SterlingInlineLegend, { items: legend, locale }) : null
526
+ ] })
527
+ ] }),
528
+ /* @__PURE__ */ jsx3("div", { className: "sterling-figure__chart", children }),
529
+ /* @__PURE__ */ jsxs4("figcaption", { className: "sterling-figure__caption", children: [
530
+ /* @__PURE__ */ jsxs4("span", { children: [
531
+ /* @__PURE__ */ jsxs4("span", { className: "sterling-figure__caption-label", children: [
532
+ resolvedSourceLabel,
533
+ ":"
534
+ ] }),
535
+ " ",
536
+ source
537
+ ] }),
538
+ resolvedSignature === false ? null : /* @__PURE__ */ jsx3("span", { className: "sterling-figure__signature", children: resolvedSignature })
539
+ ] })
540
+ ] })
541
+ }
542
+ );
543
+ }
544
+
545
+ // src/charts.tsx
546
+ import { chord as d3Chord, ribbon as d3Ribbon } from "d3-chord";
547
+ import { sankey as d3Sankey, sankeyLinkHorizontal } from "d3-sankey";
548
+ import { arc as d3Arc } from "d3-shape";
549
+
550
+ // src/plot.tsx
551
+ import { scaleLinear, scaleTime } from "d3-scale";
552
+
553
+ // src/visualStyle.ts
554
+ var sterlingVisualStyle = {
555
+ stroke: {
556
+ grid: 1,
557
+ detail: 1.5,
558
+ candle: 1.75,
559
+ mark: 2,
560
+ series: 2.25,
561
+ emphasis: 2.5,
562
+ interval: 7,
563
+ halo: 3,
564
+ ring: 44,
565
+ flowMinimum: 1
566
+ },
567
+ opacity: {
568
+ ghost: 0.04,
569
+ contour: 0.08,
570
+ surface: 0.1,
571
+ area: 0.24,
572
+ ridge: 0.34,
573
+ interval: 0.28,
574
+ relationship: 0.42,
575
+ secondaryMark: 0.66,
576
+ guide: 0.7,
577
+ signal: 0.84,
578
+ mutedMark: 0.34
579
+ }
580
+ };
581
+
582
+ // src/plot.tsx
583
+ import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
584
+ var axisText = "var(--sterling-text)";
585
+ var axisMuted = "var(--sterling-muted)";
586
+ var axisGrid = "var(--sterling-grid)";
587
+ var axisLine = "var(--sterling-edge)";
588
+ var MONO = "var(--font-mono)";
589
+ var TICK = 5;
590
+ var TICK_LABEL = 11;
591
+ var AXIS_TITLE = 12;
592
+ var defaultMargin = { top: 26, right: 26, bottom: 56, left: 70 };
593
+ function frame(width, height, margin = defaultMargin) {
594
+ return {
595
+ width,
596
+ height,
597
+ x0: margin.left,
598
+ x1: width - margin.right,
599
+ y0: margin.top,
600
+ y1: height - margin.bottom
601
+ };
602
+ }
603
+ function linearScale(values, range, { zero = false, padFraction = 0.05 } = {}) {
604
+ let min = Math.min(...values);
605
+ let max = Math.max(...values);
606
+ if (!Number.isFinite(min) || !Number.isFinite(max)) {
607
+ min = 0;
608
+ max = 1;
609
+ }
610
+ if (min === max) {
611
+ const bump = Math.abs(min) || 1;
612
+ min -= bump;
613
+ max += bump;
614
+ }
615
+ if (zero) {
616
+ min = Math.min(0, min);
617
+ max = Math.max(0, max);
618
+ } else {
619
+ const pad = (max - min) * padFraction;
620
+ min -= pad;
621
+ max += pad;
622
+ }
623
+ return scaleLinear().domain([min, max]).nice().range(range);
624
+ }
625
+ function timeScale(domain, range) {
626
+ return scaleTime().domain(domain).range(range).nice();
627
+ }
628
+ function tickValues(scale, count) {
629
+ return scale.ticks(count);
630
+ }
631
+ function Gridlines({
632
+ scale,
633
+ x0,
634
+ x1,
635
+ count = 5
636
+ }) {
637
+ return /* @__PURE__ */ jsx4("g", { "aria-hidden": "true", children: tickValues(scale, count).map((value) => /* @__PURE__ */ jsx4("line", { x1: x0, x2: x1, y1: scale(value), y2: scale(value), stroke: axisGrid, strokeWidth: sterlingVisualStyle.stroke.grid }, value)) });
638
+ }
639
+ function AxisLeft({
640
+ scale,
641
+ x,
642
+ gridX1,
643
+ count = 5,
644
+ title,
645
+ format
646
+ }) {
647
+ const values = tickValues(scale, count);
648
+ const fmt = format ?? ((value) => scale.tickFormat(count)(value));
649
+ const [yBottom, yTop] = scale.range();
650
+ return /* @__PURE__ */ jsxs5("g", { children: [
651
+ gridX1 !== void 0 ? values.map((value) => /* @__PURE__ */ jsx4("line", { x1: x, x2: gridX1, y1: scale(value), y2: scale(value), stroke: axisGrid, strokeWidth: sterlingVisualStyle.stroke.grid }, `g${value}`)) : null,
652
+ /* @__PURE__ */ jsx4("line", { x1: x, x2: x, y1: yBottom, y2: yTop, stroke: axisLine }),
653
+ values.map((value) => /* @__PURE__ */ jsxs5("g", { children: [
654
+ /* @__PURE__ */ jsx4("line", { x1: x - TICK, x2: x, y1: scale(value), y2: scale(value), stroke: axisLine }),
655
+ /* @__PURE__ */ jsx4("text", { x: x - TICK - 4, y: scale(value) + 3.6, textAnchor: "end", fill: axisMuted, fontSize: TICK_LABEL, fontFamily: MONO, children: fmt(value) })
656
+ ] }, value)),
657
+ title ? /* @__PURE__ */ jsx4(
658
+ "text",
659
+ {
660
+ transform: `translate(${x - 48} ${(yBottom + yTop) / 2}) rotate(-90)`,
661
+ textAnchor: "middle",
662
+ fill: axisMuted,
663
+ fontSize: AXIS_TITLE,
664
+ fontFamily: MONO,
665
+ children: title
666
+ }
667
+ ) : null
668
+ ] });
669
+ }
670
+ function AxisBottom({
671
+ scale,
672
+ y,
673
+ count = 6,
674
+ title,
675
+ format,
676
+ titleGap = 42
677
+ }) {
678
+ const values = tickValues(scale, count);
679
+ const fmt = format ?? ((value) => scale.tickFormat(count)(value));
680
+ const [x0, x1] = scale.range();
681
+ return /* @__PURE__ */ jsxs5("g", { children: [
682
+ /* @__PURE__ */ jsx4("line", { x1: x0, x2: x1, y1: y, y2: y, stroke: axisLine }),
683
+ values.map((value) => /* @__PURE__ */ jsxs5("g", { children: [
684
+ /* @__PURE__ */ jsx4("line", { x1: scale(value), x2: scale(value), y1: y, y2: y + TICK, stroke: axisLine }),
685
+ /* @__PURE__ */ jsx4("text", { x: scale(value), y: y + TICK + 13, textAnchor: "middle", fill: axisMuted, fontSize: TICK_LABEL, fontFamily: MONO, children: fmt(value) })
686
+ ] }, value)),
687
+ title ? /* @__PURE__ */ jsx4("text", { x: (x0 + x1) / 2, y: y + titleGap, textAnchor: "middle", fill: axisMuted, fontSize: AXIS_TITLE, fontFamily: MONO, children: title }) : null
688
+ ] });
689
+ }
690
+ function TimeAxisBottom({
691
+ scale,
692
+ y,
693
+ title,
694
+ targetPx = 96,
695
+ titleGap = 42
696
+ }) {
697
+ const [x0, x1] = scale.range();
698
+ const count = Math.max(2, Math.round((x1 - x0) / targetPx));
699
+ const values = scale.ticks(count);
700
+ const fmt = scale.tickFormat(count);
701
+ return /* @__PURE__ */ jsxs5("g", { children: [
702
+ /* @__PURE__ */ jsx4("line", { x1: x0, x2: x1, y1: y, y2: y, stroke: axisLine }),
703
+ values.map((value) => /* @__PURE__ */ jsxs5("g", { children: [
704
+ /* @__PURE__ */ jsx4("line", { x1: scale(value), x2: scale(value), y1: y, y2: y + TICK, stroke: axisLine }),
705
+ /* @__PURE__ */ jsx4("text", { x: scale(value), y: y + TICK + 13, textAnchor: "middle", fill: axisMuted, fontSize: TICK_LABEL, fontFamily: MONO, children: fmt(value) })
706
+ ] }, +value)),
707
+ title ? /* @__PURE__ */ jsx4("text", { x: (x0 + x1) / 2, y: y + titleGap, textAnchor: "middle", fill: axisMuted, fontSize: AXIS_TITLE, fontFamily: MONO, children: title }) : null
708
+ ] });
709
+ }
710
+ function bandCenters(count, x0, x1) {
711
+ const step = (x1 - x0) / Math.max(count, 1);
712
+ const centers = Array.from({ length: count }, (_, index) => x0 + step * (index + 0.5));
713
+ return { centers, step };
714
+ }
715
+ function divergentSignedColor(value, maxAbs) {
716
+ const stops = sterlingDivergentColors.length - 1;
717
+ const t = Math.max(0, Math.min(1, (value + maxAbs) / (2 * maxAbs)));
718
+ return sterlingDivergentColors[Math.round((1 - t) * stops)];
719
+ }
720
+ var divergentSignedRamp = [...sterlingDivergentColors].reverse();
721
+ function tukeySummary(values) {
722
+ const sorted = [...values].sort((left, right) => left - right);
723
+ const q = (p) => {
724
+ if (sorted.length === 0) return 0;
725
+ const position = (sorted.length - 1) * p;
726
+ const lower = Math.floor(position);
727
+ const upper = Math.ceil(position);
728
+ if (lower === upper) return sorted[lower];
729
+ return sorted[lower] + (sorted[upper] - sorted[lower]) * (position - lower);
730
+ };
731
+ const q1 = q(0.25);
732
+ const median = q(0.5);
733
+ const q3 = q(0.75);
734
+ const iqr = q3 - q1;
735
+ const lowerFence = q1 - 1.5 * iqr;
736
+ const upperFence = q3 + 1.5 * iqr;
737
+ const inside = sorted.filter((value) => value >= lowerFence && value <= upperFence);
738
+ const whiskerLow = inside.length ? inside[0] : q1;
739
+ const whiskerHigh = inside.length ? inside[inside.length - 1] : q3;
740
+ const outliers = sorted.filter((value) => value < lowerFence || value > upperFence);
741
+ return { q1, median, q3, iqr, whiskerLow, whiskerHigh, outliers, min: sorted[0], max: sorted[sorted.length - 1] };
742
+ }
743
+
744
+ // src/charts.tsx
745
+ import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
746
+ var chartText = "var(--sterling-text)";
747
+ var chartMuted = "var(--sterling-muted)";
748
+ var chartGrid = "var(--sterling-grid)";
749
+ function SterlingBarChart({
750
+ data,
751
+ ariaLabel,
752
+ unit = "",
753
+ xLabel
754
+ }) {
755
+ const rowHeight = 46;
756
+ const top = 12;
757
+ const barHeight = 28;
758
+ const axisBottom = xLabel ? 50 : 34;
759
+ const x0 = 150;
760
+ const x1 = 684;
761
+ const height = top + data.length * rowHeight + axisBottom;
762
+ const baseline = top + data.length * rowHeight;
763
+ const x = linearScale(data.map((datum) => datum.value), [x0, x1], { zero: true });
764
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 720 ${height}`, role: "img", "aria-label": ariaLabel, children: [
765
+ x.ticks(6).map((value) => /* @__PURE__ */ jsx5("line", { x1: x(value), x2: x(value), y1: top, y2: baseline, stroke: chartGrid, strokeWidth: sterlingVisualStyle.stroke.grid }, value)),
766
+ data.map((datum, index) => {
767
+ const y = top + index * rowHeight + (rowHeight - barHeight) / 2 - 6;
768
+ const width = x(datum.value) - x0;
769
+ return /* @__PURE__ */ jsxs6("g", { children: [
770
+ /* @__PURE__ */ jsx5("text", { x: x0 - 12, y: y + barHeight / 2 + 4, textAnchor: "end", fill: chartText, fontSize: "12", fontFamily: "var(--font-mono)", children: datum.label }),
771
+ /* @__PURE__ */ jsx5(
772
+ "rect",
773
+ {
774
+ x: x0,
775
+ y,
776
+ width: Math.max(width, 2),
777
+ height: barHeight,
778
+ rx: "3",
779
+ fill: sterlingChartColors[datum.colorIndex ?? index % sterlingChartColors.length],
780
+ children: /* @__PURE__ */ jsx5("title", { children: `${datum.label}: ${datum.value}${unit}` })
781
+ }
782
+ ),
783
+ /* @__PURE__ */ jsxs6("text", { x: x(datum.value) + 8, y: y + barHeight / 2 + 4, fill: chartMuted, fontSize: "12", fontFamily: "var(--font-mono)", children: [
784
+ datum.value,
785
+ unit
786
+ ] })
787
+ ] }, datum.label);
788
+ }),
789
+ /* @__PURE__ */ jsx5(AxisBottom, { scale: x, y: baseline, count: 6, title: xLabel, format: (value) => `${value}${unit}` })
790
+ ] });
791
+ }
792
+ function scatterMark(shape, x, y, color, key, title) {
793
+ if (shape % 3 === 1) {
794
+ return /* @__PURE__ */ jsx5("rect", { x: x - 4.5, y: y - 4.5, width: "9", height: "9", rx: "1", fill: color, children: /* @__PURE__ */ jsx5("title", { children: title }) }, key);
795
+ }
796
+ if (shape % 3 === 2) {
797
+ return /* @__PURE__ */ jsx5("path", { d: `M ${x} ${y - 5.5} L ${x + 5.2} ${y + 4.5} L ${x - 5.2} ${y + 4.5} Z`, fill: color, children: /* @__PURE__ */ jsx5("title", { children: title }) }, key);
798
+ }
799
+ return /* @__PURE__ */ jsx5("circle", { cx: x, cy: y, r: "4.8", fill: color, children: /* @__PURE__ */ jsx5("title", { children: title }) }, key);
800
+ }
801
+ function SterlingScatterPlot({
802
+ data,
803
+ ariaLabel,
804
+ xLabel,
805
+ yLabel,
806
+ showLegend = false,
807
+ zeroX = false,
808
+ zeroY = false
809
+ }) {
810
+ const groups = [...new Set(data.map((datum) => datum.group))];
811
+ const f = frame(720, 380);
812
+ const x = linearScale(data.map((datum) => datum.x), [f.x0, f.x1], { zero: zeroX });
813
+ const y = linearScale(data.map((datum) => datum.y), [f.y1, f.y0], { zero: zeroY });
814
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 ${f.width} ${f.height}`, role: "img", "aria-label": ariaLabel, children: [
815
+ /* @__PURE__ */ jsx5(Gridlines, { scale: y, x0: f.x0, x1: f.x1, count: 5 }),
816
+ data.map((datum, index) => {
817
+ const groupIndex = groups.indexOf(datum.group);
818
+ return scatterMark(
819
+ groupIndex,
820
+ x(datum.x),
821
+ y(datum.y),
822
+ sterlingChartColors[groupIndex],
823
+ `${datum.group}-${index}`,
824
+ datum.label ?? `${datum.group}: ${datum.x}, ${datum.y}`
825
+ );
826
+ }),
827
+ /* @__PURE__ */ jsx5(AxisLeft, { scale: y, x: f.x0, count: 5, title: yLabel }),
828
+ /* @__PURE__ */ jsx5(AxisBottom, { scale: x, y: f.y1, count: 6, title: xLabel }),
829
+ showLegend ? groups.map((group, index) => /* @__PURE__ */ jsxs6("g", { transform: `translate(${f.x1 - 150 + index % 2 * 92} ${f.y0 + 2 + Math.floor(index / 2) * 20})`, children: [
830
+ scatterMark(index, 0, 0, sterlingChartColors[index], `legend-${group}`, group),
831
+ /* @__PURE__ */ jsx5("text", { x: "11", y: "4", fill: chartMuted, fontSize: "10.5", fontFamily: "var(--font-mono)", children: group })
832
+ ] }, group)) : null
833
+ ] });
834
+ }
835
+ function SterlingLineChart({
836
+ labels,
837
+ series,
838
+ ariaLabel,
839
+ yLabel,
840
+ xLabel,
841
+ times,
842
+ markers,
843
+ showLegend = false
844
+ }) {
845
+ const f = frame(720, 380);
846
+ const values = series.flatMap((item) => item.values);
847
+ const y = linearScale(values, [f.y1, f.y0], { zero: true });
848
+ const isTime = Array.isArray(times) && times.length === labels.length && labels.length > 1;
849
+ const time = isTime ? timeScale([times[0], times[times.length - 1]], [f.x0, f.x1]) : null;
850
+ const xAt = (index) => time ? time(new Date(times[index])) : f.x0 + index / Math.max(labels.length - 1, 1) * (f.x1 - f.x0);
851
+ const showMarkers = markers ?? labels.length <= 40;
852
+ const labelStep = Math.max(1, Math.ceil(labels.length / 8));
853
+ const lastIndex = labels.length - 1;
854
+ const showCatTick = (index) => {
855
+ if (index === lastIndex) return true;
856
+ if (index % labelStep !== 0) return false;
857
+ return lastIndex - index >= labelStep * 0.55;
858
+ };
859
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 ${f.width} ${f.height}`, role: "img", "aria-label": ariaLabel, children: [
860
+ /* @__PURE__ */ jsx5(Gridlines, { scale: y, x0: f.x0, x1: f.x1, count: 5 }),
861
+ series.map((item, seriesIndex) => {
862
+ const path = item.values.map((value, index) => `${index === 0 ? "M" : "L"} ${xAt(index)} ${y(value)}`).join(" ");
863
+ return /* @__PURE__ */ jsxs6("g", { children: [
864
+ /* @__PURE__ */ jsx5("path", { d: path, fill: "none", stroke: sterlingChartColors[seriesIndex], strokeWidth: sterlingVisualStyle.stroke.series, strokeLinecap: "round", strokeLinejoin: "round" }),
865
+ showMarkers ? item.values.map((value, index) => /* @__PURE__ */ jsx5("circle", { cx: xAt(index), cy: y(value), r: "3.4", fill: sterlingChartColors[seriesIndex], children: /* @__PURE__ */ jsx5("title", { children: `${item.label}, ${labels[index]}: ${value}` }) }, index)) : null
866
+ ] }, item.label);
867
+ }),
868
+ /* @__PURE__ */ jsx5(AxisLeft, { scale: y, x: f.x0, count: 5, title: yLabel }),
869
+ time ? /* @__PURE__ */ jsx5(TimeAxisBottom, { scale: time, y: f.y1, title: xLabel }) : /* @__PURE__ */ jsxs6("g", { children: [
870
+ /* @__PURE__ */ jsx5("line", { x1: f.x0, x2: f.x1, y1: f.y1, y2: f.y1, stroke: "var(--sterling-edge)" }),
871
+ labels.map(
872
+ (label, index) => showCatTick(index) ? /* @__PURE__ */ jsxs6("g", { children: [
873
+ /* @__PURE__ */ jsx5("line", { x1: xAt(index), x2: xAt(index), y1: f.y1, y2: f.y1 + 5, stroke: "var(--sterling-edge)" }),
874
+ /* @__PURE__ */ jsx5("text", { x: xAt(index), y: f.y1 + 18, textAnchor: "middle", fill: chartMuted, fontSize: "11", fontFamily: "var(--font-mono)", children: label })
875
+ ] }, `${label}-${index}`) : null
876
+ ),
877
+ xLabel ? /* @__PURE__ */ jsx5("text", { x: (f.x0 + f.x1) / 2, y: f.y1 + 42, textAnchor: "middle", fill: chartMuted, fontSize: "12", fontFamily: "var(--font-mono)", children: xLabel }) : null
878
+ ] }),
879
+ showLegend ? series.map((item, index) => /* @__PURE__ */ jsxs6("g", { transform: `translate(${f.x0 + 28 + index * 176} ${f.y0 - 2})`, children: [
880
+ /* @__PURE__ */ jsx5("line", { x1: "0", x2: "22", stroke: sterlingChartColors[index], strokeWidth: sterlingVisualStyle.stroke.series }),
881
+ /* @__PURE__ */ jsx5("text", { x: "30", y: "4", fill: chartMuted, fontSize: "11", fontFamily: "var(--font-mono)", children: item.label })
882
+ ] }, item.label)) : null
883
+ ] });
884
+ }
885
+ function SterlingHeatmap({
886
+ values,
887
+ rowLabels,
888
+ columnLabels,
889
+ ariaLabel,
890
+ domain,
891
+ legendTitle,
892
+ showValues = false
893
+ }) {
894
+ const flat = values.flat();
895
+ const maxAbs = domain ?? Math.max(1, Math.ceil(Math.max(...flat.map((value) => Math.abs(value))) * 10) / 10);
896
+ const width = 720;
897
+ const startX = 168;
898
+ const startY = 46;
899
+ const rightPad = 24;
900
+ const gridWidth = width - startX - rightPad;
901
+ const cellW = gridWidth / Math.max(columnLabels.length, 1);
902
+ const cellH = Math.min(cellW * 0.5, 46);
903
+ const gap = 3;
904
+ const gridHeight = cellH * rowLabels.length;
905
+ const height = startY + gridHeight + 60;
906
+ const legendWidth = Math.min(gridWidth, 340);
907
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": ariaLabel, children: [
908
+ columnLabels.map((label, index) => /* @__PURE__ */ jsx5("text", { x: startX + index * cellW + cellW / 2, y: startY - 12, textAnchor: "middle", fill: chartText, fontSize: "11", fontFamily: "var(--font-mono)", children: label }, label)),
909
+ rowLabels.map((label, row) => /* @__PURE__ */ jsxs6("g", { children: [
910
+ /* @__PURE__ */ jsx5("text", { x: startX - 12, y: startY + row * cellH + cellH / 2 + 4, textAnchor: "end", fill: chartText, fontSize: "11", fontFamily: "var(--font-mono)", children: label }),
911
+ columnLabels.map((column, columnIndex) => {
912
+ const value = values[row]?.[columnIndex] ?? 0;
913
+ return /* @__PURE__ */ jsxs6("g", { children: [
914
+ /* @__PURE__ */ jsx5(
915
+ "rect",
916
+ {
917
+ x: startX + columnIndex * cellW,
918
+ y: startY + row * cellH,
919
+ width: cellW - gap,
920
+ height: cellH - gap,
921
+ rx: "3",
922
+ fill: divergentSignedColor(value, maxAbs),
923
+ children: /* @__PURE__ */ jsx5("title", { children: `${label}, ${column}: ${value.toFixed(2)}` })
924
+ }
925
+ ),
926
+ showValues ? /* @__PURE__ */ jsx5(
927
+ "text",
928
+ {
929
+ x: startX + columnIndex * cellW + (cellW - gap) / 2,
930
+ y: startY + row * cellH + (cellH - gap) / 2 + 4,
931
+ textAnchor: "middle",
932
+ fill: Math.abs(value) > maxAbs * 0.55 ? "var(--sterling-surface)" : chartText,
933
+ fontSize: "10.5",
934
+ fontFamily: "var(--font-mono)",
935
+ children: value.toFixed(1)
936
+ }
937
+ ) : null
938
+ ] }, column);
939
+ })
940
+ ] }, label)),
941
+ legendTitle ? /* @__PURE__ */ jsx5("text", { x: startX, y: height - 38, fill: chartMuted, fontSize: "10.5", fontFamily: "var(--font-mono)", children: legendTitle }) : null,
942
+ divergentSignedRamp.map((color, index) => /* @__PURE__ */ jsx5("rect", { x: startX + index * legendWidth / divergentSignedRamp.length, y: height - 28, width: legendWidth / divergentSignedRamp.length + 0.5, height: "13", fill: color }, `${color}-${index}`)),
943
+ /* @__PURE__ */ jsx5("text", { x: startX, y: height - 5, fill: chartMuted, fontSize: "10", fontFamily: "var(--font-mono)", children: -maxAbs }),
944
+ /* @__PURE__ */ jsx5("text", { x: startX + legendWidth / 2, y: height - 5, textAnchor: "middle", fill: chartMuted, fontSize: "10", fontFamily: "var(--font-mono)", children: "0" }),
945
+ /* @__PURE__ */ jsx5("text", { x: startX + legendWidth, y: height - 5, textAnchor: "end", fill: chartMuted, fontSize: "10", fontFamily: "var(--font-mono)", children: `+${maxAbs}` })
946
+ ] });
947
+ }
948
+ function SterlingSequentialSurface({
949
+ values: suppliedValues,
950
+ ariaLabel,
951
+ lowLabel = "low / bajo",
952
+ highLabel = "high / alto"
953
+ }) {
954
+ const defaultRows = 10;
955
+ const defaultColumns = 18;
956
+ const generatedValues = Array.from({ length: defaultRows }, (_, row) => Array.from({ length: defaultColumns }, (_2, column) => {
957
+ const x = column / (defaultColumns - 1);
958
+ const y = row / (defaultRows - 1);
959
+ const firstPeak = Math.exp(-(((x - 0.3) / 0.19) ** 2 + ((y - 0.34) / 0.25) ** 2));
960
+ const secondPeak = 0.76 * Math.exp(-(((x - 0.72) / 0.23) ** 2 + ((y - 0.67) / 0.2) ** 2));
961
+ const saddle = 0.22 * Math.sin(x * Math.PI * 2.4) * Math.cos(y * Math.PI * 1.7);
962
+ return firstPeak + secondPeak + saddle;
963
+ }));
964
+ const values = suppliedValues?.length && suppliedValues.some((row) => row.length) ? suppliedValues : generatedValues;
965
+ const rows = values.length;
966
+ const columns = Math.max(1, ...values.map((row) => row.length));
967
+ const flat = values.flat();
968
+ const min = Math.min(...flat);
969
+ const max = Math.max(...flat);
970
+ const cellWidth = 558 / columns;
971
+ const cellHeight = 240 / Math.max(rows, 1);
972
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: "0 0 720 340", role: "img", "aria-label": ariaLabel, children: [
973
+ /* @__PURE__ */ jsx5("g", { transform: "translate(82 34)", children: values.flatMap((rowValues, row) => rowValues.map((value, column) => {
974
+ const index = Math.max(0, Math.min(sterlingHeatColors.length - 1, Math.round((value - min) / Math.max(max - min, 1) * (sterlingHeatColors.length - 1))));
975
+ return /* @__PURE__ */ jsx5("rect", { x: column * cellWidth, y: row * cellHeight, width: cellWidth + 0.35, height: cellHeight + 0.35, fill: sterlingHeatColors[index], children: /* @__PURE__ */ jsx5("title", { children: `x ${column + 1}, y ${row + 1}: ${value.toFixed(2)}` }) }, `${row}-${column}`);
976
+ })) }),
977
+ /* @__PURE__ */ jsx5("text", { x: "82", y: "302", fill: chartMuted, fontSize: "10", fontFamily: "var(--font-mono)", children: lowLabel }),
978
+ sterlingHeatColors.map((color, index) => /* @__PURE__ */ jsx5("rect", { x: 152 + index * 34, y: "289", width: "34", height: "15", fill: color }, color)),
979
+ /* @__PURE__ */ jsx5("text", { x: "540", y: "302", fill: chartMuted, fontSize: "10", fontFamily: "var(--font-mono)", children: highLabel })
980
+ ] });
981
+ }
982
+ function SterlingCorrelogram({
983
+ values,
984
+ labels,
985
+ ariaLabel
986
+ }) {
987
+ const cell = 67;
988
+ const startX = 112;
989
+ const startY = 34;
990
+ const size = labels.length * cell;
991
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 ${startX + size + 54} ${startY + size + 72}`, role: "img", "aria-label": ariaLabel, children: [
992
+ labels.flatMap((rowLabel, row) => labels.map((columnLabel, column) => {
993
+ const value = values[row]?.[column] ?? 0;
994
+ const color = divergentSignedColor(value, 1);
995
+ const centerX = startX + column * cell + cell / 2;
996
+ const centerY = startY + row * cell + cell / 2;
997
+ return /* @__PURE__ */ jsxs6("g", { children: [
998
+ /* @__PURE__ */ jsx5("rect", { x: startX + column * cell, y: startY + row * cell, width: cell, height: cell, fill: "none", stroke: chartGrid }),
999
+ row === column ? /* @__PURE__ */ jsx5("text", { x: centerX, y: centerY + 4, textAnchor: "middle", fill: chartText, fontSize: "10.5", fontFamily: "var(--font-mono)", children: rowLabel }) : row > column ? /* @__PURE__ */ jsx5(
1000
+ "ellipse",
1001
+ {
1002
+ cx: centerX,
1003
+ cy: centerY,
1004
+ rx: "25",
1005
+ ry: Math.max(2.5, 25 * (1 - Math.abs(value))),
1006
+ transform: `rotate(${value >= 0 ? -45 : 45} ${centerX} ${centerY})`,
1007
+ fill: color,
1008
+ fillOpacity: sterlingVisualStyle.opacity.secondaryMark,
1009
+ stroke: color,
1010
+ children: /* @__PURE__ */ jsx5("title", { children: `${rowLabel}, ${columnLabel}: ${value.toFixed(2)}` })
1011
+ }
1012
+ ) : /* @__PURE__ */ jsx5("text", { x: centerX, y: centerY + 5, textAnchor: "middle", fill: Math.abs(value) >= 0.3 ? color : chartMuted, fontSize: "15", fontFamily: "var(--font-mono)", fontWeight: "700", children: value.toFixed(2) })
1013
+ ] }, `${rowLabel}-${columnLabel}`);
1014
+ })),
1015
+ /* @__PURE__ */ jsx5("text", { x: startX, y: startY + size + 41, fill: chartMuted, fontSize: "10", fontFamily: "var(--font-mono)", children: "-1" }),
1016
+ divergentSignedRamp.map((color, index) => /* @__PURE__ */ jsx5("rect", { x: startX + 28 + index * 27, y: startY + size + 27, width: "27", height: "16", fill: color }, `${color}-${index}`)),
1017
+ /* @__PURE__ */ jsx5("text", { x: startX + 340, y: startY + size + 41, fill: chartMuted, fontSize: "10", fontFamily: "var(--font-mono)", children: "+1" })
1018
+ ] });
1019
+ }
1020
+ function SterlingHistogram({
1021
+ values,
1022
+ bins = 16,
1023
+ ariaLabel,
1024
+ xLabel,
1025
+ yLabel
1026
+ }) {
1027
+ const min = Math.min(...values);
1028
+ const max = Math.max(...values);
1029
+ const step = Math.max((max - min) / bins, Number.EPSILON);
1030
+ const counts = Array.from({ length: bins }, () => 0);
1031
+ values.forEach((value) => {
1032
+ counts[Math.min(bins - 1, Math.floor((value - min) / step))] += 1;
1033
+ });
1034
+ const top = Math.max(...counts, 1);
1035
+ const f = frame(720, 380);
1036
+ const x = linearScale([min, max], [f.x0, f.x1]);
1037
+ const y = linearScale([0, top], [f.y1, f.y0], { zero: true });
1038
+ const barWidth = x(min + step) - x(min);
1039
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 ${f.width} ${f.height}`, role: "img", "aria-label": ariaLabel, children: [
1040
+ /* @__PURE__ */ jsx5(Gridlines, { scale: y, x0: f.x0, x1: f.x1, count: 5 }),
1041
+ counts.map((count, index) => {
1042
+ const lower = min + index * step;
1043
+ const upper = lower + step;
1044
+ return /* @__PURE__ */ jsx5(
1045
+ "rect",
1046
+ {
1047
+ x: x(lower) + 1,
1048
+ y: y(count),
1049
+ width: Math.max(barWidth - 2, 1),
1050
+ height: f.y1 - y(count),
1051
+ fill: sterlingChartColors[0],
1052
+ children: /* @__PURE__ */ jsx5("title", { children: `${lower.toFixed(1)}-${upper.toFixed(1)}: ${count}` })
1053
+ },
1054
+ index
1055
+ );
1056
+ }),
1057
+ /* @__PURE__ */ jsx5(AxisLeft, { scale: y, x: f.x0, count: 5, title: yLabel, format: (value) => `${Math.round(value)}` }),
1058
+ /* @__PURE__ */ jsx5(AxisBottom, { scale: x, y: f.y1, count: 7, title: xLabel })
1059
+ ] });
1060
+ }
1061
+ function SterlingBoxPlot({ groups, ariaLabel, yLabel }) {
1062
+ const all = groups.flatMap((group) => group.values);
1063
+ const top = 24;
1064
+ const rowH = 80;
1065
+ const axisBottom = 48;
1066
+ const height = top + groups.length * rowH + axisBottom;
1067
+ const baseline = top + groups.length * rowH + 6;
1068
+ const x0 = 168;
1069
+ const x1 = 690;
1070
+ const x = linearScale(all, [x0, x1]);
1071
+ const boxHalf = 24;
1072
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 720 ${height}`, role: "img", "aria-label": ariaLabel, children: [
1073
+ x.ticks(6).map((value) => /* @__PURE__ */ jsx5("line", { x1: x(value), x2: x(value), y1: top, y2: baseline, stroke: chartGrid, strokeWidth: sterlingVisualStyle.stroke.grid }, value)),
1074
+ groups.map((group, index) => {
1075
+ const summary = tukeySummary(group.values);
1076
+ const center = top + index * rowH + rowH / 2 - 4;
1077
+ const color = sterlingChartColors[index];
1078
+ return /* @__PURE__ */ jsxs6("g", { children: [
1079
+ /* @__PURE__ */ jsx5("text", { x: x0 - 12, y: center + 4, textAnchor: "end", fill: chartText, fontSize: "12", fontFamily: "var(--font-mono)", children: group.label }),
1080
+ /* @__PURE__ */ jsx5("line", { x1: x(summary.whiskerLow), x2: x(summary.q1), y1: center, y2: center, stroke: color, strokeWidth: sterlingVisualStyle.stroke.mark }),
1081
+ /* @__PURE__ */ jsx5("line", { x1: x(summary.q3), x2: x(summary.whiskerHigh), y1: center, y2: center, stroke: color, strokeWidth: sterlingVisualStyle.stroke.mark }),
1082
+ /* @__PURE__ */ jsx5("line", { x1: x(summary.whiskerLow), x2: x(summary.whiskerLow), y1: center - 12, y2: center + 12, stroke: color, strokeWidth: sterlingVisualStyle.stroke.mark }),
1083
+ /* @__PURE__ */ jsx5("line", { x1: x(summary.whiskerHigh), x2: x(summary.whiskerHigh), y1: center - 12, y2: center + 12, stroke: color, strokeWidth: sterlingVisualStyle.stroke.mark }),
1084
+ /* @__PURE__ */ jsx5("rect", { x: x(summary.q1), y: center - boxHalf, width: Math.max(x(summary.q3) - x(summary.q1), 2), height: boxHalf * 2, rx: "3", fill: color, fillOpacity: sterlingVisualStyle.opacity.interval, stroke: color, strokeWidth: sterlingVisualStyle.stroke.mark, children: /* @__PURE__ */ jsx5("title", { children: `${group.label}: n=${group.values.length}, median ${summary.median.toFixed(2)}, IQR ${summary.q1.toFixed(2)}-${summary.q3.toFixed(2)}` }) }),
1085
+ /* @__PURE__ */ jsx5("line", { x1: x(summary.median), x2: x(summary.median), y1: center - boxHalf, y2: center + boxHalf, stroke: color, strokeWidth: sterlingVisualStyle.stroke.emphasis }),
1086
+ summary.outliers.map((value, outlierIndex) => /* @__PURE__ */ jsx5("circle", { cx: x(value), cy: center, r: "3.4", fill: "none", stroke: color, strokeWidth: sterlingVisualStyle.stroke.detail, children: /* @__PURE__ */ jsx5("title", { children: `${group.label} outlier: ${value.toFixed(2)}` }) }, outlierIndex))
1087
+ ] }, group.label);
1088
+ }),
1089
+ /* @__PURE__ */ jsx5(AxisBottom, { scale: x, y: baseline, count: 6, title: yLabel })
1090
+ ] });
1091
+ }
1092
+ function SterlingDumbbellChart({
1093
+ data,
1094
+ ariaLabel,
1095
+ leftLabel,
1096
+ rightLabel,
1097
+ xLabel
1098
+ }) {
1099
+ const top = 56;
1100
+ const rowH = 66;
1101
+ const axisBottom = xLabel ? 50 : 34;
1102
+ const x0 = 150;
1103
+ const x1 = 660;
1104
+ const height = top + data.length * rowH + axisBottom;
1105
+ const baseline = top + data.length * rowH;
1106
+ const x = linearScale(data.flatMap((datum) => [datum.left, datum.right]), [x0, x1], { zero: true });
1107
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 720 ${height}`, role: "img", "aria-label": ariaLabel, children: [
1108
+ x.ticks(6).map((value) => /* @__PURE__ */ jsx5("line", { x1: x(value), x2: x(value), y1: top - 20, y2: baseline, stroke: chartGrid, strokeWidth: sterlingVisualStyle.stroke.grid }, value)),
1109
+ data.map((datum, index) => {
1110
+ const y = top + index * rowH + rowH / 2 - 4;
1111
+ return /* @__PURE__ */ jsxs6("g", { children: [
1112
+ /* @__PURE__ */ jsx5("text", { x: x0 - 14, y: y + 4, textAnchor: "end", fill: chartText, fontSize: "12", fontFamily: "var(--font-mono)", children: datum.label }),
1113
+ /* @__PURE__ */ jsx5("line", { x1: x(datum.left), x2: x(datum.right), y1: y, y2: y, stroke: chartGrid, strokeWidth: sterlingVisualStyle.stroke.series, strokeLinecap: "round" }),
1114
+ /* @__PURE__ */ jsx5("circle", { cx: x(datum.left), cy: y, r: "8", fill: sterlingChartColors[0], children: /* @__PURE__ */ jsx5("title", { children: `${datum.label}, ${leftLabel}: ${datum.left.toFixed(2)}` }) }),
1115
+ /* @__PURE__ */ jsx5("circle", { cx: x(datum.right), cy: y, r: "8", fill: sterlingChartColors[3], children: /* @__PURE__ */ jsx5("title", { children: `${datum.label}, ${rightLabel}: ${datum.right.toFixed(2)}` }) })
1116
+ ] }, datum.label);
1117
+ }),
1118
+ /* @__PURE__ */ jsx5(AxisBottom, { scale: x, y: baseline, count: 6, title: xLabel })
1119
+ ] });
1120
+ }
1121
+ function kernelDensity(values, steps = 64, domain) {
1122
+ const min = domain?.[0] ?? Math.min(...values);
1123
+ const max = domain?.[1] ?? Math.max(...values);
1124
+ const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
1125
+ const spread = Math.sqrt(values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / Math.max(values.length - 1, 1));
1126
+ const bandwidth = Math.max(1.06 * spread * values.length ** -0.2, (max - min) / 100, 1e-3);
1127
+ return Array.from({ length: steps }, (_, index) => {
1128
+ const value = min + index / (steps - 1) * (max - min);
1129
+ const density = values.reduce((sum, observation) => {
1130
+ const z = (value - observation) / bandwidth;
1131
+ return sum + Math.exp(-0.5 * z * z) / Math.sqrt(2 * Math.PI);
1132
+ }, 0) / (values.length * bandwidth);
1133
+ return { value, density };
1134
+ });
1135
+ }
1136
+ function SterlingDensityChart({ values, ariaLabel, xLabel, yLabel }) {
1137
+ const density = kernelDensity(values);
1138
+ const maxDensity = Math.max(...density.map((point) => point.density), Number.EPSILON);
1139
+ const f = frame(720, 380);
1140
+ const x = linearScale([density[0].value, density[density.length - 1].value], [f.x0, f.x1], { padFraction: 0 });
1141
+ const y = linearScale([0, maxDensity], [f.y1, f.y0], { zero: true });
1142
+ const line = density.map((point, index) => `${index === 0 ? "M" : "L"} ${x(point.value)} ${y(point.density)}`).join(" ");
1143
+ const area = `${line} L ${x(density[density.length - 1].value)} ${f.y1} L ${x(density[0].value)} ${f.y1} Z`;
1144
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 ${f.width} ${f.height}`, role: "img", "aria-label": ariaLabel, children: [
1145
+ /* @__PURE__ */ jsx5(Gridlines, { scale: y, x0: f.x0, x1: f.x1, count: 5 }),
1146
+ /* @__PURE__ */ jsx5("path", { d: area, fill: sterlingSequentialColors[4], fillOpacity: sterlingVisualStyle.opacity.area }),
1147
+ /* @__PURE__ */ jsx5("path", { d: line, fill: "none", stroke: sterlingSequentialColors[7], strokeWidth: sterlingVisualStyle.stroke.series, strokeLinecap: "round", strokeLinejoin: "round" }),
1148
+ /* @__PURE__ */ jsx5(AxisLeft, { scale: y, x: f.x0, count: 5, title: yLabel }),
1149
+ /* @__PURE__ */ jsx5(AxisBottom, { scale: x, y: f.y1, count: 7, title: xLabel })
1150
+ ] });
1151
+ }
1152
+ function SterlingViolinPlot({ groups, ariaLabel, yLabel }) {
1153
+ const all = groups.flatMap((group) => group.values);
1154
+ const observedMin = Math.min(...all);
1155
+ const observedMax = Math.max(...all);
1156
+ const bandwidthOf = (values) => {
1157
+ const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
1158
+ const spread = Math.sqrt(values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / Math.max(values.length - 1, 1));
1159
+ return Math.max(1.06 * spread * values.length ** -0.2, 1e-6);
1160
+ };
1161
+ const maxBandwidth = Math.max(...groups.map((group) => bandwidthOf(group.values)));
1162
+ const margin = Math.max((observedMax - observedMin) * 0.05, maxBandwidth * 3);
1163
+ const domain = [observedMin - margin, observedMax + margin];
1164
+ const f = frame(720, 390, { top: 26, right: 26, bottom: 44, left: 70 });
1165
+ const y = linearScale(domain, [f.y1, f.y0], { padFraction: 0 });
1166
+ const { centers } = bandCenters(groups.length, f.x0, f.x1);
1167
+ const halfWidth = Math.min(46, (f.x1 - f.x0) / groups.length * 0.42);
1168
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 ${f.width} ${f.height}`, role: "img", "aria-label": ariaLabel, children: [
1169
+ /* @__PURE__ */ jsx5(Gridlines, { scale: y, x0: f.x0, x1: f.x1, count: 5 }),
1170
+ groups.map((group, groupIndex) => {
1171
+ const estimate = kernelDensity(group.values, 96, domain);
1172
+ const peak = Math.max(...estimate.map((point) => point.density), Number.EPSILON);
1173
+ const cutoff = peak * 0.01;
1174
+ const firstIndex = estimate.findIndex((point) => point.density >= cutoff);
1175
+ const lastIndex = estimate.length - 1 - [...estimate].reverse().findIndex((point) => point.density >= cutoff);
1176
+ const density = estimate.slice(Math.max(0, firstIndex - 1), Math.min(estimate.length - 1, lastIndex + 1) + 1);
1177
+ const center = centers[groupIndex];
1178
+ const right = density.map((point) => `${center + point.density / peak * halfWidth} ${y(point.value)}`);
1179
+ const left = [...density].reverse().map((point) => `${center - point.density / peak * halfWidth} ${y(point.value)}`);
1180
+ const path = `M ${right.join(" L ")} L ${left.join(" L ")} Z`;
1181
+ const summary = tukeySummary(group.values);
1182
+ const color = sterlingChartColors[groupIndex];
1183
+ return /* @__PURE__ */ jsxs6("g", { children: [
1184
+ /* @__PURE__ */ jsx5("path", { d: path, fill: color, fillOpacity: sterlingVisualStyle.opacity.area, stroke: color, strokeWidth: sterlingVisualStyle.stroke.series, children: /* @__PURE__ */ jsx5("title", { children: `${group.label}: n=${group.values.length}, median ${summary.median.toFixed(1)}, IQR ${summary.q1.toFixed(1)}-${summary.q3.toFixed(1)}` }) }),
1185
+ /* @__PURE__ */ jsx5("line", { x1: center, x2: center, y1: y(summary.whiskerLow), y2: y(summary.whiskerHigh), stroke: color, strokeWidth: sterlingVisualStyle.stroke.detail }),
1186
+ /* @__PURE__ */ jsx5("line", { x1: center - 5, x2: center + 5, y1: y(summary.whiskerLow), y2: y(summary.whiskerLow), stroke: color, strokeWidth: sterlingVisualStyle.stroke.detail }),
1187
+ /* @__PURE__ */ jsx5("line", { x1: center - 5, x2: center + 5, y1: y(summary.whiskerHigh), y2: y(summary.whiskerHigh), stroke: color, strokeWidth: sterlingVisualStyle.stroke.detail }),
1188
+ /* @__PURE__ */ jsx5("line", { x1: center, x2: center, y1: y(summary.q3), y2: y(summary.q1), stroke: color, strokeWidth: sterlingVisualStyle.stroke.interval, strokeLinecap: "round" }),
1189
+ /* @__PURE__ */ jsx5("circle", { cx: center, cy: y(summary.median), r: "4.5", fill: "var(--sterling-surface)", stroke: color, strokeWidth: sterlingVisualStyle.stroke.emphasis }),
1190
+ summary.outliers.map((value, outlierIndex) => /* @__PURE__ */ jsx5("circle", { cx: center, cy: y(value), r: "3", fill: "none", stroke: color, strokeWidth: sterlingVisualStyle.stroke.detail, children: /* @__PURE__ */ jsx5("title", { children: `${group.label} outlier: ${value.toFixed(1)}` }) }, outlierIndex)),
1191
+ /* @__PURE__ */ jsx5("text", { x: center, y: f.y1 + 18, textAnchor: "middle", fill: chartMuted, fontSize: "10.5", fontFamily: "var(--font-mono)", children: group.label })
1192
+ ] }, group.label);
1193
+ }),
1194
+ /* @__PURE__ */ jsx5(AxisLeft, { scale: y, x: f.x0, count: 5, title: yLabel }),
1195
+ /* @__PURE__ */ jsx5("line", { x1: f.x0, x2: f.x1, y1: f.y1, y2: f.y1, stroke: "var(--sterling-edge)" })
1196
+ ] });
1197
+ }
1198
+ function SterlingDonutChart({ data, ariaLabel, centerLabel, centerValue }) {
1199
+ const total = data.reduce((sum, datum) => sum + datum.value, 0);
1200
+ const radius = 96;
1201
+ const circumference = 2 * Math.PI * radius;
1202
+ let offset = 0;
1203
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: "0 0 720 350", role: "img", "aria-label": ariaLabel, children: [
1204
+ /* @__PURE__ */ jsx5("g", { transform: "translate(360 174) rotate(-90)", children: data.map((datum, index) => {
1205
+ const length = datum.value / total * circumference;
1206
+ const currentOffset = offset;
1207
+ offset += length;
1208
+ return /* @__PURE__ */ jsx5(
1209
+ "circle",
1210
+ {
1211
+ cx: "0",
1212
+ cy: "0",
1213
+ r: radius,
1214
+ fill: "none",
1215
+ stroke: sterlingChartColors[index],
1216
+ strokeWidth: sterlingVisualStyle.stroke.ring,
1217
+ strokeDasharray: `${length} ${circumference - length}`,
1218
+ strokeDashoffset: -currentOffset,
1219
+ children: /* @__PURE__ */ jsx5("title", { children: `${datum.label}: ${datum.value} (${(datum.value / total * 100).toFixed(1)}%)` })
1220
+ },
1221
+ datum.label
1222
+ );
1223
+ }) }),
1224
+ /* @__PURE__ */ jsx5("text", { x: "360", y: "171", textAnchor: "middle", fill: chartText, fontSize: "30", fontFamily: "var(--font-display)", fontWeight: "600", children: centerValue ?? total.toLocaleString() }),
1225
+ /* @__PURE__ */ jsx5("text", { x: "360", y: "195", textAnchor: "middle", fill: chartMuted, fontSize: "11", fontFamily: "var(--font-mono)", children: centerLabel })
1226
+ ] });
1227
+ }
1228
+ function SterlingLollipopChart({ data, ariaLabel, unit = "", xLabel }) {
1229
+ const top = 30;
1230
+ const rowH = 56;
1231
+ const axisBottom = xLabel ? 50 : 34;
1232
+ const x0 = 200;
1233
+ const x1 = 660;
1234
+ const height = top + data.length * rowH + axisBottom;
1235
+ const baseline = top + data.length * rowH;
1236
+ const x = linearScale(data.map((datum) => datum.value), [x0, x1], { zero: true });
1237
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: `0 0 720 ${height}`, role: "img", "aria-label": ariaLabel, children: [
1238
+ x.ticks(6).map((value) => /* @__PURE__ */ jsx5("line", { x1: x(value), x2: x(value), y1: top, y2: baseline, stroke: chartGrid, strokeWidth: sterlingVisualStyle.stroke.grid }, value)),
1239
+ data.map((datum, index) => {
1240
+ const y = top + index * rowH + rowH / 2 - 4;
1241
+ const color = sterlingChartColors[index];
1242
+ return /* @__PURE__ */ jsxs6("g", { children: [
1243
+ /* @__PURE__ */ jsx5("text", { x: x0 - 14, y: y + 4, textAnchor: "end", fill: chartText, fontSize: "12", fontFamily: "var(--font-mono)", children: datum.label }),
1244
+ /* @__PURE__ */ jsx5("line", { x1: x(0), x2: x(datum.value), y1: y, y2: y, stroke: color, strokeOpacity: sterlingVisualStyle.opacity.secondaryMark, strokeWidth: sterlingVisualStyle.stroke.series }),
1245
+ /* @__PURE__ */ jsx5("circle", { cx: x(datum.value), cy: y, r: "8", fill: color, children: /* @__PURE__ */ jsx5("title", { children: `${datum.label}: ${datum.value}${unit}` }) }),
1246
+ /* @__PURE__ */ jsxs6("text", { x: x(datum.value) + 15, y: y + 4, fill: chartMuted, fontSize: "11", fontFamily: "var(--font-mono)", children: [
1247
+ datum.value,
1248
+ unit
1249
+ ] })
1250
+ ] }, datum.label);
1251
+ }),
1252
+ /* @__PURE__ */ jsx5(AxisBottom, { scale: x, y: baseline, count: 6, title: xLabel, format: (value) => `${value}${unit}` })
1253
+ ] });
1254
+ }
1255
+ function polarPoint(centerX, centerY, radius, angle) {
1256
+ const radians = (angle - 90) * Math.PI / 180;
1257
+ return { x: centerX + radius * Math.cos(radians), y: centerY + radius * Math.sin(radians) };
1258
+ }
1259
+ function pieArc(centerX, centerY, radius, startAngle, endAngle) {
1260
+ const start = polarPoint(centerX, centerY, radius, endAngle);
1261
+ const end = polarPoint(centerX, centerY, radius, startAngle);
1262
+ return `M ${centerX} ${centerY} L ${start.x} ${start.y} A ${radius} ${radius} 0 ${endAngle - startAngle > 180 ? 1 : 0} 0 ${end.x} ${end.y} Z`;
1263
+ }
1264
+ function SterlingPieChart({ data, ariaLabel }) {
1265
+ const total = data.reduce((sum, datum) => sum + datum.value, 0);
1266
+ let angle = 0;
1267
+ return /* @__PURE__ */ jsx5("svg", { className: "sterling-chart", viewBox: "0 0 720 390", role: "img", "aria-label": ariaLabel, children: data.map((datum, index) => {
1268
+ const start = angle;
1269
+ const end = angle + datum.value / total * 360;
1270
+ angle = end;
1271
+ return /* @__PURE__ */ jsx5("path", { d: pieArc(360, 194, 138, start, end), fill: sterlingChartColors[index], stroke: "var(--sterling-surface)", strokeWidth: sterlingVisualStyle.stroke.mark, children: /* @__PURE__ */ jsx5("title", { children: `${datum.label}: ${datum.value.toLocaleString()} (${(datum.value / total * 100).toFixed(1)}%)` }) }, datum.label);
1272
+ }) });
1273
+ }
1274
+ function SterlingChordChart({
1275
+ rowLabels,
1276
+ columnLabels,
1277
+ values,
1278
+ ariaLabel
1279
+ }) {
1280
+ const labels = [...rowLabels, ...columnLabels];
1281
+ const matrix = Array.from({ length: labels.length }, () => Array(labels.length).fill(0));
1282
+ values.forEach((row, rowIndex) => row.forEach((value, columnIndex) => {
1283
+ matrix[rowIndex][rowLabels.length + columnIndex] = value;
1284
+ matrix[rowLabels.length + columnIndex][rowIndex] = value;
1285
+ }));
1286
+ const chords = d3Chord().padAngle(0.055).sortSubgroups((left, right) => right - left)(matrix);
1287
+ const arcPath = d3Arc().innerRadius(126).outerRadius(145).cornerRadius(3).startAngle((group) => group.startAngle).endAngle((group) => group.endAngle);
1288
+ const ribbonPath = d3Ribbon().radius(122);
1289
+ return /* @__PURE__ */ jsx5("svg", { className: "sterling-chart", viewBox: "0 0 720 420", role: "img", "aria-label": ariaLabel, children: /* @__PURE__ */ jsxs6("g", { transform: "translate(360 204)", children: [
1290
+ chords.map((item, index) => /* @__PURE__ */ jsx5("path", { d: ribbonPath(item) ?? void 0, fill: sterlingChartColors[item.source.index], fillOpacity: sterlingVisualStyle.opacity.relationship, children: /* @__PURE__ */ jsx5("title", { children: `${labels[item.source.index]} + ${labels[item.target.index]}: ${item.source.value}` }) }, `${item.source.index}-${item.target.index}-${index}`)),
1291
+ chords.groups.map((group) => /* @__PURE__ */ jsx5("path", { d: arcPath(group) ?? void 0, fill: sterlingChartColors[group.index], stroke: "var(--sterling-surface)", strokeWidth: sterlingVisualStyle.stroke.mark }, group.index)),
1292
+ chords.groups.map((group) => {
1293
+ const angle = (group.startAngle + group.endAngle) / 2;
1294
+ const x = Math.sin(angle) * 170;
1295
+ const y = -Math.cos(angle) * 170;
1296
+ return /* @__PURE__ */ jsx5("text", { x, y: y + 4, textAnchor: x < -8 ? "end" : x > 8 ? "start" : "middle", fill: chartText, fontSize: "10", fontFamily: "var(--font-mono)", children: labels[group.index] }, `label-${group.index}`);
1297
+ })
1298
+ ] }) });
1299
+ }
1300
+ function SterlingSankeyChart({ links, ariaLabel }) {
1301
+ const sources = [...new Set(links.map((link) => link.source))];
1302
+ const targets = [...new Set(links.map((link) => link.target))];
1303
+ const labels = [...sources, ...targets];
1304
+ const graph = d3Sankey().nodeWidth(14).nodePadding(12).extent([[112, 36], [608, 360]])({
1305
+ nodes: labels.map((name, index) => ({ name, colorIndex: index })),
1306
+ links: links.map((link) => ({
1307
+ source: labels.indexOf(link.source),
1308
+ target: labels.indexOf(link.target),
1309
+ value: link.value,
1310
+ sourceName: link.source,
1311
+ targetName: link.target,
1312
+ colorIndex: sources.indexOf(link.source)
1313
+ }))
1314
+ });
1315
+ const linkPath = sankeyLinkHorizontal();
1316
+ return /* @__PURE__ */ jsxs6("svg", { className: "sterling-chart", viewBox: "0 0 720 420", role: "img", "aria-label": ariaLabel, children: [
1317
+ graph.links.map((link, index) => /* @__PURE__ */ jsx5(
1318
+ "path",
1319
+ {
1320
+ d: linkPath(link) ?? void 0,
1321
+ fill: "none",
1322
+ stroke: sterlingChartColors[link.colorIndex],
1323
+ strokeOpacity: sterlingVisualStyle.opacity.relationship,
1324
+ strokeWidth: Math.max(sterlingVisualStyle.stroke.flowMinimum, link.width ?? sterlingVisualStyle.stroke.flowMinimum),
1325
+ children: /* @__PURE__ */ jsx5("title", { children: `${link.sourceName} \u2192 ${link.targetName}: ${link.value}` })
1326
+ },
1327
+ `${link.sourceName}-${link.targetName}-${index}`
1328
+ )),
1329
+ graph.nodes.map((node) => {
1330
+ const x0 = node.x0 ?? 0;
1331
+ const x1 = node.x1 ?? x0;
1332
+ const y0 = node.y0 ?? 0;
1333
+ const y1 = node.y1 ?? y0;
1334
+ const sourceSide = (node.depth ?? 0) === 0;
1335
+ return /* @__PURE__ */ jsxs6("g", { children: [
1336
+ /* @__PURE__ */ jsx5("rect", { x: x0, y: y0, width: Math.max(1, x1 - x0), height: Math.max(1, y1 - y0), rx: "2", fill: sterlingChartColors[node.colorIndex] }),
1337
+ /* @__PURE__ */ jsx5("text", { x: sourceSide ? x0 - 10 : x1 + 10, y: (y0 + y1) / 2 + 4, textAnchor: sourceSide ? "end" : "start", fill: chartText, fontSize: "11", fontFamily: "var(--font-mono)", children: node.name })
1338
+ ] }, node.name);
1339
+ })
1340
+ ] });
1341
+ }
1342
+
1343
+ // src/canonicalCharts.tsx
1344
+ import { geoAlbersUsa, geoPath } from "d3-geo";
1345
+ import { hierarchy, treemap, treemapSquarify } from "d3-hierarchy";
1346
+ import { feature } from "topojson-client";
1347
+ import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
1348
+ var muted = "var(--sterling-muted)";
1349
+ var grid = "var(--sterling-grid)";
1350
+ var surface = "var(--sterling-surface)";
1351
+ var text = "var(--sterling-text)";
1352
+ function SterlingVerticalBarChart({ data, ariaLabel, yLabel, xLabel }) {
1353
+ const f = frame(720, 380, { top: 26, right: 26, bottom: 56, left: 70 });
1354
+ const y = linearScale([0, Math.max(...data.map(([, value]) => value), 1)], [f.y1, f.y0], { zero: true });
1355
+ const { centers, step } = bandCenters(data.length, f.x0, f.x1);
1356
+ const width = Math.min(70, step * 0.62);
1357
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: `0 0 ${f.width} ${f.height}`, role: "img", "aria-label": ariaLabel, children: [
1358
+ /* @__PURE__ */ jsx6(Gridlines, { scale: y, x0: f.x0, x1: f.x1, count: 5 }),
1359
+ data.map(([label, value], index) => {
1360
+ const cx = centers[index];
1361
+ return /* @__PURE__ */ jsxs7("g", { children: [
1362
+ /* @__PURE__ */ jsx6("rect", { x: cx - width / 2, y: y(value), width, height: f.y1 - y(value), rx: "6", fill: sterlingChartColors[index], children: /* @__PURE__ */ jsx6("title", { children: `${label}: ${value.toFixed(1)}` }) }),
1363
+ /* @__PURE__ */ jsx6("text", { x: cx, y: y(value) - 8, textAnchor: "middle", fill: muted, fontSize: "11", fontFamily: "var(--font-mono)", children: value.toFixed(1) }),
1364
+ /* @__PURE__ */ jsx6("text", { x: cx, y: f.y1 + 18, textAnchor: "middle", fill: text, fontSize: "12", fontFamily: "var(--font-mono)", children: label })
1365
+ ] }, label);
1366
+ }),
1367
+ /* @__PURE__ */ jsx6(AxisLeft, { scale: y, x: f.x0, count: 5, title: yLabel }),
1368
+ /* @__PURE__ */ jsx6("line", { x1: f.x0, x2: f.x1, y1: f.y1, y2: f.y1, stroke: "var(--sterling-edge)" }),
1369
+ xLabel ? /* @__PURE__ */ jsx6("text", { x: (f.x0 + f.x1) / 2, y: f.y1 + 42, textAnchor: "middle", fill: muted, fontSize: "12", fontFamily: "var(--font-mono)", children: xLabel }) : null
1370
+ ] });
1371
+ }
1372
+ function SterlingCandlestickChart({ rows, ariaLabel, yLabel, xLabel }) {
1373
+ const values = rows.flatMap(([, open, high, low, close]) => [open, high, low, close]);
1374
+ const f = frame(720, 380, { top: 26, right: 26, bottom: 56, left: 70 });
1375
+ const y = linearScale(values, [f.y1, f.y0], { padFraction: 0.06 });
1376
+ const { centers, step } = bandCenters(rows.length, f.x0, f.x1);
1377
+ const bodyWidth = Math.min(15, step * 0.62);
1378
+ const rising = sterlingDivergentColors[2];
1379
+ const falling = sterlingDivergentColors[8];
1380
+ const labelStep = Math.max(1, Math.ceil(rows.length / 8));
1381
+ const lastIndex = rows.length - 1;
1382
+ const showDate = (index) => index === lastIndex || index % labelStep === 0 && lastIndex - index >= labelStep * 0.55;
1383
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: `0 0 ${f.width} ${f.height}`, role: "img", "aria-label": ariaLabel, children: [
1384
+ /* @__PURE__ */ jsx6(Gridlines, { scale: y, x0: f.x0, x1: f.x1, count: 5 }),
1385
+ rows.map(([date, open, high, low, close], index) => {
1386
+ const x = centers[index];
1387
+ const up = close >= open;
1388
+ const color = up ? rising : falling;
1389
+ const top = y(Math.max(open, close));
1390
+ const bottom = y(Math.min(open, close));
1391
+ return /* @__PURE__ */ jsxs7("g", { children: [
1392
+ /* @__PURE__ */ jsx6("title", { children: `${date}: open ${open}, high ${high}, low ${low}, close ${close}` }),
1393
+ /* @__PURE__ */ jsx6("line", { x1: x, x2: x, y1: y(high), y2: y(low), stroke: color, strokeWidth: sterlingVisualStyle.stroke.candle }),
1394
+ /* @__PURE__ */ jsx6("rect", { x: x - bodyWidth / 2, y: top, width: bodyWidth, height: Math.max(2, bottom - top), rx: "1.5", fill: up ? color : surface, stroke: color, strokeWidth: sterlingVisualStyle.stroke.candle }),
1395
+ showDate(index) ? /* @__PURE__ */ jsx6("text", { x, y: f.y1 + 18, textAnchor: "middle", fill: muted, fontSize: "10", fontFamily: "var(--font-mono)", children: date }) : null
1396
+ ] }, date);
1397
+ }),
1398
+ /* @__PURE__ */ jsx6(AxisLeft, { scale: y, x: f.x0, count: 5, title: yLabel, format: (value) => value.toFixed(0) }),
1399
+ /* @__PURE__ */ jsx6("line", { x1: f.x0, x2: f.x1, y1: f.y1, y2: f.y1, stroke: "var(--sterling-edge)" }),
1400
+ xLabel ? /* @__PURE__ */ jsx6("text", { x: (f.x0 + f.x1) / 2, y: f.y1 + 42, textAnchor: "middle", fill: muted, fontSize: "12", fontFamily: "var(--font-mono)", children: xLabel }) : null
1401
+ ] });
1402
+ }
1403
+ function polar(cx, cy, radius, index, count) {
1404
+ const angle = -Math.PI / 2 + index * Math.PI * 2 / count;
1405
+ return [cx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius];
1406
+ }
1407
+ function SterlingRadarChart({ labels, series, domain, ariaLabel, unit = "" }) {
1408
+ const cx = 360, cy = 196, radius = 130;
1409
+ const scale = (value) => (value - domain[0]) / Math.max(domain[1] - domain[0], 1) * radius;
1410
+ const levels = [0.2, 0.4, 0.6, 0.8, 1];
1411
+ const ringValue = (level) => domain[0] + level * (domain[1] - domain[0]);
1412
+ const format = (value) => `${Number.isInteger(value) ? value : value.toFixed(1)}${unit}`;
1413
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: "0 0 720 390", role: "img", "aria-label": ariaLabel, children: [
1414
+ levels.map((level) => /* @__PURE__ */ jsx6("polygon", { points: labels.map((_, index) => polar(cx, cy, radius * level, index, labels.length).join(",")).join(" "), fill: "none", stroke: grid }, level)),
1415
+ labels.map((label, index) => {
1416
+ const [x, y] = polar(cx, cy, radius, index, labels.length);
1417
+ const [lx, ly] = polar(cx, cy, radius + 24, index, labels.length);
1418
+ return /* @__PURE__ */ jsxs7("g", { children: [
1419
+ /* @__PURE__ */ jsx6("line", { x1: cx, y1: cy, x2: x, y2: y, stroke: grid }),
1420
+ /* @__PURE__ */ jsx6("text", { x: lx, y: ly + 4, textAnchor: "middle", fill: muted, fontSize: "10", fontFamily: "var(--font-mono)", children: label })
1421
+ ] }, label);
1422
+ }),
1423
+ series.map(([name, ...values], index) => {
1424
+ const color = sterlingSequentialColors[Math.min(9, index + 2)];
1425
+ const latest = index === series.length - 1;
1426
+ return /* @__PURE__ */ jsx6("polygon", { points: values.map((value, axis) => polar(cx, cy, scale(value), axis, labels.length).join(",")).join(" "), fill: latest ? color : "none", fillOpacity: sterlingVisualStyle.opacity.surface, stroke: color, strokeWidth: sterlingVisualStyle.stroke.detail, strokeOpacity: latest ? 1 : sterlingVisualStyle.opacity.guide, children: /* @__PURE__ */ jsx6("title", { children: `${name}: ${values.join(", ")}` }) }, name);
1427
+ }),
1428
+ [0, ...levels].map((level) => /* @__PURE__ */ jsx6(
1429
+ "text",
1430
+ {
1431
+ x: cx + 6,
1432
+ y: cy - radius * level + 3,
1433
+ fill: muted,
1434
+ fontSize: "9",
1435
+ fontFamily: "var(--font-mono)",
1436
+ stroke: surface,
1437
+ strokeWidth: sterlingVisualStyle.stroke.halo,
1438
+ paintOrder: "stroke",
1439
+ children: format(ringValue(level))
1440
+ },
1441
+ `r-${level}`
1442
+ ))
1443
+ ] });
1444
+ }
1445
+ function SterlingRidgelineChart({ groups, ariaLabel, xLabel, unit = "" }) {
1446
+ const all = groups.flatMap((group) => group.values);
1447
+ const observedMin = Math.min(...all);
1448
+ const observedMax = Math.max(...all);
1449
+ const margin = (observedMax - observedMin) * 0.08;
1450
+ const domain = [observedMin - margin, observedMax + margin];
1451
+ const x0 = 118, x1 = 664;
1452
+ const baseline = 338;
1453
+ const rowStep = (baseline - 60) / Math.max(groups.length, 1);
1454
+ const amplitude = rowStep * 1.9;
1455
+ const x = linearScale(domain, [x0, x1], { padFraction: 0 });
1456
+ const peak = Math.max(
1457
+ ...groups.map((group) => Math.max(...kernelDensity(group.values, 96, domain).map((point) => point.density))),
1458
+ Number.EPSILON
1459
+ );
1460
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: "0 0 720 390", role: "img", "aria-label": ariaLabel, children: [
1461
+ x.ticks(6).map((value) => /* @__PURE__ */ jsx6("line", { x1: x(value), x2: x(value), y1: 26, y2: baseline, stroke: grid, strokeWidth: sterlingVisualStyle.stroke.grid }, value)),
1462
+ groups.map((source, index) => {
1463
+ const base = 60 + index * rowStep;
1464
+ const density = kernelDensity(source.values, 96, domain);
1465
+ const points = density.map((point) => `${x(point.value)} ${base - point.density / peak * amplitude}`);
1466
+ const color = sterlingChartColors[index % sterlingChartColors.length];
1467
+ return /* @__PURE__ */ jsxs7("g", { children: [
1468
+ /* @__PURE__ */ jsx6("text", { x: x0 - 12, y: base + 3, textAnchor: "end", fill: text, fontSize: "10", fontFamily: "var(--font-mono)", children: source.label }),
1469
+ /* @__PURE__ */ jsx6("path", { d: `M ${x(domain[0])} ${base} L ${points.join(" L ")} L ${x(domain[1])} ${base} Z`, fill: color, fillOpacity: sterlingVisualStyle.opacity.ridge, stroke: color, strokeWidth: sterlingVisualStyle.stroke.series, strokeLinejoin: "round" })
1470
+ ] }, source.label);
1471
+ }),
1472
+ /* @__PURE__ */ jsx6(AxisBottom, { scale: x, y: baseline, count: 6, title: xLabel, format: (value) => `${value}${unit}` })
1473
+ ] });
1474
+ }
1475
+ function SterlingDensity2DChart({ points, regionLabels, ariaLabel, xLabel, yLabel }) {
1476
+ const f = frame(720, 380, { top: 26, right: 26, bottom: 56, left: 74 });
1477
+ const x = linearScale(points.map((point) => point[0]), [f.x0, f.x1]);
1478
+ const y = linearScale(points.map((point) => point[1]), [f.y1, f.y0]);
1479
+ const pxX = Math.abs(x(1e3) - x(0)) / 1e3;
1480
+ const pxY = Math.abs(y(1) - y(0));
1481
+ const specs = [[4570, 71.26, 559, 0.74, 0.36], [4012, 69.71, 605, 1.02, 0.46], [4611, 71.77, 283, 1.04, -0.03], [4703, 71.23, 664, 1.35, -0.29]];
1482
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: `0 0 ${f.width} ${f.height}`, role: "img", "aria-label": ariaLabel, children: [
1483
+ /* @__PURE__ */ jsx6(Gridlines, { scale: y, x0: f.x0, x1: f.x1, count: 5 }),
1484
+ specs.flatMap(([meanX, meanY, sdX, sdY, correlation2], index) => [1, 0.68, 0.38].map((level) => /* @__PURE__ */ jsx6("ellipse", { cx: x(meanX), cy: y(meanY), rx: sdX * pxX * 1.7 * level, ry: sdY * pxY * 1.7 * level, transform: `rotate(${-correlation2 * 35} ${x(meanX)} ${y(meanY)})`, fill: sterlingChartColors[index], fillOpacity: sterlingVisualStyle.opacity.contour, stroke: sterlingChartColors[index], strokeWidth: sterlingVisualStyle.stroke.detail }, `${index}-${level}`))),
1485
+ points.map(([income, life, group], index) => /* @__PURE__ */ jsx6("circle", { cx: x(income), cy: y(life), r: "4", fill: sterlingChartColors[group], fillOpacity: sterlingVisualStyle.opacity.secondaryMark, children: /* @__PURE__ */ jsx6("title", { children: `${regionLabels[group]}: ${income}, ${life}` }) }, index)),
1486
+ /* @__PURE__ */ jsx6(AxisLeft, { scale: y, x: f.x0, count: 5, title: yLabel }),
1487
+ /* @__PURE__ */ jsx6(AxisBottom, { scale: x, y: f.y1, count: 6, title: xLabel, format: (value) => `${(value / 1e3).toFixed(1)}k` })
1488
+ ] });
1489
+ }
1490
+ function SterlingTreemapChart({ rows, ariaLabel }) {
1491
+ const continents = [...new Set(rows.map(([continent]) => continent))];
1492
+ const rootData = {
1493
+ name: "world",
1494
+ children: continents.map((continent) => ({
1495
+ name: continent,
1496
+ continent,
1497
+ children: rows.filter(([name]) => name === continent).map(([, , country, value]) => ({ name: country, continent, value }))
1498
+ }))
1499
+ };
1500
+ const rankByCountry = /* @__PURE__ */ new Map();
1501
+ continents.forEach((continent) => {
1502
+ const list = rows.filter(([name]) => name === continent).sort((left, right) => right[3] - left[3]);
1503
+ list.forEach(([, , country], index) => rankByCountry.set(country, { rank: index, total: list.length }));
1504
+ });
1505
+ const stopFor = (country) => {
1506
+ const entry = rankByCountry.get(country);
1507
+ if (!entry || entry.total < 2) return sterlingRampStops;
1508
+ return Math.round(sterlingRampStops - entry.rank * (sterlingRampStops - 1) / (entry.total - 1));
1509
+ };
1510
+ const treeRoot = hierarchy(rootData).sum((datum) => datum.value ?? 0).sort((left, right) => (right.value ?? 0) - (left.value ?? 0));
1511
+ const root = treemap().tile(treemapSquarify).size([680, 330]).paddingInner(3).paddingOuter(4).paddingTop((node) => node.depth === 1 ? 21 : 0).round(true)(treeRoot);
1512
+ return /* @__PURE__ */ jsx6("svg", { className: "sterling-chart", viewBox: "0 0 720 370", role: "img", "aria-label": ariaLabel, children: /* @__PURE__ */ jsxs7("g", { transform: "translate(20 20)", children: [
1513
+ root.descendants().filter((node) => node.depth === 1).map((node) => {
1514
+ const color = sterlingChartColors[continents.indexOf(node.data.name)];
1515
+ return /* @__PURE__ */ jsxs7("g", { children: [
1516
+ /* @__PURE__ */ jsx6("rect", { x: node.x0, y: node.y0, width: node.x1 - node.x0, height: node.y1 - node.y0, rx: "3", fill: "none", stroke: color, strokeWidth: sterlingVisualStyle.stroke.detail }),
1517
+ /* @__PURE__ */ jsx6("text", { x: node.x0 + 6, y: node.y0 + 13, fill: color, fontSize: "9", fontFamily: "var(--font-mono)", fontWeight: "700", children: node.data.name })
1518
+ ] }, `group-${node.data.name}`);
1519
+ }),
1520
+ root.leaves().map((leaf) => {
1521
+ const continent = leaf.data.continent ?? "";
1522
+ const width = leaf.x1 - leaf.x0;
1523
+ const height = leaf.y1 - leaf.y0;
1524
+ const fontSize = width > 120 ? 10 : 8;
1525
+ const stop = stopFor(leaf.data.name);
1526
+ const fits = width > leaf.data.name.length * fontSize * 0.62 + 10 && height > fontSize * 2.4;
1527
+ return /* @__PURE__ */ jsxs7("g", { children: [
1528
+ /* @__PURE__ */ jsx6("rect", { x: leaf.x0, y: leaf.y0, width, height, rx: "2", fill: sterlingRamp(continents.indexOf(continent), stop), children: /* @__PURE__ */ jsx6("title", { children: `${leaf.data.name}: ${(leaf.value ?? 0).toLocaleString()}` }) }),
1529
+ fits ? /* @__PURE__ */ jsx6("text", { x: leaf.x0 + 6, y: leaf.y0 + fontSize + 5, fill: stop > sterlingRampStops / 2 ? surface : text, fontSize, fontFamily: "var(--font-mono)", children: leaf.data.name }) : null
1530
+ ] }, leaf.data.name);
1531
+ })
1532
+ ] }) });
1533
+ }
1534
+ function SterlingNetworkChart({ nodes, edges, ariaLabel }) {
1535
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: "0 0 560 300", role: "img", "aria-label": ariaLabel, children: [
1536
+ edges.map(([source, target], index) => /* @__PURE__ */ jsx6("line", { x1: nodes[source][1] + 42, y1: nodes[source][2], x2: nodes[target][1] + 42, y2: nodes[target][2], stroke: grid, strokeWidth: sterlingVisualStyle.stroke.detail }, index)),
1537
+ nodes.map(([name, x, y, degree, community]) => {
1538
+ const radius = 5 + (degree - 10) * 0.45;
1539
+ return /* @__PURE__ */ jsxs7("g", { children: [
1540
+ /* @__PURE__ */ jsx6("circle", { cx: x + 42, cy: y, r: radius, fill: sterlingChartColors[community], stroke: surface, strokeWidth: sterlingVisualStyle.stroke.mark, children: /* @__PURE__ */ jsx6("title", { children: `${name}: ${degree}` }) }),
1541
+ degree >= 19 || name === "Y Holtz" ? /* @__PURE__ */ jsx6("text", { x: x + 42, y: y - radius - 7, textAnchor: "middle", fill: muted, fontSize: "7.5", fontFamily: "var(--font-mono)", children: name }) : null
1542
+ ] }, name);
1543
+ })
1544
+ ] });
1545
+ }
1546
+ function SterlingDendrogramChart({ labels, merge, height, order, groups, ariaLabel, yLabel, cutLabel }) {
1547
+ const leafY = 270, maximum = Math.max(...height), axisX = 78, step = (666 - axisX - 24) / (order.length - 1);
1548
+ const leafX = new Map(order.map((leaf, position) => [leaf, axisX + 12 + position * step]));
1549
+ const groupByLeaf = /* @__PURE__ */ new Map();
1550
+ groups.forEach((members, group) => members.forEach((leaf) => groupByLeaf.set(leaf, group)));
1551
+ const dist = linearScale([0, maximum], [leafY, leafY - 206], { zero: true });
1552
+ const clusters = [];
1553
+ const child = (reference) => reference < 0 ? { x: leafX.get(-reference) ?? axisX, y: leafY, members: [-reference], group: groupByLeaf.get(-reference) ?? null } : clusters[reference - 1];
1554
+ const branches = merge.map(([leftRef, rightRef], index) => {
1555
+ const left = child(leftRef), right = child(rightRef), nodeY = dist(height[index]), members = [...left.members, ...right.members], memberships = [...new Set(members.map((leaf) => groupByLeaf.get(leaf)))], group = memberships.length === 1 ? memberships[0] ?? null : null;
1556
+ clusters.push({ x: (left.x + right.x) / 2, y: nodeY, members, group });
1557
+ return { left, right, y: nodeY, group };
1558
+ });
1559
+ const cut = 0.388, cutY = dist(cut);
1560
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: "0 0 720 390", role: "img", "aria-label": ariaLabel, children: [
1561
+ /* @__PURE__ */ jsx6(AxisLeft, { scale: dist, x: axisX, gridX1: 666, count: 5, title: yLabel }),
1562
+ /* @__PURE__ */ jsx6("line", { x1: axisX, x2: "666", y1: cutY, y2: cutY, stroke: muted, strokeDasharray: "5 5" }),
1563
+ cutLabel ? /* @__PURE__ */ jsx6("text", { x: "666", y: cutY - 6, textAnchor: "end", fill: muted, fontSize: "9", fontFamily: "var(--font-mono)", children: `${cutLabel} ${cut}` }) : null,
1564
+ groups.map((members, index) => {
1565
+ const positions = members.map((leaf) => leafX.get(leaf) ?? 0);
1566
+ const x0 = Math.min(...positions) - 9, x1 = Math.max(...positions) + 9;
1567
+ return /* @__PURE__ */ jsx6("rect", { x: x0, y: cutY, width: x1 - x0, height: leafY - cutY + 50, rx: "6", fill: sterlingChartColors[index], fillOpacity: sterlingVisualStyle.opacity.ghost, stroke: sterlingChartColors[index], strokeWidth: sterlingVisualStyle.stroke.detail }, index);
1568
+ }),
1569
+ branches.map(({ left, right, y, group }, index) => /* @__PURE__ */ jsxs7("g", { children: [
1570
+ /* @__PURE__ */ jsx6("path", { d: `M ${left.x} ${left.y} V ${y}`, stroke: left.group === null ? muted : sterlingChartColors[left.group], fill: "none", strokeWidth: sterlingVisualStyle.stroke.detail }),
1571
+ /* @__PURE__ */ jsx6("path", { d: `M ${right.x} ${right.y} V ${y}`, stroke: right.group === null ? muted : sterlingChartColors[right.group], fill: "none", strokeWidth: sterlingVisualStyle.stroke.detail }),
1572
+ /* @__PURE__ */ jsx6("path", { d: `M ${left.x} ${y} H ${right.x}`, stroke: group === null ? muted : sterlingChartColors[group], fill: "none", strokeWidth: sterlingVisualStyle.stroke.detail })
1573
+ ] }, index)),
1574
+ order.map((leaf) => {
1575
+ const x = leafX.get(leaf) ?? 0, group = groupByLeaf.get(leaf) ?? 0;
1576
+ return /* @__PURE__ */ jsxs7("g", { children: [
1577
+ /* @__PURE__ */ jsx6("circle", { cx: x, cy: leafY, r: "3.5", fill: sterlingChartColors[group] }),
1578
+ /* @__PURE__ */ jsx6("text", { x: x + 3, y: leafY + 12, transform: `rotate(50 ${x + 3} ${leafY + 12})`, fill: muted, fontSize: "7.5", fontFamily: "var(--font-mono)", children: labels[leaf - 1] })
1579
+ ] }, leaf);
1580
+ })
1581
+ ] });
1582
+ }
1583
+ function SterlingVolcanoChart({ hex, labels, ariaLabel, xLabel, yLabel }) {
1584
+ const genes = Array.from({ length: hex.length / 4 }, (_, index) => {
1585
+ const offset = index * 4;
1586
+ return [parseInt(hex.slice(offset, offset + 2), 16) / 255 * 12 - 6, parseInt(hex.slice(offset + 2, offset + 4), 16) / 255 * 5];
1587
+ });
1588
+ const f = frame(720, 380, { top: 26, right: 26, bottom: 56, left: 70 });
1589
+ const x = linearScale([-6, 6], [f.x0, f.x1], { padFraction: 0 });
1590
+ const y = linearScale([0, 5], [f.y1, f.y0], { zero: true });
1591
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: `0 0 ${f.width} ${f.height}`, role: "img", "aria-label": ariaLabel, children: [
1592
+ /* @__PURE__ */ jsx6(Gridlines, { scale: y, x0: f.x0, x1: f.x1, count: 5 }),
1593
+ /* @__PURE__ */ jsx6("line", { x1: x(0), x2: x(0), y1: f.y0, y2: f.y1, stroke: grid, strokeWidth: sterlingVisualStyle.stroke.grid }),
1594
+ [-1, 1].map((v) => /* @__PURE__ */ jsx6("line", { x1: x(v), x2: x(v), y1: f.y0, y2: f.y1, stroke: sterlingChartColors[7], strokeDasharray: "5 5", strokeOpacity: sterlingVisualStyle.opacity.guide, strokeWidth: sterlingVisualStyle.stroke.grid }, v)),
1595
+ /* @__PURE__ */ jsx6("line", { x1: f.x0, x2: f.x1, y1: y(1.3), y2: y(1.3), stroke: sterlingChartColors[7], strokeDasharray: "5 5", strokeOpacity: sterlingVisualStyle.opacity.guide, strokeWidth: sterlingVisualStyle.stroke.grid }),
1596
+ genes.map(([effect, evidence], index) => {
1597
+ const significant = Math.abs(effect) >= 1 && evidence >= 1.3;
1598
+ const color = significant ? effect < 0 ? sterlingChartColors[2] : sterlingChartColors[1] : sterlingChartColors[7];
1599
+ return /* @__PURE__ */ jsx6("circle", { cx: x(effect), cy: y(evidence), r: significant ? 2.5 : 1.6, fill: color, fillOpacity: significant ? sterlingVisualStyle.opacity.signal : sterlingVisualStyle.opacity.mutedMark }, index);
1600
+ }),
1601
+ labels.map(([gene, effect, evidence]) => /* @__PURE__ */ jsxs7("g", { children: [
1602
+ /* @__PURE__ */ jsx6("circle", { cx: x(effect), cy: y(evidence), r: "4", fill: effect < 0 ? sterlingChartColors[2] : sterlingChartColors[1], stroke: surface }),
1603
+ /* @__PURE__ */ jsx6("text", { x: x(effect), y: y(evidence) - 8, textAnchor: "middle", fill: text, fontSize: "8", fontFamily: "var(--font-mono)", children: gene })
1604
+ ] }, gene)),
1605
+ /* @__PURE__ */ jsx6(AxisLeft, { scale: y, x: f.x0, count: 5, title: yLabel }),
1606
+ /* @__PURE__ */ jsx6(AxisBottom, { scale: x, y: f.y1, count: 7, title: xLabel })
1607
+ ] });
1608
+ }
1609
+ function SterlingManhattanChart({ hex, labels, chromosomeCenters, ariaLabel, xLabel, yLabel }) {
1610
+ const points = Array.from({ length: hex.length / 8 }, (_, index) => {
1611
+ const offset = index * 8;
1612
+ return [parseInt(hex.slice(offset, offset + 4), 16) / 65535, parseInt(hex.slice(offset + 4, offset + 6), 16) / 255 * 9, parseInt(hex.slice(offset + 6, offset + 8), 16)];
1613
+ });
1614
+ const f = frame(720, 380, { top: 26, right: 26, bottom: 56, left: 70 });
1615
+ const x = (v) => f.x0 + v * (f.x1 - f.x0);
1616
+ const y = linearScale([0, 9], [f.y1, f.y0], { zero: true });
1617
+ const threshold = -Math.log10(5e-8);
1618
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: `0 0 ${f.width} ${f.height}`, role: "img", "aria-label": ariaLabel, children: [
1619
+ /* @__PURE__ */ jsx6(Gridlines, { scale: y, x0: f.x0, x1: f.x1, count: 5 }),
1620
+ /* @__PURE__ */ jsx6("line", { x1: f.x0, x2: f.x1, y1: y(threshold), y2: y(threshold), stroke: sterlingChartColors[5], strokeDasharray: "5 5" }),
1621
+ points.map(([position, evidence, chromosome], index) => /* @__PURE__ */ jsx6("circle", { cx: x(position), cy: y(evidence), r: evidence >= threshold ? 3 : 1.5, fill: sterlingChartColors[chromosome % 2 === 0 ? 4 : 0], fillOpacity: evidence >= threshold ? 1 : sterlingVisualStyle.opacity.secondaryMark }, index)),
1622
+ labels.slice(0, 1).map(([name, chromosome, position, evidence]) => /* @__PURE__ */ jsxs7("g", { children: [
1623
+ /* @__PURE__ */ jsx6("circle", { cx: x(position), cy: y(evidence), r: "4", fill: sterlingChartColors[chromosome % 2 === 0 ? 4 : 0], stroke: surface }),
1624
+ /* @__PURE__ */ jsx6("text", { x: x(position) + 8, y: y(evidence) - 8, fill: text, fontSize: "8", fontFamily: "var(--font-mono)", children: `chr${chromosome} \xB7 ${name}` })
1625
+ ] }, name)),
1626
+ /* @__PURE__ */ jsx6(AxisLeft, { scale: y, x: f.x0, count: 5, title: yLabel }),
1627
+ /* @__PURE__ */ jsx6("line", { x1: f.x0, x2: f.x1, y1: f.y1, y2: f.y1, stroke: "var(--sterling-edge)" }),
1628
+ chromosomeCenters.map((position, index) => /* @__PURE__ */ jsx6("text", { x: x(position), y: f.y1 + 16, textAnchor: "middle", fill: muted, fontSize: "8", fontFamily: "var(--font-mono)", children: index + 1 }, index)),
1629
+ xLabel ? /* @__PURE__ */ jsx6("text", { x: (f.x0 + f.x1) / 2, y: f.y1 + 42, textAnchor: "middle", fill: muted, fontSize: "12", fontFamily: "var(--font-mono)", children: xLabel }) : null
1630
+ ] });
1631
+ }
1632
+ function correlation(left, right) {
1633
+ const lm = left.reduce((a, b) => a + b, 0) / left.length, rm = right.reduce((a, b) => a + b, 0) / right.length;
1634
+ let n = 0, ls = 0, rs = 0;
1635
+ left.forEach((value, index) => {
1636
+ const l = value - lm, r = right[index] - rm;
1637
+ n += l * r;
1638
+ ls += l * l;
1639
+ rs += r * r;
1640
+ });
1641
+ return n / Math.sqrt(ls * rs);
1642
+ }
1643
+ function clusterVectors(vectors, memberships) {
1644
+ const distances = vectors.map((left, li) => vectors.map((right, ri) => li === ri ? 0 : 1 - correlation(left, right)));
1645
+ let clusters = vectors.map((_, i) => ({ members: [i], left: null, right: null, height: 0, group: memberships[i] }));
1646
+ const meanMembership = (c) => c.members.reduce((s, m) => s + memberships[m], 0) / c.members.length;
1647
+ while (clusters.length > 1) {
1648
+ let bestLeft = 0, bestRight = 1, bestDistance = Number.POSITIVE_INFINITY;
1649
+ for (let i = 0; i < clusters.length; i += 1) {
1650
+ for (let j = i + 1; j < clusters.length; j += 1) {
1651
+ const L2 = clusters[i], R2 = clusters[j];
1652
+ const d = L2.members.reduce((sum, lm) => sum + R2.members.reduce((inner, rm) => inner + distances[lm][rm], 0), 0) / (L2.members.length * R2.members.length);
1653
+ if (d < bestDistance) {
1654
+ bestDistance = d;
1655
+ bestLeft = i;
1656
+ bestRight = j;
1657
+ }
1658
+ }
1659
+ }
1660
+ let L = clusters[bestLeft], R = clusters[bestRight];
1661
+ if (meanMembership(L) > meanMembership(R)) {
1662
+ [L, R] = [R, L];
1663
+ }
1664
+ const members = [...L.members, ...R.members];
1665
+ const groupSet = [...new Set(members.map((m) => memberships[m]))];
1666
+ const merged = { members, left: L, right: R, height: bestDistance, group: groupSet.length === 1 ? groupSet[0] : null };
1667
+ clusters = clusters.filter((_, index) => index !== bestLeft && index !== bestRight);
1668
+ clusters.push(merged);
1669
+ }
1670
+ return clusters[0];
1671
+ }
1672
+ function leafOrder(node) {
1673
+ return node.left ? [...leafOrder(node.left), ...leafOrder(node.right)] : node.members;
1674
+ }
1675
+ function SterlingExpressionChart({ hex, groups, types, ariaLabel }) {
1676
+ const columns = groups.length;
1677
+ const raw = Array.from({ length: hex.length / 2 }, (_, index) => parseInt(hex.slice(index * 2, index * 2 + 2), 16) / 255 * 5 - 2.5);
1678
+ const genes = raw.length / columns;
1679
+ const matrix = Array.from({ length: genes }, (_, row) => raw.slice(row * columns, (row + 1) * columns));
1680
+ const columnVectors = Array.from({ length: columns }, (_, column) => matrix.map((row) => row[column]));
1681
+ const tree = clusterVectors(columnVectors, groups);
1682
+ const order = leafOrder(tree);
1683
+ const cell = 9.25;
1684
+ const startX = 132;
1685
+ const startY = 86;
1686
+ const matrixSize = columns * cell;
1687
+ const columnCenter = (visible) => startX + visible * cell + cell / 2;
1688
+ const rowCenter = (visible) => startY + visible * cell + cell / 2;
1689
+ const branchColor = (node) => node.group === null ? muted : sterlingChartColors[node.group];
1690
+ const maxHeight = Math.max(tree.height, 1e-4);
1691
+ const visibleIndex = new Map(order.map((original, visible) => [original, visible]));
1692
+ const cells = order.flatMap(
1693
+ (row, visibleRow) => order.map((column, visibleColumn) => {
1694
+ const value = correlation(columnVectors[row], columnVectors[column]);
1695
+ return /* @__PURE__ */ jsx6("rect", { x: startX + visibleColumn * cell, y: startY + visibleRow * cell, width: cell + 0.1, height: cell + 0.1, fill: divergentSignedColor(value, 1), children: /* @__PURE__ */ jsx6("title", { children: `${types[groups[row]]} \xD7 ${types[groups[column]]}: r ${value.toFixed(2)}` }) }, `${visibleRow}-${visibleColumn}`);
1696
+ })
1697
+ );
1698
+ const topAnnotations = order.map((column, visible) => /* @__PURE__ */ jsx6("rect", { x: startX + visible * cell, y: "78", width: cell + 0.1, height: "5", fill: sterlingChartColors[groups[column]] }, `ta-${visible}`));
1699
+ const leftAnnotations = order.map((row, visible) => /* @__PURE__ */ jsx6("rect", { x: "124", y: startY + visible * cell, width: "5", height: cell + 0.1, fill: sterlingChartColors[groups[row]] }, `la-${visible}`));
1700
+ const runs = [];
1701
+ order.forEach((column, index) => {
1702
+ const group = groups[column];
1703
+ const previous = runs[runs.length - 1];
1704
+ if (previous && previous.group === group) previous.length += 1;
1705
+ else runs.push({ group, start: index, length: 1 });
1706
+ });
1707
+ const regionFrames = runs.map(({ group, start, length }, index) => /* @__PURE__ */ jsx6("rect", { x: startX + start * cell + 0.7, y: startY + start * cell + 0.7, width: length * cell - 1.4, height: length * cell - 1.4, fill: "none", stroke: sterlingChartColors[group], strokeWidth: sterlingVisualStyle.stroke.series }, `rf-${index}`));
1708
+ const topMarks = [];
1709
+ let topKey = 0;
1710
+ const renderTop = (node) => {
1711
+ if (!node.left) return { x: columnCenter(visibleIndex.get(node.members[0])), y: 75 };
1712
+ const l = renderTop(node.left);
1713
+ const r = renderTop(node.right);
1714
+ const y = 75 - node.height / maxHeight * 55;
1715
+ const x = (l.x + r.x) / 2;
1716
+ topMarks.push(/* @__PURE__ */ jsx6("path", { d: `M${l.x} ${l.y} V${y}`, fill: "none", stroke: branchColor(node.left), strokeWidth: sterlingVisualStyle.stroke.grid }, `tm-${topKey++}`));
1717
+ topMarks.push(/* @__PURE__ */ jsx6("path", { d: `M${r.x} ${r.y} V${y}`, fill: "none", stroke: branchColor(node.right), strokeWidth: sterlingVisualStyle.stroke.grid }, `tm-${topKey++}`));
1718
+ topMarks.push(/* @__PURE__ */ jsx6("path", { d: `M${l.x} ${y} H${r.x}`, fill: "none", stroke: branchColor(node), strokeWidth: sterlingVisualStyle.stroke.grid }, `tm-${topKey++}`));
1719
+ return { x, y };
1720
+ };
1721
+ renderTop(tree);
1722
+ const leftMarks = [];
1723
+ let leftKey = 0;
1724
+ const renderLeft = (node) => {
1725
+ if (!node.left) return { x: 121, y: rowCenter(visibleIndex.get(node.members[0])) };
1726
+ const l = renderLeft(node.left);
1727
+ const r = renderLeft(node.right);
1728
+ const x = 121 - node.height / maxHeight * 78;
1729
+ const y = (l.y + r.y) / 2;
1730
+ leftMarks.push(/* @__PURE__ */ jsx6("path", { d: `M${l.x} ${l.y} H${x}`, fill: "none", stroke: branchColor(node.left), strokeWidth: sterlingVisualStyle.stroke.grid }, `lm-${leftKey++}`));
1731
+ leftMarks.push(/* @__PURE__ */ jsx6("path", { d: `M${r.x} ${r.y} H${x}`, fill: "none", stroke: branchColor(node.right), strokeWidth: sterlingVisualStyle.stroke.grid }, `lm-${leftKey++}`));
1732
+ leftMarks.push(/* @__PURE__ */ jsx6("path", { d: `M${x} ${l.y} V${r.y}`, fill: "none", stroke: branchColor(node), strokeWidth: sterlingVisualStyle.stroke.grid }, `lm-${leftKey++}`));
1733
+ return { x, y };
1734
+ };
1735
+ renderLeft(tree);
1736
+ const rowLabels = order.map((row, visible) => /* @__PURE__ */ jsx6("text", { x: startX + matrixSize + 7, y: startY + visible * cell + 6.3, fill: muted, fontSize: "6", fontFamily: "var(--font-mono)", children: `${types[groups[row]]} ${row % 4 + 1}` }, `rl-${visible}`));
1737
+ const legendStops = Array.from({ length: 21 }, (_, index) => {
1738
+ const value = -1 + index / 20 * 2;
1739
+ return /* @__PURE__ */ jsx6("rect", { x: 132 + index * 5, y: "394", width: "5.2", height: "8", fill: divergentSignedColor(value, 1) }, `ls-${index}`);
1740
+ });
1741
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: "0 0 600 416", role: "img", "aria-label": ariaLabel, children: [
1742
+ topMarks,
1743
+ leftMarks,
1744
+ topAnnotations,
1745
+ leftAnnotations,
1746
+ cells,
1747
+ regionFrames,
1748
+ rowLabels,
1749
+ legendStops,
1750
+ /* @__PURE__ */ jsx6("text", { x: "126", y: "401", textAnchor: "end", fill: muted, fontSize: "6.4", fontFamily: "var(--font-mono)", children: "-1" }),
1751
+ /* @__PURE__ */ jsx6("text", { x: "242", y: "401", fill: muted, fontSize: "6.4", fontFamily: "var(--font-mono)", children: "+1 r" })
1752
+ ] });
1753
+ }
1754
+ function SterlingGeoMapChart({ atlas, rows, ariaLabel }) {
1755
+ const topology = atlas;
1756
+ const collection = feature(atlas, topology.objects.states);
1757
+ const projection = geoAlbersUsa().fitSize([620, 286], collection);
1758
+ const path = geoPath(projection);
1759
+ const valueById = new Map(rows.map((row) => [row.id, row]));
1760
+ const maximum = Math.max(...rows.map((row) => row.engineers));
1761
+ return /* @__PURE__ */ jsxs7("svg", { className: "sterling-chart", viewBox: "0 0 720 390", role: "img", "aria-label": ariaLabel, children: [
1762
+ /* @__PURE__ */ jsx6("g", { transform: "translate(50 18)", children: collection.features.map((state, index) => {
1763
+ const id = Number(state.id), row = valueById.get(id), value = row?.engineers ?? 0, colorIndex = Math.min(9, Math.max(0, Math.round(value / maximum * 9)));
1764
+ return /* @__PURE__ */ jsx6("path", { d: path(state) ?? void 0, fill: sterlingSequentialColors[colorIndex], stroke: surface, strokeWidth: sterlingVisualStyle.stroke.grid, children: /* @__PURE__ */ jsx6("title", { children: `${row?.state ?? `State ${id}`}: ${(value * 100).toFixed(2)}%` }) }, id || index);
1765
+ }) }),
1766
+ /* @__PURE__ */ jsx6("text", { x: "132", y: "342", textAnchor: "end", fill: muted, fontSize: "9", fontFamily: "var(--font-mono)", children: "0%" }),
1767
+ sterlingSequentialColors.map((color, index) => /* @__PURE__ */ jsx6("rect", { x: 142 + index * 40, y: "330", width: "40", height: "14", fill: color }, color)),
1768
+ /* @__PURE__ */ jsxs7("text", { x: "552", y: "342", fill: muted, fontSize: "9", fontFamily: "var(--font-mono)", children: [
1769
+ (maximum * 100).toFixed(2),
1770
+ "%"
1771
+ ] }),
1772
+ /* @__PURE__ */ jsx6("text", { x: "360", y: "370", textAnchor: "middle", fill: muted, fontSize: "10", fontFamily: "var(--font-mono)", children: "engineers as share of population / proporci\xF3n de ingenieros" })
1773
+ ] });
1774
+ }
1775
+
1776
+ // src/tailwind.ts
1777
+ var orderedStops = [100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
1778
+ var rampStops = [200, 300, 400, 500, 600, 700, 800];
1779
+ function at(scale, stop, fallback) {
1780
+ return scale[stop] ?? fallback;
1781
+ }
1782
+ function createTailwindSterlingPalette({
1783
+ categorical,
1784
+ surface: surface2,
1785
+ divergent,
1786
+ heat
1787
+ }) {
1788
+ const first = categorical[0] ?? {};
1789
+ const categoricalValues = categorical.map((scale) => at(scale, 500, "currentColor"));
1790
+ return defineSterlingPalette({
1791
+ surface: surface2,
1792
+ categorical: categoricalValues,
1793
+ legend: categorical.map((scale) => at(scale, 700, at(scale, 500, "currentColor"))),
1794
+ sequential: orderedStops.map((stop) => at(first, stop, at(first, 500, "currentColor"))),
1795
+ divergent: orderedStops.map((stop) => at(divergent ?? first, stop, at(first, 500, "currentColor"))),
1796
+ heat: orderedStops.concat(950).map((stop) => at(heat ?? first, stop, at(first, 500, "currentColor"))).slice(0, 11),
1797
+ ramps: categorical.map((scale) => rampStops.map((stop) => at(scale, stop, at(scale, 500, "currentColor"))))
1798
+ });
1799
+ }
1800
+
1801
+ // src/mdx.tsx
1802
+ import { jsx as jsx7 } from "react/jsx-runtime";
1803
+ function createSterlingMdxComponents({
1804
+ locale = "en",
1805
+ figureDefaults,
1806
+ additionalComponents
1807
+ } = {}) {
1808
+ return {
1809
+ SterlingFigure: (props) => /* @__PURE__ */ jsx7(SterlingFigure, { ...figureDefaults, ...props, locale }),
1810
+ SterlingInlineLegend,
1811
+ SterlingBarChart,
1812
+ SterlingVerticalBarChart,
1813
+ SterlingCandlestickChart,
1814
+ SterlingScatterPlot,
1815
+ SterlingLineChart,
1816
+ SterlingHeatmap,
1817
+ SterlingHistogram,
1818
+ SterlingBoxPlot,
1819
+ SterlingChordChart,
1820
+ SterlingDumbbellChart,
1821
+ SterlingDensityChart,
1822
+ SterlingDensity2DChart,
1823
+ SterlingDendrogramChart,
1824
+ SterlingViolinPlot,
1825
+ SterlingDonutChart,
1826
+ SterlingLollipopChart,
1827
+ SterlingPieChart,
1828
+ SterlingSankeyChart,
1829
+ SterlingRadarChart,
1830
+ SterlingRidgelineChart,
1831
+ SterlingTreemapChart,
1832
+ SterlingNetworkChart,
1833
+ SterlingVolcanoChart,
1834
+ SterlingManhattanChart,
1835
+ SterlingExpressionChart,
1836
+ SterlingGeoMapChart,
1837
+ SterlingSequentialSurface,
1838
+ SterlingCorrelogram,
1839
+ ...additionalComponents
1840
+ };
1841
+ }
1842
+ export {
1843
+ AXIS_TITLE,
1844
+ AxisBottom,
1845
+ AxisLeft,
1846
+ Gridlines,
1847
+ SterlingBarChart,
1848
+ SterlingBoxPlot,
1849
+ SterlingCandlestickChart,
1850
+ SterlingChordChart,
1851
+ SterlingCorrelogram,
1852
+ SterlingDendrogramChart,
1853
+ SterlingDensity2DChart,
1854
+ SterlingDensityChart,
1855
+ SterlingDonutChart,
1856
+ SterlingDumbbellChart,
1857
+ SterlingExpressionChart,
1858
+ SterlingFigure,
1859
+ SterlingFigureActions,
1860
+ SterlingGeoMapChart,
1861
+ SterlingHeatmap,
1862
+ SterlingHistogram,
1863
+ SterlingInlineLegend,
1864
+ SterlingLineChart,
1865
+ SterlingLollipopChart,
1866
+ SterlingManhattanChart,
1867
+ SterlingNetworkChart,
1868
+ SterlingPieChart,
1869
+ SterlingRadarChart,
1870
+ SterlingRidgelineChart,
1871
+ SterlingSankeyChart,
1872
+ SterlingScatterPlot,
1873
+ SterlingSequentialSurface,
1874
+ SterlingTreemapChart,
1875
+ SterlingVerticalBarChart,
1876
+ SterlingViolinPlot,
1877
+ SterlingVolcanoChart,
1878
+ TICK,
1879
+ TICK_LABEL,
1880
+ TimeAxisBottom,
1881
+ axisGrid,
1882
+ axisLine,
1883
+ axisMuted,
1884
+ axisText,
1885
+ bandCenters,
1886
+ createSterlingMdxComponents,
1887
+ createTailwindSterlingPalette,
1888
+ defaultMargin,
1889
+ defineSterlingPalette,
1890
+ divergentSignedColor,
1891
+ divergentSignedRamp,
1892
+ frame,
1893
+ inferSterlingDataExport,
1894
+ kernelDensity,
1895
+ linearScale,
1896
+ sterlingCategorical,
1897
+ sterlingChartColors,
1898
+ sterlingColorNames,
1899
+ sterlingCredit,
1900
+ sterlingDivergentColors,
1901
+ sterlingHeatColors,
1902
+ sterlingLegendColors,
1903
+ sterlingPaletteStyle,
1904
+ sterlingRamp,
1905
+ sterlingRampStops,
1906
+ sterlingRowsToCsv,
1907
+ sterlingSequentialColors,
1908
+ sterlingVisualStyle,
1909
+ timeScale,
1910
+ tukeySummary
1911
+ };
1912
+ //# sourceMappingURL=index.mjs.map