@stackframe/dashboard-ui-components 2.8.84 → 2.8.85

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 (229) hide show
  1. package/dist/components/analytics-chart/analytics-chart-pie.d.ts +67 -0
  2. package/dist/components/analytics-chart/analytics-chart-pie.d.ts.map +1 -0
  3. package/dist/components/analytics-chart/analytics-chart-pie.js +253 -0
  4. package/dist/components/analytics-chart/analytics-chart-pie.js.map +1 -0
  5. package/dist/components/analytics-chart/analytics-chart.d.ts +554 -0
  6. package/dist/components/analytics-chart/analytics-chart.d.ts.map +1 -0
  7. package/dist/components/analytics-chart/analytics-chart.js +1021 -0
  8. package/dist/components/analytics-chart/analytics-chart.js.map +1 -0
  9. package/dist/components/analytics-chart/default-analytics-chart-tooltip.d.ts +66 -0
  10. package/dist/components/analytics-chart/default-analytics-chart-tooltip.d.ts.map +1 -0
  11. package/dist/components/analytics-chart/default-analytics-chart-tooltip.js +179 -0
  12. package/dist/components/analytics-chart/default-analytics-chart-tooltip.js.map +1 -0
  13. package/dist/components/analytics-chart/format.d.ts +13 -0
  14. package/dist/components/analytics-chart/format.d.ts.map +1 -0
  15. package/dist/components/analytics-chart/format.js +138 -0
  16. package/dist/components/analytics-chart/format.js.map +1 -0
  17. package/dist/components/analytics-chart/index.d.ts +8 -0
  18. package/dist/components/analytics-chart/index.js +184 -0
  19. package/dist/components/analytics-chart/palette.d.ts +15 -0
  20. package/dist/components/analytics-chart/palette.d.ts.map +1 -0
  21. package/dist/components/analytics-chart/palette.js +60 -0
  22. package/dist/components/analytics-chart/palette.js.map +1 -0
  23. package/dist/components/analytics-chart/render-data-series.d.ts +28 -0
  24. package/dist/components/analytics-chart/render-data-series.d.ts.map +1 -0
  25. package/dist/components/analytics-chart/render-data-series.js +109 -0
  26. package/dist/components/analytics-chart/render-data-series.js.map +1 -0
  27. package/dist/components/analytics-chart/state.d.ts +54 -0
  28. package/dist/components/analytics-chart/state.d.ts.map +1 -0
  29. package/dist/components/analytics-chart/state.js +142 -0
  30. package/dist/components/analytics-chart/state.js.map +1 -0
  31. package/dist/components/analytics-chart/strings.d.ts +33 -0
  32. package/dist/components/analytics-chart/strings.d.ts.map +1 -0
  33. package/dist/components/analytics-chart/strings.js +37 -0
  34. package/dist/components/analytics-chart/strings.js.map +1 -0
  35. package/dist/components/analytics-chart/types.d.ts +157 -0
  36. package/dist/components/analytics-chart/types.d.ts.map +1 -0
  37. package/dist/components/analytics-chart/types.js +21 -0
  38. package/dist/components/analytics-chart/types.js.map +1 -0
  39. package/dist/components/badge.d.ts +16 -0
  40. package/dist/components/badge.d.ts.map +1 -1
  41. package/dist/components/badge.js +16 -0
  42. package/dist/components/badge.js.map +1 -1
  43. package/dist/components/button.d.ts +15 -1
  44. package/dist/components/button.d.ts.map +1 -1
  45. package/dist/components/button.js +14 -0
  46. package/dist/components/button.js.map +1 -1
  47. package/dist/components/card.d.ts +28 -0
  48. package/dist/components/card.d.ts.map +1 -1
  49. package/dist/components/card.js +28 -0
  50. package/dist/components/card.js.map +1 -1
  51. package/dist/components/chart-card.d.ts +29 -0
  52. package/dist/components/chart-card.d.ts.map +1 -1
  53. package/dist/components/chart-card.js +29 -0
  54. package/dist/components/chart-card.js.map +1 -1
  55. package/dist/components/chart-legend.d.ts +1 -2
  56. package/dist/components/chart-legend.d.ts.map +1 -1
  57. package/dist/components/chart-legend.js +0 -4
  58. package/dist/components/chart-legend.js.map +1 -1
  59. package/dist/components/data-grid/data-grid-sizing.d.ts +11 -0
  60. package/dist/components/data-grid/data-grid-sizing.d.ts.map +1 -0
  61. package/dist/components/data-grid/data-grid-sizing.js +34 -0
  62. package/dist/components/data-grid/data-grid-sizing.js.map +1 -0
  63. package/dist/components/data-grid/data-grid-toolbar.d.ts +31 -0
  64. package/dist/components/data-grid/data-grid-toolbar.d.ts.map +1 -0
  65. package/dist/components/data-grid/data-grid-toolbar.js +226 -0
  66. package/dist/components/data-grid/data-grid-toolbar.js.map +1 -0
  67. package/dist/components/data-grid/data-grid.d.ts +233 -0
  68. package/dist/components/data-grid/data-grid.d.ts.map +1 -0
  69. package/dist/components/data-grid/data-grid.js +871 -0
  70. package/dist/components/data-grid/data-grid.js.map +1 -0
  71. package/dist/components/data-grid/index.d.ts +7 -0
  72. package/dist/components/data-grid/index.js +176 -0
  73. package/dist/components/data-grid/state.d.ts +91 -0
  74. package/dist/components/data-grid/state.d.ts.map +1 -0
  75. package/dist/components/data-grid/state.js +329 -0
  76. package/dist/components/data-grid/state.js.map +1 -0
  77. package/dist/components/data-grid/strings.d.ts +8 -0
  78. package/dist/components/data-grid/strings.d.ts.map +1 -0
  79. package/dist/components/data-grid/strings.js +42 -0
  80. package/dist/components/data-grid/strings.js.map +1 -0
  81. package/dist/components/data-grid/types.d.ts +242 -0
  82. package/dist/components/data-grid/types.d.ts.map +1 -0
  83. package/dist/components/data-grid/types.js +0 -0
  84. package/dist/components/data-grid/use-data-source.d.ts +79 -0
  85. package/dist/components/data-grid/use-data-source.d.ts.map +1 -0
  86. package/dist/components/data-grid/use-data-source.js +236 -0
  87. package/dist/components/data-grid/use-data-source.js.map +1 -0
  88. package/dist/components/empty-state.d.ts +16 -0
  89. package/dist/components/empty-state.d.ts.map +1 -1
  90. package/dist/components/empty-state.js +16 -0
  91. package/dist/components/empty-state.js.map +1 -1
  92. package/dist/components/metric-card.d.ts +24 -0
  93. package/dist/components/metric-card.d.ts.map +1 -1
  94. package/dist/components/metric-card.js +24 -0
  95. package/dist/components/metric-card.js.map +1 -1
  96. package/dist/components/progress-bar.d.ts +10 -0
  97. package/dist/components/progress-bar.d.ts.map +1 -1
  98. package/dist/components/progress-bar.js +10 -0
  99. package/dist/components/progress-bar.js.map +1 -1
  100. package/dist/components/separator.d.ts +9 -0
  101. package/dist/components/separator.d.ts.map +1 -1
  102. package/dist/components/separator.js +9 -0
  103. package/dist/components/separator.js.map +1 -1
  104. package/dist/components/skeleton.d.ts +12 -0
  105. package/dist/components/skeleton.d.ts.map +1 -1
  106. package/dist/components/skeleton.js +12 -0
  107. package/dist/components/skeleton.js.map +1 -1
  108. package/dist/components/table.d.ts +25 -0
  109. package/dist/components/table.d.ts.map +1 -1
  110. package/dist/components/table.js +25 -0
  111. package/dist/components/table.js.map +1 -1
  112. package/dist/dashboard-ui-components.global.js +8607 -2902
  113. package/dist/dashboard-ui-components.global.js.map +4 -4
  114. package/dist/esm/components/analytics-chart/analytics-chart-pie.d.ts +67 -0
  115. package/dist/esm/components/analytics-chart/analytics-chart-pie.d.ts.map +1 -0
  116. package/dist/esm/components/analytics-chart/analytics-chart-pie.js +251 -0
  117. package/dist/esm/components/analytics-chart/analytics-chart-pie.js.map +1 -0
  118. package/dist/esm/components/analytics-chart/analytics-chart.d.ts +554 -0
  119. package/dist/esm/components/analytics-chart/analytics-chart.d.ts.map +1 -0
  120. package/dist/esm/components/analytics-chart/analytics-chart.js +1019 -0
  121. package/dist/esm/components/analytics-chart/analytics-chart.js.map +1 -0
  122. package/dist/esm/components/analytics-chart/default-analytics-chart-tooltip.d.ts +66 -0
  123. package/dist/esm/components/analytics-chart/default-analytics-chart-tooltip.d.ts.map +1 -0
  124. package/dist/esm/components/analytics-chart/default-analytics-chart-tooltip.js +176 -0
  125. package/dist/esm/components/analytics-chart/default-analytics-chart-tooltip.js.map +1 -0
  126. package/dist/esm/components/analytics-chart/format.d.ts +13 -0
  127. package/dist/esm/components/analytics-chart/format.d.ts.map +1 -0
  128. package/dist/esm/components/analytics-chart/format.js +133 -0
  129. package/dist/esm/components/analytics-chart/format.js.map +1 -0
  130. package/dist/esm/components/analytics-chart/index.d.ts +8 -0
  131. package/dist/esm/components/analytics-chart/index.js +9 -0
  132. package/dist/esm/components/analytics-chart/palette.d.ts +15 -0
  133. package/dist/esm/components/analytics-chart/palette.d.ts.map +1 -0
  134. package/dist/esm/components/analytics-chart/palette.js +55 -0
  135. package/dist/esm/components/analytics-chart/palette.js.map +1 -0
  136. package/dist/esm/components/analytics-chart/render-data-series.d.ts +28 -0
  137. package/dist/esm/components/analytics-chart/render-data-series.d.ts.map +1 -0
  138. package/dist/esm/components/analytics-chart/render-data-series.js +107 -0
  139. package/dist/esm/components/analytics-chart/render-data-series.js.map +1 -0
  140. package/dist/esm/components/analytics-chart/state.d.ts +54 -0
  141. package/dist/esm/components/analytics-chart/state.d.ts.map +1 -0
  142. package/dist/esm/components/analytics-chart/state.js +126 -0
  143. package/dist/esm/components/analytics-chart/state.js.map +1 -0
  144. package/dist/esm/components/analytics-chart/strings.d.ts +33 -0
  145. package/dist/esm/components/analytics-chart/strings.d.ts.map +1 -0
  146. package/dist/esm/components/analytics-chart/strings.js +34 -0
  147. package/dist/esm/components/analytics-chart/strings.js.map +1 -0
  148. package/dist/esm/components/analytics-chart/types.d.ts +157 -0
  149. package/dist/esm/components/analytics-chart/types.d.ts.map +1 -0
  150. package/dist/esm/components/analytics-chart/types.js +18 -0
  151. package/dist/esm/components/analytics-chart/types.js.map +1 -0
  152. package/dist/esm/components/badge.d.ts +16 -0
  153. package/dist/esm/components/badge.d.ts.map +1 -1
  154. package/dist/esm/components/badge.js +16 -0
  155. package/dist/esm/components/badge.js.map +1 -1
  156. package/dist/esm/components/button.d.ts +14 -0
  157. package/dist/esm/components/button.d.ts.map +1 -1
  158. package/dist/esm/components/button.js +14 -0
  159. package/dist/esm/components/button.js.map +1 -1
  160. package/dist/esm/components/card.d.ts +28 -0
  161. package/dist/esm/components/card.d.ts.map +1 -1
  162. package/dist/esm/components/card.js +28 -0
  163. package/dist/esm/components/card.js.map +1 -1
  164. package/dist/esm/components/chart-card.d.ts +29 -0
  165. package/dist/esm/components/chart-card.d.ts.map +1 -1
  166. package/dist/esm/components/chart-card.js +29 -0
  167. package/dist/esm/components/chart-card.js.map +1 -1
  168. package/dist/esm/components/chart-legend.d.ts +1 -2
  169. package/dist/esm/components/chart-legend.d.ts.map +1 -1
  170. package/dist/esm/components/chart-legend.js +1 -3
  171. package/dist/esm/components/chart-legend.js.map +1 -1
  172. package/dist/esm/components/data-grid/data-grid-sizing.d.ts +11 -0
  173. package/dist/esm/components/data-grid/data-grid-sizing.d.ts.map +1 -0
  174. package/dist/esm/components/data-grid/data-grid-sizing.js +29 -0
  175. package/dist/esm/components/data-grid/data-grid-sizing.js.map +1 -0
  176. package/dist/esm/components/data-grid/data-grid-toolbar.d.ts +31 -0
  177. package/dist/esm/components/data-grid/data-grid-toolbar.d.ts.map +1 -0
  178. package/dist/esm/components/data-grid/data-grid-toolbar.js +223 -0
  179. package/dist/esm/components/data-grid/data-grid-toolbar.js.map +1 -0
  180. package/dist/esm/components/data-grid/data-grid.d.ts +233 -0
  181. package/dist/esm/components/data-grid/data-grid.d.ts.map +1 -0
  182. package/dist/esm/components/data-grid/data-grid.js +868 -0
  183. package/dist/esm/components/data-grid/data-grid.js.map +1 -0
  184. package/dist/esm/components/data-grid/index.d.ts +7 -0
  185. package/dist/esm/components/data-grid/index.js +7 -0
  186. package/dist/esm/components/data-grid/state.d.ts +91 -0
  187. package/dist/esm/components/data-grid/state.d.ts.map +1 -0
  188. package/dist/esm/components/data-grid/state.js +305 -0
  189. package/dist/esm/components/data-grid/state.js.map +1 -0
  190. package/dist/esm/components/data-grid/strings.d.ts +8 -0
  191. package/dist/esm/components/data-grid/strings.d.ts.map +1 -0
  192. package/dist/esm/components/data-grid/strings.js +39 -0
  193. package/dist/esm/components/data-grid/strings.js.map +1 -0
  194. package/dist/esm/components/data-grid/types.d.ts +242 -0
  195. package/dist/esm/components/data-grid/types.d.ts.map +1 -0
  196. package/dist/esm/components/data-grid/types.js +1 -0
  197. package/dist/esm/components/data-grid/use-data-source.d.ts +79 -0
  198. package/dist/esm/components/data-grid/use-data-source.d.ts.map +1 -0
  199. package/dist/esm/components/data-grid/use-data-source.js +234 -0
  200. package/dist/esm/components/data-grid/use-data-source.js.map +1 -0
  201. package/dist/esm/components/empty-state.d.ts +16 -0
  202. package/dist/esm/components/empty-state.d.ts.map +1 -1
  203. package/dist/esm/components/empty-state.js +16 -0
  204. package/dist/esm/components/empty-state.js.map +1 -1
  205. package/dist/esm/components/metric-card.d.ts +24 -0
  206. package/dist/esm/components/metric-card.d.ts.map +1 -1
  207. package/dist/esm/components/metric-card.js +24 -0
  208. package/dist/esm/components/metric-card.js.map +1 -1
  209. package/dist/esm/components/progress-bar.d.ts +10 -0
  210. package/dist/esm/components/progress-bar.d.ts.map +1 -1
  211. package/dist/esm/components/progress-bar.js +10 -0
  212. package/dist/esm/components/progress-bar.js.map +1 -1
  213. package/dist/esm/components/separator.d.ts +9 -0
  214. package/dist/esm/components/separator.d.ts.map +1 -1
  215. package/dist/esm/components/separator.js +9 -0
  216. package/dist/esm/components/separator.js.map +1 -1
  217. package/dist/esm/components/skeleton.d.ts +12 -0
  218. package/dist/esm/components/skeleton.d.ts.map +1 -1
  219. package/dist/esm/components/skeleton.js +12 -0
  220. package/dist/esm/components/skeleton.js.map +1 -1
  221. package/dist/esm/components/table.d.ts +25 -0
  222. package/dist/esm/components/table.d.ts.map +1 -1
  223. package/dist/esm/components/table.js +25 -0
  224. package/dist/esm/components/table.js.map +1 -1
  225. package/dist/esm/index.d.ts +4 -2
  226. package/dist/esm/index.js +6 -2
  227. package/dist/index.d.ts +15 -2
  228. package/dist/index.js +16 -7
  229. package/package.json +4 -3
@@ -0,0 +1,554 @@
1
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
2
+ import { ReactNode } from "react";
3
+ import { AnalyticsChartStrings } from "./strings";
4
+ import { AnalyticsChartPalette, AnalyticsChartPieProps, AnalyticsChartState, Annotation, FormatKind, Point } from "./types";
5
+ import { AnalyticsChartTooltipContext } from "./default-analytics-chart-tooltip";
6
+
7
+ //#region src/components/analytics-chart/analytics-chart.d.ts
8
+ /** Mirrors Recharts' internal `Margin` shape (not exported from their typings). */
9
+ type Margin = {
10
+ top?: number;
11
+ right?: number;
12
+ bottom?: number;
13
+ left?: number;
14
+ };
15
+ /**
16
+ * Props for {@link AnalyticsChart}.
17
+ *
18
+ * ## HOW TO REFERENCE THIS COMPONENT
19
+ *
20
+ * **In the custom dashboard sandbox** (AI-generated dashboard code): every
21
+ * export lives on the global `DashboardUI` object. Use
22
+ * `DashboardUI.AnalyticsChart`, `DashboardUI.ANALYTICS_CHART_DEFAULT_STATE`,
23
+ * `DashboardUI.pointValue`, etc. **Never** use bare identifiers like
24
+ * `<AnalyticsChart />` inside the sandbox — there is no module system and
25
+ * nothing is destructured into scope. Types (`AnalyticsChartState`,
26
+ * `Point`, …) don't exist at runtime anyway, so just drop the type
27
+ * annotations in sandbox code.
28
+ *
29
+ * **In a regular TypeScript app** (anywhere importing `@stackframe/dashboard-ui-components`
30
+ * directly): `import { AnalyticsChart, ANALYTICS_CHART_DEFAULT_STATE } from
31
+ * "@stackframe/dashboard-ui-components"` and use the bare name. Drop the
32
+ * `DashboardUI.` prefix from the examples below when doing so.
33
+ *
34
+ * ## Data shape in 30 seconds
35
+ *
36
+ * - `data` is `Point[]`. Each `Point` is `{ ts: number, values: Record<string, number> }`.
37
+ * `ts` is a Unix millisecond timestamp; `values` is keyed by **layer id**.
38
+ * - `state` is fully controlled. Start from
39
+ * `DashboardUI.ANALYTICS_CHART_DEFAULT_STATE` (which ships with a
40
+ * `"primary"` + `"compare"` + `"annotations"` layer set) and override
41
+ * what you need. Do **not** hand-build the layer array from scratch.
42
+ * - For a breakdown (e.g. signups by region), add `segments` (a `number[][]`
43
+ * with one row per `data` point) and `segmentSeries` (the category labels)
44
+ * to the primary layer. Rows of `segments` should sum to the point's layer
45
+ * value. Same for the compare layer if you want a compared breakdown.
46
+ *
47
+ * ## **SCALE YOUR DATA BEFORE PUTTING IT ON A POINT** (critical)
48
+ *
49
+ * The chart renders every visible layer on a **single shared y-axis**. If
50
+ * two layers are on different orders of magnitude (e.g. revenue in cents
51
+ * `1_200_000` and sign-ups `450`), the smaller series collapses to a flat
52
+ * line at the bottom and the chart looks broken. You **must** normalize
53
+ * both metrics into the same range before building `data`. Rules of thumb:
54
+ *
55
+ * - **Cents → dollars / units**: divide money amounts by 100 (or 1000 for
56
+ * large currencies). Use a `valueFormatter` to render the original unit
57
+ * in tooltips so the UX still reads as "$12,543".
58
+ * - **Counts vs rates**: if one layer is a count (e.g. requests) and the
59
+ * other is a ratio (e.g. error rate 0.02), multiply the ratio by the
60
+ * count's scale (or by `max(counts)`) so both sit in the same band.
61
+ * - **Very different counts** (e.g. page views `120_000` vs sign-ups `430`):
62
+ * either divide the large metric (`views / 100`) or promote the small
63
+ * one to a rate (`signups / views * 1000`). Note the transformation in
64
+ * the layer `label` ("Sign-ups per 1k views") so it's honest.
65
+ * - **Pick the target range from the layer with the most natural scale**
66
+ * — usually the metric the user actually cares about — and normalize
67
+ * everything else into it. Don't normalize by fighting Recharts with
68
+ * `yDomain` hacks; do it in the data.
69
+ *
70
+ * If the two metrics truly can't share an axis (e.g. latency ms vs error
71
+ * count), render them as **two separate `AnalyticsChart` instances** stacked
72
+ * in the layout instead of jamming them into one chart.
73
+ *
74
+ * ## Example 1 — simplest possible: one area layer, no compare
75
+ *
76
+ * ```jsx
77
+ * // Sandbox dashboard code — everything prefixed with DashboardUI.*
78
+ * function Dashboard() {
79
+ * const data = [
80
+ * { ts: Date.UTC(2026, 2, 1), values: { primary: 420 } },
81
+ * { ts: Date.UTC(2026, 2, 2), values: { primary: 512 } },
82
+ * { ts: Date.UTC(2026, 2, 3), values: { primary: 604 } },
83
+ * // ...one row per time bucket
84
+ * ];
85
+ *
86
+ * // Start from defaults, hide the compare layer so it's a single series.
87
+ * const [state, setState] = React.useState({
88
+ * ...DashboardUI.ANALYTICS_CHART_DEFAULT_STATE,
89
+ * layers: DashboardUI.ANALYTICS_CHART_DEFAULT_STATE.layers.map((l) =>
90
+ * l.kind === "compare" ? { ...l, visible: false } : l,
91
+ * ),
92
+ * });
93
+ *
94
+ * return (
95
+ * <DashboardUI.DesignChartCard title="Sign-ups" description="Last 30 days">
96
+ * <DashboardUI.AnalyticsChart
97
+ * data={data}
98
+ * state={state}
99
+ * onChange={setState}
100
+ * />
101
+ * </DashboardUI.DesignChartCard>
102
+ * );
103
+ * }
104
+ * ```
105
+ *
106
+ * ## Example 2 — current vs previous period (compare)
107
+ *
108
+ * Each point carries both layer values under their layer ids. The default
109
+ * state's `"primary"` and `"compare"` layers are already visible, so no
110
+ * state customization is needed.
111
+ *
112
+ * ```jsx
113
+ * function Dashboard() {
114
+ * const data = rows.map((r) => ({
115
+ * ts: r.bucketTs,
116
+ * values: {
117
+ * primary: r.signupsThisPeriod, // keyed by layer id "primary"
118
+ * compare: r.signupsLastPeriod, // keyed by layer id "compare"
119
+ * },
120
+ * }));
121
+ *
122
+ * const [state, setState] = React.useState(
123
+ * DashboardUI.ANALYTICS_CHART_DEFAULT_STATE,
124
+ * );
125
+ *
126
+ * return (
127
+ * <DashboardUI.AnalyticsChart
128
+ * data={data}
129
+ * state={state}
130
+ * onChange={setState}
131
+ * />
132
+ * );
133
+ * }
134
+ * ```
135
+ *
136
+ * ## Example 3 — stacked bar with region breakdown (segmented)
137
+ *
138
+ * ```jsx
139
+ * function Dashboard() {
140
+ * const regions = [
141
+ * { key: "us", label: "United States" },
142
+ * { key: "eu", label: "European Union" },
143
+ * { key: "asia", label: "Asia-Pacific" },
144
+ * ];
145
+ *
146
+ * // Row index matches `data` index; column index matches `regions`.
147
+ * // Each row MUST sum to data[i].values.primary.
148
+ * const segments = [
149
+ * [210, 140, 70], // day 0 → total 420
150
+ * [250, 170, 92], // day 1 → total 512
151
+ * [300, 200, 104], // day 2 → total 604
152
+ * ];
153
+ *
154
+ * const data = [
155
+ * { ts: Date.UTC(2026, 2, 1), values: { primary: 420 } },
156
+ * { ts: Date.UTC(2026, 2, 2), values: { primary: 512 } },
157
+ * { ts: Date.UTC(2026, 2, 3), values: { primary: 604 } },
158
+ * ];
159
+ *
160
+ * const [state, setState] = React.useState({
161
+ * ...DashboardUI.ANALYTICS_CHART_DEFAULT_STATE,
162
+ * layers: DashboardUI.ANALYTICS_CHART_DEFAULT_STATE.layers.map((l) => {
163
+ * if (l.kind === "primary") {
164
+ * return {
165
+ * ...l,
166
+ * type: "bar", // switch from area → stacked bars
167
+ * segmented: true,
168
+ * segments,
169
+ * segmentSeries: regions,
170
+ * };
171
+ * }
172
+ * if (l.kind === "compare") {
173
+ * return { ...l, visible: false };
174
+ * }
175
+ * return l;
176
+ * }),
177
+ * });
178
+ *
179
+ * return (
180
+ * <DashboardUI.AnalyticsChart
181
+ * data={data}
182
+ * state={state}
183
+ * onChange={setState}
184
+ * />
185
+ * );
186
+ * }
187
+ * ```
188
+ *
189
+ * ## Example 4 — mixing display types (e.g. revenue bars + signups area)
190
+ *
191
+ * Use two layers. The "primary" layer holds one metric; reuse the "compare"
192
+ * layer slot for the second metric by overriding its `id`, `label`, and
193
+ * `type`. Then key both values in each `Point`.
194
+ *
195
+ * **IMPORTANT**: the two metrics share a single y-axis, so scale them into
196
+ * the same range before putting them on the point. A common trick is to
197
+ * pass a `valueFormatter` that reports each layer's number with its own
198
+ * unit so tooltips still read correctly.
199
+ *
200
+ * ```jsx
201
+ * function Dashboard() {
202
+ * // Sign-ups are already in range; revenue cents would dwarf them, so
203
+ * // we normalize revenue (cents → dollars) onto the same scale.
204
+ * const data = rows.map((r) => ({
205
+ * ts: r.bucketTs,
206
+ * values: {
207
+ * revenue: r.revenueCents / 100,
208
+ * signups: r.signups,
209
+ * },
210
+ * }));
211
+ *
212
+ * const [state, setState] = React.useState({
213
+ * ...DashboardUI.ANALYTICS_CHART_DEFAULT_STATE,
214
+ * layers: DashboardUI.ANALYTICS_CHART_DEFAULT_STATE.layers.map((l) => {
215
+ * if (l.kind === "primary") {
216
+ * return { ...l, id: "revenue", label: "Revenue", type: "bar" };
217
+ * }
218
+ * if (l.kind === "compare") {
219
+ * return {
220
+ * ...l,
221
+ * id: "signups",
222
+ * label: "Sign-ups",
223
+ * type: "area",
224
+ * visible: true,
225
+ * };
226
+ * }
227
+ * return l;
228
+ * }),
229
+ * });
230
+ *
231
+ * // Per-layer formatter: `kind` lets you branch per-axis vs per-layer
232
+ * // using the layer id passed in via the tooltip context. For most
233
+ * // cases formatting by raw value is enough.
234
+ * const valueFormatter = (value, kind) => {
235
+ * if (kind.type === "currency") return `$${value.toFixed(0)}`;
236
+ * return value.toLocaleString();
237
+ * };
238
+ *
239
+ * return (
240
+ * <DashboardUI.AnalyticsChart
241
+ * data={data}
242
+ * state={state}
243
+ * onChange={setState}
244
+ * valueFormatter={valueFormatter}
245
+ * />
246
+ * );
247
+ * }
248
+ * ```
249
+ *
250
+ * ## Example 5 — segmented sign-ups stacked with a revenue line (mix + segment)
251
+ *
252
+ * Combines Example 3 and Example 4: primary layer is revenue as a line
253
+ * (un-segmented), compare layer is sign-ups as a stacked bar (segmented
254
+ * by region). Remember: row sums of the compare segments must equal
255
+ * `point.values.signups`, and both metrics share one y-axis.
256
+ *
257
+ * ```jsx
258
+ * function Dashboard() {
259
+ * const regions = [
260
+ * { key: "us", label: "United States" },
261
+ * { key: "eu", label: "European Union" },
262
+ * { key: "asia", label: "Asia-Pacific" },
263
+ * ];
264
+ *
265
+ * // Normalize revenue to the same order of magnitude as sign-ups.
266
+ * const data = rows.map((r) => ({
267
+ * ts: r.bucketTs,
268
+ * values: {
269
+ * revenue: r.revenueCents / 100,
270
+ * signups: r.signupsTotal,
271
+ * },
272
+ * }));
273
+ *
274
+ * // Row index matches `data` index. Each row sums to signupsTotal.
275
+ * const signupSegments = rows.map((r) => [
276
+ * r.signupsUs,
277
+ * r.signupsEu,
278
+ * r.signupsAsia,
279
+ * ]);
280
+ *
281
+ * const [state, setState] = React.useState({
282
+ * ...DashboardUI.ANALYTICS_CHART_DEFAULT_STATE,
283
+ * layers: DashboardUI.ANALYTICS_CHART_DEFAULT_STATE.layers.map((l) => {
284
+ * if (l.kind === "primary") {
285
+ * return { ...l, id: "revenue", label: "Revenue", type: "line" };
286
+ * }
287
+ * if (l.kind === "compare") {
288
+ * return {
289
+ * ...l,
290
+ * id: "signups",
291
+ * label: "Sign-ups",
292
+ * type: "bar",
293
+ * visible: true,
294
+ * segmented: true,
295
+ * segments: signupSegments,
296
+ * segmentSeries: regions,
297
+ * };
298
+ * }
299
+ * return l;
300
+ * }),
301
+ * });
302
+ *
303
+ * return (
304
+ * <DashboardUI.AnalyticsChart
305
+ * data={data}
306
+ * state={state}
307
+ * onChange={setState}
308
+ * />
309
+ * );
310
+ * }
311
+ * ```
312
+ */
313
+ type AnalyticsChartProps = {
314
+ /** Time-series points — each point carries `values` keyed by layer id.
315
+ * See {@link AnalyticsChartProps} for full data-shape examples. */
316
+ data: Point[]; /** Annotations. Fully prop-driven; the consumer owns the array. */
317
+ annotations?: Annotation[];
318
+ /** Fully-controlled state + dispatch. The chart reads every config and
319
+ * persistent-interaction slice from `state` and mutates it through
320
+ * `onChange`. Ephemeral interaction state (hover, brush, pin, draft) is
321
+ * managed internally and surfaces only through the state callbacks. */
322
+ state: AnalyticsChartState;
323
+ onChange: React.Dispatch<React.SetStateAction<AnalyticsChartState>>;
324
+ /** Fired when the user submits the in-chart annotation form. The consumer
325
+ * is expected to append to its own annotations array. */
326
+ onAnnotationCreate?: (annotation: Annotation) => void; /** Override any user-visible copy. Shallow-merges over the defaults. */
327
+ strings?: Partial<AnalyticsChartStrings>;
328
+ /** Override segment color ramps. Each ramp is either procedural
329
+ * (hue + sat + lightness range) or explicit (concrete color lists). */
330
+ palette?: Partial<AnalyticsChartPalette>;
331
+ /** Render slot for the tooltip body. Receives a prepared context with
332
+ * the active point, primary/compare layer views, pre-bound formatters,
333
+ * and resolved strings. Defaults to `DefaultAnalyticsChartTooltip`. */
334
+ renderTooltip?: (ctx: AnalyticsChartTooltipContext) => ReactNode;
335
+ /** Recharts plot margins. Also drives overlay positioning math so the
336
+ * crosshair, tooltip anchor, brush popup, and flag markers line up with
337
+ * the actual plot area. Defaults to `{ top: 16, right: 24, bottom: 8, left: 12 }`. */
338
+ plotMargin?: Margin; /** Y-axis reserved width in pixels. Defaults to 48. */
339
+ yAxisWidth?: number; /** Fractional headroom added to the y-axis top. Defaults to 0.1. */
340
+ yDomainPadding?: number; /** Grouped pie configuration. Each field has a sensible default. */
341
+ pie?: AnalyticsChartPieProps;
342
+ /** Custom number formatter. Receives the raw value and the kind to format
343
+ * with — the same function is invoked for both x-axis and y-axis values. */
344
+ valueFormatter?: (value: number, kind: FormatKind) => string;
345
+ };
346
+ /**
347
+ * Preferred chart for all time-series: area, line, bar, compare layers,
348
+ * segmented stacks, tooltips, zoom, and annotations. Wrap in
349
+ * `DesignChartCard` for the title/description chrome. Only fall back to
350
+ * raw Recharts for non-time-series visuals (static rankings etc.).
351
+ *
352
+ * ## Data shape
353
+ *
354
+ * `data` is `Point[]`, where `Point = { ts: number, values: Record<string, number> }`.
355
+ * `ts` is a Unix milliseconds timestamp. `values` maps layer id → numeric value
356
+ * at that bucket. Example:
357
+ *
358
+ * ```ts
359
+ * { ts: 1743465600000, values: { primary: 420 } }
360
+ * { ts: 1743465600000, values: { primary: 420, compare: 380 } } // with compare layer
361
+ * ```
362
+ *
363
+ * ## State is fully controlled — start from ANALYTICS_CHART_DEFAULT_STATE
364
+ *
365
+ * The default state ships with three pre-configured layers: `"primary"`,
366
+ * `"compare"`, and `"annotations"`. ALWAYS spread from
367
+ * `ANALYTICS_CHART_DEFAULT_STATE` and map over `layers` to override. Do NOT
368
+ * hand-build the layer array from scratch — you will miss fields and crash.
369
+ *
370
+ * ```ts
371
+ * // Default state shape (for reference — spread from the constant, don't copy):
372
+ * {
373
+ * view: "timeseries",
374
+ * layers: [
375
+ * { id: "primary", kind: "primary", label: "Current", visible: true, color: "#2563eb",
376
+ * segmented: false, type: "area", strokeStyle: "solid", fillOpacity: 0.22, inProgressFromIndex: null },
377
+ * { id: "compare", kind: "compare", label: "Previous period", visible: true, color: "#f59e0b",
378
+ * segmented: false, type: "line", strokeStyle: "dashed", inProgressFromIndex: null },
379
+ * { id: "annotations", kind: "annotations", label: "Annotations", visible: true, color: "#f59e0b" },
380
+ * ],
381
+ * xFormatKind: { type: "datetime", style: "short" },
382
+ * yFormatKind: { type: "short" },
383
+ * showGrid: true, showXAxis: true, showYAxis: true,
384
+ * zoomRange: null, pinnedIndex: null,
385
+ * }
386
+ * ```
387
+ *
388
+ * ## onChange — CRITICAL, get this right
389
+ *
390
+ * `onChange` fires with an `AnalyticsChartState` object — NOT your custom
391
+ * wrapper. If you store chart data and state together, `onChange` MUST only
392
+ * update the state part. Keep data and state in SEPARATE hooks:
393
+ *
394
+ * ```tsx
395
+ * // WRONG — overwrites your data with a bare state object, crashes on next render:
396
+ * const [combined, setCombined] = React.useState({ data: [], state: ANALYTICS_CHART_DEFAULT_STATE });
397
+ * <AnalyticsChart data={combined.data} state={combined.state} onChange={setCombined} />
398
+ *
399
+ * // RIGHT — two hooks:
400
+ * const [data, setData] = React.useState([]);
401
+ * const [chartState, setChartState] = React.useState({ ...ANALYTICS_CHART_DEFAULT_STATE });
402
+ * <AnalyticsChart data={data} state={chartState} onChange={setChartState} />
403
+ * ```
404
+ *
405
+ * NEVER pass a setter that manages a combined `{ data, state }` object directly to `onChange`.
406
+ *
407
+ * ## Common patterns
408
+ *
409
+ * ### 1. Simplest — one area layer, no compare
410
+ *
411
+ * ```tsx
412
+ * const data = rows.map(r => ({ ts: r.bucketTs, values: { primary: r.count } }));
413
+ * const [state, setState] = React.useState({
414
+ * ...ANALYTICS_CHART_DEFAULT_STATE,
415
+ * layers: ANALYTICS_CHART_DEFAULT_STATE.layers.map(l =>
416
+ * l.kind === "compare" ? { ...l, visible: false } : l
417
+ * ),
418
+ * });
419
+ * <DesignChartCard title="Signups" description="Last 30 days">
420
+ * <AnalyticsChart data={data} state={state} onChange={setState} />
421
+ * </DesignChartCard>
422
+ * ```
423
+ *
424
+ * ### 2. Current vs previous period (compare)
425
+ *
426
+ * ```tsx
427
+ * const data = rows.map(r => ({
428
+ * ts: r.bucketTs,
429
+ * values: { primary: r.thisPeriod, compare: r.lastPeriod },
430
+ * }));
431
+ * const [state, setState] = React.useState(ANALYTICS_CHART_DEFAULT_STATE);
432
+ * <AnalyticsChart data={data} state={state} onChange={setState} />
433
+ * ```
434
+ *
435
+ * ### 3. Stacked bar with breakdown (segmented)
436
+ *
437
+ * ```tsx
438
+ * const regions = [{ key: "us", label: "US" }, { key: "eu", label: "EU" }];
439
+ * const segments = rows.map(r => [r.signupsUs, r.signupsEu]); // MUST sum to primary value per row
440
+ * const [state, setState] = React.useState({
441
+ * ...ANALYTICS_CHART_DEFAULT_STATE,
442
+ * layers: ANALYTICS_CHART_DEFAULT_STATE.layers.map(l => {
443
+ * if (l.kind === "primary") return { ...l, type: "bar", segmented: true, segments, segmentSeries: regions };
444
+ * if (l.kind === "compare") return { ...l, visible: false };
445
+ * return l;
446
+ * }),
447
+ * });
448
+ * <AnalyticsChart data={data} state={state} onChange={setState} />
449
+ * ```
450
+ *
451
+ * ### 4. Two metrics on one chart (revenue bars + signups area)
452
+ *
453
+ * ```tsx
454
+ * // IMPORTANT: metrics share one y-axis, so normalize into the same range.
455
+ * const data = rows.map(r => ({
456
+ * ts: r.bucketTs,
457
+ * values: { revenue: r.revenueCents / 100, signups: r.signups },
458
+ * }));
459
+ * const [state, setState] = React.useState({
460
+ * ...ANALYTICS_CHART_DEFAULT_STATE,
461
+ * layers: ANALYTICS_CHART_DEFAULT_STATE.layers.map(l => {
462
+ * if (l.kind === "primary") return { ...l, id: "revenue", label: "Revenue", type: "bar" };
463
+ * if (l.kind === "compare") return { ...l, id: "signups", label: "Sign-ups", type: "area", visible: true };
464
+ * return l;
465
+ * }),
466
+ * });
467
+ * <AnalyticsChart data={data} state={state} onChange={setState} />
468
+ * ```
469
+ *
470
+ * ### 5. Pie view (distribution / breakdown, non-time-series)
471
+ *
472
+ * Pie needs one data point, `segments` with one row, and `segmentSeries` with labels:
473
+ *
474
+ * ```tsx
475
+ * const categories = [{ key: "verified", label: "Verified" }, { key: "unverified", label: "Unverified" }, { key: "anonymous", label: "Anonymous" }];
476
+ * const total = verified + unverified + anonymous;
477
+ * const data = [{ ts: 0, values: { primary: total } }];
478
+ * const segments = [[verified, unverified, anonymous]]; // one row; values sum to total
479
+ * const [state, setState] = React.useState({
480
+ * ...ANALYTICS_CHART_DEFAULT_STATE,
481
+ * view: "pie",
482
+ * layers: ANALYTICS_CHART_DEFAULT_STATE.layers.map(l => {
483
+ * if (l.kind === "primary") return { ...l, segmented: true, segments, segmentSeries: categories };
484
+ * if (l.kind === "compare") return { ...l, visible: false };
485
+ * return l;
486
+ * }),
487
+ * });
488
+ * <AnalyticsChart data={data} state={state} onChange={setState} />
489
+ * ```
490
+ *
491
+ * ## Segment data contract (MUST follow when segmented: true)
492
+ *
493
+ * `segments` is a 2D array: `segments[dayIndex][categoryIndex] = number`.
494
+ *
495
+ * - Outer length MUST equal `data.length` (one row per Point).
496
+ * - Inner length MUST equal `segmentSeries.length` (one value per category).
497
+ * - Each row MUST sum to `data[dayIndex].values[layerId]` (the layer's total for that day).
498
+ * - `segmentSeries` defines the category labels, in the SAME order as segment columns.
499
+ *
500
+ * Example: if `segmentSeries = [{ key: "us", label: "US" }, { key: "eu", label: "EU" }]`
501
+ * and `data[0].values.primary = 420`, then `segments[0]` must be `[usValue, euValue]`
502
+ * where `usValue + euValue === 420`. If rows don't sum to the layer total, stacked bars
503
+ * will render incorrectly (gaps or overflow).
504
+ *
505
+ * ## Palette
506
+ *
507
+ * AnalyticsChart auto-generates segment colors (blue shades for primary, amber for
508
+ * compare). You do NOT need to pass a palette prop — it just works. Segment keys
509
+ * can be any string; the component sanitizes them for CSS purposes internally.
510
+ *
511
+ * ## Layer quick reference
512
+ *
513
+ * - Layer `type` options: `"area" | "line" | "bar"`
514
+ * - Layer `kind` values: `"primary" | "compare" | "annotations"`
515
+ * - To hide a layer: `{ ...l, visible: false }`
516
+ * - To switch chart type: `{ ...l, type: "bar" }` (or `"line"`, `"area"`)
517
+ * - To rename a layer: `{ ...l, id: "myMetric", label: "My Metric" }`
518
+ *
519
+ * ## Formatting (xFormatKind / yFormatKind on state)
520
+ *
521
+ * - `{ type: "numeric" }` — plain number
522
+ * - `{ type: "short" }` — abbreviated (1.2K, 3.4M) — good default for y-axis
523
+ * - `{ type: "currency", currency: "USD", divisor: 100 }` — for cents → dollars
524
+ * - `{ type: "percent", source: "fraction" }` — for 0..1 → "45.2%"
525
+ * - `{ type: "datetime", style: "short" }` — good default for x-axis timestamps
526
+ *
527
+ * Set these on state:
528
+ * `{ ...ANALYTICS_CHART_DEFAULT_STATE, yFormatKind: { type: "currency", currency: "USD" } }`
529
+ *
530
+ * ## Scale warning
531
+ *
532
+ * All visible layers share ONE y-axis. If magnitudes differ wildly (e.g. revenue
533
+ * cents vs signup count), normalize the data BEFORE building Points. If
534
+ * normalization is impossible, use two separate `AnalyticsChart` instances stacked
535
+ * vertically.
536
+ */
537
+ declare function AnalyticsChart({
538
+ data: fullData,
539
+ annotations: fullAnnotations,
540
+ state,
541
+ onChange,
542
+ onAnnotationCreate,
543
+ strings: stringsOverride,
544
+ palette: paletteOverride,
545
+ renderTooltip,
546
+ plotMargin,
547
+ yAxisWidth,
548
+ yDomainPadding,
549
+ pie,
550
+ valueFormatter
551
+ }: AnalyticsChartProps): react_jsx_runtime0.JSX.Element;
552
+ //#endregion
553
+ export { AnalyticsChart, AnalyticsChartProps, Margin };
554
+ //# sourceMappingURL=analytics-chart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-chart.d.ts","names":[],"sources":["../../../../src/components/analytics-chart/analytics-chart.tsx"],"mappings":";;;;;;;;KAyEY,MAAA;EACV,GAAA;EACA,KAAA;EACA,MAAA;EACA,IAAA;AAAA;;;;;;;AA6SF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4SA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA5SY,mBAAA;;;EAGV,IAAA,EAAM,KAAA;EAEN,WAAA,GAAc,UAAA;;;;;EAKd,KAAA,EAAO,mBAAA;EACP,QAAA,EAAU,KAAA,CAAM,QAAA,CAAS,KAAA,CAAM,cAAA,CAAe,mBAAA;;;EAG9C,kBAAA,IAAsB,UAAA,EAAY,UAAA;EAElC,OAAA,GAAU,OAAA,CAAQ,qBAAA;;;EAGlB,OAAA,GAAU,OAAA,CAAQ,qBAAA;;;;EAIlB,aAAA,IAAiB,GAAA,EAAK,4BAAA,KAAiC,SAAA;;;;EAIvD,UAAA,GAAa,MAAA;EAEb,UAAA;EAEA,cAAA;EAEA,GAAA,GAAM,sBAAA;;;EAGN,cAAA,IAAkB,KAAA,UAAe,IAAA,EAAM,UAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwQzB,cAAA,CAAA;EACd,IAAA,EAAM,QAAA;EACN,WAAA,EAAa,eAAA;EACb,KAAA;EACA,QAAA;EACA,kBAAA;EACA,OAAA,EAAS,eAAA;EACT,OAAA,EAAS,eAAA;EACT,aAAA;EACA,UAAA;EACA,UAAA;EACA,cAAA;EACA,GAAA;EACA;AAAA,GACC,mBAAA,GAAmB,kBAAA,CAAA,GAAA,CAAA,OAAA"}