panelui-native 0.22.6 → 0.28.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 (258) hide show
  1. package/README.md +1 -0
  2. package/lib/module/components/accordion/index.js +8 -8
  3. package/lib/module/components/accordion/index.js.map +1 -1
  4. package/lib/module/components/alert/index.js +2 -2
  5. package/lib/module/components/alert/index.js.map +1 -1
  6. package/lib/module/components/area-chart/index.js +958 -0
  7. package/lib/module/components/area-chart/index.js.map +1 -0
  8. package/lib/module/components/attachment/index.js +2 -1
  9. package/lib/module/components/attachment/index.js.map +1 -1
  10. package/lib/module/components/avatar/index.js +4 -4
  11. package/lib/module/components/avatar/index.js.map +1 -1
  12. package/lib/module/components/badge/index.js +4 -4
  13. package/lib/module/components/badge/index.js.map +1 -1
  14. package/lib/module/components/bar-chart/index.js +994 -0
  15. package/lib/module/components/bar-chart/index.js.map +1 -0
  16. package/lib/module/components/bottom-sheet/index.js +213 -24
  17. package/lib/module/components/bottom-sheet/index.js.map +1 -1
  18. package/lib/module/components/breadcrumb/index.js +275 -0
  19. package/lib/module/components/breadcrumb/index.js.map +1 -0
  20. package/lib/module/components/button/index.js +11 -6
  21. package/lib/module/components/button/index.js.map +1 -1
  22. package/lib/module/components/calendar/index.js +557 -0
  23. package/lib/module/components/calendar/index.js.map +1 -0
  24. package/lib/module/components/carousel/index.js +680 -0
  25. package/lib/module/components/carousel/index.js.map +1 -0
  26. package/lib/module/components/chip/index.js +6 -6
  27. package/lib/module/components/chip/index.js.map +1 -1
  28. package/lib/module/components/date-picker/index.js +158 -0
  29. package/lib/module/components/date-picker/index.js.map +1 -0
  30. package/lib/module/components/dialog/index.js +7 -2
  31. package/lib/module/components/dialog/index.js.map +1 -1
  32. package/lib/module/components/direction/index.js +15 -20
  33. package/lib/module/components/direction/index.js.map +1 -1
  34. package/lib/module/components/empty-state/index.js +2 -2
  35. package/lib/module/components/empty-state/index.js.map +1 -1
  36. package/lib/module/components/field/index.js +5 -5
  37. package/lib/module/components/field/index.js.map +1 -1
  38. package/lib/module/components/flow/index.js +3 -3
  39. package/lib/module/components/flow/index.js.map +1 -1
  40. package/lib/module/components/frame/index.js +22 -5
  41. package/lib/module/components/frame/index.js.map +1 -1
  42. package/lib/module/components/heatmap-chart/index.js +28 -8
  43. package/lib/module/components/heatmap-chart/index.js.map +1 -1
  44. package/lib/module/components/input-group/index.js +5 -4
  45. package/lib/module/components/input-group/index.js.map +1 -1
  46. package/lib/module/components/item/index.js +9 -9
  47. package/lib/module/components/item/index.js.map +1 -1
  48. package/lib/module/components/label/index.js +4 -4
  49. package/lib/module/components/label/index.js.map +1 -1
  50. package/lib/module/components/line-chart/index.js +165 -184
  51. package/lib/module/components/line-chart/index.js.map +1 -1
  52. package/lib/module/components/loader/index.js +758 -0
  53. package/lib/module/components/loader/index.js.map +1 -0
  54. package/lib/module/components/map/basemap.js +265 -0
  55. package/lib/module/components/map/basemap.js.map +1 -0
  56. package/lib/module/components/map/index.js +850 -0
  57. package/lib/module/components/map/index.js.map +1 -0
  58. package/lib/module/components/map/maplibre.js +87 -0
  59. package/lib/module/components/map/maplibre.js.map +1 -0
  60. package/lib/module/components/marker/index.js +3 -3
  61. package/lib/module/components/marker/index.js.map +1 -1
  62. package/lib/module/components/message/index.js +15 -14
  63. package/lib/module/components/message/index.js.map +1 -1
  64. package/lib/module/components/message-scroller/index.js +5 -4
  65. package/lib/module/components/message-scroller/index.js.map +1 -1
  66. package/lib/module/components/otp-input/index.js +2 -2
  67. package/lib/module/components/popover/index.js +39 -7
  68. package/lib/module/components/popover/index.js.map +1 -1
  69. package/lib/module/components/progress/index.js +8 -2
  70. package/lib/module/components/progress/index.js.map +1 -1
  71. package/lib/module/components/radio-group/index.js +2 -2
  72. package/lib/module/components/radio-group/index.js.map +1 -1
  73. package/lib/module/components/ring-chart/index.js +423 -0
  74. package/lib/module/components/ring-chart/index.js.map +1 -0
  75. package/lib/module/components/section-rail/index.js +6 -6
  76. package/lib/module/components/section-rail/index.js.map +1 -1
  77. package/lib/module/components/select/index.js +10 -4
  78. package/lib/module/components/select/index.js.map +1 -1
  79. package/lib/module/components/separator/index.js +76 -22
  80. package/lib/module/components/separator/index.js.map +1 -1
  81. package/lib/module/components/shimmer/index.js +10 -1
  82. package/lib/module/components/shimmer/index.js.map +1 -1
  83. package/lib/module/components/slider/index.js +19 -5
  84. package/lib/module/components/slider/index.js.map +1 -1
  85. package/lib/module/components/steps/index.js +5 -5
  86. package/lib/module/components/steps/index.js.map +1 -1
  87. package/lib/module/components/switch/index.js +6 -1
  88. package/lib/module/components/switch/index.js.map +1 -1
  89. package/lib/module/components/tabs/index.js +7 -7
  90. package/lib/module/components/tabs/index.js.map +1 -1
  91. package/lib/module/components/timeline/index.js +4 -4
  92. package/lib/module/components/timeline/index.js.map +1 -1
  93. package/lib/module/components/toast/index.js +2 -2
  94. package/lib/module/components/toast/index.js.map +1 -1
  95. package/lib/module/components/toggle-button/index.js +5 -5
  96. package/lib/module/components/toggle-button/index.js.map +1 -1
  97. package/lib/module/components/tooltip/index.js +492 -0
  98. package/lib/module/components/tooltip/index.js.map +1 -0
  99. package/lib/module/components/typography/index.js +7 -2
  100. package/lib/module/components/typography/index.js.map +1 -1
  101. package/lib/module/hooks/index.js +1 -0
  102. package/lib/module/hooks/index.js.map +1 -1
  103. package/lib/module/hooks/use-back-handler.js +37 -0
  104. package/lib/module/hooks/use-back-handler.js.map +1 -0
  105. package/lib/module/hooks/use-direction.js +43 -0
  106. package/lib/module/hooks/use-direction.js.map +1 -0
  107. package/lib/module/icons/index.js +180 -2
  108. package/lib/module/icons/index.js.map +1 -1
  109. package/lib/module/index.js +12 -2
  110. package/lib/module/index.js.map +1 -1
  111. package/lib/module/primitives/keyboard-avoider.js +2 -1
  112. package/lib/module/primitives/keyboard-avoider.js.map +1 -1
  113. package/lib/module/primitives/text.js +55 -11
  114. package/lib/module/primitives/text.js.map +1 -1
  115. package/lib/module/utils/chart.js +279 -0
  116. package/lib/module/utils/chart.js.map +1 -0
  117. package/lib/module/utils/date.js +386 -0
  118. package/lib/module/utils/date.js.map +1 -0
  119. package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
  120. package/lib/typescript/src/components/alert/index.d.ts.map +1 -1
  121. package/lib/typescript/src/components/area-chart/index.d.ts +237 -0
  122. package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -0
  123. package/lib/typescript/src/components/attachment/index.d.ts.map +1 -1
  124. package/lib/typescript/src/components/bar-chart/index.d.ts +270 -0
  125. package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -0
  126. package/lib/typescript/src/components/bottom-sheet/index.d.ts +75 -4
  127. package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
  128. package/lib/typescript/src/components/breadcrumb/index.d.ts +100 -0
  129. package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -0
  130. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  131. package/lib/typescript/src/components/calendar/index.d.ts +108 -0
  132. package/lib/typescript/src/components/calendar/index.d.ts.map +1 -0
  133. package/lib/typescript/src/components/carousel/index.d.ts +179 -0
  134. package/lib/typescript/src/components/carousel/index.d.ts.map +1 -0
  135. package/lib/typescript/src/components/chip/index.d.ts.map +1 -1
  136. package/lib/typescript/src/components/date-picker/index.d.ts +91 -0
  137. package/lib/typescript/src/components/date-picker/index.d.ts.map +1 -0
  138. package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
  139. package/lib/typescript/src/components/direction/index.d.ts +11 -10
  140. package/lib/typescript/src/components/direction/index.d.ts.map +1 -1
  141. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -1
  142. package/lib/typescript/src/components/field/index.d.ts.map +1 -1
  143. package/lib/typescript/src/components/frame/index.d.ts +17 -0
  144. package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
  145. package/lib/typescript/src/components/heatmap-chart/index.d.ts +22 -3
  146. package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
  147. package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
  148. package/lib/typescript/src/components/item/index.d.ts.map +1 -1
  149. package/lib/typescript/src/components/label/index.d.ts.map +1 -1
  150. package/lib/typescript/src/components/line-chart/index.d.ts +42 -2
  151. package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
  152. package/lib/typescript/src/components/loader/index.d.ts +38 -0
  153. package/lib/typescript/src/components/loader/index.d.ts.map +1 -0
  154. package/lib/typescript/src/components/map/basemap.d.ts +76 -0
  155. package/lib/typescript/src/components/map/basemap.d.ts.map +1 -0
  156. package/lib/typescript/src/components/map/index.d.ts +367 -0
  157. package/lib/typescript/src/components/map/index.d.ts.map +1 -0
  158. package/lib/typescript/src/components/map/maplibre.d.ts +193 -0
  159. package/lib/typescript/src/components/map/maplibre.d.ts.map +1 -0
  160. package/lib/typescript/src/components/marker/index.d.ts.map +1 -1
  161. package/lib/typescript/src/components/message/index.d.ts.map +1 -1
  162. package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -1
  163. package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
  164. package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
  165. package/lib/typescript/src/components/ring-chart/index.d.ts +156 -0
  166. package/lib/typescript/src/components/ring-chart/index.d.ts.map +1 -0
  167. package/lib/typescript/src/components/select/index.d.ts.map +1 -1
  168. package/lib/typescript/src/components/separator/index.d.ts +36 -1
  169. package/lib/typescript/src/components/separator/index.d.ts.map +1 -1
  170. package/lib/typescript/src/components/shimmer/index.d.ts.map +1 -1
  171. package/lib/typescript/src/components/slider/index.d.ts.map +1 -1
  172. package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
  173. package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
  174. package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
  175. package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
  176. package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
  177. package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -1
  178. package/lib/typescript/src/components/tooltip/index.d.ts +125 -0
  179. package/lib/typescript/src/components/tooltip/index.d.ts.map +1 -0
  180. package/lib/typescript/src/components/typography/index.d.ts +6 -0
  181. package/lib/typescript/src/components/typography/index.d.ts.map +1 -1
  182. package/lib/typescript/src/hooks/index.d.ts +1 -0
  183. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  184. package/lib/typescript/src/hooks/use-back-handler.d.ts +19 -0
  185. package/lib/typescript/src/hooks/use-back-handler.d.ts.map +1 -0
  186. package/lib/typescript/src/hooks/use-direction.d.ts +20 -0
  187. package/lib/typescript/src/hooks/use-direction.d.ts.map +1 -0
  188. package/lib/typescript/src/icons/index.d.ts +8 -0
  189. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  190. package/lib/typescript/src/index.d.ts +14 -4
  191. package/lib/typescript/src/index.d.ts.map +1 -1
  192. package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
  193. package/lib/typescript/src/primitives/text.d.ts +20 -0
  194. package/lib/typescript/src/primitives/text.d.ts.map +1 -1
  195. package/lib/typescript/src/utils/chart.d.ts +96 -0
  196. package/lib/typescript/src/utils/chart.d.ts.map +1 -0
  197. package/lib/typescript/src/utils/date.d.ts +116 -0
  198. package/lib/typescript/src/utils/date.d.ts.map +1 -0
  199. package/package.json +9 -1
  200. package/src/components/accordion/index.tsx +8 -12
  201. package/src/components/alert/index.tsx +2 -2
  202. package/src/components/area-chart/index.tsx +1147 -0
  203. package/src/components/attachment/index.tsx +2 -2
  204. package/src/components/avatar/index.tsx +4 -4
  205. package/src/components/badge/index.tsx +6 -6
  206. package/src/components/bar-chart/index.tsx +1208 -0
  207. package/src/components/bottom-sheet/index.tsx +282 -21
  208. package/src/components/breadcrumb/index.tsx +363 -0
  209. package/src/components/button/index.tsx +12 -8
  210. package/src/components/calendar/index.tsx +703 -0
  211. package/src/components/carousel/index.tsx +788 -0
  212. package/src/components/chip/index.tsx +6 -8
  213. package/src/components/date-picker/index.tsx +237 -0
  214. package/src/components/dialog/index.tsx +7 -2
  215. package/src/components/direction/index.tsx +22 -22
  216. package/src/components/empty-state/index.tsx +2 -2
  217. package/src/components/field/index.tsx +3 -5
  218. package/src/components/flow/index.tsx +3 -3
  219. package/src/components/frame/index.tsx +22 -7
  220. package/src/components/heatmap-chart/index.tsx +58 -11
  221. package/src/components/input-group/index.tsx +5 -4
  222. package/src/components/item/index.tsx +9 -9
  223. package/src/components/label/index.tsx +2 -2
  224. package/src/components/line-chart/index.tsx +194 -223
  225. package/src/components/loader/index.tsx +812 -0
  226. package/src/components/map/basemap.ts +304 -0
  227. package/src/components/map/index.tsx +1116 -0
  228. package/src/components/map/maplibre.ts +201 -0
  229. package/src/components/marker/index.tsx +3 -3
  230. package/src/components/message/index.tsx +15 -14
  231. package/src/components/message-scroller/index.tsx +5 -4
  232. package/src/components/otp-input/index.tsx +2 -2
  233. package/src/components/popover/index.tsx +43 -7
  234. package/src/components/progress/index.tsx +10 -2
  235. package/src/components/radio-group/index.tsx +2 -2
  236. package/src/components/ring-chart/index.tsx +543 -0
  237. package/src/components/section-rail/index.tsx +6 -6
  238. package/src/components/select/index.tsx +10 -4
  239. package/src/components/separator/index.tsx +77 -19
  240. package/src/components/shimmer/index.tsx +10 -1
  241. package/src/components/slider/index.tsx +20 -5
  242. package/src/components/steps/index.tsx +5 -5
  243. package/src/components/switch/index.tsx +6 -1
  244. package/src/components/tabs/index.tsx +7 -9
  245. package/src/components/timeline/index.tsx +4 -4
  246. package/src/components/toast/index.tsx +2 -2
  247. package/src/components/toggle-button/index.tsx +5 -7
  248. package/src/components/tooltip/index.tsx +610 -0
  249. package/src/components/typography/index.tsx +7 -2
  250. package/src/hooks/index.ts +1 -0
  251. package/src/hooks/use-back-handler.ts +37 -0
  252. package/src/hooks/use-direction.ts +45 -0
  253. package/src/icons/index.tsx +111 -0
  254. package/src/index.ts +129 -0
  255. package/src/primitives/keyboard-avoider.tsx +2 -1
  256. package/src/primitives/text.tsx +53 -8
  257. package/src/utils/chart.ts +355 -0
  258. package/src/utils/date.ts +446 -0
@@ -0,0 +1,958 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * AreaChart — filled bands over time, stacked or overlaid.
5
+ *
6
+ * ```tsx
7
+ * <AreaChart data={traffic} xDataKey="date" stacked>
8
+ * <AreaChart.Grid />
9
+ * <AreaChart.Area dataKey="direct" />
10
+ * <AreaChart.Area dataKey="search" colorIndex={2} />
11
+ * <AreaChart.XAxis />
12
+ * <AreaChart.Tooltip />
13
+ * </AreaChart>
14
+ * ```
15
+ *
16
+ * ## Why this is not `LineChart.Area`
17
+ *
18
+ * `LineChart.Area` shades under a line, which is one series saying "this is
19
+ * the shape of the thing". This chart answers a different question — *what is
20
+ * it made of* — and that needs stacking: each band sits on the running total
21
+ * of the ones below it, so the top edge is the whole and the thickness of each
22
+ * band is its share.
23
+ *
24
+ * Stacking is not a flag that could be bolted on. It changes the y-domain from
25
+ * the largest single series to the largest *sum*, it makes each area's
26
+ * baseline a curve rather than the axis, and it makes the order the series are
27
+ * declared in load-bearing. A chart where all of that is true of some children
28
+ * and not others is a chart nobody can read, so it is a chart of its own.
29
+ *
30
+ * Unstacked, the bands overlay each other and the fills are drawn translucent,
31
+ * which is the right reading when the series are alternatives rather than
32
+ * parts — two plans compared, not two slices of one total.
33
+ */
34
+ import { Children, createContext, forwardRef, isValidElement, useContext, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from 'react';
35
+ import { StyleSheet, View } from 'react-native';
36
+ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
37
+ import Animated, { Easing, runOnJS, useAnimatedProps, useAnimatedStyle, useReducedMotion, useSharedValue, withTiming } from 'react-native-reanimated';
38
+ import Svg, { Circle, ClipPath, Defs, G, Line as SvgLine, LinearGradient, Path, Rect, Stop } from 'react-native-svg';
39
+ import { useCSSVariable } from 'uniwind';
40
+ import { Text } from "../../primitives/text.js";
41
+ import { areaPath, columnValues, compactNumber, linePath, useSeriesColor, xOf, yOf } from "../../utils/chart.js";
42
+ import { cn } from "../../utils/cn.js";
43
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
44
+ const AnimatedPath = Animated.createAnimatedComponent(Path);
45
+ const AnimatedRect = Animated.createAnimatedComponent(Rect);
46
+ const AnimatedCircle = Animated.createAnimatedComponent(Circle);
47
+ const PADDING = {
48
+ top: 12,
49
+ right: 10,
50
+ bottom: 22,
51
+ left: 10
52
+ };
53
+ const LABEL_WIDTH = 132;
54
+
55
+ /**
56
+ * Box each x label is centred in. Wide enough for a short label to sit over
57
+ * its point without the neighbours colliding; a longer one is ellipsised
58
+ * rather than allowed to push the others out of place.
59
+ */
60
+ const POINT_LABEL_WIDTH = 56;
61
+
62
+ /** Line height of an `xs` label, for centring one on the grid line it names. */
63
+ const AXIS_LABEL_HEIGHT = 16;
64
+ const DOT = 9;
65
+
66
+ /** Left gutter reserved when a `YAxis` is present, for its labels to sit in. */
67
+ const Y_AXIS_WIDTH = 44;
68
+
69
+ /** Gap between the value labels and the plot they sit beside. */
70
+ const Y_AXIS_GUTTER = 6;
71
+ const AreaChartContext = /*#__PURE__*/createContext(null);
72
+ function useChart(component) {
73
+ const context = useContext(AreaChartContext);
74
+ if (!context) {
75
+ throw new Error(`${component} must be used within an <AreaChart>`);
76
+ }
77
+ return context;
78
+ }
79
+
80
+ /**
81
+ * The point under the crosshair, for something rendered *inside* the chart.
82
+ * A readout in the card's header is outside this provider — use
83
+ * `onActiveIndexChange` for that.
84
+ */
85
+ export function useAreaChart() {
86
+ const {
87
+ data,
88
+ activeIndexJS,
89
+ xDataKey
90
+ } = useChart('useAreaChart');
91
+ return {
92
+ activeIndex: activeIndexJS,
93
+ activePoint: activeIndexJS >= 0 ? data[activeIndexJS] ?? null : null,
94
+ xDataKey
95
+ };
96
+ }
97
+
98
+ /** Imperative handle: re-run the reveal on demand, for a "replay" control. */
99
+
100
+ function ChildSlot({
101
+ children
102
+ }) {
103
+ return /*#__PURE__*/_jsx(_Fragment, {
104
+ children: children
105
+ });
106
+ }
107
+ function partition(children) {
108
+ const svg = [];
109
+ const overlay = [];
110
+ const header = [];
111
+ Children.forEach(children, (child, index) => {
112
+ if (! /*#__PURE__*/isValidElement(child)) return;
113
+ const layer = child.type.layer ?? 'svg';
114
+ const slot = /*#__PURE__*/_jsx(ChildSlot, {
115
+ children: child
116
+ }, index);
117
+ (layer === 'header' ? header : layer === 'overlay' ? overlay : svg).push(slot);
118
+ });
119
+ return {
120
+ svg,
121
+ overlay,
122
+ header
123
+ };
124
+ }
125
+ const AreaChartRoot = /*#__PURE__*/forwardRef(function AreaChartRoot({
126
+ className,
127
+ data,
128
+ xDataKey = 'date',
129
+ status = 'ready',
130
+ aspectRatio = 2,
131
+ animationDuration = 1100,
132
+ domainDuration = 500,
133
+ yDomain,
134
+ stacked = false,
135
+ curve = 'monotone',
136
+ onActiveIndexChange,
137
+ compact = false,
138
+ children,
139
+ ...props
140
+ }, ref) {
141
+ const [size, setSize] = useState({
142
+ width: 0,
143
+ height: 0
144
+ });
145
+ const [series, setSeries] = useState([]);
146
+ const [activeIndexJS, setActiveIndexJS] = useState(-1);
147
+ const clipId = `panelui-area-clip-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
148
+ const reveal = useSharedValue(0);
149
+ const domainMin = useSharedValue(0);
150
+ const domainMax = useSharedValue(0);
151
+ const activeIndex = useSharedValue(-1);
152
+ const reducedMotion = useReducedMotion();
153
+ const registerSeries = useMemo(() => (key, color) => setSeries(current => {
154
+ const existing = current.find(([k]) => k === key);
155
+ if (existing?.[1] === color) return current;
156
+ return [...current.filter(([k]) => k !== key), [key, color]];
157
+ }), []);
158
+ const unregisterSeries = useMemo(() => key => setSeries(current => current.filter(([k]) => k !== key)), []);
159
+
160
+ /*
161
+ * Whether an axis is asking for room. It has to be known before the plot is
162
+ * laid out, and only the root sees the children early enough to ask — the
163
+ * axis itself renders into a box that has already been decided.
164
+ */
165
+ const hasYAxis = useMemo(() => {
166
+ let found = false;
167
+ Children.forEach(children, child => {
168
+ if (/*#__PURE__*/isValidElement(child) && child.type.axis === 'y') {
169
+ found = true;
170
+ }
171
+ });
172
+ return found;
173
+ }, [children]);
174
+ const pad = compact ? {
175
+ top: 2,
176
+ right: 1,
177
+ bottom: 2,
178
+ left: 1
179
+ } : {
180
+ ...PADDING,
181
+ left: hasYAxis ? Y_AXIS_WIDTH : PADDING.left
182
+ };
183
+ const plot = {
184
+ left: pad.left,
185
+ top: pad.top,
186
+ width: Math.max(size.width - pad.left - pad.right, 0),
187
+ height: Math.max(size.height - pad.top - pad.bottom, 0)
188
+ };
189
+ const seriesKeys = series.map(([key]) => key).join('|');
190
+ const extent = useMemo(() => {
191
+ if (yDomain) return yDomain;
192
+ const keys = seriesKeys ? seriesKeys.split('|') : [];
193
+ let min = Infinity;
194
+ let max = -Infinity;
195
+ for (const row of data) {
196
+ /*
197
+ * Stacked, the tall thing is the sum of the row; overlaid, it is the
198
+ * largest single value in it. Reading the same number for both crops a
199
+ * stack at its largest band and clips everything above it.
200
+ */
201
+ let rowTotal = 0;
202
+ for (const key of keys) {
203
+ const value = row[key];
204
+ if (typeof value !== 'number' || Number.isNaN(value)) continue;
205
+ if (stacked) {
206
+ rowTotal += value;
207
+ } else {
208
+ if (value < min) min = value;
209
+ if (value > max) max = value;
210
+ }
211
+ }
212
+ if (stacked) {
213
+ if (rowTotal < min) min = rowTotal;
214
+ if (rowTotal > max) max = rowTotal;
215
+ }
216
+ }
217
+ if (min === Infinity) return [0, 1];
218
+ if (min === max) return [min - 1, max + 1];
219
+ /*
220
+ * An area is a filled region, and a region floating above the bottom of
221
+ * the plot reads as a shape rather than as a quantity — so a series that
222
+ * never goes below zero is drawn from zero. Padding only the top.
223
+ */
224
+ const floor = min >= 0 ? 0 : min - (max - min) * 0.1;
225
+ return [floor, max + (max - min) * 0.1];
226
+ }, [data, yDomain, seriesKeys, stacked]);
227
+ const loading = status === 'loading';
228
+ useEffect(() => {
229
+ if (loading) return;
230
+ const [min, max] = extent;
231
+ const first = domainMin.value === 0 && domainMax.value === 0;
232
+ if (first || reducedMotion) {
233
+ domainMin.value = min;
234
+ domainMax.value = max;
235
+ return;
236
+ }
237
+ domainMin.value = withTiming(min, {
238
+ duration: domainDuration
239
+ });
240
+ domainMax.value = withTiming(max, {
241
+ duration: domainDuration
242
+ });
243
+ }, [extent, loading, reducedMotion, domainDuration, domainMin, domainMax]);
244
+ const revealed = useRef(false);
245
+ const playReveal = useMemo(() => () => {
246
+ if (reducedMotion) {
247
+ reveal.value = 1;
248
+ return;
249
+ }
250
+ reveal.value = 0;
251
+ reveal.value = withTiming(1, {
252
+ duration: animationDuration,
253
+ easing: Easing.bezier(0.85, 0, 0.15, 1)
254
+ });
255
+ }, [reducedMotion, animationDuration, reveal]);
256
+ useEffect(() => {
257
+ if (revealed.current || loading || plot.width <= 0 || !data.length) return;
258
+ revealed.current = true;
259
+ playReveal();
260
+ }, [loading, plot.width, data.length, playReveal]);
261
+ useImperativeHandle(ref, () => ({
262
+ replay: playReveal
263
+ }), [playReveal]);
264
+ const clipProps = useAnimatedProps(() => ({
265
+ width: plot.width * reveal.value
266
+ }));
267
+ const handleActiveIndex = useMemo(() => index => {
268
+ setActiveIndexJS(index);
269
+ onActiveIndexChange?.(index, index >= 0 ? data[index] ?? null : null);
270
+ }, [onActiveIndexChange, data]);
271
+ const onLayout = event => {
272
+ const {
273
+ width,
274
+ height
275
+ } = event.nativeEvent.layout;
276
+ setSize(current => Math.abs(current.width - width) < 1 && Math.abs(current.height - height) < 1 ? current : {
277
+ width,
278
+ height
279
+ });
280
+ props.onLayout?.(event);
281
+ };
282
+ const context = useMemo(() => ({
283
+ data,
284
+ xDataKey,
285
+ plot,
286
+ status,
287
+ curve,
288
+ stacked,
289
+ series,
290
+ registerSeries,
291
+ unregisterSeries,
292
+ domainMin,
293
+ domainMax,
294
+ extent,
295
+ activeIndex,
296
+ activeIndexJS,
297
+ setActiveIndexJS: handleActiveIndex,
298
+ clipId
299
+ }),
300
+ // `plot` is rebuilt every render from `size`, so it is compared by value.
301
+ // eslint-disable-next-line react-hooks/exhaustive-deps
302
+ [data, xDataKey, plot.width, plot.height, plot.left, plot.top, status, curve, stacked, series, registerSeries, unregisterSeries, domainMin, domainMax, extent, activeIndex, activeIndexJS, handleActiveIndex, clipId]);
303
+ const {
304
+ svg,
305
+ overlay,
306
+ header
307
+ } = partition(children);
308
+
309
+ /*
310
+ * Two views, because the header is not part of the plot. `aspectRatio` and
311
+ * the layout measurement belong to the drawing area alone — measured on the
312
+ * outer view they would take in the header too, and the plot would lose as
313
+ * much height as the readout took while still claiming the shape asked for.
314
+ */
315
+ return /*#__PURE__*/_jsx(AreaChartContext.Provider, {
316
+ value: context,
317
+ children: /*#__PURE__*/_jsxs(View, {
318
+ ...props,
319
+ style: props.style,
320
+ className: cn('w-full', className),
321
+ children: [header, /*#__PURE__*/_jsx(View, {
322
+ onLayout: onLayout,
323
+ style: {
324
+ aspectRatio
325
+ },
326
+ className: "w-full",
327
+ children: plot.width > 0 ? /*#__PURE__*/_jsxs(_Fragment, {
328
+ children: [/*#__PURE__*/_jsxs(Svg, {
329
+ width: "100%",
330
+ height: "100%",
331
+ style: StyleSheet.absoluteFill,
332
+ children: [/*#__PURE__*/_jsx(Defs, {
333
+ children: /*#__PURE__*/_jsx(ClipPath, {
334
+ id: clipId,
335
+ children: /*#__PURE__*/_jsx(AnimatedRect, {
336
+ x: plot.left,
337
+ y: 0,
338
+ height: size.height,
339
+ animatedProps: clipProps
340
+ })
341
+ })
342
+ }), svg]
343
+ }), overlay]
344
+ }) : null
345
+ })]
346
+ })
347
+ });
348
+ });
349
+ AreaChartRoot.displayName = 'AreaChart';
350
+
351
+ /* -------------------------------------------------------------------------- */
352
+ /* SVG layer */
353
+ /* -------------------------------------------------------------------------- */
354
+
355
+ /** Horizontal rules, so a band can be read against a number. */
356
+ function AreaChartGrid({
357
+ rows = 4,
358
+ color,
359
+ dashArray = '4,6',
360
+ opacity = 1
361
+ }) {
362
+ const {
363
+ plot
364
+ } = useChart('AreaChart.Grid');
365
+ const token = useCSSVariable('--color-border');
366
+ const stroke = color ?? (typeof token === 'string' ? token : 'rgba(0,0,0,0.1)');
367
+ return /*#__PURE__*/_jsx(G, {
368
+ opacity: opacity,
369
+ children: Array.from({
370
+ length: rows + 1
371
+ }, (_unused, index) => index / rows).map(fraction => /*#__PURE__*/_jsx(SvgLine, {
372
+ x1: plot.left,
373
+ x2: plot.left + plot.width,
374
+ y1: plot.top + plot.height * fraction,
375
+ y2: plot.top + plot.height * fraction,
376
+ stroke: stroke,
377
+ strokeDasharray: dashArray,
378
+ strokeWidth: 1
379
+ }, fraction))
380
+ });
381
+ }
382
+ AreaChartGrid.displayName = 'AreaChart.Grid';
383
+ AreaChartGrid.layer = 'svg';
384
+ /**
385
+ * One filled band.
386
+ *
387
+ * The fill is a gradient by default rather than a flat wash, because a flat
388
+ * fill of any weight competes with the line on top of it — fading it downward
389
+ * keeps the top edge, which is the part carrying the numbers, the darkest
390
+ * thing in the band.
391
+ */
392
+ function AreaChartArea({
393
+ dataKey,
394
+ color,
395
+ colorIndex = 1,
396
+ fillOpacity,
397
+ gradientToOpacity = 0,
398
+ showLine = true,
399
+ strokeWidth = 2
400
+ }) {
401
+ const {
402
+ data,
403
+ plot,
404
+ status,
405
+ curve,
406
+ stacked,
407
+ series,
408
+ registerSeries,
409
+ unregisterSeries,
410
+ domainMin,
411
+ domainMax,
412
+ clipId
413
+ } = useChart('AreaChart.Area');
414
+ const stroke = useSeriesColor(color, colorIndex);
415
+ const gradientId = `panelui-area-fill-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
416
+
417
+ // Stacked bands touch, so a translucent one shows the band beneath through
418
+ // it and the two read as a third colour. Overlaid they must be translucent,
419
+ // because the one in front would otherwise hide the ones behind entirely.
420
+ const topOpacity = fillOpacity ?? (stacked ? 0.85 : 0.35);
421
+ useEffect(() => {
422
+ registerSeries(dataKey, stroke);
423
+ return () => unregisterSeries(dataKey);
424
+ }, [dataKey, stroke, registerSeries, unregisterSeries]);
425
+ const values = useMemo(() => columnValues(data, dataKey), [data, dataKey]);
426
+
427
+ /*
428
+ * The running total of the series declared below this one, which is what
429
+ * this band sits on. Registration order is declaration order, so the first
430
+ * `Area` child is the bottom of the stack.
431
+ */
432
+ const baselines = useMemo(() => {
433
+ if (!stacked) return undefined;
434
+ const below = series.slice(0, series.findIndex(([key]) => key === dataKey));
435
+ if (!below.length) return undefined;
436
+ return data.map(row => {
437
+ let total = 0;
438
+ for (const [key] of below) {
439
+ const value = row[key];
440
+ if (typeof value === 'number' && !Number.isNaN(value)) total += value;
441
+ }
442
+ return total;
443
+ });
444
+ }, [stacked, series, dataKey, data]);
445
+ const loading = status === 'loading';
446
+ const fillProps = useAnimatedProps(() => ({
447
+ d: areaPath(values, plot, domainMin.value, domainMax.value, curve, loading, baselines)
448
+ }));
449
+ const lineProps = useAnimatedProps(() => ({
450
+ d: linePath(values, plot, domainMin.value, domainMax.value, curve, loading, baselines)
451
+ }));
452
+ return /*#__PURE__*/_jsxs(G, {
453
+ clipPath: `url(#${clipId})`,
454
+ children: [/*#__PURE__*/_jsx(Defs, {
455
+ children: /*#__PURE__*/_jsxs(LinearGradient, {
456
+ id: gradientId,
457
+ x1: "0",
458
+ y1: "0",
459
+ x2: "0",
460
+ y2: "1",
461
+ children: [/*#__PURE__*/_jsx(Stop, {
462
+ offset: "0",
463
+ stopColor: stroke,
464
+ stopOpacity: topOpacity
465
+ }), /*#__PURE__*/_jsx(Stop, {
466
+ offset: "1",
467
+ stopColor: stroke,
468
+ stopOpacity: gradientToOpacity
469
+ })]
470
+ })
471
+ }), /*#__PURE__*/_jsx(AnimatedPath, {
472
+ animatedProps: fillProps,
473
+ fill: `url(#${gradientId})`
474
+ }), showLine ? /*#__PURE__*/_jsx(AnimatedPath, {
475
+ animatedProps: lineProps,
476
+ stroke: stroke,
477
+ strokeWidth: strokeWidth,
478
+ strokeLinecap: "round",
479
+ strokeLinejoin: "round",
480
+ fill: "none"
481
+ }) : null]
482
+ });
483
+ }
484
+ AreaChartArea.displayName = 'AreaChart.Area';
485
+ AreaChartArea.layer = 'svg';
486
+
487
+ /* -------------------------------------------------------------------------- */
488
+ /* Overlay layer */
489
+ /* -------------------------------------------------------------------------- */
490
+
491
+ /** The x labels. Real text, so they follow the theme's font and text scaling. */
492
+ function AreaChartXAxis({
493
+ ticks = 4,
494
+ format,
495
+ className
496
+ }) {
497
+ const {
498
+ data,
499
+ xDataKey,
500
+ plot
501
+ } = useChart('AreaChart.XAxis');
502
+ const labels = useMemo(() => {
503
+ if (!data.length) return [];
504
+ const count = Math.min(ticks, data.length);
505
+ const step = count > 1 ? (data.length - 1) / (count - 1) : 0;
506
+ return Array.from({
507
+ length: count
508
+ }, (_unused, index) => {
509
+ const dataIndex = Math.round(index * step);
510
+ const datum = data[dataIndex];
511
+ if (!datum) return null;
512
+ return {
513
+ key: dataIndex,
514
+ text: format ? format(datum, dataIndex) : String(datum[xDataKey] ?? '')
515
+ };
516
+ }).filter(label => label !== null);
517
+ }, [data, ticks, format, xDataKey]);
518
+
519
+ /*
520
+ * Each label sits on its own point rather than being spread along the axis.
521
+ * The indices `ticks` picks are not evenly spaced — twelve months shown five
522
+ * at a time gives 0,3,6,8,11 — so spreading them evenly put the ones in
523
+ * between over the wrong part of the line.
524
+ *
525
+ * The box is backed off by half its own width rather than translated by
526
+ * `-50%`, which is not reliable across React Native versions.
527
+ */
528
+ return /*#__PURE__*/_jsx(View, {
529
+ style: {
530
+ position: 'absolute',
531
+ inset: 0,
532
+ pointerEvents: 'none'
533
+ },
534
+ className: cn(className),
535
+ children: labels.map(label => /*#__PURE__*/_jsx(Text, {
536
+ size: "xs",
537
+ muted: true,
538
+ numberOfLines: 1,
539
+ style: {
540
+ position: 'absolute',
541
+ bottom: 0,
542
+ // Centred on its point, then held inside the chart. The first and
543
+ // last points sit on the plot's own edges, so a box centred on
544
+ // them hangs half its width off the side — the clamp slides those
545
+ // two back in rather than letting the numbers leave the frame.
546
+ left: Math.max(0, Math.min(xOf(label.key, data.length, plot) - POINT_LABEL_WIDTH / 2, plot.left + plot.width + PADDING.right - POINT_LABEL_WIDTH)),
547
+ width: POINT_LABEL_WIDTH,
548
+ textAlign: 'center'
549
+ },
550
+ children: label.text
551
+ }, label.key))
552
+ });
553
+ }
554
+ AreaChartXAxis.displayName = 'AreaChart.XAxis';
555
+ AreaChartXAxis.layer = 'overlay';
556
+ /** Value labels down the side, aligned to the grid lines. */
557
+ function AreaChartYAxis({
558
+ ticks = 4,
559
+ format,
560
+ className
561
+ }) {
562
+ const {
563
+ plot,
564
+ extent
565
+ } = useChart('AreaChart.YAxis');
566
+ const labels = useMemo(() => {
567
+ const [min, max] = extent;
568
+ if (min === 0 && max === 0) return [];
569
+ return Array.from({
570
+ length: ticks + 1
571
+ }, (_unused, index) => {
572
+ const value = max - (max - min) * index / ticks;
573
+ return {
574
+ key: index,
575
+ text: format ? format(value) : compactNumber(value)
576
+ };
577
+ });
578
+ }, [extent, ticks, format]);
579
+ return /*#__PURE__*/_jsx(View, {
580
+ pointerEvents: "none",
581
+ style: {
582
+ position: 'absolute',
583
+ left: 0,
584
+ // Centred on the grid line each label names: the strip is lifted half
585
+ // a label and grown by a whole one, so `justify-between` lands the
586
+ // text's middle on the line rather than its top edge on the first line
587
+ // and its bottom edge on the last.
588
+ top: plot.top - AXIS_LABEL_HEIGHT / 2,
589
+ height: plot.height + AXIS_LABEL_HEIGHT,
590
+ // The gutter the root reserved, less a little breathing room, so the
591
+ // numbers sit clear of the plot instead of against it.
592
+ width: Math.max(plot.left - Y_AXIS_GUTTER, 0)
593
+ },
594
+ className: cn('items-end justify-between', className),
595
+ children: labels.map(label => /*#__PURE__*/_jsx(Text, {
596
+ size: "xs",
597
+ muted: true,
598
+ numberOfLines: 1,
599
+ children: label.text
600
+ }, label.key))
601
+ });
602
+ }
603
+ AreaChartYAxis.displayName = 'AreaChart.YAxis';
604
+ AreaChartYAxis.layer = 'overlay';
605
+ // Read by the root, which has to leave room for the labels before it
606
+ // lays the plot out — an axis drawn over the plot is unreadable, and
607
+ // makes what it is drawn over unreadable too.
608
+ AreaChartYAxis.axis = 'y';
609
+
610
+ /** A dot riding the top edge of one band as the crosshair moves. */
611
+ function CrosshairDot({
612
+ values,
613
+ baselines,
614
+ plot,
615
+ domainMin,
616
+ domainMax,
617
+ activeIndex,
618
+ total,
619
+ color
620
+ }) {
621
+ const props = useAnimatedProps(() => {
622
+ const index = activeIndex.value;
623
+ const value = index >= 0 ? values[index] : null;
624
+ if (index < 0 || value === null || value === undefined) {
625
+ return {
626
+ opacity: 0,
627
+ cx: 0,
628
+ cy: 0
629
+ };
630
+ }
631
+ const stacked = value + (baselines?.[index] ?? 0);
632
+ return {
633
+ opacity: 1,
634
+ cx: xOf(index, total, plot),
635
+ cy: yOf(stacked, plot, domainMin.value, domainMax.value)
636
+ };
637
+ });
638
+ return /*#__PURE__*/_jsx(AnimatedCircle, {
639
+ animatedProps: props,
640
+ r: DOT / 2,
641
+ fill: color,
642
+ stroke: "white",
643
+ strokeWidth: 2
644
+ });
645
+ }
646
+ /**
647
+ * The crosshair, the gesture that drives it, and the readout that rides it.
648
+ *
649
+ * The hit area is the whole plot. A crosshair you have to land on a band to
650
+ * summon is a crosshair nobody finds.
651
+ */
652
+ function AreaChartTooltip({
653
+ color,
654
+ formatValue,
655
+ formatX,
656
+ className
657
+ }) {
658
+ const {
659
+ data,
660
+ xDataKey,
661
+ plot,
662
+ stacked,
663
+ series,
664
+ domainMin,
665
+ domainMax,
666
+ activeIndex,
667
+ activeIndexJS,
668
+ setActiveIndexJS,
669
+ status
670
+ } = useChart('AreaChart.Tooltip');
671
+ const token = useCSSVariable('--color-foreground');
672
+ const stroke = color ?? (typeof token === 'string' ? token : '#888888');
673
+ const total = data.length;
674
+ const left = plot.left;
675
+ const width = plot.width;
676
+
677
+ /*
678
+ * Declared inside the memo, next to its callers: a worklet may only call
679
+ * another worklet, and the rule is enforced by crashing rather than warning.
680
+ */
681
+ const pan = useMemo(() => {
682
+ const resolve = x => {
683
+ 'worklet';
684
+
685
+ if (total < 2 || width <= 0) return;
686
+ const ratio = (x - left) / width;
687
+ const next = Math.round(Math.min(1, Math.max(0, ratio)) * (total - 1));
688
+ if (next === activeIndex.value) return;
689
+ activeIndex.value = next;
690
+ runOnJS(setActiveIndexJS)(next);
691
+ };
692
+ return Gesture.Pan().minDistance(0).onBegin(event => {
693
+ 'worklet';
694
+
695
+ resolve(event.x);
696
+ }).onUpdate(event => {
697
+ 'worklet';
698
+
699
+ resolve(event.x);
700
+ }).onFinalize(() => {
701
+ 'worklet';
702
+
703
+ activeIndex.value = -1;
704
+ runOnJS(setActiveIndexJS)(-1);
705
+ });
706
+ }, [total, left, width, activeIndex, setActiveIndexJS]);
707
+ const crosshairStyle = useAnimatedStyle(() => {
708
+ const index = activeIndex.value;
709
+ return {
710
+ opacity: index < 0 ? 0 : 0.45,
711
+ transform: [{
712
+ translateX: index < 0 ? 0 : xOf(index, total, plot)
713
+ }]
714
+ };
715
+ });
716
+ const labelStyle = useAnimatedStyle(() => {
717
+ const index = activeIndex.value;
718
+ if (index < 0) return {
719
+ opacity: 0
720
+ };
721
+ const x = xOf(index, total, plot);
722
+ const half = LABEL_WIDTH / 2;
723
+ const clamped = Math.min(plot.left + plot.width - half, Math.max(plot.left + half, x));
724
+ return {
725
+ opacity: 1,
726
+ transform: [{
727
+ translateX: clamped - half
728
+ }]
729
+ };
730
+ });
731
+
732
+ /*
733
+ * The dots ride the *top edge* of each band, which for a stack is the
734
+ * running total rather than the value — so the baselines are recomputed here
735
+ * the same way the bands compute them, and for the same reason.
736
+ */
737
+ const stacks = useMemo(() => {
738
+ return series.map(([key, seriesColor], index) => {
739
+ const below = series.slice(0, index);
740
+ const baselines = stacked && below.length ? data.map(row => {
741
+ let sum = 0;
742
+ for (const [k] of below) {
743
+ const value = row[k];
744
+ if (typeof value === 'number' && !Number.isNaN(value)) sum += value;
745
+ }
746
+ return sum;
747
+ }) : undefined;
748
+ return {
749
+ key,
750
+ color: seriesColor,
751
+ values: columnValues(data, key),
752
+ baselines
753
+ };
754
+ });
755
+ }, [series, data, stacked]);
756
+ const active = activeIndexJS >= 0 ? data[activeIndexJS] : null;
757
+ const fmtValue = formatValue ?? (value => compactNumber(value));
758
+ const fmtX = formatX ?? (datum => String(datum[xDataKey] ?? ''));
759
+ if (status === 'loading') return null;
760
+ return /*#__PURE__*/_jsx(GestureDetector, {
761
+ gesture: pan,
762
+ children: /*#__PURE__*/_jsxs(View, {
763
+ style: StyleSheet.absoluteFill,
764
+ children: [/*#__PURE__*/_jsx(Animated.View, {
765
+ pointerEvents: "none",
766
+ style: [{
767
+ position: 'absolute',
768
+ left: 0,
769
+ top: plot.top,
770
+ width: 1,
771
+ height: plot.height,
772
+ backgroundColor: stroke
773
+ }, crosshairStyle]
774
+ }), /*#__PURE__*/_jsx(Svg, {
775
+ width: "100%",
776
+ height: "100%",
777
+ style: StyleSheet.absoluteFill,
778
+ pointerEvents: "none",
779
+ children: stacks.map(stack => /*#__PURE__*/_jsx(CrosshairDot, {
780
+ values: stack.values,
781
+ baselines: stack.baselines,
782
+ plot: plot,
783
+ domainMin: domainMin,
784
+ domainMax: domainMax,
785
+ activeIndex: activeIndex,
786
+ total: total,
787
+ color: stack.color
788
+ }, stack.key))
789
+ }), /*#__PURE__*/_jsx(Animated.View, {
790
+ pointerEvents: "none",
791
+ style: [{
792
+ position: 'absolute',
793
+ left: 0,
794
+ top: 0,
795
+ width: LABEL_WIDTH
796
+ }, labelStyle],
797
+ children: active ? /*#__PURE__*/_jsxs(View, {
798
+ className: cn('rounded-xl border border-border bg-popover px-2.5 py-1.5 shadow-lg', className),
799
+ children: [/*#__PURE__*/_jsx(Text, {
800
+ size: "xs",
801
+ muted: true,
802
+ numberOfLines: 1,
803
+ children: fmtX(active)
804
+ }), series.map(([key, seriesColor]) => {
805
+ const value = active[key];
806
+ if (typeof value !== 'number') return null;
807
+ return /*#__PURE__*/_jsxs(View, {
808
+ className: "flex-row items-center gap-1.5",
809
+ children: [/*#__PURE__*/_jsx(View, {
810
+ style: {
811
+ width: 6,
812
+ height: 6,
813
+ borderRadius: 3,
814
+ backgroundColor: seriesColor
815
+ }
816
+ }), /*#__PURE__*/_jsx(Text, {
817
+ size: "xs",
818
+ weight: "medium",
819
+ children: fmtValue(value, key)
820
+ })]
821
+ }, key);
822
+ })]
823
+ }) : null
824
+ })]
825
+ })
826
+ });
827
+ }
828
+ AreaChartTooltip.displayName = 'AreaChart.Tooltip';
829
+ AreaChartTooltip.layer = 'overlay';
830
+ /**
831
+ * A swatch and a name per series.
832
+ *
833
+ * Reversed for a stack, so the key reads in the order the bands appear on the
834
+ * chart: the last one declared is the top band, and a legend listing it last
835
+ * points at the bottom one.
836
+ */
837
+ function AreaChartLegend({
838
+ className,
839
+ labels,
840
+ ...props
841
+ }) {
842
+ const {
843
+ series,
844
+ stacked
845
+ } = useChart('AreaChart.Legend');
846
+ if (!series.length) return null;
847
+ const ordered = stacked ? [...series].reverse() : series;
848
+ return /*#__PURE__*/_jsx(View, {
849
+ ...props,
850
+ style: [{
851
+ pointerEvents: 'none'
852
+ }, props.style],
853
+ className: cn('absolute right-2 top-1 flex-row gap-3', className),
854
+ children: ordered.map(([key, color]) => /*#__PURE__*/_jsx(SeriesSwatch, {
855
+ color: color,
856
+ label: labels?.[key] ?? key
857
+ }, key))
858
+ });
859
+ }
860
+ AreaChartLegend.displayName = 'AreaChart.Legend';
861
+ AreaChartLegend.layer = 'overlay';
862
+
863
+ /** One series' colour and name. Shared by the legend and the header. */
864
+ function SeriesSwatch({
865
+ color,
866
+ label
867
+ }) {
868
+ return /*#__PURE__*/_jsxs(View, {
869
+ className: "flex-row items-center gap-1.5",
870
+ children: [/*#__PURE__*/_jsx(View, {
871
+ style: {
872
+ backgroundColor: color
873
+ },
874
+ className: "h-2 w-2 rounded-full"
875
+ }), /*#__PURE__*/_jsx(Text, {
876
+ size: "xs",
877
+ muted: true,
878
+ children: label
879
+ })]
880
+ });
881
+ }
882
+
883
+ /* -------------------------------------------------------------------------- */
884
+ /* Header layer */
885
+ /* -------------------------------------------------------------------------- */
886
+
887
+ /**
888
+ * The strip above the plot: what the chart is of, what it currently reads, and
889
+ * what the colours mean.
890
+ *
891
+ * It belongs to the chart rather than to the card around it because it is about
892
+ * the *plot* — the number changes as a finger moves along the bands, and the
893
+ * legend is the series list the chart itself is holding. The card's header is a
894
+ * caption on the tray the chart sits in; this is the chart introducing itself.
895
+ *
896
+ * The value is not derived here. A readout that follows the finger belongs to
897
+ * whoever owns the data — take it from `onActiveIndexChange` and pass the
898
+ * formatted string down, so one header can show a total when nothing is pressed
899
+ * and a point's value when something is.
900
+ */
901
+ function AreaChartHeader({
902
+ className,
903
+ title,
904
+ value,
905
+ caption,
906
+ labels,
907
+ legend = false,
908
+ children,
909
+ ...props
910
+ }) {
911
+ const {
912
+ series,
913
+ stacked
914
+ } = useChart('AreaChart.Header');
915
+ const ordered = stacked ? [...series].reverse() : series;
916
+ const trailing = children ?? (legend && ordered.length ? /*#__PURE__*/_jsx(View, {
917
+ className: "flex-row flex-wrap items-center justify-end gap-x-3 gap-y-1",
918
+ children: ordered.map(([key, color]) => /*#__PURE__*/_jsx(SeriesSwatch, {
919
+ color: color,
920
+ label: labels?.[key] ?? key
921
+ }, key))
922
+ }) : null);
923
+ return /*#__PURE__*/_jsxs(View, {
924
+ ...props,
925
+ className: cn('flex-row items-start justify-between gap-3 pb-3', className),
926
+ children: [/*#__PURE__*/_jsxs(View, {
927
+ className: "flex-1 gap-0.5",
928
+ children: [title ? /*#__PURE__*/_jsx(Text, {
929
+ size: "xs",
930
+ muted: true,
931
+ children: title
932
+ }) : null, value ? /*#__PURE__*/_jsx(Text, {
933
+ size: "xl",
934
+ weight: "bold",
935
+ children: value
936
+ }) : null, caption ? /*#__PURE__*/_jsx(Text, {
937
+ size: "xs",
938
+ muted: true,
939
+ children: caption
940
+ }) : null]
941
+ }), trailing ? /*#__PURE__*/_jsx(View, {
942
+ className: "shrink pt-1",
943
+ children: trailing
944
+ }) : null]
945
+ });
946
+ }
947
+ AreaChartHeader.displayName = 'AreaChart.Header';
948
+ AreaChartHeader.layer = 'header';
949
+ export const AreaChart = Object.assign(AreaChartRoot, {
950
+ Header: AreaChartHeader,
951
+ Grid: AreaChartGrid,
952
+ Area: AreaChartArea,
953
+ XAxis: AreaChartXAxis,
954
+ YAxis: AreaChartYAxis,
955
+ Tooltip: AreaChartTooltip,
956
+ Legend: AreaChartLegend
957
+ });
958
+ //# sourceMappingURL=index.js.map