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