@lt-three/love3 0.0.0 → 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 (377) hide show
  1. package/README.md +85 -1
  2. package/dist/native/analytics/animation.d.ts +4 -0
  3. package/dist/native/analytics/animation.js +6 -0
  4. package/dist/native/analytics/chart.types.d.ts +25 -0
  5. package/dist/native/analytics/chart.types.js +0 -0
  6. package/dist/native/analytics/line-chart.d.ts +4 -0
  7. package/dist/native/analytics/line-chart.ios.d.ts +4 -0
  8. package/dist/native/analytics/line-chart.ios.js +24 -0
  9. package/dist/native/analytics/line-chart.js +33 -0
  10. package/dist/native/analytics/metric-card.d.ts +10 -0
  11. package/dist/native/analytics/metric-card.js +29 -0
  12. package/dist/native/analytics/multi-line-chart.d.ts +18 -0
  13. package/dist/native/analytics/multi-line-chart.js +216 -0
  14. package/dist/native/analytics/pie-chart.d.ts +15 -0
  15. package/dist/native/analytics/pie-chart.ios.d.ts +4 -0
  16. package/dist/native/analytics/pie-chart.ios.js +24 -0
  17. package/dist/native/analytics/pie-chart.js +92 -0
  18. package/dist/native/analytics/single-number.d.ts +19 -0
  19. package/dist/native/analytics/single-number.js +101 -0
  20. package/dist/native/atom/avatar.d.ts +21 -0
  21. package/dist/native/atom/avatar.js +46 -0
  22. package/dist/native/atom/badge.d.ts +28 -0
  23. package/dist/native/atom/badge.js +51 -0
  24. package/dist/native/atom/button.d.ts +46 -0
  25. package/dist/native/atom/button.js +109 -0
  26. package/dist/native/atom/checkbox.d.ts +21 -0
  27. package/dist/native/atom/checkbox.js +47 -0
  28. package/dist/native/atom/colored-value.d.ts +12 -0
  29. package/dist/native/atom/colored-value.js +24 -0
  30. package/dist/native/atom/image.d.ts +21 -0
  31. package/dist/native/atom/image.js +72 -0
  32. package/dist/native/atom/label.d.ts +18 -0
  33. package/dist/native/atom/label.js +33 -0
  34. package/dist/native/atom/link.d.ts +18 -0
  35. package/dist/native/atom/link.js +25 -0
  36. package/dist/native/atom/readout.d.ts +22 -0
  37. package/dist/native/atom/readout.js +39 -0
  38. package/dist/native/atom/search-input.d.ts +12 -0
  39. package/dist/native/atom/search-input.js +36 -0
  40. package/dist/native/atom/select.d.ts +54 -0
  41. package/dist/native/atom/select.js +200 -0
  42. package/dist/native/atom/separator.d.ts +12 -0
  43. package/dist/native/atom/separator.js +16 -0
  44. package/dist/native/atom/skeleton.d.ts +12 -0
  45. package/dist/native/atom/skeleton.js +39 -0
  46. package/dist/native/atom/slider.d.ts +32 -0
  47. package/dist/native/atom/slider.js +75 -0
  48. package/dist/native/atom/spinner.d.ts +15 -0
  49. package/dist/native/atom/spinner.js +25 -0
  50. package/dist/native/atom/switch.d.ts +18 -0
  51. package/dist/native/atom/switch.js +52 -0
  52. package/dist/native/atom/symbol.d.ts +16 -0
  53. package/dist/native/atom/symbol.js +38 -0
  54. package/dist/native/atom/symbol.types.d.ts +25 -0
  55. package/dist/native/atom/symbol.types.js +49 -0
  56. package/dist/native/atom/text-field.d.ts +49 -0
  57. package/dist/native/atom/text-field.js +56 -0
  58. package/dist/native/atom/text.d.ts +2 -0
  59. package/dist/native/atom/text.js +3 -0
  60. package/dist/native/atom/theme-toggle.d.ts +21 -0
  61. package/dist/native/atom/theme-toggle.js +36 -0
  62. package/dist/native/atom/timezone-select.d.ts +16 -0
  63. package/dist/native/atom/timezone-select.js +28 -0
  64. package/dist/native/atom/typography.d.ts +13 -0
  65. package/dist/native/atom/typography.js +28 -0
  66. package/dist/native/base/button.d.ts +61 -0
  67. package/dist/native/base/button.js +92 -0
  68. package/dist/native/base/form.d.ts +72 -0
  69. package/dist/native/base/form.js +113 -0
  70. package/dist/native/base/glass-card.d.ts +15 -0
  71. package/dist/native/base/glass-card.js +38 -0
  72. package/dist/native/base/input.d.ts +29 -0
  73. package/dist/native/base/input.js +27 -0
  74. package/dist/native/base/sheet.d.ts +71 -0
  75. package/dist/native/base/sheet.js +187 -0
  76. package/dist/native/base/text.d.ts +14 -0
  77. package/dist/native/base/text.js +53 -0
  78. package/dist/native/form/checkbox.d.ts +13 -0
  79. package/dist/native/form/checkbox.js +55 -0
  80. package/dist/native/form/date-picker.d.ts +23 -0
  81. package/dist/native/form/date-picker.js +119 -0
  82. package/dist/native/form/form.d.ts +22 -0
  83. package/dist/native/form/form.js +44 -0
  84. package/dist/native/form/radio-group.d.ts +23 -0
  85. package/dist/native/form/radio-group.js +88 -0
  86. package/dist/native/form/select.d.ts +13 -0
  87. package/dist/native/form/select.js +45 -0
  88. package/dist/native/form/slider.d.ts +14 -0
  89. package/dist/native/form/slider.js +42 -0
  90. package/dist/native/form/switch.d.ts +9 -0
  91. package/dist/native/form/switch.js +59 -0
  92. package/dist/native/form/text-area.d.ts +11 -0
  93. package/dist/native/form/text-area.js +50 -0
  94. package/dist/native/form/text-input.d.ts +18 -0
  95. package/dist/native/form/text-input.js +90 -0
  96. package/dist/native/index.d.ts +1 -0
  97. package/dist/native/index.js +0 -0
  98. package/dist/native/layout/accordion.d.ts +25 -0
  99. package/dist/native/layout/accordion.js +54 -0
  100. package/dist/native/layout/card.d.ts +47 -0
  101. package/dist/native/layout/card.js +57 -0
  102. package/dist/native/layout/description.d.ts +16 -0
  103. package/dist/native/layout/description.js +32 -0
  104. package/dist/native/layout/error.d.ts +21 -0
  105. package/dist/native/layout/error.js +44 -0
  106. package/dist/native/layout/information.d.ts +25 -0
  107. package/dist/native/layout/information.js +35 -0
  108. package/dist/native/layout/locked.d.ts +38 -0
  109. package/dist/native/layout/locked.js +94 -0
  110. package/dist/native/layout/modal.d.ts +25 -0
  111. package/dist/native/layout/modal.js +93 -0
  112. package/dist/native/layout/not-found.d.ts +17 -0
  113. package/dist/native/layout/not-found.js +30 -0
  114. package/dist/native/layout/page-frame.d.ts +23 -0
  115. package/dist/native/layout/page-frame.js +46 -0
  116. package/dist/native/layout/panel.d.ts +18 -0
  117. package/dist/native/layout/panel.js +23 -0
  118. package/dist/native/layout/reorderable-list.d.ts +42 -0
  119. package/dist/native/layout/reorderable-list.js +52 -0
  120. package/dist/native/layout/sheet.d.ts +30 -0
  121. package/dist/native/layout/sheet.js +46 -0
  122. package/dist/native/layout/simple-dialog.d.ts +16 -0
  123. package/dist/native/layout/simple-dialog.js +20 -0
  124. package/dist/native/layout/simple-table.d.ts +50 -0
  125. package/dist/native/layout/simple-table.js +75 -0
  126. package/dist/native/layout/tab-bar.d.ts +33 -0
  127. package/dist/native/layout/tab-bar.js +48 -0
  128. package/dist/native/layout/tabs.d.ts +28 -0
  129. package/dist/native/layout/tabs.js +54 -0
  130. package/dist/native/layout/tuning-panel.d.ts +24 -0
  131. package/dist/native/layout/tuning-panel.js +246 -0
  132. package/dist/native/lib/autocomplete.d.ts +7 -0
  133. package/dist/native/lib/autocomplete.js +34 -0
  134. package/dist/native/lib/breakpoints.d.ts +14 -0
  135. package/dist/native/lib/breakpoints.js +17 -0
  136. package/dist/native/lib/chart-palette.d.ts +8 -0
  137. package/dist/native/lib/chart-palette.js +20 -0
  138. package/dist/native/lib/confirm.d.ts +48 -0
  139. package/dist/native/lib/confirm.js +69 -0
  140. package/dist/native/lib/control.d.ts +24 -0
  141. package/dist/native/lib/control.js +0 -0
  142. package/dist/native/lib/external-store.d.ts +16 -0
  143. package/dist/native/lib/external-store.js +30 -0
  144. package/dist/native/lib/keyboard.d.ts +15 -0
  145. package/dist/native/lib/keyboard.js +24 -0
  146. package/dist/native/lib/labels.d.ts +15 -0
  147. package/dist/native/lib/labels.js +17 -0
  148. package/dist/native/lib/loading.d.ts +10 -0
  149. package/dist/native/lib/loading.js +15 -0
  150. package/dist/native/lib/network.d.ts +11 -0
  151. package/dist/native/lib/network.js +20 -0
  152. package/dist/native/lib/scheme.d.ts +18 -0
  153. package/dist/native/lib/scheme.js +22 -0
  154. package/dist/native/lib/snackbar.d.ts +45 -0
  155. package/dist/native/lib/snackbar.js +59 -0
  156. package/dist/native/lib/theme.d.ts +34 -0
  157. package/dist/native/lib/theme.js +53 -0
  158. package/dist/native/lib/timezone.d.ts +26 -0
  159. package/dist/native/lib/timezone.js +95 -0
  160. package/dist/native/lib/token-color.d.ts +5 -0
  161. package/dist/native/lib/token-color.js +13 -0
  162. package/dist/native/lib/token-color.web.d.ts +4 -0
  163. package/dist/native/lib/token-color.web.js +7 -0
  164. package/dist/native/lib/tuning-context.d.ts +9 -0
  165. package/dist/native/lib/tuning-context.js +7 -0
  166. package/dist/native/lib/tuning.d.ts +24 -0
  167. package/dist/native/lib/tuning.js +42 -0
  168. package/dist/native/lib/typography.d.ts +10 -0
  169. package/dist/native/lib/typography.js +11 -0
  170. package/dist/native/lib/utils.d.ts +11 -0
  171. package/dist/native/lib/utils.js +21 -0
  172. package/dist/native/once/confirm.d.ts +7 -0
  173. package/dist/native/once/confirm.js +60 -0
  174. package/dist/native/once/error-boundary.d.ts +28 -0
  175. package/dist/native/once/error-boundary.js +55 -0
  176. package/dist/native/once/loading-lock.d.ts +8 -0
  177. package/dist/native/once/loading-lock.js +28 -0
  178. package/dist/native/once/offline-banner.d.ts +15 -0
  179. package/dist/native/once/offline-banner.js +35 -0
  180. package/dist/native/once/snackbar.d.ts +17 -0
  181. package/dist/native/once/snackbar.js +106 -0
  182. package/dist/native/styles.css +46 -0
  183. package/dist/shared/analytics.d.ts +71 -0
  184. package/dist/shared/analytics.js +0 -0
  185. package/dist/shared/button.d.ts +7 -0
  186. package/dist/shared/button.js +23 -0
  187. package/dist/shared/form.d.ts +45 -0
  188. package/dist/shared/form.js +46 -0
  189. package/dist/shared/index.d.ts +3 -0
  190. package/dist/shared/index.js +3 -0
  191. package/dist/tokens/contrast-pairs.d.ts +30 -0
  192. package/dist/tokens/contrast-pairs.js +404 -0
  193. package/dist/tokens/index.d.ts +6 -0
  194. package/dist/tokens/index.js +10 -0
  195. package/dist/tokens/styles.gen.css +250 -0
  196. package/dist/tokens/tokens.gen.d.ts +141 -0
  197. package/dist/tokens/tokens.gen.js +206 -0
  198. package/dist/tuning/catalog.d.ts +27 -0
  199. package/dist/tuning/catalog.gen.d.ts +4 -0
  200. package/dist/tuning/catalog.gen.js +397 -0
  201. package/dist/tuning/catalog.js +128 -0
  202. package/dist/tuning/color.d.ts +20 -0
  203. package/dist/tuning/color.js +80 -0
  204. package/dist/tuning/css.d.ts +35 -0
  205. package/dist/tuning/css.js +147 -0
  206. package/dist/tuning/index.d.ts +8 -0
  207. package/dist/tuning/index.js +9 -0
  208. package/dist/tuning/model.d.ts +33 -0
  209. package/dist/tuning/model.js +154 -0
  210. package/dist/tuning/native.d.ts +35 -0
  211. package/dist/tuning/native.js +80 -0
  212. package/dist/tuning/schema.d.ts +69 -0
  213. package/dist/tuning/schema.js +247 -0
  214. package/dist/tuning/types.d.ts +75 -0
  215. package/dist/tuning/types.js +39 -0
  216. package/dist/web/analytics/line-chart.d.ts +20 -0
  217. package/dist/web/analytics/line-chart.js +28 -0
  218. package/dist/web/analytics/metric-card.d.ts +18 -0
  219. package/dist/web/analytics/metric-card.js +40 -0
  220. package/dist/web/analytics/multi-line-chart.d.ts +39 -0
  221. package/dist/web/analytics/multi-line-chart.js +345 -0
  222. package/dist/web/atom/avatar.d.ts +11 -0
  223. package/dist/web/atom/avatar.js +24 -0
  224. package/dist/web/atom/badge.d.ts +2 -0
  225. package/dist/web/atom/badge.js +3 -0
  226. package/dist/web/atom/button.d.ts +23 -0
  227. package/dist/web/atom/button.js +35 -0
  228. package/dist/web/atom/checkbox.d.ts +19 -0
  229. package/dist/web/atom/checkbox.js +33 -0
  230. package/dist/web/atom/colored-value.d.ts +13 -0
  231. package/dist/web/atom/colored-value.js +24 -0
  232. package/dist/web/atom/help.d.ts +10 -0
  233. package/dist/web/atom/help.js +18 -0
  234. package/dist/web/atom/label.d.ts +2 -0
  235. package/dist/web/atom/label.js +3 -0
  236. package/dist/web/atom/segmented-control.d.ts +39 -0
  237. package/dist/web/atom/segmented-control.js +42 -0
  238. package/dist/web/atom/select.d.ts +43 -0
  239. package/dist/web/atom/select.js +93 -0
  240. package/dist/web/atom/separator.d.ts +2 -0
  241. package/dist/web/atom/separator.js +3 -0
  242. package/dist/web/atom/skeleton.d.ts +2 -0
  243. package/dist/web/atom/skeleton.js +3 -0
  244. package/dist/web/atom/slider.d.ts +33 -0
  245. package/dist/web/atom/slider.js +70 -0
  246. package/dist/web/atom/spinner.d.ts +8 -0
  247. package/dist/web/atom/spinner.js +14 -0
  248. package/dist/web/atom/switch.d.ts +11 -0
  249. package/dist/web/atom/switch.js +10 -0
  250. package/dist/web/atom/text-area.d.ts +23 -0
  251. package/dist/web/atom/text-area.js +27 -0
  252. package/dist/web/atom/text-field.d.ts +18 -0
  253. package/dist/web/atom/text-field.js +22 -0
  254. package/dist/web/atom/toggle-chip.d.ts +22 -0
  255. package/dist/web/atom/toggle-chip.js +28 -0
  256. package/dist/web/atom/tooltip.d.ts +23 -0
  257. package/dist/web/atom/tooltip.js +26 -0
  258. package/dist/web/atom/typography.d.ts +6 -0
  259. package/dist/web/atom/typography.js +23 -0
  260. package/dist/web/base/accordion.d.ts +9 -0
  261. package/dist/web/base/accordion.js +44 -0
  262. package/dist/web/base/alert.d.ts +11 -0
  263. package/dist/web/base/alert.js +37 -0
  264. package/dist/web/base/avatar.d.ts +8 -0
  265. package/dist/web/base/avatar.js +29 -0
  266. package/dist/web/base/badge.d.ts +12 -0
  267. package/dist/web/base/badge.js +26 -0
  268. package/dist/web/base/breadcrumb.d.ts +16 -0
  269. package/dist/web/base/breadcrumb.js +72 -0
  270. package/dist/web/base/button.d.ts +12 -0
  271. package/dist/web/base/button.js +47 -0
  272. package/dist/web/base/card.d.ts +19 -0
  273. package/dist/web/base/card.js +74 -0
  274. package/dist/web/base/checkbox.d.ts +6 -0
  275. package/dist/web/base/checkbox.js +21 -0
  276. package/dist/web/base/dialog.d.ts +20 -0
  277. package/dist/web/base/dialog.js +92 -0
  278. package/dist/web/base/dropdown-menu.d.ts +29 -0
  279. package/dist/web/base/dropdown-menu.js +125 -0
  280. package/dist/web/base/field.d.ts +27 -0
  281. package/dist/web/base/field.js +118 -0
  282. package/dist/web/base/form.d.ts +38 -0
  283. package/dist/web/base/form.js +110 -0
  284. package/dist/web/base/input.d.ts +9 -0
  285. package/dist/web/base/input.js +23 -0
  286. package/dist/web/base/label.d.ts +6 -0
  287. package/dist/web/base/label.js +15 -0
  288. package/dist/web/base/pagination.d.ts +34 -0
  289. package/dist/web/base/pagination.js +90 -0
  290. package/dist/web/base/popover.d.ts +11 -0
  291. package/dist/web/base/popover.js +37 -0
  292. package/dist/web/base/radio-group.d.ts +7 -0
  293. package/dist/web/base/radio-group.js +27 -0
  294. package/dist/web/base/scroll-area.d.ts +10 -0
  295. package/dist/web/base/scroll-area.js +41 -0
  296. package/dist/web/base/select.d.ts +17 -0
  297. package/dist/web/base/select.js +96 -0
  298. package/dist/web/base/separator.d.ts +6 -0
  299. package/dist/web/base/separator.js +17 -0
  300. package/dist/web/base/sheet.d.ts +19 -0
  301. package/dist/web/base/sheet.js +88 -0
  302. package/dist/web/base/skeleton.d.ts +5 -0
  303. package/dist/web/base/skeleton.js +14 -0
  304. package/dist/web/base/slider.d.ts +9 -0
  305. package/dist/web/base/slider.js +39 -0
  306. package/dist/web/base/sonner.d.ts +9 -0
  307. package/dist/web/base/sonner.js +25 -0
  308. package/dist/web/base/spinner.d.ts +5 -0
  309. package/dist/web/base/spinner.js +17 -0
  310. package/dist/web/base/switch.d.ts +6 -0
  311. package/dist/web/base/switch.js +19 -0
  312. package/dist/web/base/table.d.ts +14 -0
  313. package/dist/web/base/table.js +68 -0
  314. package/dist/web/base/tabs.d.ts +9 -0
  315. package/dist/web/base/tabs.js +36 -0
  316. package/dist/web/base/textarea.d.ts +9 -0
  317. package/dist/web/base/textarea.js +23 -0
  318. package/dist/web/base/tooltip.d.ts +9 -0
  319. package/dist/web/base/tooltip.js +36 -0
  320. package/dist/web/form/checkbox.d.ts +35 -0
  321. package/dist/web/form/checkbox.js +91 -0
  322. package/dist/web/form/radio-group.d.ts +24 -0
  323. package/dist/web/form/radio-group.js +75 -0
  324. package/dist/web/form/select.d.ts +16 -0
  325. package/dist/web/form/select.js +44 -0
  326. package/dist/web/form/slider.d.ts +19 -0
  327. package/dist/web/form/slider.js +51 -0
  328. package/dist/web/form/switch.d.ts +11 -0
  329. package/dist/web/form/switch.js +50 -0
  330. package/dist/web/form/text-area.d.ts +10 -0
  331. package/dist/web/form/text-area.js +31 -0
  332. package/dist/web/form/text-input.d.ts +10 -0
  333. package/dist/web/form/text-input.js +42 -0
  334. package/dist/web/index.d.ts +1 -0
  335. package/dist/web/index.js +0 -0
  336. package/dist/web/layout/accordion.d.ts +26 -0
  337. package/dist/web/layout/accordion.js +29 -0
  338. package/dist/web/layout/alert-modal.d.ts +41 -0
  339. package/dist/web/layout/alert-modal.js +71 -0
  340. package/dist/web/layout/card.d.ts +17 -0
  341. package/dist/web/layout/card.js +35 -0
  342. package/dist/web/layout/description.d.ts +12 -0
  343. package/dist/web/layout/description.js +20 -0
  344. package/dist/web/layout/information.d.ts +36 -0
  345. package/dist/web/layout/information.js +53 -0
  346. package/dist/web/layout/modal.d.ts +16 -0
  347. package/dist/web/layout/modal.js +37 -0
  348. package/dist/web/layout/page-frame.d.ts +37 -0
  349. package/dist/web/layout/page-frame.js +79 -0
  350. package/dist/web/layout/scroll-area.d.ts +10 -0
  351. package/dist/web/layout/scroll-area.js +13 -0
  352. package/dist/web/layout/sheet.d.ts +32 -0
  353. package/dist/web/layout/sheet.js +68 -0
  354. package/dist/web/layout/simple-dialog.d.ts +11 -0
  355. package/dist/web/layout/simple-dialog.js +24 -0
  356. package/dist/web/layout/simple-table.d.ts +49 -0
  357. package/dist/web/layout/simple-table.js +58 -0
  358. package/dist/web/layout/tabs.d.ts +26 -0
  359. package/dist/web/layout/tabs.js +42 -0
  360. package/dist/web/layout/titled-modal.d.ts +32 -0
  361. package/dist/web/layout/titled-modal.js +58 -0
  362. package/dist/web/lib/css-vars.d.ts +14 -0
  363. package/dist/web/lib/css-vars.js +18 -0
  364. package/dist/web/lib/dialog.d.ts +13 -0
  365. package/dist/web/lib/dialog.js +0 -0
  366. package/dist/web/lib/form.d.ts +2 -0
  367. package/dist/web/lib/form.js +3 -0
  368. package/dist/web/lib/labels.d.ts +27 -0
  369. package/dist/web/lib/labels.js +29 -0
  370. package/dist/web/lib/modal-open-focus.d.ts +17 -0
  371. package/dist/web/lib/modal-open-focus.js +44 -0
  372. package/dist/web/lib/restore-focus.d.ts +12 -0
  373. package/dist/web/lib/restore-focus.js +32 -0
  374. package/dist/web/lib/utils.d.ts +4 -0
  375. package/dist/web/lib/utils.js +10 -0
  376. package/dist/web/styles.css +162 -0
  377. package/package.json +203 -4
@@ -0,0 +1,345 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { LINE_CHART_DEFAULT_LABEL, LINE_CHART_EMPTY_TEXT, LINE_CHART_TABLE_POINT_LABEL, LINE_CHART_TABLE_SERIES_LABEL, LINE_CHART_TABLE_VALUE_LABEL, lineChartTableDropped, lineChartTableThinned } from "../lib/labels.js";
3
+ import { cssVars } from "../lib/css-vars.js";
4
+ import { useMemo } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { defineChart, dot, lineY } from "@tanstack/charts";
7
+ import { Chart } from "@tanstack/charts/react";
8
+ import { scaleLinear } from "@tanstack/charts/scales/linear";
9
+ import { tooltip } from "@tanstack/charts/tooltip";
10
+
11
+ //#region src/web/analytics/multi-line-chart.tsx
12
+ const DEFAULT_HEIGHT = 220;
13
+ /** The maximum number of rows per series in the table for screen readers. */
14
+ const MAX_TABLE_ROWS_PER_SERIES = 60;
15
+ const THEME = {
16
+ foreground: "var(--foreground)",
17
+ muted: "var(--muted-foreground)",
18
+ grid: "var(--line)",
19
+ background: "transparent",
20
+ palette: [
21
+ "var(--chart-1)",
22
+ "var(--chart-2)",
23
+ "var(--chart-3)",
24
+ "var(--chart-4)",
25
+ "var(--chart-5)"
26
+ ]
27
+ };
28
+ /** Grid lines are thin 1px lines (`--line`). The library's default opacity of 0.11 is not applied, and the token colour is used as is. */
29
+ const GRID_LINE = {
30
+ stroke: "var(--line)",
31
+ strokeOpacity: 1,
32
+ strokeWidth: 1
33
+ };
34
+ /** The axis baseline is one step darker than the grid lines (`--line-strong`). */
35
+ const AXIS_LINE = {
36
+ stroke: "var(--line-strong)",
37
+ strokeOpacity: 1,
38
+ strokeWidth: 1
39
+ };
40
+ /** Do not fade tick label text (the default is 0.68). */
41
+ const TICK_LABELS = { opacity: 1 };
42
+ /** Focus when entering the figure (`svg tabindex=0`) by keyboard is the same opaque ring as other components (no low alpha; 2px offset). */
43
+ const FOCUS_RING = "[&_svg:focus-visible]:rounded-control [&_svg:focus-visible]:ring-2 [&_svg:focus-visible]:ring-ring [&_svg:focus-visible]:ring-offset-2 [&_svg:focus-visible]:ring-offset-background [&_svg:focus-visible]:outline-none";
44
+ const TOOLTIP_VARS = cssVars({
45
+ "--ts-chart-tooltip-background": "var(--overlay)",
46
+ "--ts-chart-tooltip-color": "var(--popover-foreground)",
47
+ "--ts-chart-tooltip-border": "1px solid var(--line)",
48
+ "--ts-chart-tooltip-border-radius": "var(--radius-control)",
49
+ "--ts-chart-tooltip-shadow": "var(--shadow-md)",
50
+ "--ts-chart-tooltip-padding": "8px 10px",
51
+ "--ts-chart-tooltip-font": "400 12px/16px var(--font-sans)"
52
+ });
53
+ /**
54
+ * A multi-series line chart. Same API as native's `analytics/multi-line-chart` (`shared/analytics.ts`).
55
+ * It is drawn with `@tanstack/charts` (SVG). Grid lines are thin 1px lines, the colours are `--chart-1` to `--chart-5`, and the tooltip is an overlay surface + a shadow.
56
+ *
57
+ * - **Non-numeric points are dropped**: points whose `y` (or `t`) is not finite (NaN, Infinity) are removed before building
58
+ * (they are not in the table for screen readers either, and the number removed is noted with the table). If everything is removed, `emptyText`
59
+ * - When every series has `t`, **the horizontal position is decided by time**. If even one lacks it, each series is placed at even intervals in its own order
60
+ * (that is fine for a single series; when overlaying series with different steps, provide `t`). A series with a single point is drawn as a dot
61
+ * - There are at most three x-axis ticks, at the first, middle and last positions (a number that does not overlap when there are few points). **The label comes from the nearest point of the first series**
62
+ * (when placing by time, a time that exists only in the second or later series does not become a label). It is placed at the point's position
63
+ * - There are three y-axis ticks, top, middle and bottom. So the line does not collapse even when all points have the same value, it is moved up and down only when the range is zero
64
+ * - The default of `formatValue` has **a number of digits that depends on the data's range** (ticks can be told apart even for values from 0 to 1). Ticks, the tooltip and the table for screen readers
65
+ * go through the same formatting. When you pass one, **keep the function reference stable** (a new function every time rebuilds the chart on every render and closes the tooltip).
66
+ * The same goes for `series` (pass a reference the parent does not recreate)
67
+ * - On hover and focus, the tooltip shows the value of each series' point at that position (arrow keys also move it by keyboard), with the series name and colour.
68
+ * **Outside the range where a series has points (up to half the point spacing at the ends), no row is shown for that series** (so a far-away point's value does not look like the value at that position).
69
+ * When the point's label differs from the title, it is added to the row
70
+ * - Reading: the figure is `role="img"` named by `accessibilityLabel` (a summary). The exact values are also held in a hidden table (`table`)
71
+ * - **Series are not told apart by colour alone**: the legend has the series names, and secondary series can be dashed
72
+ */
73
+ function MultiLineChart({ series, height = DEFAULT_HEIGHT, formatValue, emptyText = LINE_CHART_EMPTY_TEXT, accessibilityLabel, style, className, legend = true, table = true }) {
74
+ const label = accessibilityLabel ?? defaultLabel(series);
75
+ const model = useMemo(() => buildModel(series, formatValue), [series, formatValue]);
76
+ if (model === null) return /* @__PURE__ */ jsx("div", {
77
+ "data-slot": "line-chart",
78
+ "data-empty": "",
79
+ style: {
80
+ minHeight: height,
81
+ ...style
82
+ },
83
+ className: cn("flex items-center justify-center", className),
84
+ children: /* @__PURE__ */ jsx("p", {
85
+ className: "text-muted-foreground text-sm",
86
+ children: emptyText
87
+ })
88
+ });
89
+ return /* @__PURE__ */ jsxs("div", {
90
+ "data-slot": "line-chart",
91
+ style,
92
+ className: cn("flex min-w-0 flex-col gap-2", FOCUS_RING, className),
93
+ children: [
94
+ /* @__PURE__ */ jsx(Chart, {
95
+ definition: model.definition,
96
+ height,
97
+ ariaLabel: label,
98
+ style: TOOLTIP_VARS
99
+ }),
100
+ legend ? /* @__PURE__ */ jsx("ul", {
101
+ "data-slot": "line-chart-legend",
102
+ className: "flex flex-wrap gap-x-4 gap-y-1 px-1",
103
+ children: series.map((entry) => /* @__PURE__ */ jsxs("li", {
104
+ className: "text-muted-foreground flex items-center gap-1.5 text-[11px]",
105
+ children: [/* @__PURE__ */ jsx("span", {
106
+ "aria-hidden": true,
107
+ className: "inline-block h-0.5 w-2.5",
108
+ style: {
109
+ background: entry.dashed === true ? `repeating-linear-gradient(90deg, ${entry.color} 0 4px, transparent 4px 6px)` : entry.color,
110
+ opacity: entry.points.length > 0 ? 1 : .4
111
+ }
112
+ }), entry.label]
113
+ }, entry.key))
114
+ }) : null,
115
+ table ? /* @__PURE__ */ jsx(DataTable, {
116
+ caption: label,
117
+ model
118
+ }) : null
119
+ ]
120
+ });
121
+ }
122
+ /** The name when there is no summary. It lists the series names. */
123
+ function defaultLabel(series) {
124
+ const names = series.map((entry) => entry.label).filter((name) => name !== "");
125
+ return names.length === 0 ? LINE_CHART_DEFAULT_LABEL : `${names.join("・")}の${LINE_CHART_DEFAULT_LABEL}`;
126
+ }
127
+ /** Samples at even intervals down to at most `limit` items (the first and the last are always kept). */
128
+ function thin(items, limit) {
129
+ if (items.length <= limit) return [...items];
130
+ const picked = /* @__PURE__ */ new Set();
131
+ for (let i = 0; i < limit; i += 1) picked.add(Math.round(i * (items.length - 1) / (limit - 1)));
132
+ return [...picked].sort((a, b) => a - b).flatMap((index) => items[index] === void 0 ? [] : [items[index]]);
133
+ }
134
+ /** The table hidden for screen readers. It is not shown on screen (sr-only). Values go through the same formatting as the figure's ticks. */
135
+ function DataTable({ caption, model }) {
136
+ const total = model.series.reduce((sum, entry) => sum + entry.points.length, 0);
137
+ const shownSeries = model.series.map((entry) => ({
138
+ entry,
139
+ points: thin(entry.points, MAX_TABLE_ROWS_PER_SERIES)
140
+ }));
141
+ const shown = shownSeries.reduce((sum, item) => sum + item.points.length, 0);
142
+ return /* @__PURE__ */ jsxs("div", {
143
+ "data-slot": "line-chart-table",
144
+ className: "sr-only",
145
+ children: [
146
+ model.dropped > 0 ? /* @__PURE__ */ jsx("p", { children: lineChartTableDropped(model.dropped) }) : null,
147
+ shown < total ? /* @__PURE__ */ jsx("p", { children: lineChartTableThinned(shown, total) }) : null,
148
+ /* @__PURE__ */ jsxs("table", { children: [
149
+ /* @__PURE__ */ jsx("caption", { children: caption }),
150
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
151
+ /* @__PURE__ */ jsx("th", {
152
+ scope: "col",
153
+ children: LINE_CHART_TABLE_SERIES_LABEL
154
+ }),
155
+ /* @__PURE__ */ jsx("th", {
156
+ scope: "col",
157
+ children: LINE_CHART_TABLE_POINT_LABEL
158
+ }),
159
+ /* @__PURE__ */ jsx("th", {
160
+ scope: "col",
161
+ children: "値"
162
+ })
163
+ ] }) }),
164
+ /* @__PURE__ */ jsx("tbody", { children: shownSeries.flatMap(({ entry, points }) => points.map((point, index) => /* @__PURE__ */ jsxs("tr", { children: [
165
+ /* @__PURE__ */ jsx("th", {
166
+ scope: "row",
167
+ children: entry.label
168
+ }),
169
+ /* @__PURE__ */ jsx("td", { children: String(point.x) }),
170
+ /* @__PURE__ */ jsx("td", { children: model.format(point.y) })
171
+ ] }, `${entry.key}-${index.toString()}`))) })
172
+ ] })
173
+ ]
174
+ });
175
+ }
176
+ /** Draws only points whose `y` (and `t`) are finite. NaN and Infinity break the scale, so they are dropped before building. */
177
+ function isUsable(point) {
178
+ return Number.isFinite(point.y) && (point.t === void 0 || Number.isFinite(point.t));
179
+ }
180
+ function buildModel(series, formatValue) {
181
+ const cleaned = series.map((entry) => ({
182
+ ...entry,
183
+ points: entry.points.filter(isUsable)
184
+ }));
185
+ const total = series.reduce((sum, entry) => sum + entry.points.length, 0);
186
+ const clean = cleaned.filter((entry) => entry.points.length > 0);
187
+ if (clean.length === 0) return null;
188
+ const kept = clean.reduce((sum, entry) => sum + entry.points.length, 0);
189
+ const built = buildDefinition(clean, formatValue);
190
+ return {
191
+ definition: built.definition,
192
+ series: clean,
193
+ dropped: total - kept,
194
+ format: built.format
195
+ };
196
+ }
197
+ /** The point nearest to a position. */
198
+ function nearestRow(entry, position) {
199
+ let best;
200
+ for (const row of entry) if (best === void 0 || Math.abs(row.x - position) < Math.abs(best.x - position)) best = row;
201
+ return best;
202
+ }
203
+ /** When every series has `t`, place by time. */
204
+ function isTimed(series) {
205
+ const times = series.flatMap((entry) => entry.points.map((point) => point.t));
206
+ return times.length > 0 && times.every((time) => time !== void 0);
207
+ }
208
+ /** The horizontal position of a point (by time when there is time, otherwise its order within the series, mapped to 0 to 1). */
209
+ function positionOf(timed, index, count, point) {
210
+ if (timed && point.t !== void 0) return point.t;
211
+ return count === 1 ? .5 : index / (count - 1);
212
+ }
213
+ function extent(values) {
214
+ let min = Infinity;
215
+ let max = -Infinity;
216
+ for (const value of values) {
217
+ if (value < min) min = value;
218
+ if (value > max) max = value;
219
+ }
220
+ return [min, max];
221
+ }
222
+ /**
223
+ * The default formatting. It takes the number of digits that lets the tick spacing (half the range, since there are three ticks, top, middle and bottom) be told apart, and adds one more digit (0 to 6 digits).
224
+ * Values from 0 to 1 get 2 digits, and a range of 100 or more gives integers. The tooltip and the table for screen readers use the same formatting.
225
+ */
226
+ function autoFormat(low, high) {
227
+ const step = (high - low) / 2;
228
+ const digits = step > 0 && Number.isFinite(step) ? Math.min(6, Math.max(0, Math.ceil(-Math.log10(step)) + 1)) : 0;
229
+ return (value) => (value === 0 ? 0 : value).toFixed(digits);
230
+ }
231
+ /** How far outside a series' point range its row is still shown (half the point spacing; for a single point, 2% of the x-axis width). */
232
+ function reachOf(entry, domainWidth) {
233
+ if (entry.length < 2) return domainWidth * .02;
234
+ const [min, max] = extent(entry.map((row) => row.x));
235
+ return (max - min) / (entry.length - 1) / 2;
236
+ }
237
+ function buildDefinition(series, formatValue) {
238
+ const timed = isTimed(series);
239
+ const rows = series.map((entry) => entry.points.map((point, index) => ({
240
+ x: positionOf(timed, index, entry.points.length, point),
241
+ y: point.y,
242
+ label: String(point.x),
243
+ series: entry.label,
244
+ color: entry.color
245
+ })));
246
+ const [min, max] = extent(series.flatMap((entry) => entry.points.map((point) => point.y)));
247
+ const span = max - min || Math.max(Math.abs(max), 1);
248
+ const low = max === min ? min - span / 2 : min;
249
+ const high = max === min ? max + span / 2 : max;
250
+ const mid = low / 2 + high / 2;
251
+ const format = formatValue ?? autoFormat(low, high);
252
+ const [xMin, xMax] = extent(rows.flatMap((entry) => entry.map((row) => row.x)));
253
+ const xDomain = timed ? xMax === xMin ? [xMin - .5, xMax + .5] : [xMin, xMax] : [0, 1];
254
+ const domainWidth = xDomain[1] - xDomain[0];
255
+ const reference = rows[0] ?? [];
256
+ const tickRows = [
257
+ xDomain[0],
258
+ (xDomain[0] + xDomain[1]) / 2,
259
+ xDomain[1]
260
+ ].map((position) => nearestRow(reference, position)).filter((row, index, all) => row !== void 0 && all.indexOf(row) === index);
261
+ const xTicks = tickRows.map((row) => row.x);
262
+ const labelAt = (position) => tickRows.find((row) => row.x === position)?.label ?? "";
263
+ const ranges = rows.map((entry) => extent(entry.map((row) => row.x)));
264
+ const reaches = rows.map((entry) => reachOf(entry, domainWidth));
265
+ return {
266
+ definition: defineChart({
267
+ marks: series.flatMap((entry, index) => {
268
+ const own = rows[index] ?? [];
269
+ const line = lineY(own, {
270
+ id: entry.key,
271
+ x: "x",
272
+ y: "y",
273
+ stroke: entry.color,
274
+ strokeWidth: entry.dashed === true ? 1.5 : 2,
275
+ strokeDasharray: entry.dashed === true ? "4 3" : void 0
276
+ });
277
+ return own.length === 1 ? [line, dot(own, {
278
+ id: `${entry.key}-dot`,
279
+ x: "x",
280
+ y: "y",
281
+ fill: entry.color,
282
+ r: 3
283
+ })] : [line];
284
+ }),
285
+ scales: {
286
+ x: {
287
+ scale: scaleLinear().domain(xDomain),
288
+ axis: {
289
+ line: AXIS_LINE,
290
+ ticks: {
291
+ values: xTicks,
292
+ format: labelAt
293
+ },
294
+ tickLabels: TICK_LABELS
295
+ }
296
+ },
297
+ y: {
298
+ scale: scaleLinear().domain([low, high]),
299
+ grid: GRID_LINE,
300
+ axis: {
301
+ line: AXIS_LINE,
302
+ ticks: {
303
+ values: [
304
+ low,
305
+ mid,
306
+ high
307
+ ],
308
+ format
309
+ },
310
+ tickLabels: TICK_LABELS
311
+ }
312
+ }
313
+ },
314
+ theme: THEME,
315
+ focus: "nearest-x",
316
+ tooltip: {
317
+ use: tooltip,
318
+ content: (points) => {
319
+ const primary = points[0];
320
+ if (primary === void 0) return { rows: [] };
321
+ const position = primary.datum.x;
322
+ return {
323
+ title: primary.datum.label,
324
+ rows: rows.flatMap((entry, index) => {
325
+ const range = ranges[index] ?? [0, 0];
326
+ const reach = reaches[index] ?? 0;
327
+ if (position < range[0] - reach || position > range[1] + reach) return [];
328
+ const hit = nearestRow(entry, position);
329
+ if (hit === void 0) return [];
330
+ return [{
331
+ label: hit.label === primary.datum.label ? hit.series : `${hit.series}(${hit.label})`,
332
+ value: format(hit.y),
333
+ color: hit.color
334
+ }];
335
+ })
336
+ };
337
+ }
338
+ }
339
+ }),
340
+ format
341
+ };
342
+ }
343
+
344
+ //#endregion
345
+ export { MultiLineChart };
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ //#region src/web/atom/avatar.d.ts
3
+ export type AvatarProps = {
4
+ src?: string;
5
+ /** Alt text for the image. Empty by default (decorative; assumes the name is shown next to it). For a standalone face with no name, pass the person's name */
6
+ alt?: string;
7
+ fallback?: React.ReactNode;
8
+ className?: string;
9
+ };
10
+ export declare function Avatar({ src, alt, fallback, className }: AvatarProps): React.JSX.Element;
11
+ //#endregion
@@ -0,0 +1,24 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { Avatar as Avatar$1, AvatarFallback, AvatarImage } from "../base/avatar.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { User } from "lucide-react";
5
+
6
+ //#region src/web/atom/avatar.tsx
7
+ function Avatar({ src, alt = "", fallback, className }) {
8
+ return /* @__PURE__ */ jsxs(Avatar$1, {
9
+ className: cn("size-6", className),
10
+ children: [/* @__PURE__ */ jsx(AvatarImage, {
11
+ src,
12
+ alt
13
+ }), /* @__PURE__ */ jsx(AvatarFallback, {
14
+ className: "bg-muted-foreground text-background",
15
+ children: fallback ?? /* @__PURE__ */ jsx(User, {
16
+ className: "size-4",
17
+ "aria-hidden": true
18
+ })
19
+ })]
20
+ });
21
+ }
22
+
23
+ //#endregion
24
+ export { Avatar };
@@ -0,0 +1,2 @@
1
+ import { Badge, BadgeVariants, badgeVariants } from "../base/badge.js";
2
+ export { Badge, type BadgeVariants, badgeVariants };
@@ -0,0 +1,3 @@
1
+ import { Badge, badgeVariants } from "../base/badge.js";
2
+
3
+ export { Badge, badgeVariants };
@@ -0,0 +1,23 @@
1
+ import { ButtonSize, ButtonVariant } from "../../shared/button.js";
2
+ import { Button as Button$1 } from "../base/button.js";
3
+ import React, { ComponentType, SVGProps } from "react";
4
+ //#region src/web/atom/button.d.ts
5
+ export type ButtonIcon = ComponentType<SVGProps<SVGSVGElement>>;
6
+ type ButtonIconProps = {
7
+ iconLeft?: never;
8
+ iconRight?: never;
9
+ } | {
10
+ iconLeft: ButtonIcon;
11
+ iconRight?: never;
12
+ } | {
13
+ iconLeft?: never;
14
+ iconRight: ButtonIcon;
15
+ };
16
+ export type ButtonProps = {
17
+ action?: "previous-page";
18
+ variant?: ButtonVariant;
19
+ size?: ButtonSize;
20
+ } & ButtonIconProps;
21
+ export declare function Button(props: ButtonProps & React.ComponentProps<typeof Button$1>): React.JSX.Element;
22
+ //#endregion
23
+ export {};
@@ -0,0 +1,35 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { Button as Button$1 } from "../base/button.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+
5
+ //#region src/web/atom/button.tsx
6
+ function Button(props) {
7
+ const { type = "button", onClick, iconLeft: IconLeft, iconRight: IconRight, className, children, action, asChild, ...remain } = props;
8
+ const innerOnClick = (event) => {
9
+ onClick?.(event);
10
+ switch (action) {
11
+ case "previous-page": window.history.back();
12
+ }
13
+ };
14
+ if (asChild) return /* @__PURE__ */ jsx(Button$1, {
15
+ asChild: true,
16
+ className: cn("cursor-pointer", className),
17
+ onClick: innerOnClick,
18
+ ...remain,
19
+ children
20
+ });
21
+ return /* @__PURE__ */ jsxs(Button$1, {
22
+ type,
23
+ className: cn("cursor-pointer", className),
24
+ onClick: innerOnClick,
25
+ ...remain,
26
+ children: [
27
+ IconLeft && /* @__PURE__ */ jsx(IconLeft, { className: "!w-3 !h-3" }),
28
+ children,
29
+ IconRight && /* @__PURE__ */ jsx(IconRight, { className: "!w-3 !h-3" })
30
+ ]
31
+ });
32
+ }
33
+
34
+ //#endregion
35
+ export { Button };
@@ -0,0 +1,19 @@
1
+ import { Checkbox as Checkbox$1 } from "../base/checkbox.js";
2
+ import { ComponentProps, ReactNode } from "react";
3
+ //#region src/web/atom/checkbox.d.ts
4
+ export type CheckboxProps = Omit<ComponentProps<typeof Checkbox$1>, "checked" | "onCheckedChange"> & {
5
+ /** Checked state (controlled mode) */
6
+ checked: boolean;
7
+ /** Called when the value changes */
8
+ onCheckedChange: (checked: boolean) => void;
9
+ /** Label shown to the right of the checkbox */
10
+ label?: ReactNode;
11
+ /** Extra class for the wrapper */
12
+ wrapperClassName?: string;
13
+ };
14
+ /**
15
+ * A simple Checkbox that does not depend on a form context.
16
+ * Passing `label` adds a clickable label on the right.
17
+ */
18
+ export declare function Checkbox({ checked, onCheckedChange, label, wrapperClassName, id, ...rest }: CheckboxProps): import("react").JSX.Element;
19
+ //#endregion
@@ -0,0 +1,33 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { Checkbox as Checkbox$1 } from "../base/checkbox.js";
3
+ import { Label } from "../base/label.js";
4
+ import { useId } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+
7
+ //#region src/web/atom/checkbox.tsx
8
+ /**
9
+ * A simple Checkbox that does not depend on a form context.
10
+ * Passing `label` adds a clickable label on the right.
11
+ */
12
+ function Checkbox({ checked, onCheckedChange, label, wrapperClassName, id, ...rest }) {
13
+ const generated = useId();
14
+ const inputId = id ?? generated;
15
+ const box = /* @__PURE__ */ jsx(Checkbox$1, {
16
+ id: inputId,
17
+ checked,
18
+ onCheckedChange: (c) => onCheckedChange(c === true),
19
+ ...rest
20
+ });
21
+ if (label === void 0) return box;
22
+ return /* @__PURE__ */ jsxs("div", {
23
+ className: cn("inline-flex items-center gap-1.5", wrapperClassName),
24
+ children: [box, /* @__PURE__ */ jsx(Label, {
25
+ htmlFor: inputId,
26
+ className: "cursor-pointer",
27
+ children: label
28
+ })]
29
+ });
30
+ }
31
+
32
+ //#endregion
33
+ export { Checkbox };
@@ -0,0 +1,13 @@
1
+ import { ColoredValueSharedProps } from "../../shared/analytics.js";
2
+ import { ReactNode } from "react";
3
+ //#region src/web/atom/colored-value.d.ts
4
+ export type ColoredValueProps<TValue> = ColoredValueSharedProps<TValue>;
5
+ /**
6
+ * A number whose colour changes with its sign. Same API as native's `atom/colored-value` (`shared/analytics.ts`).
7
+ * The default colours are the tokens `state-success` / `state-error` (4.5:1 or more in light and dark), not a fixed Tailwind palette.
8
+ * It is a number, so it is drawn monospaced (tabular).
9
+ *
10
+ * **Do not convey meaning by colour alone.** `format` puts the sign (`+` / `−`) into the value, so the direction is clear even to people who cannot see the colour.
11
+ */
12
+ export declare function ColoredValue<TValue>({ value, format, isPositive, isNegative, positiveColor, negativeColor, className }: ColoredValueProps<TValue>): ReactNode;
13
+ //#endregion
@@ -0,0 +1,24 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+
4
+ //#region src/web/atom/colored-value.tsx
5
+ /**
6
+ * A number whose colour changes with its sign. Same API as native's `atom/colored-value` (`shared/analytics.ts`).
7
+ * The default colours are the tokens `state-success` / `state-error` (4.5:1 or more in light and dark), not a fixed Tailwind palette.
8
+ * It is a number, so it is drawn monospaced (tabular).
9
+ *
10
+ * **Do not convey meaning by colour alone.** `format` puts the sign (`+` / `−`) into the value, so the direction is clear even to people who cannot see the colour.
11
+ */
12
+ function ColoredValue({ value, format, isPositive, isNegative, positiveColor = "text-state-success", negativeColor = "text-state-error", className }) {
13
+ const formatted = format ? format(value) : String(value);
14
+ const tone = isPositive?.(value) ? "positive" : isNegative?.(value) ? "negative" : void 0;
15
+ return /* @__PURE__ */ jsx("span", {
16
+ "data-slot": "colored-value",
17
+ "data-tone": tone,
18
+ className: cn("font-mono tabular-nums", tone === "positive" && positiveColor, tone === "negative" && negativeColor, className),
19
+ children: formatted
20
+ });
21
+ }
22
+
23
+ //#endregion
24
+ export { ColoredValue };
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ //#region src/web/atom/help.d.ts
3
+ type Props = {
4
+ children: React.ReactNode;
5
+ /** Accessible name of the trigger (it is shown as an icon only, so the name is given here) */
6
+ label?: string;
7
+ };
8
+ export declare function Help({ children, label }: Props): React.JSX.Element;
9
+ //#endregion
10
+ export {};
@@ -0,0 +1,18 @@
1
+ import { HELP_LABEL } from "../lib/labels.js";
2
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../base/tooltip.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { CircleQuestionMark } from "lucide-react";
5
+
6
+ //#region src/web/atom/help.tsx
7
+ function Help({ children, label = HELP_LABEL }) {
8
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
9
+ "aria-label": label,
10
+ children: /* @__PURE__ */ jsx(CircleQuestionMark, {
11
+ className: "h-4 w-4",
12
+ "aria-hidden": true
13
+ })
14
+ }), /* @__PURE__ */ jsx(TooltipContent, { children })] });
15
+ }
16
+
17
+ //#endregion
18
+ export { Help };
@@ -0,0 +1,2 @@
1
+ import { Label } from "../base/label.js";
2
+ export { Label };
@@ -0,0 +1,3 @@
1
+ import { Label } from "../base/label.js";
2
+
3
+ export { Label };
@@ -0,0 +1,39 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/web/atom/segmented-control.d.ts
3
+ export type SegmentedOption<T extends string | number> = {
4
+ value: T;
5
+ label: ReactNode;
6
+ disabled?: boolean;
7
+ /** DEV-E2E: ID used by e2e to identify individual segments */
8
+ testId?: string;
9
+ };
10
+ export type SegmentedControlProps<T extends string | number> = {
11
+ value: T;
12
+ onValueChange: (value: T) => void;
13
+ options: SegmentedOption<T>[];
14
+ /**
15
+ * - `joined` (default) ... share a single border, with a 1px divider between segments
16
+ * - `separate` ... each segment has its own independent border
17
+ * - `underline` ... no border; tabs where only the selected one gets an underline
18
+ * - `solid` ... each segment has a 1px border and the borders overlap with neighbours. The selected one is filled with indigo
19
+ */
20
+ variant?: "joined" | "separate" | "underline" | "solid";
21
+ /** `md` is the body size (16/26, pad 9/16). */
22
+ size?: "xs" | "sm" | "md";
23
+ /** Divide the full width equally among the segments */
24
+ fill?: boolean;
25
+ className?: string;
26
+ "aria-label"?: string;
27
+ };
28
+ /**
29
+ * Horizontal exclusive selection (tabs, mode picker, interval picker, filter kinds).
30
+ *
31
+ * The selected segment gets the indigo (action) fill with white text (corner radius is the control step).
32
+ * It sets `role="radiogroup"` and `aria-checked` on each segment, so callers
33
+ * do not have to assemble aria themselves.
34
+ *
35
+ * Hand-rolled versions (`.map` over `<button>` with a ternary className) tend to drift in
36
+ * dividers, selected-state styling and keyboard handling, so they are consolidated here.
37
+ */
38
+ export declare function SegmentedControl<T extends string | number>({ value, onValueChange, options, variant, size, fill, className, "aria-label": ariaLabel }: SegmentedControlProps<T>): import("react").JSX.Element;
39
+ //#endregion
@@ -0,0 +1,42 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+
4
+ //#region src/web/atom/segmented-control.tsx
5
+ const SIZE = {
6
+ xs: "px-2 py-1 text-xs",
7
+ sm: "px-3 py-1.5 text-sm",
8
+ md: "px-4 py-[9px] text-base leading-[26px]"
9
+ };
10
+ /**
11
+ * Horizontal exclusive selection (tabs, mode picker, interval picker, filter kinds).
12
+ *
13
+ * The selected segment gets the indigo (action) fill with white text (corner radius is the control step).
14
+ * It sets `role="radiogroup"` and `aria-checked` on each segment, so callers
15
+ * do not have to assemble aria themselves.
16
+ *
17
+ * Hand-rolled versions (`.map` over `<button>` with a ternary className) tend to drift in
18
+ * dividers, selected-state styling and keyboard handling, so they are consolidated here.
19
+ */
20
+ function SegmentedControl({ value, onValueChange, options, variant = "joined", size = "sm", fill, className, "aria-label": ariaLabel }) {
21
+ return /* @__PURE__ */ jsx("div", {
22
+ role: "radiogroup",
23
+ "aria-label": ariaLabel,
24
+ className: cn("inline-flex", fill && "flex w-full", variant === "joined" && "border-input bg-card rounded-control border", variant === "separate" && "gap-1", className),
25
+ children: options.map((option) => {
26
+ const active = option.value === value;
27
+ return /* @__PURE__ */ jsx("button", {
28
+ type: "button",
29
+ role: "radio",
30
+ "aria-checked": active,
31
+ disabled: option.disabled,
32
+ "data-testid": option.testId,
33
+ onClick: () => onValueChange(option.value),
34
+ className: cn("cursor-pointer whitespace-nowrap transition-colors disabled:cursor-not-allowed disabled:opacity-50", SIZE[size], fill && "flex-1", variant === "joined" && "not-first:border-input not-first:border-l first:rounded-l-control last:rounded-r-control", variant === "separate" && "border-input bg-card rounded-control border", variant === "underline" && "border-b-2 border-transparent", variant === "solid" && "border-input bg-card not-first:-ml-px first:rounded-l-control last:rounded-r-control border", active ? variant === "underline" ? "border-action text-foreground font-medium" : cn("bg-action text-action-foreground", variant === "solid" && "border-action relative") : "text-muted-foreground hover:bg-accent hover:text-foreground"),
35
+ children: option.label
36
+ }, String(option.value));
37
+ })
38
+ });
39
+ }
40
+
41
+ //#endregion
42
+ export { SegmentedControl };