@btndprojects/loam-ui-native 0.0.0-stage → 0.1.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 (269) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -2
  3. package/dist/accordion/index.js +3 -0
  4. package/dist/adaptive/index.js +2 -0
  5. package/dist/alert/index.js +3 -0
  6. package/dist/analyzer/index.js +6 -0
  7. package/dist/announcement/index.js +6 -0
  8. package/dist/audio-player/index.js +3 -0
  9. package/dist/avatar/index.js +3 -0
  10. package/dist/avatar-group/index.js +3 -0
  11. package/dist/badge/index.js +5 -0
  12. package/dist/banner/index.js +2 -0
  13. package/dist/board/index.js +7 -0
  14. package/dist/button/index.js +5 -0
  15. package/dist/camera-permission/index.js +13 -0
  16. package/dist/capture-result/index.js +18 -0
  17. package/dist/card/index.js +4 -0
  18. package/dist/card-form/index.js +10 -0
  19. package/dist/carousel/index.js +2 -0
  20. package/dist/chart/index.js +6 -0
  21. package/dist/charts/index.js +7 -0
  22. package/dist/checkbox/index.js +7 -0
  23. package/dist/checklist/index.js +10 -0
  24. package/dist/checkout/index.js +13 -0
  25. package/dist/chip/index.js +7 -0
  26. package/dist/chunk-223Q2T2Q.js +229 -0
  27. package/dist/chunk-2A5NIQAK.js +254 -0
  28. package/dist/chunk-2GK6PHGE.js +363 -0
  29. package/dist/chunk-2WUTXHMU.js +1 -0
  30. package/dist/chunk-2ZZ7TDJ4.js +324 -0
  31. package/dist/chunk-35OFI3JG.js +499 -0
  32. package/dist/chunk-3ARE4TES.js +195 -0
  33. package/dist/chunk-3ECPOSKO.js +1 -0
  34. package/dist/chunk-3ENTMPQY.js +229 -0
  35. package/dist/chunk-3HJH6FF4.js +98 -0
  36. package/dist/chunk-3NMD74ZU.js +4 -0
  37. package/dist/chunk-3WPW4KUN.js +182 -0
  38. package/dist/chunk-423B7SPN.js +993 -0
  39. package/dist/chunk-47FWHGUN.js +394 -0
  40. package/dist/chunk-4BXMXBJO.js +141 -0
  41. package/dist/chunk-4DS5ZWVC.js +83 -0
  42. package/dist/chunk-4GBOGOTV.js +295 -0
  43. package/dist/chunk-4SZYY6AN.js +99 -0
  44. package/dist/chunk-4YIP3CWS.js +237 -0
  45. package/dist/chunk-4YQOR7JM.js +204 -0
  46. package/dist/chunk-4ZAHCY44.js +177 -0
  47. package/dist/chunk-52X2VYTD.js +228 -0
  48. package/dist/chunk-5P26LBDL.js +1 -0
  49. package/dist/chunk-5XLRYDAX.js +1 -0
  50. package/dist/chunk-5YNTNN5A.js +177 -0
  51. package/dist/chunk-6HT2LP62.js +504 -0
  52. package/dist/chunk-6N33LFQA.js +35 -0
  53. package/dist/chunk-6X6PQ443.js +432 -0
  54. package/dist/chunk-7CFF4HXS.js +126 -0
  55. package/dist/chunk-7O2FMUPT.js +755 -0
  56. package/dist/chunk-AI4DWZCD.js +162 -0
  57. package/dist/chunk-AWJOCGFZ.js +213 -0
  58. package/dist/chunk-AZRX6SAC.js +285 -0
  59. package/dist/chunk-B54QE2FN.js +1 -0
  60. package/dist/chunk-BC475SJH.js +1 -0
  61. package/dist/chunk-BEDBFJND.js +202 -0
  62. package/dist/chunk-BTTFCMKG.js +166 -0
  63. package/dist/chunk-BV3B2HYZ.js +1635 -0
  64. package/dist/chunk-C2NTXVWM.js +98 -0
  65. package/dist/chunk-CDRZHT42.js +424 -0
  66. package/dist/chunk-CJ5EOYZE.js +62 -0
  67. package/dist/chunk-CUMUNPXY.js +128 -0
  68. package/dist/chunk-CUS3ZSCU.js +1 -0
  69. package/dist/chunk-CYRBHQQZ.js +411 -0
  70. package/dist/chunk-DHWADYZX.js +144 -0
  71. package/dist/chunk-DKCSPATL.js +1 -0
  72. package/dist/chunk-DM5QUKKO.js +326 -0
  73. package/dist/chunk-DYBH2I6W.js +1 -0
  74. package/dist/chunk-DZ255O2F.js +1 -0
  75. package/dist/chunk-E35BPSGW.js +1 -0
  76. package/dist/chunk-EJBDRVIH.js +166 -0
  77. package/dist/chunk-F43NP42L.js +1 -0
  78. package/dist/chunk-F5XNPWS7.js +1024 -0
  79. package/dist/chunk-FMYDDECK.js +1521 -0
  80. package/dist/chunk-G3HOYSF7.js +1 -0
  81. package/dist/chunk-GDKZTW34.js +395 -0
  82. package/dist/chunk-GL7B2DOW.js +134 -0
  83. package/dist/chunk-GNNJ4GCC.js +471 -0
  84. package/dist/chunk-GNZ56EUI.js +1 -0
  85. package/dist/chunk-H6NIOQHK.js +346 -0
  86. package/dist/chunk-H6PC7RDA.js +1 -0
  87. package/dist/chunk-HET4LLMX.js +129 -0
  88. package/dist/chunk-HPPW424V.js +334 -0
  89. package/dist/chunk-HQOQIBJI.js +1 -0
  90. package/dist/chunk-HU2EQNRZ.js +44 -0
  91. package/dist/chunk-HZ6CNMAF.js +870 -0
  92. package/dist/chunk-IIQTAY3J.js +715 -0
  93. package/dist/chunk-ITTYJZWX.js +271 -0
  94. package/dist/chunk-IVNRBPGC.js +1018 -0
  95. package/dist/chunk-J4KLDGI4.js +1 -0
  96. package/dist/chunk-J7Q7VCVC.js +254 -0
  97. package/dist/chunk-JDEOITQ2.js +1 -0
  98. package/dist/chunk-JNH2TYIN.js +96 -0
  99. package/dist/chunk-JNY4ZHAR.js +634 -0
  100. package/dist/chunk-JRLTGLN4.js +1 -0
  101. package/dist/chunk-JTRJJLKL.js +1 -0
  102. package/dist/chunk-JUAMLEHX.js +379 -0
  103. package/dist/chunk-JXJB2X6M.js +131 -0
  104. package/dist/chunk-KMI5ML5I.js +262 -0
  105. package/dist/chunk-KTAC4MGE.js +4 -0
  106. package/dist/chunk-LAECSR4H.js +178 -0
  107. package/dist/chunk-LK4BCSGX.js +1 -0
  108. package/dist/chunk-LOE5YNN3.js +470 -0
  109. package/dist/chunk-LRJNDEIA.js +302 -0
  110. package/dist/chunk-LSS54GLJ.js +116 -0
  111. package/dist/chunk-LZLHELAG.js +137 -0
  112. package/dist/chunk-M2LUXGKZ.js +1 -0
  113. package/dist/chunk-MOUJOP55.js +425 -0
  114. package/dist/chunk-N32RGOA5.js +159 -0
  115. package/dist/chunk-N5ZNE2YE.js +1 -0
  116. package/dist/chunk-NBNCBML6.js +1 -0
  117. package/dist/chunk-NKB7BZYK.js +167 -0
  118. package/dist/chunk-NQYBUO3K.js +1795 -0
  119. package/dist/chunk-NY2U3PBI.js +59 -0
  120. package/dist/chunk-O4AEHWII.js +464 -0
  121. package/dist/chunk-O7AE6DAF.js +146 -0
  122. package/dist/chunk-OETCUXQP.js +134 -0
  123. package/dist/chunk-OOPPQH27.js +179 -0
  124. package/dist/chunk-OPXFZ4Y5.js +204 -0
  125. package/dist/chunk-P23EQNRQ.js +338 -0
  126. package/dist/chunk-PJXUJYS6.js +248 -0
  127. package/dist/chunk-PNWUWOBF.js +1 -0
  128. package/dist/chunk-PUPM2YGF.js +376 -0
  129. package/dist/chunk-PZXZJBMP.js +620 -0
  130. package/dist/chunk-Q72LJOVE.js +169 -0
  131. package/dist/chunk-QCDXZBC5.js +247 -0
  132. package/dist/chunk-QDYDBCFP.js +197 -0
  133. package/dist/chunk-QJJ6MADV.js +274 -0
  134. package/dist/chunk-QKXCPTFS.js +349 -0
  135. package/dist/chunk-QWCMU5B2.js +860 -0
  136. package/dist/chunk-RIBDXGUG.js +108 -0
  137. package/dist/chunk-RJQOOHT2.js +214 -0
  138. package/dist/chunk-RNGLIHFA.js +191 -0
  139. package/dist/chunk-RQ3HASCC.js +6 -0
  140. package/dist/chunk-RSOJT5YW.js +124 -0
  141. package/dist/chunk-RTIKSY6A.js +1 -0
  142. package/dist/chunk-RUZHTGKB.js +237 -0
  143. package/dist/chunk-RW6ZL2FQ.js +284 -0
  144. package/dist/chunk-S7F6XLTK.js +540 -0
  145. package/dist/chunk-SBMNWWBV.js +155 -0
  146. package/dist/chunk-SDTQPRRB.js +1 -0
  147. package/dist/chunk-SYNOJOQO.js +377 -0
  148. package/dist/chunk-T753XT36.js +1 -0
  149. package/dist/chunk-TKQYWZ5V.js +196 -0
  150. package/dist/chunk-TKVFVF5D.js +95 -0
  151. package/dist/chunk-TOXAGRDQ.js +144 -0
  152. package/dist/chunk-TUTI5HOZ.js +112 -0
  153. package/dist/chunk-U7TFM2JA.js +140 -0
  154. package/dist/chunk-UC2WGUP3.js +627 -0
  155. package/dist/chunk-UWFLYMAR.js +1126 -0
  156. package/dist/chunk-UWSZB7BJ.js +1 -0
  157. package/dist/chunk-UYG5L34B.js +87 -0
  158. package/dist/chunk-VBOXAEPI.js +336 -0
  159. package/dist/chunk-VCDBXW5H.js +188 -0
  160. package/dist/chunk-VCXPHHDS.js +1 -0
  161. package/dist/chunk-VEAYTRPX.js +414 -0
  162. package/dist/chunk-VFCUIDRT.js +1 -0
  163. package/dist/chunk-VQHPQEUH.js +273 -0
  164. package/dist/chunk-VXDZFJGP.js +874 -0
  165. package/dist/chunk-WS56BL3D.js +1 -0
  166. package/dist/chunk-WSW6ZFAQ.js +98 -0
  167. package/dist/chunk-XNUVB5OL.js +375 -0
  168. package/dist/chunk-XOSCY2CW.js +242 -0
  169. package/dist/chunk-XXXEFXY5.js +99 -0
  170. package/dist/chunk-Y5CSSPV4.js +1 -0
  171. package/dist/chunk-Y6DU4J47.js +1 -0
  172. package/dist/chunk-YC6WORFL.js +535 -0
  173. package/dist/chunk-YDGVJETP.js +60 -0
  174. package/dist/chunk-YF4NSSMT.js +366 -0
  175. package/dist/chunk-YG3ZJHGU.js +223 -0
  176. package/dist/chunk-YIJ55TBC.js +1 -0
  177. package/dist/chunk-YKZ4RZZF.js +1 -0
  178. package/dist/chunk-YLLCXTMC.js +37 -0
  179. package/dist/chunk-YTA74UZR.js +1 -0
  180. package/dist/chunk-YTFKPRUK.js +845 -0
  181. package/dist/chunk-Z7H7VSFZ.js +323 -0
  182. package/dist/chunk-ZK2H4O5P.js +1 -0
  183. package/dist/chunk-ZVFE5QGU.js +524 -0
  184. package/dist/coachmark/index.js +4 -0
  185. package/dist/code-block/index.js +4 -0
  186. package/dist/color-field/index.js +7 -0
  187. package/dist/commerce/index.js +20 -0
  188. package/dist/connectivity/index.js +2 -0
  189. package/dist/context-menu/index.js +5 -0
  190. package/dist/copy-button/index.js +3 -0
  191. package/dist/data-grid/index.js +24 -0
  192. package/dist/date-range-picker/index.js +13 -0
  193. package/dist/description-list/index.js +2 -0
  194. package/dist/dialog/index.js +3 -0
  195. package/dist/empty-state/index.js +3 -0
  196. package/dist/error-summary/index.js +3 -0
  197. package/dist/fab/index.js +2 -0
  198. package/dist/file-upload/index.js +9 -0
  199. package/dist/filter-bar/index.js +8 -0
  200. package/dist/flow-run/index.js +14 -0
  201. package/dist/form/index.js +3 -0
  202. package/dist/gallery/index.js +2 -0
  203. package/dist/header/index.js +5 -0
  204. package/dist/hold-to-confirm/index.js +4 -0
  205. package/dist/index.d.ts +22883 -0
  206. package/dist/index.js +158 -0
  207. package/dist/infinite-list/index.js +2 -0
  208. package/dist/inline-edit/index.js +5 -0
  209. package/dist/input/index.js +5 -0
  210. package/dist/interactions/index.js +2 -0
  211. package/dist/invoice/index.js +10 -0
  212. package/dist/kbd/index.js +1 -0
  213. package/dist/keyboard/index.js +3 -0
  214. package/dist/list-row/index.js +7 -0
  215. package/dist/log-viewer/index.js +2 -0
  216. package/dist/map-view/index.js +3 -0
  217. package/dist/markdown/index.js +6 -0
  218. package/dist/metric/index.js +4 -0
  219. package/dist/money/index.js +3 -0
  220. package/dist/nav-drawer/index.js +2 -0
  221. package/dist/navigation/index.js +2 -0
  222. package/dist/notification-center/index.js +16 -0
  223. package/dist/number-roll/index.js +3 -0
  224. package/dist/one-handed/index.js +5 -0
  225. package/dist/os-surfaces/index.js +3 -0
  226. package/dist/otp/index.js +7 -0
  227. package/dist/page-header/index.js +1 -0
  228. package/dist/payment-method/index.js +12 -0
  229. package/dist/payment-status/index.js +16 -0
  230. package/dist/permission/index.js +2 -0
  231. package/dist/picker/index.js +7 -0
  232. package/dist/progress/index.js +5 -0
  233. package/dist/pull-to-refresh/index.js +4 -0
  234. package/dist/radio-group/index.js +6 -0
  235. package/dist/rating/index.js +2 -0
  236. package/dist/receivables/index.js +20 -0
  237. package/dist/refund/index.js +16 -0
  238. package/dist/save-status/index.js +3 -0
  239. package/dist/scroll-area/index.js +1 -0
  240. package/dist/search-bar/index.js +5 -0
  241. package/dist/segmented-control/index.js +5 -0
  242. package/dist/sheet/index.js +4 -0
  243. package/dist/skeleton/index.js +2 -0
  244. package/dist/slider/index.js +5 -0
  245. package/dist/spinner/index.js +2 -0
  246. package/dist/split-button/index.js +7 -0
  247. package/dist/spotlight/index.js +2 -0
  248. package/dist/stage-timeline/index.js +4 -0
  249. package/dist/status-dot/index.js +2 -0
  250. package/dist/stepper/index.js +6 -0
  251. package/dist/streaming-text/index.js +3 -0
  252. package/dist/survey/index.js +19 -0
  253. package/dist/switch/index.js +5 -0
  254. package/dist/system-state/index.js +4 -0
  255. package/dist/tab-bar/index.js +2 -0
  256. package/dist/tabs/index.js +2 -0
  257. package/dist/textarea/index.js +6 -0
  258. package/dist/thinking/index.js +2 -0
  259. package/dist/timeline/index.js +4 -0
  260. package/dist/toast/index.js +2 -0
  261. package/dist/toolbar/index.js +2 -0
  262. package/dist/tour/index.js +11 -0
  263. package/dist/truncation-notice/index.js +2 -0
  264. package/dist/update-notice/index.js +11 -0
  265. package/dist/video-player/index.js +2 -0
  266. package/dist/viewfinder/index.js +5 -0
  267. package/dist/widget/index.js +4 -0
  268. package/dist/wizard/index.js +6 -0
  269. package/package.json +76 -3
@@ -0,0 +1,755 @@
1
+ import { Skeleton } from './chunk-RIBDXGUG.js';
2
+ import { Button } from './chunk-3ARE4TES.js';
3
+ import { Icon } from './chunk-TKVFVF5D.js';
4
+ import * as React2 from 'react';
5
+ import { PanResponder, View, Text, Pressable, ScrollView } from 'react-native';
6
+ import Svg, { Defs, LinearGradient, Stop, Pattern, Rect, ClipPath, G, Text as Text$1, Circle, Path } from 'react-native-svg';
7
+ import { createValuePalette, FULL_WINDOW, normalizeSpec, resolveHeight, isWholePlot, renderChart, tableModel, describeChart, readoutRows, pick, clampWindow, zoomAt, isZoomed, panBy, toCsv, NARROW_WIDTH, TIGHT_WIDTH, sceneToSvg } from '@btndprojects/loam-charts';
8
+ import { createStyleSheet, useLoamTheme, useStyles } from '@btndprojects/loam-theme-native';
9
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
10
+
11
+ function nativeChartPalette(theme) {
12
+ const c = theme.color;
13
+ const chart = theme.chart;
14
+ const tokens = {
15
+ surface: c.surface,
16
+ "surface-2": c.surface2,
17
+ "surface-3": c.surface3,
18
+ text: c.text,
19
+ "text-2": c.text2,
20
+ "text-3": c.text3,
21
+ border: c.border,
22
+ "border-strong": c.borderStrong,
23
+ accent: c.accent,
24
+ "accent-strong": c.accentStrong,
25
+ "accent-soft": c.accentSoft,
26
+ good: c.good,
27
+ warn: c.warn,
28
+ danger: c.danger,
29
+ info: c.info,
30
+ "chart-grid": chart.grid,
31
+ "chart-axis": chart.axis,
32
+ "state-hover-strong": theme.state.hoverStrong
33
+ };
34
+ return createValuePalette({
35
+ series: [chart.c1, chart.c2, chart.c3, chart.c4, chart.c5, chart.c6, ...chart.tier],
36
+ seriesInk: [chart.c1Ink, chart.c2Ink, chart.c3Ink, chart.c4Ink, chart.c5Ink, chart.c6Ink, ...chart.tierInk],
37
+ seriesEdge: chart.edge,
38
+ seq: chart.seq,
39
+ div: chart.div,
40
+ tokens
41
+ });
42
+ }
43
+ function fontOf(theme, role) {
44
+ if (role === "mono") return theme.font.mono;
45
+ if (role === "display") return theme.font.display;
46
+ return theme.font.sans;
47
+ }
48
+ function nodeOf(n, key, theme, clipId) {
49
+ switch (n.k) {
50
+ case "path":
51
+ return /* @__PURE__ */ jsx(
52
+ Path,
53
+ {
54
+ d: n.d,
55
+ fill: n.fill ?? "none",
56
+ stroke: n.stroke,
57
+ strokeWidth: n.sw,
58
+ strokeDasharray: n.dash,
59
+ strokeLinecap: n.cap,
60
+ strokeLinejoin: n.join,
61
+ opacity: n.op
62
+ },
63
+ key
64
+ );
65
+ case "rect":
66
+ return /* @__PURE__ */ jsx(
67
+ Rect,
68
+ {
69
+ x: n.x,
70
+ y: n.y,
71
+ width: n.w,
72
+ height: n.h,
73
+ rx: n.rx,
74
+ fill: n.fill ?? "none",
75
+ stroke: n.stroke,
76
+ strokeWidth: n.sw,
77
+ opacity: n.op
78
+ },
79
+ key
80
+ );
81
+ case "circle":
82
+ return /* @__PURE__ */ jsx(
83
+ Circle,
84
+ {
85
+ cx: n.cx,
86
+ cy: n.cy,
87
+ r: n.r,
88
+ fill: n.fill ?? "none",
89
+ stroke: n.stroke,
90
+ strokeWidth: n.sw,
91
+ opacity: n.op
92
+ },
93
+ key
94
+ );
95
+ case "text":
96
+ return /* @__PURE__ */ jsx(
97
+ Text$1,
98
+ {
99
+ x: n.x,
100
+ y: n.y,
101
+ textAnchor: n.anchor,
102
+ transform: n.transform,
103
+ fontSize: n.size,
104
+ fontWeight: n.weight ? String(n.weight) : void 0,
105
+ fill: n.fill,
106
+ fontFamily: fontOf(theme, n.font),
107
+ letterSpacing: n.tracking,
108
+ opacity: n.op,
109
+ children: n.text
110
+ },
111
+ key
112
+ );
113
+ case "group":
114
+ return /* @__PURE__ */ jsx(G, { clipPath: n.clip === "plot" ? `url(#${clipId})` : void 0, children: n.children.map((c, i) => nodeOf(c, i, theme, clipId)) }, key);
115
+ default:
116
+ return null;
117
+ }
118
+ }
119
+ function SceneNodes({
120
+ scene,
121
+ theme,
122
+ clipId
123
+ }) {
124
+ return /* @__PURE__ */ jsx(Fragment, { children: scene.map((n, i) => nodeOf(n, i, theme, clipId)) });
125
+ }
126
+ function patternBody(kind, edge) {
127
+ switch (kind) {
128
+ case "diagonal":
129
+ return /* @__PURE__ */ jsx(Path, { d: "M0,6 l6,-6 M-1.5,1.5 l3,-3 M4.5,7.5 l3,-3", stroke: edge, strokeWidth: 1.6, opacity: 0.9 });
130
+ case "dots":
131
+ return /* @__PURE__ */ jsx(Circle, { cx: 3, cy: 3, r: 1.35, fill: edge, opacity: 0.9 });
132
+ case "cross":
133
+ return /* @__PURE__ */ jsx(Path, { d: "M0,3 h6 M3,0 v6", stroke: edge, strokeWidth: 1.2, opacity: 0.85 });
134
+ case "horizontal":
135
+ return /* @__PURE__ */ jsx(Path, { d: "M0,2 h6 M0,5 h6", stroke: edge, strokeWidth: 1.3, opacity: 0.85 });
136
+ case "vertical":
137
+ return /* @__PURE__ */ jsx(Path, { d: "M2,0 v6 M5,0 v6", stroke: edge, strokeWidth: 1.3, opacity: 0.85 });
138
+ default:
139
+ return null;
140
+ }
141
+ }
142
+ function SceneDefsNodes({ defs, clipId }) {
143
+ return /* @__PURE__ */ jsxs(Defs, { children: [
144
+ defs.gradients.map((g) => /* @__PURE__ */ jsxs(LinearGradient, { id: g.id, x1: "0", y1: "0", x2: "0", y2: "1", children: [
145
+ /* @__PURE__ */ jsx(Stop, { offset: "0%", stopColor: g.from, stopOpacity: g.fromOpacity }),
146
+ /* @__PURE__ */ jsx(Stop, { offset: "100%", stopColor: g.to, stopOpacity: g.toOpacity })
147
+ ] }, g.id)),
148
+ defs.patterns.map((p) => /* @__PURE__ */ jsxs(Pattern, { id: p.id, width: 6, height: 6, patternUnits: "userSpaceOnUse", children: [
149
+ /* @__PURE__ */ jsx(Rect, { width: 6, height: 6, fill: p.color, opacity: 0.5 }),
150
+ patternBody(p.kind, p.edge)
151
+ ] }, p.id)),
152
+ /* @__PURE__ */ jsx(ClipPath, { id: clipId, children: /* @__PURE__ */ jsx(Rect, { x: defs.clip.x, y: defs.clip.y, width: defs.clip.w, height: defs.clip.h }) })
153
+ ] });
154
+ }
155
+ var UID = 0;
156
+ function svgDocument(frame, uid, width, height, fonts) {
157
+ const body = sceneToSvg(frame.scene, frame.defs, uid);
158
+ const rootVars = `--font-sans:${fonts.sans};--font-mono:${fonts.mono}`;
159
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" style="${escapeAttr(rootVars)}">${body}</svg>`;
160
+ }
161
+ function escapeAttr(value) {
162
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
163
+ }
164
+ function Chart({
165
+ style,
166
+ bare,
167
+ defaultTableOpen = false,
168
+ onExportCsv,
169
+ onExportImage,
170
+ onVisibilityChange,
171
+ onZoomChange,
172
+ ...specInput
173
+ }) {
174
+ const theme = useLoamTheme();
175
+ const { styles } = useStyles(stylesheet);
176
+ const uid = React2.useMemo(() => "lc" + ++UID, []);
177
+ const palette = React2.useMemo(() => nativeChartPalette(theme), [theme]);
178
+ const [width, setWidth] = React2.useState(0);
179
+ const [hidden, setHidden] = React2.useState({});
180
+ const [solo, setSolo] = React2.useState(null);
181
+ const [pinned, setPinned] = React2.useState(null);
182
+ const [tableOpen, setTableOpen] = React2.useState(defaultTableOpen);
183
+ const [zoom, setZoomState] = React2.useState(FULL_WINDOW);
184
+ const spec = React2.useMemo(() => normalizeSpec(specInput), [specInput]);
185
+ const measured = width || 360;
186
+ const narrow = measured < NARROW_WIDTH;
187
+ const tight = measured < TIGHT_WIDTH;
188
+ const height = resolveHeight(spec, measured, narrow);
189
+ const wholePlot = isWholePlot(spec.type);
190
+ const frame = React2.useMemo(
191
+ () => renderChart(spec, {
192
+ width: measured,
193
+ height,
194
+ narrow,
195
+ tight,
196
+ highlight: pinned,
197
+ zoom,
198
+ hidden,
199
+ solo,
200
+ palette,
201
+ uid
202
+ }),
203
+ [spec, measured, height, narrow, tight, pinned, zoom, hidden, solo, palette, uid]
204
+ );
205
+ const table = React2.useMemo(() => tableModel(frame), [frame]);
206
+ const description = React2.useMemo(() => describeChart(frame, { keyboardHint: false }), [frame]);
207
+ const rows = React2.useMemo(
208
+ () => pinned ? readoutRows(frame, pinned, { itemOnly: spec.tooltip.trigger === "item" && !narrow }) : [],
209
+ [frame, pinned, narrow, spec.tooltip.trigger]
210
+ );
211
+ const frameRef = React2.useRef(frame);
212
+ frameRef.current = frame;
213
+ const specRef = React2.useRef(spec);
214
+ specRef.current = spec;
215
+ const scaleRef = React2.useRef(1);
216
+ scaleRef.current = measured / (width || measured);
217
+ const originRef = React2.useRef({ x: 0, y: 0 });
218
+ const scrub = React2.useCallback((lx, ly) => {
219
+ const f = frameRef.current;
220
+ const s = specRef.current;
221
+ const k = scaleRef.current;
222
+ const x = (lx - originRef.current.x) * k;
223
+ const y = (ly - originRef.current.y) * k;
224
+ const mode = s.tooltip.trigger === "item" ? "nearest" : "index";
225
+ const hit = pick(f.items, x, y, isWholePlot(s.type) ? "nearest" : mode, { radius: 56, horizontal: f.horizontal });
226
+ if (hit) {
227
+ setPinned({ si: hit.si, di: hit.di, kind: hit.linkKind });
228
+ s.onSelect?.(hit, s.series[hit.si]);
229
+ }
230
+ }, []);
231
+ const zoomEnabled = !!spec.zoom && !wholePlot;
232
+ const zoomRef = React2.useRef(zoom);
233
+ zoomRef.current = zoom;
234
+ const setZoom = React2.useCallback(
235
+ (next) => {
236
+ const win = clampWindow(next, 0.03);
237
+ setZoomState(win);
238
+ onZoomChange?.(win);
239
+ },
240
+ [onZoomChange]
241
+ );
242
+ const resetZoom = React2.useCallback(() => setZoom(FULL_WINDOW), [setZoom]);
243
+ const pinchRef = React2.useRef(null);
244
+ const lastTapRef = React2.useRef(0);
245
+ const pinchGeometry = React2.useCallback((touches) => {
246
+ const [a, b] = touches;
247
+ if (!a || !b) return null;
248
+ const dist = Math.hypot(a.pageX - b.pageX, a.pageY - b.pageY);
249
+ const f = frameRef.current;
250
+ const plot = f.plot;
251
+ const midX = (a.pageX + b.pageX) / 2 - originRef.current.x - plot.x;
252
+ return { dist, anchor: plot.w ? Math.min(1, Math.max(0, midX / plot.w)) : 0.5 };
253
+ }, []);
254
+ const responder = React2.useMemo(
255
+ () => PanResponder.create({
256
+ onStartShouldSetPanResponder: () => true,
257
+ // Axis intent, and the whole reason a chart can live inside a
258
+ // ScrollView: a dominantly vertical drag is the page's, and this
259
+ // responder never claims it. A chart that eats the scroll traps the
260
+ // reader mid-page; one that claims no touch at all is unreadable.
261
+ // Two fingers are always ours: nothing else on the page pinches.
262
+ onMoveShouldSetPanResponder: (e, g) => zoomEnabled && e.nativeEvent.touches.length >= 2 || Math.abs(g.dx) > Math.abs(g.dy) * 1.25 && Math.abs(g.dx) > 6,
263
+ onPanResponderGrant: (e) => {
264
+ const now = Date.now();
265
+ if (zoomEnabled && now - lastTapRef.current < 300 && isZoomed(zoomRef.current)) {
266
+ lastTapRef.current = 0;
267
+ resetZoom();
268
+ return;
269
+ }
270
+ lastTapRef.current = now;
271
+ pinchRef.current = null;
272
+ scrub(e.nativeEvent.locationX, e.nativeEvent.locationY);
273
+ },
274
+ onPanResponderMove: (e) => {
275
+ const touches = e.nativeEvent.touches;
276
+ if (zoomEnabled && touches.length >= 2) {
277
+ const geo = pinchGeometry(touches);
278
+ if (!geo) return;
279
+ if (!pinchRef.current) {
280
+ pinchRef.current = { dist: geo.dist, win: zoomRef.current, anchor: geo.anchor };
281
+ return;
282
+ }
283
+ const start = pinchRef.current;
284
+ if (start.dist <= 0) return;
285
+ setZoom(zoomAt(start.win, start.dist / geo.dist, start.anchor, 0.03));
286
+ return;
287
+ }
288
+ pinchRef.current = null;
289
+ scrub(e.nativeEvent.locationX, e.nativeEvent.locationY);
290
+ },
291
+ onPanResponderRelease: () => {
292
+ pinchRef.current = null;
293
+ },
294
+ onPanResponderTerminate: () => {
295
+ pinchRef.current = null;
296
+ }
297
+ }),
298
+ [scrub, zoomEnabled, resetZoom, setZoom, pinchGeometry]
299
+ );
300
+ const legendEntries = React2.useMemo(() => {
301
+ const itemLegend = ["pie", "donut", "polarArea", "funnel", "pyramid", "radialBar", "waffle"].includes(spec.type);
302
+ if (wholePlot && !itemLegend) return [];
303
+ if (wholePlot) {
304
+ const raw = spec.series[0]?.data || spec.data;
305
+ return (Array.isArray(raw) ? raw : []).map((d, i) => ({
306
+ id: "i" + i,
307
+ label: (d && typeof d === "object" ? d.label : null) || (spec.xAxis.data || [])[i] || "Item " + (i + 1),
308
+ color: palette.series(i),
309
+ edge: palette.edge(i),
310
+ off: false,
311
+ index: i
312
+ }));
313
+ }
314
+ return spec.series.map((z, i) => ({
315
+ id: z.id,
316
+ label: z.label || z.id,
317
+ color: z.color || palette.series(i),
318
+ edge: z.color || palette.edge(i),
319
+ off: !!hidden[z.id] || solo != null && solo !== i || !!z.hidden,
320
+ index: i
321
+ }));
322
+ }, [spec, wholePlot, hidden, solo, palette]);
323
+ const toggle = (id, index) => {
324
+ if (wholePlot) {
325
+ setPinned((p) => p && p.di === index ? null : { si: 0, di: index });
326
+ return;
327
+ }
328
+ setSolo(null);
329
+ setHidden((h) => {
330
+ const next = { ...h, [id]: !h[id] };
331
+ if (!spec.series.some((s) => !next[s.id] && !s.hidden)) return h;
332
+ onVisibilityChange?.(Object.keys(next).filter((k) => next[k]));
333
+ return next;
334
+ });
335
+ };
336
+ const onPlotKeyDown = React2.useCallback(
337
+ (event) => {
338
+ const n = frame.axisLabels.length;
339
+ if (!n) return;
340
+ const cur = { si: pinned?.si ?? 0, di: pinned?.di ?? 0 };
341
+ let { si, di } = cur;
342
+ const step = event.shiftKey ? Math.max(1, Math.round(n / 10)) : 1;
343
+ switch (event.key) {
344
+ case "ArrowRight":
345
+ di = Math.min(n - 1, di + step);
346
+ break;
347
+ case "ArrowLeft":
348
+ di = Math.max(0, di - step);
349
+ break;
350
+ case "ArrowUp":
351
+ si = Math.max(0, si - 1);
352
+ break;
353
+ case "ArrowDown":
354
+ si = Math.min(spec.series.length - 1, si + 1);
355
+ break;
356
+ case "Home":
357
+ di = 0;
358
+ break;
359
+ case "End":
360
+ di = n - 1;
361
+ break;
362
+ case "Escape":
363
+ setPinned(null);
364
+ return;
365
+ case "t":
366
+ case "T":
367
+ setTableOpen((v) => !v);
368
+ return;
369
+ // The zoom keys exist only where zooming does, so a key that would do
370
+ // nothing is never promised.
371
+ case "+":
372
+ case "=":
373
+ if (zoomEnabled) setZoom(zoomAt(zoomRef.current, 1 / 1.3, 0.5, 0.03));
374
+ return;
375
+ case "-":
376
+ case "_":
377
+ if (zoomEnabled) setZoom(zoomAt(zoomRef.current, 1.3, 0.5, 0.03));
378
+ return;
379
+ case "0":
380
+ if (zoomEnabled) resetZoom();
381
+ return;
382
+ case "[":
383
+ if (zoomEnabled) setZoom(panBy(zoomRef.current, -0.1));
384
+ return;
385
+ case "]":
386
+ if (zoomEnabled) setZoom(panBy(zoomRef.current, 0.1));
387
+ return;
388
+ default:
389
+ return;
390
+ }
391
+ event.preventDefault?.();
392
+ setPinned({ si, di });
393
+ },
394
+ [frame.axisLabels.length, pinned, spec.series.length, zoomEnabled, setZoom, resetZoom]
395
+ );
396
+ const anyData = spec.series.some((z) => (z.data || z.points || []).length) || spec.value != null || !!spec.data || !!spec.items || !!spec.matrix || !!spec.rows || !!spec.columns || !!spec.categories;
397
+ const state = spec.state === "loading" || spec.state === "error" ? spec.state : frame.problem ? "error" : !anyData || spec.state === "empty" ? "empty" : "ready";
398
+ const onPlotLayout = (e) => {
399
+ const w = e.nativeEvent.layout.width;
400
+ if (Math.abs(w - width) > 0.5) setWidth(w);
401
+ };
402
+ const showReadout = !wholePlot && spec.tooltip.trigger !== "none" && spec.readout !== false;
403
+ return /* @__PURE__ */ jsxs(
404
+ View,
405
+ {
406
+ style: [
407
+ bare ? styles.bare : styles.card,
408
+ !(spec.title || spec.subtitle) && styles.headless,
409
+ style
410
+ ],
411
+ children: [
412
+ (spec.title || spec.subtitle) && /* @__PURE__ */ jsxs(View, { style: styles.header, children: [
413
+ /* @__PURE__ */ jsxs(View, { style: styles.headerText, children: [
414
+ spec.title ? /* @__PURE__ */ jsx(Text, { style: styles.title, children: spec.title }) : null,
415
+ spec.subtitle ? /* @__PURE__ */ jsx(Text, { style: styles.subtitle, children: spec.subtitle }) : null
416
+ ] }),
417
+ /* @__PURE__ */ jsxs(View, { style: styles.toolbar, children: [
418
+ /* @__PURE__ */ jsx(
419
+ ToolButton,
420
+ {
421
+ icon: "grid",
422
+ label: tableOpen ? "Hide the data table" : "Show the data table",
423
+ active: tableOpen,
424
+ onPress: () => setTableOpen((v) => !v)
425
+ }
426
+ ),
427
+ onExportCsv ? /* @__PURE__ */ jsx(
428
+ ToolButton,
429
+ {
430
+ icon: "download",
431
+ label: "Export the visible window as CSV",
432
+ onPress: () => onExportCsv(toCsv(table), (spec.title || "chart").replace(/[^\w.-]+/g, "-") + ".csv")
433
+ }
434
+ ) : null,
435
+ onExportImage ? /* @__PURE__ */ jsx(
436
+ ToolButton,
437
+ {
438
+ icon: "image",
439
+ label: "Share the chart as an image",
440
+ onPress: () => onExportImage(
441
+ svgDocument(frame, uid, measured, height, {
442
+ sans: theme.font.sans,
443
+ mono: theme.font.mono
444
+ }),
445
+ (spec.title || "chart").replace(/[^\w.-]+/g, "-") + ".svg"
446
+ )
447
+ }
448
+ ) : null
449
+ ] })
450
+ ] }),
451
+ showReadout && state === "ready" ? /* @__PURE__ */ jsx(View, { style: styles.readout, accessibilityLiveRegion: "polite", children: rows.length === 0 ? /* @__PURE__ */ jsx(Text, { style: styles.readoutHint, children: "Drag across the chart to read values." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
452
+ /* @__PURE__ */ jsxs(View, { style: styles.readoutBody, children: [
453
+ /* @__PURE__ */ jsx(Text, { style: styles.readoutLabel, children: frame.axisLabels[pinned?.di ?? 0] }),
454
+ /* @__PURE__ */ jsx(View, { style: styles.readoutRows, children: rows.map((r, i) => /* @__PURE__ */ jsxs(View, { style: styles.readoutRow, children: [
455
+ /* @__PURE__ */ jsx(View, { style: [styles.swatch, { backgroundColor: r.color, borderColor: r.edge ?? r.color }] }),
456
+ /* @__PURE__ */ jsxs(Text, { style: styles.readoutName, children: [
457
+ r.label,
458
+ ":"
459
+ ] }),
460
+ /* @__PURE__ */ jsx(Text, { style: styles.readoutValue, children: r.value })
461
+ ] }, i)) })
462
+ ] }),
463
+ /* @__PURE__ */ jsx(
464
+ Pressable,
465
+ {
466
+ accessibilityRole: "button",
467
+ accessibilityLabel: "Clear the reading",
468
+ style: ({ pressed }) => [styles.readoutClear, pressed && styles.pressed],
469
+ onPress: () => setPinned(null),
470
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", size: 15, color: theme.color.text3 })
471
+ }
472
+ )
473
+ ] }) }) : null,
474
+ legendEntries.length > 1 ? /* @__PURE__ */ jsxs(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, contentContainerStyle: styles.legend, children: [
475
+ legendEntries.map((en) => /* @__PURE__ */ jsxs(
476
+ Pressable,
477
+ {
478
+ accessibilityRole: "button",
479
+ accessibilityState: { selected: !en.off },
480
+ accessibilityLabel: en.label,
481
+ hitSlop: { top: theme.space[6], bottom: theme.space[6], left: theme.space[4], right: theme.space[4] },
482
+ style: ({ pressed }) => [styles.chip, en.off && styles.chipOff, pressed && styles.pressed],
483
+ onPress: () => toggle(en.id, en.index),
484
+ onLongPress: () => setSolo((s) => s === en.index ? null : en.index),
485
+ children: [
486
+ /* @__PURE__ */ jsx(
487
+ View,
488
+ {
489
+ style: [
490
+ styles.swatch,
491
+ // Outlined in the series edge: the pastel fill alone does
492
+ // not clear 3:1 on the card.
493
+ { backgroundColor: en.color, borderColor: en.edge },
494
+ // The web twin greys the chip with `grayscale(1)`; RN has no
495
+ // filter, so the muting is the system's own disabled opacity
496
+ // rather than a hand-picked number. Only the *swatch* dims —
497
+ // see the `chipOff` note for why the label does not.
498
+ en.off && { opacity: theme.state.disabledOpacity }
499
+ ]
500
+ }
501
+ ),
502
+ /* @__PURE__ */ jsx(Text, { style: [styles.chipText, en.off && styles.chipTextOff], children: en.label })
503
+ ]
504
+ },
505
+ en.id
506
+ )),
507
+ /* @__PURE__ */ jsx(Text, { style: styles.legendHint, children: "tap to filter \xB7 hold to solo" })
508
+ ] }) : null,
509
+ /* @__PURE__ */ jsx(View, { style: styles.plotWrap, children: /* @__PURE__ */ jsxs(View, { onLayout: onPlotLayout, style: styles.plot, children: [
510
+ state === "loading" ? /* @__PURE__ */ jsx(
511
+ View,
512
+ {
513
+ accessible: true,
514
+ accessibilityRole: "progressbar",
515
+ accessibilityLabel: "Loading chart",
516
+ style: [styles.skeleton, { height }],
517
+ children: Array.from({ length: 10 }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { style: { flex: 1, height: 28 + i * 37 % 62 + "%" }, radius: "sm" }, i))
518
+ }
519
+ ) : null,
520
+ state === "empty" || state === "error" ? /* @__PURE__ */ jsxs(View, { style: [styles.message, { minHeight: Math.max(180, height * 0.7) }], children: [
521
+ /* @__PURE__ */ jsx(View, { style: styles.messageIcon, children: /* @__PURE__ */ jsx(Icon, { name: state === "error" ? "alert-triangle" : "chart-bar", size: 22, color: theme.color.text3 }) }),
522
+ /* @__PURE__ */ jsx(Text, { style: styles.messageTitle, children: state === "error" ? frame.problem?.title || spec.errorTitle || "That data did not load" : spec.emptyTitle || "No data in this range" }),
523
+ /* @__PURE__ */ jsx(Text, { style: styles.messageText, children: state === "error" ? frame.problem?.text || spec.errorText || "Nothing was drawn, because a chart of nothing looks exactly like a chart of zero." : spec.emptyText || "The series exist \u2014 the window is empty. Widen the range or clear a filter." }),
524
+ state === "error" && spec.onRetry ? /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onPress: spec.onRetry, children: "Try again" }) : null,
525
+ state === "empty" && spec.onReset ? /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onPress: spec.onReset, children: "Clear the filter" }) : null
526
+ ] }) : null,
527
+ state === "ready" ? /* @__PURE__ */ jsx(
528
+ View,
529
+ {
530
+ ...responder.panHandlers,
531
+ accessible: true,
532
+ focusable: zoomEnabled || void 0,
533
+ accessibilityRole: "image",
534
+ accessibilityLabel: description,
535
+ ...{ onKeyDown: onPlotKeyDown },
536
+ onLayout: (e) => {
537
+ originRef.current = { x: 0, y: 0 };
538
+ },
539
+ children: /* @__PURE__ */ jsxs(Svg, { width: "100%", height, viewBox: `0 0 ${measured} ${height}`, children: [
540
+ /* @__PURE__ */ jsx(SceneDefsNodes, { defs: frame.defs, clipId: uid + "-clip" }),
541
+ /* @__PURE__ */ jsx(SceneNodes, { scene: frame.scene, theme, clipId: uid + "-clip" })
542
+ ] })
543
+ }
544
+ ) : null,
545
+ zoomEnabled && isZoomed(zoom) ? /* @__PURE__ */ jsx(View, { style: styles.zoomPill, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onPress: resetZoom, accessibilityLabel: "Reset the zoom", children: "Reset zoom" }) }) : null
546
+ ] }) }),
547
+ (spec.footnote || spec.source) && /* @__PURE__ */ jsx(View, { style: styles.caption, children: /* @__PURE__ */ jsx(Text, { style: styles.captionText, children: [spec.footnote, spec.source && "Source: " + spec.source].filter(Boolean).join(" \xB7 ") }) }),
548
+ tableOpen ? /* @__PURE__ */ jsx(ChartDataTable, { model: table, title: spec.title }) : null
549
+ ]
550
+ }
551
+ );
552
+ }
553
+ function ToolButton({
554
+ icon,
555
+ label,
556
+ active,
557
+ onPress
558
+ }) {
559
+ const theme = useLoamTheme();
560
+ const { styles } = useStyles(stylesheet);
561
+ return /* @__PURE__ */ jsx(
562
+ Pressable,
563
+ {
564
+ accessibilityRole: "button",
565
+ accessibilityLabel: label,
566
+ accessibilityState: { selected: !!active },
567
+ style: ({ pressed }) => [styles.toolButton, active && styles.toolButtonActive, pressed && styles.pressed],
568
+ onPress,
569
+ children: /* @__PURE__ */ jsx(Icon, { name: icon, size: 17, color: active ? theme.color.accentStrong : theme.color.text2 })
570
+ }
571
+ );
572
+ }
573
+ function ChartDataTable({ model, title }) {
574
+ const { styles } = useStyles(stylesheet);
575
+ return /* @__PURE__ */ jsxs(View, { style: styles.tableWrap, children: [
576
+ /* @__PURE__ */ jsx(Text, { style: styles.tableCaption, children: (title || "Chart data") + " \u2014 " + model.rows.length + " rows" }),
577
+ /* @__PURE__ */ jsx(ScrollView, { horizontal: true, focusable: true, showsHorizontalScrollIndicator: true, children: /* @__PURE__ */ jsx(ScrollView, { style: styles.tableScroll, focusable: true, nestedScrollEnabled: true, children: /* @__PURE__ */ jsxs(View, { accessibilityRole: "none", children: [
578
+ /* @__PURE__ */ jsx(View, { style: [styles.tr, styles.thead], children: model.head.map((h, i) => /* @__PURE__ */ jsx(Text, { style: [styles.th, i === 0 && styles.thFirst], children: h }, i)) }),
579
+ model.rows.map((r, i) => /* @__PURE__ */ jsx(View, { style: [styles.tr, i % 2 ? styles.trAlt : null], children: r.map((c, j) => /* @__PURE__ */ jsx(Text, { style: [styles.td, j === 0 && styles.tdFirst], children: c }, j)) }, i))
580
+ ] }) }) })
581
+ ] });
582
+ }
583
+ var stylesheet = createStyleSheet((theme) => ({
584
+ zoomPill: {
585
+ alignItems: "flex-end",
586
+ paddingTop: theme.space[8]
587
+ },
588
+ card: {
589
+ borderRadius: theme.radius.xl,
590
+ borderWidth: theme.borderWidth.base,
591
+ borderColor: theme.color.border,
592
+ backgroundColor: theme.color.surface,
593
+ overflow: "hidden"
594
+ },
595
+ bare: { backgroundColor: "transparent" },
596
+ // Without a header nothing else carries the top inset, and the readout or the
597
+ // legend would sit on the card's edge.
598
+ headless: { paddingTop: theme.space.s4 },
599
+ header: {
600
+ flexDirection: "row",
601
+ alignItems: "flex-start",
602
+ gap: theme.space.s3,
603
+ paddingHorizontal: theme.space.s4,
604
+ paddingTop: theme.space.s4,
605
+ paddingBottom: theme.space.s3
606
+ },
607
+ headerText: { flex: 1, minWidth: 0, gap: theme.space.s1 },
608
+ title: { fontFamily: theme.font.sans, fontSize: theme.fontSize[15], fontWeight: theme.fontWeight.extrabold, color: theme.color.text },
609
+ subtitle: { fontFamily: theme.font.sans, fontSize: theme.fontSize[13], color: theme.color.text3, lineHeight: theme.space[18] },
610
+ toolbar: { flexDirection: "row", gap: theme.space.s2 },
611
+ toolButton: {
612
+ width: theme.tapMin,
613
+ height: theme.tapMin,
614
+ alignItems: "center",
615
+ justifyContent: "center",
616
+ borderRadius: theme.radius.sm,
617
+ borderWidth: theme.borderWidth.base,
618
+ borderColor: theme.color.borderStrong,
619
+ backgroundColor: theme.color.surface2
620
+ },
621
+ // The active edge is a graphical object under SC 1.4.11, so it takes the
622
+ // *measured* accent rather than the brand input — the same colour the focus
623
+ // ring uses, for the same reason.
624
+ toolButtonActive: { borderColor: theme.focus.ringColor, backgroundColor: theme.color.accentSoft },
625
+ // Press feedback for every chart control. Web leans on the unlayered global
626
+ // `button:hover { filter: var(--hover-filter) }`; native has no equivalent,
627
+ // so without this a tap on the toolbar, a legend chip or the readout's clear
628
+ // does nothing visible. `state.pressed` is a translucent layer, so it
629
+ // composites over whatever fill the control already carries rather than
630
+ // replacing it.
631
+ pressed: { backgroundColor: theme.state.pressed },
632
+ readout: {
633
+ flexDirection: "row",
634
+ alignItems: "center",
635
+ gap: theme.space.s3,
636
+ minHeight: theme.space[46],
637
+ marginHorizontal: theme.space.s4,
638
+ marginBottom: theme.space.s3,
639
+ paddingHorizontal: theme.space.s3,
640
+ paddingVertical: theme.space.s2,
641
+ borderRadius: theme.radius.md,
642
+ borderWidth: theme.borderWidth.base,
643
+ borderColor: theme.color.border,
644
+ backgroundColor: theme.color.surface2
645
+ },
646
+ readoutBody: { flex: 1, minWidth: 0, gap: theme.space.s1 },
647
+ readoutHint: { fontFamily: theme.font.sans, fontSize: theme.fontSize[13], color: theme.color.text3 },
648
+ readoutLabel: {
649
+ fontFamily: theme.font.mono,
650
+ fontSize: theme.fontSize[11],
651
+ letterSpacing: theme.tracking.wide,
652
+ color: theme.color.text3
653
+ },
654
+ readoutRows: {
655
+ flexDirection: "row",
656
+ flexWrap: "wrap",
657
+ columnGap: theme.space.s3,
658
+ rowGap: theme.space.s1
659
+ },
660
+ readoutRow: { flexDirection: "row", alignItems: "center", gap: theme.space[6] },
661
+ readoutName: { fontFamily: theme.font.sans, fontSize: theme.fontSize[13], color: theme.color.text2 },
662
+ readoutValue: { fontFamily: theme.font.sans, fontSize: theme.fontSize[13], fontWeight: theme.fontWeight.bold, color: theme.color.text },
663
+ readoutClear: {
664
+ width: theme.space[38],
665
+ height: theme.space[38],
666
+ alignItems: "center",
667
+ justifyContent: "center",
668
+ borderRadius: theme.radius.sm,
669
+ borderWidth: theme.borderWidth.base,
670
+ borderColor: theme.color.borderStrong,
671
+ backgroundColor: theme.color.surface
672
+ },
673
+ legend: { flexDirection: "row", alignItems: "center", gap: theme.space.s2, paddingHorizontal: theme.space.s4, paddingBottom: theme.space.s3 },
674
+ chip: {
675
+ flexDirection: "row",
676
+ alignItems: "center",
677
+ gap: theme.space[7],
678
+ height: theme.space[32],
679
+ paddingHorizontal: theme.space.s3,
680
+ borderRadius: theme.radius.full,
681
+ borderWidth: theme.borderWidth.base,
682
+ borderColor: theme.color.borderStrong,
683
+ backgroundColor: theme.color.surface2
684
+ },
685
+ // No `opacity` here. An off chip is already carried on three axes — `border` for
686
+ // `borderStrong`, transparent for `surface2`, and `text3` for `text2` in `chipTextOff`
687
+ // — and dimming on top of that dims a *measured* ink: `text3` clears AA against the
688
+ // surface with no headroom, so 60% puts an interactive control under it.
689
+ chipOff: { borderColor: theme.color.border, backgroundColor: "transparent" },
690
+ chipText: { fontFamily: theme.font.sans, fontSize: theme.fontSize[13], fontWeight: theme.fontWeight.semibold, color: theme.color.text2 },
691
+ chipTextOff: { color: theme.color.text3 },
692
+ legendHint: { fontFamily: theme.font.mono, fontSize: theme.fontSize[11], color: theme.color.text3, paddingLeft: theme.space.s1 },
693
+ swatch: { width: theme.space[9], height: theme.space[9], borderRadius: theme.radius[3], borderWidth: 1 },
694
+ // The inset lives on a wrapper so the plot's own `onLayout` width is the
695
+ // width the scene is drawn at — padding on the measured view would put the
696
+ // viewBox and the drawn box out of step.
697
+ plotWrap: { paddingHorizontal: theme.space.s4, paddingBottom: theme.space.s3 },
698
+ plot: { position: "relative", minWidth: 0 },
699
+ skeleton: { flexDirection: "row", alignItems: "flex-end", gap: theme.space[8] },
700
+ message: { alignItems: "center", justifyContent: "center", gap: theme.space.s2, paddingHorizontal: theme.space.s5, paddingVertical: theme.space.s6 },
701
+ messageIcon: {
702
+ width: theme.tapMin,
703
+ height: theme.tapMin,
704
+ alignItems: "center",
705
+ justifyContent: "center",
706
+ borderRadius: theme.radius.md,
707
+ backgroundColor: theme.color.surface2
708
+ },
709
+ messageTitle: { fontFamily: theme.font.sans, fontSize: theme.fontSize[14], fontWeight: theme.fontWeight.bold, color: theme.color.text, textAlign: "center" },
710
+ messageText: { fontFamily: theme.font.sans, fontSize: theme.fontSize[13], lineHeight: theme.space[19], color: theme.color.text3, textAlign: "center" },
711
+ caption: {
712
+ borderTopWidth: theme.borderWidth.base,
713
+ borderTopColor: theme.color.border,
714
+ backgroundColor: theme.color.surface2,
715
+ paddingHorizontal: theme.space.s4,
716
+ paddingVertical: theme.space.s2
717
+ },
718
+ captionText: { fontFamily: theme.font.mono, fontSize: theme.fontSize[11], color: theme.color.text3 },
719
+ tableWrap: { borderTopWidth: theme.borderWidth.base, borderTopColor: theme.color.border, maxHeight: theme.space[256] },
720
+ tableScroll: { maxHeight: theme.space[224] },
721
+ tableCaption: {
722
+ fontFamily: theme.font.mono,
723
+ fontSize: theme.fontSize[11],
724
+ letterSpacing: theme.tracking.wide,
725
+ color: theme.color.text3,
726
+ paddingHorizontal: theme.space.s4,
727
+ paddingVertical: theme.space.s2
728
+ },
729
+ tr: { flexDirection: "row" },
730
+ trAlt: { backgroundColor: theme.color.surface2 },
731
+ thead: { backgroundColor: theme.color.surface2 },
732
+ th: {
733
+ minWidth: theme.space[96],
734
+ paddingHorizontal: theme.space[3],
735
+ paddingVertical: theme.space[2],
736
+ fontFamily: theme.font.sans,
737
+ fontSize: theme.fontSize[13],
738
+ fontWeight: theme.fontWeight.bold,
739
+ color: theme.color.text2,
740
+ textAlign: "right"
741
+ },
742
+ thFirst: { textAlign: "left", minWidth: theme.space[120] },
743
+ td: {
744
+ minWidth: theme.space[96],
745
+ paddingHorizontal: theme.space[3],
746
+ paddingVertical: theme.space[2],
747
+ fontFamily: theme.font.sans,
748
+ fontSize: theme.fontSize[13],
749
+ color: theme.color.text,
750
+ textAlign: "right"
751
+ },
752
+ tdFirst: { textAlign: "left", minWidth: theme.space[120], fontWeight: theme.fontWeight.semibold, color: theme.color.text2 }
753
+ }));
754
+
755
+ export { Chart, ChartDataTable, SceneDefsNodes, SceneNodes, nativeChartPalette };