panelui-native 0.25.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/README.md +18 -1
  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 +182 -50
  7. package/lib/module/components/area-chart/index.js.map +1 -1
  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 +3 -3
  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 +197 -56
  15. package/lib/module/components/bar-chart/index.js.map +1 -1
  16. package/lib/module/components/bottom-sheet/index.js +15 -10
  17. package/lib/module/components/bottom-sheet/index.js.map +1 -1
  18. package/lib/module/components/breadcrumb/index.js +6 -6
  19. package/lib/module/components/breadcrumb/index.js.map +1 -1
  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 +792 -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 +4 -4
  27. package/lib/module/components/chip/index.js.map +1 -1
  28. package/lib/module/components/code-block/highlight.js +363 -0
  29. package/lib/module/components/code-block/highlight.js.map +1 -0
  30. package/lib/module/components/code-block/index.js +313 -0
  31. package/lib/module/components/code-block/index.js.map +1 -0
  32. package/lib/module/components/date-picker/index.js +165 -0
  33. package/lib/module/components/date-picker/index.js.map +1 -0
  34. package/lib/module/components/dialog/index.js +7 -2
  35. package/lib/module/components/dialog/index.js.map +1 -1
  36. package/lib/module/components/direction/index.js +2 -1
  37. package/lib/module/components/direction/index.js.map +1 -1
  38. package/lib/module/components/empty-state/index.js +2 -2
  39. package/lib/module/components/empty-state/index.js.map +1 -1
  40. package/lib/module/components/field/index.js +5 -5
  41. package/lib/module/components/field/index.js.map +1 -1
  42. package/lib/module/components/flow/index.js +3 -3
  43. package/lib/module/components/flow/index.js.map +1 -1
  44. package/lib/module/components/frame/index.js +5 -5
  45. package/lib/module/components/frame/index.js.map +1 -1
  46. package/lib/module/components/input-group/index.js +3 -2
  47. package/lib/module/components/input-group/index.js.map +1 -1
  48. package/lib/module/components/item/index.js +9 -9
  49. package/lib/module/components/item/index.js.map +1 -1
  50. package/lib/module/components/label/index.js +4 -4
  51. package/lib/module/components/label/index.js.map +1 -1
  52. package/lib/module/components/line-chart/index.js +167 -44
  53. package/lib/module/components/line-chart/index.js.map +1 -1
  54. package/lib/module/components/map/index.js +7 -7
  55. package/lib/module/components/map/index.js.map +1 -1
  56. package/lib/module/components/marker/index.js +3 -3
  57. package/lib/module/components/marker/index.js.map +1 -1
  58. package/lib/module/components/menu/index.js +714 -0
  59. package/lib/module/components/menu/index.js.map +1 -0
  60. package/lib/module/components/message/index.js +7 -7
  61. package/lib/module/components/message/index.js.map +1 -1
  62. package/lib/module/components/message-scroller/index.js +5 -4
  63. package/lib/module/components/message-scroller/index.js.map +1 -1
  64. package/lib/module/components/plan/index.js +296 -0
  65. package/lib/module/components/plan/index.js.map +1 -0
  66. package/lib/module/components/popover/index.js +73 -10
  67. package/lib/module/components/popover/index.js.map +1 -1
  68. package/lib/module/components/radio-group/index.js +15 -10
  69. package/lib/module/components/radio-group/index.js.map +1 -1
  70. package/lib/module/components/reasoning/index.js +244 -0
  71. package/lib/module/components/reasoning/index.js.map +1 -0
  72. package/lib/module/components/section-rail/index.js +4 -4
  73. package/lib/module/components/section-rail/index.js.map +1 -1
  74. package/lib/module/components/select/index.js +10 -4
  75. package/lib/module/components/select/index.js.map +1 -1
  76. package/lib/module/components/sources/index.js +221 -0
  77. package/lib/module/components/sources/index.js.map +1 -0
  78. package/lib/module/components/steps/index.js +4 -4
  79. package/lib/module/components/steps/index.js.map +1 -1
  80. package/lib/module/components/tabs/index.js +7 -7
  81. package/lib/module/components/tabs/index.js.map +1 -1
  82. package/lib/module/components/task/index.js +300 -0
  83. package/lib/module/components/task/index.js.map +1 -0
  84. package/lib/module/components/textarea/index.js +295 -0
  85. package/lib/module/components/textarea/index.js.map +1 -0
  86. package/lib/module/components/timeline/index.js +4 -4
  87. package/lib/module/components/timeline/index.js.map +1 -1
  88. package/lib/module/components/toast/index.js +2 -2
  89. package/lib/module/components/toast/index.js.map +1 -1
  90. package/lib/module/components/toggle-button/index.js +5 -5
  91. package/lib/module/components/toggle-button/index.js.map +1 -1
  92. package/lib/module/components/tooltip/index.js +648 -0
  93. package/lib/module/components/tooltip/index.js.map +1 -0
  94. package/lib/module/components/typography/index.js +2 -2
  95. package/lib/module/components/typography/index.js.map +1 -1
  96. package/lib/module/hooks/index.js +1 -0
  97. package/lib/module/hooks/index.js.map +1 -1
  98. package/lib/module/hooks/use-back-handler.js +37 -0
  99. package/lib/module/hooks/use-back-handler.js.map +1 -0
  100. package/lib/module/icons/index.js +202 -0
  101. package/lib/module/icons/index.js.map +1 -1
  102. package/lib/module/index.js +13 -1
  103. package/lib/module/index.js.map +1 -1
  104. package/lib/module/primitives/collapse.js +112 -0
  105. package/lib/module/primitives/collapse.js.map +1 -0
  106. package/lib/module/primitives/keyboard-avoider.js +2 -1
  107. package/lib/module/primitives/keyboard-avoider.js.map +1 -1
  108. package/lib/module/primitives/text.js +25 -1
  109. package/lib/module/primitives/text.js.map +1 -1
  110. package/lib/module/utils/date.js +386 -0
  111. package/lib/module/utils/date.js.map +1 -0
  112. package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
  113. package/lib/typescript/src/components/alert/index.d.ts.map +1 -1
  114. package/lib/typescript/src/components/area-chart/index.d.ts +41 -1
  115. package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
  116. package/lib/typescript/src/components/attachment/index.d.ts.map +1 -1
  117. package/lib/typescript/src/components/bar-chart/index.d.ts +44 -2
  118. package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -1
  119. package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
  120. package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -1
  121. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  122. package/lib/typescript/src/components/calendar/index.d.ts +173 -0
  123. package/lib/typescript/src/components/calendar/index.d.ts.map +1 -0
  124. package/lib/typescript/src/components/carousel/index.d.ts +179 -0
  125. package/lib/typescript/src/components/carousel/index.d.ts.map +1 -0
  126. package/lib/typescript/src/components/chip/index.d.ts.map +1 -1
  127. package/lib/typescript/src/components/code-block/highlight.d.ts +39 -0
  128. package/lib/typescript/src/components/code-block/highlight.d.ts.map +1 -0
  129. package/lib/typescript/src/components/code-block/index.d.ts +113 -0
  130. package/lib/typescript/src/components/code-block/index.d.ts.map +1 -0
  131. package/lib/typescript/src/components/date-picker/index.d.ts +97 -0
  132. package/lib/typescript/src/components/date-picker/index.d.ts.map +1 -0
  133. package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
  134. package/lib/typescript/src/components/direction/index.d.ts.map +1 -1
  135. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -1
  136. package/lib/typescript/src/components/field/index.d.ts.map +1 -1
  137. package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
  138. package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
  139. package/lib/typescript/src/components/item/index.d.ts.map +1 -1
  140. package/lib/typescript/src/components/label/index.d.ts.map +1 -1
  141. package/lib/typescript/src/components/line-chart/index.d.ts +41 -2
  142. package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
  143. package/lib/typescript/src/components/map/index.d.ts.map +1 -1
  144. package/lib/typescript/src/components/marker/index.d.ts.map +1 -1
  145. package/lib/typescript/src/components/menu/index.d.ts +261 -0
  146. package/lib/typescript/src/components/menu/index.d.ts.map +1 -0
  147. package/lib/typescript/src/components/message/index.d.ts.map +1 -1
  148. package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -1
  149. package/lib/typescript/src/components/plan/index.d.ts +127 -0
  150. package/lib/typescript/src/components/plan/index.d.ts.map +1 -0
  151. package/lib/typescript/src/components/popover/index.d.ts +35 -1
  152. package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
  153. package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
  154. package/lib/typescript/src/components/reasoning/index.d.ts +97 -0
  155. package/lib/typescript/src/components/reasoning/index.d.ts.map +1 -0
  156. package/lib/typescript/src/components/select/index.d.ts.map +1 -1
  157. package/lib/typescript/src/components/sources/index.d.ts +85 -0
  158. package/lib/typescript/src/components/sources/index.d.ts.map +1 -0
  159. package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
  160. package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
  161. package/lib/typescript/src/components/task/index.d.ts +103 -0
  162. package/lib/typescript/src/components/task/index.d.ts.map +1 -0
  163. package/lib/typescript/src/components/textarea/index.d.ts +175 -0
  164. package/lib/typescript/src/components/textarea/index.d.ts.map +1 -0
  165. package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
  166. package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
  167. package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -1
  168. package/lib/typescript/src/components/tooltip/index.d.ts +180 -0
  169. package/lib/typescript/src/components/tooltip/index.d.ts.map +1 -0
  170. package/lib/typescript/src/components/typography/index.d.ts.map +1 -1
  171. package/lib/typescript/src/hooks/index.d.ts +1 -0
  172. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  173. package/lib/typescript/src/hooks/use-back-handler.d.ts +19 -0
  174. package/lib/typescript/src/hooks/use-back-handler.d.ts.map +1 -0
  175. package/lib/typescript/src/icons/index.d.ts +17 -0
  176. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  177. package/lib/typescript/src/index.d.ts +16 -4
  178. package/lib/typescript/src/index.d.ts.map +1 -1
  179. package/lib/typescript/src/primitives/collapse.d.ts +42 -0
  180. package/lib/typescript/src/primitives/collapse.d.ts.map +1 -0
  181. package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
  182. package/lib/typescript/src/primitives/text.d.ts +20 -0
  183. package/lib/typescript/src/primitives/text.d.ts.map +1 -1
  184. package/lib/typescript/src/utils/date.d.ts +116 -0
  185. package/lib/typescript/src/utils/date.d.ts.map +1 -0
  186. package/package.json +1 -1
  187. package/src/components/accordion/index.tsx +8 -12
  188. package/src/components/alert/index.tsx +2 -2
  189. package/src/components/area-chart/index.tsx +208 -55
  190. package/src/components/attachment/index.tsx +2 -2
  191. package/src/components/avatar/index.tsx +3 -3
  192. package/src/components/badge/index.tsx +6 -6
  193. package/src/components/bar-chart/index.tsx +214 -54
  194. package/src/components/bottom-sheet/index.tsx +17 -16
  195. package/src/components/breadcrumb/index.tsx +6 -8
  196. package/src/components/button/index.tsx +12 -8
  197. package/src/components/calendar/index.tsx +970 -0
  198. package/src/components/carousel/index.tsx +788 -0
  199. package/src/components/chip/index.tsx +4 -6
  200. package/src/components/code-block/highlight.ts +278 -0
  201. package/src/components/code-block/index.tsx +341 -0
  202. package/src/components/date-picker/index.tsx +255 -0
  203. package/src/components/dialog/index.tsx +7 -2
  204. package/src/components/direction/index.tsx +2 -1
  205. package/src/components/empty-state/index.tsx +2 -2
  206. package/src/components/field/index.tsx +3 -5
  207. package/src/components/flow/index.tsx +3 -3
  208. package/src/components/frame/index.tsx +5 -7
  209. package/src/components/input-group/index.tsx +3 -2
  210. package/src/components/item/index.tsx +9 -9
  211. package/src/components/label/index.tsx +2 -2
  212. package/src/components/line-chart/index.tsx +189 -50
  213. package/src/components/map/index.tsx +7 -9
  214. package/src/components/marker/index.tsx +3 -3
  215. package/src/components/menu/index.tsx +812 -0
  216. package/src/components/message/index.tsx +7 -7
  217. package/src/components/message-scroller/index.tsx +5 -4
  218. package/src/components/plan/index.tsx +325 -0
  219. package/src/components/popover/index.tsx +111 -8
  220. package/src/components/radio-group/index.tsx +13 -4
  221. package/src/components/reasoning/index.tsx +297 -0
  222. package/src/components/section-rail/index.tsx +4 -4
  223. package/src/components/select/index.tsx +10 -4
  224. package/src/components/sources/index.tsx +278 -0
  225. package/src/components/steps/index.tsx +4 -4
  226. package/src/components/tabs/index.tsx +7 -9
  227. package/src/components/task/index.tsx +329 -0
  228. package/src/components/textarea/index.tsx +381 -0
  229. package/src/components/timeline/index.tsx +4 -4
  230. package/src/components/toast/index.tsx +2 -2
  231. package/src/components/toggle-button/index.tsx +5 -7
  232. package/src/components/tooltip/index.tsx +793 -0
  233. package/src/components/typography/index.tsx +2 -2
  234. package/src/hooks/index.ts +1 -0
  235. package/src/hooks/use-back-handler.ts +37 -0
  236. package/src/icons/index.tsx +147 -0
  237. package/src/index.ts +116 -0
  238. package/src/primitives/collapse.tsx +128 -0
  239. package/src/primitives/keyboard-avoider.tsx +2 -1
  240. package/src/primitives/text.tsx +28 -1
  241. package/src/utils/date.ts +446 -0
  242. package/theme.css +112 -0
@@ -106,7 +106,7 @@ const PADDING = { top: 12, right: 10, bottom: 22, left: 10 };
106
106
  * stays a flat list of children instead of two nested slots the caller has to
107
107
  * remember the order of.
108
108
  */
109
- type Layer = 'svg' | 'overlay';
109
+ type Layer = 'svg' | 'overlay' | 'header';
110
110
 
111
111
  export type LineChartStatus = 'loading' | 'ready';
112
112
  export type LineChartCurve = 'monotone' | 'linear';
@@ -388,39 +388,43 @@ const LineChartRoot = forwardRef<LineChartHandle, LineChartProps>(function LineC
388
388
  ]
389
389
  );
390
390
 
391
- const { svg, overlay } = partition(children);
391
+ const { svg, overlay, header } = partition(children);
392
392
 
393
+ /*
394
+ * Two views, because the header is not part of the plot. `aspectRatio` and
395
+ * the layout measurement belong to the drawing area alone — measured on the
396
+ * outer view they would take in the header too, and the plot would lose as
397
+ * much height as the readout took while still claiming the shape asked for.
398
+ */
393
399
  return (
394
400
  <LineChartContext.Provider value={context}>
395
- <View
396
- {...props}
397
- onLayout={onLayout}
398
- style={[{ aspectRatio }, props.style]}
399
- className={cn('w-full', className)}
400
- >
401
- {plot.width > 0 ? (
402
- <>
403
- <Svg width="100%" height="100%" style={StyleSheet.absoluteFill}>
404
- <Defs>
405
- {/*
406
- * One clip for everything in the plot. Sharing it is what makes
407
- * the reveal read as the chart arriving, rather than as three
408
- * separate things animating in at once.
409
- */}
410
- <ClipPath id={clipId}>
411
- <AnimatedRect
412
- x={plot.left}
413
- y={0}
414
- height={size.height}
415
- animatedProps={clipProps}
416
- />
417
- </ClipPath>
418
- </Defs>
419
- {svg}
420
- </Svg>
421
- {overlay}
422
- </>
423
- ) : null}
401
+ <View {...props} style={props.style} className={cn('w-full', className)}>
402
+ {header}
403
+ <View onLayout={onLayout} style={{ aspectRatio }} className="w-full">
404
+ {plot.width > 0 ? (
405
+ <>
406
+ <Svg width="100%" height="100%" style={StyleSheet.absoluteFill}>
407
+ <Defs>
408
+ {/*
409
+ * One clip for everything in the plot. Sharing it is what
410
+ * makes the reveal read as the chart arriving, rather than as
411
+ * three separate things animating in at once.
412
+ */}
413
+ <ClipPath id={clipId}>
414
+ <AnimatedRect
415
+ x={plot.left}
416
+ y={0}
417
+ height={size.height}
418
+ animatedProps={clipProps}
419
+ />
420
+ </ClipPath>
421
+ </Defs>
422
+ {svg}
423
+ </Svg>
424
+ {overlay}
425
+ </>
426
+ ) : null}
427
+ </View>
424
428
  </View>
425
429
  </LineChartContext.Provider>
426
430
  );
@@ -431,18 +435,19 @@ LineChartRoot.displayName = 'LineChart';
431
435
  function partition(children: ReactNode) {
432
436
  const svg: ReactNode[] = [];
433
437
  const overlay: ReactNode[] = [];
438
+ const header: ReactNode[] = [];
434
439
 
435
440
  Children.forEach(children, (child, index) => {
436
441
  if (!isValidElement(child)) return;
437
442
  const layer = (child.type as { layer?: Layer }).layer ?? 'svg';
438
- (layer === 'overlay' ? overlay : svg).push(
443
+ (layer === 'header' ? header : layer === 'overlay' ? overlay : svg).push(
439
444
  // Children of a `Children.forEach` need keys of their own once they are
440
445
  // put into a new array.
441
446
  <ChildSlot key={index}>{child}</ChildSlot>
442
447
  );
443
448
  });
444
449
 
445
- return { svg, overlay };
450
+ return { svg, overlay, header };
446
451
  }
447
452
 
448
453
  /** Identity wrapper, purely so the partitioned arrays can carry keys. */
@@ -737,20 +742,44 @@ function LineChartXAxis({ ticks = 4, format, className }: LineChartXAxisProps) {
737
742
  }).filter((label): label is { key: number; text: string } => label !== null);
738
743
  }, [data, ticks, format, xDataKey]);
739
744
 
745
+ /*
746
+ * Each label sits on its own point rather than being spread along the axis.
747
+ * The indices `ticks` picks are not evenly spaced — twelve months shown five
748
+ * at a time gives 0,3,6,8,11 — so spreading them evenly put the ones in
749
+ * between over the wrong part of the line.
750
+ *
751
+ * The box is backed off by half its own width rather than translated by
752
+ * `-50%`, which is not reliable across React Native versions.
753
+ */
740
754
  return (
741
755
  <View
742
- pointerEvents="none"
743
- style={{
744
- position: 'absolute',
745
- left: plot.left,
746
- right: PADDING.right,
747
- bottom: 0,
748
- height: PADDING.bottom,
749
- }}
750
- className={cn('flex-row items-center justify-between', className)}
756
+ style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}
757
+ className={cn(className)}
751
758
  >
752
759
  {labels.map((label) => (
753
- <Text key={label.key} size="xs" muted>
760
+ <Text
761
+ key={label.key}
762
+ size="xs"
763
+ muted
764
+ numberOfLines={1}
765
+ style={{
766
+ position: 'absolute',
767
+ bottom: 0,
768
+ // Centred on its point, then held inside the chart. The first and
769
+ // last points sit on the plot's own edges, so a box centred on
770
+ // them hangs half its width off the side — the clamp slides those
771
+ // two back in rather than letting the numbers leave the frame.
772
+ left: Math.max(
773
+ 0,
774
+ Math.min(
775
+ xOf(label.key, data.length, plot) - POINT_LABEL_WIDTH / 2,
776
+ plot.left + plot.width + PADDING.right - POINT_LABEL_WIDTH
777
+ )
778
+ ),
779
+ width: POINT_LABEL_WIDTH,
780
+ textAlign: 'center',
781
+ }}
782
+ >
754
783
  {label.text}
755
784
  </Text>
756
785
  ))}
@@ -955,6 +984,16 @@ LineChartTooltip.layer = 'overlay' as Layer;
955
984
 
956
985
  const LABEL_WIDTH = 112;
957
986
 
987
+ /**
988
+ * Box each x label is centred in. Wide enough for a short label to sit over
989
+ * its point without the neighbours colliding; a longer one is ellipsised
990
+ * rather than allowed to push the others out of place.
991
+ */
992
+ const POINT_LABEL_WIDTH = 56;
993
+
994
+ /** Line height of an `xs` label, for centring one on the grid line it names. */
995
+ const AXIS_LABEL_HEIGHT = 16;
996
+
958
997
  /** A compact number for the crosshair label: 1204 → "1.2k", 24801 → "24.8k". */
959
998
  const DOT = 9;
960
999
 
@@ -1018,20 +1057,16 @@ export interface LineChartLegendProps extends ViewProps {
1018
1057
  */
1019
1058
  function LineChartLegend({ className, labels, ...props }: LineChartLegendProps) {
1020
1059
  const { series } = useChart('LineChart.Legend');
1060
+ if (!series.length) return null;
1021
1061
 
1022
1062
  return (
1023
1063
  <View
1024
- pointerEvents="none"
1025
1064
  className={cn('absolute left-2.5 top-0 flex-row flex-wrap items-center gap-4', className)}
1026
1065
  {...props}
1066
+ style={[{ pointerEvents: 'none' }, props.style]}
1027
1067
  >
1028
1068
  {series.map(([key, color]) => (
1029
- <View key={key} className="flex-row items-center gap-1.5">
1030
- <View style={{ backgroundColor: color }} className="h-2 w-2 rounded-full" />
1031
- <Text size="xs" muted>
1032
- {labels?.[key] ?? key}
1033
- </Text>
1034
- </View>
1069
+ <SeriesSwatch key={key} color={color} label={labels?.[key] ?? key} />
1035
1070
  ))}
1036
1071
  </View>
1037
1072
  );
@@ -1039,6 +1074,109 @@ function LineChartLegend({ className, labels, ...props }: LineChartLegendProps)
1039
1074
  LineChartLegend.displayName = 'LineChart.Legend';
1040
1075
  LineChartLegend.layer = 'overlay' as Layer;
1041
1076
 
1077
+ /** One series' colour and name. Shared by the legend and the header. */
1078
+ function SeriesSwatch({ color, label }: { color: string; label: string }) {
1079
+ return (
1080
+ <View className="flex-row items-center gap-1.5">
1081
+ <View style={{ backgroundColor: color }} className="h-2 w-2 rounded-full" />
1082
+ <Text size="xs" muted>
1083
+ {label}
1084
+ </Text>
1085
+ </View>
1086
+ );
1087
+ }
1088
+
1089
+ /* -------------------------------------------------------------------------- */
1090
+ /* Header layer */
1091
+ /* -------------------------------------------------------------------------- */
1092
+
1093
+ export interface LineChartHeaderProps extends ViewProps {
1094
+ className?: string;
1095
+ /** Small line above the value — what the chart is of. */
1096
+ title?: string;
1097
+ /** The readout. The largest thing on the card, and the first thing read. */
1098
+ value?: string;
1099
+ /** One muted line under the value — a period, a comparison, a total. */
1100
+ caption?: string;
1101
+ /** Prettier names for the series keys, as the legend takes. */
1102
+ labels?: Record<string, string>;
1103
+ /**
1104
+ * Draw a swatch and a name per series along the trailing edge. Prefer this to
1105
+ * `LineChart.Legend` on a chart that has a header: the legend floats over the
1106
+ * plot, where it competes with the lines for the same corner.
1107
+ */
1108
+ legend?: boolean;
1109
+ /** Trailing slot — a control, a badge, a range picker. Wins over `legend`. */
1110
+ children?: ReactNode;
1111
+ }
1112
+
1113
+ /**
1114
+ * The strip above the plot: what the chart is of, what it currently reads, and
1115
+ * what the colours mean.
1116
+ *
1117
+ * It belongs to the chart rather than to the card around it because it is about
1118
+ * the *plot* — the number changes as a finger moves along the line, and the
1119
+ * legend is the series list the chart itself is holding. The card's header is a
1120
+ * caption on the tray the chart sits in; this is the chart introducing itself.
1121
+ *
1122
+ * The value is not derived here. A readout that follows the finger belongs to
1123
+ * whoever owns the data — take it from `onActiveIndexChange` and pass the
1124
+ * formatted string down, so one header can show a total when nothing is pressed
1125
+ * and a point's value when something is.
1126
+ */
1127
+ function LineChartHeader({
1128
+ className,
1129
+ title,
1130
+ value,
1131
+ caption,
1132
+ labels,
1133
+ legend = false,
1134
+ children,
1135
+ ...props
1136
+ }: LineChartHeaderProps) {
1137
+ const { series } = useChart('LineChart.Header');
1138
+ const trailing =
1139
+ children ??
1140
+ (legend && series.length ? (
1141
+ <View className="flex-row flex-wrap items-center justify-end gap-x-3 gap-y-1">
1142
+ {series.map(([key, color]) => (
1143
+ <SeriesSwatch key={key} color={color} label={labels?.[key] ?? key} />
1144
+ ))}
1145
+ </View>
1146
+ ) : null);
1147
+
1148
+ return (
1149
+ <View
1150
+ {...props}
1151
+ className={cn('flex-row items-start justify-between gap-3 pb-3', className)}
1152
+ >
1153
+ <View className="flex-1 gap-0.5">
1154
+ {title ? (
1155
+ <Text size="xs" muted>
1156
+ {title}
1157
+ </Text>
1158
+ ) : null}
1159
+ {value ? (
1160
+ <Text size="xl" weight="bold">
1161
+ {value}
1162
+ </Text>
1163
+ ) : null}
1164
+ {caption ? (
1165
+ <Text size="xs" muted>
1166
+ {caption}
1167
+ </Text>
1168
+ ) : null}
1169
+ </View>
1170
+ {/* Shrinkable, unlike a view's default in React Native. Held rigid, a
1171
+ three-series key takes the width it wants and the caption underneath
1172
+ the value wraps to two lines to make room for it. */}
1173
+ {trailing ? <View className="shrink pt-1">{trailing}</View> : null}
1174
+ </View>
1175
+ );
1176
+ }
1177
+ LineChartHeader.displayName = 'LineChart.Header';
1178
+ LineChartHeader.layer = 'header' as Layer;
1179
+
1042
1180
  /* -------------------------------------------------------------------------- */
1043
1181
  /* Scales and paths */
1044
1182
  /* -------------------------------------------------------------------------- */
@@ -1046,6 +1184,7 @@ LineChartLegend.layer = 'overlay' as Layer;
1046
1184
  /** Only reached if the theme CSS was never imported. */
1047
1185
 
1048
1186
  export const LineChart = Object.assign(LineChartRoot, {
1187
+ Header: LineChartHeader,
1049
1188
  Grid: LineChartGrid,
1050
1189
  Area: LineChartArea,
1051
1190
  Line: LineChartLine,
@@ -46,7 +46,7 @@ import { Pressable, View, type ViewProps } from 'react-native';
46
46
  import { useCSSVariable } from 'uniwind';
47
47
  import { tv } from 'tailwind-variants';
48
48
  import { CompassIcon, CrosshairIcon, MinusIcon, PlusIcon } from '../../icons';
49
- import { Text } from '../../primitives/text';
49
+ import { Text, textChildren } from '../../primitives/text';
50
50
  import { cn } from '../../utils/cn';
51
51
  import {
52
52
  buildBasemapStyle,
@@ -334,7 +334,7 @@ const MapRoot = forwardRef<MapHandle, MapProps>(function MapRoot(
334
334
  : { center: center ?? [0, 20], zoom, bearing, pitch }
335
335
  }
336
336
  />
337
- {children}
337
+ {textChildren(children)}
338
338
  </MapLibreMap>
339
339
  </MapContext.Provider>
340
340
  </View>
@@ -471,13 +471,11 @@ function MapLabel({ children, className, side = 'bottom' }: MapLabelProps) {
471
471
  style={{ left: -120, right: -120 }}
472
472
  >
473
473
  <View className={cn('rounded-md bg-background/85 px-1.5 py-0.5', className)}>
474
- {typeof children === 'string' ? (
474
+ {textChildren(children, (text) => (
475
475
  <Text size="xs" weight="medium" numberOfLines={1}>
476
- {children}
476
+ {text}
477
477
  </Text>
478
- ) : (
479
- children
480
- )}
478
+ ))}
481
479
  </View>
482
480
  </View>
483
481
  );
@@ -525,7 +523,7 @@ function MapPopup({ children, className, title, lngLat }: MapPopupProps) {
525
523
  {title}
526
524
  </Text>
527
525
  ) : null}
528
- {children}
526
+ {textChildren(children)}
529
527
  </View>
530
528
  </Marker>
531
529
  );
@@ -563,7 +561,7 @@ function ControlButton({
563
561
  className={slots.control()}
564
562
  onPress={onPress}
565
563
  >
566
- {children}
564
+ {textChildren(children)}
567
565
  </Pressable>
568
566
  );
569
567
  }
@@ -25,7 +25,7 @@ import { tv, type VariantProps } from 'tailwind-variants';
25
25
  import { useCSSVariable } from 'uniwind';
26
26
  import { IconColorProvider } from '../../icons';
27
27
  import { AnimatedPressable, type AnimatedPressableProps } from '../../primitives/animated-pressable';
28
- import { Text, type TextProps } from '../../primitives/text';
28
+ import { Text, type TextProps, textChildren } from '../../primitives/text';
29
29
  import { cn } from '../../utils/cn';
30
30
  import { Separator } from '../separator';
31
31
  import { Shimmer } from '../shimmer';
@@ -90,7 +90,7 @@ const MarkerRoot = forwardRef<View, MarkerProps>(
90
90
  variant === 'separator' ? (
91
91
  <>
92
92
  <Separator className="flex-1" />
93
- {children}
93
+ {textChildren(children)}
94
94
  <Separator className="flex-1" />
95
95
  </>
96
96
  ) : (
@@ -149,7 +149,7 @@ const MarkerIcon = forwardRef<View, MarkerIconProps>(
149
149
  {...props}
150
150
  >
151
151
  <IconColorProvider color={typeof muted === 'string' ? muted : undefined}>
152
- {children}
152
+ {textChildren(children)}
153
153
  </IconColorProvider>
154
154
  </View>
155
155
  );