@arcfusionz/arc-analytic-ui 0.0.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.
Files changed (156) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +13 -0
  2. package/dist/components/AnomalyCard/AnomalyCard.chart.d.ts +64 -0
  3. package/dist/components/AnomalyCard/AnomalyCard.chart.js +341 -0
  4. package/dist/components/AnomalyCard/AnomalyCard.d.ts +169 -0
  5. package/dist/components/AnomalyCard/AnomalyCard.js +1019 -0
  6. package/dist/components/AnomalyCard/AnomalyCard.model.d.ts +508 -0
  7. package/dist/components/AnomalyCard/AnomalyCard.model.js +553 -0
  8. package/dist/components/AnomalyCard/index.d.ts +4 -0
  9. package/dist/components/AnomalyCard/index.js +4 -0
  10. package/dist/components/ChartCard/ChartCard.chart.d.ts +72 -0
  11. package/dist/components/ChartCard/ChartCard.chart.js +1333 -0
  12. package/dist/components/ChartCard/ChartCard.d.ts +206 -0
  13. package/dist/components/ChartCard/ChartCard.js +718 -0
  14. package/dist/components/ChartCard/ChartCard.model.d.ts +452 -0
  15. package/dist/components/ChartCard/ChartCard.model.js +1223 -0
  16. package/dist/components/ChartCard/index.d.ts +4 -0
  17. package/dist/components/ChartCard/index.js +4 -0
  18. package/dist/components/ComparisonCard/ComparisonCard.chart.d.ts +80 -0
  19. package/dist/components/ComparisonCard/ComparisonCard.chart.js +337 -0
  20. package/dist/components/ComparisonCard/ComparisonCard.d.ts +200 -0
  21. package/dist/components/ComparisonCard/ComparisonCard.js +1325 -0
  22. package/dist/components/ComparisonCard/ComparisonCard.model.d.ts +309 -0
  23. package/dist/components/ComparisonCard/ComparisonCard.model.js +263 -0
  24. package/dist/components/ComparisonCard/index.d.ts +4 -0
  25. package/dist/components/ComparisonCard/index.js +4 -0
  26. package/dist/components/ForecastCard/ForecastCard.chart.d.ts +82 -0
  27. package/dist/components/ForecastCard/ForecastCard.chart.js +416 -0
  28. package/dist/components/ForecastCard/ForecastCard.d.ts +148 -0
  29. package/dist/components/ForecastCard/ForecastCard.js +1029 -0
  30. package/dist/components/ForecastCard/ForecastCard.model.d.ts +497 -0
  31. package/dist/components/ForecastCard/ForecastCard.model.js +554 -0
  32. package/dist/components/ForecastCard/index.d.ts +4 -0
  33. package/dist/components/ForecastCard/index.js +4 -0
  34. package/dist/components/HighlightCard/HighlightCard.chart.d.ts +122 -0
  35. package/dist/components/HighlightCard/HighlightCard.chart.js +660 -0
  36. package/dist/components/HighlightCard/HighlightCard.d.ts +202 -0
  37. package/dist/components/HighlightCard/HighlightCard.js +1286 -0
  38. package/dist/components/HighlightCard/HighlightCard.model.d.ts +692 -0
  39. package/dist/components/HighlightCard/HighlightCard.model.js +875 -0
  40. package/dist/components/HighlightCard/index.d.ts +4 -0
  41. package/dist/components/HighlightCard/index.js +4 -0
  42. package/dist/components/InsightCard/InsightCard.chart.d.ts +91 -0
  43. package/dist/components/InsightCard/InsightCard.chart.js +471 -0
  44. package/dist/components/InsightCard/InsightCard.d.ts +161 -0
  45. package/dist/components/InsightCard/InsightCard.js +961 -0
  46. package/dist/components/InsightCard/InsightCard.model.d.ts +405 -0
  47. package/dist/components/InsightCard/InsightCard.model.js +421 -0
  48. package/dist/components/InsightCard/index.d.ts +4 -0
  49. package/dist/components/InsightCard/index.js +4 -0
  50. package/dist/components/MetricCard/MetricCard.chart.d.ts +79 -0
  51. package/dist/components/MetricCard/MetricCard.chart.js +435 -0
  52. package/dist/components/MetricCard/MetricCard.d.ts +227 -0
  53. package/dist/components/MetricCard/MetricCard.js +1293 -0
  54. package/dist/components/MetricCard/MetricCard.model.d.ts +521 -0
  55. package/dist/components/MetricCard/MetricCard.model.js +627 -0
  56. package/dist/components/MetricCard/index.d.ts +4 -0
  57. package/dist/components/MetricCard/index.js +4 -0
  58. package/dist/components/ProfileCard/ProfileCard.chart.d.ts +64 -0
  59. package/dist/components/ProfileCard/ProfileCard.chart.js +427 -0
  60. package/dist/components/ProfileCard/ProfileCard.d.ts +140 -0
  61. package/dist/components/ProfileCard/ProfileCard.js +735 -0
  62. package/dist/components/ProfileCard/ProfileCard.model.d.ts +279 -0
  63. package/dist/components/ProfileCard/ProfileCard.model.js +244 -0
  64. package/dist/components/ProfileCard/index.d.ts +4 -0
  65. package/dist/components/ProfileCard/index.js +4 -0
  66. package/dist/components/ProgressCard/ProgressCard.chart.d.ts +73 -0
  67. package/dist/components/ProgressCard/ProgressCard.chart.js +373 -0
  68. package/dist/components/ProgressCard/ProgressCard.d.ts +137 -0
  69. package/dist/components/ProgressCard/ProgressCard.js +908 -0
  70. package/dist/components/ProgressCard/ProgressCard.model.d.ts +385 -0
  71. package/dist/components/ProgressCard/ProgressCard.model.js +389 -0
  72. package/dist/components/ProgressCard/index.d.ts +4 -0
  73. package/dist/components/ProgressCard/index.js +4 -0
  74. package/dist/components/ReportCard/ReportCard.chart.d.ts +45 -0
  75. package/dist/components/ReportCard/ReportCard.chart.js +231 -0
  76. package/dist/components/ReportCard/ReportCard.d.ts +153 -0
  77. package/dist/components/ReportCard/ReportCard.js +1118 -0
  78. package/dist/components/ReportCard/ReportCard.model.d.ts +392 -0
  79. package/dist/components/ReportCard/ReportCard.model.js +399 -0
  80. package/dist/components/ReportCard/index.d.ts +4 -0
  81. package/dist/components/ReportCard/index.js +4 -0
  82. package/dist/components/SankeyCard/SankeyCard.chart.d.ts +75 -0
  83. package/dist/components/SankeyCard/SankeyCard.chart.js +249 -0
  84. package/dist/components/SankeyCard/SankeyCard.d.ts +185 -0
  85. package/dist/components/SankeyCard/SankeyCard.js +788 -0
  86. package/dist/components/SankeyCard/SankeyCard.model.d.ts +501 -0
  87. package/dist/components/SankeyCard/SankeyCard.model.js +789 -0
  88. package/dist/components/SankeyCard/index.d.ts +4 -0
  89. package/dist/components/SankeyCard/index.js +4 -0
  90. package/dist/components/StatusCard/StatusCard.chart.d.ts +60 -0
  91. package/dist/components/StatusCard/StatusCard.chart.js +188 -0
  92. package/dist/components/StatusCard/StatusCard.d.ts +176 -0
  93. package/dist/components/StatusCard/StatusCard.js +989 -0
  94. package/dist/components/StatusCard/StatusCard.model.d.ts +555 -0
  95. package/dist/components/StatusCard/StatusCard.model.js +776 -0
  96. package/dist/components/StatusCard/index.d.ts +4 -0
  97. package/dist/components/StatusCard/index.js +4 -0
  98. package/dist/components/TableCard/TableCard.chart.d.ts +53 -0
  99. package/dist/components/TableCard/TableCard.chart.js +192 -0
  100. package/dist/components/TableCard/TableCard.d.ts +140 -0
  101. package/dist/components/TableCard/TableCard.js +811 -0
  102. package/dist/components/TableCard/TableCard.model.d.ts +381 -0
  103. package/dist/components/TableCard/TableCard.model.js +475 -0
  104. package/dist/components/TableCard/index.d.ts +4 -0
  105. package/dist/components/TableCard/index.js +4 -0
  106. package/dist/components/TargetCard/TargetCard.chart.d.ts +52 -0
  107. package/dist/components/TargetCard/TargetCard.chart.js +273 -0
  108. package/dist/components/TargetCard/TargetCard.d.ts +143 -0
  109. package/dist/components/TargetCard/TargetCard.js +910 -0
  110. package/dist/components/TargetCard/TargetCard.model.d.ts +468 -0
  111. package/dist/components/TargetCard/TargetCard.model.js +581 -0
  112. package/dist/components/TargetCard/index.d.ts +4 -0
  113. package/dist/components/TargetCard/index.js +4 -0
  114. package/dist/components/TreeCard/TreeCard.chart.d.ts +80 -0
  115. package/dist/components/TreeCard/TreeCard.chart.js +178 -0
  116. package/dist/components/TreeCard/TreeCard.d.ts +182 -0
  117. package/dist/components/TreeCard/TreeCard.js +1187 -0
  118. package/dist/components/TreeCard/TreeCard.model.d.ts +330 -0
  119. package/dist/components/TreeCard/TreeCard.model.js +401 -0
  120. package/dist/components/TreeCard/index.d.ts +4 -0
  121. package/dist/components/TreeCard/index.js +4 -0
  122. package/dist/components/TrendCard/TrendCard.chart.d.ts +75 -0
  123. package/dist/components/TrendCard/TrendCard.chart.js +393 -0
  124. package/dist/components/TrendCard/TrendCard.d.ts +224 -0
  125. package/dist/components/TrendCard/TrendCard.js +1053 -0
  126. package/dist/components/TrendCard/TrendCard.model.d.ts +545 -0
  127. package/dist/components/TrendCard/TrendCard.model.js +623 -0
  128. package/dist/components/TrendCard/index.d.ts +4 -0
  129. package/dist/components/TrendCard/index.js +4 -0
  130. package/dist/index.d.ts +76 -0
  131. package/dist/index.js +57 -0
  132. package/dist/lib/vega/ArcVegaChart.d.ts +24 -0
  133. package/dist/lib/vega/ArcVegaChart.js +42 -0
  134. package/dist/lib/vega/ArcVegaSurface.d.ts +13 -0
  135. package/dist/lib/vega/ArcVegaSurface.js +38 -0
  136. package/dist/lib/vega/axisTicks.js +156 -0
  137. package/dist/lib/vega/config.d.ts +29 -0
  138. package/dist/lib/vega/config.js +113 -0
  139. package/dist/lib/vega/embed.d.ts +29 -0
  140. package/dist/lib/vega/embed.js +29 -0
  141. package/dist/lib/vega/engine.d.ts +10 -0
  142. package/dist/lib/vega/engine.js +6 -0
  143. package/dist/lib/vega/formatters.d.ts +125 -0
  144. package/dist/lib/vega/formatters.js +277 -0
  145. package/dist/lib/vega/index.d.ts +11 -0
  146. package/dist/lib/vega/load.d.ts +13 -0
  147. package/dist/lib/vega/load.js +21 -0
  148. package/dist/lib/vega/time.d.ts +45 -0
  149. package/dist/lib/vega/time.js +105 -0
  150. package/dist/lib/vega/tokens.d.ts +73 -0
  151. package/dist/lib/vega/tokens.js +137 -0
  152. package/dist/lib/vega/tooltip.d.ts +45 -0
  153. package/dist/lib/vega/tooltip.js +124 -0
  154. package/dist/lib/vega/useArcVega.d.ts +43 -0
  155. package/dist/lib/vega/useArcVega.js +121 -0
  156. package/package.json +60 -0
@@ -0,0 +1,113 @@
1
+ //#region src/lib/vega/config.ts
2
+ function withLocale(format, locale) {
3
+ return locale ? `${format}@${locale}` : format;
4
+ }
5
+ /**
6
+ * The Vega-Lite `config` that makes a chart an Arc chart: transparent
7
+ * background, the brand font stacks (Vega draws its own text, so the Thai
8
+ * fallback rides the stack string), a hairline solid grid on `chart-grid`,
9
+ * quiet axes and labels in the muted text role, ranges drawn from the
10
+ * `chart-*` slots, thin quiet marks, ARIA on, and the Intl-backed
11
+ * `arcNumber` / `arcTime` formatters as the default format types.
12
+ *
13
+ * Rebuild it whenever `tokens` change (a theme flip) — it is part of the spec,
14
+ * so that is the one legitimate re-embed.
15
+ */
16
+ function arcVegaConfig(tokens, { locale, timeZone, vegaLocale } = {}) {
17
+ const timeFormat = [
18
+ "day",
19
+ locale ?? "",
20
+ timeZone ?? ""
21
+ ].join("@").replace(/@+$/, "");
22
+ return {
23
+ background: "transparent",
24
+ font: tokens.fontSans,
25
+ view: { stroke: null },
26
+ axis: {
27
+ domainColor: tokens.axis,
28
+ domainWidth: 1,
29
+ gridColor: tokens.grid,
30
+ gridWidth: 1,
31
+ gridOpacity: 1,
32
+ tickColor: tokens.axis,
33
+ tickSize: 4,
34
+ labelColor: tokens.axisLabel,
35
+ labelFont: tokens.fontSans,
36
+ labelFontSize: 11,
37
+ labelPadding: 6,
38
+ labelOverlap: true,
39
+ titleColor: tokens.axisLabel,
40
+ titleFont: tokens.fontHeading,
41
+ titleFontSize: 11,
42
+ titleFontWeight: 500,
43
+ titlePadding: 8
44
+ },
45
+ legend: {
46
+ orient: "right",
47
+ labelColor: tokens.foreground,
48
+ labelFont: tokens.fontSans,
49
+ labelFontSize: 11,
50
+ titleColor: tokens.mutedForeground,
51
+ titleFont: tokens.fontHeading,
52
+ titleFontSize: 11,
53
+ titleFontWeight: 500,
54
+ symbolType: "circle",
55
+ symbolSize: 64
56
+ },
57
+ title: {
58
+ color: tokens.foreground,
59
+ font: tokens.fontHeading,
60
+ fontSize: 14,
61
+ fontWeight: 600,
62
+ anchor: "start",
63
+ subtitleColor: tokens.mutedForeground,
64
+ subtitleFont: tokens.fontSans,
65
+ subtitleFontSize: 12
66
+ },
67
+ range: {
68
+ category: tokens.category,
69
+ ordinal: tokens.sequential,
70
+ ramp: tokens.sequential,
71
+ heatmap: tokens.sequential,
72
+ diverging: [
73
+ tokens.divergingNegative,
74
+ tokens.divergingMid,
75
+ tokens.divergingPositive
76
+ ]
77
+ },
78
+ mark: { color: tokens.emphasis },
79
+ line: {
80
+ strokeWidth: 2,
81
+ strokeCap: "round",
82
+ strokeJoin: "round"
83
+ },
84
+ area: {
85
+ fillOpacity: .12,
86
+ line: false
87
+ },
88
+ bar: { cornerRadiusEnd: 2 },
89
+ rule: {
90
+ color: tokens.reference,
91
+ strokeWidth: 1
92
+ },
93
+ point: {
94
+ size: 64,
95
+ filled: true
96
+ },
97
+ circle: { size: 64 },
98
+ text: {
99
+ color: tokens.foreground,
100
+ font: tokens.fontSans,
101
+ fontSize: 11
102
+ },
103
+ customFormatTypes: true,
104
+ numberFormatType: "arcNumber",
105
+ numberFormat: withLocale("compact", locale),
106
+ timeFormatType: "arcTime",
107
+ timeFormat,
108
+ ...vegaLocale ? { locale: vegaLocale } : {},
109
+ aria: true
110
+ };
111
+ }
112
+ //#endregion
113
+ export { arcVegaConfig };
@@ -0,0 +1,29 @@
1
+ import { TooltipHandler } from "vega";
2
+ import { EmbedOptions } from "vega-embed";
3
+ //#region src/lib/vega/embed.d.ts
4
+ /**
5
+ * `vega.Warn`. The numeric level is used rather than the constant because the
6
+ * constant is not in vega's typings and importing vega here would pull the
7
+ * engine into a module that must stay engine-free.
8
+ */
9
+ declare const VEGA_WARN = 2;
10
+ interface ArcEmbedOptionsInput {
11
+ /** The bridge's tooltip handler (`useArcTooltipHandler().handler`), or `false` for none. */
12
+ tooltip?: TooltipHandler | false;
13
+ /** `svg` (default) gives an accessibility tree, DPR-crisp output and deterministic snapshots; `canvas` only for a measured case above the budget. */
14
+ renderer?: "svg" | "canvas";
15
+ /** Vega log level. Default `VEGA_WARN` — a warning is a spec bug to fix, never to silence. */
16
+ logLevel?: number;
17
+ }
18
+ /**
19
+ * vega-embed options for an Arc chart: Vega-Lite mode, no actions menu (an
20
+ * Arc `Menu` calling `view.toImageURL` replaces it), no injected stylesheet
21
+ * (consumer packages ship no CSS), vega-tooltip replaced by the bridge's
22
+ * handler, hover off.
23
+ *
24
+ * **Memoize the result** — react-vega deep-compares options on every render
25
+ * and a fresh object (or a fresh handler function) re-embeds the view.
26
+ */
27
+ declare function arcEmbedOptions({ tooltip, renderer, logLevel }?: ArcEmbedOptionsInput): EmbedOptions;
28
+ //#endregion
29
+ export { ArcEmbedOptionsInput, VEGA_WARN, arcEmbedOptions };
@@ -0,0 +1,29 @@
1
+ //#region src/lib/vega/embed.ts
2
+ /**
3
+ * `vega.Warn`. The numeric level is used rather than the constant because the
4
+ * constant is not in vega's typings and importing vega here would pull the
5
+ * engine into a module that must stay engine-free.
6
+ */
7
+ const VEGA_WARN = 2;
8
+ /**
9
+ * vega-embed options for an Arc chart: Vega-Lite mode, no actions menu (an
10
+ * Arc `Menu` calling `view.toImageURL` replaces it), no injected stylesheet
11
+ * (consumer packages ship no CSS), vega-tooltip replaced by the bridge's
12
+ * handler, hover off.
13
+ *
14
+ * **Memoize the result** — react-vega deep-compares options on every render
15
+ * and a fresh object (or a fresh handler function) re-embeds the view.
16
+ */
17
+ function arcEmbedOptions({ tooltip = false, renderer = "svg", logLevel = 2 } = {}) {
18
+ return {
19
+ mode: "vega-lite",
20
+ renderer,
21
+ actions: false,
22
+ defaultStyle: false,
23
+ hover: false,
24
+ tooltip,
25
+ logLevel
26
+ };
27
+ }
28
+ //#endregion
29
+ export { VEGA_WARN, arcEmbedOptions };
@@ -0,0 +1,10 @@
1
+ import { ArcVegaDatasets, UseArcVegaParams, useArcVega } from "./useArcVega.js";
2
+ import { ArcVegaSurface, ArcVegaSurfaceProps } from "./ArcVegaSurface.js";
3
+ import { changeset, expressionFunction as expressionFunction$1 } from "vega";
4
+ import { compile } from "vega-lite";
5
+ import { VegaEmbed, useVegaEmbed as useVegaEmbed$1 } from "react-vega";
6
+ declare namespace engine_d_exports {
7
+ export { ArcVegaDatasets, ArcVegaSurface, ArcVegaSurfaceProps, UseArcVegaParams, VegaEmbed, changeset, compile, expressionFunction$1 as expressionFunction, useArcVega, useVegaEmbed$1 as useVegaEmbed };
8
+ }
9
+ //#endregion
10
+ export { type ArcVegaDatasets, ArcVegaSurface, type ArcVegaSurfaceProps, type UseArcVegaParams, VegaEmbed, changeset, compile, engine_d_exports, expressionFunction$1 as expressionFunction, useArcVega, useVegaEmbed$1 as useVegaEmbed };
@@ -0,0 +1,6 @@
1
+ import { useArcVega } from "./useArcVega.js";
2
+ import { ArcVegaSurface } from "./ArcVegaSurface.js";
3
+ import { changeset, expressionFunction } from "vega";
4
+ import { compile } from "vega-lite";
5
+ import { VegaEmbed, useVegaEmbed } from "react-vega";
6
+ export { ArcVegaSurface, VegaEmbed, changeset, compile, expressionFunction, useArcVega, useVegaEmbed };
@@ -0,0 +1,125 @@
1
+ //#region src/lib/vega/formatters.d.ts
2
+ /**
3
+ * One formatting grammar shared by axes, tooltips, tiles, and tables, so the
4
+ * same value never rounds two ways (Few's precision pitfall). Everything is
5
+ * built on `Intl`, so locale and time zone are first-class, and the two Vega
6
+ * expression functions registered by `registerArcFormatters` call straight
7
+ * into these helpers.
8
+ *
9
+ * Number grammar — `kind[:arg[:arg]][@locale]`:
10
+ *
11
+ * "number" 0–2 fraction digits (Intl decimal) 1,234.5
12
+ * "number:0" fixed maximum fraction digits 1,235
13
+ * "integer" whole numbers 1,235
14
+ * "compact" short scale, 1 fraction digit 3.8M
15
+ * "compact:0" 4M
16
+ * "percent" ratio → percent, 1 fraction digit 12.4%
17
+ * "percent:0" 12%
18
+ * "currency:THB" Intl currency, default digits ฿3,848,305.93
19
+ * "currency:THB:0" fixed digits ฿3,848,306
20
+ * "currency:THB:compact" short scale ฿3.8M
21
+ * "compact@th-TH" any kind, with a locale suffix
22
+ *
23
+ * Time grammar — `granularity[@locale[@timeZone]]`, granularity one of
24
+ * `day` (8 Sep 2026), `month` (Sep 2026), `year`, `time` (16:30),
25
+ * `datetime` (8 Sep 2026, 16:30), plus the axis-length forms `day-short`
26
+ * (8 Sep), `day-time` (8 Sep, 4 PM) and `month-short` (Sep) for tick labels
27
+ * that have no room for a year — `day-time` is the one a window shorter than
28
+ * two days needs, where a bare clock time repeats at both ends. It carries the
29
+ * hour only: minutes on an axis label are noise, they push the text past the
30
+ * width an axis label is allowed, and the tooltip has them anyway. Thai products that use the Gregorian calendar pass
31
+ * `th-TH-u-ca-gregory`; a bare `th-TH` defaults to the Buddhist calendar.
32
+ */
33
+ /** Rendered for null, undefined, NaN and infinite values — a typographic dash, never "0". */
34
+ declare const MISSING_VALUE = "—";
35
+ type ArcNumberFormatKind = "number" | "integer" | "compact" | "percent" | "currency";
36
+ interface ArcNumberFormatSpec {
37
+ kind: ArcNumberFormatKind;
38
+ /** Maximum fraction digits, when the format fixes them. */
39
+ digits?: number;
40
+ /** Short-scale notation, for currency formats (`currency:THB:compact`). */
41
+ compact?: boolean;
42
+ /** ISO 4217 code for currency formats. */
43
+ currency?: string;
44
+ /** BCP 47 tag from the `@locale` suffix. */
45
+ locale?: string;
46
+ }
47
+ type ArcTimeGranularity = "day" | "month" | "year" | "time" | "datetime" | "day-short" | "day-time" | "month-short";
48
+ interface ArcTimeFormatSpec {
49
+ granularity: ArcTimeGranularity;
50
+ locale?: string;
51
+ timeZone?: string;
52
+ }
53
+ /** Parses the number grammar. Unknown kinds fall back to `number` rather than throwing — a bad format string must not take a chart down. */
54
+ declare function parseArcNumberFormat(format: string | undefined | null): ArcNumberFormatSpec;
55
+ /** Parses the time grammar. */
56
+ declare function parseArcTimeFormat(format: string | undefined | null): ArcTimeFormatSpec;
57
+ /**
58
+ * Formats a number through the grammar above. `extra` merges Intl options on
59
+ * top (deltas pass `{ signDisplay: "exceptZero" }`); a missing or non-finite
60
+ * value renders as `MISSING_VALUE`.
61
+ */
62
+ declare function formatNumber(value: number | null | undefined, format?: string, locale?: string, extra?: Intl.NumberFormatOptions): string;
63
+ /**
64
+ * Formats a date at a granularity. `timeZone` defaults to the runtime's; pass
65
+ * it explicitly wherever output must be deterministic (Chromatic, tests, or a
66
+ * product that reports in one business time zone).
67
+ */
68
+ declare function formatDate(value: Date | string | number | null | undefined, { granularity, locale, timeZone }?: Partial<ArcTimeFormatSpec>): string;
69
+ /**
70
+ * Formats a period between two dates at a granularity, collapsing the parts
71
+ * they share the way the locale does — `Aug 7 – 9, 2026`, `Aug 28 – Sep 8,
72
+ * 2026`, `7–9 ส.ค. 2026` — through `Intl.DateTimeFormat.formatRange`, with
73
+ * `start – end` as the fallback where a runtime lacks it. The bounds may come
74
+ * in either order; an unparseable bound renders as `MISSING_VALUE`.
75
+ */
76
+ declare function formatDateRange(start: Date | string | number | null | undefined, end: Date | string | number | null | undefined, { granularity, locale, timeZone }?: Partial<ArcTimeFormatSpec>): string;
77
+ type ArcDeltaMode = "percent" | "absolute";
78
+ type ArcDeltaDirection = "up" | "down" | "flat";
79
+ interface FormatDeltaOptions {
80
+ /** `percent` (default) is relative change; `absolute` is the difference in the value's own units. */
81
+ mode?: ArcDeltaMode;
82
+ /** The value's format — an absolute delta of a `percent` value is expressed in percentage points. */
83
+ format?: string;
84
+ locale?: string;
85
+ /** Fraction digits for a percent delta. Default 1. */
86
+ digits?: number;
87
+ /** Unit appended to a percentage-point delta. Default `" pp"`. */
88
+ pointsSuffix?: string;
89
+ }
90
+ interface FormattedDelta {
91
+ direction: ArcDeltaDirection;
92
+ /** Relative change as a ratio, or `null` when the previous value is zero (undefined). */
93
+ percent: number | null;
94
+ /** `current - previous`, in the value's units. */
95
+ absolute: number;
96
+ /** Signed text, e.g. `+12.4%`, `-0.3 pp`, `+฿120K`. */
97
+ text: string;
98
+ /** Which mode actually rendered: a percent delta from zero falls back to absolute. */
99
+ renderedMode: ArcDeltaMode;
100
+ }
101
+ /**
102
+ * Formats the change between two readings with the sign always shown. A
103
+ * relative change from zero is undefined, so `mode: "percent"` renders the
104
+ * absolute difference instead (and says so in `renderedMode`). An absolute
105
+ * delta of a percent-formatted value is in percentage points — never "-7%"
106
+ * for churn going from 4.1% to 3.8%.
107
+ */
108
+ declare function formatDelta(current: number, previous: number, { mode, format, locale, digits, pointsSuffix }?: FormatDeltaOptions): FormattedDelta;
109
+ type ExpressionFunctionRegistrar = (name: string, fn: (...args: never[]) => unknown) => unknown;
110
+ /**
111
+ * Registers `arcNumber`, `arcTime` and the responsive axis helpers
112
+ * `arcAxisTicks` / `arcAxisLabelAlign` as Vega expression functions, once.
113
+ * Takes `vega.expressionFunction` as an argument so this module never imports
114
+ * the engine itself; the engine-bound modules call it at load time, before the
115
+ * first `parse` — Vega requires that. Returns whether this call did the
116
+ * registering.
117
+ *
118
+ * Both axis helpers take `(domain, width, discrete, format, kind, anchors?)`:
119
+ * `kind` picks the label formatter (`time` → `arcTime`, `number` →
120
+ * `arcNumber`, else the raw category), and `anchors` — a highlighted window's
121
+ * `[start, end]` — switches from evenly spaced labels to the window's bounds.
122
+ */
123
+ declare function registerArcFormatters(expressionFunction: ExpressionFunctionRegistrar): boolean;
124
+ //#endregion
125
+ export { ArcDeltaDirection, ArcDeltaMode, ArcNumberFormatKind, ArcNumberFormatSpec, ArcTimeFormatSpec, ArcTimeGranularity, FormatDeltaOptions, FormattedDelta, MISSING_VALUE, formatDate, formatDateRange, formatDelta, formatNumber, parseArcNumberFormat, parseArcTimeFormat, registerArcFormatters };
@@ -0,0 +1,277 @@
1
+ import { axisLabelAlign, selectAxisTicks } from "./axisTicks.js";
2
+ //#region src/lib/vega/formatters.ts
3
+ /**
4
+ * One formatting grammar shared by axes, tooltips, tiles, and tables, so the
5
+ * same value never rounds two ways (Few's precision pitfall). Everything is
6
+ * built on `Intl`, so locale and time zone are first-class, and the two Vega
7
+ * expression functions registered by `registerArcFormatters` call straight
8
+ * into these helpers.
9
+ *
10
+ * Number grammar — `kind[:arg[:arg]][@locale]`:
11
+ *
12
+ * "number" 0–2 fraction digits (Intl decimal) 1,234.5
13
+ * "number:0" fixed maximum fraction digits 1,235
14
+ * "integer" whole numbers 1,235
15
+ * "compact" short scale, 1 fraction digit 3.8M
16
+ * "compact:0" 4M
17
+ * "percent" ratio → percent, 1 fraction digit 12.4%
18
+ * "percent:0" 12%
19
+ * "currency:THB" Intl currency, default digits ฿3,848,305.93
20
+ * "currency:THB:0" fixed digits ฿3,848,306
21
+ * "currency:THB:compact" short scale ฿3.8M
22
+ * "compact@th-TH" any kind, with a locale suffix
23
+ *
24
+ * Time grammar — `granularity[@locale[@timeZone]]`, granularity one of
25
+ * `day` (8 Sep 2026), `month` (Sep 2026), `year`, `time` (16:30),
26
+ * `datetime` (8 Sep 2026, 16:30), plus the axis-length forms `day-short`
27
+ * (8 Sep), `day-time` (8 Sep, 4 PM) and `month-short` (Sep) for tick labels
28
+ * that have no room for a year — `day-time` is the one a window shorter than
29
+ * two days needs, where a bare clock time repeats at both ends. It carries the
30
+ * hour only: minutes on an axis label are noise, they push the text past the
31
+ * width an axis label is allowed, and the tooltip has them anyway. Thai products that use the Gregorian calendar pass
32
+ * `th-TH-u-ca-gregory`; a bare `th-TH` defaults to the Buddhist calendar.
33
+ */
34
+ /** Rendered for null, undefined, NaN and infinite values — a typographic dash, never "0". */
35
+ const MISSING_VALUE = "—";
36
+ const NUMBER_KINDS = [
37
+ "number",
38
+ "integer",
39
+ "compact",
40
+ "percent",
41
+ "currency"
42
+ ];
43
+ const TIME_GRANULARITIES = [
44
+ "day",
45
+ "month",
46
+ "year",
47
+ "time",
48
+ "datetime",
49
+ "day-short",
50
+ "day-time",
51
+ "month-short"
52
+ ];
53
+ /** Parses the number grammar. Unknown kinds fall back to `number` rather than throwing — a bad format string must not take a chart down. */
54
+ function parseArcNumberFormat(format) {
55
+ const [body = "", locale] = String(format ?? "number").split("@");
56
+ const [rawKind = "number", ...args] = body.split(":");
57
+ const kind = NUMBER_KINDS.includes(rawKind) ? rawKind : "number";
58
+ const spec = { kind };
59
+ if (locale) spec.locale = locale;
60
+ let rest = args;
61
+ if (kind === "currency") {
62
+ const [code, ...others] = args;
63
+ if (code) spec.currency = code.toUpperCase();
64
+ rest = others;
65
+ }
66
+ for (const arg of rest) if (/^\d+$/.test(arg)) spec.digits = Number(arg);
67
+ else if (arg === "compact") spec.compact = true;
68
+ return spec;
69
+ }
70
+ /** Parses the time grammar. */
71
+ function parseArcTimeFormat(format) {
72
+ const [rawGranularity = "day", locale, timeZone] = String(format ?? "day").split("@");
73
+ const spec = { granularity: TIME_GRANULARITIES.includes(rawGranularity) ? rawGranularity : "day" };
74
+ if (locale) spec.locale = locale;
75
+ if (timeZone) spec.timeZone = timeZone;
76
+ return spec;
77
+ }
78
+ const numberFormatCache = /* @__PURE__ */ new Map();
79
+ const dateFormatCache = /* @__PURE__ */ new Map();
80
+ function numberOptions(spec) {
81
+ switch (spec.kind) {
82
+ case "integer": return { maximumFractionDigits: 0 };
83
+ case "compact": return {
84
+ notation: "compact",
85
+ compactDisplay: "short",
86
+ maximumFractionDigits: spec.digits ?? 1
87
+ };
88
+ case "percent": return {
89
+ style: "percent",
90
+ maximumFractionDigits: spec.digits ?? 1
91
+ };
92
+ case "currency": {
93
+ const options = {
94
+ style: "currency",
95
+ currency: spec.currency ?? "USD",
96
+ currencyDisplay: "narrowSymbol"
97
+ };
98
+ if (spec.compact) {
99
+ options.notation = "compact";
100
+ options.compactDisplay = "short";
101
+ }
102
+ if (spec.digits != null || spec.compact) {
103
+ options.minimumFractionDigits = 0;
104
+ options.maximumFractionDigits = spec.digits ?? (spec.compact ? 1 : 0);
105
+ }
106
+ return options;
107
+ }
108
+ default: return { maximumFractionDigits: spec.digits ?? 2 };
109
+ }
110
+ }
111
+ function getNumberFormat(spec, locale, extra) {
112
+ const resolvedLocale = spec.locale ?? locale;
113
+ const options = {
114
+ ...numberOptions(spec),
115
+ ...extra
116
+ };
117
+ const key = `${resolvedLocale ?? ""}|${JSON.stringify(options)}`;
118
+ let formatter = numberFormatCache.get(key);
119
+ if (!formatter) {
120
+ try {
121
+ formatter = new Intl.NumberFormat(resolvedLocale, options);
122
+ } catch {
123
+ formatter = new Intl.NumberFormat(void 0, { maximumFractionDigits: 2 });
124
+ }
125
+ numberFormatCache.set(key, formatter);
126
+ }
127
+ return formatter;
128
+ }
129
+ /**
130
+ * Formats a number through the grammar above. `extra` merges Intl options on
131
+ * top (deltas pass `{ signDisplay: "exceptZero" }`); a missing or non-finite
132
+ * value renders as `MISSING_VALUE`.
133
+ */
134
+ function formatNumber(value, format = "number", locale, extra) {
135
+ if (value == null || !Number.isFinite(value)) return "—";
136
+ return getNumberFormat(parseArcNumberFormat(format), locale, extra).format(value);
137
+ }
138
+ function toDate(value) {
139
+ return value instanceof Date ? value : new Date(value);
140
+ }
141
+ const TIME_OPTIONS = {
142
+ day: {
143
+ day: "numeric",
144
+ month: "short",
145
+ year: "numeric"
146
+ },
147
+ month: {
148
+ month: "short",
149
+ year: "numeric"
150
+ },
151
+ year: { year: "numeric" },
152
+ time: {
153
+ hour: "2-digit",
154
+ minute: "2-digit"
155
+ },
156
+ datetime: {
157
+ day: "numeric",
158
+ month: "short",
159
+ year: "numeric",
160
+ hour: "2-digit",
161
+ minute: "2-digit"
162
+ },
163
+ "day-short": {
164
+ day: "numeric",
165
+ month: "short"
166
+ },
167
+ "day-time": {
168
+ day: "numeric",
169
+ month: "short",
170
+ hour: "numeric"
171
+ },
172
+ "month-short": { month: "short" }
173
+ };
174
+ /**
175
+ * Formats a date at a granularity. `timeZone` defaults to the runtime's; pass
176
+ * it explicitly wherever output must be deterministic (Chromatic, tests, or a
177
+ * product that reports in one business time zone).
178
+ */
179
+ function formatDate(value, { granularity = "day", locale, timeZone } = {}) {
180
+ if (value == null) return "—";
181
+ const date = toDate(value);
182
+ if (Number.isNaN(date.getTime())) return "—";
183
+ return getDateFormat(granularity, locale, timeZone).format(date);
184
+ }
185
+ function getDateFormat(granularity, locale, timeZone) {
186
+ const key = `${locale ?? ""}|${timeZone ?? ""}|${granularity}`;
187
+ let formatter = dateFormatCache.get(key);
188
+ if (!formatter) {
189
+ try {
190
+ formatter = new Intl.DateTimeFormat(locale, {
191
+ ...TIME_OPTIONS[granularity],
192
+ timeZone
193
+ });
194
+ } catch {
195
+ formatter = new Intl.DateTimeFormat(void 0, TIME_OPTIONS[granularity]);
196
+ }
197
+ dateFormatCache.set(key, formatter);
198
+ }
199
+ return formatter;
200
+ }
201
+ /**
202
+ * Formats a period between two dates at a granularity, collapsing the parts
203
+ * they share the way the locale does — `Aug 7 – 9, 2026`, `Aug 28 – Sep 8,
204
+ * 2026`, `7–9 ส.ค. 2026` — through `Intl.DateTimeFormat.formatRange`, with
205
+ * `start – end` as the fallback where a runtime lacks it. The bounds may come
206
+ * in either order; an unparseable bound renders as `MISSING_VALUE`.
207
+ */
208
+ function formatDateRange(start, end, { granularity = "day", locale, timeZone } = {}) {
209
+ if (start == null || end == null) return "—";
210
+ const a = toDate(start);
211
+ const b = toDate(end);
212
+ if (Number.isNaN(a.getTime()) || Number.isNaN(b.getTime())) return "—";
213
+ const [from, to] = a.getTime() <= b.getTime() ? [a, b] : [b, a];
214
+ const formatter = getDateFormat(granularity, locale, timeZone);
215
+ if (typeof formatter.formatRange === "function") try {
216
+ return formatter.formatRange(from, to);
217
+ } catch {}
218
+ return `${formatter.format(from)} – ${formatter.format(to)}`;
219
+ }
220
+ /**
221
+ * Formats the change between two readings with the sign always shown. A
222
+ * relative change from zero is undefined, so `mode: "percent"` renders the
223
+ * absolute difference instead (and says so in `renderedMode`). An absolute
224
+ * delta of a percent-formatted value is in percentage points — never "-7%"
225
+ * for churn going from 4.1% to 3.8%.
226
+ */
227
+ function formatDelta(current, previous, { mode = "percent", format = "number", locale, digits = 1, pointsSuffix = " pp" } = {}) {
228
+ const absolute = current - previous;
229
+ const direction = absolute > 0 ? "up" : absolute < 0 ? "down" : "flat";
230
+ const percent = previous === 0 ? null : absolute / Math.abs(previous);
231
+ const signed = { signDisplay: "exceptZero" };
232
+ if (mode === "percent" && percent != null) return {
233
+ direction,
234
+ percent,
235
+ absolute,
236
+ renderedMode: "percent",
237
+ text: formatNumber(percent, `percent:${digits}`, locale, signed)
238
+ };
239
+ const valueSpec = parseArcNumberFormat(format);
240
+ return {
241
+ direction,
242
+ percent,
243
+ absolute,
244
+ renderedMode: "absolute",
245
+ text: valueSpec.kind === "percent" ? `${formatNumber(absolute * 100, `number:${valueSpec.digits ?? 1}`, locale, signed)}${pointsSuffix}` : formatNumber(absolute, format, locale, signed)
246
+ };
247
+ }
248
+ let registered = false;
249
+ /**
250
+ * Registers `arcNumber`, `arcTime` and the responsive axis helpers
251
+ * `arcAxisTicks` / `arcAxisLabelAlign` as Vega expression functions, once.
252
+ * Takes `vega.expressionFunction` as an argument so this module never imports
253
+ * the engine itself; the engine-bound modules call it at load time, before the
254
+ * first `parse` — Vega requires that. Returns whether this call did the
255
+ * registering.
256
+ *
257
+ * Both axis helpers take `(domain, width, discrete, format, kind, anchors?)`:
258
+ * `kind` picks the label formatter (`time` → `arcTime`, `number` →
259
+ * `arcNumber`, else the raw category), and `anchors` — a highlighted window's
260
+ * `[start, end]` — switches from evenly spaced labels to the window's bounds.
261
+ */
262
+ function registerArcFormatters(expressionFunction) {
263
+ if (registered) return false;
264
+ registered = true;
265
+ const arcNumber = (value, format) => formatNumber(typeof value === "number" ? value : Number(value), String(format ?? "number"));
266
+ const arcTime = (value, format) => formatDate(value, parseArcTimeFormat(String(format ?? "day")));
267
+ const labelFormatter = (format, kind) => (value) => kind === "time" ? arcTime(value, format) : kind === "number" ? arcNumber(value, format) : String(value);
268
+ const arcAxisTicks = (domain, width, discrete, format, kind, anchors) => selectAxisTicks(domain, width, discrete, labelFormatter(format, kind), anchors);
269
+ const arcAxisLabelAlign = (value, domain, width, discrete, format, kind, anchors) => axisLabelAlign(value, domain, width, discrete, labelFormatter(format, kind), anchors);
270
+ expressionFunction("arcNumber", arcNumber);
271
+ expressionFunction("arcTime", arcTime);
272
+ expressionFunction("arcAxisTicks", arcAxisTicks);
273
+ expressionFunction("arcAxisLabelAlign", arcAxisLabelAlign);
274
+ return true;
275
+ }
276
+ //#endregion
277
+ export { MISSING_VALUE, formatDate, formatDateRange, formatDelta, formatNumber, parseArcNumberFormat, parseArcTimeFormat, registerArcFormatters };
@@ -0,0 +1,11 @@
1
+ import { ArcDeltaDirection, ArcDeltaMode, ArcNumberFormatKind, ArcNumberFormatSpec, ArcTimeFormatSpec, ArcTimeGranularity, FormatDeltaOptions, FormattedDelta, MISSING_VALUE, formatDate, formatDateRange, formatDelta, formatNumber, parseArcNumberFormat, parseArcTimeFormat, registerArcFormatters } from "./formatters.js";
2
+ import { ChartTokens, readChartTokens, useChartTokens } from "./tokens.js";
3
+ import { ArcVegaConfigOptions, arcVegaConfig } from "./config.js";
4
+ import { ZonedParts, resolveInstant, timeZoneOffsetMs, zonedDayStart, zonedMidnight, zonedParts } from "./time.js";
5
+ import { ArcTooltipContext, ArcTooltipHandle, ArcTooltipRows, UseArcTooltipHandlerOptions, useArcTooltipHandler } from "./tooltip.js";
6
+ import { ArcEmbedOptionsInput, VEGA_WARN, arcEmbedOptions } from "./embed.js";
7
+ import { ArcVegaDatasets, UseArcVegaParams } from "./useArcVega.js";
8
+ import { ArcVegaSurfaceProps } from "./ArcVegaSurface.js";
9
+ import { ArcEngine, getLoadedEngine, loadEngine } from "./load.js";
10
+ import { ArcVegaChart, ArcVegaChartProps, useArcEngine } from "./ArcVegaChart.js";
11
+ export { type ArcDeltaDirection, type ArcDeltaMode, type ArcEmbedOptionsInput, type ArcEngine, type ArcNumberFormatKind, type ArcNumberFormatSpec, type ArcTimeFormatSpec, type ArcTimeGranularity, type ArcTooltipContext, type ArcTooltipHandle, ArcTooltipRows, ArcVegaChart, type ArcVegaChartProps, type ArcVegaConfigOptions, type ArcVegaDatasets, type ArcVegaSurfaceProps, type ChartTokens, type FormatDeltaOptions, type FormattedDelta, MISSING_VALUE, type UseArcTooltipHandlerOptions, type UseArcVegaParams, VEGA_WARN, type ZonedParts, arcEmbedOptions, arcVegaConfig, formatDate, formatDateRange, formatDelta, formatNumber, getLoadedEngine, loadEngine, parseArcNumberFormat, parseArcTimeFormat, readChartTokens, registerArcFormatters, resolveInstant, timeZoneOffsetMs, useArcEngine, useArcTooltipHandler, useChartTokens, zonedDayStart, zonedMidnight, zonedParts };
@@ -0,0 +1,13 @@
1
+ import { engine_d_exports } from "./engine.js";
2
+ //#region src/lib/vega/load.d.ts
3
+ type ArcEngine = typeof engine_d_exports;
4
+ /**
5
+ * Loads the engine chunk once and shares the promise. Components render their
6
+ * frame, title and `Skeleton` immediately and mount the chart when this
7
+ * resolves.
8
+ */
9
+ declare function loadEngine(): Promise<ArcEngine>;
10
+ /** The engine if a previous `loadEngine()` already resolved — lets later mounts skip the fallback frame. */
11
+ declare function getLoadedEngine(): ArcEngine | null;
12
+ //#endregion
13
+ export { ArcEngine, getLoadedEngine, loadEngine };
@@ -0,0 +1,21 @@
1
+ //#region src/lib/vega/load.ts
2
+ let pending = null;
3
+ let resolved = null;
4
+ /**
5
+ * Loads the engine chunk once and shares the promise. Components render their
6
+ * frame, title and `Skeleton` immediately and mount the chart when this
7
+ * resolves.
8
+ */
9
+ function loadEngine() {
10
+ pending ??= import("./engine.js").then((engine) => {
11
+ resolved = engine;
12
+ return engine;
13
+ });
14
+ return pending;
15
+ }
16
+ /** The engine if a previous `loadEngine()` already resolved — lets later mounts skip the fallback frame. */
17
+ function getLoadedEngine() {
18
+ return resolved;
19
+ }
20
+ //#endregion
21
+ export { getLoadedEngine, loadEngine };