@stackframe/dashboard-ui-components 2.8.84 → 2.8.86

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 +8562 -2857
  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,1021 @@
1
+ "use client";
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+ const require_chunk = require('../../chunk-BE-pF4vm.js');
5
+ let _phosphor_icons_react = require("@phosphor-icons/react");
6
+ let _stackframe_stack_ui = require("@stackframe/stack-ui");
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+ let recharts = require("recharts");
10
+ let ___chart_container_js = require("../chart-container.js");
11
+ let __default_analytics_chart_tooltip_js = require("./default-analytics-chart-tooltip.js");
12
+ let __format_js = require("./format.js");
13
+ let __types_js = require("./types.js");
14
+ let ___button_js = require("../button.js");
15
+ let __analytics_chart_pie_js = require("./analytics-chart-pie.js");
16
+ let __palette_js = require("./palette.js");
17
+ let __render_data_series_js = require("./render-data-series.js");
18
+ let __state_js = require("./state.js");
19
+ let __strings_js = require("./strings.js");
20
+
21
+ //#region src/components/analytics-chart/analytics-chart.tsx
22
+ const FALLBACK_PRIMARY_STYLE = {
23
+ color: "#2563eb",
24
+ type: "area",
25
+ strokeStyle: "solid",
26
+ fillOpacity: 0
27
+ };
28
+ const FALLBACK_COMPARE_STYLE = {
29
+ color: "#f59e0b",
30
+ type: "line",
31
+ strokeStyle: "dashed",
32
+ fillOpacity: 0
33
+ };
34
+ const FALLBACK_ANNOTATION_COLOR = "#f59e0b";
35
+ function buildTooltipLayerView(args) {
36
+ const { show, layer, color, segmented, segmentSeries, segmentRows, segmentTotals, segmentColorsLight, segmentColorsDark, activeIndex, activePoint, fallbackLabel } = args;
37
+ if (!show || !layer) return null;
38
+ const segments = segmented ? segmentSeries.map((s, sIdx) => ({
39
+ key: s.key,
40
+ label: s.label,
41
+ value: segmentRows[activeIndex]?.[sIdx] ?? 0,
42
+ color: segmentColorsLight[sIdx],
43
+ colorDark: segmentColorsDark[sIdx]
44
+ })) : [];
45
+ return {
46
+ id: layer.id,
47
+ label: layer.label || fallbackLabel || "",
48
+ color,
49
+ colorDark: color,
50
+ total: segmented ? segmentTotals[activeIndex] ?? 0 : (0, __types_js.pointValue)(activePoint, layer.id),
51
+ segmented,
52
+ segments
53
+ };
54
+ }
55
+ /**
56
+ * Preferred chart for all time-series: area, line, bar, compare layers,
57
+ * segmented stacks, tooltips, zoom, and annotations. Wrap in
58
+ * `DesignChartCard` for the title/description chrome. Only fall back to
59
+ * raw Recharts for non-time-series visuals (static rankings etc.).
60
+ *
61
+ * ## Data shape
62
+ *
63
+ * `data` is `Point[]`, where `Point = { ts: number, values: Record<string, number> }`.
64
+ * `ts` is a Unix milliseconds timestamp. `values` maps layer id → numeric value
65
+ * at that bucket. Example:
66
+ *
67
+ * ```ts
68
+ * { ts: 1743465600000, values: { primary: 420 } }
69
+ * { ts: 1743465600000, values: { primary: 420, compare: 380 } } // with compare layer
70
+ * ```
71
+ *
72
+ * ## State is fully controlled — start from ANALYTICS_CHART_DEFAULT_STATE
73
+ *
74
+ * The default state ships with three pre-configured layers: `"primary"`,
75
+ * `"compare"`, and `"annotations"`. ALWAYS spread from
76
+ * `ANALYTICS_CHART_DEFAULT_STATE` and map over `layers` to override. Do NOT
77
+ * hand-build the layer array from scratch — you will miss fields and crash.
78
+ *
79
+ * ```ts
80
+ * // Default state shape (for reference — spread from the constant, don't copy):
81
+ * {
82
+ * view: "timeseries",
83
+ * layers: [
84
+ * { id: "primary", kind: "primary", label: "Current", visible: true, color: "#2563eb",
85
+ * segmented: false, type: "area", strokeStyle: "solid", fillOpacity: 0.22, inProgressFromIndex: null },
86
+ * { id: "compare", kind: "compare", label: "Previous period", visible: true, color: "#f59e0b",
87
+ * segmented: false, type: "line", strokeStyle: "dashed", inProgressFromIndex: null },
88
+ * { id: "annotations", kind: "annotations", label: "Annotations", visible: true, color: "#f59e0b" },
89
+ * ],
90
+ * xFormatKind: { type: "datetime", style: "short" },
91
+ * yFormatKind: { type: "short" },
92
+ * showGrid: true, showXAxis: true, showYAxis: true,
93
+ * zoomRange: null, pinnedIndex: null,
94
+ * }
95
+ * ```
96
+ *
97
+ * ## onChange — CRITICAL, get this right
98
+ *
99
+ * `onChange` fires with an `AnalyticsChartState` object — NOT your custom
100
+ * wrapper. If you store chart data and state together, `onChange` MUST only
101
+ * update the state part. Keep data and state in SEPARATE hooks:
102
+ *
103
+ * ```tsx
104
+ * // WRONG — overwrites your data with a bare state object, crashes on next render:
105
+ * const [combined, setCombined] = React.useState({ data: [], state: ANALYTICS_CHART_DEFAULT_STATE });
106
+ * <AnalyticsChart data={combined.data} state={combined.state} onChange={setCombined} />
107
+ *
108
+ * // RIGHT — two hooks:
109
+ * const [data, setData] = React.useState([]);
110
+ * const [chartState, setChartState] = React.useState({ ...ANALYTICS_CHART_DEFAULT_STATE });
111
+ * <AnalyticsChart data={data} state={chartState} onChange={setChartState} />
112
+ * ```
113
+ *
114
+ * NEVER pass a setter that manages a combined `{ data, state }` object directly to `onChange`.
115
+ *
116
+ * ## Common patterns
117
+ *
118
+ * ### 1. Simplest — one area layer, no compare
119
+ *
120
+ * ```tsx
121
+ * const data = rows.map(r => ({ ts: r.bucketTs, values: { primary: r.count } }));
122
+ * const [state, setState] = React.useState({
123
+ * ...ANALYTICS_CHART_DEFAULT_STATE,
124
+ * layers: ANALYTICS_CHART_DEFAULT_STATE.layers.map(l =>
125
+ * l.kind === "compare" ? { ...l, visible: false } : l
126
+ * ),
127
+ * });
128
+ * <DesignChartCard title="Signups" description="Last 30 days">
129
+ * <AnalyticsChart data={data} state={state} onChange={setState} />
130
+ * </DesignChartCard>
131
+ * ```
132
+ *
133
+ * ### 2. Current vs previous period (compare)
134
+ *
135
+ * ```tsx
136
+ * const data = rows.map(r => ({
137
+ * ts: r.bucketTs,
138
+ * values: { primary: r.thisPeriod, compare: r.lastPeriod },
139
+ * }));
140
+ * const [state, setState] = React.useState(ANALYTICS_CHART_DEFAULT_STATE);
141
+ * <AnalyticsChart data={data} state={state} onChange={setState} />
142
+ * ```
143
+ *
144
+ * ### 3. Stacked bar with breakdown (segmented)
145
+ *
146
+ * ```tsx
147
+ * const regions = [{ key: "us", label: "US" }, { key: "eu", label: "EU" }];
148
+ * const segments = rows.map(r => [r.signupsUs, r.signupsEu]); // MUST sum to primary value per row
149
+ * const [state, setState] = React.useState({
150
+ * ...ANALYTICS_CHART_DEFAULT_STATE,
151
+ * layers: ANALYTICS_CHART_DEFAULT_STATE.layers.map(l => {
152
+ * if (l.kind === "primary") return { ...l, type: "bar", segmented: true, segments, segmentSeries: regions };
153
+ * if (l.kind === "compare") return { ...l, visible: false };
154
+ * return l;
155
+ * }),
156
+ * });
157
+ * <AnalyticsChart data={data} state={state} onChange={setState} />
158
+ * ```
159
+ *
160
+ * ### 4. Two metrics on one chart (revenue bars + signups area)
161
+ *
162
+ * ```tsx
163
+ * // IMPORTANT: metrics share one y-axis, so normalize into the same range.
164
+ * const data = rows.map(r => ({
165
+ * ts: r.bucketTs,
166
+ * values: { revenue: r.revenueCents / 100, signups: r.signups },
167
+ * }));
168
+ * const [state, setState] = React.useState({
169
+ * ...ANALYTICS_CHART_DEFAULT_STATE,
170
+ * layers: ANALYTICS_CHART_DEFAULT_STATE.layers.map(l => {
171
+ * if (l.kind === "primary") return { ...l, id: "revenue", label: "Revenue", type: "bar" };
172
+ * if (l.kind === "compare") return { ...l, id: "signups", label: "Sign-ups", type: "area", visible: true };
173
+ * return l;
174
+ * }),
175
+ * });
176
+ * <AnalyticsChart data={data} state={state} onChange={setState} />
177
+ * ```
178
+ *
179
+ * ### 5. Pie view (distribution / breakdown, non-time-series)
180
+ *
181
+ * Pie needs one data point, `segments` with one row, and `segmentSeries` with labels:
182
+ *
183
+ * ```tsx
184
+ * const categories = [{ key: "verified", label: "Verified" }, { key: "unverified", label: "Unverified" }, { key: "anonymous", label: "Anonymous" }];
185
+ * const total = verified + unverified + anonymous;
186
+ * const data = [{ ts: 0, values: { primary: total } }];
187
+ * const segments = [[verified, unverified, anonymous]]; // one row; values sum to total
188
+ * const [state, setState] = React.useState({
189
+ * ...ANALYTICS_CHART_DEFAULT_STATE,
190
+ * view: "pie",
191
+ * layers: ANALYTICS_CHART_DEFAULT_STATE.layers.map(l => {
192
+ * if (l.kind === "primary") return { ...l, segmented: true, segments, segmentSeries: categories };
193
+ * if (l.kind === "compare") return { ...l, visible: false };
194
+ * return l;
195
+ * }),
196
+ * });
197
+ * <AnalyticsChart data={data} state={state} onChange={setState} />
198
+ * ```
199
+ *
200
+ * ## Segment data contract (MUST follow when segmented: true)
201
+ *
202
+ * `segments` is a 2D array: `segments[dayIndex][categoryIndex] = number`.
203
+ *
204
+ * - Outer length MUST equal `data.length` (one row per Point).
205
+ * - Inner length MUST equal `segmentSeries.length` (one value per category).
206
+ * - Each row MUST sum to `data[dayIndex].values[layerId]` (the layer's total for that day).
207
+ * - `segmentSeries` defines the category labels, in the SAME order as segment columns.
208
+ *
209
+ * Example: if `segmentSeries = [{ key: "us", label: "US" }, { key: "eu", label: "EU" }]`
210
+ * and `data[0].values.primary = 420`, then `segments[0]` must be `[usValue, euValue]`
211
+ * where `usValue + euValue === 420`. If rows don't sum to the layer total, stacked bars
212
+ * will render incorrectly (gaps or overflow).
213
+ *
214
+ * ## Palette
215
+ *
216
+ * AnalyticsChart auto-generates segment colors (blue shades for primary, amber for
217
+ * compare). You do NOT need to pass a palette prop — it just works. Segment keys
218
+ * can be any string; the component sanitizes them for CSS purposes internally.
219
+ *
220
+ * ## Layer quick reference
221
+ *
222
+ * - Layer `type` options: `"area" | "line" | "bar"`
223
+ * - Layer `kind` values: `"primary" | "compare" | "annotations"`
224
+ * - To hide a layer: `{ ...l, visible: false }`
225
+ * - To switch chart type: `{ ...l, type: "bar" }` (or `"line"`, `"area"`)
226
+ * - To rename a layer: `{ ...l, id: "myMetric", label: "My Metric" }`
227
+ *
228
+ * ## Formatting (xFormatKind / yFormatKind on state)
229
+ *
230
+ * - `{ type: "numeric" }` — plain number
231
+ * - `{ type: "short" }` — abbreviated (1.2K, 3.4M) — good default for y-axis
232
+ * - `{ type: "currency", currency: "USD", divisor: 100 }` — for cents → dollars
233
+ * - `{ type: "percent", source: "fraction" }` — for 0..1 → "45.2%"
234
+ * - `{ type: "datetime", style: "short" }` — good default for x-axis timestamps
235
+ *
236
+ * Set these on state:
237
+ * `{ ...ANALYTICS_CHART_DEFAULT_STATE, yFormatKind: { type: "currency", currency: "USD" } }`
238
+ *
239
+ * ## Scale warning
240
+ *
241
+ * All visible layers share ONE y-axis. If magnitudes differ wildly (e.g. revenue
242
+ * cents vs signup count), normalize the data BEFORE building Points. If
243
+ * normalization is impossible, use two separate `AnalyticsChart` instances stacked
244
+ * vertically.
245
+ */
246
+ function AnalyticsChart({ data: fullData, annotations: fullAnnotations = [], state, onChange, onAnnotationCreate, strings: stringsOverride, palette: paletteOverride, renderTooltip, plotMargin, yAxisWidth = 48, yDomainPadding = .1, pie, valueFormatter }) {
247
+ const resolvedPlotMargin = (0, react.useMemo)(() => ({
248
+ top: plotMargin?.top ?? 16,
249
+ right: plotMargin?.right ?? 24,
250
+ bottom: plotMargin?.bottom ?? 8,
251
+ left: plotMargin?.left ?? 12
252
+ }), [plotMargin]);
253
+ const fmtValue = valueFormatter ?? __format_js.formatValue;
254
+ const strings = (0, react.useMemo)(() => (0, __strings_js.resolveAnalyticsChartStrings)(stringsOverride), [stringsOverride]);
255
+ const palette = (0, react.useMemo)(() => (0, __palette_js.resolveAnalyticsChartPalette)(paletteOverride), [paletteOverride]);
256
+ const renderTooltipFn = (0, react.useMemo)(() => renderTooltip ?? ((ctx) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__default_analytics_chart_tooltip_js.DefaultAnalyticsChartTooltip, { ctx })), [renderTooltip]);
257
+ const { xFormatKind, yFormatKind, layers } = state;
258
+ const timeseries = (0, __state_js.isTimeseriesState)(state) ? state : null;
259
+ const showGrid = timeseries?.showGrid ?? false;
260
+ const showXAxis = timeseries?.showXAxis ?? false;
261
+ const showYAxis = timeseries?.showYAxis ?? false;
262
+ const zoomRange = timeseries?.zoomRange ?? null;
263
+ const pinnedIndex = timeseries?.pinnedIndex ?? null;
264
+ const primaryLayer = (0, __state_js.findPrimaryLayer)(layers);
265
+ const compareLayer = (0, __state_js.findCompareLayer)(layers);
266
+ const annotationsLayer = (0, __state_js.findAnnotationsLayer)(layers);
267
+ const showPrimary = primaryLayer?.visible ?? false;
268
+ const showCompare = compareLayer?.visible ?? false;
269
+ const showAnnotationsLayer = annotationsLayer?.visible ?? false;
270
+ const primaryStyle = primaryLayer ? (0, __state_js.resolveDataLayerStyle)(primaryLayer) : FALLBACK_PRIMARY_STYLE;
271
+ const compareStyle = compareLayer ? (0, __state_js.resolveDataLayerStyle)(compareLayer) : FALLBACK_COMPARE_STYLE;
272
+ const primaryColor = primaryStyle.color;
273
+ const compareColor = compareStyle.color;
274
+ const annotationColor = annotationsLayer?.color ?? FALLBACK_ANNOTATION_COLOR;
275
+ const primaryStroke = __state_js.STROKE_DASHARRAY[primaryStyle.strokeStyle];
276
+ const compareStroke = __state_js.STROKE_DASHARRAY[compareStyle.strokeStyle];
277
+ const primaryFillOpacity = primaryStyle.fillOpacity;
278
+ const compareFillOpacity = compareStyle.fillOpacity;
279
+ const setTimeseriesField = (0, react.useCallback)((key, value) => {
280
+ onChange((prev) => {
281
+ if (prev.view !== "timeseries") return prev;
282
+ return {
283
+ ...prev,
284
+ [key]: value
285
+ };
286
+ });
287
+ }, [onChange]);
288
+ const wrapperRef = (0, react.useRef)(null);
289
+ const [hoverIndex, setHoverIndex] = (0, react.useState)(null);
290
+ const [committedRange, setCommittedRange] = (0, react.useState)(null);
291
+ const [annotationDraft, setAnnotationDraft] = (0, react.useState)(null);
292
+ const [dragAnchor, setDragAnchor] = (0, react.useState)(null);
293
+ const [brushStart, setBrushStart] = (0, react.useState)(null);
294
+ const [brushEnd, setBrushEnd] = (0, react.useState)(null);
295
+ const [pieHoverKey, setPieHoverKey] = (0, react.useState)(null);
296
+ const activeIndex = pinnedIndex ?? hoverIndex;
297
+ const primarySegmentSeries = (0, react.useMemo)(() => primaryLayer?.segmentSeries ?? __state_js.EMPTY_SERIES, [primaryLayer?.segmentSeries]);
298
+ const compareSegmentSeries = (0, react.useMemo)(() => compareLayer?.segmentSeries ?? __state_js.EMPTY_SERIES, [compareLayer?.segmentSeries]);
299
+ const primaryFullSegments = (0, react.useMemo)(() => primaryLayer?.segments ?? __state_js.EMPTY_MATRIX, [primaryLayer?.segments]);
300
+ const compareFullSegments = (0, react.useMemo)(() => compareLayer?.segments ?? __state_js.EMPTY_MATRIX, [compareLayer?.segments]);
301
+ const primarySegmented = (primaryLayer?.segmented ?? false) && showPrimary && primarySegmentSeries.length > 0 && primaryFullSegments.length > 0;
302
+ const compareSegmented = (compareLayer?.segmented ?? false) && showCompare && compareSegmentSeries.length > 0 && compareFullSegments.length > 0;
303
+ const visibleStart = zoomRange ? zoomRange[0] : 0;
304
+ const visibleEnd = zoomRange ? zoomRange[1] : fullData.length - 1;
305
+ const data = (0, react.useMemo)(() => fullData.slice(visibleStart, visibleEnd + 1), [
306
+ fullData,
307
+ visibleStart,
308
+ visibleEnd
309
+ ]);
310
+ const primarySegments = (0, react.useMemo)(() => primaryFullSegments.slice(visibleStart, visibleEnd + 1), [
311
+ primaryFullSegments,
312
+ visibleStart,
313
+ visibleEnd
314
+ ]);
315
+ const compareSegments = (0, react.useMemo)(() => compareFullSegments.slice(visibleStart, visibleEnd + 1), [
316
+ compareFullSegments,
317
+ visibleStart,
318
+ visibleEnd
319
+ ]);
320
+ const primarySegmentTotals = (0, react.useMemo)(() => primarySegments.map((row) => row.reduce((a, b) => a + b, 0)), [primarySegments]);
321
+ const compareSegmentTotals = (0, react.useMemo)(() => compareSegments.map((row) => row.reduce((a, b) => a + b, 0)), [compareSegments]);
322
+ const yDomainMax = (0, react.useMemo)(() => {
323
+ const layerMaxes = layers.filter(__state_js.isAnalyticsChartDataLayer).map((l) => l.id).map((id) => data.reduce((m, p) => Math.max(m, (0, __types_js.pointValue)(p, id)), 0));
324
+ const primaryStackMax = primarySegmentTotals.reduce((m, v) => Math.max(m, v), 0);
325
+ const compareStackMax = compareSegmentTotals.reduce((m, v) => Math.max(m, v), 0);
326
+ const rawMax = Math.max(0, ...layerMaxes, primaryStackMax, compareStackMax);
327
+ return Math.ceil(rawMax * (1 + yDomainPadding));
328
+ }, [
329
+ data,
330
+ layers,
331
+ primarySegmentTotals,
332
+ compareSegmentTotals,
333
+ yDomainPadding
334
+ ]);
335
+ const segmentColors = (0, react.useMemo)(() => {
336
+ return {
337
+ primary: {
338
+ light: (0, __palette_js.buildRampColors)(palette.primary, primarySegmentSeries.length, "light"),
339
+ dark: (0, __palette_js.buildRampColors)(palette.primary, primarySegmentSeries.length, "dark")
340
+ },
341
+ compare: {
342
+ light: (0, __palette_js.buildRampColors)(palette.compare, compareSegmentSeries.length, "light"),
343
+ dark: (0, __palette_js.buildRampColors)(palette.compare, compareSegmentSeries.length, "dark")
344
+ }
345
+ };
346
+ }, [
347
+ primarySegmentSeries.length,
348
+ compareSegmentSeries.length,
349
+ palette
350
+ ]);
351
+ const aggregatedPrimarySegments = (0, react.useMemo)(() => primarySegmentSeries.map((_, sIdx) => primarySegments.reduce((acc, row) => acc + (row[sIdx] ?? 0), 0)), [primarySegmentSeries, primarySegments]);
352
+ const aggregatedCompareSegments = (0, react.useMemo)(() => compareSegmentSeries.map((_, sIdx) => compareSegments.reduce((acc, row) => acc + (row[sIdx] ?? 0), 0)), [compareSegmentSeries, compareSegments]);
353
+ const aggregatedPrimaryTotal = (0, react.useMemo)(() => aggregatedPrimarySegments.reduce((a, b) => a + b, 0), [aggregatedPrimarySegments]);
354
+ const aggregatedCompareTotal = (0, react.useMemo)(() => aggregatedCompareSegments.reduce((a, b) => a + b, 0), [aggregatedCompareSegments]);
355
+ const annotations = (0, react.useMemo)(() => {
356
+ return fullAnnotations.filter((a) => a.index >= visibleStart && a.index <= visibleEnd).map((a) => ({
357
+ ...a,
358
+ index: a.index - visibleStart
359
+ }));
360
+ }, [
361
+ fullAnnotations,
362
+ visibleStart,
363
+ visibleEnd
364
+ ]);
365
+ const brushing = brushStart != null;
366
+ const N = data.length;
367
+ const primaryKey = primaryLayer?.id ?? "__analytics_primary";
368
+ const compareKey = compareLayer?.id ?? "__analytics_compare";
369
+ const primarySolidKey = `${primaryKey}_solid`;
370
+ const primaryDashedKey = `${primaryKey}_dashed`;
371
+ const compareSolidKey = `${compareKey}_solid`;
372
+ const compareDashedKey = `${compareKey}_dashed`;
373
+ const primarySegKey = (0, react.useCallback)((segKey) => `${primaryKey}_seg_${(0, __types_js.cssIdent)(segKey)}`, [primaryKey]);
374
+ const compareSegKey = (0, react.useCallback)((segKey) => `${compareKey}_seg_${(0, __types_js.cssIdent)(segKey)}`, [compareKey]);
375
+ const primaryInProgressLocalIdx = (0, __state_js.computeLocalInProgressIdx)(primaryLayer?.inProgressFromIndex, visibleStart, visibleEnd);
376
+ const compareInProgressLocalIdx = (0, __state_js.computeLocalInProgressIdx)(compareLayer?.inProgressFromIndex, visibleStart, visibleEnd);
377
+ const primaryHasInProgress = primaryInProgressLocalIdx != null && !primarySegmented && (primaryStyle.type === "line" || primaryStyle.type === "area");
378
+ const compareHasInProgress = compareInProgressLocalIdx != null && !compareSegmented && (compareStyle.type === "line" || compareStyle.type === "area");
379
+ const chartData = (0, react.useMemo)(() => {
380
+ return data.map((point, i) => {
381
+ const row = {
382
+ index: i,
383
+ ts: point.ts
384
+ };
385
+ for (const [k, v] of Object.entries(point.values)) row[k] = v;
386
+ if (primaryLayer && primaryHasInProgress) {
387
+ const k = primaryInProgressLocalIdx;
388
+ const v = (0, __types_js.pointValue)(point, primaryLayer.id);
389
+ row[primarySolidKey] = i < k ? v : null;
390
+ row[primaryDashedKey] = i >= k - 1 ? v : null;
391
+ }
392
+ if (compareLayer && compareHasInProgress) {
393
+ const k = compareInProgressLocalIdx;
394
+ const v = (0, __types_js.pointValue)(point, compareLayer.id);
395
+ row[compareSolidKey] = i < k ? v : null;
396
+ row[compareDashedKey] = i >= k - 1 ? v : null;
397
+ }
398
+ if (primarySegmented) primarySegmentSeries.forEach((s, sIdx) => {
399
+ row[primarySegKey(s.key)] = primarySegments[i]?.[sIdx] ?? 0;
400
+ });
401
+ if (compareSegmented) compareSegmentSeries.forEach((s, sIdx) => {
402
+ row[compareSegKey(s.key)] = compareSegments[i]?.[sIdx] ?? 0;
403
+ });
404
+ return row;
405
+ });
406
+ }, [
407
+ data,
408
+ primaryLayer,
409
+ compareLayer,
410
+ primarySolidKey,
411
+ primaryDashedKey,
412
+ compareSolidKey,
413
+ compareDashedKey,
414
+ primarySegKey,
415
+ compareSegKey,
416
+ primarySegments,
417
+ compareSegments,
418
+ primarySegmentSeries,
419
+ compareSegmentSeries,
420
+ primarySegmented,
421
+ compareSegmented,
422
+ primaryHasInProgress,
423
+ compareHasInProgress,
424
+ primaryInProgressLocalIdx,
425
+ compareInProgressLocalIdx
426
+ ]);
427
+ const chartConfig = (0, react.useMemo)(() => {
428
+ const primaryLabel = primaryLayer?.label ?? "";
429
+ const compareLabel = compareLayer?.label ?? "";
430
+ const config = {};
431
+ if (primaryLayer) {
432
+ config[primaryLayer.id] = {
433
+ label: primaryLabel,
434
+ color: primaryColor
435
+ };
436
+ if (primaryHasInProgress) {
437
+ config[primarySolidKey] = {
438
+ label: primaryLabel,
439
+ color: primaryColor
440
+ };
441
+ config[primaryDashedKey] = {
442
+ label: primaryLabel,
443
+ color: primaryColor
444
+ };
445
+ }
446
+ }
447
+ if (compareLayer) {
448
+ config[compareLayer.id] = {
449
+ label: compareLabel,
450
+ color: compareColor
451
+ };
452
+ if (compareHasInProgress) {
453
+ config[compareSolidKey] = {
454
+ label: compareLabel,
455
+ color: compareColor
456
+ };
457
+ config[compareDashedKey] = {
458
+ label: compareLabel,
459
+ color: compareColor
460
+ };
461
+ }
462
+ }
463
+ if (primarySegmented) primarySegmentSeries.forEach((s, i) => {
464
+ config[primarySegKey(s.key)] = {
465
+ label: s.label,
466
+ theme: {
467
+ light: segmentColors.primary.light[i],
468
+ dark: segmentColors.primary.dark[i]
469
+ }
470
+ };
471
+ });
472
+ if (compareSegmented) compareSegmentSeries.forEach((s, i) => {
473
+ config[compareSegKey(s.key)] = {
474
+ label: s.label,
475
+ theme: {
476
+ light: segmentColors.compare.light[i],
477
+ dark: segmentColors.compare.dark[i]
478
+ }
479
+ };
480
+ });
481
+ return config;
482
+ }, [
483
+ primaryLayer,
484
+ compareLayer,
485
+ primarySolidKey,
486
+ primaryDashedKey,
487
+ compareSolidKey,
488
+ compareDashedKey,
489
+ primarySegKey,
490
+ compareSegKey,
491
+ primaryColor,
492
+ compareColor,
493
+ primaryHasInProgress,
494
+ compareHasInProgress,
495
+ primarySegmented,
496
+ compareSegmented,
497
+ primarySegmentSeries,
498
+ compareSegmentSeries,
499
+ segmentColors
500
+ ]);
501
+ const handleChartMouseMove = (0, react.useCallback)((rechartsState) => {
502
+ const i = rechartsState.activeTooltipIndex;
503
+ if (typeof i !== "number") return;
504
+ setHoverIndex(i);
505
+ if (dragAnchor != null && (brushStart != null || i !== dragAnchor)) {
506
+ if (brushStart == null) setBrushStart(dragAnchor);
507
+ setBrushEnd(i);
508
+ }
509
+ }, [dragAnchor, brushStart]);
510
+ const handleChartMouseDown = (0, react.useCallback)((rechartsState, e) => {
511
+ if (e.button !== 0) return;
512
+ const i = rechartsState.activeTooltipIndex;
513
+ if (typeof i !== "number") return;
514
+ e.preventDefault();
515
+ setDragAnchor(i);
516
+ setAnnotationDraft(null);
517
+ }, []);
518
+ const handleChartMouseUp = (0, react.useCallback)((_, e) => {
519
+ e.stopPropagation();
520
+ if (brushStart != null && brushEnd != null) {
521
+ const lo = Math.min(brushStart, brushEnd);
522
+ const hi = Math.max(brushStart, brushEnd);
523
+ setBrushStart(null);
524
+ setBrushEnd(null);
525
+ setDragAnchor(null);
526
+ if (hi - lo >= 1) setCommittedRange([lo, hi]);
527
+ return;
528
+ }
529
+ setDragAnchor(null);
530
+ if (pinnedIndex != null) setTimeseriesField("pinnedIndex", null);
531
+ else if (hoverIndex != null) setTimeseriesField("pinnedIndex", hoverIndex);
532
+ }, [
533
+ brushStart,
534
+ brushEnd,
535
+ hoverIndex,
536
+ pinnedIndex,
537
+ setTimeseriesField
538
+ ]);
539
+ const handleChartMouseLeave = (0, react.useCallback)(() => {
540
+ if (!brushing) setHoverIndex(null);
541
+ }, [brushing]);
542
+ const handleKeyDown = (0, react.useCallback)((e) => {
543
+ if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
544
+ e.preventDefault();
545
+ setHoverIndex((cur) => {
546
+ const base = cur ?? pinnedIndex ?? 0;
547
+ return e.key === "ArrowRight" ? Math.min(N - 1, base + 1) : Math.max(0, base - 1);
548
+ });
549
+ return;
550
+ }
551
+ if (e.key === "Home") {
552
+ e.preventDefault();
553
+ setHoverIndex(0);
554
+ return;
555
+ }
556
+ if (e.key === "End") {
557
+ e.preventDefault();
558
+ setHoverIndex(N - 1);
559
+ return;
560
+ }
561
+ if (e.key === "Enter" || e.key === " ") {
562
+ e.preventDefault();
563
+ if (pinnedIndex != null) setTimeseriesField("pinnedIndex", null);
564
+ else if (hoverIndex != null) setTimeseriesField("pinnedIndex", hoverIndex);
565
+ }
566
+ }, [
567
+ N,
568
+ hoverIndex,
569
+ pinnedIndex,
570
+ setTimeseriesField
571
+ ]);
572
+ (0, react.useEffect)(() => {
573
+ if (pinnedIndex == null) return;
574
+ const onKey = (e) => {
575
+ if (e.key === "Escape") setTimeseriesField("pinnedIndex", null);
576
+ };
577
+ const onDown = (e) => {
578
+ if (!wrapperRef.current) return;
579
+ if (!wrapperRef.current.contains(e.target)) setTimeseriesField("pinnedIndex", null);
580
+ };
581
+ window.addEventListener("keydown", onKey);
582
+ window.addEventListener("mousedown", onDown);
583
+ return () => {
584
+ window.removeEventListener("keydown", onKey);
585
+ window.removeEventListener("mousedown", onDown);
586
+ };
587
+ }, [pinnedIndex, setTimeseriesField]);
588
+ const plotInset = resolvedPlotMargin.left + resolvedPlotMargin.right;
589
+ const indexToCss = (0, react.useCallback)((i) => {
590
+ if (N <= 1) return `calc(${resolvedPlotMargin.left}px + (100% - ${plotInset}px) * 0.5)`;
591
+ const t = Math.max(0, Math.min(1, i / (N - 1)));
592
+ return `calc(${resolvedPlotMargin.left}px + (100% - ${plotInset}px) * ${t})`;
593
+ }, [
594
+ N,
595
+ resolvedPlotMargin.left,
596
+ plotInset
597
+ ]);
598
+ const shouldFlip = (activeIndex != null ? N <= 1 ? 50 : activeIndex / (N - 1) * 100 : 0) > 68;
599
+ const activePoint = activeIndex != null ? data[activeIndex] : null;
600
+ const chartAriaLabel = primaryLayer?.label || "Chart";
601
+ if (state.view === "pie") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__analytics_chart_pie_js.AnalyticsChartPie, {
602
+ wrapperRef,
603
+ primarySegmentSeries,
604
+ compareSegmentSeries,
605
+ aggregatedPrimarySegments,
606
+ aggregatedCompareSegments,
607
+ aggregatedPrimaryTotal,
608
+ aggregatedCompareTotal,
609
+ segmentColors,
610
+ showPrimary,
611
+ showCompare,
612
+ xFormatKind,
613
+ yFormatKind,
614
+ hoverKey: pieHoverKey,
615
+ setHoverKey: setPieHoverKey,
616
+ zoomRange,
617
+ onResetZoom: () => {
618
+ onChange((prev) => ({
619
+ ...prev,
620
+ zoomRange: null,
621
+ pinnedIndex: null
622
+ }));
623
+ setCommittedRange(null);
624
+ setAnnotationDraft(null);
625
+ setHoverIndex(null);
626
+ },
627
+ visibleStart,
628
+ visibleEnd,
629
+ fullData,
630
+ strings,
631
+ fmtValue,
632
+ pie
633
+ });
634
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
635
+ ref: wrapperRef,
636
+ className: (0, _stackframe_stack_ui.cn)("relative w-full select-none rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/40 dark:focus-visible:ring-blue-400/40", brushing && "[&_.recharts-wrapper]:cursor-ew-resize", !brushing && "[&_.recharts-wrapper]:cursor-crosshair"),
637
+ onClick: (e) => {
638
+ e.stopPropagation();
639
+ },
640
+ onKeyDown: handleKeyDown,
641
+ onFocus: () => {
642
+ if (hoverIndex == null) setHoverIndex(pinnedIndex ?? Math.floor(N / 2));
643
+ },
644
+ tabIndex: 0,
645
+ role: "img",
646
+ "aria-label": `${chartAriaLabel} over the visible ${data.length}-day range. Use arrow keys to move the cursor, Enter to pin, Escape to release. Click and drag to select a range.`,
647
+ children: [
648
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___chart_container_js.DesignChartContainer, {
649
+ config: chartConfig,
650
+ className: "aspect-auto h-[260px] w-full sm:h-[320px]",
651
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(recharts.ComposedChart, {
652
+ data: chartData,
653
+ margin: resolvedPlotMargin,
654
+ onMouseMove: handleChartMouseMove,
655
+ onMouseDown: handleChartMouseDown,
656
+ onMouseUp: handleChartMouseUp,
657
+ onMouseLeave: handleChartMouseLeave,
658
+ children: [
659
+ showGrid && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.CartesianGrid, {
660
+ vertical: false,
661
+ strokeDasharray: "3 3"
662
+ }),
663
+ showXAxis && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.XAxis, {
664
+ dataKey: "index",
665
+ tickLine: false,
666
+ axisLine: false,
667
+ tickMargin: 10,
668
+ minTickGap: 32,
669
+ tickFormatter: (value) => {
670
+ const idx = Number(value);
671
+ if (idx < 0 || idx >= data.length) return "";
672
+ return fmtValue(data[idx].ts, xFormatKind);
673
+ }
674
+ }),
675
+ showYAxis && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.YAxis, {
676
+ tickLine: false,
677
+ axisLine: false,
678
+ tickMargin: 8,
679
+ width: yAxisWidth,
680
+ domain: [0, yDomainMax],
681
+ allowDataOverflow: false,
682
+ tickFormatter: (value) => fmtValue(Math.round(Number(value)), yFormatKind)
683
+ }),
684
+ showAnnotationsLayer && annotations.map((a) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.ReferenceLine, {
685
+ x: a.index,
686
+ stroke: annotationColor,
687
+ strokeDasharray: "3 4",
688
+ strokeOpacity: .55,
689
+ ifOverflow: "visible"
690
+ }, a.index)),
691
+ committedRange && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.ReferenceArea, {
692
+ x1: committedRange[0],
693
+ x2: committedRange[1],
694
+ fill: primaryColor,
695
+ fillOpacity: .07,
696
+ stroke: primaryColor,
697
+ strokeOpacity: .45,
698
+ strokeDasharray: "3 3",
699
+ ifOverflow: "visible"
700
+ }),
701
+ brushStart != null && brushEnd != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.ReferenceArea, {
702
+ x1: Math.min(brushStart, brushEnd),
703
+ x2: Math.max(brushStart, brushEnd),
704
+ fill: primaryColor,
705
+ fillOpacity: .15,
706
+ stroke: primaryColor,
707
+ strokeOpacity: .55,
708
+ ifOverflow: "visible"
709
+ }),
710
+ showPrimary && primaryLayer && (0, __render_data_series_js.renderDataSeries)({
711
+ layer: primaryLayer,
712
+ segmented: primarySegmented,
713
+ segmentSeries: primarySegmentSeries,
714
+ segKey: primarySegKey,
715
+ stackId: "primary-segments",
716
+ strokeDasharray: primaryStroke,
717
+ segmentedStrokeDasharray: void 0,
718
+ fillOpacity: primaryFillOpacity,
719
+ segmentedFillOpacity: .78,
720
+ strokeWidth: 2,
721
+ segmentedStrokeWidth: .75,
722
+ inProgressKeys: primaryHasInProgress ? {
723
+ solid: primarySolidKey,
724
+ dashed: primaryDashedKey
725
+ } : null
726
+ }),
727
+ showCompare && compareLayer && (0, __render_data_series_js.renderDataSeries)({
728
+ layer: compareLayer,
729
+ segmented: compareSegmented,
730
+ segmentSeries: compareSegmentSeries,
731
+ segKey: compareSegKey,
732
+ stackId: "compare-segments",
733
+ strokeDasharray: compareStroke,
734
+ segmentedStrokeDasharray: compareStroke,
735
+ fillOpacity: compareFillOpacity,
736
+ segmentedFillOpacity: .6,
737
+ baseOpacity: compareSegmented ? .9 : 1,
738
+ strokeWidth: 1.5,
739
+ segmentedStrokeWidth: .75,
740
+ inProgressKeys: compareHasInProgress ? {
741
+ solid: compareSolidKey,
742
+ dashed: compareDashedKey
743
+ } : null
744
+ })
745
+ ]
746
+ })
747
+ }),
748
+ activeIndex != null && activePoint && !brushing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
749
+ className: "pointer-events-none absolute inset-y-4 z-10 w-px border-l border-dashed border-foreground/30",
750
+ style: { left: indexToCss(activeIndex) }
751
+ }),
752
+ brushStart != null && brushEnd != null && (() => {
753
+ const lo = Math.min(brushStart, brushEnd);
754
+ const hi = Math.max(brushStart, brushEnd);
755
+ const days = hi - lo + 1;
756
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
757
+ className: "pointer-events-none absolute -top-1 z-30 -translate-x-1/2 -translate-y-full rounded-lg border border-blue-500/30 bg-background/95 px-3 py-1.5 text-[11px] shadow-lg backdrop-blur-xl dark:border-blue-400/30",
758
+ style: { left: indexToCss((lo + hi) / 2) },
759
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
760
+ className: "flex items-center gap-2",
761
+ children: [
762
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
763
+ className: "font-mono text-[10px] uppercase tracking-wider text-muted-foreground",
764
+ children: strings.rangeLabel
765
+ }),
766
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
767
+ className: "font-mono tabular-nums text-foreground",
768
+ children: [
769
+ fmtValue(data[lo].ts, xFormatKind),
770
+ " – ",
771
+ fmtValue(data[hi].ts, xFormatKind)
772
+ ]
773
+ }),
774
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
775
+ className: "font-mono text-[10px] tabular-nums text-muted-foreground",
776
+ children: ["· ", strings.daysShort(days)]
777
+ })
778
+ ]
779
+ })
780
+ });
781
+ })(),
782
+ zoomRange && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
783
+ className: "absolute right-2 top-2 z-20",
784
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
785
+ type: "button",
786
+ onClick: () => {
787
+ onChange((prev) => ({
788
+ ...prev,
789
+ zoomRange: null,
790
+ pinnedIndex: null
791
+ }));
792
+ setCommittedRange(null);
793
+ setAnnotationDraft(null);
794
+ setHoverIndex(null);
795
+ },
796
+ className: "inline-flex items-center gap-1.5 rounded-full bg-blue-500/10 px-2.5 py-1 font-mono text-[10px] font-semibold uppercase tracking-wider text-blue-600 ring-1 ring-blue-500/30 transition-colors duration-150 hover:bg-blue-500/15 hover:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 dark:text-blue-300 dark:ring-blue-400/30",
797
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.MagnifyingGlassMinusIcon, {
798
+ weight: "bold",
799
+ className: "size-3",
800
+ "aria-hidden": "true"
801
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: strings.resetZoom })]
802
+ })
803
+ }),
804
+ committedRange && !brushing && (() => {
805
+ const [lo, hi] = committedRange;
806
+ const center = (lo + hi) / 2;
807
+ const centerPct = N <= 1 ? 50 : center / (N - 1) * 100;
808
+ const anchorStyle = centerPct < 22 ? { left: "8px" } : centerPct > 78 ? { right: "8px" } : {
809
+ left: indexToCss(center),
810
+ transform: "translateX(-50%)"
811
+ };
812
+ const days = hi - lo + 1;
813
+ const draft = annotationDraft;
814
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
815
+ className: "absolute top-2 z-30",
816
+ style: anchorStyle,
817
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
818
+ className: "flex items-center gap-1 rounded-full border border-foreground/10 bg-background/95 py-1 pl-2 pr-1 shadow-[0_10px_28px_rgba(15,23,42,0.18)] backdrop-blur-xl dark:shadow-[0_10px_28px_rgba(0,0,0,0.55)]",
819
+ children: draft == null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
820
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
821
+ className: "flex items-center gap-1.5 whitespace-nowrap px-1 font-mono text-[10px] tabular-nums text-muted-foreground",
822
+ children: [
823
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
824
+ className: "text-foreground",
825
+ children: [
826
+ fmtValue(data[lo].ts, xFormatKind),
827
+ " – ",
828
+ fmtValue(data[hi].ts, xFormatKind)
829
+ ]
830
+ }),
831
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
832
+ className: "text-foreground/30",
833
+ children: "·"
834
+ }),
835
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: strings.daysShort(days) })
836
+ ]
837
+ }),
838
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
839
+ className: "h-4 w-px bg-foreground/10",
840
+ "aria-hidden": "true"
841
+ }),
842
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(___button_js.DesignButton, {
843
+ size: "sm",
844
+ variant: "ghost",
845
+ className: "h-7 gap-1.5 px-2 text-[11px]",
846
+ onClick: () => {
847
+ onChange((prev) => ({
848
+ ...prev,
849
+ zoomRange: [visibleStart + lo, visibleStart + hi],
850
+ pinnedIndex: null
851
+ }));
852
+ setCommittedRange(null);
853
+ setAnnotationDraft(null);
854
+ setHoverIndex(null);
855
+ },
856
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.MagnifyingGlassPlusIcon, {
857
+ weight: "bold",
858
+ className: "size-3.5",
859
+ "aria-hidden": "true"
860
+ }), strings.zoomIn]
861
+ }),
862
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(___button_js.DesignButton, {
863
+ size: "sm",
864
+ variant: "ghost",
865
+ className: "h-7 gap-1.5 px-2 text-[11px]",
866
+ onClick: () => setAnnotationDraft(""),
867
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.FlagIcon, {
868
+ weight: "bold",
869
+ className: "size-3.5",
870
+ "aria-hidden": "true"
871
+ }), strings.annotate]
872
+ }),
873
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___button_js.DesignButton, {
874
+ size: "sm",
875
+ variant: "ghost",
876
+ className: "h-7 w-7 p-0",
877
+ "aria-label": strings.clearSelection,
878
+ onClick: () => setCommittedRange(null),
879
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.XIcon, {
880
+ weight: "bold",
881
+ className: "size-3.5",
882
+ "aria-hidden": "true"
883
+ })
884
+ })
885
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
886
+ className: "flex items-center gap-1 px-1",
887
+ onSubmit: (e) => {
888
+ e.preventDefault();
889
+ const label = draft.trim();
890
+ if (label.length === 0) return;
891
+ const mid = Math.round((lo + hi) / 2);
892
+ onAnnotationCreate?.({
893
+ index: visibleStart + mid,
894
+ label,
895
+ description: label
896
+ });
897
+ setCommittedRange(null);
898
+ setAnnotationDraft(null);
899
+ },
900
+ children: [
901
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.FlagIcon, {
902
+ weight: "bold",
903
+ className: "size-3.5 text-amber-500 dark:text-amber-400",
904
+ "aria-hidden": "true"
905
+ }),
906
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
907
+ autoFocus: true,
908
+ type: "text",
909
+ value: draft,
910
+ onChange: (e) => setAnnotationDraft(e.target.value),
911
+ onKeyDown: (e) => {
912
+ if (e.key === "Escape") {
913
+ e.preventDefault();
914
+ setAnnotationDraft(null);
915
+ }
916
+ },
917
+ maxLength: 40,
918
+ placeholder: strings.annotationPlaceholder,
919
+ "aria-label": strings.annotationLabelAria,
920
+ className: "w-44 bg-transparent px-1 py-0.5 font-mono text-[11px] text-foreground placeholder:text-muted-foreground/60 focus-visible:outline-none"
921
+ }),
922
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___button_js.DesignButton, {
923
+ type: "submit",
924
+ size: "sm",
925
+ variant: "default",
926
+ className: "h-7 px-2.5 text-[11px]",
927
+ disabled: draft.trim().length === 0,
928
+ children: strings.save
929
+ }),
930
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___button_js.DesignButton, {
931
+ type: "button",
932
+ size: "sm",
933
+ variant: "ghost",
934
+ className: "h-7 px-2 text-[11px]",
935
+ onClick: () => setAnnotationDraft(null),
936
+ children: strings.cancel
937
+ })
938
+ ]
939
+ })
940
+ })
941
+ });
942
+ })(),
943
+ showAnnotationsLayer && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
944
+ className: "pointer-events-none absolute inset-x-0 top-1 h-0",
945
+ children: annotations.map((a) => {
946
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
947
+ className: "absolute -translate-x-1/2",
948
+ style: { left: indexToCss(a.index) },
949
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
950
+ type: "button",
951
+ "aria-label": a.description,
952
+ className: "peer pointer-events-auto inline-flex items-center gap-1 rounded-full bg-amber-500/15 px-1.5 py-[1px] font-mono text-[9px] font-semibold uppercase tracking-wider text-amber-700 ring-1 ring-amber-500/30 dark:text-amber-300 dark:bg-amber-500/20 dark:ring-amber-500/40 transition-colors duration-150 hover:bg-amber-500/25 hover:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/50",
953
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.FlagIcon, {
954
+ weight: "fill",
955
+ className: "size-2.5",
956
+ "aria-hidden": "true"
957
+ }), a.label]
958
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
959
+ role: "tooltip",
960
+ className: "pointer-events-none absolute left-1/2 top-full z-30 mt-1 hidden -translate-x-1/2 whitespace-nowrap rounded-md bg-foreground px-2 py-1 text-[10px] font-normal text-background shadow-md peer-hover:block peer-focus-visible:block",
961
+ children: a.description
962
+ })]
963
+ }, a.index);
964
+ })
965
+ }),
966
+ activeIndex != null && activePoint && (() => {
967
+ const primaryView = buildTooltipLayerView({
968
+ show: showPrimary,
969
+ layer: primaryLayer,
970
+ color: primaryColor,
971
+ segmented: primarySegmented,
972
+ segmentSeries: primarySegmentSeries,
973
+ segmentRows: primarySegments,
974
+ segmentTotals: primarySegmentTotals,
975
+ segmentColorsLight: segmentColors.primary.light,
976
+ segmentColorsDark: segmentColors.primary.dark,
977
+ activeIndex,
978
+ activePoint,
979
+ fallbackLabel: "Chart"
980
+ });
981
+ const compareView = buildTooltipLayerView({
982
+ show: showCompare,
983
+ layer: compareLayer,
984
+ color: compareColor,
985
+ segmented: compareSegmented,
986
+ segmentSeries: compareSegmentSeries,
987
+ segmentRows: compareSegments,
988
+ segmentTotals: compareSegmentTotals,
989
+ segmentColorsLight: segmentColors.compare.light,
990
+ segmentColorsDark: segmentColors.compare.dark,
991
+ activeIndex,
992
+ activePoint
993
+ });
994
+ const delta = primaryView && compareView ? (0, __format_js.formatDelta)(primaryView.total, compareView.total) : null;
995
+ const ctx = {
996
+ activeIndex,
997
+ point: activePoint,
998
+ isPinned: pinnedIndex != null,
999
+ primary: primaryView,
1000
+ compare: compareView,
1001
+ delta,
1002
+ formatValue: (v) => fmtValue(v, yFormatKind),
1003
+ formatDate: (ts) => fmtValue(ts, xFormatKind),
1004
+ strings
1005
+ };
1006
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1007
+ className: (0, _stackframe_stack_ui.cn)("pointer-events-none absolute top-10 z-20", "transition-[transform,opacity] duration-150"),
1008
+ style: {
1009
+ left: indexToCss(activeIndex),
1010
+ transform: shouldFlip ? "translateX(calc(-100% - 16px))" : "translateX(16px)"
1011
+ },
1012
+ children: renderTooltipFn(ctx)
1013
+ });
1014
+ })()
1015
+ ]
1016
+ });
1017
+ }
1018
+
1019
+ //#endregion
1020
+ exports.AnalyticsChart = AnalyticsChart;
1021
+ //# sourceMappingURL=analytics-chart.js.map