@eifi1/ui-kit 0.7.1 → 0.8.1

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 (222) hide show
  1. package/README.md +32 -17
  2. package/dist/chart.d.ts +3 -2
  3. package/dist/components/alert-banner.d.ts +59 -10
  4. package/dist/components/alert-banner.js +114 -8
  5. package/dist/components/alert-banner.js.map +1 -1
  6. package/dist/components/amount-input.d.ts +3 -0
  7. package/dist/components/button-group.d.ts +26 -0
  8. package/dist/components/button-group.js +49 -0
  9. package/dist/components/button-group.js.map +1 -0
  10. package/dist/components/calculator.d.ts +3 -0
  11. package/dist/components/chart-zoom.d.ts +63 -14
  12. package/dist/components/chart-zoom.js +82 -16
  13. package/dist/components/chart-zoom.js.map +1 -1
  14. package/dist/components/chip.d.ts +67 -2
  15. package/dist/components/chip.js +73 -8
  16. package/dist/components/chip.js.map +1 -1
  17. package/dist/components/confirm-dialog.d.ts +120 -0
  18. package/dist/components/confirm-dialog.js +96 -0
  19. package/dist/components/confirm-dialog.js.map +1 -0
  20. package/dist/components/copy-button.d.ts +62 -0
  21. package/dist/components/copy-button.js +95 -0
  22. package/dist/components/copy-button.js.map +1 -0
  23. package/dist/components/data-table-filter-popover.d.ts +1 -1
  24. package/dist/components/data-table-filters.d.ts +1 -1
  25. package/dist/components/data-table-pagination.d.ts +5 -1
  26. package/dist/components/data-table-pagination.js +71 -58
  27. package/dist/components/data-table-pagination.js.map +1 -1
  28. package/dist/components/data-table-sort.d.ts +28 -5
  29. package/dist/components/data-table-sort.js +11 -7
  30. package/dist/components/data-table-sort.js.map +1 -1
  31. package/dist/components/data-table.d.ts +1 -1
  32. package/dist/components/data-table.js +544 -423
  33. package/dist/components/data-table.js.map +1 -1
  34. package/dist/components/date-picker.d.ts +51 -5
  35. package/dist/components/date-picker.js +152 -40
  36. package/dist/components/date-picker.js.map +1 -1
  37. package/dist/components/description-list.d.ts +60 -0
  38. package/dist/components/description-list.js +112 -0
  39. package/dist/components/description-list.js.map +1 -0
  40. package/dist/components/disclosure.d.ts +37 -2
  41. package/dist/components/disclosure.js +14 -5
  42. package/dist/components/disclosure.js.map +1 -1
  43. package/dist/components/facing-pair.d.ts +2 -0
  44. package/dist/components/file-button.d.ts +17 -1
  45. package/dist/components/file-button.js +8 -1
  46. package/dist/components/file-button.js.map +1 -1
  47. package/dist/components/file-dropzone.d.ts +54 -9
  48. package/dist/components/file-dropzone.js +100 -70
  49. package/dist/components/file-dropzone.js.map +1 -1
  50. package/dist/components/floating-panel.d.ts +107 -0
  51. package/dist/components/floating-panel.js +206 -0
  52. package/dist/components/floating-panel.js.map +1 -0
  53. package/dist/components/full-bleed-dialog.d.ts +12 -1
  54. package/dist/components/full-bleed-dialog.js +28 -5
  55. package/dist/components/full-bleed-dialog.js.map +1 -1
  56. package/dist/components/modal.d.ts +6 -2
  57. package/dist/components/modal.js +2 -1
  58. package/dist/components/modal.js.map +1 -1
  59. package/dist/components/number-field.d.ts +3 -0
  60. package/dist/components/number-input.d.ts +3 -0
  61. package/dist/components/numpad-sheet.d.ts +3 -0
  62. package/dist/components/progress-bar.d.ts +54 -0
  63. package/dist/components/progress-bar.js +94 -0
  64. package/dist/components/progress-bar.js.map +1 -0
  65. package/dist/components/scroll-area.d.ts +53 -0
  66. package/dist/components/scroll-area.js +86 -0
  67. package/dist/components/scroll-area.js.map +1 -0
  68. package/dist/components/separator.d.ts +23 -0
  69. package/dist/components/separator.js +24 -0
  70. package/dist/components/separator.js.map +1 -0
  71. package/dist/components/series-chart-ticks.d.ts +34 -1
  72. package/dist/components/series-chart-ticks.js +83 -1
  73. package/dist/components/series-chart-ticks.js.map +1 -1
  74. package/dist/components/series-chart.d.ts +314 -19
  75. package/dist/components/series-chart.js +510 -120
  76. package/dist/components/series-chart.js.map +1 -1
  77. package/dist/components/skeleton.d.ts +36 -0
  78. package/dist/components/skeleton.js +35 -0
  79. package/dist/components/skeleton.js.map +1 -0
  80. package/dist/components/stat-tile.d.ts +60 -6
  81. package/dist/components/stat-tile.js +60 -26
  82. package/dist/components/stat-tile.js.map +1 -1
  83. package/dist/components/table.d.ts +83 -0
  84. package/dist/components/table.js +166 -0
  85. package/dist/components/table.js.map +1 -0
  86. package/dist/components/toggle-legend.d.ts +3 -2
  87. package/dist/components/toggle-legend.js +1 -1
  88. package/dist/components/toggle-legend.js.map +1 -1
  89. package/dist/components/tree-view.d.ts +129 -0
  90. package/dist/components/tree-view.js +376 -0
  91. package/dist/components/tree-view.js.map +1 -0
  92. package/dist/components/treemap.d.ts +9 -2
  93. package/dist/components/treemap.js +6 -2
  94. package/dist/components/treemap.js.map +1 -1
  95. package/dist/components/ui.d.ts +94 -13
  96. package/dist/components/ui.js +111 -29
  97. package/dist/components/ui.js.map +1 -1
  98. package/dist/components/use-table-state.d.ts +1 -1
  99. package/dist/{data-table-filters-noy0Abvi.d.ts → data-table-filters-CF1PXqjQ.d.ts} +73 -3
  100. package/dist/data-table.d.ts +2 -2
  101. package/dist/data-table.js.map +1 -1
  102. package/dist/hooks/use-copy-to-clipboard.d.ts +28 -0
  103. package/dist/hooks/use-copy-to-clipboard.js +81 -0
  104. package/dist/hooks/use-copy-to-clipboard.js.map +1 -0
  105. package/dist/hooks/use-debounce.d.ts +43 -0
  106. package/dist/hooks/use-debounce.js +56 -0
  107. package/dist/hooks/use-debounce.js.map +1 -0
  108. package/dist/hooks/use-file-drop.d.ts +72 -0
  109. package/dist/hooks/use-file-drop.js +57 -0
  110. package/dist/hooks/use-file-drop.js.map +1 -0
  111. package/dist/i18n/defaults.d.ts +3 -0
  112. package/dist/i18n/defaults.js +7 -1
  113. package/dist/i18n/defaults.js.map +1 -1
  114. package/dist/i18n/kit-labels.d.ts +15 -0
  115. package/dist/i18n/kit-labels.js +6 -2
  116. package/dist/i18n/kit-labels.js.map +1 -1
  117. package/dist/i18n/locales/de-CH-informal.d.ts +43 -0
  118. package/dist/i18n/locales/de-CH-informal.js +8 -0
  119. package/dist/i18n/locales/de-CH-informal.js.map +1 -0
  120. package/dist/i18n/locales/de-CH.d.ts +3 -0
  121. package/dist/i18n/locales/de-CH.js +1 -14
  122. package/dist/i18n/locales/de-CH.js.map +1 -1
  123. package/dist/i18n/locales/de-informal.d.ts +64 -0
  124. package/dist/i18n/locales/de-informal.js +37 -0
  125. package/dist/i18n/locales/de-informal.js.map +1 -0
  126. package/dist/i18n/locales/de.d.ts +3 -0
  127. package/dist/i18n/locales/de.js +25 -0
  128. package/dist/i18n/locales/de.js.map +1 -1
  129. package/dist/i18n/locales/es.d.ts +3 -0
  130. package/dist/i18n/locales/es.js +25 -0
  131. package/dist/i18n/locales/es.js.map +1 -1
  132. package/dist/i18n/locales/fr.d.ts +3 -0
  133. package/dist/i18n/locales/fr.js +25 -0
  134. package/dist/i18n/locales/fr.js.map +1 -1
  135. package/dist/i18n/locales/hu.d.ts +3 -0
  136. package/dist/i18n/locales/hu.js +25 -0
  137. package/dist/i18n/locales/hu.js.map +1 -1
  138. package/dist/i18n/locales/it.d.ts +3 -0
  139. package/dist/i18n/locales/it.js +25 -0
  140. package/dist/i18n/locales/it.js.map +1 -1
  141. package/dist/i18n/locales/zh.d.ts +3 -0
  142. package/dist/i18n/locales/zh.js +25 -0
  143. package/dist/i18n/locales/zh.js.map +1 -1
  144. package/dist/i18n/swiss.d.ts +13 -0
  145. package/dist/i18n/swiss.js +19 -0
  146. package/dist/i18n/swiss.js.map +1 -0
  147. package/dist/index.d.ts +25 -10
  148. package/dist/index.js +22 -0
  149. package/dist/index.js.map +1 -1
  150. package/dist/lib/dates.d.ts +37 -1
  151. package/dist/lib/dates.js +19 -0
  152. package/dist/lib/dates.js.map +1 -1
  153. package/dist/search/command-palette.d.ts +15 -1
  154. package/dist/search/command-palette.js +10 -2
  155. package/dist/search/command-palette.js.map +1 -1
  156. package/dist/wizard/stepper-nav.d.ts +35 -2
  157. package/dist/wizard/stepper-nav.js +54 -22
  158. package/dist/wizard/stepper-nav.js.map +1 -1
  159. package/dist/wizard/types.d.ts +107 -5
  160. package/dist/wizard/types.js +2 -1
  161. package/dist/wizard/types.js.map +1 -1
  162. package/dist/wizard/use-wizard.js +141 -21
  163. package/dist/wizard/use-wizard.js.map +1 -1
  164. package/dist/wizard/wizard-summary.d.ts +10 -2
  165. package/dist/wizard/wizard-summary.js +3 -1
  166. package/dist/wizard/wizard-summary.js.map +1 -1
  167. package/dist/wizard.d.ts +4 -1
  168. package/dist/wizard.js.map +1 -1
  169. package/package.json +1 -1
  170. package/src/components/alert-banner.tsx +211 -20
  171. package/src/components/button-group.tsx +75 -0
  172. package/src/components/chart-zoom.tsx +158 -20
  173. package/src/components/chip.tsx +160 -9
  174. package/src/components/confirm-dialog.tsx +242 -0
  175. package/src/components/copy-button.tsx +158 -0
  176. package/src/components/data-table-pagination.tsx +19 -6
  177. package/src/components/data-table-sort.ts +49 -10
  178. package/src/components/data-table.tsx +293 -24
  179. package/src/components/date-picker.tsx +262 -57
  180. package/src/components/description-list.tsx +174 -0
  181. package/src/components/disclosure.tsx +58 -5
  182. package/src/components/file-button.tsx +22 -2
  183. package/src/components/file-dropzone.tsx +198 -100
  184. package/src/components/floating-panel.tsx +341 -0
  185. package/src/components/full-bleed-dialog.tsx +53 -5
  186. package/src/components/modal.tsx +9 -2
  187. package/src/components/progress-bar.tsx +163 -0
  188. package/src/components/scroll-area.tsx +129 -0
  189. package/src/components/separator.tsx +37 -0
  190. package/src/components/series-chart-ticks.ts +135 -0
  191. package/src/components/series-chart.tsx +934 -67
  192. package/src/components/skeleton.tsx +65 -0
  193. package/src/components/stat-tile.tsx +160 -27
  194. package/src/components/table.tsx +263 -0
  195. package/src/components/toggle-legend.tsx +4 -3
  196. package/src/components/tree-view.tsx +589 -0
  197. package/src/components/treemap.tsx +13 -2
  198. package/src/components/ui.tsx +270 -39
  199. package/src/data-table.ts +2 -0
  200. package/src/hooks/use-copy-to-clipboard.ts +122 -0
  201. package/src/hooks/use-debounce.ts +105 -0
  202. package/src/hooks/use-file-drop.ts +123 -0
  203. package/src/i18n/defaults.ts +6 -0
  204. package/src/i18n/kit-labels.tsx +19 -0
  205. package/src/i18n/locales/de-CH-informal.ts +10 -0
  206. package/src/i18n/locales/de-CH.ts +1 -16
  207. package/src/i18n/locales/de-informal.ts +61 -0
  208. package/src/i18n/locales/de.ts +25 -0
  209. package/src/i18n/locales/es.ts +25 -0
  210. package/src/i18n/locales/fr.ts +25 -0
  211. package/src/i18n/locales/hu.ts +25 -0
  212. package/src/i18n/locales/it.ts +25 -0
  213. package/src/i18n/locales/zh.ts +25 -0
  214. package/src/i18n/swiss.ts +25 -0
  215. package/src/index.ts +29 -0
  216. package/src/lib/dates.ts +48 -0
  217. package/src/search/command-palette.tsx +29 -3
  218. package/src/wizard/stepper-nav.tsx +102 -28
  219. package/src/wizard/types.ts +106 -4
  220. package/src/wizard/use-wizard.ts +211 -31
  221. package/src/wizard/wizard-summary.tsx +24 -12
  222. package/src/wizard.ts +3 -2
@@ -1,17 +1,54 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useMemo } from "react";
4
- import { CartesianGrid, Label, Line, LineChart, ReferenceLine, XAxis, YAxis } from "recharts";
4
+ import {
5
+ Area,
6
+ Bar,
7
+ CartesianGrid,
8
+ ComposedChart,
9
+ Label,
10
+ Line,
11
+ ReferenceDot,
12
+ ReferenceLine,
13
+ XAxis,
14
+ YAxis
15
+ } from "recharts";
5
16
  import { ChartContainer, ChartTooltip, ChartTooltipContent } from "./chart.js";
6
- import { DEFAULT_Y_AXIS, withChartZoom } from "./chart-zoom.js";
17
+ import {
18
+ DEFAULT_Y_AXIS,
19
+ axisExtent,
20
+ withChartZoom
21
+ } from "./chart-zoom.js";
7
22
  import { STEP_DASH, strokeDash } from "./toggle-legend.js";
8
23
  import { DEFAULT_SERIES_CHART_LABELS } from "./series-chart-labels.js";
9
- import { niceTicks } from "./series-chart-ticks.js";
24
+ import { categoryTicks, integerTicks, niceTicks, timeTicksWithUnit } from "./series-chart-ticks.js";
10
25
  import { paletteFor } from "../theme/chart-palette.js";
11
26
  import { useKitLabels, useKitLocale } from "../i18n/kit-labels.js";
12
27
  import { cn } from "../lib/cn.js";
28
+ const TONE_COLOR = {
29
+ muted: "var(--text-muted)",
30
+ brand: "var(--brand)",
31
+ income: "var(--money-income)",
32
+ expense: "var(--money-expense)",
33
+ net: "var(--money-net)",
34
+ success: "var(--success)",
35
+ warning: "var(--warning)",
36
+ danger: "var(--danger)",
37
+ info: "var(--info)"
38
+ };
13
39
  const AXIS_TITLE_STRIP = 16;
14
40
  const AXIS_TITLE_OFFSET = 10;
41
+ const MAX_TILTED_TICK_BAND = 120;
42
+ const TICK_CHAR_WIDTH = 7;
43
+ const TICK_LINE_HEIGHT = 12;
44
+ const TICK_GAP = 8;
45
+ function tiltedTickBand(labels, angle) {
46
+ if (!angle) return void 0;
47
+ const rad = Math.abs(angle) * Math.PI / 180;
48
+ const longest = Math.max(0, ...labels.map((label) => label.length)) * TICK_CHAR_WIDTH;
49
+ const band = Math.sin(rad) * longest + Math.cos(rad) * TICK_LINE_HEIGHT + TICK_GAP;
50
+ return Math.min(MAX_TILTED_TICK_BAND, Math.max(30, Math.ceil(band)));
51
+ }
15
52
  const AXIS_TICK_WIDTH = 48;
16
53
  const AUTO_PAD = 0.04;
17
54
  function paddedDomain(values) {
@@ -54,6 +91,29 @@ function seriesKey(...parts) {
54
91
  const key = parts.map((part) => String(part).replace(/[^A-Za-z0-9_-]/g, "_")).join("_");
55
92
  return /^[A-Za-z_]/.test(key) ? key : `_${key}`;
56
93
  }
94
+ function anchoredBand(extent) {
95
+ if (!extent) return void 0;
96
+ const low = Math.min(0, extent[0]);
97
+ const high = Math.max(0, extent[1]);
98
+ const pad = (high - low) * AUTO_PAD || 1;
99
+ return [low < 0 ? low - pad : 0, high > 0 ? high + pad : 0];
100
+ }
101
+ function visibleSeries(series, hidden) {
102
+ return series.map((entry, index) => ({ ...entry, color: entry.color ?? paletteFor(index) })).filter((entry) => !hidden.has(entry.key));
103
+ }
104
+ function seriesLegendEntries(series) {
105
+ return series.map((entry, index) => {
106
+ const own = entry.dash ?? (entry.dashed ? 1 : 0);
107
+ const dash = entry.step ? STEP_DASH : own;
108
+ const line = (entry.type ?? "line") === "line";
109
+ return {
110
+ key: entry.key,
111
+ label: entry.label,
112
+ color: entry.color ?? paletteFor(index),
113
+ ...line && dash !== 0 ? { marker: "stroke", dash } : {}
114
+ };
115
+ });
116
+ }
57
117
  function useDefaultFormat(localeProp) {
58
118
  const locale = useKitLocale(localeProp);
59
119
  return useMemo(() => {
@@ -62,9 +122,83 @@ function useDefaultFormat(localeProp) {
62
122
  }, [locale]);
63
123
  }
64
124
  const ONE_UNTITLED_AXIS = [{ id: DEFAULT_Y_AXIS, title: "" }];
65
- function StaticSeriesChart({
66
- rows,
67
- x = {},
125
+ const PLOTTED_X = "__seriesChartX";
126
+ const finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
127
+ function toTime(value) {
128
+ if (value instanceof Date) return finite(value.getTime());
129
+ if (typeof value === "number") return finite(value);
130
+ if (typeof value !== "string") return void 0;
131
+ const day = /^(\d{4})-(\d{2})(?:-(\d{2}))?$/.exec(value);
132
+ if (day) return new Date(Number(day[1]), Number(day[2]) - 1, Number(day[3] ?? 1)).getTime();
133
+ return finite(Date.parse(value));
134
+ }
135
+ const categoryId = (value) => value instanceof Date ? `d:${value.getTime()}` : `${typeof value}:${String(value)}`;
136
+ function xModel(rows, x) {
137
+ const sourceKey = x.key ?? "x";
138
+ if (x.type === "category") {
139
+ const slots = /* @__PURE__ */ new Map();
140
+ rows.forEach((row, index) => {
141
+ const id = categoryId(row[sourceKey]);
142
+ if (!slots.has(id)) slots.set(id, index);
143
+ });
144
+ return {
145
+ kind: "category",
146
+ sourceKey,
147
+ plotKey: PLOTTED_X,
148
+ rows: rows.map((row, index) => ({ ...row, [PLOTTED_X]: index })),
149
+ source: rows,
150
+ position: (value) => slots.get(categoryId(value)),
151
+ valueAt: (position) => rows[Math.round(position)]?.[sourceKey]
152
+ };
153
+ }
154
+ if (x.type === "time") {
155
+ return {
156
+ kind: "time",
157
+ sourceKey,
158
+ plotKey: PLOTTED_X,
159
+ rows: rows.map((row) => ({ ...row, [PLOTTED_X]: toTime(row[sourceKey]) })),
160
+ source: rows,
161
+ position: toTime,
162
+ valueAt: (position) => new Date(position)
163
+ };
164
+ }
165
+ return {
166
+ kind: "number",
167
+ sourceKey,
168
+ plotKey: sourceKey,
169
+ rows,
170
+ source: rows,
171
+ position: finite,
172
+ valueAt: (position) => position
173
+ };
174
+ }
175
+ function resolveTicks(values, domain, position) {
176
+ if (!values || !domain) return void 0;
177
+ const list = typeof values === "function" ? values(domain) : values;
178
+ if (!list) return void 0;
179
+ const slack = (domain[1] - domain[0]) * 1e-9;
180
+ return list.map(position).filter((at) => at !== void 0 && at >= domain[0] - slack && at <= domain[1] + slack);
181
+ }
182
+ function extend(span, values) {
183
+ let out = span ? [span[0], span[1]] : void 0;
184
+ for (const value of values) {
185
+ if (value === void 0 || !Number.isFinite(value)) continue;
186
+ out = out ? [Math.min(out[0], value), Math.max(out[1], value)] : [value, value];
187
+ }
188
+ return out;
189
+ }
190
+ const TIME_TICK_FORMAT = {
191
+ hour: { hour: "2-digit", minute: "2-digit" },
192
+ day: { day: "numeric", month: "short" },
193
+ month: { month: "short", year: "2-digit" },
194
+ year: { year: "numeric" }
195
+ };
196
+ const LINE_WIDTH = 2;
197
+ const STEP_WIDTH = 1.5;
198
+ const BAR_RADIUS = [3, 3, 0, 0];
199
+ function SeriesPlot({
200
+ model,
201
+ xAxis,
68
202
  series,
69
203
  axes = ONE_UNTITLED_AXIS,
70
204
  valueFormat,
@@ -73,18 +207,34 @@ function StaticSeriesChart({
73
207
  empty,
74
208
  animationMs = 0,
75
209
  spans,
210
+ references,
211
+ markers,
212
+ onPointClick,
213
+ tooltip,
76
214
  zoom,
77
215
  labels: labelsProp,
78
- locale,
216
+ locale: localeProp,
79
217
  className
80
218
  }) {
81
219
  const labels = useKitLabels("seriesChart", DEFAULT_SERIES_CHART_LABELS, labelsProp);
82
- const number = useDefaultFormat(locale);
83
- if (!rows.length || !series.length) {
84
- return /* @__PURE__ */ jsx("div", { className: cn("flex w-full items-center justify-center px-2 text-center", height), children: empty !== void 0 ? empty : /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: labels.empty }) });
220
+ const locale = useKitLocale(localeProp);
221
+ const number = useDefaultFormat(localeProp);
222
+ const heightClass = typeof height === "string" ? height : void 0;
223
+ const heightStyle = typeof height === "number" ? { height } : void 0;
224
+ if (!model.source.length || !series.length) {
225
+ return /* @__PURE__ */ jsx(
226
+ "div",
227
+ {
228
+ className: cn("flex w-full items-center justify-center px-2 text-center", heightClass),
229
+ style: heightStyle,
230
+ children: empty !== void 0 ? empty : /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: labels.empty })
231
+ }
232
+ );
85
233
  }
86
- const xKey = x.key ?? "x";
87
- const xFormat = x.format ?? number;
234
+ const x = xAxis;
235
+ const xKey = model.plotKey;
236
+ const plotted = model.rows;
237
+ const rows = model.source;
88
238
  const config = Object.fromEntries(
89
239
  series.map((entry, index) => [
90
240
  entry.key,
@@ -96,134 +246,374 @@ function StaticSeriesChart({
96
246
  const onLeft = visible.some((axis) => (axis.orientation ?? "left") === "left");
97
247
  const onRight = visible.some((axis) => axis.orientation === "right");
98
248
  const margin = {
99
- top: 8,
249
+ // Room for a marker's label over a point at the top of the band.
250
+ top: markers?.some((marker) => marker.label) ? 22 : 8,
100
251
  right: onRight ? 0 : 10,
101
252
  left: onLeft ? 0 : 10,
102
253
  // Under the ticks, not under the whole axis: whatever comes next owns the gap.
103
254
  bottom: x.title ? 18 : 2
104
255
  };
105
- const fittedX = zoom?.xDomain ?? paddedDomain(rows.map((row) => row[xKey]));
106
- const fittedY = (axis) => zoom?.yDomains[axis.id] ?? axis.domain ?? paddedDomain(
107
- series.filter((entry) => (entry.axis ?? DEFAULT_Y_AXIS) === axis.id).flatMap((entry) => rows.map((row) => row[entry.key]))
256
+ const refs = references ?? [];
257
+ const xRefs = refs.filter((ref) => ref.axis === "x");
258
+ const yRefs = refs.filter((ref) => ref.axis !== "x");
259
+ const xPos = (value) => model.position(value);
260
+ const fittedX = zoom?.xDomain ?? (model.kind === "category" ? [-0.5, rows.length - 0.5] : padBand(
261
+ ...extend(
262
+ void 0,
263
+ [
264
+ ...plotted.map((row) => finite(row[xKey])),
265
+ ...xRefs.map((ref) => xPos(ref.value)),
266
+ ...(markers ?? []).map((marker) => xPos(marker.x))
267
+ ]
268
+ ) ?? [Infinity, -Infinity]
269
+ ));
270
+ const timeUnit = model.kind === "time" ? timeTicksWithUnit(fittedX) : void 0;
271
+ const integerX = model.kind === "number" && (x.integerTicks ?? plotted.every((row) => {
272
+ const value = finite(row[xKey]);
273
+ return value === void 0 || Number.isInteger(value);
274
+ }));
275
+ const xTicks = resolveTicks(x.tickValues, fittedX, xPos) ?? (model.kind === "category" ? categoryTicks(fittedX, rows.length) : model.kind === "time" ? timeUnit?.ticks : integerX ? integerTicks(fittedX) ?? niceTicks(fittedX) : niceTicks(fittedX));
276
+ const timeFormat = new Intl.DateTimeFormat(locale, TIME_TICK_FORMAT[timeUnit?.unit ?? "day"]);
277
+ const dateFormat = new Intl.DateTimeFormat(locale, {
278
+ dateStyle: "medium",
279
+ ...timeUnit?.unit === "hour" ? { timeStyle: "short" } : {}
280
+ });
281
+ const categoryText = (value) => value instanceof Date ? dateFormat.format(value) : typeof value === "number" ? number(value) : String(value ?? "");
282
+ const tickAt = (position) => ({
283
+ value: model.valueAt(position),
284
+ ...model.kind === "category" ? { index: Math.round(position) } : {},
285
+ ...timeUnit ? { unit: timeUnit.unit } : {}
286
+ });
287
+ const defaultFormat = (position, tick) => model.kind === "category" ? categoryText(tick.value) : model.kind === "time" ? timeFormat.format(position) : number(position);
288
+ const format = x.format ?? defaultFormat;
289
+ const xFormat = (position) => format(position, tickAt(position));
290
+ const label = x.label ?? (model.kind === "time" ? (position) => dateFormat.format(position) : format);
291
+ const xLabel = (position) => label(position, tickAt(position));
292
+ const tickAngle = x.ticks === false ? 0 : x.tickAngle ?? 0;
293
+ const tickBand = tiltedTickBand((xTicks ?? []).map((tick) => xFormat(tick)), tickAngle);
294
+ const source = {
295
+ rows: plotted,
296
+ series,
297
+ axes,
298
+ xKey
299
+ };
300
+ const anchored = (axis) => axis.includeZero ?? series.some(
301
+ (entry) => (entry.axis ?? DEFAULT_Y_AXIS) === axis.id && (entry.type ?? "line") !== "line"
108
302
  );
109
- return /* @__PURE__ */ jsx(ChartContainer, { config, className: cn("w-full", height, className), children: /* @__PURE__ */ jsxs(LineChart, { data: rows, margin, children: [
110
- /* @__PURE__ */ jsx(CartesianGrid, { yAxisId: gridAxis }),
111
- /* @__PURE__ */ jsx(
112
- XAxis,
113
- {
114
- dataKey: xKey,
115
- type: "number",
116
- domain: fittedX ?? ["dataMin", "dataMax"],
117
- ticks: niceTicks(fittedX),
118
- allowDataOverflow: zoom?.xDomain !== void 0,
119
- tickLine: false,
120
- axisLine: false,
121
- minTickGap: 32,
122
- tick: x.ticks === false ? false : void 0,
123
- height: x.ticks === false && !x.title ? 4 : void 0,
124
- tickFormatter: xFormat,
125
- children: x.title && /* @__PURE__ */ jsx(
126
- Label,
303
+ const onAxis = (axisId) => (ref) => (ref.axis ?? DEFAULT_Y_AXIS) === axisId;
304
+ const fittedY = (axis) => {
305
+ const own = [
306
+ ...yRefs.filter(onAxis(axis.id)).map((ref) => finite(ref.value)),
307
+ ...(markers ?? []).filter(onAxis(axis.id)).map((marker) => finite(marker.y))
308
+ ];
309
+ if (anchored(axis)) {
310
+ if (zoom?.xDomain) return anchoredBand(axisExtent(source, axis.id, zoom.xDomain)) ?? axis.domain;
311
+ return axis.domain ?? anchoredBand(extend(axisExtent(source, axis.id), own));
312
+ }
313
+ return zoom?.yDomains[axis.id] ?? axis.domain ?? padBand(...extend(axisExtent(source, axis.id), own) ?? [Infinity, -Infinity]);
314
+ };
315
+ const xOf = (index) => model.kind === "category" ? rows[index][model.sourceKey] : model.valueAt(finite(plotted[index][xKey]) ?? NaN);
316
+ const hitAt = (index, key) => {
317
+ const row = rows[index];
318
+ return row ? { index, row, x: xOf(index), key } : void 0;
319
+ };
320
+ const dotFor = (entry) => {
321
+ const want = entry.dot;
322
+ if (!want) return false;
323
+ const color = `var(--color-${entry.key})`;
324
+ return (props) => {
325
+ const { cx, cy, index } = props;
326
+ const value = finite(rows[index]?.[entry.key]);
327
+ if (value === void 0 || !Number.isFinite(cx) || !Number.isFinite(cy)) return /* @__PURE__ */ jsx("g", {});
328
+ const point = {
329
+ key: entry.key,
330
+ index,
331
+ row: rows[index],
332
+ x: xOf(index),
333
+ value,
334
+ cx,
335
+ cy,
336
+ color
337
+ };
338
+ const drawn = want === true ? true : want(point);
339
+ if (drawn === true) {
340
+ return /* @__PURE__ */ jsx(
341
+ "circle",
127
342
  {
128
- value: x.title,
129
- position: "insideBottom",
130
- offset: -12,
131
- className: "fill-[var(--text-muted)] text-[11px]"
343
+ className: "recharts-dot",
344
+ cx,
345
+ cy,
346
+ r: 3,
347
+ fill: color,
348
+ stroke: "var(--bg-surface)",
349
+ strokeWidth: 1.5
132
350
  }
133
- )
351
+ );
134
352
  }
135
- ),
136
- axes.map((axis) => {
137
- const domain = fittedY(axis);
138
- return /* @__PURE__ */ jsx(
139
- YAxis,
353
+ if (drawn === false || drawn == null) return /* @__PURE__ */ jsx("g", {});
354
+ return /* @__PURE__ */ jsx("g", { children: drawn });
355
+ };
356
+ };
357
+ const animation = {
358
+ isAnimationActive: animationMs > 0,
359
+ animationDuration: animationMs,
360
+ animationEasing: "ease-out"
361
+ };
362
+ return /* @__PURE__ */ jsx(
363
+ ChartContainer,
364
+ {
365
+ config,
366
+ className: cn("w-full", heightClass, className, onPointClick && "cursor-pointer"),
367
+ style: heightStyle,
368
+ children: /* @__PURE__ */ jsxs(
369
+ ComposedChart,
140
370
  {
141
- yAxisId: axis.id,
142
- hide: axis.hide,
143
- domain,
144
- ticks: niceTicks(domain),
145
- allowDataOverflow: zoom?.yDomains[axis.id] !== void 0,
146
- orientation: axis.orientation ?? "left",
147
- width: axisBandWidth(axis.width, Boolean(axis.title) && !axis.hide),
148
- tickLine: false,
149
- axisLine: false,
150
- tickFormatter: axis.format ?? number,
151
- tick: axis.color ? { fill: axis.color } : void 0,
152
- children: axis.title && !axis.hide && /* @__PURE__ */ jsx(
153
- Label,
154
- {
155
- value: axis.title,
156
- angle: axis.orientation === "right" ? 90 : -90,
157
- position: axis.orientation === "right" ? "insideRight" : "insideLeft",
158
- style: { textAnchor: "middle", fill: soleSeriesColor(axis, series) },
159
- offset: AXIS_TITLE_OFFSET,
160
- className: "fill-[var(--text-muted)] text-[11px]"
161
- }
162
- )
163
- },
164
- axis.id
165
- );
166
- }),
167
- /* @__PURE__ */ jsx(
168
- ChartTooltip,
169
- {
170
- content: /* @__PURE__ */ jsx(
171
- ChartTooltipContent,
172
- {
173
- labelFormatter: (value) => (x.label ?? xFormat)(Number(value)),
174
- valueFormatter: valueFormat ?? number
175
- }
176
- )
177
- }
178
- ),
179
- series.map((entry) => /* @__PURE__ */ jsx(
180
- Line,
181
- {
182
- yAxisId: entry.axis ?? DEFAULT_Y_AXIS,
183
- type: entry.step ? "stepAfter" : "monotone",
184
- dataKey: entry.key,
185
- stroke: `var(--color-${entry.key})`,
186
- strokeWidth: entry.step ? 1.5 : 2,
187
- strokeDasharray: strokeDash(
188
- entry.step ? STEP_DASH : entry.dash ?? (entry.dashed ? 1 : 0)
189
- ),
190
- dot: false,
191
- connectNulls,
192
- isAnimationActive: animationMs > 0,
193
- animationDuration: animationMs,
194
- animationEasing: "ease-out"
195
- },
196
- entry.key
197
- )),
198
- spans?.map((span) => /* @__PURE__ */ jsx(
199
- ReferenceLine,
200
- {
201
- yAxisId: span.axis ?? DEFAULT_Y_AXIS,
202
- segment: [
203
- { x: span.x, y: span.from },
204
- { x: span.x, y: span.to }
205
- ],
206
- stroke: span.color ?? "var(--text-muted)",
207
- strokeWidth: 2,
208
- ifOverflow: "discard"
209
- },
210
- span.key
211
- )),
212
- zoom?.layer
213
- ] }) });
214
- }
215
- const SeriesChart = withChartZoom(StaticSeriesChart);
371
+ data: plotted,
372
+ margin,
373
+ stackOffset: "sign",
374
+ onClick: onPointClick ? (state) => {
375
+ const index = Number(state?.activeTooltipIndex ?? state?.activeIndex);
376
+ const hit = Number.isInteger(index) ? hitAt(index) : void 0;
377
+ if (hit) onPointClick(hit);
378
+ } : void 0,
379
+ children: [
380
+ /* @__PURE__ */ jsx(CartesianGrid, { yAxisId: gridAxis }),
381
+ /* @__PURE__ */ jsx(
382
+ XAxis,
383
+ {
384
+ dataKey: xKey,
385
+ type: "number",
386
+ domain: fittedX ?? ["dataMin", "dataMax"],
387
+ ticks: xTicks,
388
+ allowDataOverflow: zoom?.xDomain !== void 0 || model.kind === "category",
389
+ tickLine: false,
390
+ axisLine: false,
391
+ minTickGap: 32,
392
+ tick: x.ticks === false ? false : void 0,
393
+ ...tickAngle ? { angle: tickAngle, textAnchor: tickAngle < 0 ? "end" : "start" } : {},
394
+ height: x.ticks === false && !x.title ? 4 : tickBand,
395
+ tickFormatter: (value) => xFormat(Number(value)),
396
+ children: x.title && /* @__PURE__ */ jsx(
397
+ Label,
398
+ {
399
+ value: x.title,
400
+ position: "insideBottom",
401
+ offset: -12,
402
+ className: "fill-[var(--text-muted)] text-[11px]"
403
+ }
404
+ )
405
+ }
406
+ ),
407
+ axes.map((axis) => {
408
+ const domain = fittedY(axis);
409
+ const zoomed = anchored(axis) ? zoom?.xDomain !== void 0 : zoom?.yDomains[axis.id] !== void 0;
410
+ return /* @__PURE__ */ jsx(
411
+ YAxis,
412
+ {
413
+ yAxisId: axis.id,
414
+ hide: axis.hide,
415
+ domain,
416
+ ticks: resolveTicks(axis.tickValues, domain, finite) ?? niceTicks(domain),
417
+ allowDataOverflow: zoomed,
418
+ orientation: axis.orientation ?? "left",
419
+ width: axisBandWidth(axis.width, Boolean(axis.title) && !axis.hide),
420
+ tickLine: false,
421
+ axisLine: false,
422
+ tickFormatter: axis.format ?? number,
423
+ tick: axis.color ? { fill: axis.color } : void 0,
424
+ children: axis.title && !axis.hide && /* @__PURE__ */ jsx(
425
+ Label,
426
+ {
427
+ value: axis.title,
428
+ angle: axis.orientation === "right" ? 90 : -90,
429
+ position: axis.orientation === "right" ? "insideRight" : "insideLeft",
430
+ style: { textAnchor: "middle", fill: soleSeriesColor(axis, series) },
431
+ offset: AXIS_TITLE_OFFSET,
432
+ className: "fill-[var(--text-muted)] text-[11px]"
433
+ }
434
+ )
435
+ },
436
+ axis.id
437
+ );
438
+ }),
439
+ /* @__PURE__ */ jsx(
440
+ ChartTooltip,
441
+ {
442
+ ...tooltip?.allowEscapeViewBox || tooltip?.boundary ? { allowEscapeViewBox: tooltip.allowEscapeViewBox ?? { x: true } } : {},
443
+ ...tooltip?.boundary ? { offset: 0 } : {},
444
+ content: /* @__PURE__ */ jsx(
445
+ ChartTooltipContent,
446
+ {
447
+ labelFormatter: (value) => xLabel(Number(value)),
448
+ valueFormatter: valueFormat ?? number,
449
+ boundaryRef: tooltip?.boundary
450
+ }
451
+ )
452
+ }
453
+ ),
454
+ series.map((entry) => {
455
+ const type = entry.type ?? "line";
456
+ const yAxisId = entry.axis ?? DEFAULT_Y_AXIS;
457
+ const color = `var(--color-${entry.key})`;
458
+ const own = entry.dash ?? (entry.dashed ? 1 : 0);
459
+ const dash = entry.step ? strokeDash(STEP_DASH) : typeof own === "string" ? own : strokeDash(own);
460
+ const curve = entry.step ? "stepAfter" : entry.curve ?? "monotone";
461
+ const width = entry.strokeWidth ?? (entry.step ? STEP_WIDTH : LINE_WIDTH);
462
+ if (type === "bar") {
463
+ return /* @__PURE__ */ jsx(
464
+ Bar,
465
+ {
466
+ yAxisId,
467
+ dataKey: entry.key,
468
+ stackId: entry.stack,
469
+ fill: color,
470
+ fillOpacity: entry.fillOpacity,
471
+ radius: entry.stack === void 0 ? BAR_RADIUS : 0,
472
+ onClick: onPointClick ? (_bar, index, event) => {
473
+ event.stopPropagation();
474
+ const hit = hitAt(index, entry.key);
475
+ if (hit) onPointClick(hit);
476
+ } : void 0,
477
+ ...animation
478
+ },
479
+ entry.key
480
+ );
481
+ }
482
+ if (type === "area") {
483
+ return /* @__PURE__ */ jsx(
484
+ Area,
485
+ {
486
+ yAxisId,
487
+ type: curve,
488
+ dataKey: entry.key,
489
+ stackId: entry.stack,
490
+ stroke: color,
491
+ strokeWidth: width,
492
+ strokeDasharray: dash,
493
+ fill: color,
494
+ fillOpacity: entry.fillOpacity ?? (entry.stack !== void 0 ? 0.55 : 0.2),
495
+ dot: dotFor(entry),
496
+ ...entry.activeDot !== void 0 ? { activeDot: entry.activeDot } : {},
497
+ connectNulls,
498
+ ...animation
499
+ },
500
+ entry.key
501
+ );
502
+ }
503
+ return /* @__PURE__ */ jsx(
504
+ Line,
505
+ {
506
+ yAxisId,
507
+ type: curve,
508
+ dataKey: entry.key,
509
+ stroke: color,
510
+ strokeWidth: width,
511
+ strokeDasharray: dash,
512
+ dot: dotFor(entry),
513
+ ...entry.activeDot !== void 0 ? { activeDot: entry.activeDot } : {},
514
+ connectNulls,
515
+ ...animation
516
+ },
517
+ entry.key
518
+ );
519
+ }),
520
+ spans?.map((span) => /* @__PURE__ */ jsx(
521
+ ReferenceLine,
522
+ {
523
+ yAxisId: span.axis ?? DEFAULT_Y_AXIS,
524
+ segment: [
525
+ { x: span.x, y: span.from },
526
+ { x: span.x, y: span.to }
527
+ ],
528
+ stroke: span.color ?? "var(--text-muted)",
529
+ strokeWidth: 2,
530
+ ifOverflow: "discard"
531
+ },
532
+ span.key
533
+ )),
534
+ refs.map((ref, index) => {
535
+ const vertical = ref.axis === "x";
536
+ const at = vertical ? xPos(ref.value) : finite(ref.value);
537
+ if (at === void 0) return null;
538
+ const color = ref.color ?? TONE_COLOR[ref.tone ?? "muted"];
539
+ return /* @__PURE__ */ jsx(
540
+ ReferenceLine,
541
+ {
542
+ yAxisId: vertical ? gridAxis : ref.axis ?? DEFAULT_Y_AXIS,
543
+ ...vertical ? { x: at } : { y: at },
544
+ stroke: color,
545
+ strokeWidth: 1,
546
+ strokeDasharray: strokeDash(ref.dash ?? 1),
547
+ ifOverflow: "discard",
548
+ label: ref.label ? {
549
+ value: ref.label,
550
+ position: "insideTopLeft",
551
+ fontSize: 10,
552
+ fill: color
553
+ } : void 0
554
+ },
555
+ ref.key ?? `reference-${index}`
556
+ );
557
+ }),
558
+ markers?.map((marker, index) => {
559
+ const at = xPos(marker.x);
560
+ if (at === void 0 || finite(marker.y) === void 0) return null;
561
+ const color = marker.color ?? TONE_COLOR[marker.tone ?? "brand"];
562
+ return /* @__PURE__ */ jsx(
563
+ ReferenceDot,
564
+ {
565
+ yAxisId: marker.axis ?? DEFAULT_Y_AXIS,
566
+ x: at,
567
+ y: marker.y,
568
+ r: marker.r ?? 4.5,
569
+ fill: color,
570
+ stroke: "var(--bg-surface)",
571
+ strokeWidth: 1.5,
572
+ ifOverflow: "discard",
573
+ label: marker.label ? {
574
+ value: marker.label,
575
+ position: "top",
576
+ fontSize: 11,
577
+ fontWeight: 600,
578
+ fill: "var(--text-primary)"
579
+ } : void 0
580
+ },
581
+ marker.key ?? `marker-${index}`
582
+ );
583
+ }),
584
+ zoom?.layer
585
+ ]
586
+ }
587
+ )
588
+ }
589
+ );
590
+ }
591
+ const ZoomablePlot = withChartZoom(SeriesPlot);
592
+ function plotProps(props) {
593
+ const x = props.x ?? {};
594
+ const model = xModel(props.rows, x);
595
+ return { ...props, rows: model.rows, x: { key: model.plotKey }, model, xAxis: x };
596
+ }
597
+ function StaticSeriesChart(props) {
598
+ return /* @__PURE__ */ jsx(SeriesPlot, { ...plotProps(props) });
599
+ }
600
+ function SeriesChart(props) {
601
+ return /* @__PURE__ */ jsx(ZoomablePlot, { ...plotProps(props) });
602
+ }
216
603
  export {
217
604
  AXIS_TICK_WIDTH,
218
605
  AXIS_TITLE_STRIP,
219
606
  SeriesChart,
220
607
  StaticSeriesChart,
608
+ anchoredBand,
221
609
  axisBandWidth,
222
610
  mergeSeries,
223
611
  oneAxis,
224
612
  padBand,
225
613
  paddedDomain,
226
614
  seriesKey,
227
- soleSeriesColor
615
+ seriesLegendEntries,
616
+ soleSeriesColor,
617
+ visibleSeries
228
618
  };
229
619
  //# sourceMappingURL=series-chart.js.map