gridsmith-ui 0.30.1 → 0.31.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/CHANGELOG.md +73 -1
- package/dist/charts.d.ts +114 -1
- package/dist/charts.js +1113 -206
- package/dist/charts.js.map +1 -1
- package/dist/chat.js +24 -10
- package/dist/chat.js.map +1 -1
- package/dist/{chunk-ULCACQSA.js → chunk-2Q4HOGEI.js} +16 -51
- package/dist/chunk-2Q4HOGEI.js.map +1 -0
- package/dist/{chunk-T52LAFKG.js → chunk-3J67NISC.js} +13 -15
- package/dist/chunk-3J67NISC.js.map +1 -0
- package/dist/{chunk-L6HWACK7.js → chunk-53F7GVBQ.js} +6 -3
- package/dist/chunk-53F7GVBQ.js.map +1 -0
- package/dist/{chunk-SLRTSBCJ.js → chunk-BKYWKMUN.js} +73 -678
- package/dist/chunk-BKYWKMUN.js.map +1 -0
- package/dist/{chunk-KMKBOTFR.js → chunk-EKWXPSTI.js} +10 -12
- package/dist/chunk-EKWXPSTI.js.map +1 -0
- package/dist/{chunk-SJZWEBHL.js → chunk-GP2COVJC.js} +24 -11
- package/dist/chunk-GP2COVJC.js.map +1 -0
- package/dist/{chunk-OVHBQT3Z.js → chunk-GT6M74IZ.js} +6 -6
- package/dist/{chunk-OVHBQT3Z.js.map → chunk-GT6M74IZ.js.map} +1 -1
- package/dist/{chunk-IELIF4PJ.js → chunk-HUNEJIKV.js} +3 -3
- package/dist/{chunk-IELIF4PJ.js.map → chunk-HUNEJIKV.js.map} +1 -1
- package/dist/{chunk-NEUWZUK6.js → chunk-LE72KHIE.js} +3 -4
- package/dist/chunk-LE72KHIE.js.map +1 -0
- package/dist/{chunk-OX3NNMWG.js → chunk-OFACAHRV.js} +10 -6
- package/dist/chunk-OFACAHRV.js.map +1 -0
- package/dist/{chunk-R5QXMNW7.js → chunk-OWQ7M6R7.js} +177 -27
- package/dist/chunk-OWQ7M6R7.js.map +1 -0
- package/dist/{chunk-TYNDXMBV.js → chunk-QOMP4D6V.js} +3 -3
- package/dist/{chunk-TYNDXMBV.js.map → chunk-QOMP4D6V.js.map} +1 -1
- package/dist/chunk-U7WBI4AV.js +285 -0
- package/dist/chunk-U7WBI4AV.js.map +1 -0
- package/dist/chunk-V2ARGHJN.js +755 -0
- package/dist/chunk-V2ARGHJN.js.map +1 -0
- package/dist/chunk-V73LHNRL.js +51 -0
- package/dist/chunk-V73LHNRL.js.map +1 -0
- package/dist/{chunk-UDJOIVKQ.js → chunk-XSQD3UGY.js} +77 -7
- package/dist/chunk-XSQD3UGY.js.map +1 -0
- package/dist/{chunk-ISONVOA7.js → chunk-YFHTREH5.js} +6 -8
- package/dist/chunk-YFHTREH5.js.map +1 -0
- package/dist/data-grid.js +10 -13
- package/dist/data-grid.js.map +1 -1
- package/dist/editor-mentions.js +2 -4
- package/dist/editor-mentions.js.map +1 -1
- package/dist/editor.js +3 -7
- package/dist/editor.js.map +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +104 -16
- package/dist/index.js +1075 -654
- package/dist/index.js.map +1 -1
- package/dist/kanban.js +9 -13
- package/dist/kanban.js.map +1 -1
- package/dist/resizable.js +9 -13
- package/dist/resizable.js.map +1 -1
- package/dist/virtualized.js +10 -13
- package/dist/virtualized.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-6C2IUMY7.js +0 -46
- package/dist/chunk-6C2IUMY7.js.map +0 -1
- package/dist/chunk-7Q46JXSK.js +0 -51
- package/dist/chunk-7Q46JXSK.js.map +0 -1
- package/dist/chunk-HTXP5BMM.js +0 -38
- package/dist/chunk-HTXP5BMM.js.map +0 -1
- package/dist/chunk-ISONVOA7.js.map +0 -1
- package/dist/chunk-KMKBOTFR.js.map +0 -1
- package/dist/chunk-L6HWACK7.js.map +0 -1
- package/dist/chunk-NEUWZUK6.js.map +0 -1
- package/dist/chunk-NKUCS67T.js +0 -13
- package/dist/chunk-NKUCS67T.js.map +0 -1
- package/dist/chunk-OX3NNMWG.js.map +0 -1
- package/dist/chunk-R5QXMNW7.js.map +0 -1
- package/dist/chunk-SJZWEBHL.js.map +0 -1
- package/dist/chunk-SLRTSBCJ.js.map +0 -1
- package/dist/chunk-T52LAFKG.js.map +0 -1
- package/dist/chunk-UDJOIVKQ.js.map +0 -1
- package/dist/chunk-ULCACQSA.js.map +0 -1
- package/dist/chunk-WIUGPZOH.js +0 -88
- package/dist/chunk-WIUGPZOH.js.map +0 -1
- package/dist/chunk-YL5KD5RC.js +0 -49
- package/dist/chunk-YL5KD5RC.js.map +0 -1
package/dist/charts.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { TITLE_TEXT, Skeleton } from './chunk-
|
|
2
|
+
import { SegmentedControl, TITLE_TEXT, Skeleton } from './chunk-U7WBI4AV.js';
|
|
3
|
+
import { useKeyboardScroll } from './chunk-GP2COVJC.js';
|
|
3
4
|
import { useFormatLocale, useHydrated } from './chunk-LJYSVZMI.js';
|
|
5
|
+
import './chunk-BKYWKMUN.js';
|
|
4
6
|
import { formatLocale } from './chunk-7QLXYRPD.js';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
7
|
+
import './chunk-7RKC5C7G.js';
|
|
8
|
+
import { FormChangeCtx } from './chunk-72SDAX54.js';
|
|
9
|
+
import { elementType } from './chunk-2Q4HOGEI.js';
|
|
10
|
+
import './chunk-HUNEJIKV.js';
|
|
11
|
+
import { mergeRefs } from './chunk-XSQD3UGY.js';
|
|
12
|
+
import { warnOnce } from './chunk-53F7GVBQ.js';
|
|
13
|
+
import { cn, FOCUS_INSET, GLASS_SURFACE } from './chunk-OWQ7M6R7.js';
|
|
14
|
+
import { createContext, forwardRef, useContext, useRef, useState, useId, useMemo, useLayoutEffect, useEffect, useCallback, useSyncExternalStore, Children, isValidElement } from 'react';
|
|
15
|
+
import { ResponsiveContainer, BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Bar, LineChart as LineChart$1, Line, AreaChart as AreaChart$1, Area, PieChart as PieChart$1, Pie, Cell, ComposedChart, ScatterChart as ScatterChart$1, ZAxis, Scatter, Symbols, RadarChart as RadarChart$1, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Radar, RadialBarChart as RadialBarChart$1, RadialBar, FunnelChart as FunnelChart$1, Funnel, LabelList, Treemap, Sankey, Text, Legend, ReferenceArea, ReferenceLine, getNiceTickValues, Rectangle, usePlotArea, ZIndexLayer, DefaultZIndexes } from 'recharts';
|
|
16
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
17
|
import { createPortal } from 'react-dom';
|
|
11
|
-
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
12
18
|
|
|
13
19
|
var CHART_TOKENS = [
|
|
14
20
|
"--ds-chart-1",
|
|
@@ -18,6 +24,7 @@ var CHART_TOKENS = [
|
|
|
18
24
|
"--ds-chart-5",
|
|
19
25
|
"--ds-chart-6"
|
|
20
26
|
];
|
|
27
|
+
var CHART_COLOR_COUNT = CHART_TOKENS.length;
|
|
21
28
|
var CHART_TOKENS_SOFT = [
|
|
22
29
|
"--ds-chart-1-soft",
|
|
23
30
|
"--ds-chart-2-soft",
|
|
@@ -40,6 +47,7 @@ var FALLBACK_COLORS = {
|
|
|
40
47
|
"--ds-chart-5-soft": "rgba(8, 145, 178, 0.3)",
|
|
41
48
|
"--ds-chart-6-soft": "rgba(124, 58, 237, 0.3)",
|
|
42
49
|
"--ds-bg-surface": "#ffffff",
|
|
50
|
+
"--ds-bg-canvas": "#ffffff",
|
|
43
51
|
"--ds-border-subtle": "#e5e7eb",
|
|
44
52
|
"--ds-text-primary": "#111827",
|
|
45
53
|
"--ds-text-secondary": "#6b7280",
|
|
@@ -275,30 +283,30 @@ function categoryKind(labels, locale) {
|
|
|
275
283
|
return "names";
|
|
276
284
|
}
|
|
277
285
|
if (raw instanceof Date) continue;
|
|
278
|
-
const
|
|
279
|
-
if (!
|
|
280
|
-
if (!list.some((re) => re.test(
|
|
286
|
+
const text2 = String(raw).trim();
|
|
287
|
+
if (!text2) continue;
|
|
288
|
+
if (!list.some((re) => re.test(text2))) return "names";
|
|
281
289
|
}
|
|
282
290
|
return "scale";
|
|
283
291
|
}
|
|
284
292
|
function resolvedAxisFont(el) {
|
|
285
293
|
const doc = el.ownerDocument;
|
|
286
294
|
const view = doc.defaultView;
|
|
287
|
-
let
|
|
295
|
+
let probe2 = el.querySelector("[data-chart-probe]");
|
|
288
296
|
let temporary = false;
|
|
289
|
-
if (!
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
el.appendChild(
|
|
297
|
+
if (!probe2) {
|
|
298
|
+
probe2 = doc.createElement("span");
|
|
299
|
+
probe2.setAttribute("aria-hidden", "true");
|
|
300
|
+
probe2.style.cssText = "position:absolute;visibility:hidden;pointer-events:none;white-space:nowrap;font-size:var(--ds-font-size-xs);font-family:var(--ds-font-sans)";
|
|
301
|
+
el.appendChild(probe2);
|
|
294
302
|
temporary = true;
|
|
295
303
|
}
|
|
296
|
-
const cs = view?.getComputedStyle(
|
|
304
|
+
const cs = view?.getComputedStyle(probe2);
|
|
297
305
|
const px = parseFloat(cs?.fontSize ?? "") || 12;
|
|
298
306
|
const family = (cs?.fontFamily ?? "").trim() || "sans-serif";
|
|
299
307
|
const style = cs?.fontStyle && cs.fontStyle !== "normal" ? `${cs.fontStyle} ` : "";
|
|
300
308
|
const weight = cs?.fontWeight && cs.fontWeight !== "400" && cs.fontWeight !== "normal" ? `${cs.fontWeight} ` : "";
|
|
301
|
-
if (temporary)
|
|
309
|
+
if (temporary) probe2.remove();
|
|
302
310
|
return { font: `${style}${weight}${px}px ${family}`, px };
|
|
303
311
|
}
|
|
304
312
|
var shared = null;
|
|
@@ -321,66 +329,659 @@ function createAxisMeasurer(el) {
|
|
|
321
329
|
const { font, px } = resolvedAxisFont(el);
|
|
322
330
|
ctx.font = font;
|
|
323
331
|
const widths = /* @__PURE__ */ new Map();
|
|
324
|
-
const
|
|
325
|
-
let w = widths.get(
|
|
332
|
+
const measure2 = (text2) => {
|
|
333
|
+
let w = widths.get(text2);
|
|
326
334
|
if (w === void 0) {
|
|
327
335
|
ctx.font = font;
|
|
328
|
-
w = ctx.measureText(
|
|
329
|
-
widths.set(
|
|
336
|
+
w = ctx.measureText(text2).width;
|
|
337
|
+
widths.set(text2, w);
|
|
330
338
|
}
|
|
331
339
|
return w;
|
|
332
340
|
};
|
|
333
|
-
return { measure, px, font };
|
|
341
|
+
return { measure: measure2, px, font };
|
|
334
342
|
}
|
|
335
343
|
var ELLIPSIS = "\u2026";
|
|
336
344
|
var letters = (s) => s.replace(/\s+/g, "").replace(/…/g, "").length;
|
|
337
|
-
function prefixThatFits(
|
|
345
|
+
function prefixThatFits(text2, width, measure2, suffix = "") {
|
|
338
346
|
let lo = 1;
|
|
339
|
-
let hi =
|
|
347
|
+
let hi = text2.length;
|
|
340
348
|
let best = 0;
|
|
341
349
|
while (lo <= hi) {
|
|
342
350
|
const mid = lo + hi >> 1;
|
|
343
|
-
if (
|
|
351
|
+
if (measure2(text2.slice(0, mid).trimEnd() + suffix) <= width) {
|
|
344
352
|
best = mid;
|
|
345
353
|
lo = mid + 1;
|
|
346
354
|
} else hi = mid - 1;
|
|
347
355
|
}
|
|
348
356
|
return best;
|
|
349
357
|
}
|
|
350
|
-
function fitLabel(name, width,
|
|
351
|
-
const
|
|
352
|
-
if (!
|
|
353
|
-
const words =
|
|
358
|
+
function fitLabel(name, width, measure2) {
|
|
359
|
+
const text2 = name.replace(/\s+/g, " ").trim();
|
|
360
|
+
if (!measure2 || !text2 || width <= 0 || measure2(text2) <= width) return { lines: [text2], cut: false, shown: letters(text2) };
|
|
361
|
+
const words = text2.split(" ");
|
|
354
362
|
let first = "";
|
|
355
363
|
let firstShown = "";
|
|
356
364
|
let rest = "";
|
|
357
365
|
for (let i = words.length - 1; i >= 1; i--) {
|
|
358
366
|
const candidate = words.slice(0, i).join(" ");
|
|
359
|
-
if (
|
|
367
|
+
if (measure2(candidate) <= width) {
|
|
360
368
|
first = firstShown = candidate;
|
|
361
369
|
rest = words.slice(i).join(" ");
|
|
362
370
|
break;
|
|
363
371
|
}
|
|
364
372
|
}
|
|
365
373
|
if (!first) {
|
|
366
|
-
const k2 = Math.max(1, prefixThatFits(
|
|
367
|
-
firstShown =
|
|
368
|
-
rest =
|
|
369
|
-
first = rest && !
|
|
374
|
+
const k2 = Math.max(1, prefixThatFits(text2, width, measure2, "-"));
|
|
375
|
+
firstShown = text2.slice(0, k2).trimEnd();
|
|
376
|
+
rest = text2.slice(k2).trimStart();
|
|
377
|
+
first = rest && !text2.slice(k2 - 1, k2 + 1).includes(" ") ? `${firstShown}-` : firstShown;
|
|
370
378
|
}
|
|
371
379
|
if (!rest) return { lines: [first], cut: false, shown: letters(firstShown) };
|
|
372
|
-
if (
|
|
373
|
-
const k = prefixThatFits(rest, width,
|
|
380
|
+
if (measure2(rest) <= width) return { lines: [first, rest], cut: false, shown: letters(firstShown) + letters(rest) };
|
|
381
|
+
const k = prefixThatFits(rest, width, measure2, ELLIPSIS);
|
|
374
382
|
const kept = k > 0 ? rest.slice(0, k).trimEnd() : "";
|
|
375
383
|
return { lines: [first, kept + ELLIPSIS], cut: true, shown: letters(firstShown) + letters(kept) };
|
|
376
384
|
}
|
|
377
385
|
var MIN_NAME_CHARS = 6;
|
|
378
|
-
function namesCutShort(names, width,
|
|
386
|
+
function namesCutShort(names, width, measure2) {
|
|
379
387
|
return names.some((n) => {
|
|
380
|
-
const fit = fitLabel(n, width,
|
|
388
|
+
const fit = fitLabel(n, width, measure2);
|
|
381
389
|
return fit.cut && fit.shown < MIN_NAME_CHARS;
|
|
382
390
|
});
|
|
383
391
|
}
|
|
392
|
+
var TICK_COUNT = 5;
|
|
393
|
+
var TIME_UNITS = [["hour", 36e5], ["minute", 6e4], ["second", 1e3], ["millisecond", 1]];
|
|
394
|
+
var BYTE_UNITS = [["petabyte", 1e15], ["terabyte", 1e12], ["gigabyte", 1e9], ["megabyte", 1e6], ["kilobyte", 1e3], ["byte", 1]];
|
|
395
|
+
var MAX_FRACTION = 10;
|
|
396
|
+
function exact(format, maxDigits = MAX_FRACTION) {
|
|
397
|
+
for (let digits = 0; digits < maxDigits; digits++) {
|
|
398
|
+
const label = format(digits);
|
|
399
|
+
if (label === format(digits + 6)) return label;
|
|
400
|
+
}
|
|
401
|
+
return format(maxDigits);
|
|
402
|
+
}
|
|
403
|
+
var unitFormat = (unit, value, locale) => (digits) => new Intl.NumberFormat(locale, { style: "unit", unit, unitDisplay: "short", maximumFractionDigits: digits }).format(value);
|
|
404
|
+
var unitsOf = (format) => format === "bytes" ? BYTE_UNITS : TIME_UNITS;
|
|
405
|
+
function unitReached(units, value) {
|
|
406
|
+
return units.find(([, size]) => Math.abs(value) >= size) ?? units[units.length - 1];
|
|
407
|
+
}
|
|
408
|
+
function axisUnit(units, ticks) {
|
|
409
|
+
const largest = Math.max(0, ...ticks.map(Math.abs));
|
|
410
|
+
const exactIn = (size) => ticks.every((t) => Math.abs(t / size * 100 - Math.round(t / size * 100)) < 1e-6);
|
|
411
|
+
return units.find(([, size]) => largest / size >= 1 && exactIn(size)) ?? units[units.length - 1];
|
|
412
|
+
}
|
|
413
|
+
function valueAxisTicks(values) {
|
|
414
|
+
const finite = values.filter(Number.isFinite);
|
|
415
|
+
if (!finite.length) return [];
|
|
416
|
+
const lo = Math.min(0, ...finite);
|
|
417
|
+
const hi = Math.max(0, ...finite);
|
|
418
|
+
if (lo === hi) return [lo];
|
|
419
|
+
return getNiceTickValues([lo, hi], TICK_COUNT, true).map(Number);
|
|
420
|
+
}
|
|
421
|
+
function axisTickFormatter(values, format, locale) {
|
|
422
|
+
if (typeof format === "function") return format;
|
|
423
|
+
if (format === "percent") return (value) => exact((d) => new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: d }).format(value / 100));
|
|
424
|
+
if (format === "milliseconds" || format === "bytes") {
|
|
425
|
+
const [unit, size] = axisUnit(unitsOf(format), valueAxisTicks(values));
|
|
426
|
+
return (value) => exact(unitFormat(unit, value / size, locale));
|
|
427
|
+
}
|
|
428
|
+
const compact = (value) => exact((d) => new Intl.NumberFormat(locale, { notation: "compact", maximumFractionDigits: d }).format(value));
|
|
429
|
+
const plain = (value) => exact((d) => new Intl.NumberFormat(locale, { maximumFractionDigits: d }).format(value));
|
|
430
|
+
const largest = Math.max(0, ...values.filter(Number.isFinite).map(Math.abs));
|
|
431
|
+
return largest >= COMPACT_FROM ? compact : plain;
|
|
432
|
+
}
|
|
433
|
+
var COMPACT_FROM = 1e4;
|
|
434
|
+
function fullValueFormatter(format, locale) {
|
|
435
|
+
if (typeof format === "function") return format;
|
|
436
|
+
if (format === void 0 || format === "number") return (value) => value.toLocaleString(locale);
|
|
437
|
+
if (format === "percent") return (value) => exact((d) => new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: d }).format(value / 100), 4);
|
|
438
|
+
return (value) => {
|
|
439
|
+
const [unit, size] = unitReached(unitsOf(format), value);
|
|
440
|
+
return exact(unitFormat(unit, value / size, locale), 4);
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function numberOf(raw) {
|
|
444
|
+
const v = typeof raw === "number" ? raw : typeof raw === "string" && raw.trim() !== "" ? Number(raw) : NaN;
|
|
445
|
+
return Number.isFinite(v) ? v : null;
|
|
446
|
+
}
|
|
447
|
+
function axisExtent(data, series) {
|
|
448
|
+
return data.flatMap((row) => {
|
|
449
|
+
const own = [];
|
|
450
|
+
const up = /* @__PURE__ */ new Map();
|
|
451
|
+
const down = /* @__PURE__ */ new Map();
|
|
452
|
+
for (const s of series) {
|
|
453
|
+
const v = numberOf(row[s.dataKey]);
|
|
454
|
+
if (v === null) continue;
|
|
455
|
+
if (s.stack == null) own.push(v);
|
|
456
|
+
else if (v >= 0) up.set(s.stack, (up.get(s.stack) ?? 0) + v);
|
|
457
|
+
else down.set(s.stack, (down.get(s.stack) ?? 0) + v);
|
|
458
|
+
}
|
|
459
|
+
return [...own, ...up.values(), ...down.values()];
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
function seriesValues(data, keys, stacked = false) {
|
|
463
|
+
return axisExtent(data, keys.map((dataKey) => ({ dataKey, stack: stacked ? "stack" : null })));
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// ../../showcase/src/components/charts/chart-access.ts
|
|
467
|
+
var isNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
468
|
+
var text = (v) => v == null ? "" : String(v);
|
|
469
|
+
function listOf(items) {
|
|
470
|
+
if (items.length <= 1) return items[0] ?? "";
|
|
471
|
+
return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
|
|
472
|
+
}
|
|
473
|
+
function percent(part, whole) {
|
|
474
|
+
if (!(whole > 0)) return "0%";
|
|
475
|
+
const p = part / whole * 100;
|
|
476
|
+
if (p > 0 && p < 1) return "under 1%";
|
|
477
|
+
if (p > 99 && p < 100) return "over 99%";
|
|
478
|
+
return `${Math.round(p)}%`;
|
|
479
|
+
}
|
|
480
|
+
function referenceNote(r, format) {
|
|
481
|
+
return isNumber(r.to) ? `${r.label}: ${format(r.value)} to ${format(r.to)}` : `${r.label}: ${format(r.value)}`;
|
|
482
|
+
}
|
|
483
|
+
var SUMMED = 3;
|
|
484
|
+
function seriesSentence(label, points, over, f) {
|
|
485
|
+
if (!points.length) return null;
|
|
486
|
+
if (points.length === 1) return `${label}: ${f(points[0].value)} (${points[0].cat}).`;
|
|
487
|
+
const high = points.reduce((a, b) => b.value > a.value ? b : a);
|
|
488
|
+
const low = points.reduce((a, b) => b.value < a.value ? b : a);
|
|
489
|
+
if (high.value === low.value) return over === "scale" ? `${label}: ${f(high.value)} throughout.` : `${label}: ${f(high.value)} for all ${points.length}.`;
|
|
490
|
+
const extremes = `highest ${f(high.value)} (${high.cat}), lowest ${f(low.value)} (${low.cat})`;
|
|
491
|
+
if (over === "names") return `${label}: ${extremes}.`;
|
|
492
|
+
const first = points[0], end = points[points.length - 1];
|
|
493
|
+
const steps = points.slice(1).map((p, i) => p.value - points[i].value);
|
|
494
|
+
const up = steps.every((d) => d >= 0), down = steps.every((d) => d <= 0);
|
|
495
|
+
const course = `${up ? "up from" : down ? "down from" : "from"} ${f(first.value)} (${first.cat}) to ${f(end.value)} (${end.cat})`;
|
|
496
|
+
return up || down ? `${label}: ${course}.` : `${label}: ${course}, ${extremes}.`;
|
|
497
|
+
}
|
|
498
|
+
function cartesianAccess(o) {
|
|
499
|
+
const { data, categoryKey, series, format } = o;
|
|
500
|
+
const cats = data.map((row) => text(row[categoryKey]));
|
|
501
|
+
const last = data.length - 1;
|
|
502
|
+
const forecastFrom = o.forecast && o.forecast.from >= 0 && o.forecast.from < data.length ? o.forecast.from : -1;
|
|
503
|
+
const rows = data.map((row, i) => [
|
|
504
|
+
forecastFrom >= 0 && i >= forecastFrom ? `${cats[i]} (${o.forecast.label})` : cats[i],
|
|
505
|
+
...series.map((s) => {
|
|
506
|
+
const v = numberOf(row[s.dataKey]);
|
|
507
|
+
return v === null ? "\u2013" : format(v, s.dataKey);
|
|
508
|
+
})
|
|
509
|
+
]);
|
|
510
|
+
const runs = forecastFrom < last ? `${cats[forecastFrom]} to ${cats[last]}` : cats[forecastFrom];
|
|
511
|
+
const forecastNote = forecastFrom >= 0 ? `${o.forecast.label}: ${forecastFrom === 0 ? `every value (${runs})` : runs}` : null;
|
|
512
|
+
const notes = [
|
|
513
|
+
...(o.references ?? []).map((r) => referenceNote(r, (v) => format(v))),
|
|
514
|
+
...forecastNote ? [forecastNote] : []
|
|
515
|
+
];
|
|
516
|
+
const names = series.map((s) => s.label);
|
|
517
|
+
const span = o.over === "scale" && cats.length > 1 ? ` from ${cats[0]} to ${cats[last]}` : cats.length === 1 ? ` for ${cats[0]}` : ` across ${cats.length} categories`;
|
|
518
|
+
const sentences = [`${o.kind}: ${listOf(names)}${span}.`];
|
|
519
|
+
const measured = forecastFrom > 0 ? forecastFrom : data.length;
|
|
520
|
+
for (const s of series.slice(0, SUMMED)) {
|
|
521
|
+
const points = data.slice(0, measured).map((row, i) => ({ cat: cats[i], value: numberOf(row[s.dataKey]) })).filter((p) => p.value !== null);
|
|
522
|
+
const said = seriesSentence(s.label, points, o.over, (v) => format(v, s.dataKey));
|
|
523
|
+
if (said) sentences.push(said);
|
|
524
|
+
}
|
|
525
|
+
if (series.length > SUMMED) sentences.push(`And ${series.length - SUMMED} more series.`);
|
|
526
|
+
for (const note of notes) sentences.push(`${note}.`);
|
|
527
|
+
return {
|
|
528
|
+
table: { columns: [o.categoryHeading || "Category", ...names], numeric: [false, ...series.map(() => true)], rows, notes },
|
|
529
|
+
description: sentences.join(" ")
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
function partsAccess(o) {
|
|
533
|
+
const items = o.items.map((p) => ({ ...p, value: numberOf(p.value), max: numberOf(p.max) })).filter((p) => p.value !== null);
|
|
534
|
+
const f = (v) => o.format(v);
|
|
535
|
+
const total = items.reduce((sum, p) => sum + Math.max(0, p.value), 0);
|
|
536
|
+
const largest = items.reduce((a, b) => !a || b.value > a.value ? b : a, null);
|
|
537
|
+
const smallest = items.reduce((a, b) => !a || b.value < a.value ? b : a, null);
|
|
538
|
+
const grouped = o.kind === "treemap" && items.some((p) => p.group);
|
|
539
|
+
const withMax = o.kind === "radial" && items.some((p) => p.max !== null);
|
|
540
|
+
if (o.kind === "funnel") {
|
|
541
|
+
const first = items[0], last = items[items.length - 1];
|
|
542
|
+
return {
|
|
543
|
+
table: {
|
|
544
|
+
columns: ["Stage", "Value", "Of the first stage"],
|
|
545
|
+
numeric: [false, true, true],
|
|
546
|
+
rows: items.map((p) => [p.name, f(p.value), first && first.value > 0 ? percent(p.value, first.value) : "\u2013"]),
|
|
547
|
+
notes: []
|
|
548
|
+
},
|
|
549
|
+
// a first stage of nothing has no share to carry through
|
|
550
|
+
description: first && last && items.length > 1 ? `Funnel of ${items.length} stages, from ${first.name} (${f(first.value)}) to ${last.name} (${f(last.value)})${first.value > 0 ? `: ${percent(last.value, first.value)} of the first stage reach the last` : ""}.` : `Funnel of ${items.length} ${items.length === 1 ? "stage" : "stages"}.`
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
const columns = [...grouped ? ["Group"] : [], "Name", "Value", ...withMax ? ["Of its maximum"] : o.kind === "radial" ? [] : ["Share"]];
|
|
554
|
+
const rows = items.map((p) => [
|
|
555
|
+
...grouped ? [p.group ?? ""] : [],
|
|
556
|
+
p.name,
|
|
557
|
+
f(p.value),
|
|
558
|
+
...withMax ? [p.max !== null ? percent(p.value, p.max) : "\u2013"] : o.kind === "radial" ? [] : [percent(p.value, total)]
|
|
559
|
+
]);
|
|
560
|
+
const what = o.kind === "pie" ? "Pie chart" : o.kind === "radial" ? "Radial bar chart" : "Treemap";
|
|
561
|
+
const unit = o.kind === "pie" ? "parts" : o.kind === "radial" ? "bars" : "items";
|
|
562
|
+
let description = `${what} of ${items.length} ${items.length === 1 ? unit.replace(/s$/, "") : unit}`;
|
|
563
|
+
const groups = new Set(items.map((p) => p.group)).size;
|
|
564
|
+
if (grouped) description += ` in ${groups} ${groups === 1 ? "group" : "groups"}`;
|
|
565
|
+
if (largest && smallest && items.length > 1) {
|
|
566
|
+
description += o.kind === "radial" ? `: ${largest.name} the highest (${f(largest.value)}), ${smallest.name} the lowest (${f(smallest.value)}).` : `: ${largest.name} the largest (${f(largest.value)}, ${percent(largest.value, total)}), ${smallest.name} the smallest (${f(smallest.value)}, ${percent(smallest.value, total)}).`;
|
|
567
|
+
} else {
|
|
568
|
+
description += ".";
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
table: { columns, numeric: columns.map((c) => c !== "Group" && c !== "Name"), rows, notes: [], rowHeader: grouped ? 1 : 0 },
|
|
572
|
+
description
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
function pointsAccess(o) {
|
|
576
|
+
const withZ = !!o.zHeading && o.series.some((s) => s.points.some((p) => numberOf(p.z) !== null));
|
|
577
|
+
const rows = [];
|
|
578
|
+
const xs = [];
|
|
579
|
+
const ys = [];
|
|
580
|
+
for (const s of o.series) {
|
|
581
|
+
for (const p of s.points) {
|
|
582
|
+
const x = numberOf(p.x), y = numberOf(p.y), z = numberOf(p.z);
|
|
583
|
+
if (x === null || y === null) continue;
|
|
584
|
+
xs.push(x);
|
|
585
|
+
ys.push(y);
|
|
586
|
+
rows.push([s.name, o.formatX(x), o.formatY(y), ...withZ ? [z !== null ? String(z) : "\u2013"] : []]);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const range = (vs, fmt) => vs.length ? `from ${fmt(Math.min(...vs))} to ${fmt(Math.max(...vs))}` : "";
|
|
590
|
+
return {
|
|
591
|
+
table: {
|
|
592
|
+
columns: ["Series", o.xHeading, o.yHeading, ...withZ ? [o.zHeading] : []],
|
|
593
|
+
numeric: [false, true, true, ...withZ ? [true] : []],
|
|
594
|
+
rows,
|
|
595
|
+
notes: []
|
|
596
|
+
},
|
|
597
|
+
description: `Scatter chart of ${rows.length} ${rows.length === 1 ? "point" : "points"} in ${o.series.length} ${o.series.length === 1 ? "series" : "series"}${xs.length ? `: ${o.xHeading} ${range(xs, o.formatX)}, ${o.yHeading} ${range(ys, o.formatY)}` : ""}.`
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
function flowAccess(o) {
|
|
601
|
+
const name = (i) => o.nodes[i]?.name ?? String(i);
|
|
602
|
+
const undrawn = new Set(o.undrawn ?? []);
|
|
603
|
+
const links = o.links.map((l) => ({ ...l, value: numberOf(l.value), drawn: !undrawn.has(l) })).filter((l) => l.value !== null);
|
|
604
|
+
const largest = links.filter((l) => l.drawn).reduce((a, b) => !a || b.value > a.value ? b : a, null);
|
|
605
|
+
const back = links.filter((l) => !l.drawn);
|
|
606
|
+
const backSaid = back.length === 1 ? ` The flow from ${name(back[0].source)} to ${name(back[0].target)} runs back, so it isn't drawn.` : back.length > 1 ? ` ${back.length} flows that run back aren't drawn.` : "";
|
|
607
|
+
return {
|
|
608
|
+
table: {
|
|
609
|
+
columns: ["From", "To", "Value"],
|
|
610
|
+
numeric: [false, false, true],
|
|
611
|
+
rows: links.map((l) => [name(l.source), name(l.target), o.format(l.value)]),
|
|
612
|
+
notes: back.map((l) => `Not drawn, as it runs back: ${name(l.source)} to ${name(l.target)} (${o.format(l.value)})`)
|
|
613
|
+
},
|
|
614
|
+
description: `Flow chart of ${links.length} ${links.length === 1 ? "flow" : "flows"} between ${o.nodes.length} ${o.nodes.length === 1 ? "point" : "points"}${largest ? `: the largest from ${name(largest.source)} to ${name(largest.target)} (${o.format(largest.value)})` : ""}.${backSaid}`
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
var GAP = 2;
|
|
618
|
+
var overlaps = (a, b) => a.x < b.x + b.width && b.x < a.x + a.width && a.y < b.y + b.height && b.y < a.y + a.height;
|
|
619
|
+
function placeNames(names) {
|
|
620
|
+
const placed = [];
|
|
621
|
+
const moves = /* @__PURE__ */ new Map();
|
|
622
|
+
for (const [id, box] of [...names].sort((a, b) => a[1].order - b[1].order)) {
|
|
623
|
+
const clear = (away) => {
|
|
624
|
+
let dy2 = 0;
|
|
625
|
+
for (let tries = 0; tries <= placed.length; tries++) {
|
|
626
|
+
const hit = placed.find((p) => overlaps({ ...box, y: box.y + dy2 }, p));
|
|
627
|
+
if (!hit) break;
|
|
628
|
+
dy2 = away > 0 ? hit.y + hit.height + GAP - box.y : hit.y - GAP - box.height - box.y;
|
|
629
|
+
}
|
|
630
|
+
return dy2;
|
|
631
|
+
};
|
|
632
|
+
const inside = (dy2) => !box.bounds || box.y + dy2 >= box.bounds.top - 0.5 && box.y + dy2 + box.height <= box.bounds.bottom + 0.5;
|
|
633
|
+
let dy = clear(box.away);
|
|
634
|
+
if (!inside(dy)) {
|
|
635
|
+
const other = clear(box.away > 0 ? -1 : 1);
|
|
636
|
+
if (inside(other)) dy = other;
|
|
637
|
+
}
|
|
638
|
+
moves.set(id, dy);
|
|
639
|
+
placed.push({ ...box, y: box.y + dy });
|
|
640
|
+
}
|
|
641
|
+
return moves;
|
|
642
|
+
}
|
|
643
|
+
var PlotNamesContext = createContext(null);
|
|
644
|
+
function PlotNames({ children }) {
|
|
645
|
+
const boxes = useRef(/* @__PURE__ */ new Map());
|
|
646
|
+
const [moves, setMoves] = useState(() => /* @__PURE__ */ new Map());
|
|
647
|
+
const place = useCallback((id, box) => {
|
|
648
|
+
if (box) boxes.current.set(id, box);
|
|
649
|
+
else boxes.current.delete(id);
|
|
650
|
+
const next = placeNames([...boxes.current]);
|
|
651
|
+
setMoves((prev) => prev.size === next.size && [...next].every(([k, v]) => prev.get(k) === v) ? prev : next);
|
|
652
|
+
}, []);
|
|
653
|
+
const value = useMemo(() => ({ place, moves }), [place, moves]);
|
|
654
|
+
return /* @__PURE__ */ jsx(PlotNamesContext.Provider, { value, children });
|
|
655
|
+
}
|
|
656
|
+
function markOf(index, marks) {
|
|
657
|
+
return (index + Math.floor(index / CHART_COLOR_COUNT)) % marks;
|
|
658
|
+
}
|
|
659
|
+
var DASHES = [void 0, "6 3", "2 3", "8 3 2 3", "12 3 3 3", "1 4"];
|
|
660
|
+
function seriesDash(index, count) {
|
|
661
|
+
return count >= 2 ? DASHES[markOf(index, DASHES.length)] : void 0;
|
|
662
|
+
}
|
|
663
|
+
function lineDashes(lines) {
|
|
664
|
+
const marks = lines.map((line, i) => lines.length >= 2 ? markOf(i, DASHES.length) : line.dashed ? 1 : 0);
|
|
665
|
+
const colour = (i) => i % CHART_COLOR_COUNT;
|
|
666
|
+
const inColour = (i, k, ...but) => marks.some((m, j) => m === k && colour(j) === colour(i) && j !== i && !but.includes(j));
|
|
667
|
+
lines.forEach((line, i) => {
|
|
668
|
+
if (!line.dashed || marks[i] !== 0) return;
|
|
669
|
+
const free = DASHES.findIndex((_, k) => k > 0 && !marks.includes(k));
|
|
670
|
+
if (free > 0) {
|
|
671
|
+
marks[i] = free;
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
const trade = lines.findIndex((other, j) => j !== i && !other.dashed && marks[j] !== 0 && !inColour(i, marks[j], j) && !inColour(j, 0, i));
|
|
675
|
+
if (trade >= 0) {
|
|
676
|
+
[marks[i], marks[trade]] = [marks[trade], 0];
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const own = DASHES.findIndex((_, k) => k > 0 && !inColour(i, k));
|
|
680
|
+
marks[i] = own > 0 ? own : 1;
|
|
681
|
+
});
|
|
682
|
+
return marks.map((k) => DASHES[k]);
|
|
683
|
+
}
|
|
684
|
+
var SYMBOLS = ["circle", "square", "triangle", "diamond", "cross", "star", "wye"];
|
|
685
|
+
function seriesSymbol(index, count) {
|
|
686
|
+
return count >= 2 ? SYMBOLS[index % SYMBOLS.length] : "circle";
|
|
687
|
+
}
|
|
688
|
+
var PATTERNS = 5;
|
|
689
|
+
function seriesPatternKind(index, count) {
|
|
690
|
+
const mark = markOf(index, PATTERNS + 1);
|
|
691
|
+
return count < 2 || mark === 0 ? void 0 : mark - 1;
|
|
692
|
+
}
|
|
693
|
+
function seriesPattern(chartId, index, count) {
|
|
694
|
+
if (seriesPatternKind(index, count) === void 0) return void 0;
|
|
695
|
+
return `${chartId.replace(/[^a-zA-Z0-9_-]/g, "")}-series-${index}`;
|
|
696
|
+
}
|
|
697
|
+
var aheadPattern = (id) => `${id}-ahead`;
|
|
698
|
+
function parseRgba(colour) {
|
|
699
|
+
const c = colour.trim();
|
|
700
|
+
const hex = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(c)?.[1];
|
|
701
|
+
if (hex) {
|
|
702
|
+
const full = hex.length <= 4 ? [...hex].map((h) => h + h).join("") : hex;
|
|
703
|
+
const n = (k) => parseInt(full.slice(k * 2, k * 2 + 2), 16);
|
|
704
|
+
return [n(0), n(1), n(2), full.length === 8 ? n(3) / 255 : 1];
|
|
705
|
+
}
|
|
706
|
+
const fn = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/i.exec(c);
|
|
707
|
+
if (!fn) return null;
|
|
708
|
+
const alpha = fn[4] === void 0 ? 1 : fn[4].endsWith("%") ? parseFloat(fn[4]) / 100 : parseFloat(fn[4]);
|
|
709
|
+
return [Number(fn[1]), Number(fn[2]), Number(fn[3]), alpha];
|
|
710
|
+
}
|
|
711
|
+
var probe;
|
|
712
|
+
var drawnColours = /* @__PURE__ */ new Map();
|
|
713
|
+
var UNREAD = "rgba(1, 2, 3, 0.5)";
|
|
714
|
+
function drawn(colour, ground = [255, 255, 255]) {
|
|
715
|
+
const key = `${colour}|${ground.join(",")}`;
|
|
716
|
+
if (drawnColours.has(key)) return drawnColours.get(key);
|
|
717
|
+
let out = null;
|
|
718
|
+
const parsed = parseRgba(colour);
|
|
719
|
+
if (parsed) {
|
|
720
|
+
const [r, g, b, a] = parsed;
|
|
721
|
+
out = [r, g, b].map((v, i) => Math.round(v * a + ground[i] * (1 - a)));
|
|
722
|
+
} else {
|
|
723
|
+
if (probe === void 0) {
|
|
724
|
+
try {
|
|
725
|
+
probe = typeof document === "undefined" || /jsdom/i.test(navigator.userAgent) ? null : document.createElement("canvas").getContext("2d", { willReadFrequently: true });
|
|
726
|
+
} catch {
|
|
727
|
+
probe = null;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (probe) {
|
|
731
|
+
probe.fillStyle = UNREAD;
|
|
732
|
+
const unread = probe.fillStyle;
|
|
733
|
+
probe.fillStyle = colour;
|
|
734
|
+
if (probe.fillStyle !== unread) {
|
|
735
|
+
probe.fillStyle = `rgb(${ground.join(",")})`;
|
|
736
|
+
probe.fillRect(0, 0, 1, 1);
|
|
737
|
+
probe.fillStyle = colour;
|
|
738
|
+
probe.fillRect(0, 0, 1, 1);
|
|
739
|
+
const d = probe.getImageData(0, 0, 1, 1).data;
|
|
740
|
+
out = [d[0], d[1], d[2]];
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
drawnColours.set(key, out);
|
|
745
|
+
return out;
|
|
746
|
+
}
|
|
747
|
+
var channel = (c) => c / 255 <= 0.04045 ? c / 255 / 12.92 : ((c / 255 + 0.055) / 1.055) ** 2.4;
|
|
748
|
+
var luminance = ([r, g, b]) => 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
|
749
|
+
function contrast(a, b) {
|
|
750
|
+
const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
|
|
751
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
752
|
+
}
|
|
753
|
+
var css = (c) => `rgb(${c.join(" ")})`;
|
|
754
|
+
function chartGround(surface, canvas) {
|
|
755
|
+
const page = drawn(canvas);
|
|
756
|
+
return page && drawn(surface, page);
|
|
757
|
+
}
|
|
758
|
+
function groundColour(surface, canvas) {
|
|
759
|
+
const ground = chartGround(surface, canvas);
|
|
760
|
+
return ground ? css(ground) : surface;
|
|
761
|
+
}
|
|
762
|
+
function markInk(fill, surface, ink, canvas) {
|
|
763
|
+
const ground = chartGround(surface, canvas);
|
|
764
|
+
const filled = ground && drawn(fill, ground);
|
|
765
|
+
const text2 = ground && drawn(ink, ground);
|
|
766
|
+
if (!ground || !filled || !text2) return surface;
|
|
767
|
+
const theme = contrast(filled, text2) >= contrast(filled, ground) ? text2 : ground;
|
|
768
|
+
if (contrast(filled, theme) >= 3) return css(theme);
|
|
769
|
+
return css(contrast(filled, [0, 0, 0]) >= contrast(filled, [255, 255, 255]) ? [0, 0, 0] : [255, 255, 255]);
|
|
770
|
+
}
|
|
771
|
+
var AHEAD_STRENGTH = 0.45;
|
|
772
|
+
function aheadFill(colour, surface, canvas) {
|
|
773
|
+
const ground = chartGround(surface, canvas);
|
|
774
|
+
const own = ground && drawn(colour, ground);
|
|
775
|
+
if (!ground || !own) return null;
|
|
776
|
+
return css(own.map((v, i) => Math.round(v * AHEAD_STRENGTH + ground[i] * (1 - AHEAD_STRENGTH))));
|
|
777
|
+
}
|
|
778
|
+
function Pattern({ id, kind, fill, on }) {
|
|
779
|
+
const mark = { stroke: on, strokeWidth: 1.5 };
|
|
780
|
+
return /* @__PURE__ */ jsxs("pattern", { id, width: 6, height: 6, patternUnits: "userSpaceOnUse", children: [
|
|
781
|
+
/* @__PURE__ */ jsx("rect", { width: 6, height: 6, fill }),
|
|
782
|
+
kind === 0 && /* @__PURE__ */ jsx("path", { d: "M-1,1 l2,-2 M0,6 l6,-6 M5,7 l2,-2", ...mark }),
|
|
783
|
+
kind === 1 && /* @__PURE__ */ jsx("circle", { cx: 3, cy: 3, r: 1.3, fill: on }),
|
|
784
|
+
kind === 2 && /* @__PURE__ */ jsx("path", { d: "M-1,1 l2,-2 M0,6 l6,-6 M5,7 l2,-2 M-1,5 l2,2 M0,0 l6,6 M5,-1 l2,2", ...mark, strokeWidth: 1 }),
|
|
785
|
+
kind === 3 && /* @__PURE__ */ jsx("path", { d: "M0,3 h6", ...mark }),
|
|
786
|
+
kind === 4 && /* @__PURE__ */ jsx("path", { d: "M3,0 v6", ...mark })
|
|
787
|
+
] });
|
|
788
|
+
}
|
|
789
|
+
function SeriesPatterns({ chartId, colors, surface, ahead = false }) {
|
|
790
|
+
const ink = useResolvedToken("--ds-text-primary");
|
|
791
|
+
const canvas = useResolvedToken("--ds-bg-canvas");
|
|
792
|
+
if (colors.length < 2) return null;
|
|
793
|
+
return /* @__PURE__ */ jsx("defs", { children: colors.map((color, i) => {
|
|
794
|
+
const id = seriesPattern(chartId, i, colors.length);
|
|
795
|
+
const kind = seriesPatternKind(i, colors.length);
|
|
796
|
+
if (!id || kind === void 0) return null;
|
|
797
|
+
const faint = ahead ? aheadFill(color, surface, canvas) : null;
|
|
798
|
+
return [
|
|
799
|
+
/* @__PURE__ */ jsx(Pattern, { id, kind, fill: color, on: markInk(color, surface, ink, canvas) }, id),
|
|
800
|
+
faint && /* @__PURE__ */ jsx(Pattern, { id: aheadPattern(id), kind, fill: faint, on: markInk(faint, surface, ink, canvas) }, aheadPattern(id))
|
|
801
|
+
];
|
|
802
|
+
}) });
|
|
803
|
+
}
|
|
804
|
+
function SeriesSwatch({ mark, color }) {
|
|
805
|
+
if (mark?.kind === "line") {
|
|
806
|
+
return /* @__PURE__ */ jsx("svg", { width: 18, height: 10, "aria-hidden": "true", className: "shrink-0", children: /* @__PURE__ */ jsx("line", { x1: 1, y1: 5, x2: 17, y2: 5, stroke: mark.color, strokeWidth: 2.5, strokeDasharray: mark.dash, strokeLinecap: "butt" }) });
|
|
807
|
+
}
|
|
808
|
+
if (mark?.kind === "symbol") {
|
|
809
|
+
return /* @__PURE__ */ jsx("svg", { width: 12, height: 12, "aria-hidden": "true", className: "shrink-0", children: /* @__PURE__ */ jsx(Symbols, { cx: 6, cy: 6, size: 60, type: mark.symbol, fill: mark.color }) });
|
|
810
|
+
}
|
|
811
|
+
const fill = mark?.kind === "fill" && mark.pattern ? `url(#${mark.pattern})` : mark?.color ?? color;
|
|
812
|
+
return /* @__PURE__ */ jsx("svg", { width: 12, height: 12, "aria-hidden": "true", className: "shrink-0", children: /* @__PURE__ */ jsx("rect", { x: 0.75, y: 0.75, width: 10.5, height: 10.5, rx: 2.5, fill, stroke: mark?.kind === "fill" && mark.edge || mark?.color || color, strokeWidth: 1.5 }) });
|
|
813
|
+
}
|
|
814
|
+
var isNumber2 = (v) => typeof v === "number" && Number.isFinite(v);
|
|
815
|
+
var OFFSET = 5;
|
|
816
|
+
function PlotName({ x = 0, y = 0, viewBox, value, id, order, place, ink }) {
|
|
817
|
+
const names = useContext(PlotNamesContext);
|
|
818
|
+
const plot = usePlotArea();
|
|
819
|
+
const surface = useResolvedToken("--ds-bg-surface");
|
|
820
|
+
const canvas = useResolvedToken("--ds-bg-canvas");
|
|
821
|
+
const halo = useMemo(() => groundColour(surface, canvas), [surface, canvas]);
|
|
822
|
+
const text2 = useRef(null);
|
|
823
|
+
const words = String(value ?? "");
|
|
824
|
+
const [size, setSize] = useState(null);
|
|
825
|
+
useLayoutEffect(() => {
|
|
826
|
+
const drawn2 = text2.current && typeof text2.current.getBBox === "function" ? text2.current.getBBox() : null;
|
|
827
|
+
const next = drawn2 && drawn2.width > 0 ? { width: drawn2.width, height: drawn2.height } : { width: words.length * 7, height: 14 };
|
|
828
|
+
setSize((prev) => prev && prev.width === next.width && prev.height === next.height ? prev : next);
|
|
829
|
+
}, [words]);
|
|
830
|
+
const line = { across: viewBox?.height === 0 && (viewBox?.width ?? 0) > 0, up: viewBox?.width === 0 && (viewBox?.height ?? 0) > 0 };
|
|
831
|
+
const top = place !== "insideBottomRight";
|
|
832
|
+
const flip = !!size && !!plot && (line.across && top && y + size.height > plot.y + plot.height + 0.5 || line.up && x - size.width < plot.x - 0.5);
|
|
833
|
+
const placed = flip && line.across ? { x, y: (viewBox?.y ?? 0) - OFFSET, top: false, start: false } : flip && line.up ? { x: (viewBox?.x ?? 0) + OFFSET, y, top: false, start: true } : { x, y, top, start: place === "insideTopLeft" };
|
|
834
|
+
const inside = (from, length, edge, span) => from < edge ? edge - from : from + length > edge + span ? Math.max(edge - from, edge + span - (from + length)) : 0;
|
|
835
|
+
const at = size && plot ? {
|
|
836
|
+
...placed,
|
|
837
|
+
x: placed.x + inside(placed.start ? placed.x : placed.x - size.width, size.width, plot.x, plot.width),
|
|
838
|
+
y: placed.y + inside(placed.top ? placed.y : placed.y - size.height, size.height, plot.y, plot.height)
|
|
839
|
+
} : placed;
|
|
840
|
+
const register = names?.place;
|
|
841
|
+
const bounds = plot ? { top: plot.y, bottom: plot.y + plot.height } : void 0;
|
|
842
|
+
useLayoutEffect(() => {
|
|
843
|
+
if (!register || !size) return;
|
|
844
|
+
register(id, { x: at.start ? at.x : at.x - size.width, y: at.top ? at.y : at.y - size.height, ...size, order, away: at.top ? 1 : -1, ...bounds ? { bounds } : {} });
|
|
845
|
+
return () => register(id, null);
|
|
846
|
+
}, [register, id, at.x, at.y, at.top, at.start, size, order, bounds?.top, bounds?.bottom]);
|
|
847
|
+
const move = names?.moves.get(id) ?? 0;
|
|
848
|
+
return /* @__PURE__ */ jsx(ZIndexLayer, { zIndex: DefaultZIndexes.label, children: /* @__PURE__ */ jsx("g", { transform: move ? `translate(0 ${move})` : void 0, children: /* @__PURE__ */ jsx(
|
|
849
|
+
"text",
|
|
850
|
+
{
|
|
851
|
+
ref: text2,
|
|
852
|
+
className: "recharts-label",
|
|
853
|
+
"data-plot-name": "",
|
|
854
|
+
x: at.x,
|
|
855
|
+
y: at.y,
|
|
856
|
+
dy: at.top ? "0.71em" : void 0,
|
|
857
|
+
textAnchor: at.start ? "start" : "end",
|
|
858
|
+
fill: ink,
|
|
859
|
+
stroke: halo,
|
|
860
|
+
strokeWidth: 3,
|
|
861
|
+
strokeLinejoin: "round",
|
|
862
|
+
paintOrder: "stroke",
|
|
863
|
+
style: { fontSize: "var(--ds-font-size-xs)", fontFamily: "var(--ds-font-sans)" },
|
|
864
|
+
children: words
|
|
865
|
+
}
|
|
866
|
+
) }) });
|
|
867
|
+
}
|
|
868
|
+
var nameLabel = (value, position, id, order, ink) => ({
|
|
869
|
+
value,
|
|
870
|
+
position,
|
|
871
|
+
content: /* @__PURE__ */ jsx(PlotName, { id, order, place: position, ink })
|
|
872
|
+
});
|
|
873
|
+
var onAxis = (look) => look.axisId === void 0 ? {} : look.axis === "y" ? { yAxisId: look.axisId } : { xAxisId: look.axisId };
|
|
874
|
+
function referenceElements(refs, look) {
|
|
875
|
+
return (refs ?? []).map((r, i) => {
|
|
876
|
+
const axisProps = onAxis(look);
|
|
877
|
+
if (isNumber2(r.to)) {
|
|
878
|
+
const span = look.axis === "y" ? { y1: Math.min(r.value, r.to), y2: Math.max(r.value, r.to) } : { x1: Math.min(r.value, r.to), x2: Math.max(r.value, r.to) };
|
|
879
|
+
return /* @__PURE__ */ jsx(
|
|
880
|
+
ReferenceArea,
|
|
881
|
+
{
|
|
882
|
+
...span,
|
|
883
|
+
...axisProps,
|
|
884
|
+
fill: look.ink,
|
|
885
|
+
fillOpacity: 0.1,
|
|
886
|
+
stroke: "none",
|
|
887
|
+
ifOverflow: "extendDomain",
|
|
888
|
+
label: nameLabel(r.label, look.axis === "y" ? "insideTopLeft" : "insideTopRight", `reference-${i}`, i, look.ink)
|
|
889
|
+
},
|
|
890
|
+
`reference-${i}`
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
return /* @__PURE__ */ jsx(
|
|
894
|
+
ReferenceLine,
|
|
895
|
+
{
|
|
896
|
+
...look.axis === "y" ? { y: r.value } : { x: r.value },
|
|
897
|
+
...axisProps,
|
|
898
|
+
stroke: look.ink,
|
|
899
|
+
strokeDasharray: "4 3",
|
|
900
|
+
strokeWidth: 1.5,
|
|
901
|
+
ifOverflow: "extendDomain",
|
|
902
|
+
label: nameLabel(`${r.label}: ${look.format(r.value)}`, look.axis === "y" ? "insideTopRight" : "insideBottomRight", `reference-${i}`, i, look.ink)
|
|
903
|
+
},
|
|
904
|
+
`reference-${i}`
|
|
905
|
+
);
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
function referenceValues(refs) {
|
|
909
|
+
return (refs ?? []).flatMap((r) => isNumber2(r.to) ? [r.value, r.to] : [r.value]);
|
|
910
|
+
}
|
|
911
|
+
function forecastIndex(data, categoryKey, from) {
|
|
912
|
+
if (from === void 0 || from === null) return -1;
|
|
913
|
+
return data.findIndex((row) => row[categoryKey] === from || String(row[categoryKey]) === String(from));
|
|
914
|
+
}
|
|
915
|
+
var forecastHatchId = (chartId) => `${chartId.replace(/[^a-zA-Z0-9_-]/g, "")}-forecast`;
|
|
916
|
+
function ForecastHatch({ id, ink }) {
|
|
917
|
+
return /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("pattern", { id, width: 6, height: 6, patternUnits: "userSpaceOnUse", patternTransform: "rotate(45)", children: /* @__PURE__ */ jsx("line", { x1: 0, y1: 0, x2: 0, y2: 6, stroke: ink, strokeWidth: 1.5, strokeOpacity: 0.25 }) }) });
|
|
918
|
+
}
|
|
919
|
+
function forecastElements(categories, from, label, look) {
|
|
920
|
+
if (from < 0 || from >= categories.length || look.scale === "point" && categories.length < 2) return [];
|
|
921
|
+
const start = look.scale === "point" ? Math.max(0, from - 1) : from;
|
|
922
|
+
const first = categories[start], last = categories[categories.length - 1];
|
|
923
|
+
const span = look.scale === "point" ? (categories.length - 1 - start) / Math.max(1, categories.length - 1) : (categories.length - start) / categories.length;
|
|
924
|
+
const wide = span >= 0.25;
|
|
925
|
+
const elements = [
|
|
926
|
+
/* @__PURE__ */ jsx(
|
|
927
|
+
ReferenceArea,
|
|
928
|
+
{
|
|
929
|
+
...look.axis === "y" ? { x1: first, x2: last } : { y1: first, y2: last },
|
|
930
|
+
...onAxis(look),
|
|
931
|
+
fill: `url(#${look.hatch})`,
|
|
932
|
+
stroke: "none",
|
|
933
|
+
label: nameLabel(label, look.axis === "y" ? wide ? "insideTopLeft" : "insideTopRight" : wide ? "insideTopRight" : "insideBottomRight", "forecast", Number.MAX_SAFE_INTEGER, look.ink)
|
|
934
|
+
},
|
|
935
|
+
"forecast"
|
|
936
|
+
)
|
|
937
|
+
];
|
|
938
|
+
if (from > 0) {
|
|
939
|
+
elements.push(
|
|
940
|
+
/* @__PURE__ */ jsx(
|
|
941
|
+
ReferenceLine,
|
|
942
|
+
{
|
|
943
|
+
...look.axis === "y" ? { x: first } : { y: first },
|
|
944
|
+
...onAxis(look),
|
|
945
|
+
position: look.scale === "band" ? "start" : void 0,
|
|
946
|
+
stroke: look.ink,
|
|
947
|
+
strokeDasharray: "2 3"
|
|
948
|
+
},
|
|
949
|
+
"forecast-start"
|
|
950
|
+
)
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
return elements;
|
|
954
|
+
}
|
|
955
|
+
function ChartTable({ table, name, labelledBy }) {
|
|
956
|
+
const [frame, setFrame] = useState(null);
|
|
957
|
+
useKeyboardScroll(frame, name, true, false, "both");
|
|
958
|
+
return /* @__PURE__ */ jsxs("div", { "data-chart-table": "", className: "flex min-h-0 flex-1 flex-col gap-ds-2", children: [
|
|
959
|
+
/* @__PURE__ */ jsx(
|
|
960
|
+
"div",
|
|
961
|
+
{
|
|
962
|
+
ref: setFrame,
|
|
963
|
+
className: cn(
|
|
964
|
+
"min-h-0 max-h-[var(--ds-chart-height-lg)] overflow-auto rounded-[var(--ds-radius-md)]",
|
|
965
|
+
"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
|
|
966
|
+
FOCUS_INSET
|
|
967
|
+
),
|
|
968
|
+
children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-sm", "aria-labelledby": labelledBy, "aria-label": labelledBy ? void 0 : name, children: [
|
|
969
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-[var(--ds-bg-muted)]", children: /* @__PURE__ */ jsx("tr", { children: table.columns.map((column, i) => /* @__PURE__ */ jsx(
|
|
970
|
+
"th",
|
|
971
|
+
{
|
|
972
|
+
scope: "col",
|
|
973
|
+
className: cn("px-ds-3 py-ds-2 text-xs font-semibold text-[var(--ds-text-secondary)] whitespace-nowrap", table.numeric[i] ? "text-right" : "text-left"),
|
|
974
|
+
children: column
|
|
975
|
+
},
|
|
976
|
+
i
|
|
977
|
+
)) }) }),
|
|
978
|
+
/* @__PURE__ */ jsx("tbody", { children: table.rows.map((row, r) => /* @__PURE__ */ jsx("tr", { className: "border-t-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", children: row.map((cell, i) => i === (table.rowHeader ?? 0) ? /* @__PURE__ */ jsx("th", { scope: "row", className: "px-ds-3 py-ds-2 text-left font-normal text-[var(--ds-text-primary)]", children: cell }, i) : /* @__PURE__ */ jsx("td", { className: cn("px-ds-3 py-ds-2 text-[var(--ds-text-primary)]", table.numeric[i] && "text-right tabular-nums whitespace-nowrap"), children: cell }, i)) }, r)) })
|
|
979
|
+
] })
|
|
980
|
+
}
|
|
981
|
+
),
|
|
982
|
+
table.notes.length > 0 && /* @__PURE__ */ jsx("ul", { "data-chart-notes": "", className: "text-xs text-[var(--ds-text-secondary)]", children: table.notes.map((note, i) => /* @__PURE__ */ jsx("li", { children: note }, i)) })
|
|
983
|
+
] });
|
|
984
|
+
}
|
|
384
985
|
var CHART_HEIGHTS = {
|
|
385
986
|
sm: "var(--ds-chart-height-sm)",
|
|
386
987
|
md: "var(--ds-chart-height-md)",
|
|
@@ -450,17 +1051,22 @@ var ChartWrapper = forwardRef(function ChartWrapper2({
|
|
|
450
1051
|
className,
|
|
451
1052
|
children,
|
|
452
1053
|
formatValue,
|
|
453
|
-
"aria-label": ariaLabel
|
|
1054
|
+
"aria-label": ariaLabel,
|
|
1055
|
+
access,
|
|
1056
|
+
description,
|
|
1057
|
+
marks
|
|
454
1058
|
}, ref) {
|
|
455
1059
|
const container = useContext(ChartContainerContext);
|
|
456
1060
|
const rootRef = useRef(null);
|
|
457
|
-
const
|
|
1061
|
+
const [plotEl, setPlotEl] = useState(null);
|
|
1062
|
+
const [view, setView] = useState("chart");
|
|
1063
|
+
const descriptionId = useId();
|
|
458
1064
|
const [legend, setLegend] = useState(null);
|
|
459
1065
|
const [live, setLive] = useState(null);
|
|
460
|
-
const frame = useMemo(() => ({ legend, live, formatValue }), [legend, live, formatValue]);
|
|
461
|
-
const [
|
|
1066
|
+
const frame = useMemo(() => ({ legend, live, formatValue, marks }), [legend, live, formatValue, marks]);
|
|
1067
|
+
const [drawn2, setDrawn] = useState(null);
|
|
462
1068
|
useLayoutEffect(() => {
|
|
463
|
-
const plot =
|
|
1069
|
+
const plot = plotEl;
|
|
464
1070
|
if (!plot || typeof ResizeObserver === "undefined") return;
|
|
465
1071
|
const observer = new ResizeObserver((entries) => {
|
|
466
1072
|
const box = entries[entries.length - 1]?.contentRect;
|
|
@@ -469,14 +1075,16 @@ var ChartWrapper = forwardRef(function ChartWrapper2({
|
|
|
469
1075
|
});
|
|
470
1076
|
observer.observe(plot);
|
|
471
1077
|
return () => observer.disconnect();
|
|
472
|
-
}, []);
|
|
1078
|
+
}, [plotEl]);
|
|
473
1079
|
const fill = container?.fillArea === true;
|
|
474
1080
|
const fixed = container?.height ?? fixedChartHeight(height);
|
|
475
1081
|
const plotStyle = fill ? { flex: "1 1 0%", minHeight: 0 } : plotBoxStyle(size, fixed);
|
|
476
|
-
const
|
|
1082
|
+
const sentence = description?.trim() ? description : access?.description;
|
|
1083
|
+
const describedBy = sentence ? { "aria-describedby": descriptionId } : {};
|
|
1084
|
+
const named = ariaLabel ? { role: "img", "aria-label": ariaLabel, ...describedBy } : container?.titleId ? { role: "img", "aria-labelledby": container.titleId, ...describedBy } : {};
|
|
477
1085
|
useEffect(() => {
|
|
478
1086
|
const root = rootRef.current;
|
|
479
|
-
const plot =
|
|
1087
|
+
const plot = plotEl;
|
|
480
1088
|
if (!root || !plot || !categoryAxis || typeof MutationObserver === "undefined") return;
|
|
481
1089
|
const selector = `.recharts-${categoryAxis}Axis-tick-labels .recharts-cartesian-axis-tick-value`;
|
|
482
1090
|
const count = () => {
|
|
@@ -490,7 +1098,22 @@ var ChartWrapper = forwardRef(function ChartWrapper2({
|
|
|
490
1098
|
const observer = new MutationObserver(count);
|
|
491
1099
|
observer.observe(plot, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["data-cut"] });
|
|
492
1100
|
return () => observer.disconnect();
|
|
493
|
-
}, [categoryAxis]);
|
|
1101
|
+
}, [categoryAxis, plotEl]);
|
|
1102
|
+
const register = container?.register;
|
|
1103
|
+
useLayoutEffect(() => register?.(), [register]);
|
|
1104
|
+
const switcher = access ? /* @__PURE__ */ jsx(FormChangeCtx, { value: null, children: /* @__PURE__ */ jsx(
|
|
1105
|
+
SegmentedControl,
|
|
1106
|
+
{
|
|
1107
|
+
size: "xs",
|
|
1108
|
+
"aria-label": `Show ${ariaLabel || container?.title || "the chart"} as`,
|
|
1109
|
+
value: view,
|
|
1110
|
+
onChange: (v) => setView(v === "table" ? "table" : "chart"),
|
|
1111
|
+
options: [{ value: "chart", label: "Chart" }, { value: "table", label: "Table" }]
|
|
1112
|
+
}
|
|
1113
|
+
) }) : null;
|
|
1114
|
+
const actions = container?.actions && container.charts === 1 ? container.actions : null;
|
|
1115
|
+
const sentenceShown = !fill || view === "table";
|
|
1116
|
+
const sentenceLine = sentence ? /* @__PURE__ */ jsx("p", { id: descriptionId, "data-chart-description": "", className: sentenceShown ? "min-w-0 text-sm text-[var(--ds-text-secondary)]" : "sr-only", children: sentence }) : null;
|
|
494
1117
|
return /* @__PURE__ */ jsxs(
|
|
495
1118
|
"div",
|
|
496
1119
|
{
|
|
@@ -501,10 +1124,16 @@ var ChartWrapper = forwardRef(function ChartWrapper2({
|
|
|
501
1124
|
className: cn("relative flex w-full flex-col", fill && "h-full", className),
|
|
502
1125
|
style: { containerType: "inline-size", minInlineSize: "min(100%, 16rem)", ...fill ? {} : { flex: "1 0 auto" } },
|
|
503
1126
|
children: [
|
|
504
|
-
/* @__PURE__ */ jsxs(
|
|
1127
|
+
(sentenceLine && sentenceShown || switcher && !actions) && /* @__PURE__ */ jsxs("div", { "data-chart-head": "", className: "mb-ds-3 flex items-start justify-between gap-ds-3", children: [
|
|
1128
|
+
sentenceShown && sentenceLine,
|
|
1129
|
+
!actions && switcher && /* @__PURE__ */ jsx("div", { className: "ml-auto shrink-0", children: switcher })
|
|
1130
|
+
] }),
|
|
1131
|
+
!sentenceShown && sentenceLine,
|
|
1132
|
+
actions && switcher && createPortal(switcher, actions),
|
|
1133
|
+
view === "chart" || !access ? /* @__PURE__ */ jsxs(
|
|
505
1134
|
"div",
|
|
506
1135
|
{
|
|
507
|
-
ref:
|
|
1136
|
+
ref: setPlotEl,
|
|
508
1137
|
"data-chart-plot": "",
|
|
509
1138
|
className: "relative w-full",
|
|
510
1139
|
style: plotStyle,
|
|
@@ -512,10 +1141,10 @@ var ChartWrapper = forwardRef(function ChartWrapper2({
|
|
|
512
1141
|
...named,
|
|
513
1142
|
children: [
|
|
514
1143
|
axisTitle && /* @__PURE__ */ jsx("span", { "data-chart-axis-title": "", className: "absolute left-0 top-0 whitespace-nowrap text-xs text-[var(--ds-text-muted)] font-[family-name:var(--ds-font-sans)] leading-normal", children: axisTitle }),
|
|
515
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-0", style:
|
|
1144
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0", style: drawn2 ? { minWidth: drawn2.width, minHeight: drawn2.height } : void 0, children: /* @__PURE__ */ jsx(ChartFrameContext.Provider, { value: frame, children: /* @__PURE__ */ jsx(PlotNames, { children }) }) })
|
|
516
1145
|
]
|
|
517
1146
|
}
|
|
518
|
-
),
|
|
1147
|
+
) : /* @__PURE__ */ jsx(ChartTable, { table: access.table, name: `${ariaLabel || container?.title || "The chart"}: values`, labelledBy: ariaLabel ? void 0 : container?.titleId }),
|
|
519
1148
|
/* @__PURE__ */ jsx("div", { ref: setLegend, "data-chart-legend": "" }),
|
|
520
1149
|
/* @__PURE__ */ jsx("div", { ref: setLive, role: "status", "aria-live": "polite", className: "sr-only" }),
|
|
521
1150
|
/* @__PURE__ */ jsx(
|
|
@@ -531,76 +1160,6 @@ var ChartWrapper = forwardRef(function ChartWrapper2({
|
|
|
531
1160
|
}
|
|
532
1161
|
);
|
|
533
1162
|
});
|
|
534
|
-
var TICK_COUNT = 5;
|
|
535
|
-
var TIME_UNITS = [["hour", 36e5], ["minute", 6e4], ["second", 1e3], ["millisecond", 1]];
|
|
536
|
-
var BYTE_UNITS = [["petabyte", 1e15], ["terabyte", 1e12], ["gigabyte", 1e9], ["megabyte", 1e6], ["kilobyte", 1e3], ["byte", 1]];
|
|
537
|
-
var MAX_FRACTION = 10;
|
|
538
|
-
function exact(format, maxDigits = MAX_FRACTION) {
|
|
539
|
-
for (let digits = 0; digits < maxDigits; digits++) {
|
|
540
|
-
const label = format(digits);
|
|
541
|
-
if (label === format(digits + 6)) return label;
|
|
542
|
-
}
|
|
543
|
-
return format(maxDigits);
|
|
544
|
-
}
|
|
545
|
-
var unitFormat = (unit, value, locale) => (digits) => new Intl.NumberFormat(locale, { style: "unit", unit, unitDisplay: "short", maximumFractionDigits: digits }).format(value);
|
|
546
|
-
var unitsOf = (format) => format === "bytes" ? BYTE_UNITS : TIME_UNITS;
|
|
547
|
-
function unitReached(units, value) {
|
|
548
|
-
return units.find(([, size]) => Math.abs(value) >= size) ?? units[units.length - 1];
|
|
549
|
-
}
|
|
550
|
-
function axisUnit(units, ticks) {
|
|
551
|
-
const largest = Math.max(0, ...ticks.map(Math.abs));
|
|
552
|
-
const exactIn = (size) => ticks.every((t) => Math.abs(t / size * 100 - Math.round(t / size * 100)) < 1e-6);
|
|
553
|
-
return units.find(([, size]) => largest / size >= 1 && exactIn(size)) ?? units[units.length - 1];
|
|
554
|
-
}
|
|
555
|
-
function valueAxisTicks(values) {
|
|
556
|
-
const finite = values.filter(Number.isFinite);
|
|
557
|
-
if (!finite.length) return [];
|
|
558
|
-
const lo = Math.min(0, ...finite);
|
|
559
|
-
const hi = Math.max(0, ...finite);
|
|
560
|
-
if (lo === hi) return [lo];
|
|
561
|
-
return getNiceTickValues([lo, hi], TICK_COUNT, true).map(Number);
|
|
562
|
-
}
|
|
563
|
-
function axisTickFormatter(values, format, locale) {
|
|
564
|
-
if (typeof format === "function") return format;
|
|
565
|
-
if (format === "percent") return (value) => exact((d) => new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: d }).format(value / 100));
|
|
566
|
-
if (format === "milliseconds" || format === "bytes") {
|
|
567
|
-
const [unit, size] = axisUnit(unitsOf(format), valueAxisTicks(values));
|
|
568
|
-
return (value) => exact(unitFormat(unit, value / size, locale));
|
|
569
|
-
}
|
|
570
|
-
const compact = (value) => exact((d) => new Intl.NumberFormat(locale, { notation: "compact", maximumFractionDigits: d }).format(value));
|
|
571
|
-
const plain = (value) => exact((d) => new Intl.NumberFormat(locale, { maximumFractionDigits: d }).format(value));
|
|
572
|
-
const largest = Math.max(0, ...values.filter(Number.isFinite).map(Math.abs));
|
|
573
|
-
return largest >= COMPACT_FROM ? compact : plain;
|
|
574
|
-
}
|
|
575
|
-
var COMPACT_FROM = 1e4;
|
|
576
|
-
function fullValueFormatter(format, locale) {
|
|
577
|
-
if (typeof format === "function") return format;
|
|
578
|
-
if (format === void 0 || format === "number") return (value) => value.toLocaleString(locale);
|
|
579
|
-
if (format === "percent") return (value) => exact((d) => new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: d }).format(value / 100), 4);
|
|
580
|
-
return (value) => {
|
|
581
|
-
const [unit, size] = unitReached(unitsOf(format), value);
|
|
582
|
-
return exact(unitFormat(unit, value / size, locale), 4);
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
function axisExtent(data, series) {
|
|
586
|
-
return data.flatMap((row) => {
|
|
587
|
-
const own = [];
|
|
588
|
-
const up = /* @__PURE__ */ new Map();
|
|
589
|
-
const down = /* @__PURE__ */ new Map();
|
|
590
|
-
for (const s of series) {
|
|
591
|
-
const raw = row[s.dataKey];
|
|
592
|
-
const v = typeof raw === "number" ? raw : typeof raw === "string" && raw.trim() !== "" ? Number(raw) : NaN;
|
|
593
|
-
if (!Number.isFinite(v)) continue;
|
|
594
|
-
if (s.stack == null) own.push(v);
|
|
595
|
-
else if (v >= 0) up.set(s.stack, (up.get(s.stack) ?? 0) + v);
|
|
596
|
-
else down.set(s.stack, (down.get(s.stack) ?? 0) + v);
|
|
597
|
-
}
|
|
598
|
-
return [...own, ...up.values(), ...down.values()];
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
function seriesValues(data, keys, stacked = false) {
|
|
602
|
-
return axisExtent(data, keys.map((dataKey) => ({ dataKey, stack: stacked ? "stack" : null })));
|
|
603
|
-
}
|
|
604
1163
|
var hasLabel = (label) => label != null && label !== "" && typeof label !== "number";
|
|
605
1164
|
var entryName = (entry, label) => !hasLabel(label) && typeof entry.payload?.name === "string" && entry.payload.name || entry.name;
|
|
606
1165
|
var entryValue = (entry, format, locale) => typeof entry.value === "number" ? format ? format(entry.value, entry.dataKey) : entry.value.toLocaleString(locale) : entry.value;
|
|
@@ -617,12 +1176,15 @@ function tooltipAnnouncement(label, payload, format, locale = formatLocale()) {
|
|
|
617
1176
|
const values = payload.map((entry) => `${entryName(entry, label)} ${entryValue(entry, format, locale)}`).join(", ");
|
|
618
1177
|
return hasLabel(label) ? `${String(label)}: ${values}` : values;
|
|
619
1178
|
}
|
|
1179
|
+
function plainColour(value) {
|
|
1180
|
+
return typeof value === "string" && value.trim() !== "" && !value.trim().startsWith("url(") ? value : void 0;
|
|
1181
|
+
}
|
|
620
1182
|
function ChartTooltipContent({ active, payload, label }) {
|
|
621
1183
|
const bgSurface = useResolvedToken("--ds-bg-surface");
|
|
622
1184
|
const borderSubtle = useResolvedToken("--ds-border-subtle");
|
|
623
1185
|
const textPrimary = useResolvedToken("--ds-text-primary");
|
|
624
1186
|
const textSecondary = useResolvedToken("--ds-text-secondary");
|
|
625
|
-
const { live, formatValue } = useContext(ChartFrameContext);
|
|
1187
|
+
const { live, formatValue, marks } = useContext(ChartFrameContext);
|
|
626
1188
|
const locale = useFormatLocale();
|
|
627
1189
|
const on = !!active && !!payload?.length;
|
|
628
1190
|
const showLabel = hasLabel(label);
|
|
@@ -638,9 +1200,10 @@ function ChartTooltipContent({ active, payload, label }) {
|
|
|
638
1200
|
children: [
|
|
639
1201
|
showLabel && /* @__PURE__ */ jsx("p", { className: "font-medium mb-ds-1", style: { color: textPrimary }, children: label }),
|
|
640
1202
|
payload.map((entry, i) => {
|
|
641
|
-
const dotColor = entry.payload?.fill
|
|
1203
|
+
const dotColor = plainColour(entry.payload?.fill) ?? plainColour(entry.color) ?? textSecondary;
|
|
1204
|
+
const mark = (entry.graphicalItemId ? marks?.[entry.graphicalItemId] : void 0) ?? (entry.dataKey ? marks?.[entry.dataKey] : void 0) ?? marks?.[String(entry.name)] ?? marks?.[String(entry.payload?.name)];
|
|
642
1205
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", style: { marginTop: i > 0 ? 2 : 0 }, children: [
|
|
643
|
-
/* @__PURE__ */ jsx(
|
|
1206
|
+
mark ? /* @__PURE__ */ jsx(SeriesSwatch, { mark, color: dotColor }) : /* @__PURE__ */ jsx(
|
|
644
1207
|
"span",
|
|
645
1208
|
{
|
|
646
1209
|
className: "shrink-0 rounded-[var(--ds-radius-full)]",
|
|
@@ -671,17 +1234,22 @@ function swatchEdge(entry) {
|
|
|
671
1234
|
}
|
|
672
1235
|
function ChartLegendContent({ payload }) {
|
|
673
1236
|
const textSecondary = useResolvedToken("--ds-text-secondary");
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
1237
|
+
const { marks } = useContext(ChartFrameContext);
|
|
1238
|
+
const entries = payload?.filter((entry) => entry.type !== "none") ?? [];
|
|
1239
|
+
if (!entries.length) return null;
|
|
1240
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-center gap-y-ds-1 gap-x-ds-4 font-[family-name:var(--ds-font-sans)] text-sm", children: entries.map((entry, i) => {
|
|
1241
|
+
const mark = (entry.dataKey ? marks?.[entry.dataKey] : void 0) ?? marks?.[entry.value];
|
|
1242
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-1.5", children: [
|
|
1243
|
+
mark ? /* @__PURE__ */ jsx(SeriesSwatch, { mark, color: entry.color }) : /* @__PURE__ */ jsx(
|
|
1244
|
+
"span",
|
|
1245
|
+
{
|
|
1246
|
+
className: "shrink-0 rounded-[var(--ds-radius-full)]",
|
|
1247
|
+
style: { width: 10, height: 10, backgroundColor: entry.color, border: `1.5px solid ${swatchEdge(entry)}` }
|
|
1248
|
+
}
|
|
1249
|
+
),
|
|
1250
|
+
/* @__PURE__ */ jsx("span", { "data-legend-label": "", style: { color: textSecondary }, children: entry.value })
|
|
1251
|
+
] }, i);
|
|
1252
|
+
}) });
|
|
685
1253
|
}
|
|
686
1254
|
function seriesSorter(order, by = "dataKey") {
|
|
687
1255
|
return (item) => {
|
|
@@ -726,17 +1294,17 @@ function xAxisTick(props) {
|
|
|
726
1294
|
var LINE_EM = 1.2;
|
|
727
1295
|
var NAME_GAP = 8;
|
|
728
1296
|
var NAME_COLUMN_INSET = 12;
|
|
729
|
-
function namesTick(
|
|
1297
|
+
function namesTick(measure2, orientation, column) {
|
|
730
1298
|
return function NamesTick(props) {
|
|
731
1299
|
const { x, y, payload, width, visibleTicksCount, className, fill, fontSize, fontFamily } = props;
|
|
732
1300
|
const name = String(payload?.value ?? "");
|
|
733
1301
|
const room = orientation === "x" ? (Number(width) || 0) / Math.max(1, Number(visibleTicksCount) || 1) - NAME_GAP : (column ?? (Number(width) || 0)) - NAME_COLUMN_INSET;
|
|
734
|
-
const fit = fitLabel(name, room,
|
|
735
|
-
const
|
|
1302
|
+
const fit = fitLabel(name, room, measure2);
|
|
1303
|
+
const text2 = { fill, fontSize, fontFamily, stroke: "none" };
|
|
736
1304
|
const firstDy = orientation === "x" ? 0.71 : 0.355 - (fit.lines.length - 1) * LINE_EM / 2;
|
|
737
1305
|
return /* @__PURE__ */ jsxs("g", { className, "data-cut": fit.cut ? "" : void 0, "data-lines": fit.lines.length, children: [
|
|
738
1306
|
/* @__PURE__ */ jsx("title", { children: name }),
|
|
739
|
-
fit.lines.map((line, i) => /* @__PURE__ */ jsx("text", { x, y, dy: `${firstDy + i * LINE_EM}em`, textAnchor: orientation === "x" ? "middle" : "end", ...
|
|
1307
|
+
fit.lines.map((line, i) => /* @__PURE__ */ jsx("text", { x, y, dy: `${firstDy + i * LINE_EM}em`, textAnchor: orientation === "x" ? "middle" : "end", ...text2, children: line }, i))
|
|
740
1308
|
] });
|
|
741
1309
|
};
|
|
742
1310
|
}
|
|
@@ -750,8 +1318,8 @@ function useAxisMeasure(ref, enabled = true) {
|
|
|
750
1318
|
const update = (fresh = false) => {
|
|
751
1319
|
if (!live) return;
|
|
752
1320
|
const measured = el.clientWidth;
|
|
753
|
-
const
|
|
754
|
-
const row =
|
|
1321
|
+
const probe2 = el.querySelector("[data-chart-probe]");
|
|
1322
|
+
const row = probe2?.getBoundingClientRect().height ?? 0;
|
|
755
1323
|
const next = createAxisMeasurer(el);
|
|
756
1324
|
setState((prev) => {
|
|
757
1325
|
const width = measured > 0 ? measured : prev.width;
|
|
@@ -779,10 +1347,10 @@ function useAxisMeasure(ref, enabled = true) {
|
|
|
779
1347
|
}, [ref, enabled]);
|
|
780
1348
|
return state;
|
|
781
1349
|
}
|
|
782
|
-
function valueAxisWidth(values,
|
|
1350
|
+
function valueAxisWidth(values, measure2, format = String) {
|
|
783
1351
|
const ticks = valueAxisTicks(values);
|
|
784
|
-
if (!ticks.length) return Math.ceil(
|
|
785
|
-
return Math.ceil(Math.max(...ticks.map((t) =>
|
|
1352
|
+
if (!ticks.length) return Math.ceil(measure2(format(0))) + 8;
|
|
1353
|
+
return Math.ceil(Math.max(...ticks.map((t) => measure2(format(t))))) + 8;
|
|
786
1354
|
}
|
|
787
1355
|
var SWITCH_HYSTERESIS = 32;
|
|
788
1356
|
function useSidewaysSwitch(enabled, names, m, axisWidth, sideMargins) {
|
|
@@ -862,7 +1430,7 @@ function anyStack(bars, stacked) {
|
|
|
862
1430
|
function barGroups(bars, stacked) {
|
|
863
1431
|
return Math.max(1, new Set(bars.map((bar, i) => barStackOf(bar, stacked) ?? `\0${i}`)).size);
|
|
864
1432
|
}
|
|
865
|
-
function barShape(bars, index, stacked, radius, horizontal = false) {
|
|
1433
|
+
function barShape(bars, index, stacked, radius, horizontal = false, forecast) {
|
|
866
1434
|
const round = horizontal ? [0, radius, radius, 0] : [radius, radius, 0, 0];
|
|
867
1435
|
const square = [0, 0, 0, 0];
|
|
868
1436
|
const stack = barStackOf(bars[index], stacked);
|
|
@@ -875,7 +1443,8 @@ function barShape(bars, index, stacked, radius, horizontal = false) {
|
|
|
875
1443
|
const value = Number(row[b.dataKey]);
|
|
876
1444
|
return !(down ? value < 0 : value > 0);
|
|
877
1445
|
});
|
|
878
|
-
|
|
1446
|
+
const ahead = forecast && forecast.from >= 0 && (p.index ?? -1) >= forecast.from ? { ...forecast.fill ? { fill: forecast.fill } : { fillOpacity: AHEAD_STRENGTH }, stroke: forecast.edge, strokeWidth: 1.5, strokeDasharray: "3 2" } : {};
|
|
1447
|
+
return /* @__PURE__ */ jsx(Rectangle, { ...withoutRowFields(props), ...ahead, radius: onTop ? round : square });
|
|
879
1448
|
};
|
|
880
1449
|
return Shape;
|
|
881
1450
|
}
|
|
@@ -901,16 +1470,21 @@ var BarChart = markChart(forwardRef(function BarChart2({
|
|
|
901
1470
|
valueFormat,
|
|
902
1471
|
animated,
|
|
903
1472
|
"aria-label": ariaLabel,
|
|
904
|
-
className
|
|
1473
|
+
className,
|
|
1474
|
+
description,
|
|
1475
|
+
references,
|
|
1476
|
+
forecastFrom,
|
|
1477
|
+
forecastLabel = "Forecast"
|
|
905
1478
|
}, ref) {
|
|
906
1479
|
const wrapperRef = useRef(null);
|
|
1480
|
+
const chartId = useId();
|
|
907
1481
|
const labels = useMemo(() => (data ?? []).map((d) => String(d[xAxisKey] ?? "")), [data, xAxisKey]);
|
|
908
1482
|
const locale = useFormatLocale();
|
|
909
1483
|
const hydrated = useHydrated();
|
|
910
1484
|
const kind = categoryKind2 ?? categoryKind((data ?? []).map((d) => d[xAxisKey]), hydrated ? void 0 : locale);
|
|
911
1485
|
const names = kind === "names";
|
|
912
1486
|
const measured = useAxisMeasure(wrapperRef, (data?.length ?? 0) > 0);
|
|
913
|
-
const values = useMemo(() => valueExtent(data ?? [], bars, stacked), [data, bars, stacked]);
|
|
1487
|
+
const values = useMemo(() => [...valueExtent(data ?? [], bars, stacked), ...referenceValues(references)], [data, bars, stacked, references]);
|
|
914
1488
|
const tickFormat = useMemo(() => axisTickFormatter(values, valueFormat, locale), [values, valueFormat, locale]);
|
|
915
1489
|
const fullFormat = useMemo(() => fullValueFormatter(valueFormat, locale), [valueFormat, locale]);
|
|
916
1490
|
const axisWidth = useMemo(
|
|
@@ -946,6 +1520,31 @@ var BarChart = markChart(forwardRef(function BarChart2({
|
|
|
946
1520
|
axis: titles ? `calc(${CHART_ROW} + ${titles} * ${TITLE_LINE_CSS})` : CHART_ROW
|
|
947
1521
|
} : { model: "ratio" };
|
|
948
1522
|
const maxBarSize = sideways ? Math.max(4, Math.round(measured.rowPx * perCategory * 0.7 / groups)) : void 0;
|
|
1523
|
+
const surface = useResolvedToken("--ds-bg-surface");
|
|
1524
|
+
const canvas = useResolvedToken("--ds-bg-canvas");
|
|
1525
|
+
const ink = useResolvedToken("--ds-text-secondary");
|
|
1526
|
+
const fills = bars.map((bar, i) => bar.soft ? softColors[i] : solidColors[i]);
|
|
1527
|
+
const patterns2 = bars.map((_, i) => seriesPattern(chartId, i, bars.length));
|
|
1528
|
+
const aheadBarFill = (fill, pattern) => {
|
|
1529
|
+
const faint = aheadFill(fill, surface, canvas);
|
|
1530
|
+
return faint && pattern ? `url(#${aheadPattern(pattern)})` : faint;
|
|
1531
|
+
};
|
|
1532
|
+
const marks = useMemo(
|
|
1533
|
+
() => Object.fromEntries(bars.map((bar, i) => [bar.dataKey, { kind: "fill", color: bar.soft ? softColors[i] : solidColors[i], edge: solidColors[i], pattern: seriesPattern(chartId, i, bars.length) }])),
|
|
1534
|
+
[bars, softColors, solidColors, chartId]
|
|
1535
|
+
);
|
|
1536
|
+
const forecastAt = forecastIndex(data ?? [], xAxisKey, forecastFrom);
|
|
1537
|
+
const access = useMemo(() => cartesianAccess({
|
|
1538
|
+
kind: "Bar chart",
|
|
1539
|
+
data: data ?? [],
|
|
1540
|
+
categoryKey: xAxisKey,
|
|
1541
|
+
categoryHeading: categoryTitle,
|
|
1542
|
+
series: bars.map((b) => ({ dataKey: b.dataKey, label: b.label ?? b.dataKey })),
|
|
1543
|
+
format: fullFormat,
|
|
1544
|
+
over: names ? "names" : "scale",
|
|
1545
|
+
references,
|
|
1546
|
+
forecast: forecastAt >= 0 ? { from: forecastAt, label: forecastLabel } : void 0
|
|
1547
|
+
}), [data, xAxisKey, categoryTitle, bars, fullFormat, names, references, forecastAt, forecastLabel]);
|
|
949
1548
|
if (!data?.length) return null;
|
|
950
1549
|
return /* @__PURE__ */ jsx(
|
|
951
1550
|
ChartWrapper,
|
|
@@ -962,6 +1561,9 @@ var BarChart = markChart(forwardRef(function BarChart2({
|
|
|
962
1561
|
formatValue: fullFormat,
|
|
963
1562
|
className,
|
|
964
1563
|
"aria-label": ariaLabel,
|
|
1564
|
+
access,
|
|
1565
|
+
description,
|
|
1566
|
+
marks,
|
|
965
1567
|
children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
|
|
966
1568
|
BarChart$1,
|
|
967
1569
|
{
|
|
@@ -971,6 +1573,7 @@ var BarChart = markChart(forwardRef(function BarChart2({
|
|
|
971
1573
|
margin: { top: 4 + (topTitle ? titleLine(measured.px) : 0), right: sideways || names ? 4 : xTicks.endMargin, bottom: 4, left: 4 },
|
|
972
1574
|
accessibilityLayer: true,
|
|
973
1575
|
children: [
|
|
1576
|
+
/* @__PURE__ */ jsx(SeriesPatterns, { chartId, colors: fills, surface, ahead: forecastAt >= 0 }),
|
|
974
1577
|
showGrid && /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke, vertical: !sideways, horizontal: true }),
|
|
975
1578
|
sideways ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
976
1579
|
/* @__PURE__ */ jsx(XAxis, { type: "number", ...axisStyle.tick, axisLine: false, tickLine: false, height: "auto", label: bottomLabel, tickFormatter: tickFormat }),
|
|
@@ -1006,15 +1609,18 @@ var BarChart = markChart(forwardRef(function BarChart2({
|
|
|
1006
1609
|
] }),
|
|
1007
1610
|
showTooltip && /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltipContent, {}), cursor: { fill: "var(--ds-bg-muted)", opacity: 0.5 } }),
|
|
1008
1611
|
legendVisible && /* @__PURE__ */ jsx(ChartLegend, { order: bars.map((b) => b.dataKey) }),
|
|
1612
|
+
referenceElements(references, { axis: sideways ? "x" : "y", ink, format: (v) => fullFormat(v) }),
|
|
1613
|
+
forecastAt >= 0 && /* @__PURE__ */ jsx(ForecastHatch, { id: forecastHatchId(chartId), ink }),
|
|
1614
|
+
forecastElements(data.map((row) => row[xAxisKey]), forecastAt, forecastLabel, { axis: sideways ? "x" : "y", ink, scale: "band", hatch: forecastHatchId(chartId) }),
|
|
1009
1615
|
bars.map((bar, i) => /* @__PURE__ */ jsx(
|
|
1010
1616
|
Bar,
|
|
1011
1617
|
{
|
|
1012
1618
|
dataKey: bar.dataKey,
|
|
1013
1619
|
name: bar.label ?? bar.dataKey,
|
|
1014
|
-
fill:
|
|
1620
|
+
fill: patterns2[i] ? `url(#${patterns2[i]})` : fills[i],
|
|
1015
1621
|
stroke: bar.soft ? solidColors[i] : void 0,
|
|
1016
1622
|
strokeWidth: bar.soft ? 1 : void 0,
|
|
1017
|
-
shape: barShape(bars, i, stacked, radius, sideways),
|
|
1623
|
+
shape: barShape(bars, i, stacked, radius, sideways, forecastAt >= 0 ? { from: forecastAt, edge: solidColors[i], fill: aheadBarFill(fills[i], patterns2[i]) } : void 0),
|
|
1018
1624
|
stackId: barStackOf(bar, stacked),
|
|
1019
1625
|
maxBarSize,
|
|
1020
1626
|
isAnimationActive: isAnimated
|
|
@@ -1043,10 +1649,14 @@ var LineChart = markChart(forwardRef(function LineChart2({
|
|
|
1043
1649
|
valueFormat,
|
|
1044
1650
|
animated,
|
|
1045
1651
|
"aria-label": ariaLabel,
|
|
1046
|
-
className
|
|
1652
|
+
className,
|
|
1653
|
+
description,
|
|
1654
|
+
references,
|
|
1655
|
+
forecastFrom,
|
|
1656
|
+
forecastLabel = "Forecast"
|
|
1047
1657
|
}, ref) {
|
|
1048
1658
|
const wrapperRef = useRef(null);
|
|
1049
|
-
const values = useMemo(() => seriesValues(data ?? [], lines.map((s) => s.dataKey), false), [data, lines]);
|
|
1659
|
+
const values = useMemo(() => [...seriesValues(data ?? [], lines.map((s) => s.dataKey), false), ...referenceValues(references)], [data, lines, references]);
|
|
1050
1660
|
const locale = useFormatLocale();
|
|
1051
1661
|
const tickFormat = useMemo(() => axisTickFormatter(values, valueFormat, locale), [values, valueFormat, locale]);
|
|
1052
1662
|
const fullFormat = useMemo(() => fullValueFormatter(valueFormat, locale), [valueFormat, locale]);
|
|
@@ -1059,6 +1669,25 @@ var LineChart = markChart(forwardRef(function LineChart2({
|
|
|
1059
1669
|
const legendVisible = showLegend ?? lines.length >= 2;
|
|
1060
1670
|
const axisLabelStyle = { fontSize: "var(--ds-font-size-xs)", fontFamily: "var(--ds-font-sans)", fill: axisStyle.tick.fill };
|
|
1061
1671
|
const top = 4 + (yAxisLabel ? Math.ceil(measured.px * 1.5) + 4 : 0);
|
|
1672
|
+
const ink = useResolvedToken("--ds-text-secondary");
|
|
1673
|
+
const dashes = useMemo(() => lineDashes(lines), [lines]);
|
|
1674
|
+
const marks = useMemo(
|
|
1675
|
+
() => Object.fromEntries(lines.map((l, i) => [l.dataKey, { kind: "line", color: colors[i], dash: dashes[i] }])),
|
|
1676
|
+
[lines, colors, dashes]
|
|
1677
|
+
);
|
|
1678
|
+
const forecastAt = forecastIndex(data ?? [], xAxisKey, forecastFrom);
|
|
1679
|
+
const hatch = forecastHatchId(useId());
|
|
1680
|
+
const access = useMemo(() => cartesianAccess({
|
|
1681
|
+
kind: "Line chart",
|
|
1682
|
+
data: data ?? [],
|
|
1683
|
+
categoryKey: xAxisKey,
|
|
1684
|
+
categoryHeading: xAxisLabel,
|
|
1685
|
+
series: lines.map((l) => ({ dataKey: l.dataKey, label: l.label ?? l.dataKey })),
|
|
1686
|
+
format: fullFormat,
|
|
1687
|
+
over: "scale",
|
|
1688
|
+
references,
|
|
1689
|
+
forecast: forecastAt >= 0 ? { from: forecastAt, label: forecastLabel } : void 0
|
|
1690
|
+
}), [data, xAxisKey, xAxisLabel, lines, fullFormat, references, forecastAt, forecastLabel]);
|
|
1062
1691
|
if (!data?.length) return null;
|
|
1063
1692
|
return /* @__PURE__ */ jsx(
|
|
1064
1693
|
ChartWrapper,
|
|
@@ -1074,6 +1703,9 @@ var LineChart = markChart(forwardRef(function LineChart2({
|
|
|
1074
1703
|
formatValue: fullFormat,
|
|
1075
1704
|
className,
|
|
1076
1705
|
"aria-label": ariaLabel,
|
|
1706
|
+
access,
|
|
1707
|
+
description,
|
|
1708
|
+
marks,
|
|
1077
1709
|
children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(LineChart$1, { data, margin: { top, right: xTicks.endMargin, bottom: 4, left: 4 }, accessibilityLayer: true, children: [
|
|
1078
1710
|
showGrid && /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }),
|
|
1079
1711
|
/* @__PURE__ */ jsx(
|
|
@@ -1093,6 +1725,9 @@ var LineChart = markChart(forwardRef(function LineChart2({
|
|
|
1093
1725
|
/* @__PURE__ */ jsx(YAxis, { width: "auto", ...axisStyle.tick, axisLine: false, tickLine: false, tickFormatter: tickFormat }),
|
|
1094
1726
|
showTooltip && /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltipContent, {}) }),
|
|
1095
1727
|
legendVisible && /* @__PURE__ */ jsx(ChartLegend, { order: lines.map((l) => l.dataKey) }),
|
|
1728
|
+
referenceElements(references, { axis: "y", ink, format: (v) => fullFormat(v) }),
|
|
1729
|
+
forecastAt >= 0 && /* @__PURE__ */ jsx(ForecastHatch, { id: hatch, ink }),
|
|
1730
|
+
forecastElements(data.map((row) => row[xAxisKey]), forecastAt, forecastLabel, { axis: "y", ink, scale: "point", hatch }),
|
|
1096
1731
|
lines.map((line, i) => /* @__PURE__ */ jsx(
|
|
1097
1732
|
Line,
|
|
1098
1733
|
{
|
|
@@ -1101,7 +1736,7 @@ var LineChart = markChart(forwardRef(function LineChart2({
|
|
|
1101
1736
|
name: line.label ?? line.dataKey,
|
|
1102
1737
|
stroke: colors[i],
|
|
1103
1738
|
strokeWidth: 2,
|
|
1104
|
-
strokeDasharray:
|
|
1739
|
+
strokeDasharray: dashes[i],
|
|
1105
1740
|
dot: showDots,
|
|
1106
1741
|
activeDot: { r: 5, strokeWidth: 2 },
|
|
1107
1742
|
isAnimationActive: isAnimated
|
|
@@ -1129,10 +1764,14 @@ var AreaChart = markChart(forwardRef(function AreaChart2({
|
|
|
1129
1764
|
valueFormat,
|
|
1130
1765
|
animated,
|
|
1131
1766
|
"aria-label": ariaLabel,
|
|
1132
|
-
className
|
|
1767
|
+
className,
|
|
1768
|
+
description,
|
|
1769
|
+
references,
|
|
1770
|
+
forecastFrom,
|
|
1771
|
+
forecastLabel = "Forecast"
|
|
1133
1772
|
}, ref) {
|
|
1134
1773
|
const wrapperRef = useRef(null);
|
|
1135
|
-
const values = useMemo(() => seriesValues(data ?? [], areas.map((s) => s.dataKey), stacked), [data, areas, stacked]);
|
|
1774
|
+
const values = useMemo(() => [...seriesValues(data ?? [], areas.map((s) => s.dataKey), stacked), ...referenceValues(references)], [data, areas, stacked, references]);
|
|
1136
1775
|
const locale = useFormatLocale();
|
|
1137
1776
|
const tickFormat = useMemo(() => axisTickFormatter(values, valueFormat, locale), [values, valueFormat, locale]);
|
|
1138
1777
|
const fullFormat = useMemo(() => fullValueFormatter(valueFormat, locale), [valueFormat, locale]);
|
|
@@ -1147,6 +1786,25 @@ var AreaChart = markChart(forwardRef(function AreaChart2({
|
|
|
1147
1786
|
const legendVisible = showLegend ?? areas.length >= 2;
|
|
1148
1787
|
const axisLabelStyle = { fontSize: "var(--ds-font-size-xs)", fontFamily: "var(--ds-font-sans)", fill: axisStyle.tick.fill };
|
|
1149
1788
|
const top = 4 + (yAxisLabel ? Math.ceil(measured.px * 1.5) + 4 : 0);
|
|
1789
|
+
const ink = useResolvedToken("--ds-text-secondary");
|
|
1790
|
+
const dashes = areas.map((_, i) => seriesDash(i, areas.length));
|
|
1791
|
+
const marks = useMemo(
|
|
1792
|
+
() => Object.fromEntries(areas.map((a, i) => [a.dataKey, { kind: "line", color: solidColors[i], dash: seriesDash(i, areas.length) }])),
|
|
1793
|
+
[areas, solidColors]
|
|
1794
|
+
);
|
|
1795
|
+
const forecastAt = forecastIndex(data ?? [], xAxisKey, forecastFrom);
|
|
1796
|
+
const hatch = forecastHatchId(instanceId);
|
|
1797
|
+
const access = useMemo(() => cartesianAccess({
|
|
1798
|
+
kind: "Area chart",
|
|
1799
|
+
data: data ?? [],
|
|
1800
|
+
categoryKey: xAxisKey,
|
|
1801
|
+
categoryHeading: xAxisLabel,
|
|
1802
|
+
series: areas.map((a) => ({ dataKey: a.dataKey, label: a.label ?? a.dataKey })),
|
|
1803
|
+
format: fullFormat,
|
|
1804
|
+
over: "scale",
|
|
1805
|
+
references,
|
|
1806
|
+
forecast: forecastAt >= 0 ? { from: forecastAt, label: forecastLabel } : void 0
|
|
1807
|
+
}), [data, xAxisKey, xAxisLabel, areas, fullFormat, references, forecastAt, forecastLabel]);
|
|
1150
1808
|
if (!data?.length) return null;
|
|
1151
1809
|
return /* @__PURE__ */ jsx(
|
|
1152
1810
|
ChartWrapper,
|
|
@@ -1162,6 +1820,9 @@ var AreaChart = markChart(forwardRef(function AreaChart2({
|
|
|
1162
1820
|
formatValue: fullFormat,
|
|
1163
1821
|
className,
|
|
1164
1822
|
"aria-label": ariaLabel,
|
|
1823
|
+
access,
|
|
1824
|
+
description,
|
|
1825
|
+
marks,
|
|
1165
1826
|
children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(AreaChart$1, { data, margin: { top, right: xTicks.endMargin, bottom: 4, left: 4 }, accessibilityLayer: true, children: [
|
|
1166
1827
|
/* @__PURE__ */ jsx("defs", { children: areas.map((area, i) => /* @__PURE__ */ jsxs("linearGradient", { id: `gradient-${instanceId}-${area.dataKey}`, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
1167
1828
|
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: softColors[i], stopOpacity: 1 }),
|
|
@@ -1185,6 +1846,9 @@ var AreaChart = markChart(forwardRef(function AreaChart2({
|
|
|
1185
1846
|
/* @__PURE__ */ jsx(YAxis, { width: "auto", ...axisStyle.tick, axisLine: false, tickLine: false, tickFormatter: tickFormat }),
|
|
1186
1847
|
showTooltip && /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltipContent, {}) }),
|
|
1187
1848
|
legendVisible && /* @__PURE__ */ jsx(ChartLegend, { order: areas.map((a) => a.dataKey) }),
|
|
1849
|
+
referenceElements(references, { axis: "y", ink, format: (v) => fullFormat(v) }),
|
|
1850
|
+
forecastAt >= 0 && /* @__PURE__ */ jsx(ForecastHatch, { id: hatch, ink }),
|
|
1851
|
+
forecastElements(data.map((row) => row[xAxisKey]), forecastAt, forecastLabel, { axis: "y", ink, scale: "point", hatch }),
|
|
1188
1852
|
areas.map((area, i) => /* @__PURE__ */ jsx(
|
|
1189
1853
|
Area,
|
|
1190
1854
|
{
|
|
@@ -1193,6 +1857,7 @@ var AreaChart = markChart(forwardRef(function AreaChart2({
|
|
|
1193
1857
|
name: area.label ?? area.dataKey,
|
|
1194
1858
|
stroke: solidColors[i],
|
|
1195
1859
|
strokeWidth: 2,
|
|
1860
|
+
strokeDasharray: dashes[i],
|
|
1196
1861
|
fill: gradient ? `url(#gradient-${instanceId}-${area.dataKey})` : softColors[i],
|
|
1197
1862
|
stackId: stacked ? "stack" : void 0,
|
|
1198
1863
|
isAnimationActive: isAnimated
|
|
@@ -1212,14 +1877,26 @@ var PieChart = markChart(forwardRef(function PieChart2({
|
|
|
1212
1877
|
showLabels = false,
|
|
1213
1878
|
animated,
|
|
1214
1879
|
"aria-label": ariaLabel,
|
|
1215
|
-
className
|
|
1880
|
+
className,
|
|
1881
|
+
description
|
|
1216
1882
|
}, ref) {
|
|
1217
1883
|
const colors = useChartColors(data.length, false);
|
|
1884
|
+
const chartId = useId();
|
|
1885
|
+
const surface = useResolvedToken("--ds-bg-surface");
|
|
1886
|
+
const locale = useFormatLocale();
|
|
1887
|
+
const format = useMemo(() => fullValueFormatter(void 0, locale), [locale]);
|
|
1218
1888
|
const isAnimated = useChartAnimation(animated);
|
|
1219
|
-
const renderLabel = showLabels ? ({ percent }) => `${((
|
|
1220
|
-
const chartData = data.map((d) => ({ name: d.name, value: d.value }));
|
|
1889
|
+
const renderLabel = showLabels ? ({ percent: percent2 }) => `${((percent2 ?? 0) * 100).toFixed(0)}%` : false;
|
|
1890
|
+
const chartData = data.map((d) => ({ name: d.name, value: numberOf(d.value) ?? 0 }));
|
|
1891
|
+
const fills = data.map((_, i) => colors[i % colors.length]);
|
|
1892
|
+
const marks = useMemo(
|
|
1893
|
+
() => Object.fromEntries(data.map((d, i) => [d.name, { kind: "fill", color: colors[i % colors.length], pattern: seriesPattern(chartId, i, data.length) }])),
|
|
1894
|
+
[data, colors, chartId]
|
|
1895
|
+
);
|
|
1896
|
+
const access = useMemo(() => partsAccess({ kind: "pie", items: data.map((d) => ({ name: d.name, value: d.value })), format }), [data, format]);
|
|
1221
1897
|
if (!data?.length) return null;
|
|
1222
|
-
return /* @__PURE__ */ jsx(ChartWrapper, { ref, height, size: { model: "square" }, chart: "pie", className, "aria-label": ariaLabel, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(PieChart$1, { accessibilityLayer: true, children: [
|
|
1898
|
+
return /* @__PURE__ */ jsx(ChartWrapper, { ref, height, size: { model: "square" }, chart: "pie", className, "aria-label": ariaLabel, access, description, marks, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(PieChart$1, { accessibilityLayer: true, children: [
|
|
1899
|
+
/* @__PURE__ */ jsx(SeriesPatterns, { chartId, colors: fills, surface }),
|
|
1223
1900
|
/* @__PURE__ */ jsx(
|
|
1224
1901
|
Pie,
|
|
1225
1902
|
{
|
|
@@ -1235,7 +1912,10 @@ var PieChart = markChart(forwardRef(function PieChart2({
|
|
|
1235
1912
|
isAnimationActive: isAnimated,
|
|
1236
1913
|
fontSize: "var(--ds-font-size-xs)",
|
|
1237
1914
|
fontFamily: "var(--ds-font-sans)",
|
|
1238
|
-
children: data.map((_, i) =>
|
|
1915
|
+
children: data.map((_, i) => {
|
|
1916
|
+
const pattern = seriesPattern(chartId, i, data.length);
|
|
1917
|
+
return /* @__PURE__ */ jsx(Cell, { fill: pattern ? `url(#${pattern})` : fills[i] }, i);
|
|
1918
|
+
})
|
|
1239
1919
|
}
|
|
1240
1920
|
),
|
|
1241
1921
|
showTooltip && /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltipContent, {}) }),
|
|
@@ -1262,9 +1942,14 @@ var CompositeChart = markChart(forwardRef(function CompositeChart2({
|
|
|
1262
1942
|
lineValueFormat = valueFormat,
|
|
1263
1943
|
animated,
|
|
1264
1944
|
"aria-label": ariaLabel,
|
|
1265
|
-
className
|
|
1945
|
+
className,
|
|
1946
|
+
description,
|
|
1947
|
+
references,
|
|
1948
|
+
forecastFrom,
|
|
1949
|
+
forecastLabel = "Forecast"
|
|
1266
1950
|
}, ref) {
|
|
1267
1951
|
const wrapperRef = useRef(null);
|
|
1952
|
+
const chartId = useId();
|
|
1268
1953
|
const labels = useMemo(() => (data ?? []).map((d) => String(d[xAxisKey] ?? "")), [data, xAxisKey]);
|
|
1269
1954
|
const locale = useFormatLocale();
|
|
1270
1955
|
const hydrated = useHydrated();
|
|
@@ -1277,8 +1962,9 @@ var CompositeChart = markChart(forwardRef(function CompositeChart2({
|
|
|
1277
1962
|
...dualAxis ? [] : lines.map((l) => ({ dataKey: l.dataKey, stack: null }))
|
|
1278
1963
|
]);
|
|
1279
1964
|
const lineValues2 = (data ?? []).flatMap((row) => lines.map((l) => Number(row[l.dataKey])));
|
|
1280
|
-
|
|
1281
|
-
|
|
1965
|
+
const onLines = !bars.length && dualAxis;
|
|
1966
|
+
return [[...values2, ...onLines ? [] : referenceValues(references)], [...lineValues2, ...onLines ? referenceValues(references) : []]];
|
|
1967
|
+
}, [data, bars, lines, stacked, dualAxis, references]);
|
|
1282
1968
|
const tickFormat = useMemo(() => axisTickFormatter(values, valueFormat, locale), [values, valueFormat, locale]);
|
|
1283
1969
|
const lineTickFormat = useMemo(() => axisTickFormatter(lineValues, lineValueFormat, locale), [lineValues, lineValueFormat, locale]);
|
|
1284
1970
|
const fullFormat = useMemo(() => {
|
|
@@ -1321,6 +2007,32 @@ var CompositeChart = markChart(forwardRef(function CompositeChart2({
|
|
|
1321
2007
|
const maxBarSize = sideways ? Math.max(4, Math.round(measured.rowPx * perCategory * 0.7 / groups)) : void 0;
|
|
1322
2008
|
const barAxis = sideways ? { xAxisId: "left" } : { yAxisId: "left" };
|
|
1323
2009
|
const lineAxis = sideways ? { xAxisId: dualAxis ? "right" : "left" } : { yAxisId: dualAxis ? "right" : "left" };
|
|
2010
|
+
const valueAxisId = bars.length || !dualAxis ? "left" : "right";
|
|
2011
|
+
const surface = useResolvedToken("--ds-bg-surface");
|
|
2012
|
+
const canvas = useResolvedToken("--ds-bg-canvas");
|
|
2013
|
+
const ink = useResolvedToken("--ds-text-secondary");
|
|
2014
|
+
const patterns2 = bars.map((_, i) => seriesPattern(chartId, i, bars.length));
|
|
2015
|
+
const aheadBarFill = (fill, pattern) => {
|
|
2016
|
+
const faint = aheadFill(fill, surface, canvas);
|
|
2017
|
+
return faint && pattern ? `url(#${aheadPattern(pattern)})` : faint;
|
|
2018
|
+
};
|
|
2019
|
+
const dashes = useMemo(() => lineDashes(lines), [lines]);
|
|
2020
|
+
const marks = useMemo(() => Object.fromEntries([
|
|
2021
|
+
...bars.map((bar, i) => [bar.dataKey, { kind: "fill", color: barSoftColors[i], edge: barSolidColors[i], pattern: seriesPattern(chartId, i, bars.length) }]),
|
|
2022
|
+
...lines.map((line, i) => [line.dataKey, { kind: "line", color: lineColors[i], dash: dashes[i] }])
|
|
2023
|
+
]), [bars, lines, barSoftColors, barSolidColors, lineColors, chartId, dashes]);
|
|
2024
|
+
const forecastAt = forecastIndex(data ?? [], xAxisKey, forecastFrom);
|
|
2025
|
+
const access = useMemo(() => cartesianAccess({
|
|
2026
|
+
kind: "Bar and line chart",
|
|
2027
|
+
data: data ?? [],
|
|
2028
|
+
categoryKey: xAxisKey,
|
|
2029
|
+
categoryHeading: xAxisLabel,
|
|
2030
|
+
series: [...bars, ...lines].map((s) => ({ dataKey: s.dataKey, label: s.label ?? s.dataKey })),
|
|
2031
|
+
format: fullFormat,
|
|
2032
|
+
over: names ? "names" : "scale",
|
|
2033
|
+
references,
|
|
2034
|
+
forecast: forecastAt >= 0 ? { from: forecastAt, label: forecastLabel } : void 0
|
|
2035
|
+
}), [data, xAxisKey, xAxisLabel, bars, lines, fullFormat, names, references, forecastAt, forecastLabel]);
|
|
1324
2036
|
if (!data?.length) return null;
|
|
1325
2037
|
return /* @__PURE__ */ jsx(
|
|
1326
2038
|
ChartWrapper,
|
|
@@ -1337,6 +2049,9 @@ var CompositeChart = markChart(forwardRef(function CompositeChart2({
|
|
|
1337
2049
|
formatValue: fullFormat,
|
|
1338
2050
|
className,
|
|
1339
2051
|
"aria-label": ariaLabel,
|
|
2052
|
+
access,
|
|
2053
|
+
description,
|
|
2054
|
+
marks,
|
|
1340
2055
|
children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
|
|
1341
2056
|
ComposedChart,
|
|
1342
2057
|
{
|
|
@@ -1346,6 +2061,7 @@ var CompositeChart = markChart(forwardRef(function CompositeChart2({
|
|
|
1346
2061
|
margin: { top, right: sideways || names ? 4 : xTicks.endMargin, bottom: 4, left: 4 },
|
|
1347
2062
|
accessibilityLayer: true,
|
|
1348
2063
|
children: [
|
|
2064
|
+
/* @__PURE__ */ jsx(SeriesPatterns, { chartId, colors: barSoftColors, surface, ahead: forecastAt >= 0 }),
|
|
1349
2065
|
showGrid && /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke, vertical: !sideways, horizontal: true }),
|
|
1350
2066
|
sideways ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1351
2067
|
/* @__PURE__ */ jsx(XAxis, { xAxisId: "left", type: "number", ...axisStyle.tick, axisLine: false, tickLine: false, height: "auto", label: bottomLabel, tickFormatter: tickFormat }),
|
|
@@ -1383,15 +2099,18 @@ var CompositeChart = markChart(forwardRef(function CompositeChart2({
|
|
|
1383
2099
|
] }),
|
|
1384
2100
|
showTooltip && /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltipContent, {}) }),
|
|
1385
2101
|
showLegend && /* @__PURE__ */ jsx(ChartLegend, { order: [...bars.map((b) => b.dataKey), ...lines.map((l) => l.dataKey)] }),
|
|
2102
|
+
referenceElements(references, { axis: sideways ? "x" : "y", axisId: valueAxisId, ink, format: (v) => fullFormat(v, valueAxisId === "right" ? lines[0]?.dataKey : void 0) }),
|
|
2103
|
+
forecastAt >= 0 && /* @__PURE__ */ jsx(ForecastHatch, { id: forecastHatchId(chartId), ink }),
|
|
2104
|
+
forecastElements(data.map((row) => row[xAxisKey]), forecastAt, forecastLabel, { axis: sideways ? "x" : "y", axisId: valueAxisId, ink, scale: bars.length ? "band" : "point", hatch: forecastHatchId(chartId) }),
|
|
1386
2105
|
bars.map((bar, i) => /* @__PURE__ */ jsx(
|
|
1387
2106
|
Bar,
|
|
1388
2107
|
{
|
|
1389
2108
|
dataKey: bar.dataKey,
|
|
1390
2109
|
name: bar.label ?? bar.dataKey,
|
|
1391
|
-
fill: barSoftColors[i],
|
|
2110
|
+
fill: patterns2[i] ? `url(#${patterns2[i]})` : barSoftColors[i],
|
|
1392
2111
|
stroke: barSolidColors[i],
|
|
1393
2112
|
strokeWidth: 1,
|
|
1394
|
-
shape: barShape(bars, i, stacked, radius, sideways),
|
|
2113
|
+
shape: barShape(bars, i, stacked, radius, sideways, forecastAt >= 0 ? { from: forecastAt, edge: barSolidColors[i], fill: aheadBarFill(barSoftColors[i], patterns2[i]) } : void 0),
|
|
1395
2114
|
stackId: barStackOf(bar, stacked),
|
|
1396
2115
|
maxBarSize,
|
|
1397
2116
|
...barAxis,
|
|
@@ -1407,7 +2126,7 @@ var CompositeChart = markChart(forwardRef(function CompositeChart2({
|
|
|
1407
2126
|
name: line.label ?? line.dataKey,
|
|
1408
2127
|
stroke: lineColors[i],
|
|
1409
2128
|
strokeWidth: 2,
|
|
1410
|
-
strokeDasharray:
|
|
2129
|
+
strokeDasharray: dashes[i],
|
|
1411
2130
|
dot: false,
|
|
1412
2131
|
...lineAxis,
|
|
1413
2132
|
isAnimationActive: isAnimated
|
|
@@ -1432,7 +2151,8 @@ var ScatterChart = markChart(forwardRef(function ScatterChart2({
|
|
|
1432
2151
|
xValueFormat,
|
|
1433
2152
|
animated,
|
|
1434
2153
|
"aria-label": ariaLabel,
|
|
1435
|
-
className
|
|
2154
|
+
className,
|
|
2155
|
+
description
|
|
1436
2156
|
}, ref) {
|
|
1437
2157
|
const wrapperRef = useRef(null);
|
|
1438
2158
|
const locale = useFormatLocale();
|
|
@@ -1453,6 +2173,23 @@ var ScatterChart = markChart(forwardRef(function ScatterChart2({
|
|
|
1453
2173
|
const isAnimated = useChartAnimation(animated);
|
|
1454
2174
|
const legendVisible = showLegend ?? data.length >= 2;
|
|
1455
2175
|
const axisLabelStyle = { fontSize: "var(--ds-font-size-xs)", fontFamily: "var(--ds-font-sans)", fill: axisStyle.tick.fill };
|
|
2176
|
+
const seriesId = useId();
|
|
2177
|
+
const idOf = (i) => `${seriesId}-scatter-${i}`;
|
|
2178
|
+
const marks = useMemo(
|
|
2179
|
+
() => Object.fromEntries((data ?? []).flatMap((s, i) => {
|
|
2180
|
+
const mark = { kind: "symbol", color: colors[i], symbol: seriesSymbol(i, data.length) };
|
|
2181
|
+
return [[s.name, mark], [`${seriesId}-scatter-${i}`, mark]];
|
|
2182
|
+
})),
|
|
2183
|
+
[data, colors, seriesId]
|
|
2184
|
+
);
|
|
2185
|
+
const access = useMemo(() => pointsAccess({
|
|
2186
|
+
series: data ?? [],
|
|
2187
|
+
xHeading: xLabel ?? "x",
|
|
2188
|
+
yHeading: yLabel ?? "y",
|
|
2189
|
+
zHeading: "Size",
|
|
2190
|
+
formatX: (v) => fullFormat(v, "x"),
|
|
2191
|
+
formatY: (v) => fullFormat(v, "y")
|
|
2192
|
+
}), [data, xLabel, yLabel, fullFormat]);
|
|
1456
2193
|
if (!data?.length) return null;
|
|
1457
2194
|
return /* @__PURE__ */ jsx(
|
|
1458
2195
|
ChartWrapper,
|
|
@@ -1465,6 +2202,9 @@ var ScatterChart = markChart(forwardRef(function ScatterChart2({
|
|
|
1465
2202
|
formatValue: fullFormat,
|
|
1466
2203
|
className,
|
|
1467
2204
|
"aria-label": ariaLabel,
|
|
2205
|
+
access,
|
|
2206
|
+
description,
|
|
2207
|
+
marks,
|
|
1468
2208
|
children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
|
|
1469
2209
|
ScatterChart$1,
|
|
1470
2210
|
{
|
|
@@ -1504,11 +2244,12 @@ var ScatterChart = markChart(forwardRef(function ScatterChart2({
|
|
|
1504
2244
|
data.map((series, i) => /* @__PURE__ */ jsx(
|
|
1505
2245
|
Scatter,
|
|
1506
2246
|
{
|
|
2247
|
+
id: idOf(i),
|
|
1507
2248
|
name: series.name,
|
|
1508
2249
|
data: series.points,
|
|
1509
2250
|
fill: colors[i],
|
|
1510
2251
|
isAnimationActive: isAnimated,
|
|
1511
|
-
shape: (p) => /* @__PURE__ */ jsx(Symbols, { ...withoutRowFields(p), type:
|
|
2252
|
+
shape: (p) => /* @__PURE__ */ jsx(Symbols, { ...withoutRowFields(p), type: seriesSymbol(i, data.length) })
|
|
1512
2253
|
},
|
|
1513
2254
|
series.name
|
|
1514
2255
|
))
|
|
@@ -1530,7 +2271,8 @@ var RadarChart = markChart(forwardRef(function RadarChart2({
|
|
|
1530
2271
|
animated,
|
|
1531
2272
|
valueFormat = "number",
|
|
1532
2273
|
"aria-label": ariaLabel,
|
|
1533
|
-
className
|
|
2274
|
+
className,
|
|
2275
|
+
description
|
|
1534
2276
|
}, ref) {
|
|
1535
2277
|
const solidColors = useChartColors(dataKeys.length, false);
|
|
1536
2278
|
const softColors = useChartColors(dataKeys.length, true);
|
|
@@ -1540,8 +2282,21 @@ var RadarChart = markChart(forwardRef(function RadarChart2({
|
|
|
1540
2282
|
const locale = useFormatLocale();
|
|
1541
2283
|
const tickFormat = useMemo(() => axisTickFormatter(values, valueFormat, locale), [values, valueFormat, locale]);
|
|
1542
2284
|
const fullFormat = useMemo(() => fullValueFormatter(valueFormat, locale), [valueFormat, locale]);
|
|
2285
|
+
const dashes = dataKeys.map((_, i) => seriesDash(i, dataKeys.length));
|
|
2286
|
+
const marks = useMemo(
|
|
2287
|
+
() => Object.fromEntries(dataKeys.map((dk, i) => [dk.key, { kind: "line", color: solidColors[i], dash: seriesDash(i, dataKeys.length) }])),
|
|
2288
|
+
[dataKeys, solidColors]
|
|
2289
|
+
);
|
|
2290
|
+
const access = useMemo(() => cartesianAccess({
|
|
2291
|
+
kind: "Radar chart",
|
|
2292
|
+
data: data ?? [],
|
|
2293
|
+
categoryKey: axisKey,
|
|
2294
|
+
series: dataKeys.map((dk) => ({ dataKey: dk.key, label: dk.label ?? dk.key })),
|
|
2295
|
+
format: fullFormat,
|
|
2296
|
+
over: "names"
|
|
2297
|
+
}), [data, axisKey, dataKeys, fullFormat]);
|
|
1543
2298
|
if (!data?.length) return null;
|
|
1544
|
-
return /* @__PURE__ */ jsx(ChartWrapper, { ref, height, size: { model: "square" }, chart: "radar", className, "aria-label": ariaLabel, formatValue: fullFormat, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(RadarChart$1, { data, cx: "50%", cy: "50%", outerRadius: "75%", accessibilityLayer: true, children: [
|
|
2299
|
+
return /* @__PURE__ */ jsx(ChartWrapper, { ref, height, size: { model: "square" }, chart: "radar", className, "aria-label": ariaLabel, formatValue: fullFormat, access, description, marks, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(RadarChart$1, { data, cx: "50%", cy: "50%", outerRadius: "75%", accessibilityLayer: true, children: [
|
|
1545
2300
|
/* @__PURE__ */ jsx(PolarGrid, { stroke: "var(--ds-border-subtle)" }),
|
|
1546
2301
|
/* @__PURE__ */ jsx(
|
|
1547
2302
|
PolarAngleAxis,
|
|
@@ -1568,6 +2323,7 @@ var RadarChart = markChart(forwardRef(function RadarChart2({
|
|
|
1568
2323
|
stroke: solidColors[i],
|
|
1569
2324
|
fill: filled ? softColors[i] : "transparent",
|
|
1570
2325
|
strokeWidth: 2,
|
|
2326
|
+
strokeDasharray: dashes[i],
|
|
1571
2327
|
dot: showDots ? { r: 3, fill: solidColors[i] } : false,
|
|
1572
2328
|
isAnimationActive: isAnimated
|
|
1573
2329
|
},
|
|
@@ -1585,18 +2341,30 @@ var RadialBarChart = markChart(forwardRef(function RadialBarChart2({
|
|
|
1585
2341
|
endAngle = -270,
|
|
1586
2342
|
animated,
|
|
1587
2343
|
"aria-label": ariaLabel,
|
|
1588
|
-
className
|
|
2344
|
+
className,
|
|
2345
|
+
description
|
|
1589
2346
|
}, ref) {
|
|
1590
2347
|
const colors = useChartColors(data.length, false);
|
|
2348
|
+
const chartId = useId();
|
|
2349
|
+
const surface = useResolvedToken("--ds-bg-surface");
|
|
2350
|
+
const locale = useFormatLocale();
|
|
2351
|
+
const format = useMemo(() => fullValueFormatter(void 0, locale), [locale]);
|
|
1591
2352
|
const isAnimated = useChartAnimation(animated);
|
|
1592
2353
|
const chartData = data.map((d, i) => ({
|
|
1593
2354
|
name: d.name,
|
|
1594
|
-
|
|
2355
|
+
// a number written as text is drawn as the table and the sentence read it (Sprint 25.8.9)
|
|
2356
|
+
value: numberOf(d.value) ?? 0,
|
|
1595
2357
|
...d.max !== void 0 ? { max: d.max } : {},
|
|
1596
|
-
|
|
2358
|
+
// Sprint 25.8.9 item 4: each bar after the first fills with a pattern over its colour, as the legend shows it
|
|
2359
|
+
fill: seriesPattern(chartId, i, data.length) ? `url(#${seriesPattern(chartId, i, data.length)})` : colors[i % colors.length]
|
|
1597
2360
|
}));
|
|
2361
|
+
const marks = useMemo(
|
|
2362
|
+
() => Object.fromEntries(data.map((d, i) => [d.name, { kind: "fill", color: colors[i % colors.length], pattern: seriesPattern(chartId, i, data.length) }])),
|
|
2363
|
+
[data, colors, chartId]
|
|
2364
|
+
);
|
|
2365
|
+
const access = useMemo(() => partsAccess({ kind: "radial", items: data.map((d) => ({ name: d.name, value: d.value, ...d.max !== void 0 ? { max: d.max } : {} })), format }), [data, format]);
|
|
1598
2366
|
if (!data?.length) return null;
|
|
1599
|
-
return /* @__PURE__ */ jsx(ChartWrapper, { ref, height, size: { model: "square" }, chart: "radial-bar", className, "aria-label": ariaLabel, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
|
|
2367
|
+
return /* @__PURE__ */ jsx(ChartWrapper, { ref, height, size: { model: "square" }, chart: "radial-bar", className, "aria-label": ariaLabel, access, description, marks, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
|
|
1600
2368
|
RadialBarChart$1,
|
|
1601
2369
|
{
|
|
1602
2370
|
data: chartData,
|
|
@@ -1608,6 +2376,7 @@ var RadialBarChart = markChart(forwardRef(function RadialBarChart2({
|
|
|
1608
2376
|
endAngle,
|
|
1609
2377
|
accessibilityLayer: true,
|
|
1610
2378
|
children: [
|
|
2379
|
+
/* @__PURE__ */ jsx(SeriesPatterns, { chartId, colors: data.map((_, i) => colors[i % colors.length]), surface }),
|
|
1611
2380
|
/* @__PURE__ */ jsx(
|
|
1612
2381
|
RadialBar,
|
|
1613
2382
|
{
|
|
@@ -1631,17 +2400,21 @@ var FunnelChart = markChart(forwardRef(function FunnelChart2({
|
|
|
1631
2400
|
showLabels = true,
|
|
1632
2401
|
animated,
|
|
1633
2402
|
"aria-label": ariaLabel,
|
|
1634
|
-
className
|
|
2403
|
+
className,
|
|
2404
|
+
description
|
|
1635
2405
|
}, ref) {
|
|
1636
2406
|
const colors = useChartColors(data.length, false);
|
|
2407
|
+
const locale = useFormatLocale();
|
|
2408
|
+
const format = useMemo(() => fullValueFormatter(void 0, locale), [locale]);
|
|
2409
|
+
const access = useMemo(() => partsAccess({ kind: "funnel", items: data.map((d) => ({ name: d.name, value: d.value })), format }), [data, format]);
|
|
1637
2410
|
const isAnimated = useChartAnimation(animated);
|
|
1638
2411
|
const chartData = data.map((d, i) => ({
|
|
1639
2412
|
name: d.name,
|
|
1640
|
-
value: d.value,
|
|
2413
|
+
value: numberOf(d.value) ?? 0,
|
|
1641
2414
|
fill: colors[i % colors.length]
|
|
1642
2415
|
}));
|
|
1643
2416
|
if (!data?.length) return null;
|
|
1644
|
-
return /* @__PURE__ */ jsx(ChartWrapper, { ref, height, size: { model: "rows", units: data.length * STAGE_ROWS, axis: "16px" }, chart: "funnel", className, "aria-label": ariaLabel, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(FunnelChart$1, { accessibilityLayer: true, children: [
|
|
2417
|
+
return /* @__PURE__ */ jsx(ChartWrapper, { ref, height, size: { model: "rows", units: data.length * STAGE_ROWS, axis: "16px" }, chart: "funnel", className, "aria-label": ariaLabel, access, description, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(FunnelChart$1, { accessibilityLayer: true, children: [
|
|
1645
2418
|
/* @__PURE__ */ jsx(Funnel, { dataKey: "value", data: chartData, isAnimationActive: isAnimated, children: showLabels && /* @__PURE__ */ jsx(
|
|
1646
2419
|
LabelList,
|
|
1647
2420
|
{
|
|
@@ -1727,9 +2500,9 @@ var TINT_L_MIN = 0.1;
|
|
|
1727
2500
|
var TINT_L_STEP = 0.01;
|
|
1728
2501
|
var WHITE_RANGE = [1, 1];
|
|
1729
2502
|
var DARK_RANGE = luminanceRange(DARK_TEXT_CANDIDATE);
|
|
1730
|
-
function minContrast(
|
|
2503
|
+
function minContrast(text2, fills) {
|
|
1731
2504
|
let min = Infinity;
|
|
1732
|
-
for (const f of fills) min = Math.min(min, worstContrastOf(
|
|
2505
|
+
for (const f of fills) min = Math.min(min, worstContrastOf(text2, luminanceRange(f)));
|
|
1733
2506
|
return min;
|
|
1734
2507
|
}
|
|
1735
2508
|
function onFillDarkTint(fill, ...moreFills) {
|
|
@@ -1754,22 +2527,25 @@ function onFillTextValue(fill, ...moreFills) {
|
|
|
1754
2527
|
if (tint) return oklchCss(tint);
|
|
1755
2528
|
return minContrast(DARK_RANGE, fills) >= whiteMin ? ON_FILL_DARK : ON_FILL_WHITE;
|
|
1756
2529
|
}
|
|
2530
|
+
function treemapLeaves(nodes, path = []) {
|
|
2531
|
+
return nodes.flatMap((node) => node.children?.length ? treemapLeaves(node.children, [...path, node.name]) : [{ name: node.name, value: numberOf(node.value) ?? 0, ...path.length ? { group: path.join(" \u203A ") } : {} }]);
|
|
2532
|
+
}
|
|
1757
2533
|
var LABEL_INSET = 4;
|
|
1758
2534
|
function treemapLabelLines(width, height, metrics) {
|
|
1759
2535
|
if (width - 2 * LABEL_INSET < metrics.fontSize * 3) return 0;
|
|
1760
2536
|
return Math.max(0, Math.floor((height - 2 * LABEL_INSET) / metrics.lineHeight));
|
|
1761
2537
|
}
|
|
1762
2538
|
var computedColors = /* @__PURE__ */ new Map();
|
|
1763
|
-
function computedColor(
|
|
1764
|
-
if (typeof document === "undefined") return
|
|
1765
|
-
let value = computedColors.get(
|
|
2539
|
+
function computedColor(css2) {
|
|
2540
|
+
if (typeof document === "undefined") return css2;
|
|
2541
|
+
let value = computedColors.get(css2);
|
|
1766
2542
|
if (value === void 0) {
|
|
1767
|
-
const
|
|
1768
|
-
|
|
1769
|
-
document.body.appendChild(
|
|
1770
|
-
value = getComputedStyle(
|
|
1771
|
-
|
|
1772
|
-
computedColors.set(
|
|
2543
|
+
const probe2 = document.createElement("span");
|
|
2544
|
+
probe2.style.color = css2;
|
|
2545
|
+
document.body.appendChild(probe2);
|
|
2546
|
+
value = getComputedStyle(probe2).color;
|
|
2547
|
+
probe2.remove();
|
|
2548
|
+
computedColors.set(css2, value);
|
|
1773
2549
|
}
|
|
1774
2550
|
return value;
|
|
1775
2551
|
}
|
|
@@ -1781,8 +2557,8 @@ function contrastColor(color) {
|
|
|
1781
2557
|
const rgb = /^rgba?\(\s*(\d+)[\s,]+(\d+)[\s,]+(\d+)/.exec(color);
|
|
1782
2558
|
const c = color.replace("#", "");
|
|
1783
2559
|
const [r, g, b] = rgb ? [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])] : c.length >= 6 ? [parseInt(c.substring(0, 2), 16), parseInt(c.substring(2, 4), 16), parseInt(c.substring(4, 6), 16)] : [0, 0, 0];
|
|
1784
|
-
const
|
|
1785
|
-
return
|
|
2560
|
+
const luminance2 = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
2561
|
+
return luminance2 > 0.55 ? "#1f2937" : "#ffffff";
|
|
1786
2562
|
}
|
|
1787
2563
|
function TreemapContent({ x = 0, y = 0, width = 0, height: h = 0, name, depth, colors, radius, index = 0, metrics }) {
|
|
1788
2564
|
const fill = colors[index % colors.length];
|
|
@@ -1821,15 +2597,15 @@ function addFillToNodes(nodes, colors, counter = { i: 0 }) {
|
|
|
1821
2597
|
}
|
|
1822
2598
|
const fill = colors[counter.i % colors.length];
|
|
1823
2599
|
counter.i++;
|
|
1824
|
-
return { ...node, fill };
|
|
2600
|
+
return { ...node, value: numberOf(node.value) ?? 0, fill };
|
|
1825
2601
|
});
|
|
1826
2602
|
}
|
|
1827
2603
|
var LABEL_TEXT = "text-xs leading-tight font-[family-name:var(--ds-font-sans)]";
|
|
1828
2604
|
function useLabelMetrics() {
|
|
1829
|
-
const
|
|
2605
|
+
const probe2 = useRef(null);
|
|
1830
2606
|
const [metrics, setMetrics] = useState(null);
|
|
1831
2607
|
useLayoutEffect(() => {
|
|
1832
|
-
const el =
|
|
2608
|
+
const el = probe2.current;
|
|
1833
2609
|
if (!el) return;
|
|
1834
2610
|
const cs = getComputedStyle(el);
|
|
1835
2611
|
const fontSize = parseFloat(cs.fontSize);
|
|
@@ -1837,7 +2613,7 @@ function useLabelMetrics() {
|
|
|
1837
2613
|
if (!(fontSize > 0) || !(lineHeight > 0)) return;
|
|
1838
2614
|
if (!metrics || metrics.fontSize !== fontSize || metrics.lineHeight !== lineHeight) setMetrics({ fontSize, lineHeight });
|
|
1839
2615
|
});
|
|
1840
|
-
return [
|
|
2616
|
+
return [probe2, metrics];
|
|
1841
2617
|
}
|
|
1842
2618
|
var TreemapChart = markChart(forwardRef(function TreemapChart2({
|
|
1843
2619
|
data,
|
|
@@ -1845,15 +2621,19 @@ var TreemapChart = markChart(forwardRef(function TreemapChart2({
|
|
|
1845
2621
|
showTooltip = true,
|
|
1846
2622
|
animated,
|
|
1847
2623
|
"aria-label": ariaLabel,
|
|
1848
|
-
className
|
|
2624
|
+
className,
|
|
2625
|
+
description
|
|
1849
2626
|
}, ref) {
|
|
1850
2627
|
const colors = useChartColors(6, false);
|
|
2628
|
+
const locale = useFormatLocale();
|
|
2629
|
+
const format = useMemo(() => fullValueFormatter(void 0, locale), [locale]);
|
|
2630
|
+
const access = useMemo(() => partsAccess({ kind: "treemap", items: treemapLeaves(data ?? []), format }), [data, format]);
|
|
1851
2631
|
const isAnimated = useChartAnimation(animated);
|
|
1852
2632
|
const radius = useRadius();
|
|
1853
|
-
const [
|
|
2633
|
+
const [probe2, metrics] = useLabelMetrics();
|
|
1854
2634
|
const chartData = addFillToNodes(data, colors);
|
|
1855
2635
|
if (!data?.length) return null;
|
|
1856
|
-
return /* @__PURE__ */ jsxs(ChartWrapper, { ref, height, size: { model: "ratio" }, chart: "treemap", className, "aria-label": ariaLabel, children: [
|
|
2636
|
+
return /* @__PURE__ */ jsxs(ChartWrapper, { ref, height, size: { model: "ratio" }, chart: "treemap", className, "aria-label": ariaLabel, access, description, children: [
|
|
1857
2637
|
/* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsx(
|
|
1858
2638
|
Treemap,
|
|
1859
2639
|
{
|
|
@@ -1867,35 +2647,153 @@ var TreemapChart = markChart(forwardRef(function TreemapChart2({
|
|
|
1867
2647
|
children: showTooltip && /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltipContent, {}) })
|
|
1868
2648
|
}
|
|
1869
2649
|
) }),
|
|
1870
|
-
/* @__PURE__ */ jsx("span", { ref:
|
|
2650
|
+
/* @__PURE__ */ jsx("span", { ref: probe2, "aria-hidden": true, className: LABEL_TEXT, style: { position: "absolute", visibility: "hidden", pointerEvents: "none" }, children: "M" })
|
|
1871
2651
|
] });
|
|
1872
2652
|
}));
|
|
2653
|
+
function oneWayFlows(links) {
|
|
2654
|
+
const next = /* @__PURE__ */ new Map();
|
|
2655
|
+
const reaches = (from, to) => {
|
|
2656
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2657
|
+
const stack = [from];
|
|
2658
|
+
while (stack.length) {
|
|
2659
|
+
const at = stack.pop();
|
|
2660
|
+
if (at === to) return true;
|
|
2661
|
+
if (seen.has(at)) continue;
|
|
2662
|
+
seen.add(at);
|
|
2663
|
+
stack.push(...next.get(at) ?? []);
|
|
2664
|
+
}
|
|
2665
|
+
return false;
|
|
2666
|
+
};
|
|
2667
|
+
const kept = /* @__PURE__ */ new Set();
|
|
2668
|
+
const loops = [];
|
|
2669
|
+
const largestFirst = links.map((link, i) => ({ link, i })).sort((a, b) => (numberOf(b.link.value) ?? 0) - (numberOf(a.link.value) ?? 0) || a.i - b.i);
|
|
2670
|
+
for (const { link, i } of largestFirst) {
|
|
2671
|
+
if (reaches(link.target, link.source)) {
|
|
2672
|
+
loops.push(link);
|
|
2673
|
+
continue;
|
|
2674
|
+
}
|
|
2675
|
+
kept.add(i);
|
|
2676
|
+
next.set(link.source, [...next.get(link.source) ?? [], link.target]);
|
|
2677
|
+
}
|
|
2678
|
+
return { flows: links.filter((_, i) => kept.has(i)), loops };
|
|
2679
|
+
}
|
|
2680
|
+
function stepsOf(count, flows) {
|
|
2681
|
+
const depth = new Array(count).fill(0);
|
|
2682
|
+
const valid = flows.filter((f) => f.source >= 0 && f.source < count && f.target >= 0 && f.target < count);
|
|
2683
|
+
for (let pass = 0; pass < count; pass++) {
|
|
2684
|
+
let moved = false;
|
|
2685
|
+
for (const f of valid) if (depth[f.target] < depth[f.source] + 1) {
|
|
2686
|
+
depth[f.target] = depth[f.source] + 1;
|
|
2687
|
+
moved = true;
|
|
2688
|
+
}
|
|
2689
|
+
if (!moved) break;
|
|
2690
|
+
}
|
|
2691
|
+
return Math.max(0, ...depth);
|
|
2692
|
+
}
|
|
2693
|
+
var measure;
|
|
2694
|
+
function textWidth(text2, el) {
|
|
2695
|
+
const style = getComputedStyle(el);
|
|
2696
|
+
const size = parseFloat(style.fontSize) || 12;
|
|
2697
|
+
if (measure === void 0) {
|
|
2698
|
+
try {
|
|
2699
|
+
measure = /jsdom/i.test(navigator.userAgent) ? null : document.createElement("canvas").getContext("2d");
|
|
2700
|
+
} catch {
|
|
2701
|
+
measure = null;
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
if (!measure) return text2.length * size * 0.6;
|
|
2705
|
+
measure.font = `${style.fontWeight || "400"} ${size}px ${style.fontFamily || "sans-serif"}`;
|
|
2706
|
+
return measure.measureText(text2).width;
|
|
2707
|
+
}
|
|
2708
|
+
var NAME_GAP2 = 6;
|
|
2709
|
+
var INSET = 8;
|
|
2710
|
+
function SankeyNode({ x, y, width, height, payload, ink, steps, inset, drawn: drawn2 }) {
|
|
2711
|
+
const end = steps > 0 && !payload.targetLinks?.length;
|
|
2712
|
+
const name = String(payload.name ?? "");
|
|
2713
|
+
const text2 = useRef(null);
|
|
2714
|
+
const [shown, setShown] = useState(name);
|
|
2715
|
+
useLayoutEffect(() => {
|
|
2716
|
+
const el = text2.current;
|
|
2717
|
+
const drawing = drawn2 > 0 ? drawn2 : Number(el?.ownerSVGElement?.getAttribute("width")) || 0;
|
|
2718
|
+
if (!el || drawing <= 0) {
|
|
2719
|
+
setShown(name);
|
|
2720
|
+
return;
|
|
2721
|
+
}
|
|
2722
|
+
const step = steps > 0 ? (drawing - 2 * inset - width) / steps : drawing - 2 * inset;
|
|
2723
|
+
const gap = step - width;
|
|
2724
|
+
const shared2 = end || (payload.depth ?? 0) === steps - 1;
|
|
2725
|
+
const room = Math.max(0, (shared2 ? gap / 2 : gap) - 2 * NAME_GAP2);
|
|
2726
|
+
if (textWidth(name, el) <= room) {
|
|
2727
|
+
setShown(name);
|
|
2728
|
+
return;
|
|
2729
|
+
}
|
|
2730
|
+
let fits = 0;
|
|
2731
|
+
for (let lo = 1, hi = name.length - 1; lo <= hi; ) {
|
|
2732
|
+
const mid = Math.floor((lo + hi) / 2);
|
|
2733
|
+
if (textWidth(`${name.slice(0, mid).trimEnd()}\u2026`, el) <= room) {
|
|
2734
|
+
fits = mid;
|
|
2735
|
+
lo = mid + 1;
|
|
2736
|
+
} else hi = mid - 1;
|
|
2737
|
+
}
|
|
2738
|
+
setShown(fits > 0 ? `${name.slice(0, fits).trimEnd()}\u2026` : "");
|
|
2739
|
+
}, [name, steps, inset, width, end, payload.depth, drawn2]);
|
|
2740
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
2741
|
+
/* @__PURE__ */ jsx("rect", { x, y, width, height, fill: payload.fill }),
|
|
2742
|
+
/* @__PURE__ */ jsx(
|
|
2743
|
+
"text",
|
|
2744
|
+
{
|
|
2745
|
+
ref: text2,
|
|
2746
|
+
"data-sankey-name": "",
|
|
2747
|
+
x: end ? x - NAME_GAP2 : x + width + NAME_GAP2,
|
|
2748
|
+
y: y + height / 2,
|
|
2749
|
+
textAnchor: end ? "end" : "start",
|
|
2750
|
+
dominantBaseline: "middle",
|
|
2751
|
+
fill: ink,
|
|
2752
|
+
style: { fontSize: "var(--ds-font-size-xs)", fontFamily: "var(--ds-font-sans)" },
|
|
2753
|
+
children: shown
|
|
2754
|
+
}
|
|
2755
|
+
)
|
|
2756
|
+
] });
|
|
2757
|
+
}
|
|
1873
2758
|
var SankeyChart = markChart(forwardRef(function SankeyChart2({
|
|
1874
2759
|
data,
|
|
1875
2760
|
height = "auto",
|
|
1876
2761
|
showTooltip = true,
|
|
1877
2762
|
"aria-label": ariaLabel,
|
|
1878
|
-
className
|
|
2763
|
+
className,
|
|
2764
|
+
description
|
|
1879
2765
|
}, ref) {
|
|
1880
2766
|
const colors = useChartColors(data.nodes.length, false);
|
|
2767
|
+
const locale = useFormatLocale();
|
|
2768
|
+
const format = useMemo(() => fullValueFormatter(void 0, locale), [locale]);
|
|
2769
|
+
const { flows, loops } = useMemo(() => oneWayFlows(data?.links ?? []), [data]);
|
|
2770
|
+
const [drawn2, setDrawn] = useState(0);
|
|
2771
|
+
const steps = useMemo(() => stepsOf(data?.nodes?.length ?? 0, flows), [data, flows]);
|
|
2772
|
+
const access = useMemo(() => flowAccess({ nodes: data?.nodes ?? [], links: data?.links ?? [], undrawn: loops, format }), [data, loops, format]);
|
|
1881
2773
|
const linkStroke = useResolvedToken("--ds-border-subtle");
|
|
2774
|
+
const nameInk = useResolvedToken("--ds-text-secondary");
|
|
1882
2775
|
const nodesWithColor = data.nodes.map((node, i) => ({
|
|
1883
2776
|
...node,
|
|
1884
2777
|
fill: colors[i % colors.length]
|
|
1885
2778
|
}));
|
|
2779
|
+
useEffect(() => {
|
|
2780
|
+
const loop = loops[0];
|
|
2781
|
+
if (loop) warnOnce("SankeyChart", `the flow from "${data.nodes[loop.source]?.name ?? loop.source}" to "${data.nodes[loop.target]?.name ?? loop.target}" closes a loop, so it isn't drawn: a Sankey chart's flows run one way. Its table still lists it.`);
|
|
2782
|
+
}, [loops, data]);
|
|
1886
2783
|
const sankeyData = {
|
|
1887
2784
|
nodes: nodesWithColor,
|
|
1888
|
-
links:
|
|
2785
|
+
links: flows.map((flow) => ({ ...flow, value: numberOf(flow.value) ?? 0 }))
|
|
1889
2786
|
};
|
|
1890
2787
|
if (!data?.nodes?.length) return null;
|
|
1891
|
-
return /* @__PURE__ */ jsx(ChartWrapper, { ref, height, size: { model: "ratio" }, chart: "sankey", className, "aria-label": ariaLabel, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsx(
|
|
2788
|
+
return /* @__PURE__ */ jsx(ChartWrapper, { ref, height, size: { model: "ratio" }, chart: "sankey", className, "aria-label": ariaLabel, access, description, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", onResize: (w) => setDrawn(w), children: /* @__PURE__ */ jsx(
|
|
1892
2789
|
Sankey,
|
|
1893
2790
|
{
|
|
1894
2791
|
data: sankeyData,
|
|
1895
2792
|
nodeWidth: 10,
|
|
1896
2793
|
nodePadding: 24,
|
|
1897
|
-
margin: { top:
|
|
2794
|
+
margin: { top: INSET, right: INSET, bottom: INSET, left: INSET },
|
|
1898
2795
|
link: { stroke: linkStroke, strokeOpacity: 0.4 },
|
|
2796
|
+
node: (n) => /* @__PURE__ */ jsx(SankeyNode, { x: n.x, y: n.y, width: n.width, height: n.height, payload: n.payload, ink: nameInk, steps, inset: INSET, drawn: drawn2 }),
|
|
1899
2797
|
children: showTooltip && /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltipContent, {}) })
|
|
1900
2798
|
}
|
|
1901
2799
|
) }) });
|
|
@@ -1995,9 +2893,15 @@ function ChartContainer({
|
|
|
1995
2893
|
const fixed = fixedChartHeight(height);
|
|
1996
2894
|
const sizesItself = !loading && !!children && isLibraryChart(children);
|
|
1997
2895
|
const area = fixed ?? CHART_HEIGHTS.md;
|
|
2896
|
+
const [actions, setActions] = useState(null);
|
|
2897
|
+
const [charts, setCharts] = useState(0);
|
|
2898
|
+
const register = useCallback(() => {
|
|
2899
|
+
setCharts((n) => n + 1);
|
|
2900
|
+
return () => setCharts((n) => n - 1);
|
|
2901
|
+
}, []);
|
|
1998
2902
|
const context = useMemo(
|
|
1999
|
-
() => ({ height: fixed, titleId: title ? titleId : void 0, fillArea: !sizesItself }),
|
|
2000
|
-
[fixed, title, titleId, sizesItself]
|
|
2903
|
+
() => ({ height: fixed, titleId: title ? titleId : void 0, title: title || void 0, fillArea: !sizesItself, actions, charts, register }),
|
|
2904
|
+
[fixed, title, titleId, sizesItself, actions, charts, register]
|
|
2001
2905
|
);
|
|
2002
2906
|
let body = null;
|
|
2003
2907
|
if (loading) {
|
|
@@ -2029,7 +2933,10 @@ function ChartContainer({
|
|
|
2029
2933
|
className
|
|
2030
2934
|
),
|
|
2031
2935
|
children: [
|
|
2032
|
-
title && /* @__PURE__ */
|
|
2936
|
+
title && /* @__PURE__ */ jsxs("div", { "data-chart-header": "", className: "mb-ds-4 flex items-start justify-between gap-ds-3", children: [
|
|
2937
|
+
/* @__PURE__ */ jsx("h3", { id: titleId, className: cn(CHART_TITLE_CLASSES, "min-w-0 flex-1"), children: title }),
|
|
2938
|
+
/* @__PURE__ */ jsx("div", { ref: setActions, "data-chart-actions": "", className: "shrink-0 empty:hidden" })
|
|
2939
|
+
] }),
|
|
2033
2940
|
/* @__PURE__ */ jsx(
|
|
2034
2941
|
"div",
|
|
2035
2942
|
{
|