@silverpoint/core 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-I6BAZ7MJ.js +622 -0
- package/dist/chunk-I6BAZ7MJ.js.map +1 -0
- package/dist/dashboard-demos.d.ts +104 -0
- package/dist/dashboard-demos.js +94 -0
- package/dist/dashboard-demos.js.map +1 -0
- package/dist/index.d.ts +94 -485
- package/dist/index.js +258 -613
- package/dist/index.js.map +1 -1
- package/dist/props-3G1XYd-r.d.ts +605 -0
- package/dist/types-CbVMKvyQ.d.ts +148 -0
- package/dist/ui-demos.d.ts +17 -0
- package/dist/ui-demos.js +129 -0
- package/dist/ui-demos.js.map +1 -0
- package/dist/ui.d.ts +545 -0
- package/dist/ui.js +715 -0
- package/dist/ui.js.map +1 -0
- package/package.json +19 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { diagnose, circlePath, round2, DEFAULTS, readSeries, checkVolume, accessorName, read, formatCategory, formatValue, cartesianPlot, extent, seriesValues, valueAxis, categoryAxis, categoryLabels, formatNumber, finite, inset, rectPath, warnValue, toneOf, toneLevel, checkCount, linearScale, legend, AXIS_BAND, PLOT_INSET, horizontalValueAxis, pillPath, checkSectors, readSectors, polarFrame, ringTones, sectorPath, pointAt, sectorLegend, RIM_LABELS, rimLabels, arcPath, ringTone, polarLabel, TICK_CHAR, roundGeometry } from './chunk-I6BAZ7MJ.js';
|
|
2
|
+
export { DEFAULTS, FORCED_PRECISION_QUERY, SilverpointError, __setDiagnosticSink, bandScale, circlePath, deriveSeed, diagnose, expandDomain, extent, fnv1a32, instanceId, linearScale, max, min, resolveConfig, resolveSeed, round2, roundGeometry, roundPathData } from './chunk-I6BAZ7MJ.js';
|
|
2
3
|
import { curveStepAfter, curveNatural, curveLinear, curveMonotoneX, line, curveStep, curveStepBefore, area } from 'd3-shape';
|
|
3
4
|
import { chordDirected } from 'd3-chord';
|
|
4
5
|
|
|
5
|
-
// src/render/round.ts
|
|
6
|
-
function round2(value) {
|
|
7
|
-
const rounded = Math.round(value * 100) / 100;
|
|
8
|
-
return Object.is(rounded, -0) ? 0 : rounded;
|
|
9
|
-
}
|
|
10
|
-
var NUMBER = /-?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?/g;
|
|
11
|
-
function roundPathData(d) {
|
|
12
|
-
return d.replace(NUMBER, (token) => String(round2(Number(token))));
|
|
13
|
-
}
|
|
14
|
-
function roundRect(rect) {
|
|
15
|
-
return {
|
|
16
|
-
x: round2(rect.x),
|
|
17
|
-
y: round2(rect.y),
|
|
18
|
-
width: round2(rect.width),
|
|
19
|
-
height: round2(rect.height)
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
function roundStroke(stroke) {
|
|
23
|
-
return { ...stroke, d: roundPathData(stroke.d) };
|
|
24
|
-
}
|
|
25
|
-
function roundTile(tile) {
|
|
26
|
-
return {
|
|
27
|
-
...tile,
|
|
28
|
-
width: round2(tile.width),
|
|
29
|
-
height: round2(tile.height),
|
|
30
|
-
angle: round2(tile.angle),
|
|
31
|
-
strokes: tile.strokes.map(roundStroke)
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
function roundGeometry(geometry) {
|
|
35
|
-
return {
|
|
36
|
-
viewBox: roundRect(geometry.viewBox),
|
|
37
|
-
plot: roundRect(geometry.plot),
|
|
38
|
-
strokes: geometry.strokes.map(roundStroke),
|
|
39
|
-
labels: geometry.labels.map((label) => ({ ...label, x: round2(label.x), y: round2(label.y) })),
|
|
40
|
-
hitAreas: geometry.hitAreas.map((hit) => ({
|
|
41
|
-
...hit,
|
|
42
|
-
x: round2(hit.x),
|
|
43
|
-
y: round2(hit.y),
|
|
44
|
-
...hit.box ? { box: roundRect(hit.box) } : {}
|
|
45
|
-
})),
|
|
46
|
-
defs: geometry.defs.map(roundTile)
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
6
|
// src/render/serialize.ts
|
|
51
7
|
function serializeGeometry(geometry) {
|
|
52
8
|
return JSON.stringify(geometry);
|
|
@@ -64,47 +20,6 @@ function pathBytes(geometry) {
|
|
|
64
20
|
}
|
|
65
21
|
var PATH_BYTE_BUDGET = 4e4;
|
|
66
22
|
|
|
67
|
-
// src/render/seed.ts
|
|
68
|
-
var FNV_OFFSET_BASIS = 2166136261;
|
|
69
|
-
var FNV_PRIME = 16777619;
|
|
70
|
-
function utf8Bytes(input) {
|
|
71
|
-
const bytes = [];
|
|
72
|
-
for (const char of input) {
|
|
73
|
-
const code = char.codePointAt(0) ?? 0;
|
|
74
|
-
if (code < 128) {
|
|
75
|
-
bytes.push(code);
|
|
76
|
-
} else if (code < 2048) {
|
|
77
|
-
bytes.push(192 | code >> 6, 128 | code & 63);
|
|
78
|
-
} else if (code < 65536) {
|
|
79
|
-
bytes.push(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
|
|
80
|
-
} else {
|
|
81
|
-
bytes.push(
|
|
82
|
-
240 | code >> 18,
|
|
83
|
-
128 | code >> 12 & 63,
|
|
84
|
-
128 | code >> 6 & 63,
|
|
85
|
-
128 | code & 63
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return bytes;
|
|
90
|
-
}
|
|
91
|
-
function fnv1a32(input) {
|
|
92
|
-
let hash = FNV_OFFSET_BASIS;
|
|
93
|
-
for (const byte of utf8Bytes(input)) {
|
|
94
|
-
hash ^= byte;
|
|
95
|
-
hash = Math.imul(hash, FNV_PRIME);
|
|
96
|
-
}
|
|
97
|
-
return hash >>> 0;
|
|
98
|
-
}
|
|
99
|
-
function deriveSeed(id) {
|
|
100
|
-
return fnv1a32(id);
|
|
101
|
-
}
|
|
102
|
-
function resolveSeed(seed, id) {
|
|
103
|
-
if (seed === void 0) return deriveSeed(id);
|
|
104
|
-
if (typeof seed === "string") return fnv1a32(seed);
|
|
105
|
-
return Number.isFinite(seed) ? Math.trunc(seed) >>> 0 : 0;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
23
|
// src/render/prng.ts
|
|
109
24
|
function createPrng(seed) {
|
|
110
25
|
let state = seed >>> 0;
|
|
@@ -135,122 +50,6 @@ function weekday(day) {
|
|
|
135
50
|
return ((day + 4) % 7 + 7) % 7;
|
|
136
51
|
}
|
|
137
52
|
|
|
138
|
-
// src/diagnostics/diagnose.ts
|
|
139
|
-
var SilverpointError = class extends Error {
|
|
140
|
-
code;
|
|
141
|
-
constructor(code, message) {
|
|
142
|
-
super(message);
|
|
143
|
-
this.name = "SilverpointError";
|
|
144
|
-
this.code = code;
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
var consoleSink = (_code, message) => {
|
|
148
|
-
console.warn(message);
|
|
149
|
-
};
|
|
150
|
-
var sink = consoleSink;
|
|
151
|
-
var reported = /* @__PURE__ */ new Set();
|
|
152
|
-
function __setDiagnosticSink(next) {
|
|
153
|
-
sink = next ?? consoleSink;
|
|
154
|
-
reported.clear();
|
|
155
|
-
return () => {
|
|
156
|
-
sink = consoleSink;
|
|
157
|
-
reported.clear();
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
function format(code, chart, detail, template) {
|
|
161
|
-
const specifics = detail.message ? ` ${detail.message}` : "";
|
|
162
|
-
return `[${code}] ${chart}: ${template.what} (\`${detail.property}\`).${specifics} ${template.todo}. (${template.req})`;
|
|
163
|
-
}
|
|
164
|
-
var ERRORS = {
|
|
165
|
-
SP009: {
|
|
166
|
-
req: "REQ-097",
|
|
167
|
-
what: "a scale bound cannot be derived from the data and was not supplied",
|
|
168
|
-
todo: "Pass the named property explicitly"
|
|
169
|
-
},
|
|
170
|
-
SP012: {
|
|
171
|
-
req: "REQ-127",
|
|
172
|
-
what: "the ground does not reach the minimum contrast",
|
|
173
|
-
todo: "Recompute the palette; colours are not chosen by eye"
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
function diagnose(code, chart, detail) {
|
|
177
|
-
const error = ERRORS[code];
|
|
178
|
-
if (error) {
|
|
179
|
-
throw new SilverpointError(code, format(code, chart, detail, error));
|
|
180
|
-
}
|
|
181
|
-
if (process.env.NODE_ENV !== "production") {
|
|
182
|
-
developmentDiagnose(code, chart, detail);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
function developmentDiagnose(code, chart, detail) {
|
|
186
|
-
const template = WARNINGS[code];
|
|
187
|
-
if (!template) return;
|
|
188
|
-
const message = format(code, chart, detail, template);
|
|
189
|
-
if (code === "SP005") {
|
|
190
|
-
throw new SilverpointError(code, message);
|
|
191
|
-
}
|
|
192
|
-
if (reported.has(message)) return;
|
|
193
|
-
reported.add(message);
|
|
194
|
-
sink(code, message);
|
|
195
|
-
}
|
|
196
|
-
var WARNINGS = {
|
|
197
|
-
SP001: {
|
|
198
|
-
req: "REQ-007",
|
|
199
|
-
what: "the dataset is empty; the ground's empty state is drawn",
|
|
200
|
-
todo: "Pass at least one row, or omit `data` to see the demo dataset"
|
|
201
|
-
},
|
|
202
|
-
SP002: {
|
|
203
|
-
req: "REQ-008",
|
|
204
|
-
what: "a value cannot be drawn as given (null, not finite, or outside the data contract); it is corrected or omitted",
|
|
205
|
-
todo: "Check the value against the chart's data contract (Data Model \xA72)"
|
|
206
|
-
},
|
|
207
|
-
SP003: {
|
|
208
|
-
req: "REQ-009",
|
|
209
|
-
what: "the container measures 0 px; the render is deferred",
|
|
210
|
-
todo: "Give the container a size, or pin `width` and `height`"
|
|
211
|
-
},
|
|
212
|
-
SP004: {
|
|
213
|
-
req: "REQ-010",
|
|
214
|
-
what: "the scale domain has zero length; it is expanded with the ground's domainPadding",
|
|
215
|
-
todo: "Nothing to do if the data is constant; otherwise check the accessor"
|
|
216
|
-
},
|
|
217
|
-
SP005: {
|
|
218
|
-
req: "REQ-025",
|
|
219
|
-
what: "more than one element is marked with white heightening; only the first is applied",
|
|
220
|
-
todo: "Heighten a single element per chart"
|
|
221
|
-
},
|
|
222
|
-
SP006: {
|
|
223
|
-
req: "REQ-026",
|
|
224
|
-
what: "the inker is not registered; falling back to NullInker",
|
|
225
|
-
todo: "Register the inker or fix the ground's `inker` token"
|
|
226
|
-
},
|
|
227
|
-
SP007: {
|
|
228
|
-
req: "REQ-045",
|
|
229
|
-
what: "the ground is not registered; falling back to silverpoint",
|
|
230
|
-
todo: "Call registerGround() before rendering, or fix the name"
|
|
231
|
-
},
|
|
232
|
-
SP008: {
|
|
233
|
-
req: "REQ-096",
|
|
234
|
-
what: "the data volume is above the family's threshold",
|
|
235
|
-
todo: "Aggregate the data before charting it"
|
|
236
|
-
},
|
|
237
|
-
SP010: {
|
|
238
|
-
req: "REQ-091",
|
|
239
|
-
what: "the chord ring has more than 12 categories",
|
|
240
|
-
todo: "Group the smaller categories"
|
|
241
|
-
},
|
|
242
|
-
SP011: {
|
|
243
|
-
req: "NFR \xA77",
|
|
244
|
-
what: "the path byte budget of 40 KB is exceeded",
|
|
245
|
-
todo: "Use `hatchFill: 'tile'` or a larger hatch gap"
|
|
246
|
-
},
|
|
247
|
-
SP013: {
|
|
248
|
-
req: "REQ-032",
|
|
249
|
-
what: "the @silverpoint/fonts face failed to load; rendering fell back to the system stack",
|
|
250
|
-
todo: "Install @silverpoint/fonts and import its stylesheet; golden images will not match until then"
|
|
251
|
-
}
|
|
252
|
-
};
|
|
253
|
-
|
|
254
53
|
// src/render/heighten.ts
|
|
255
54
|
function isOutlineOf(candidate, heightened) {
|
|
256
55
|
return candidate.part === "ink" && (candidate.paint ?? "stroke") === "stroke" && candidate.d === heightened.d;
|
|
@@ -285,6 +84,7 @@ function pathView(stroke) {
|
|
|
285
84
|
role: stroke.role,
|
|
286
85
|
paint,
|
|
287
86
|
dash: stroke.dash ?? null,
|
|
87
|
+
weight: stroke.tonalWeight === void 0 ? null : String(stroke.tonalWeight),
|
|
288
88
|
fill: paint === "tile" && stroke.tile ? `url(#${stroke.tile})` : null
|
|
289
89
|
};
|
|
290
90
|
}
|
|
@@ -336,6 +136,7 @@ function pathString(path) {
|
|
|
336
136
|
["data-role", path.role],
|
|
337
137
|
["data-paint", path.paint],
|
|
338
138
|
["data-dash", path.dash],
|
|
139
|
+
["data-weight", path.weight],
|
|
339
140
|
["fill", path.fill]
|
|
340
141
|
])}></path>`;
|
|
341
142
|
}
|
|
@@ -392,66 +193,6 @@ async function checkTypeface(fonts, chart, face = DISPLAY_FACE_PROBE) {
|
|
|
392
193
|
return loaded;
|
|
393
194
|
}
|
|
394
195
|
|
|
395
|
-
// src/scales/util.ts
|
|
396
|
-
function min(values) {
|
|
397
|
-
let result;
|
|
398
|
-
for (const value of values) {
|
|
399
|
-
if (Number.isFinite(value) && (result === void 0 || value < result)) result = value;
|
|
400
|
-
}
|
|
401
|
-
return result;
|
|
402
|
-
}
|
|
403
|
-
function max(values) {
|
|
404
|
-
let result;
|
|
405
|
-
for (const value of values) {
|
|
406
|
-
if (Number.isFinite(value) && (result === void 0 || value > result)) result = value;
|
|
407
|
-
}
|
|
408
|
-
return result;
|
|
409
|
-
}
|
|
410
|
-
function extent(values) {
|
|
411
|
-
const low = min(values);
|
|
412
|
-
const high = max(values);
|
|
413
|
-
return low === void 0 || high === void 0 ? void 0 : [low, high];
|
|
414
|
-
}
|
|
415
|
-
function expandDomain(domain, padding, site, fromZero = false) {
|
|
416
|
-
const [low, high] = domain;
|
|
417
|
-
if (low !== high) return [low, high];
|
|
418
|
-
const spread = low === 0 ? padding : Math.abs(low) * padding;
|
|
419
|
-
if (fromZero && low === 0) {
|
|
420
|
-
if (process.env.NODE_ENV !== "production") {
|
|
421
|
-
diagnose("SP004", site.chart, { property: site.property, message: `Domain [0, 0] became [0, ${2 * spread}].` });
|
|
422
|
-
}
|
|
423
|
-
return [0, 2 * spread];
|
|
424
|
-
}
|
|
425
|
-
if (process.env.NODE_ENV !== "production") {
|
|
426
|
-
diagnose("SP004", site.chart, {
|
|
427
|
-
property: site.property,
|
|
428
|
-
message: `Domain [${low}, ${high}] became [${low - spread}, ${high + spread}].`
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
return [low - spread, high + spread];
|
|
432
|
-
}
|
|
433
|
-
function linearScale(domain, range, options) {
|
|
434
|
-
const expanded = expandDomain(domain, options.padding, options, options.fromZero);
|
|
435
|
-
const scale = scaleLinear().domain(expanded).range(range);
|
|
436
|
-
if (options.nice) scale.nice(4);
|
|
437
|
-
const [low, high] = scale.domain();
|
|
438
|
-
const result = ((value) => scale(value));
|
|
439
|
-
Object.defineProperty(result, "domain", { value: [low, high] });
|
|
440
|
-
result.ticks = (count) => scale.ticks(count);
|
|
441
|
-
return result;
|
|
442
|
-
}
|
|
443
|
-
function bandScale(domain, range, options = {}) {
|
|
444
|
-
const scale = scaleBand().domain(domain).range(range).paddingInner(options.paddingInner ?? 0).paddingOuter(options.paddingOuter ?? 0);
|
|
445
|
-
const bandwidth = scale.bandwidth();
|
|
446
|
-
const result = ((value) => scale(value));
|
|
447
|
-
Object.defineProperty(result, "bandwidth", { value: bandwidth });
|
|
448
|
-
result.center = (value) => {
|
|
449
|
-
const start = scale(value);
|
|
450
|
-
return start === void 0 ? void 0 : start + bandwidth / 2;
|
|
451
|
-
};
|
|
452
|
-
return result;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
196
|
// src/ink/null-inker.ts
|
|
456
197
|
var NullInker = Object.freeze({
|
|
457
198
|
name: "null",
|
|
@@ -474,72 +215,6 @@ function resolveInker(name, chart, builtins = []) {
|
|
|
474
215
|
return NullInker;
|
|
475
216
|
}
|
|
476
217
|
|
|
477
|
-
// src/config/resolve.ts
|
|
478
|
-
var DEFAULTS = Object.freeze({
|
|
479
|
-
ground: "silverpoint",
|
|
480
|
-
substrate: "cream",
|
|
481
|
-
mode: "ink",
|
|
482
|
-
chrome: "card",
|
|
483
|
-
hatchFill: "tile",
|
|
484
|
-
height: 160,
|
|
485
|
-
dataTable: "hidden",
|
|
486
|
-
locale: "en"
|
|
487
|
-
});
|
|
488
|
-
function resolveConfig(props, provider = {}, environment = {}) {
|
|
489
|
-
const mode = environment.forcedPrecision ? "precision" : props.mode ?? provider.mode ?? DEFAULTS.mode;
|
|
490
|
-
return {
|
|
491
|
-
ground: props.ground ?? provider.ground ?? DEFAULTS.ground,
|
|
492
|
-
substrate: props.substrate ?? provider.substrate ?? DEFAULTS.substrate,
|
|
493
|
-
mode,
|
|
494
|
-
chrome: props.chrome ?? DEFAULTS.chrome,
|
|
495
|
-
hatchFill: props.hatchFill ?? DEFAULTS.hatchFill,
|
|
496
|
-
height: props.height ?? DEFAULTS.height,
|
|
497
|
-
dataTable: props.dataTable ?? DEFAULTS.dataTable,
|
|
498
|
-
locale: props.locale ?? provider.locale ?? environment.locale ?? DEFAULTS.locale
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
var FORCED_PRECISION_QUERY = "(prefers-contrast: more), (forced-colors: active)";
|
|
502
|
-
function instanceId(chart, token) {
|
|
503
|
-
const slug = chart.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
504
|
-
const safe = String(token).replace(/[^A-Za-z0-9_-]/g, "");
|
|
505
|
-
return `sp-${slug}-${safe}`;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// src/charts/shared/format.ts
|
|
509
|
-
function read(accessor, datum, index) {
|
|
510
|
-
return typeof accessor === "function" ? accessor(datum, index) : datum[accessor];
|
|
511
|
-
}
|
|
512
|
-
function accessorName(accessor, fallback) {
|
|
513
|
-
return typeof accessor === "string" ? accessor : fallback;
|
|
514
|
-
}
|
|
515
|
-
var FORMATTERS = /* @__PURE__ */ new Map();
|
|
516
|
-
var MAX_FORMATTERS = 64;
|
|
517
|
-
function formatNumber(value, locale, options) {
|
|
518
|
-
const resolved = options ?? { maximumFractionDigits: 2 };
|
|
519
|
-
const read2 = [];
|
|
520
|
-
for (const name in resolved) read2.push(`${name}=${JSON.stringify(resolved[name])}`);
|
|
521
|
-
const key = `${locale}|${read2.sort().join("&")}`;
|
|
522
|
-
let formatter = FORMATTERS.get(key);
|
|
523
|
-
if (!formatter) {
|
|
524
|
-
if (FORMATTERS.size >= MAX_FORMATTERS) FORMATTERS.clear();
|
|
525
|
-
formatter = new Intl.NumberFormat(locale, resolved);
|
|
526
|
-
FORMATTERS.set(key, formatter);
|
|
527
|
-
}
|
|
528
|
-
return formatter.format(value);
|
|
529
|
-
}
|
|
530
|
-
function formatValue(value, locale, options) {
|
|
531
|
-
if (typeof value === "number") {
|
|
532
|
-
return Number.isFinite(value) ? formatNumber(value, locale, options) : "\u2014";
|
|
533
|
-
}
|
|
534
|
-
return value === null || value === void 0 ? "\u2014" : String(value);
|
|
535
|
-
}
|
|
536
|
-
function formatCategory(value, locale) {
|
|
537
|
-
return formatValue(value, locale, typeof value === "number" ? { useGrouping: false, maximumFractionDigits: 2 } : void 0);
|
|
538
|
-
}
|
|
539
|
-
function circlePath(cx, cy, r) {
|
|
540
|
-
return `M${cx - r},${cy}A${r},${r},0,1,0,${cx + r},${cy}A${r},${r},0,1,0,${cx - r},${cy}Z`;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
218
|
// src/charts/shared/card.ts
|
|
544
219
|
var CARD_PADDING = 16;
|
|
545
220
|
var TITLE_ROW = 20;
|
|
@@ -607,162 +282,6 @@ function cardLayout(props, options) {
|
|
|
607
282
|
return { viewBox: { x: 0, y: 0, width, height }, area: area5, strokes, labels };
|
|
608
283
|
}
|
|
609
284
|
|
|
610
|
-
// src/charts/shared/cells.ts
|
|
611
|
-
function rectPath(rect) {
|
|
612
|
-
const { x, y, width, height } = rect;
|
|
613
|
-
return `M${x},${y}H${x + width}V${y + height}H${x}Z`;
|
|
614
|
-
}
|
|
615
|
-
function inset(rect, by) {
|
|
616
|
-
return {
|
|
617
|
-
x: rect.x + by,
|
|
618
|
-
y: rect.y + by,
|
|
619
|
-
width: Math.max(rect.width - 2 * by, 0),
|
|
620
|
-
height: Math.max(rect.height - 2 * by, 0)
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
function finite(raw) {
|
|
624
|
-
return typeof raw === "number" && Number.isFinite(raw) ? raw : void 0;
|
|
625
|
-
}
|
|
626
|
-
function toneOf(ratio) {
|
|
627
|
-
if (!(ratio > 0)) return 0;
|
|
628
|
-
if (ratio <= 0.25) return 1;
|
|
629
|
-
if (ratio <= 0.5) return 2;
|
|
630
|
-
if (ratio <= 0.75) return 3;
|
|
631
|
-
return 4;
|
|
632
|
-
}
|
|
633
|
-
function toneLevel(raw) {
|
|
634
|
-
return Math.min(Math.max(Math.round(raw), 0), 4);
|
|
635
|
-
}
|
|
636
|
-
function warnValue(chart, property, message) {
|
|
637
|
-
if (process.env.NODE_ENV !== "production") diagnose("SP002", chart, { property, message });
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// src/charts/shared/cartesian.ts
|
|
641
|
-
var AXIS_BAND = 20;
|
|
642
|
-
var PLOT_INSET = 6;
|
|
643
|
-
var LABEL_SPACING = 48;
|
|
644
|
-
function cartesianPlot(area5) {
|
|
645
|
-
return {
|
|
646
|
-
x: area5.x + PLOT_INSET,
|
|
647
|
-
y: area5.y + PLOT_INSET,
|
|
648
|
-
width: Math.max(area5.width - 2 * PLOT_INSET, 1),
|
|
649
|
-
height: Math.max(area5.height - AXIS_BAND - PLOT_INSET, 1)
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
function readSeries(data, accessor, fallbackName) {
|
|
653
|
-
let missing = 0;
|
|
654
|
-
const points = data.map((datum, index) => {
|
|
655
|
-
const raw = read(accessor, datum, index);
|
|
656
|
-
const value = typeof raw === "number" && Number.isFinite(raw) ? raw : void 0;
|
|
657
|
-
if (value === void 0) missing += 1;
|
|
658
|
-
return { index, datum, value };
|
|
659
|
-
});
|
|
660
|
-
return { key: accessorName(accessor, fallbackName), points, missing };
|
|
661
|
-
}
|
|
662
|
-
var POINTS_PER_SERIES = 500;
|
|
663
|
-
function checkVolume(chart, series) {
|
|
664
|
-
for (const s of series) checkCount(chart, s.key, s.points.length);
|
|
665
|
-
}
|
|
666
|
-
function checkCount(chart, property, count) {
|
|
667
|
-
if (process.env.NODE_ENV === "production" || count <= POINTS_PER_SERIES) return;
|
|
668
|
-
diagnose("SP008", chart, { property, message: `${count} points; the ceiling is ${POINTS_PER_SERIES} per series.` });
|
|
669
|
-
}
|
|
670
|
-
function seriesValues(series) {
|
|
671
|
-
return series.flatMap((s) => s.points.flatMap((p) => p.value === void 0 ? [] : [p.value]));
|
|
672
|
-
}
|
|
673
|
-
function valueAxis(plot, domain, options) {
|
|
674
|
-
const bottom = plot.y + plot.height;
|
|
675
|
-
const low = options.zero === false ? domain[0] : Math.min(0, domain[0]);
|
|
676
|
-
const high = options.zero === false ? domain[1] : Math.max(0, domain[1]);
|
|
677
|
-
const scale = linearScale([low, high], [bottom, plot.y], {
|
|
678
|
-
chart: options.chart,
|
|
679
|
-
property: options.property,
|
|
680
|
-
padding: options.padding,
|
|
681
|
-
nice: options.nice !== false,
|
|
682
|
-
fromZero: options.zero !== false
|
|
683
|
-
});
|
|
684
|
-
const strokes = [];
|
|
685
|
-
const labels = [];
|
|
686
|
-
for (const tick of scale.ticks(4)) {
|
|
687
|
-
const ty = scale(tick);
|
|
688
|
-
if (tick === scale.domain[0]) continue;
|
|
689
|
-
strokes.push({ d: `M${plot.x},${ty}H${plot.x + plot.width}`, role: "ornament", part: "grid" });
|
|
690
|
-
labels.push({ x: plot.x, y: ty - 3, text: formatNumber(tick, options.locale, options.numberFormat), kind: "tick", part: "axis", anchor: "start" });
|
|
691
|
-
}
|
|
692
|
-
strokes.push({ d: `M${plot.x},${bottom}H${plot.x + plot.width}`, role: "ornament", part: "rule" });
|
|
693
|
-
return { scale, strokes, labels };
|
|
694
|
-
}
|
|
695
|
-
function categoryAxis(xValues, plot, options) {
|
|
696
|
-
const numeric = options.numeric !== false && xValues.length > 0 && xValues.every((v) => typeof v === "number" && Number.isFinite(v));
|
|
697
|
-
if (numeric) {
|
|
698
|
-
const numbers = xValues;
|
|
699
|
-
const x = linearScale(extent(numbers) ?? [0, 0], [plot.x, plot.x + plot.width], {
|
|
700
|
-
chart: options.chart,
|
|
701
|
-
property: options.property,
|
|
702
|
-
padding: options.padding
|
|
703
|
-
});
|
|
704
|
-
return { at: (index) => x(numbers[index] ?? 0), bandwidth: 0 };
|
|
705
|
-
}
|
|
706
|
-
const keys = xValues.map((_, index) => String(index));
|
|
707
|
-
const band = bandScale(keys, [plot.x, plot.x + plot.width], { paddingInner: options.paddingInner, paddingOuter: options.paddingOuter });
|
|
708
|
-
return { at: (index) => band.center(String(index)) ?? plot.x, bandwidth: band.bandwidth };
|
|
709
|
-
}
|
|
710
|
-
function categoryLabels(texts, at2, area5, plot) {
|
|
711
|
-
const count = texts.length;
|
|
712
|
-
const step = Math.max(1, Math.ceil(count / Math.max(2, Math.floor(plot.width / LABEL_SPACING))));
|
|
713
|
-
const labels = [];
|
|
714
|
-
for (let index = 0; index < count; index += step) {
|
|
715
|
-
labels.push({
|
|
716
|
-
x: at2(index),
|
|
717
|
-
y: area5.y + area5.height - 5,
|
|
718
|
-
text: texts[index] ?? "",
|
|
719
|
-
kind: "tick",
|
|
720
|
-
part: "axis",
|
|
721
|
-
anchor: index === 0 ? "start" : "middle"
|
|
722
|
-
});
|
|
723
|
-
}
|
|
724
|
-
return labels;
|
|
725
|
-
}
|
|
726
|
-
var LEGEND_BAND = 22;
|
|
727
|
-
var TICK_CHAR = 5.2;
|
|
728
|
-
var LEGEND_CHAR = TICK_CHAR;
|
|
729
|
-
function legend(area5, entries) {
|
|
730
|
-
const strokes = [];
|
|
731
|
-
const labels = [];
|
|
732
|
-
let x = area5.x + PLOT_INSET;
|
|
733
|
-
for (const entry of entries) {
|
|
734
|
-
const swatch = { x, y: area5.y + 4, width: 12, height: 7 };
|
|
735
|
-
strokes.push({ d: rectPath(swatch), role: "ornament", part: entry.part, ...entry.tone ? { tone: entry.tone } : {}, ...entry.dash ? { dash: entry.dash } : {} });
|
|
736
|
-
labels.push({ x: x + 16, y: area5.y + 11, text: entry.name, kind: "tick", part: "axis", anchor: "start" });
|
|
737
|
-
x += 16 + entry.name.length * LEGEND_CHAR + 12;
|
|
738
|
-
}
|
|
739
|
-
return { area: { x: area5.x, y: area5.y + LEGEND_BAND, width: area5.width, height: Math.max(area5.height - LEGEND_BAND, 1) }, strokes, labels };
|
|
740
|
-
}
|
|
741
|
-
function pillPath(rect) {
|
|
742
|
-
const { x, y, width, height } = rect;
|
|
743
|
-
const r = Math.min(width, height) / 2;
|
|
744
|
-
if (r <= 0) return `M${x},${y}H${x + width}V${y + height}H${x}Z`;
|
|
745
|
-
return `M${x},${y + r}A${r},${r},0,0,1,${x + r},${y}H${x + width - r}A${r},${r},0,0,1,${x + width},${y + r}V${y + height - r}A${r},${r},0,0,1,${x + width - r},${y + height}H${x + r}A${r},${r},0,0,1,${x},${y + height - r}Z`;
|
|
746
|
-
}
|
|
747
|
-
function horizontalValueAxis(plot, area5, domain, options) {
|
|
748
|
-
const scale = linearScale([Math.min(0, domain[0]), Math.max(0, domain[1])], [plot.x, plot.x + plot.width], {
|
|
749
|
-
chart: options.chart,
|
|
750
|
-
property: options.property,
|
|
751
|
-
padding: options.padding,
|
|
752
|
-
nice: true,
|
|
753
|
-
fromZero: true
|
|
754
|
-
});
|
|
755
|
-
const strokes = [];
|
|
756
|
-
const labels = [];
|
|
757
|
-
for (const tick of scale.ticks(4)) {
|
|
758
|
-
const tx = scale(tick);
|
|
759
|
-
if (tick !== 0) strokes.push({ d: `M${tx},${plot.y}V${plot.y + plot.height}`, role: "ornament", part: "grid" });
|
|
760
|
-
labels.push({ x: tx, y: area5.y + area5.height - 5, text: formatNumber(tick, options.locale, options.numberFormat), kind: "tick", part: "axis", anchor: "middle" });
|
|
761
|
-
}
|
|
762
|
-
strokes.push({ d: `M${scale(0)},${plot.y}V${plot.y + plot.height}`, role: "ornament", part: "rule" });
|
|
763
|
-
return { scale, strokes, labels };
|
|
764
|
-
}
|
|
765
|
-
|
|
766
285
|
// src/charts/shared/shell.ts
|
|
767
286
|
function modelBase(chart, props, context, fallbackName, table) {
|
|
768
287
|
const { id } = context;
|
|
@@ -1175,7 +694,7 @@ function buildBulletChart(props, context) {
|
|
|
1175
694
|
}
|
|
1176
695
|
const rowHeight = plot.height / rows.length;
|
|
1177
696
|
const barHeight = Math.min(MAX_BAR, rowHeight * 0.35);
|
|
1178
|
-
const
|
|
697
|
+
const at = (percent) => plot.x + percent / 100 * plot.width;
|
|
1179
698
|
let met = 0;
|
|
1180
699
|
for (const row of rows) {
|
|
1181
700
|
const top = plot.y + row.index * rowHeight;
|
|
@@ -1187,16 +706,16 @@ function buildBulletChart(props, context) {
|
|
|
1187
706
|
warnValue(CHART2, actualName, `Row ${row.index} has no finite value; its bar is omitted.`);
|
|
1188
707
|
} else {
|
|
1189
708
|
const actual = clampPercent(row.actual, actualName, row.index);
|
|
1190
|
-
const bar = { x: plot.x, y: barY, width:
|
|
709
|
+
const bar = { x: plot.x, y: barY, width: at(actual) - plot.x, height: barHeight };
|
|
1191
710
|
if (bar.width > 0) strokes.push({ d: rectPath(bar), role: "encoding", part: "ink", tone: 2 });
|
|
1192
711
|
labels.push({ x: plot.x + plot.width, y: baseline, text: formatNumber(row.actual, locale, numberFormat), kind: "tick", part: "axis", anchor: "end" });
|
|
1193
|
-
hitAreas.push({ seriesKey: actualName, index: row.index, datum: row.datum, value: row.actual, x:
|
|
712
|
+
hitAreas.push({ seriesKey: actualName, index: row.index, datum: row.datum, value: row.actual, x: at(actual), y: barY + barHeight / 2, box: bar });
|
|
1194
713
|
if (row.target !== void 0 && row.actual >= row.target) met += 1;
|
|
1195
714
|
}
|
|
1196
715
|
if (row.target === void 0) {
|
|
1197
716
|
warnValue(CHART2, targetName, `Row ${row.index} has no finite target; its marker is omitted.`);
|
|
1198
717
|
} else {
|
|
1199
|
-
const x =
|
|
718
|
+
const x = at(clampPercent(row.target, targetName, row.index));
|
|
1200
719
|
strokes.push({ d: `M${x},${barY - MARKER_REACH}V${barY + barHeight + MARKER_REACH}`, role: "encoding", part: "ink", weight: 2 });
|
|
1201
720
|
}
|
|
1202
721
|
}
|
|
@@ -2478,109 +1997,6 @@ function buildFunnelChart(props, context) {
|
|
|
2478
1997
|
}
|
|
2479
1998
|
var funnelChart = /* @__PURE__ */ Object.freeze({ name: CHART15, build: buildFunnelChart });
|
|
2480
1999
|
|
|
2481
|
-
// src/charts/shared/polar.ts
|
|
2482
|
-
function polarFrame(area5, labelRoom = 0) {
|
|
2483
|
-
return {
|
|
2484
|
-
cx: area5.x + area5.width / 2,
|
|
2485
|
-
cy: area5.y + area5.height / 2,
|
|
2486
|
-
radius: Math.max(Math.min(area5.width, area5.height) / 2 - PLOT_INSET - labelRoom, 1)
|
|
2487
|
-
};
|
|
2488
|
-
}
|
|
2489
|
-
var RIM_LABELS = 14;
|
|
2490
|
-
function rimLabels(frame, entries, most = 24) {
|
|
2491
|
-
const step = Math.max(1, Math.ceil(entries.length / most));
|
|
2492
|
-
return entries.filter((_, i) => i % step === 0).map((e) => polarLabel(frame, e.angle, frame.radius + 8, e.text));
|
|
2493
|
-
}
|
|
2494
|
-
function pointAt(frame, angle, r) {
|
|
2495
|
-
return { x: frame.cx + r * Math.sin(angle), y: frame.cy - r * Math.cos(angle) };
|
|
2496
|
-
}
|
|
2497
|
-
var EPSILON = 1e-9;
|
|
2498
|
-
var at = (frame, angle, r) => {
|
|
2499
|
-
const p = pointAt(frame, angle, r);
|
|
2500
|
-
return `${p.x},${p.y}`;
|
|
2501
|
-
};
|
|
2502
|
-
function arcTo(frame, r, end, clockwise) {
|
|
2503
|
-
return `A${r},${r},0,0,${clockwise ? 1 : 0},${at(frame, end, r)}`;
|
|
2504
|
-
}
|
|
2505
|
-
function sweep(frame, r, start, end, clockwise) {
|
|
2506
|
-
if (Math.abs(end - start) <= Math.PI) return arcTo(frame, r, end, clockwise);
|
|
2507
|
-
const middle = (start + end) / 2;
|
|
2508
|
-
return arcTo(frame, r, middle, clockwise) + arcTo(frame, r, end, clockwise);
|
|
2509
|
-
}
|
|
2510
|
-
function arcPath(frame, r, start, end) {
|
|
2511
|
-
if (Math.abs(end - start) < EPSILON || r <= 0) return "";
|
|
2512
|
-
return `M${at(frame, start, r)}${sweep(frame, r, start, end, end > start)}`;
|
|
2513
|
-
}
|
|
2514
|
-
function sectorPath(frame, shape) {
|
|
2515
|
-
const { inner, outer, start, end } = shape;
|
|
2516
|
-
if (Math.abs(end - start) < EPSILON || outer <= 0) return "";
|
|
2517
|
-
const clockwise = end > start;
|
|
2518
|
-
const outerArc = `M${at(frame, start, outer)}${sweep(frame, outer, start, end, clockwise)}`;
|
|
2519
|
-
if (inner <= 0) return `${outerArc}L${frame.cx},${frame.cy}Z`;
|
|
2520
|
-
return `${outerArc}L${at(frame, end, inner)}${sweep(frame, inner, end, start, !clockwise)}Z`;
|
|
2521
|
-
}
|
|
2522
|
-
var POLE = 0.1;
|
|
2523
|
-
function polarLabel(frame, angle, r, text, kind = "tick") {
|
|
2524
|
-
const p = pointAt(frame, angle, r);
|
|
2525
|
-
const side = Math.sin(angle);
|
|
2526
|
-
const anchor = side > POLE ? "start" : side < -POLE ? "end" : "middle";
|
|
2527
|
-
return { x: p.x, y: p.y + 3, text, kind, part: "axis", anchor };
|
|
2528
|
-
}
|
|
2529
|
-
function readSectors(data, nameKey, valueKey, chart, locale) {
|
|
2530
|
-
const valueName = accessorName(valueKey, "value");
|
|
2531
|
-
const seen = /* @__PURE__ */ new Map();
|
|
2532
|
-
const sectors = [];
|
|
2533
|
-
data.forEach((datum, index) => {
|
|
2534
|
-
const value = finite(read(valueKey, datum, index));
|
|
2535
|
-
if (value === void 0 || value < 0) {
|
|
2536
|
-
warnValue(chart, valueName, `Row ${index} has a ${valueName} of ${String(read(valueKey, datum, index))}; a sector needs a finite value \u2265 0, so it is omitted.`);
|
|
2537
|
-
return;
|
|
2538
|
-
}
|
|
2539
|
-
const given = read(nameKey, datum, index);
|
|
2540
|
-
if (given === void 0 || given === null || given === "") warnValue(chart, accessorName(nameKey, "name"), `Row ${index} has no name; it is shown as "\u2014".`);
|
|
2541
|
-
const raw = formatCategory(given, locale);
|
|
2542
|
-
const count = (seen.get(raw) ?? 0) + 1;
|
|
2543
|
-
seen.set(raw, count);
|
|
2544
|
-
if (count > 1) warnValue(chart, accessorName(nameKey, "name"), `The name "${raw}" repeats; row ${index} is shown as "${raw} (${count})".`);
|
|
2545
|
-
sectors.push({ index, datum, name: count > 1 ? `${raw} (${count})` : raw, value });
|
|
2546
|
-
});
|
|
2547
|
-
return sectors;
|
|
2548
|
-
}
|
|
2549
|
-
var SECTORS_PER_CHART = 60;
|
|
2550
|
-
function checkSectors(chart, property, count) {
|
|
2551
|
-
if (process.env.NODE_ENV === "production" || count <= SECTORS_PER_CHART) return;
|
|
2552
|
-
diagnose("SP008", chart, { property, message: `${count} sectors; the ceiling is ${SECTORS_PER_CHART}.` });
|
|
2553
|
-
}
|
|
2554
|
-
var RING_TONES = [1, 2, 3, 4];
|
|
2555
|
-
function ringTone(index, count) {
|
|
2556
|
-
const tone = RING_TONES[index % RING_TONES.length];
|
|
2557
|
-
if (count > 1 && index === count - 1 && tone === RING_TONES[0]) return RING_TONES[2];
|
|
2558
|
-
return tone;
|
|
2559
|
-
}
|
|
2560
|
-
function ringTones(values) {
|
|
2561
|
-
const drawn = values.filter((v) => v > 0).length;
|
|
2562
|
-
let k = 0;
|
|
2563
|
-
return values.map((v) => v > 0 ? ringTone(k++, drawn) : ringTone(Math.min(k, Math.max(drawn - 1, 0)), drawn));
|
|
2564
|
-
}
|
|
2565
|
-
var LEGEND_ROW = 14;
|
|
2566
|
-
function sectorLegend(area5, entries) {
|
|
2567
|
-
const strokes = [];
|
|
2568
|
-
const labels = [];
|
|
2569
|
-
const fits2 = Math.max(Math.floor(area5.height / LEGEND_ROW), 1);
|
|
2570
|
-
const shown = entries.length > fits2 ? fits2 - 1 : entries.length;
|
|
2571
|
-
const top = area5.y + (area5.height - Math.min(entries.length, fits2) * LEGEND_ROW) / 2;
|
|
2572
|
-
entries.slice(0, shown).forEach((entry, row) => {
|
|
2573
|
-
const y = top + row * LEGEND_ROW;
|
|
2574
|
-
strokes.push({ d: rectPath({ x: area5.x, y: y + 3, width: 9, height: 7 }), role: "ornament", part: "ink", tone: entry.tone });
|
|
2575
|
-
labels.push({ x: area5.x + 14, y: y + 10, text: entry.name, kind: "tick", part: "text", anchor: "start" });
|
|
2576
|
-
labels.push({ x: area5.x + area5.width, y: y + 10, text: entry.share, kind: "tick", part: "axis", anchor: "end" });
|
|
2577
|
-
});
|
|
2578
|
-
if (shown < entries.length) {
|
|
2579
|
-
labels.push({ x: area5.x + 14, y: top + shown * LEGEND_ROW + 10, text: `+${entries.length - shown} more`, kind: "tick", part: "axis", anchor: "start" });
|
|
2580
|
-
}
|
|
2581
|
-
return { strokes, labels };
|
|
2582
|
-
}
|
|
2583
|
-
|
|
2584
2000
|
// src/charts/donut-chart/demo.ts
|
|
2585
2001
|
var DONUT_CHART_DEMO = /* @__PURE__ */ Object.freeze(
|
|
2586
2002
|
/* @__PURE__ */ [
|
|
@@ -2807,8 +2223,8 @@ function buildPolarBarChart(props, context) {
|
|
|
2807
2223
|
const tip = hole + b.value / largest * reach;
|
|
2808
2224
|
const d = sectorPath(frame, { inner: hole, outer: tip, start, end });
|
|
2809
2225
|
if (d && tip > hole) strokes.push({ d, role: "encoding", part: "ink", tone: 2 });
|
|
2810
|
-
const
|
|
2811
|
-
hitAreas.push({ seriesKey: valueName, index: b.index, datum: b.datum, value: b.value, x:
|
|
2226
|
+
const at = pointAt(frame, (start + end) / 2, tip);
|
|
2227
|
+
hitAreas.push({ seriesKey: valueName, index: b.index, datum: b.datum, value: b.value, x: at.x, y: at.y });
|
|
2812
2228
|
});
|
|
2813
2229
|
labels.push(...rimLabels(frame, bars.map((b, k) => ({ angle: (k + 0.5) * slot, text: b.name }))));
|
|
2814
2230
|
const top = bars.reduce((a, b) => b.value > a.value ? b : a);
|
|
@@ -2870,8 +2286,8 @@ function buildCoxcombChart(props, context) {
|
|
|
2870
2286
|
const r = frame.radius * Math.sqrt(s.value / largest);
|
|
2871
2287
|
const d = sectorPath(frame, { inner: 0, outer: r, start: from, end: from + slot });
|
|
2872
2288
|
if (d) strokes.push({ d, role: "encoding", part: "ink", tone: tones[k] });
|
|
2873
|
-
const
|
|
2874
|
-
hitAreas.push({ seriesKey: valueName, index: s.index, datum: s.datum, value: s.value, x:
|
|
2289
|
+
const at = pointAt(frame, from + slot / 2, r * 0.6);
|
|
2290
|
+
hitAreas.push({ seriesKey: valueName, index: s.index, datum: s.datum, value: s.value, x: at.x, y: at.y });
|
|
2875
2291
|
});
|
|
2876
2292
|
labels.push(...rimLabels(frame, sectors.map((s, k) => ({ angle: start + (k + 0.5) * slot, text: s.name }))));
|
|
2877
2293
|
const top = sectors.reduce((a, b) => b.value > a.value ? b : a);
|
|
@@ -3028,9 +2444,9 @@ function buildScalar(family, props, context) {
|
|
|
3028
2444
|
const drawing = family.draw(frame, value);
|
|
3029
2445
|
const strokes = [...card.strokes, ...drawing.strokes];
|
|
3030
2446
|
const labels = [...card.labels, ...drawing.labels];
|
|
3031
|
-
const
|
|
3032
|
-
labels.push({ x: frame.cx, y:
|
|
3033
|
-
if (caption) labels.push({ x: frame.cx, y:
|
|
2447
|
+
const at = family.readoutAt(frame);
|
|
2448
|
+
labels.push({ x: frame.cx, y: at.value, text: printed, kind: "value", part: "text", anchor: "middle" });
|
|
2449
|
+
if (caption) labels.push({ x: frame.cx, y: at.caption, text: caption, kind: "tick", part: "axis", anchor: "middle" });
|
|
3034
2450
|
const hitAreas = [];
|
|
3035
2451
|
if (value !== void 0 && drawing.tip) {
|
|
3036
2452
|
hitAreas.push({ seriesKey: "value", index: 0, datum: { value }, value, x: drawing.tip.x, y: drawing.tip.y });
|
|
@@ -3927,8 +3343,8 @@ function buildWindRose(props, context) {
|
|
|
3927
3343
|
if (r <= 0 || r > frame.radius + 0.01) continue;
|
|
3928
3344
|
strokes.push({ d: arcPath(frame, r, 0, 2 * Math.PI), role: "ornament", part: "grid" });
|
|
3929
3345
|
if (r > frame.radius - RIM_LABELS) continue;
|
|
3930
|
-
const
|
|
3931
|
-
labels.push({ x:
|
|
3346
|
+
const at = pointAt(frame, width / 2, r);
|
|
3347
|
+
labels.push({ x: at.x + 2, y: at.y - 2, text: `${formatNumber(tick, locale, { maximumFractionDigits: 0 })}%`, kind: "tick", part: "axis", anchor: "start" });
|
|
3932
3348
|
}
|
|
3933
3349
|
tally.forEach((row, k) => {
|
|
3934
3350
|
const start = k * width - width / 2 + width * SECTOR_GAP;
|
|
@@ -4187,13 +3603,13 @@ var POINTER = 7;
|
|
|
4187
3603
|
var CHAR2 = 5.2;
|
|
4188
3604
|
var CAP = 7;
|
|
4189
3605
|
var AIR = 1;
|
|
4190
|
-
function fits(frame, name,
|
|
3606
|
+
function fits(frame, name, at, inner, outer, start, end) {
|
|
4191
3607
|
const width = name.length * CHAR2;
|
|
4192
3608
|
const corners = [
|
|
4193
|
-
[
|
|
4194
|
-
[
|
|
4195
|
-
[
|
|
4196
|
-
[
|
|
3609
|
+
[at.x - width / 2, at.y + 3 - CAP],
|
|
3610
|
+
[at.x + width / 2, at.y + 3 - CAP],
|
|
3611
|
+
[at.x - width / 2, at.y + 3],
|
|
3612
|
+
[at.x + width / 2, at.y + 3]
|
|
4197
3613
|
];
|
|
4198
3614
|
const middle = (start + end) / 2;
|
|
4199
3615
|
return corners.every(([x, y]) => {
|
|
@@ -4204,7 +3620,6 @@ function fits(frame, name, at2, inner, outer, start, end) {
|
|
|
4204
3620
|
});
|
|
4205
3621
|
}
|
|
4206
3622
|
function buildVolvelleChart(props, context) {
|
|
4207
|
-
const usesDemo = props.data === void 0;
|
|
4208
3623
|
const all = props.data ?? VOLVELLE_CHART_DEMO;
|
|
4209
3624
|
const { locale } = context;
|
|
4210
3625
|
const cap = props.rings !== void 0 && Number.isInteger(props.rings) && props.rings > 0 ? props.rings : all.length;
|
|
@@ -4218,16 +3633,13 @@ function buildVolvelleChart(props, context) {
|
|
|
4218
3633
|
checkSectors(CHART23, "segments", segments.length);
|
|
4219
3634
|
rings.push({ label: formatCategory(datum.label, locale), segments: segments.map((s) => formatCategory(s, locale)), given: index });
|
|
4220
3635
|
});
|
|
4221
|
-
|
|
4222
|
-
warnValue(CHART23, props.indexValue !== void 0 ? "indexValue" : "indexRing", "Without `data` the demo keeps its own index; `indexRing` and `indexValue` apply to your rings.");
|
|
4223
|
-
}
|
|
4224
|
-
let ringIndex = usesDemo || props.indexRing === void 0 ? 0 : rings.findIndex((r) => r.given === props.indexRing);
|
|
3636
|
+
let ringIndex = props.indexRing === void 0 ? 0 : rings.findIndex((r) => r.given === props.indexRing);
|
|
4225
3637
|
if (ringIndex < 0) {
|
|
4226
3638
|
if (rings.length > 0) warnValue(CHART23, "indexRing", `Ring ${String(props.indexRing)} does not exist or has no segments; the index reads the first ring.`);
|
|
4227
3639
|
ringIndex = 0;
|
|
4228
3640
|
}
|
|
4229
3641
|
const turned = rings[ringIndex];
|
|
4230
|
-
let segment = turned &&
|
|
3642
|
+
let segment = turned && props.indexValue !== void 0 ? turned.segments.indexOf(props.indexValue) : 0;
|
|
4231
3643
|
if (segment < 0) {
|
|
4232
3644
|
warnValue(CHART23, "indexValue", `"${String(props.indexValue)}" is not on ring "${turned?.label}"; the index reads its first segment.`);
|
|
4233
3645
|
segment = 0;
|
|
@@ -4765,6 +4177,239 @@ var bubbleChart = /* @__PURE__ */ Object.freeze({
|
|
|
4765
4177
|
build: (props, context) => buildPoints(FAMILY6, props, context, BUBBLE_CHART_DEMO)
|
|
4766
4178
|
});
|
|
4767
4179
|
|
|
4768
|
-
|
|
4180
|
+
// src/dashboard/defaults.ts
|
|
4181
|
+
var BREAKPOINTS = ["sm", "md", "lg"];
|
|
4182
|
+
var DASHBOARD = "Dashboard";
|
|
4183
|
+
var MAX_COLUMNS = 12;
|
|
4184
|
+
var MAX_ROW_SPAN = 6;
|
|
4185
|
+
var DASHBOARD_DEFAULTS = /* @__PURE__ */ Object.freeze({
|
|
4186
|
+
columns: /* @__PURE__ */ Object.freeze({ sm: 1, md: 2, lg: 4 }),
|
|
4187
|
+
rowHeight: 240,
|
|
4188
|
+
gap: 16,
|
|
4189
|
+
span: 1,
|
|
4190
|
+
headingLevel: 2,
|
|
4191
|
+
ssrWidth: 1200
|
|
4192
|
+
});
|
|
4193
|
+
function perBreakpoint(value, fallback) {
|
|
4194
|
+
const given = value !== null && typeof value === "object" ? value : void 0;
|
|
4195
|
+
const bare = value !== void 0 && given === void 0 ? value : void 0;
|
|
4196
|
+
return Object.fromEntries(BREAKPOINTS.map((bp) => [bp, bare ?? given?.[bp] ?? fallback[bp]]));
|
|
4197
|
+
}
|
|
4198
|
+
function corrected(property, given, used, rule) {
|
|
4199
|
+
if (process.env.NODE_ENV !== "production") {
|
|
4200
|
+
diagnose("SP002", DASHBOARD, { property, message: `${String(given)} is not ${rule}; ${used} is used.` });
|
|
4201
|
+
}
|
|
4202
|
+
return used;
|
|
4203
|
+
}
|
|
4204
|
+
var isInteger = (n) => typeof n === "number" && Number.isInteger(n);
|
|
4205
|
+
function resolveLayout(layout) {
|
|
4206
|
+
const asked = perBreakpoint(layout?.columns, DASHBOARD_DEFAULTS.columns);
|
|
4207
|
+
const columns = Object.fromEntries(
|
|
4208
|
+
BREAKPOINTS.map((bp) => {
|
|
4209
|
+
const n = asked[bp];
|
|
4210
|
+
const ok = isInteger(n) && n >= 1 && n <= MAX_COLUMNS;
|
|
4211
|
+
return [bp, ok ? n : corrected(`columns.${bp}`, n, DASHBOARD_DEFAULTS.columns[bp], `an integer from 1 to ${MAX_COLUMNS}`)];
|
|
4212
|
+
})
|
|
4213
|
+
);
|
|
4214
|
+
const rowHeight = layout?.rowHeight;
|
|
4215
|
+
const gap = layout?.gap;
|
|
4216
|
+
const one = { sm: 1, md: 1, lg: 1 };
|
|
4217
|
+
return {
|
|
4218
|
+
columns,
|
|
4219
|
+
rowHeight: rowHeight === void 0 ? DASHBOARD_DEFAULTS.rowHeight : Number.isFinite(rowHeight) && rowHeight > 0 ? rowHeight : corrected("rowHeight", rowHeight, DASHBOARD_DEFAULTS.rowHeight, "a number above 0"),
|
|
4220
|
+
gap: gap === void 0 ? DASHBOARD_DEFAULTS.gap : Number.isFinite(gap) && gap >= 0 ? gap : corrected("gap", gap, DASHBOARD_DEFAULTS.gap, "a number of 0 or more"),
|
|
4221
|
+
cells: (layout?.cells ?? []).map((cell, index) => {
|
|
4222
|
+
const col = perBreakpoint(cell.colSpan, one);
|
|
4223
|
+
const row = perBreakpoint(cell.rowSpan, one);
|
|
4224
|
+
const where = `cells[${index}]`;
|
|
4225
|
+
const span = Object.fromEntries(
|
|
4226
|
+
BREAKPOINTS.map((bp) => {
|
|
4227
|
+
let c = col[bp];
|
|
4228
|
+
if (!isInteger(c) || c < 1) c = corrected(`${where}.colSpan.${bp}`, c, 1, "an integer of 1 or more");
|
|
4229
|
+
else if (c > columns[bp]) {
|
|
4230
|
+
if (process.env.NODE_ENV !== "production") {
|
|
4231
|
+
diagnose("SP014", DASHBOARD, { property: `${where}.colSpan.${bp}`, message: `Cell "${cell.id}" spans ${c} columns at ${bp}, which has ${columns[bp]}; it spans ${columns[bp]}.` });
|
|
4232
|
+
}
|
|
4233
|
+
c = columns[bp];
|
|
4234
|
+
}
|
|
4235
|
+
let r = row[bp];
|
|
4236
|
+
if (!isInteger(r)) r = corrected(`${where}.rowSpan.${bp}`, r, 1, `an integer from 1 to ${MAX_ROW_SPAN}`);
|
|
4237
|
+
else if (r < 1 || r > MAX_ROW_SPAN) r = corrected(`${where}.rowSpan.${bp}`, r, Math.min(Math.max(r, 1), MAX_ROW_SPAN), `an integer from 1 to ${MAX_ROW_SPAN}`);
|
|
4238
|
+
return [bp, { col: c, row: r }];
|
|
4239
|
+
})
|
|
4240
|
+
);
|
|
4241
|
+
return { id: cell.id, span };
|
|
4242
|
+
})
|
|
4243
|
+
};
|
|
4244
|
+
}
|
|
4245
|
+
|
|
4246
|
+
// src/dashboard/resolve.ts
|
|
4247
|
+
var UNSAFE = /[^A-Za-z0-9_.:-]+/g;
|
|
4248
|
+
var MD_FROM = 640;
|
|
4249
|
+
var LG_FROM = 1024;
|
|
4250
|
+
var PROBE = 100;
|
|
4251
|
+
function safeIdOf(id) {
|
|
4252
|
+
return id.replace(UNSAFE, "-");
|
|
4253
|
+
}
|
|
4254
|
+
function warn(code, property, message) {
|
|
4255
|
+
if (process.env.NODE_ENV !== "production") diagnose(code, DASHBOARD, { property, message });
|
|
4256
|
+
}
|
|
4257
|
+
function breakpointOf(width) {
|
|
4258
|
+
return width >= LG_FROM ? "lg" : width >= MD_FROM ? "md" : "sm";
|
|
4259
|
+
}
|
|
4260
|
+
function nominalBox(layout, span, width) {
|
|
4261
|
+
const bp = breakpointOf(width);
|
|
4262
|
+
const { col, row } = span[bp];
|
|
4263
|
+
const cols = layout.columns[bp];
|
|
4264
|
+
const { gap, rowHeight } = layout;
|
|
4265
|
+
return {
|
|
4266
|
+
width: round2((width - gap * (cols - 1)) / cols * col + gap * (col - 1)),
|
|
4267
|
+
height: round2(rowHeight * row + gap * (row - 1))
|
|
4268
|
+
};
|
|
4269
|
+
}
|
|
4270
|
+
function cellStyle(span) {
|
|
4271
|
+
const style = {};
|
|
4272
|
+
for (const bp of BREAKPOINTS) style[`--sp-cell-col-${bp}`] = String(span[bp].col);
|
|
4273
|
+
for (const bp of BREAKPOINTS) style[`--sp-cell-row-${bp}`] = String(span[bp].row);
|
|
4274
|
+
return style;
|
|
4275
|
+
}
|
|
4276
|
+
function resolveDashboard(props, childCells) {
|
|
4277
|
+
const layout = resolveLayout(props.layout);
|
|
4278
|
+
let ssrWidth = DASHBOARD_DEFAULTS.ssrWidth;
|
|
4279
|
+
if (props.ssrWidth !== void 0) {
|
|
4280
|
+
if (Number.isFinite(props.ssrWidth) && props.ssrWidth > 0) ssrWidth = props.ssrWidth;
|
|
4281
|
+
else warn("SP002", "ssrWidth", `${String(props.ssrWidth)} is not a width above 0; ${ssrWidth} is used.`);
|
|
4282
|
+
}
|
|
4283
|
+
const declared = /* @__PURE__ */ new Map();
|
|
4284
|
+
const order = [];
|
|
4285
|
+
for (const cell of layout.cells) {
|
|
4286
|
+
if (declared.has(cell.id)) {
|
|
4287
|
+
warn("SP015", "layout.cells", `Layout cell "${cell.id}" is declared twice; the later declaration is ignored.`);
|
|
4288
|
+
continue;
|
|
4289
|
+
}
|
|
4290
|
+
declared.set(cell.id, cell.span);
|
|
4291
|
+
order.push(cell.id);
|
|
4292
|
+
}
|
|
4293
|
+
const holder = /* @__PURE__ */ new Map();
|
|
4294
|
+
const unplaced = [];
|
|
4295
|
+
childCells.forEach((cell, index) => {
|
|
4296
|
+
if (cell === void 0) {
|
|
4297
|
+
unplaced.push(index);
|
|
4298
|
+
} else if (!declared.has(cell)) {
|
|
4299
|
+
warn("SP015", "cell", `Child ${index} names cell "${cell}", which the layout does not declare; it is placed in source order, span 1.`);
|
|
4300
|
+
unplaced.push(index);
|
|
4301
|
+
} else if (holder.has(cell)) {
|
|
4302
|
+
warn("SP015", "cell", `Children ${holder.get(cell)} and ${index} both name cell "${cell}"; the later is placed in source order, span 1.`);
|
|
4303
|
+
unplaced.push(index);
|
|
4304
|
+
} else {
|
|
4305
|
+
holder.set(cell, index);
|
|
4306
|
+
}
|
|
4307
|
+
});
|
|
4308
|
+
for (const id of order) {
|
|
4309
|
+
if (!holder.has(id)) warn("SP015", "layout.cells", `Layout cell "${id}" has no child; it is dropped.`);
|
|
4310
|
+
}
|
|
4311
|
+
const single = { sm: { col: 1, row: 1 }, md: { col: 1, row: 1 }, lg: { col: 1, row: 1 } };
|
|
4312
|
+
const base = safeIdOf(props.id);
|
|
4313
|
+
const taken = /* @__PURE__ */ new Set();
|
|
4314
|
+
const chartIdOf = (token) => {
|
|
4315
|
+
const stem = `${base}--${token.replace(UNSAFE, "-")}`;
|
|
4316
|
+
let id = stem;
|
|
4317
|
+
for (let n = 2; taken.has(id); n += 1) id = `${stem}-${n}`;
|
|
4318
|
+
taken.add(id);
|
|
4319
|
+
return id;
|
|
4320
|
+
};
|
|
4321
|
+
const cellOf = (id, child, span, token) => ({
|
|
4322
|
+
id,
|
|
4323
|
+
child,
|
|
4324
|
+
chartId: chartIdOf(token),
|
|
4325
|
+
span,
|
|
4326
|
+
nominal: nominalBox(layout, span, ssrWidth),
|
|
4327
|
+
style: cellStyle(span)
|
|
4328
|
+
});
|
|
4329
|
+
const cells = [
|
|
4330
|
+
...order.filter((id) => holder.has(id)).map((id) => cellOf(id, holder.get(id), declared.get(id), id)),
|
|
4331
|
+
...unplaced.map((index) => cellOf(String(index), index, single, String(index)))
|
|
4332
|
+
];
|
|
4333
|
+
const style = {};
|
|
4334
|
+
for (const bp of BREAKPOINTS) style[`--sp-dashboard-columns-${bp}`] = String(layout.columns[bp]);
|
|
4335
|
+
style["--sp-dashboard-row-height"] = `${layout.rowHeight}px`;
|
|
4336
|
+
style["--sp-dashboard-layout-gap"] = `${layout.gap}px`;
|
|
4337
|
+
return { id: props.id, cells, style };
|
|
4338
|
+
}
|
|
4339
|
+
function cellChartBox(cell, chartProps, recipe) {
|
|
4340
|
+
const width = chartProps.width ?? cell.width;
|
|
4341
|
+
if (chartProps.height !== void 0) return { width, height: chartProps.height };
|
|
4342
|
+
let outer;
|
|
4343
|
+
if (recipe && width > 0) {
|
|
4344
|
+
const model = recipe.build({ ...chartProps, width, height: PROBE }, { id: "sp-probe", width, locale: "en", emptyState: { text: "", rule: false }, domainPadding: 0 });
|
|
4345
|
+
if (model.status !== "deferred") outer = model.geometry.viewBox.height;
|
|
4346
|
+
}
|
|
4347
|
+
outer ??= cardLayout(chartProps, { width, areaHeight: PROBE, chrome: chartProps.chrome ?? "card", locale: "en" }).viewBox.height;
|
|
4348
|
+
return { width, height: Math.max(1, round2(cell.height - (outer - PROBE))) };
|
|
4349
|
+
}
|
|
4350
|
+
|
|
4351
|
+
// src/dashboard/view.ts
|
|
4352
|
+
function dashboardView(props, children) {
|
|
4353
|
+
const model = resolveDashboard(props, children.map((c) => c.cell));
|
|
4354
|
+
if (props.title === void 0 && props.label === void 0 && process.env.NODE_ENV !== "production") {
|
|
4355
|
+
diagnose("SP002", "Dashboard", { property: "title", message: "Name the region with `title` or `label`." });
|
|
4356
|
+
}
|
|
4357
|
+
const base = safeIdOf(props.id);
|
|
4358
|
+
const ground = props.ground ?? DEFAULTS.ground;
|
|
4359
|
+
const groundName = typeof ground === "string" ? ground : ground.name;
|
|
4360
|
+
const titled = props.title !== void 0;
|
|
4361
|
+
const heading = titled ? { level: props.headingLevel ?? DASHBOARD_DEFAULTS.headingLevel, id: `${base}-title`, text: props.title } : void 0;
|
|
4362
|
+
const description = props.description !== void 0 ? { id: `${base}-desc`, text: props.description } : void 0;
|
|
4363
|
+
const config = {};
|
|
4364
|
+
for (const key of ["ground", "substrate", "mode", "locale"]) {
|
|
4365
|
+
if (props[key] !== void 0) config[key] = props[key];
|
|
4366
|
+
}
|
|
4367
|
+
return {
|
|
4368
|
+
model,
|
|
4369
|
+
section: {
|
|
4370
|
+
className: ["sp-dashboard", `sp-ground-${groundName}`, props.className].filter(Boolean).join(" "),
|
|
4371
|
+
substrate: props.substrate ?? DEFAULTS.substrate,
|
|
4372
|
+
...heading ? { labelledby: heading.id } : { label: props.label },
|
|
4373
|
+
...description ? { describedby: description.id } : {},
|
|
4374
|
+
style: model.style
|
|
4375
|
+
},
|
|
4376
|
+
...heading ? { heading } : {},
|
|
4377
|
+
...description ? { description } : {},
|
|
4378
|
+
cells: model.cells.map((cell) => ({
|
|
4379
|
+
child: cell.child,
|
|
4380
|
+
labelledby: `${children[cell.child]?.id ?? cell.chartId}-title`,
|
|
4381
|
+
style: cell.style,
|
|
4382
|
+
context: { chartId: cell.chartId, box: cell.nominal, config }
|
|
4383
|
+
}))
|
|
4384
|
+
};
|
|
4385
|
+
}
|
|
4386
|
+
function inCell(props, cell, recipe) {
|
|
4387
|
+
if (!cell) return { props, width: props.width };
|
|
4388
|
+
const box = cellChartBox(cell.box, props, recipe);
|
|
4389
|
+
const merged = { ...props };
|
|
4390
|
+
for (const [key, value] of Object.entries(cell.config)) merged[key] ??= value;
|
|
4391
|
+
return {
|
|
4392
|
+
props: { ...merged, id: props.id ?? cell.chartId, height: props.height ?? box.height },
|
|
4393
|
+
width: props.width ?? box.width
|
|
4394
|
+
};
|
|
4395
|
+
}
|
|
4396
|
+
|
|
4397
|
+
// src/dashboard/link.ts
|
|
4398
|
+
function linkedItems(model, key, value) {
|
|
4399
|
+
const { hitAreas } = model.geometry;
|
|
4400
|
+
if (hitAreas.length > 0 && !hitAreas.some((hit) => key in hit.datum) && process.env.NODE_ENV !== "production") {
|
|
4401
|
+
diagnose("SP016", model.chart, { property: "link.key", message: `No datum has a "${key}" field.` });
|
|
4402
|
+
}
|
|
4403
|
+
return hitAreas.flatMap((hit, index) => hit.datum[key] === value ? [index] : []);
|
|
4404
|
+
}
|
|
4405
|
+
function linkFrom(key, source, active) {
|
|
4406
|
+
return active ? { key, value: active.datum[key], source } : null;
|
|
4407
|
+
}
|
|
4408
|
+
function linkedMarks(model, state) {
|
|
4409
|
+
if (!state || state.source === model.id) return [];
|
|
4410
|
+
return linkedItems(model, state.key, state.value).map((index) => readout(model, toActiveItem(model.geometry.hitAreas[index])).marker.d);
|
|
4411
|
+
}
|
|
4412
|
+
|
|
4413
|
+
export { DASHBOARD_DEFAULTS, DISPLAY_FACE_PROBE, LINE_CHART_DEMO, LINE_CHART_DEMO_KEYS, MIN_TOUCH_TARGET, NAVIGATION_KEYS, NullInker, PATH_BYTE_BUDGET, activityGrid, addDays, areaChart, barChart, breakpointOf, bubbleChart, bulletChart, candlestickChart, cellChartBox, checkTypeface, chordRing, composedChart, coxcombChart, createPrng, dashboardView, donutChart, enforceHeightening, funnelChart, gaugeArc, heatmapChart, inCell, isoDate, kpiCard, lineChart, linkFrom, linkedItems, linkedMarks, meterChart, orbitChart, parseGeometry, parseIsoDate, pathBytes, perBreakpoint, polarBarChart, pyramidChart, radarChart, radialArcGroup, radialRings, rangeBandChart, readout, reduceInteraction, registerInker, resolveActive, resolveDashboard, resolveInker, resolveLayout, sameActive, sankeyChart, scatterChart, serializeGeometry, sparklineRows, stackedBarChart, stepActive, stepChart, streamChart, svgString, toSvgPoint, toSvgView, treemapChart, volvelleChart, waterfallChart, weekday, windRose };
|
|
4769
4414
|
//# sourceMappingURL=index.js.map
|
|
4770
4415
|
//# sourceMappingURL=index.js.map
|