@opendata-ai/openchart-core 8.3.0 → 8.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5096 -4649
- package/dist/index.js +508 -199
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
- package/schema/chart.schema.json +793 -22
- package/schema/table.schema.json +167 -9
- package/schema/vizspec.schema.json +1734 -81
package/dist/index.js
CHANGED
|
@@ -443,6 +443,274 @@ function findAccessibleColor(baseColor, bg, targetRatio = 4.5) {
|
|
|
443
443
|
return baseColor;
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
+
// src/styles/token-definitions.ts
|
|
447
|
+
var CSS_TOKEN_ROOT_SELECTORS = [
|
|
448
|
+
".oc-root",
|
|
449
|
+
".oc-chart-root",
|
|
450
|
+
".oc-table-wrapper",
|
|
451
|
+
".oc-table-root",
|
|
452
|
+
".oc-graph-wrapper",
|
|
453
|
+
".oc-graph-root",
|
|
454
|
+
".oc-sankey-root",
|
|
455
|
+
".oc-tilemap-root",
|
|
456
|
+
".oc-barlist-root",
|
|
457
|
+
".oc-map-root"
|
|
458
|
+
];
|
|
459
|
+
var CSS_TOKENS = [
|
|
460
|
+
{
|
|
461
|
+
name: "--oc-font-family",
|
|
462
|
+
light: '\n "Inter Variable", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "--oc-font-mono",
|
|
466
|
+
light: '"JetBrains Mono", "Fira Code", "Cascadia Code", monospace'
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
section: "Animation easing presets via CSS linear()",
|
|
470
|
+
name: "--oc-ease-smooth",
|
|
471
|
+
light: "linear(\n 0,\n 0.157,\n 0.438,\n 0.64,\n 0.766,\n 0.85,\n 0.906,\n 0.941,\n 0.964,\n 0.978,\n 0.988,\n 0.994,\n 0.998,\n 1\n )"
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
name: "--oc-ease-snappy",
|
|
475
|
+
light: "linear(\n 0,\n 0.012,\n 0.048,\n 0.108,\n 0.194,\n 0.302,\n 0.426,\n 0.559,\n 0.69,\n 0.808,\n 0.905,\n 0.973,\n 1.013,\n 1.028,\n 1.023,\n 1.006,\n 0.984,\n 0.966,\n 0.957,\n 0.957,\n 0.964,\n 0.975,\n 0.986,\n 0.995,\n 1,\n 1.003,\n 1.002,\n 1,\n 0.998,\n 0.998,\n 0.999,\n 1\n )",
|
|
476
|
+
consumers: "EASE_VAR_MAP in vanilla renderers"
|
|
477
|
+
},
|
|
478
|
+
{ section: "Animation timing defaults", name: "--oc-animation-duration", light: "450ms" },
|
|
479
|
+
{ name: "--oc-animation-stagger", light: "30ms" },
|
|
480
|
+
{ name: "--oc-annotation-delay", light: "150ms" },
|
|
481
|
+
{ section: "Typography scale (editorial design system)", name: "--oc-title-size", light: "26px" },
|
|
482
|
+
{
|
|
483
|
+
name: "--oc-title-weight",
|
|
484
|
+
light: "600",
|
|
485
|
+
comment: "one ladder: 400 regular / 500 medium / 600 semibold / 700 bold"
|
|
486
|
+
},
|
|
487
|
+
{ name: "--oc-title-tracking", light: "-0.022em" },
|
|
488
|
+
{ name: "--oc-subtitle-size", light: "14px" },
|
|
489
|
+
{ name: "--oc-subtitle-weight", light: "400" },
|
|
490
|
+
{ name: "--oc-source-size", light: "11px" },
|
|
491
|
+
{ name: "--oc-source-weight", light: "400" },
|
|
492
|
+
{ name: "--oc-body-size", light: "13px" },
|
|
493
|
+
{ name: "--oc-eyebrow-size", light: "11px" },
|
|
494
|
+
{ name: "--oc-eyebrow-weight", light: "500" },
|
|
495
|
+
{ name: "--oc-eyebrow-tracking", light: "0.08em" },
|
|
496
|
+
{ section: "Surfaces (light mode defaults)", name: "--oc-bg", light: "#ffffff", dark: "#09090b" },
|
|
497
|
+
{ name: "--oc-card", light: "#ffffff", dark: "#111113" },
|
|
498
|
+
{
|
|
499
|
+
name: "--oc-secondary",
|
|
500
|
+
light: "#f4f4f5",
|
|
501
|
+
dark: "#27272a",
|
|
502
|
+
comment: "zinc-100, raised surface"
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
section: `Text levels. Names invert across modes: in light mode "muted" sits at
|
|
506
|
+
* a lighter zinc step than "subtle" because both are picked relative to
|
|
507
|
+
* the active background, not from a fixed lightness ladder. The intent
|
|
508
|
+
* is "muted = first step away from primary text"; "subtle = next step
|
|
509
|
+
* down"; etc.
|
|
510
|
+
*
|
|
511
|
+
* Light mode: text=zinc-950, secondary=zinc-700, muted=zinc-500,
|
|
512
|
+
* subtle=zinc-400, faint=zinc-300
|
|
513
|
+
* Dark mode (dark.css): inverts the surface tokens but keeps the same
|
|
514
|
+
* muted -> subtle -> faint progression away from primary.`,
|
|
515
|
+
name: "--oc-text",
|
|
516
|
+
light: "#09090b",
|
|
517
|
+
dark: "#f7f8f8"
|
|
518
|
+
},
|
|
519
|
+
{ name: "--oc-text-secondary", light: "#3f3f46", dark: "#d0d6e0" },
|
|
520
|
+
{ name: "--oc-text-muted", light: "#71717a", dark: "#a1a1aa" },
|
|
521
|
+
{ name: "--oc-text-faint", light: "#d4d4d8", dark: "#52525b" },
|
|
522
|
+
{
|
|
523
|
+
section: `Derived neutral ramp. Named by the share of *text* in a text-toward-
|
|
524
|
+
* background mix: 100 is nearly the background, 800 is nearly the text.
|
|
525
|
+
* These static values are the mix against the default surfaces; mounts
|
|
526
|
+
* restamp them from ResolvedTheme.colors.neutral, which a custom warm or
|
|
527
|
+
* cool theme derives from its own text/background pair.`,
|
|
528
|
+
name: "--oc-gray-100",
|
|
529
|
+
light: "#f0f0f0",
|
|
530
|
+
dark: "#171719"
|
|
531
|
+
},
|
|
532
|
+
{ name: "--oc-gray-200", light: "#e1e1e2", dark: "#262627" },
|
|
533
|
+
{ name: "--oc-gray-300", light: "#c9c9c9", dark: "#3d3e3f" },
|
|
534
|
+
{ name: "--oc-gray-400", light: "#9d9d9d", dark: "#68696a" },
|
|
535
|
+
{ name: "--oc-gray-600", light: "#787879", dark: "#8c8c8d" },
|
|
536
|
+
{ name: "--oc-gray-800", light: "#474748", dark: "#bcbcbd" },
|
|
537
|
+
{
|
|
538
|
+
section: "Lines",
|
|
539
|
+
name: "--oc-gridline",
|
|
540
|
+
light: "rgba(0, 0, 0, 0.08)",
|
|
541
|
+
dark: "rgba(255, 255, 255, 0.06)"
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
name: "--oc-axis",
|
|
545
|
+
light: "rgba(0, 0, 0, 0.14)",
|
|
546
|
+
dark: "rgba(255, 255, 255, 0.14)",
|
|
547
|
+
comment: "hairline = theme.colors.hairline, not the tick-label ink"
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
name: "--oc-border",
|
|
551
|
+
light: "rgba(0, 0, 0, 0.08)",
|
|
552
|
+
dark: "rgba(255, 255, 255, 0.1)"
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
name: "--oc-border-radius",
|
|
556
|
+
light: "8px",
|
|
557
|
+
comment: "containers and tooltips; marks use --oc-radius-sm"
|
|
558
|
+
},
|
|
559
|
+
{ section: "Radius ladder", name: "--oc-radius-sm", light: "2px" },
|
|
560
|
+
{ name: "--oc-radius-md", light: "6px" },
|
|
561
|
+
{ name: "--oc-radius-lg", light: "var(--oc-border-radius)" },
|
|
562
|
+
{ name: "--oc-radius-full", light: "999px" },
|
|
563
|
+
{
|
|
564
|
+
section: "Brand and semantic",
|
|
565
|
+
name: "--oc-accent",
|
|
566
|
+
light: "#06b6d4",
|
|
567
|
+
dark: "#06b6d4"
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
name: "--oc-accent-strong",
|
|
571
|
+
light: "#0891b2",
|
|
572
|
+
dark: "#06b6d4",
|
|
573
|
+
comment: "darker cyan for line strokes on light bg",
|
|
574
|
+
consumers: "stamped via adaptForLightLineStroke"
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
name: "--oc-positive",
|
|
578
|
+
light: "#15803d",
|
|
579
|
+
dark: "#4ade80",
|
|
580
|
+
comment: "kept equal to DEFAULT_THEME.colors.positive (token-theme-parity test)"
|
|
581
|
+
},
|
|
582
|
+
{ name: "--oc-negative", light: "#dc2626", dark: "#f87171" },
|
|
583
|
+
{
|
|
584
|
+
name: "--oc-positive-tint",
|
|
585
|
+
light: "color-mix(in srgb, var(--oc-positive) 10%, var(--oc-bg))",
|
|
586
|
+
comment: "delta chip background"
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
name: "--oc-negative-tint",
|
|
590
|
+
light: "color-mix(in srgb, var(--oc-negative) 10%, var(--oc-bg))"
|
|
591
|
+
},
|
|
592
|
+
{ name: "--oc-focus", light: "#3b82f6", dark: "#60a5fa" },
|
|
593
|
+
{
|
|
594
|
+
name: "--oc-focus-ring",
|
|
595
|
+
light: "rgba(59, 130, 246, 0.1)",
|
|
596
|
+
comment: "static, intentionally NOT derived from --oc-focus and NOT flipped in dark mode"
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
name: "--oc-focus-ring-strong",
|
|
600
|
+
light: "rgba(59, 130, 246, 0.25)"
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
name: "--oc-editable-hover",
|
|
604
|
+
light: "rgba(79, 70, 229, 0.35)",
|
|
605
|
+
comment: "edit-mode hover outline, indigo one-off"
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
section: "Spacing scale (4px base) \u2014 only --oc-space-2/4 are consumed (JS-stamped)",
|
|
609
|
+
name: "--oc-space-2",
|
|
610
|
+
light: "8px",
|
|
611
|
+
consumers: "stamped at mount from theme.spacing (theme-tokens.ts); component CSS must NOT consume these"
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
name: "--oc-space-4",
|
|
615
|
+
light: "16px",
|
|
616
|
+
consumers: "stamped at mount from theme.spacing (theme-tokens.ts); component CSS must NOT consume these"
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
section: `Hover language. The hovered element goes to full ink; the rest of its
|
|
620
|
+
* group drops to --oc-hover-dim over --oc-hover-duration. Maps override the
|
|
621
|
+
* dim locally (3000 county paths at 0.3 reads as a blackout, not a hint).`,
|
|
622
|
+
name: "--oc-hover-dim",
|
|
623
|
+
light: "0.3"
|
|
624
|
+
},
|
|
625
|
+
{ name: "--oc-hover-duration", light: "140ms" },
|
|
626
|
+
{ name: "--oc-map-hover-dim", light: "0.75" },
|
|
627
|
+
{
|
|
628
|
+
section: "Interactive states",
|
|
629
|
+
name: "--oc-hover-bg",
|
|
630
|
+
light: "rgba(0, 0, 0, 0.025)",
|
|
631
|
+
dark: "rgba(255, 255, 255, 0.05)"
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
name: "--oc-tooltip-bg",
|
|
635
|
+
light: "rgba(255, 255, 255, 0.88)",
|
|
636
|
+
dark: "rgba(17, 17, 19, 0.92)"
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
name: "--oc-tooltip-border",
|
|
640
|
+
light: "rgba(0, 0, 0, 0.08)",
|
|
641
|
+
dark: "rgba(255, 255, 255, 0.08)"
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
name: "--oc-tooltip-shadow",
|
|
645
|
+
light: "0 2px 8px rgba(0, 0, 0, 0.08), 0 0 1px rgba(0, 0, 0, 0.12)",
|
|
646
|
+
dark: "0 2px 8px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.4)"
|
|
647
|
+
},
|
|
648
|
+
{ name: "--oc-tooltip-text", light: "#09090b", dark: "#f7f8f8" },
|
|
649
|
+
{ name: "--oc-legend-text", light: "#3f3f46", dark: "#d0d6e0" }
|
|
650
|
+
];
|
|
651
|
+
var TOKEN_MAP = new Map(CSS_TOKENS.map((t) => [t.name, t]));
|
|
652
|
+
function cssTokenDefault(name, mode) {
|
|
653
|
+
const token = TOKEN_MAP.get(name);
|
|
654
|
+
if (!token) throw new Error(`Unknown CSS token: ${name}`);
|
|
655
|
+
if (mode === "dark" && token.dark !== void 0) return token.dark;
|
|
656
|
+
return token.light;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// src/colors/neutral.ts
|
|
660
|
+
var STEPS = [
|
|
661
|
+
[100, 0.06],
|
|
662
|
+
[200, 0.12],
|
|
663
|
+
[300, 0.22],
|
|
664
|
+
[400, 0.4],
|
|
665
|
+
[600, 0.55],
|
|
666
|
+
[800, 0.75]
|
|
667
|
+
];
|
|
668
|
+
function parseHex(color) {
|
|
669
|
+
const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color.trim());
|
|
670
|
+
if (!m) return null;
|
|
671
|
+
const hex = m[1].length === 3 ? m[1].split("").map((c) => c + c).join("") : m[1];
|
|
672
|
+
return [
|
|
673
|
+
parseInt(hex.slice(0, 2), 16),
|
|
674
|
+
parseInt(hex.slice(2, 4), 16),
|
|
675
|
+
parseInt(hex.slice(4, 6), 16)
|
|
676
|
+
];
|
|
677
|
+
}
|
|
678
|
+
function mix(text, bg, t) {
|
|
679
|
+
const c = (i) => Math.round(text[i] * t + bg[i] * (1 - t));
|
|
680
|
+
const hex = (n) => Math.max(0, Math.min(255, n)).toString(16).padStart(2, "0");
|
|
681
|
+
return `#${hex(c(0))}${hex(c(1))}${hex(c(2))}`;
|
|
682
|
+
}
|
|
683
|
+
function staticRamp(mode) {
|
|
684
|
+
return {
|
|
685
|
+
100: cssTokenDefault("--oc-gray-100", mode),
|
|
686
|
+
200: cssTokenDefault("--oc-gray-200", mode),
|
|
687
|
+
300: cssTokenDefault("--oc-gray-300", mode),
|
|
688
|
+
400: cssTokenDefault("--oc-gray-400", mode),
|
|
689
|
+
600: cssTokenDefault("--oc-gray-600", mode),
|
|
690
|
+
800: cssTokenDefault("--oc-gray-800", mode),
|
|
691
|
+
secondary: cssTokenDefault("--oc-text-secondary", mode),
|
|
692
|
+
faint: cssTokenDefault("--oc-text-faint", mode),
|
|
693
|
+
border: cssTokenDefault("--oc-border", mode),
|
|
694
|
+
surface: cssTokenDefault("--oc-bg", mode)
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
function deriveNeutralRamp(text, bg, isDark = false) {
|
|
698
|
+
const mode = isDark ? "dark" : "light";
|
|
699
|
+
const surface = isOpaqueColor(bg) ? bg : cssTokenDefault("--oc-bg", mode);
|
|
700
|
+
if (!isOpaqueColor(bg)) return staticRamp(mode);
|
|
701
|
+
const t = parseHex(text);
|
|
702
|
+
const b = parseHex(bg);
|
|
703
|
+
if (!t || !b) return { ...staticRamp(mode), surface };
|
|
704
|
+
const ramp = Object.fromEntries(STEPS.map(([step, w]) => [step, mix(t, b, w)]));
|
|
705
|
+
return {
|
|
706
|
+
...ramp,
|
|
707
|
+
secondary: ramp[800],
|
|
708
|
+
faint: ramp[300],
|
|
709
|
+
border: ramp[100],
|
|
710
|
+
surface
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
|
|
446
714
|
// src/colors/palettes.ts
|
|
447
715
|
var ACHROMATIC_RAMP = {
|
|
448
716
|
fg: "#f7f8f8",
|
|
@@ -464,23 +732,73 @@ var ACHROMATIC_RAMP = {
|
|
|
464
732
|
};
|
|
465
733
|
var CATEGORICAL_PALETTE = [
|
|
466
734
|
"#06b6d4",
|
|
467
|
-
// cyan
|
|
468
|
-
"#
|
|
469
|
-
//
|
|
470
|
-
"#
|
|
471
|
-
//
|
|
472
|
-
"#
|
|
473
|
-
//
|
|
474
|
-
"#
|
|
475
|
-
//
|
|
476
|
-
"#
|
|
477
|
-
//
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
"#
|
|
481
|
-
//
|
|
482
|
-
"#
|
|
483
|
-
//
|
|
735
|
+
// cyan — oklch(0.715 0.126 215) — sRGB literal, primary accent
|
|
736
|
+
"#e29e47",
|
|
737
|
+
// ochre — oklch(0.750 0.130 70)
|
|
738
|
+
"#4170cb",
|
|
739
|
+
// blue — oklch(0.559 0.150 262)
|
|
740
|
+
"#b2417f",
|
|
741
|
+
// rose — oklch(0.550 0.161 350)
|
|
742
|
+
"#3ca368",
|
|
743
|
+
// green — oklch(0.640 0.130 155)
|
|
744
|
+
"#a584dc"
|
|
745
|
+
// violet — oklch(0.679 0.130 300)
|
|
746
|
+
];
|
|
747
|
+
var CATEGORICAL_FILL_PALETTE = [
|
|
748
|
+
"#51bccc",
|
|
749
|
+
// cyan — oklch(0.739 0.100 209)
|
|
750
|
+
"#dda96b",
|
|
751
|
+
// ochre — oklch(0.770 0.100 70)
|
|
752
|
+
"#587fc8",
|
|
753
|
+
// blue — oklch(0.601 0.120 262)
|
|
754
|
+
"#bf6291",
|
|
755
|
+
// rose — oklch(0.619 0.131 350)
|
|
756
|
+
"#62ab7d",
|
|
757
|
+
// green — oklch(0.681 0.101 155)
|
|
758
|
+
"#ae96da"
|
|
759
|
+
// violet — oklch(0.719 0.100 300)
|
|
760
|
+
];
|
|
761
|
+
var CATEGORICAL_PALETTE_DARK = [
|
|
762
|
+
"#22d3ee",
|
|
763
|
+
// cyan — oklch(0.797 0.134 212) — sRGB literal
|
|
764
|
+
"#f9be78",
|
|
765
|
+
// ochre — oklch(0.840 0.111 70)
|
|
766
|
+
"#6591e1",
|
|
767
|
+
// blue — oklch(0.660 0.129 262)
|
|
768
|
+
"#d771a4",
|
|
769
|
+
// rose — oklch(0.680 0.140 350)
|
|
770
|
+
"#6ebf8c",
|
|
771
|
+
// green — oklch(0.739 0.109 155)
|
|
772
|
+
"#bca1ed"
|
|
773
|
+
// violet — oklch(0.760 0.110 300)
|
|
774
|
+
];
|
|
775
|
+
var CATEGORICAL_FILL_PALETTE_DARK = [
|
|
776
|
+
"#72cedc",
|
|
777
|
+
// cyan — oklch(0.800 0.090 208)
|
|
778
|
+
"#f3c898",
|
|
779
|
+
// ochre — oklch(0.859 0.079 70)
|
|
780
|
+
"#7698d6",
|
|
781
|
+
// blue — oklch(0.679 0.100 262)
|
|
782
|
+
"#d281a8",
|
|
783
|
+
// rose — oklch(0.700 0.111 350)
|
|
784
|
+
"#82ba95",
|
|
785
|
+
// green — oklch(0.740 0.079 155)
|
|
786
|
+
"#c2afe7"
|
|
787
|
+
// violet — oklch(0.789 0.081 300)
|
|
788
|
+
];
|
|
789
|
+
var CATEGORICAL_EXTENDED_PALETTE = [
|
|
790
|
+
"#8e8f19",
|
|
791
|
+
// olive — oklch(0.629 0.130 110)
|
|
792
|
+
"#b086eb",
|
|
793
|
+
// periwinkle— oklch(0.701 0.149 302)
|
|
794
|
+
"#ac3225",
|
|
795
|
+
// brick — oklch(0.501 0.161 30)
|
|
796
|
+
"#00bfc0",
|
|
797
|
+
// teal — oklch(0.729 0.124 195)
|
|
798
|
+
"#a5538c",
|
|
799
|
+
// mulberry — oklch(0.560 0.129 340)
|
|
800
|
+
"#65b1f6"
|
|
801
|
+
// sky — oklch(0.740 0.126 248)
|
|
484
802
|
];
|
|
485
803
|
var SEQUENTIAL_BLUE = {
|
|
486
804
|
name: "blue",
|
|
@@ -972,6 +1290,16 @@ function getLayoutStrategy(breakpoint, heightClass = "normal") {
|
|
|
972
1290
|
const base = getWidthStrategy(breakpoint);
|
|
973
1291
|
return applyHeightConstraints(base, heightClass);
|
|
974
1292
|
}
|
|
1293
|
+
var HEIGHT_NO_GRID_MAX = 150;
|
|
1294
|
+
var COMPACT_MAX_X_TICKS = 3;
|
|
1295
|
+
function resolveChromeEconomy(width, height) {
|
|
1296
|
+
const economy = {
|
|
1297
|
+
gridlines: height >= HEIGHT_NO_GRID_MAX,
|
|
1298
|
+
axes: height >= HEIGHT_CRAMPED_MAX
|
|
1299
|
+
};
|
|
1300
|
+
if (getBreakpoint(width) === "compact") economy.maxXTicks = COMPACT_MAX_X_TICKS;
|
|
1301
|
+
return economy;
|
|
1302
|
+
}
|
|
975
1303
|
|
|
976
1304
|
// src/responsive/metrics.ts
|
|
977
1305
|
var AXIS_TITLE_OFFSET_COMPACT = 35;
|
|
@@ -983,7 +1311,7 @@ var HPAD_COMPACT_FRACTION = 0.5;
|
|
|
983
1311
|
var HPAD_COMPACT_MIN = 4;
|
|
984
1312
|
var TICK_LABEL_OFFSET = 6;
|
|
985
1313
|
var AXIS_TITLE_TRAILING_PAD = 4;
|
|
986
|
-
var AXIS_TITLE_GAP =
|
|
1314
|
+
var AXIS_TITLE_GAP = 3;
|
|
987
1315
|
function axisTitleOffset(maxTickLabelWidth, titleFontSize, width, inline = false) {
|
|
988
1316
|
const halfTitleGlyph = Math.ceil(titleFontSize / 2);
|
|
989
1317
|
if (inline) {
|
|
@@ -1063,18 +1391,22 @@ function truncateRotatedLabel(label, tickAngle, tickFontSize, tickFontWeight, me
|
|
|
1063
1391
|
var DEFAULT_THEME = {
|
|
1064
1392
|
colors: {
|
|
1065
1393
|
categorical: [...CATEGORICAL_PALETTE],
|
|
1394
|
+
categoricalFill: [...CATEGORICAL_FILL_PALETTE],
|
|
1066
1395
|
sequential: SEQUENTIAL_PALETTES,
|
|
1067
1396
|
diverging: DIVERGING_PALETTES,
|
|
1068
1397
|
background: "transparent",
|
|
1069
1398
|
text: "#09090b",
|
|
1070
|
-
gridline: "rgba(0,0,0,0.
|
|
1399
|
+
gridline: "rgba(0,0,0,0.08)",
|
|
1400
|
+
// Structural hairline: axis lines, separators. Deliberately heavier than
|
|
1401
|
+
// the gridline and lighter than any ink, so structure reads as structure.
|
|
1402
|
+
hairline: "rgba(0,0,0,0.14)",
|
|
1071
1403
|
// Used for axis lines/ticks AND axis tick label fill. Must clear WCAG AA
|
|
1072
1404
|
// contrast (4.5:1) on white because tick labels are rendered with this
|
|
1073
1405
|
// color. Zinc-500 hits ~5.7:1.
|
|
1074
1406
|
axis: "#71717a",
|
|
1075
1407
|
annotationFill: "rgba(0,0,0,0.04)",
|
|
1076
1408
|
annotationText: "#71717a",
|
|
1077
|
-
positive: "#
|
|
1409
|
+
positive: "#15803d",
|
|
1078
1410
|
negative: "#dc2626"
|
|
1079
1411
|
},
|
|
1080
1412
|
fonts: {
|
|
@@ -1086,19 +1418,21 @@ var DEFAULT_THEME = {
|
|
|
1086
1418
|
body: 13,
|
|
1087
1419
|
small: 11,
|
|
1088
1420
|
axisTick: 11,
|
|
1089
|
-
metricLabel:
|
|
1421
|
+
metricLabel: 11,
|
|
1090
1422
|
metricValue: 22
|
|
1091
1423
|
},
|
|
1424
|
+
// One ladder everywhere: 400 regular / 500 medium (eyebrow, labels, UI,
|
|
1425
|
+
// values) / 600 semibold (title, series names, annotation lede) / 700 bold.
|
|
1092
1426
|
weights: {
|
|
1093
|
-
normal:
|
|
1094
|
-
medium:
|
|
1095
|
-
semibold:
|
|
1427
|
+
normal: 400,
|
|
1428
|
+
medium: 500,
|
|
1429
|
+
semibold: 600,
|
|
1096
1430
|
bold: 700
|
|
1097
1431
|
}
|
|
1098
1432
|
},
|
|
1099
1433
|
spacing: {
|
|
1100
1434
|
padding: 20,
|
|
1101
|
-
chromeGap:
|
|
1435
|
+
chromeGap: 6,
|
|
1102
1436
|
chromeToChart: 8,
|
|
1103
1437
|
chartToFooter: 8,
|
|
1104
1438
|
axisMargin: 6,
|
|
@@ -1110,17 +1444,18 @@ var DEFAULT_THEME = {
|
|
|
1110
1444
|
// so the gap holds regardless of font size.
|
|
1111
1445
|
xAxisLabelPadding: 4
|
|
1112
1446
|
},
|
|
1113
|
-
|
|
1447
|
+
/** Containers and tooltips. Marks carry their own 2px radius constant. */
|
|
1448
|
+
borderRadius: 8,
|
|
1114
1449
|
chrome: {
|
|
1115
1450
|
eyebrow: {
|
|
1116
1451
|
fontSize: 11,
|
|
1117
|
-
fontWeight:
|
|
1452
|
+
fontWeight: 500,
|
|
1118
1453
|
color: "#06b6d4",
|
|
1119
1454
|
lineHeight: 1.4
|
|
1120
1455
|
},
|
|
1121
1456
|
title: {
|
|
1122
1457
|
fontSize: 26,
|
|
1123
|
-
fontWeight:
|
|
1458
|
+
fontWeight: 600,
|
|
1124
1459
|
color: "#09090b",
|
|
1125
1460
|
lineHeight: 1.15
|
|
1126
1461
|
},
|
|
@@ -1149,6 +1484,8 @@ var DEFAULT_THEME = {
|
|
|
1149
1484
|
lineHeight: 1.4
|
|
1150
1485
|
}
|
|
1151
1486
|
},
|
|
1487
|
+
/** No editorial rule by default; the `broadsheet` preset sets one. */
|
|
1488
|
+
rule: null,
|
|
1152
1489
|
seriesStrategy: "palette"
|
|
1153
1490
|
};
|
|
1154
1491
|
|
|
@@ -1215,9 +1552,15 @@ function adaptTheme(theme) {
|
|
|
1215
1552
|
const lightChrome = DEFAULT_THEME.chrome;
|
|
1216
1553
|
const overridden = (current, lightDefault, darkDefault) => current === lightDefault ? darkDefault : current;
|
|
1217
1554
|
const darkText = pairs?.["colors.text"] ? pairs["colors.text"].dark : overridden(theme.colors.text, light.text, DARK_TEXT);
|
|
1218
|
-
const darkGridline = pairs?.["colors.gridline"] ? pairs["colors.gridline"].dark : overridden(theme.colors.gridline, light.gridline, "rgba(255,255,255,0.
|
|
1555
|
+
const darkGridline = pairs?.["colors.gridline"] ? pairs["colors.gridline"].dark : overridden(theme.colors.gridline, light.gridline, "rgba(255,255,255,0.06)");
|
|
1556
|
+
const darkHairline = pairs?.["colors.hairline"] ? pairs["colors.hairline"].dark : overridden(theme.colors.hairline, light.hairline, "rgba(255,255,255,0.14)");
|
|
1219
1557
|
const darkAxis = pairs?.["colors.axis"] ? pairs["colors.axis"].dark : overridden(theme.colors.axis, light.axis, "#a1a1aa");
|
|
1220
|
-
const categorical = theme.colors.categorical
|
|
1558
|
+
const categorical = overridden(theme.colors.categorical, light.categorical, [
|
|
1559
|
+
...CATEGORICAL_PALETTE_DARK
|
|
1560
|
+
]);
|
|
1561
|
+
const categoricalFill = overridden(theme.colors.categoricalFill, light.categoricalFill, [
|
|
1562
|
+
...CATEGORICAL_FILL_PALETTE_DARK
|
|
1563
|
+
]);
|
|
1221
1564
|
return {
|
|
1222
1565
|
...theme,
|
|
1223
1566
|
isDark: true,
|
|
@@ -1230,8 +1573,11 @@ function adaptTheme(theme) {
|
|
|
1230
1573
|
annotationFill: pairs?.["colors.annotationFill"] ? pairs["colors.annotationFill"].dark : overridden(theme.colors.annotationFill, light.annotationFill, "rgba(255,255,255,0.06)"),
|
|
1231
1574
|
annotationText: pairs?.["colors.annotationText"] ? pairs["colors.annotationText"].dark : overridden(theme.colors.annotationText, light.annotationText, darkMuted),
|
|
1232
1575
|
categorical,
|
|
1233
|
-
|
|
1234
|
-
|
|
1576
|
+
categoricalFill,
|
|
1577
|
+
hairline: darkHairline,
|
|
1578
|
+
positive: pairs?.["colors.positive"] ? pairs["colors.positive"].dark : overridden(theme.colors.positive, light.positive, "#4ade80"),
|
|
1579
|
+
negative: pairs?.["colors.negative"] ? pairs["colors.negative"].dark : overridden(theme.colors.negative, light.negative, "#f87171"),
|
|
1580
|
+
neutral: deriveNeutralRamp(darkText, darkBg, true)
|
|
1235
1581
|
},
|
|
1236
1582
|
chrome: {
|
|
1237
1583
|
// Eyebrow keeps its accent tint (cyan in both modes) unless the user
|
|
@@ -1261,7 +1607,10 @@ function adaptTheme(theme) {
|
|
|
1261
1607
|
...theme.chrome.footer,
|
|
1262
1608
|
color: pairs?.["chrome.footer.color"] ? pairs["chrome.footer.color"].dark : overridden(theme.chrome.footer.color, lightChrome.footer.color, darkMuted)
|
|
1263
1609
|
}
|
|
1264
|
-
}
|
|
1610
|
+
},
|
|
1611
|
+
// The editorial rule is a brand device: it keeps its authored color unless
|
|
1612
|
+
// the theme gave an explicit dark half of the pair.
|
|
1613
|
+
rule: theme.rule && pairs?.["rule.color"] ? { ...theme.rule, color: pairs["rule.color"].dark } : theme.rule
|
|
1265
1614
|
};
|
|
1266
1615
|
}
|
|
1267
1616
|
function adaptForLightLineStroke(color) {
|
|
@@ -1324,6 +1673,71 @@ var wire = {
|
|
|
1324
1673
|
},
|
|
1325
1674
|
borderRadius: 2
|
|
1326
1675
|
};
|
|
1676
|
+
var broadsheet = {
|
|
1677
|
+
colors: {
|
|
1678
|
+
categorical: ["#1d5f8a", "#e3120b", "#7fb0d3", "#e0a63b", "#3a8a6e", "#8c8680"],
|
|
1679
|
+
categoricalFill: ["#3b7097", "#ee493a", "#97c2e2", "#efbb64", "#579a81", "#9e9893"],
|
|
1680
|
+
background: { light: "#fffdf9", dark: "#171513" },
|
|
1681
|
+
text: { light: "#1a1714", dark: "#f4f1ea" },
|
|
1682
|
+
gridline: { light: "rgba(26,23,20,0.08)", dark: "rgba(244,241,234,0.07)" },
|
|
1683
|
+
// Heavier than the default 0.14: newsprint rules are drawn to be seen.
|
|
1684
|
+
hairline: { light: "rgba(26,23,20,0.28)", dark: "rgba(244,241,234,0.28)" },
|
|
1685
|
+
axis: { light: "#6b645c", dark: "#a8a099" },
|
|
1686
|
+
annotationFill: { light: "rgba(26,23,20,0.05)", dark: "rgba(244,241,234,0.07)" },
|
|
1687
|
+
annotationText: { light: "#6b645c", dark: "#a8a099" }
|
|
1688
|
+
},
|
|
1689
|
+
fonts: {
|
|
1690
|
+
sizes: { title: 24, subtitle: 14, body: 13, small: 11 }
|
|
1691
|
+
},
|
|
1692
|
+
spacing: {
|
|
1693
|
+
padding: 20,
|
|
1694
|
+
chromeGap: 8,
|
|
1695
|
+
chromeToChart: 16,
|
|
1696
|
+
chartToFooter: 12
|
|
1697
|
+
},
|
|
1698
|
+
chrome: {
|
|
1699
|
+
eyebrow: { color: { light: "#e3120b", dark: "#f4463f" }, fontSize: 11, fontWeight: 700 },
|
|
1700
|
+
title: { fontSize: 24, fontWeight: 700, lineHeight: 1.18 },
|
|
1701
|
+
subtitle: { fontWeight: 400, lineHeight: 1.45 }
|
|
1702
|
+
},
|
|
1703
|
+
/** The masthead device: a short red bar above the eyebrow. */
|
|
1704
|
+
rule: { color: { light: "#e3120b", dark: "#f4463f" }, width: 40, thickness: 3 },
|
|
1705
|
+
borderRadius: 0
|
|
1706
|
+
};
|
|
1707
|
+
var terminal = {
|
|
1708
|
+
colors: {
|
|
1709
|
+
categorical: [...CATEGORICAL_PALETTE_DARK],
|
|
1710
|
+
categoricalFill: [...CATEGORICAL_FILL_PALETTE_DARK],
|
|
1711
|
+
background: { light: "#0b0f14", dark: "#0b0f14" },
|
|
1712
|
+
text: { light: "#e6edf3", dark: "#e6edf3" },
|
|
1713
|
+
gridline: { light: "rgba(230,237,243,0.06)", dark: "rgba(230,237,243,0.06)" },
|
|
1714
|
+
hairline: { light: "rgba(230,237,243,0.14)", dark: "rgba(230,237,243,0.14)" },
|
|
1715
|
+
// 6.25:1 on #0b0f14 — tick labels are drawn in this color too.
|
|
1716
|
+
axis: { light: "#8b949e", dark: "#8b949e" },
|
|
1717
|
+
annotationFill: { light: "rgba(230,237,243,0.06)", dark: "rgba(230,237,243,0.06)" },
|
|
1718
|
+
annotationText: { light: "#8b949e", dark: "#8b949e" },
|
|
1719
|
+
positive: { light: "#4ade80", dark: "#4ade80" },
|
|
1720
|
+
negative: { light: "#f87171", dark: "#f87171" }
|
|
1721
|
+
},
|
|
1722
|
+
fonts: {
|
|
1723
|
+
family: '"Inter Variable", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
1724
|
+
mono: '"JetBrains Mono", "Fira Code", "Cascadia Code", monospace',
|
|
1725
|
+
sizes: { title: 15, subtitle: 12, body: 12, small: 10, axisTick: 10 }
|
|
1726
|
+
},
|
|
1727
|
+
spacing: {
|
|
1728
|
+
padding: 12,
|
|
1729
|
+
chromeGap: 3,
|
|
1730
|
+
chromeToChart: 6,
|
|
1731
|
+
chartToFooter: 6
|
|
1732
|
+
},
|
|
1733
|
+
chrome: {
|
|
1734
|
+
eyebrow: { color: { light: "#22d3ee", dark: "#22d3ee" }, fontSize: 10, fontWeight: 500 },
|
|
1735
|
+
title: { fontSize: 15, fontWeight: 600, lineHeight: 1.3 },
|
|
1736
|
+
subtitle: { fontWeight: 400, lineHeight: 1.4 }
|
|
1737
|
+
},
|
|
1738
|
+
borderRadius: 4,
|
|
1739
|
+
seriesStrategy: "accent-neutral"
|
|
1740
|
+
};
|
|
1327
1741
|
|
|
1328
1742
|
// src/theme/resolve.ts
|
|
1329
1743
|
function deepMerge(target, source) {
|
|
@@ -1350,11 +1764,13 @@ function themeConfigToPartial(config) {
|
|
|
1350
1764
|
colors.categorical = config.colors;
|
|
1351
1765
|
} else {
|
|
1352
1766
|
if (config.colors.categorical) colors.categorical = config.colors.categorical;
|
|
1767
|
+
if (config.colors.categoricalFill) colors.categoricalFill = config.colors.categoricalFill;
|
|
1353
1768
|
if (config.colors.sequential) colors.sequential = config.colors.sequential;
|
|
1354
1769
|
if (config.colors.diverging) colors.diverging = config.colors.diverging;
|
|
1355
1770
|
if (config.colors.background) colors.background = resolveTokenLight(config.colors.background);
|
|
1356
1771
|
if (config.colors.text) colors.text = resolveTokenLight(config.colors.text);
|
|
1357
1772
|
if (config.colors.gridline) colors.gridline = resolveTokenLight(config.colors.gridline);
|
|
1773
|
+
if (config.colors.hairline) colors.hairline = resolveTokenLight(config.colors.hairline);
|
|
1358
1774
|
if (config.colors.axis) colors.axis = resolveTokenLight(config.colors.axis);
|
|
1359
1775
|
if (config.colors.annotationFill)
|
|
1360
1776
|
colors.annotationFill = resolveTokenLight(config.colors.annotationFill);
|
|
@@ -1363,6 +1779,9 @@ function themeConfigToPartial(config) {
|
|
|
1363
1779
|
if (config.colors.positive) colors.positive = resolveTokenLight(config.colors.positive);
|
|
1364
1780
|
if (config.colors.negative) colors.negative = resolveTokenLight(config.colors.negative);
|
|
1365
1781
|
}
|
|
1782
|
+
if (colors.categorical && !colors.categoricalFill) {
|
|
1783
|
+
colors.categoricalFill = colors.categorical;
|
|
1784
|
+
}
|
|
1366
1785
|
partial.colors = colors;
|
|
1367
1786
|
}
|
|
1368
1787
|
if (config.fonts) {
|
|
@@ -1408,6 +1827,13 @@ function themeConfigToPartial(config) {
|
|
|
1408
1827
|
}
|
|
1409
1828
|
partial.chrome = chrome;
|
|
1410
1829
|
}
|
|
1830
|
+
if (config.rule) {
|
|
1831
|
+
partial.rule = {
|
|
1832
|
+
color: resolveTokenLight(config.rule.color),
|
|
1833
|
+
width: config.rule.width,
|
|
1834
|
+
thickness: config.rule.thickness
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1411
1837
|
if (config.seriesStrategy) {
|
|
1412
1838
|
partial.seriesStrategy = config.seriesStrategy;
|
|
1413
1839
|
}
|
|
@@ -1420,6 +1846,7 @@ function collectTokenPairs(config) {
|
|
|
1420
1846
|
"background",
|
|
1421
1847
|
"text",
|
|
1422
1848
|
"gridline",
|
|
1849
|
+
"hairline",
|
|
1423
1850
|
"axis",
|
|
1424
1851
|
"annotationFill",
|
|
1425
1852
|
"annotationText",
|
|
@@ -1433,6 +1860,9 @@ function collectTokenPairs(config) {
|
|
|
1433
1860
|
}
|
|
1434
1861
|
}
|
|
1435
1862
|
}
|
|
1863
|
+
if (config.rule?.color && typeof config.rule.color === "object" && "light" in config.rule.color) {
|
|
1864
|
+
pairs["rule.color"] = config.rule.color;
|
|
1865
|
+
}
|
|
1436
1866
|
if (config.chrome) {
|
|
1437
1867
|
for (const [element, value] of Object.entries(config.chrome)) {
|
|
1438
1868
|
if (value && typeof value === "object" && "color" in value && value.color && typeof value.color === "object" && "light" in value.color) {
|
|
@@ -1491,9 +1921,9 @@ function adjustOpacity(hex, opacity) {
|
|
|
1491
1921
|
const r = parseInt(m[1].slice(0, 2), 16);
|
|
1492
1922
|
const g = parseInt(m[1].slice(2, 4), 16);
|
|
1493
1923
|
const b = parseInt(m[1].slice(4, 6), 16);
|
|
1494
|
-
const
|
|
1924
|
+
const mix2 = (c) => Math.round(c * opacity + 128 * (1 - opacity));
|
|
1495
1925
|
const toHex = (n) => n.toString(16).padStart(2, "0");
|
|
1496
|
-
return `#${toHex(
|
|
1926
|
+
return `#${toHex(mix2(r))}${toHex(mix2(g))}${toHex(mix2(b))}`;
|
|
1497
1927
|
}
|
|
1498
1928
|
function resolveTheme(userTheme, base = DEFAULT_THEME) {
|
|
1499
1929
|
let merged = userTheme ? deepMerge(base, themeConfigToPartial(userTheme)) : { ...base };
|
|
@@ -1523,6 +1953,10 @@ function resolveTheme(userTheme, base = DEFAULT_THEME) {
|
|
|
1523
1953
|
}
|
|
1524
1954
|
return {
|
|
1525
1955
|
...merged,
|
|
1956
|
+
colors: {
|
|
1957
|
+
...merged.colors,
|
|
1958
|
+
neutral: deriveNeutralRamp(merged.colors.text, merged.colors.background, dark)
|
|
1959
|
+
},
|
|
1526
1960
|
isDark: dark,
|
|
1527
1961
|
_tokenPairs: userTheme ? collectTokenPairs(userTheme) : void 0
|
|
1528
1962
|
};
|
|
@@ -1626,6 +2060,19 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
|
|
|
1626
2060
|
let topY = pad;
|
|
1627
2061
|
const topElements = {};
|
|
1628
2062
|
const eyebrowNorm = chromeMode === "compact" ? null : normalizeChromeText(chrome.eyebrow);
|
|
2063
|
+
const titleNormPeek = normalizeChromeText(chrome.title);
|
|
2064
|
+
const subtitleNormPeek = chromeMode === "compact" ? null : normalizeChromeText(chrome.subtitle);
|
|
2065
|
+
let ruleElement;
|
|
2066
|
+
if (theme.rule && (eyebrowNorm || titleNormPeek || subtitleNormPeek)) {
|
|
2067
|
+
ruleElement = {
|
|
2068
|
+
x: pad,
|
|
2069
|
+
y: topY,
|
|
2070
|
+
width: theme.rule.width,
|
|
2071
|
+
thickness: theme.rule.thickness,
|
|
2072
|
+
color: theme.rule.color
|
|
2073
|
+
};
|
|
2074
|
+
topY += theme.rule.thickness + chromeGap;
|
|
2075
|
+
}
|
|
1629
2076
|
if (eyebrowNorm) {
|
|
1630
2077
|
const style = buildTextStyle(
|
|
1631
2078
|
theme.chrome.eyebrow,
|
|
@@ -1647,7 +2094,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
|
|
|
1647
2094
|
topElements.eyebrow = element;
|
|
1648
2095
|
topY += estimateTextHeight(style.fontSize, lineCount, style.lineHeight) + chromeGap;
|
|
1649
2096
|
}
|
|
1650
|
-
const titleNorm =
|
|
2097
|
+
const titleNorm = titleNormPeek;
|
|
1651
2098
|
if (titleNorm) {
|
|
1652
2099
|
const style = buildTextStyle(
|
|
1653
2100
|
theme.chrome.title,
|
|
@@ -1669,7 +2116,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
|
|
|
1669
2116
|
topElements.title = element;
|
|
1670
2117
|
topY += estimateTextHeight(style.fontSize, lineCount, style.lineHeight) + chromeGap;
|
|
1671
2118
|
}
|
|
1672
|
-
const subtitleNorm =
|
|
2119
|
+
const subtitleNorm = subtitleNormPeek;
|
|
1673
2120
|
if (subtitleNorm) {
|
|
1674
2121
|
const style = buildTextStyle(
|
|
1675
2122
|
theme.chrome.subtitle,
|
|
@@ -1705,6 +2152,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
|
|
|
1705
2152
|
return {
|
|
1706
2153
|
topHeight,
|
|
1707
2154
|
bottomHeight: compactBottom,
|
|
2155
|
+
...ruleElement ? { rule: ruleElement } : {},
|
|
1708
2156
|
...topElements
|
|
1709
2157
|
};
|
|
1710
2158
|
}
|
|
@@ -1811,6 +2259,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
|
|
|
1811
2259
|
return {
|
|
1812
2260
|
topHeight,
|
|
1813
2261
|
bottomHeight,
|
|
2262
|
+
...ruleElement ? { rule: ruleElement } : {},
|
|
1814
2263
|
...topElements,
|
|
1815
2264
|
...bottomElements,
|
|
1816
2265
|
...brandElement ? { brand: brandElement } : {}
|
|
@@ -2072,6 +2521,12 @@ var D3_FORMAT_SUFFIX_RE = /^(.*~?[efgsrdxXobcnp%])(.+)$/;
|
|
|
2072
2521
|
function buildD3Formatter(formatStr) {
|
|
2073
2522
|
if (!formatStr) return null;
|
|
2074
2523
|
if (formatStr === "ordinal") return formatOrdinal;
|
|
2524
|
+
if (formatStr === "compact") {
|
|
2525
|
+
return (v) => {
|
|
2526
|
+
if (!Number.isFinite(v)) return String(v);
|
|
2527
|
+
return Math.abs(v) >= 1e3 ? abbreviateNumber(v) : formatNumber(v);
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2075
2530
|
try {
|
|
2076
2531
|
const fmt = d3Format(formatStr);
|
|
2077
2532
|
if (formatStr.includes("s")) {
|
|
@@ -2142,6 +2597,12 @@ function isYearContext(ctx) {
|
|
|
2142
2597
|
function resolveNumberFormatter(formatStr, ctx) {
|
|
2143
2598
|
if (!formatStr) return null;
|
|
2144
2599
|
if (formatStr === "ordinal") return formatOrdinal;
|
|
2600
|
+
if (formatStr === "compact") {
|
|
2601
|
+
return (v) => {
|
|
2602
|
+
if (!Number.isFinite(v)) return String(v);
|
|
2603
|
+
return Math.abs(v) >= 1e3 ? abbreviateNumber(v) : formatNumber(v);
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2145
2606
|
if (formatStr === "percent") {
|
|
2146
2607
|
const fraction = ctx?.extent ? Math.max(Math.abs(ctx.extent[0]), Math.abs(ctx.extent[1])) <= 1 : true;
|
|
2147
2608
|
return (v) => formatPercent(v, { fraction });
|
|
@@ -2583,168 +3044,6 @@ function deepMergeSpec(target, source) {
|
|
|
2583
3044
|
}
|
|
2584
3045
|
return result;
|
|
2585
3046
|
}
|
|
2586
|
-
|
|
2587
|
-
// src/styles/token-definitions.ts
|
|
2588
|
-
var CSS_TOKEN_ROOT_SELECTORS = [
|
|
2589
|
-
".oc-root",
|
|
2590
|
-
".oc-chart-root",
|
|
2591
|
-
".oc-table-wrapper",
|
|
2592
|
-
".oc-table-root",
|
|
2593
|
-
".oc-graph-wrapper",
|
|
2594
|
-
".oc-graph-root",
|
|
2595
|
-
".oc-sankey-root",
|
|
2596
|
-
".oc-tilemap-root",
|
|
2597
|
-
".oc-barlist-root",
|
|
2598
|
-
".oc-map-root"
|
|
2599
|
-
];
|
|
2600
|
-
var CSS_TOKENS = [
|
|
2601
|
-
{
|
|
2602
|
-
name: "--oc-font-family",
|
|
2603
|
-
light: '\n "Inter Variable", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
2604
|
-
},
|
|
2605
|
-
{
|
|
2606
|
-
name: "--oc-font-mono",
|
|
2607
|
-
light: '"JetBrains Mono", "Fira Code", "Cascadia Code", monospace'
|
|
2608
|
-
},
|
|
2609
|
-
{
|
|
2610
|
-
section: "Animation easing presets via CSS linear()",
|
|
2611
|
-
name: "--oc-ease-smooth",
|
|
2612
|
-
light: "linear(\n 0,\n 0.157,\n 0.438,\n 0.64,\n 0.766,\n 0.85,\n 0.906,\n 0.941,\n 0.964,\n 0.978,\n 0.988,\n 0.994,\n 0.998,\n 1\n )"
|
|
2613
|
-
},
|
|
2614
|
-
{
|
|
2615
|
-
name: "--oc-ease-snappy",
|
|
2616
|
-
light: "linear(\n 0,\n 0.012,\n 0.048,\n 0.108,\n 0.194,\n 0.302,\n 0.426,\n 0.559,\n 0.69,\n 0.808,\n 0.905,\n 0.973,\n 1.013,\n 1.028,\n 1.023,\n 1.006,\n 0.984,\n 0.966,\n 0.957,\n 0.957,\n 0.964,\n 0.975,\n 0.986,\n 0.995,\n 1,\n 1.003,\n 1.002,\n 1,\n 0.998,\n 0.998,\n 0.999,\n 1\n )",
|
|
2617
|
-
consumers: "EASE_VAR_MAP in vanilla renderers"
|
|
2618
|
-
},
|
|
2619
|
-
{ section: "Animation timing defaults", name: "--oc-animation-duration", light: "500ms" },
|
|
2620
|
-
{ name: "--oc-animation-stagger", light: "80ms" },
|
|
2621
|
-
{ name: "--oc-annotation-delay", light: "200ms" },
|
|
2622
|
-
{ section: "Typography scale (editorial design system)", name: "--oc-title-size", light: "26px" },
|
|
2623
|
-
{ name: "--oc-title-weight", light: "590" },
|
|
2624
|
-
{ name: "--oc-title-tracking", light: "-0.022em" },
|
|
2625
|
-
{ name: "--oc-subtitle-size", light: "14px" },
|
|
2626
|
-
{ name: "--oc-subtitle-weight", light: "450" },
|
|
2627
|
-
{ name: "--oc-source-size", light: "11px" },
|
|
2628
|
-
{ name: "--oc-source-weight", light: "450" },
|
|
2629
|
-
{ name: "--oc-body-size", light: "13px" },
|
|
2630
|
-
{ name: "--oc-eyebrow-size", light: "11px" },
|
|
2631
|
-
{ name: "--oc-eyebrow-weight", light: "550" },
|
|
2632
|
-
{ name: "--oc-eyebrow-tracking", light: "0.08em" },
|
|
2633
|
-
{ section: "Surfaces (light mode defaults)", name: "--oc-bg", light: "#ffffff", dark: "#09090b" },
|
|
2634
|
-
{ name: "--oc-card", light: "#ffffff", dark: "#111113" },
|
|
2635
|
-
{
|
|
2636
|
-
name: "--oc-secondary",
|
|
2637
|
-
light: "#f4f4f5",
|
|
2638
|
-
dark: "#27272a",
|
|
2639
|
-
comment: "zinc-100, raised surface"
|
|
2640
|
-
},
|
|
2641
|
-
{
|
|
2642
|
-
section: `Text levels. Names invert across modes: in light mode "muted" sits at
|
|
2643
|
-
* a lighter zinc step than "subtle" because both are picked relative to
|
|
2644
|
-
* the active background, not from a fixed lightness ladder. The intent
|
|
2645
|
-
* is "muted = first step away from primary text"; "subtle = next step
|
|
2646
|
-
* down"; etc.
|
|
2647
|
-
*
|
|
2648
|
-
* Light mode: text=zinc-950, secondary=zinc-700, muted=zinc-500,
|
|
2649
|
-
* subtle=zinc-400, faint=zinc-300
|
|
2650
|
-
* Dark mode (dark.css): inverts the surface tokens but keeps the same
|
|
2651
|
-
* muted -> subtle -> faint progression away from primary.`,
|
|
2652
|
-
name: "--oc-text",
|
|
2653
|
-
light: "#09090b",
|
|
2654
|
-
dark: "#f7f8f8"
|
|
2655
|
-
},
|
|
2656
|
-
{ name: "--oc-text-secondary", light: "#3f3f46", dark: "#d0d6e0" },
|
|
2657
|
-
{ name: "--oc-text-muted", light: "#71717a", dark: "#a1a1aa" },
|
|
2658
|
-
{ name: "--oc-text-faint", light: "#d4d4d8", dark: "#52525b" },
|
|
2659
|
-
{
|
|
2660
|
-
section: "Lines",
|
|
2661
|
-
name: "--oc-gridline",
|
|
2662
|
-
light: "rgba(0, 0, 0, 0.1)",
|
|
2663
|
-
dark: "rgba(255, 255, 255, 0.05)"
|
|
2664
|
-
},
|
|
2665
|
-
{
|
|
2666
|
-
name: "--oc-axis",
|
|
2667
|
-
light: "rgba(0, 0, 0, 0.1)",
|
|
2668
|
-
dark: "rgba(255, 255, 255, 0.1)"
|
|
2669
|
-
},
|
|
2670
|
-
{
|
|
2671
|
-
name: "--oc-border",
|
|
2672
|
-
light: "rgba(0, 0, 0, 0.08)",
|
|
2673
|
-
dark: "rgba(255, 255, 255, 0.1)"
|
|
2674
|
-
},
|
|
2675
|
-
{ name: "--oc-border-radius", light: "2px" },
|
|
2676
|
-
{
|
|
2677
|
-
section: "Brand and semantic",
|
|
2678
|
-
name: "--oc-accent",
|
|
2679
|
-
light: "#06b6d4",
|
|
2680
|
-
dark: "#06b6d4"
|
|
2681
|
-
},
|
|
2682
|
-
{
|
|
2683
|
-
name: "--oc-accent-strong",
|
|
2684
|
-
light: "#0891b2",
|
|
2685
|
-
dark: "#06b6d4",
|
|
2686
|
-
comment: "darker cyan for line strokes on light bg",
|
|
2687
|
-
consumers: "stamped via adaptForLightLineStroke"
|
|
2688
|
-
},
|
|
2689
|
-
{ name: "--oc-positive", light: "#10b981", dark: "#34d399" },
|
|
2690
|
-
{ name: "--oc-negative", light: "#e11d48", dark: "#fb7185" },
|
|
2691
|
-
{ name: "--oc-focus", light: "#3b82f6", dark: "#60a5fa" },
|
|
2692
|
-
{
|
|
2693
|
-
name: "--oc-focus-ring",
|
|
2694
|
-
light: "rgba(59, 130, 246, 0.1)",
|
|
2695
|
-
comment: "static, intentionally NOT derived from --oc-focus and NOT flipped in dark mode"
|
|
2696
|
-
},
|
|
2697
|
-
{
|
|
2698
|
-
name: "--oc-focus-ring-strong",
|
|
2699
|
-
light: "rgba(59, 130, 246, 0.25)"
|
|
2700
|
-
},
|
|
2701
|
-
{
|
|
2702
|
-
name: "--oc-editable-hover",
|
|
2703
|
-
light: "rgba(79, 70, 229, 0.35)",
|
|
2704
|
-
comment: "edit-mode hover outline, indigo one-off"
|
|
2705
|
-
},
|
|
2706
|
-
{
|
|
2707
|
-
section: "Spacing scale (4px base) \u2014 only --oc-space-2/4 are consumed (JS-stamped)",
|
|
2708
|
-
name: "--oc-space-2",
|
|
2709
|
-
light: "8px",
|
|
2710
|
-
consumers: "stamped at mount from theme.spacing (theme-tokens.ts); component CSS must NOT consume these"
|
|
2711
|
-
},
|
|
2712
|
-
{
|
|
2713
|
-
name: "--oc-space-4",
|
|
2714
|
-
light: "16px",
|
|
2715
|
-
consumers: "stamped at mount from theme.spacing (theme-tokens.ts); component CSS must NOT consume these"
|
|
2716
|
-
},
|
|
2717
|
-
{
|
|
2718
|
-
section: "Interactive states",
|
|
2719
|
-
name: "--oc-hover-bg",
|
|
2720
|
-
light: "rgba(0, 0, 0, 0.025)",
|
|
2721
|
-
dark: "rgba(255, 255, 255, 0.05)"
|
|
2722
|
-
},
|
|
2723
|
-
{
|
|
2724
|
-
name: "--oc-tooltip-bg",
|
|
2725
|
-
light: "rgba(255, 255, 255, 0.88)",
|
|
2726
|
-
dark: "rgba(17, 17, 19, 0.92)"
|
|
2727
|
-
},
|
|
2728
|
-
{
|
|
2729
|
-
name: "--oc-tooltip-border",
|
|
2730
|
-
light: "rgba(0, 0, 0, 0.08)",
|
|
2731
|
-
dark: "rgba(255, 255, 255, 0.08)"
|
|
2732
|
-
},
|
|
2733
|
-
{
|
|
2734
|
-
name: "--oc-tooltip-shadow",
|
|
2735
|
-
light: "0 2px 8px rgba(0, 0, 0, 0.08), 0 0 1px rgba(0, 0, 0, 0.12)",
|
|
2736
|
-
dark: "0 2px 8px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.4)"
|
|
2737
|
-
},
|
|
2738
|
-
{ name: "--oc-tooltip-text", light: "#09090b", dark: "#f7f8f8" },
|
|
2739
|
-
{ name: "--oc-legend-text", light: "#3f3f46", dark: "#d0d6e0" }
|
|
2740
|
-
];
|
|
2741
|
-
var TOKEN_MAP = new Map(CSS_TOKENS.map((t) => [t.name, t]));
|
|
2742
|
-
function cssTokenDefault(name, mode) {
|
|
2743
|
-
const token = TOKEN_MAP.get(name);
|
|
2744
|
-
if (!token) throw new Error(`Unknown CSS token: ${name}`);
|
|
2745
|
-
if (mode === "dark" && token.dark !== void 0) return token.dark;
|
|
2746
|
-
return token.light;
|
|
2747
|
-
}
|
|
2748
3047
|
export {
|
|
2749
3048
|
ACHROMATIC_RAMP,
|
|
2750
3049
|
AXIS_TITLE_GAP,
|
|
@@ -2756,7 +3055,12 @@ export {
|
|
|
2756
3055
|
BRAND_RESERVE_WIDTH,
|
|
2757
3056
|
BREAKPOINT_COMPACT_MAX,
|
|
2758
3057
|
BREAKPOINT_MEDIUM_MAX,
|
|
3058
|
+
CATEGORICAL_EXTENDED_PALETTE,
|
|
3059
|
+
CATEGORICAL_FILL_PALETTE,
|
|
3060
|
+
CATEGORICAL_FILL_PALETTE_DARK,
|
|
2759
3061
|
CATEGORICAL_PALETTE,
|
|
3062
|
+
CATEGORICAL_PALETTE_DARK,
|
|
3063
|
+
COMPACT_MAX_X_TICKS,
|
|
2760
3064
|
COMPACT_WIDTH,
|
|
2761
3065
|
CSS_TOKENS,
|
|
2762
3066
|
CSS_TOKEN_ROOT_SELECTORS,
|
|
@@ -2766,6 +3070,7 @@ export {
|
|
|
2766
3070
|
FOOTNOTE_LINE_HEIGHT,
|
|
2767
3071
|
GRAPH_ENCODING_RULES,
|
|
2768
3072
|
HEIGHT_CRAMPED_MAX,
|
|
3073
|
+
HEIGHT_NO_GRID_MAX,
|
|
2769
3074
|
HEIGHT_SHORT_MAX,
|
|
2770
3075
|
HPAD_COMPACT_FRACTION,
|
|
2771
3076
|
HPAD_COMPACT_MIN,
|
|
@@ -2799,6 +3104,7 @@ export {
|
|
|
2799
3104
|
areaChart,
|
|
2800
3105
|
axisTitleOffset,
|
|
2801
3106
|
barChart,
|
|
3107
|
+
broadsheet,
|
|
2802
3108
|
buildCompactStepFormatter,
|
|
2803
3109
|
buildD3Formatter,
|
|
2804
3110
|
buildTemporalFormatter,
|
|
@@ -2813,6 +3119,7 @@ export {
|
|
|
2813
3119
|
dataTable,
|
|
2814
3120
|
deepMergeSpec,
|
|
2815
3121
|
defaultNumberFormatter,
|
|
3122
|
+
deriveNeutralRamp,
|
|
2816
3123
|
detectCollision,
|
|
2817
3124
|
donutChart,
|
|
2818
3125
|
dotChart,
|
|
@@ -2861,6 +3168,7 @@ export {
|
|
|
2861
3168
|
overlapArea,
|
|
2862
3169
|
pickLabelColor,
|
|
2863
3170
|
pieChart,
|
|
3171
|
+
resolveChromeEconomy,
|
|
2864
3172
|
resolveCollisions,
|
|
2865
3173
|
resolveMarkDef,
|
|
2866
3174
|
resolveMarkType,
|
|
@@ -2870,6 +3178,7 @@ export {
|
|
|
2870
3178
|
resolveTheme,
|
|
2871
3179
|
scatterChart,
|
|
2872
3180
|
simulateColorBlindness,
|
|
3181
|
+
terminal,
|
|
2873
3182
|
textAscent,
|
|
2874
3183
|
tileMap,
|
|
2875
3184
|
truncateRotatedLabel,
|