@seekalfred/primitives 0.6.0 → 0.7.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 (322) hide show
  1. package/README.md +19 -11
  2. package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
  3. package/dist/activity-heatmap/activity-heatmap.js +699 -0
  4. package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
  5. package/dist/activity-heatmap/index.d.ts +2 -0
  6. package/dist/activity-heatmap/index.js +2 -0
  7. package/dist/alert/alert.d.ts +5 -1
  8. package/dist/alert/alert.js +68 -7
  9. package/dist/alert/alert.js.map +1 -1
  10. package/dist/alert-dialog/alert-dialog.d.ts +45 -18
  11. package/dist/alert-dialog/alert-dialog.js +128 -9
  12. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  13. package/dist/alert-dialog/index.d.ts +2 -2
  14. package/dist/alert-dialog/index.js +2 -2
  15. package/dist/animated-counter/animated-counter.d.ts +38 -0
  16. package/dist/animated-counter/animated-counter.js +230 -0
  17. package/dist/animated-counter/animated-counter.js.map +1 -0
  18. package/dist/animated-counter/index.d.ts +2 -0
  19. package/dist/animated-counter/index.js +2 -0
  20. package/dist/announcement-bar/announcement-bar.d.ts +67 -0
  21. package/dist/announcement-bar/announcement-bar.js +364 -0
  22. package/dist/announcement-bar/announcement-bar.js.map +1 -0
  23. package/dist/announcement-bar/index.d.ts +2 -0
  24. package/dist/announcement-bar/index.js +2 -0
  25. package/dist/badge/badge.d.ts +1 -1
  26. package/dist/bar-chart/bar-chart.d.ts +51 -0
  27. package/dist/bar-chart/bar-chart.js +565 -0
  28. package/dist/bar-chart/bar-chart.js.map +1 -0
  29. package/dist/bar-chart/index.d.ts +2 -0
  30. package/dist/bar-chart/index.js +2 -0
  31. package/dist/breadcrumb/breadcrumb.d.ts +2 -2
  32. package/dist/breadcrumb/breadcrumb.js +12 -3
  33. package/dist/breadcrumb/breadcrumb.js.map +1 -1
  34. package/dist/brush-chart/brush-chart.d.ts +64 -0
  35. package/dist/brush-chart/brush-chart.js +1056 -0
  36. package/dist/brush-chart/brush-chart.js.map +1 -0
  37. package/dist/brush-chart/index.d.ts +2 -0
  38. package/dist/brush-chart/index.js +2 -0
  39. package/dist/button/button.d.ts +2 -2
  40. package/dist/button/button.js +31 -21
  41. package/dist/button/button.js.map +1 -1
  42. package/dist/button-group/button-group.js +3 -4
  43. package/dist/button-group/button-group.js.map +1 -1
  44. package/dist/card/card.js +1 -1
  45. package/dist/card/card.js.map +1 -1
  46. package/dist/card-stack/card-stack.d.ts +54 -0
  47. package/dist/card-stack/card-stack.js +492 -0
  48. package/dist/card-stack/card-stack.js.map +1 -0
  49. package/dist/card-stack/index.d.ts +2 -0
  50. package/dist/card-stack/index.js +2 -0
  51. package/dist/carousel/carousel.d.ts +56 -0
  52. package/dist/carousel/carousel.js +389 -0
  53. package/dist/carousel/carousel.js.map +1 -0
  54. package/dist/carousel/index.d.ts +2 -0
  55. package/dist/carousel/index.js +2 -0
  56. package/dist/checkbox/checkbox.js +4 -4
  57. package/dist/checkbox/checkbox.js.map +1 -1
  58. package/dist/chip-group/chip-group.d.ts +37 -0
  59. package/dist/chip-group/chip-group.js +148 -0
  60. package/dist/chip-group/chip-group.js.map +1 -0
  61. package/dist/chip-group/index.d.ts +2 -0
  62. package/dist/chip-group/index.js +2 -0
  63. package/dist/countdown/countdown.d.ts +45 -0
  64. package/dist/countdown/countdown.js +298 -0
  65. package/dist/countdown/countdown.js.map +1 -0
  66. package/dist/countdown/index.d.ts +2 -0
  67. package/dist/countdown/index.js +2 -0
  68. package/dist/date-picker/picker-parts.js +1 -1
  69. package/dist/date-picker/picker-parts.js.map +1 -1
  70. package/dist/dialog/dialog.js +1 -1
  71. package/dist/dialog/dialog.js.map +1 -1
  72. package/dist/donut-chart/donut-chart.d.ts +64 -0
  73. package/dist/donut-chart/donut-chart.js +576 -0
  74. package/dist/donut-chart/donut-chart.js.map +1 -0
  75. package/dist/donut-chart/index.d.ts +2 -0
  76. package/dist/donut-chart/index.js +2 -0
  77. package/dist/empty-state/empty-state.d.ts +21 -10
  78. package/dist/empty-state/empty-state.js +32 -26
  79. package/dist/empty-state/empty-state.js.map +1 -1
  80. package/dist/empty-state/index.d.ts +2 -2
  81. package/dist/empty-state/index.js +2 -2
  82. package/dist/file-drop-zone/file-drop-zone.js +1 -1
  83. package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
  84. package/dist/filter-bar/filter-bar.d.ts +47 -26
  85. package/dist/filter-bar/filter-bar.js +272 -156
  86. package/dist/filter-bar/filter-bar.js.map +1 -1
  87. package/dist/gauge/gauge.d.ts +45 -0
  88. package/dist/gauge/gauge.js +219 -0
  89. package/dist/gauge/gauge.js.map +1 -0
  90. package/dist/gauge/index.d.ts +2 -0
  91. package/dist/gauge/index.js +2 -0
  92. package/dist/in-view-title/in-view-title.d.ts +33 -0
  93. package/dist/in-view-title/in-view-title.js +119 -0
  94. package/dist/in-view-title/in-view-title.js.map +1 -0
  95. package/dist/in-view-title/index.d.ts +2 -0
  96. package/dist/in-view-title/index.js +2 -0
  97. package/dist/index.d.ts +49 -6
  98. package/dist/index.js +48 -8
  99. package/dist/lib/cn.js +11 -1
  100. package/dist/lib/cn.js.map +1 -1
  101. package/dist/lib/field-message.js +28 -10
  102. package/dist/lib/field-message.js.map +1 -1
  103. package/dist/lib/field-surface.js +9 -8
  104. package/dist/lib/field-surface.js.map +1 -1
  105. package/dist/lib/flag-emoji.js +50 -0
  106. package/dist/lib/flag-emoji.js.map +1 -0
  107. package/dist/lib/flip.js +11 -3
  108. package/dist/lib/flip.js.map +1 -1
  109. package/dist/lib/menu-highlight.js +64 -0
  110. package/dist/lib/menu-highlight.js.map +1 -0
  111. package/dist/lib/motion.js +9 -2
  112. package/dist/lib/motion.js.map +1 -1
  113. package/dist/lib/rolling-value.js +1 -1
  114. package/dist/lib/rolling-value.js.map +1 -1
  115. package/dist/lib/spring-tween.js +500 -0
  116. package/dist/lib/spring-tween.js.map +1 -0
  117. package/dist/lib/swap-text.js +3 -1
  118. package/dist/lib/swap-text.js.map +1 -1
  119. package/dist/line-chart/index.d.ts +2 -0
  120. package/dist/line-chart/index.js +2 -0
  121. package/dist/line-chart/line-chart.d.ts +76 -0
  122. package/dist/line-chart/line-chart.js +611 -0
  123. package/dist/line-chart/line-chart.js.map +1 -0
  124. package/dist/mention-input/index.d.ts +2 -0
  125. package/dist/mention-input/index.js +2 -0
  126. package/dist/mention-input/mention-input.d.ts +98 -0
  127. package/dist/mention-input/mention-input.js +576 -0
  128. package/dist/mention-input/mention-input.js.map +1 -0
  129. package/dist/menu/menu.d.ts +2 -0
  130. package/dist/menu/menu.js +23 -12
  131. package/dist/menu/menu.js.map +1 -1
  132. package/dist/money-input/index.d.ts +2 -0
  133. package/dist/money-input/index.js +2 -0
  134. package/dist/money-input/money-input.d.ts +65 -0
  135. package/dist/money-input/money-input.js +607 -0
  136. package/dist/money-input/money-input.js.map +1 -0
  137. package/dist/morph-select/index.d.ts +2 -0
  138. package/dist/morph-select/index.js +2 -0
  139. package/dist/morph-select/morph-select.d.ts +71 -0
  140. package/dist/morph-select/morph-select.js +693 -0
  141. package/dist/morph-select/morph-select.js.map +1 -0
  142. package/dist/multi-select/index.d.ts +2 -0
  143. package/dist/multi-select/index.js +2 -0
  144. package/dist/multi-select/multi-select.d.ts +36 -0
  145. package/dist/multi-select/multi-select.js +152 -0
  146. package/dist/multi-select/multi-select.js.map +1 -0
  147. package/dist/number-field/index.d.ts +2 -0
  148. package/dist/number-field/index.js +2 -0
  149. package/dist/number-field/number-field.d.ts +42 -0
  150. package/dist/number-field/number-field.js +199 -0
  151. package/dist/number-field/number-field.js.map +1 -0
  152. package/dist/onboarding-checklist/index.d.ts +2 -0
  153. package/dist/onboarding-checklist/index.js +2 -0
  154. package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
  155. package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
  156. package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
  157. package/dist/password-field/password-field.js +2 -2
  158. package/dist/password-field/password-field.js.map +1 -1
  159. package/dist/phone-input/index.d.ts +3 -0
  160. package/dist/phone-input/index.js +3 -0
  161. package/dist/phone-input/phone-countries.d.ts +29 -0
  162. package/dist/phone-input/phone-countries.js +170 -0
  163. package/dist/phone-input/phone-countries.js.map +1 -0
  164. package/dist/phone-input/phone-input.d.ts +66 -0
  165. package/dist/phone-input/phone-input.js +440 -0
  166. package/dist/phone-input/phone-input.js.map +1 -0
  167. package/dist/popover/popover.d.ts +12 -0
  168. package/dist/popover/popover.js +31 -7
  169. package/dist/popover/popover.js.map +1 -1
  170. package/dist/progress/progress.js +1 -1
  171. package/dist/radio-group/radio-group.js +1 -1
  172. package/dist/radio-group/radio-group.js.map +1 -1
  173. package/dist/reactions/index.d.ts +2 -0
  174. package/dist/reactions/index.js +2 -0
  175. package/dist/reactions/reactions.d.ts +50 -0
  176. package/dist/reactions/reactions.js +315 -0
  177. package/dist/reactions/reactions.js.map +1 -0
  178. package/dist/rich-text-editor/index.d.ts +3 -0
  179. package/dist/rich-text-editor/index.js +2 -0
  180. package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
  181. package/dist/rich-text-editor/rich-text-editor.js +909 -0
  182. package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
  183. package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
  184. package/dist/rich-text-editor/rich-text-engine.js +366 -0
  185. package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
  186. package/dist/rich-text-editor/rich-text-html.js +174 -0
  187. package/dist/rich-text-editor/rich-text-html.js.map +1 -0
  188. package/dist/rich-text-editor/rich-text-icons.js +43 -0
  189. package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
  190. package/dist/ridgeline/index.d.ts +2 -0
  191. package/dist/ridgeline/index.js +2 -0
  192. package/dist/ridgeline/ridgeline.d.ts +55 -0
  193. package/dist/ridgeline/ridgeline.js +701 -0
  194. package/dist/ridgeline/ridgeline.js.map +1 -0
  195. package/dist/scroll-area/index.d.ts +2 -2
  196. package/dist/scroll-area/index.js +2 -2
  197. package/dist/scroll-area/scroll-area.d.ts +68 -31
  198. package/dist/scroll-area/scroll-area.js +208 -59
  199. package/dist/scroll-area/scroll-area.js.map +1 -1
  200. package/dist/scrub-input/evaluate.d.ts +14 -0
  201. package/dist/scrub-input/evaluate.js +72 -0
  202. package/dist/scrub-input/evaluate.js.map +1 -0
  203. package/dist/scrub-input/index.d.ts +3 -0
  204. package/dist/scrub-input/index.js +3 -0
  205. package/dist/scrub-input/scrub-input.d.ts +74 -0
  206. package/dist/scrub-input/scrub-input.js +486 -0
  207. package/dist/scrub-input/scrub-input.js.map +1 -0
  208. package/dist/search-field/search-field.d.ts +2 -1
  209. package/dist/search-field/search-field.js +4 -4
  210. package/dist/search-field/search-field.js.map +1 -1
  211. package/dist/select/select.d.ts +3 -1
  212. package/dist/select/select.js +20 -8
  213. package/dist/select/select.js.map +1 -1
  214. package/dist/separator/separator.d.ts +1 -1
  215. package/dist/sheet/sheet.js +2 -1
  216. package/dist/sheet/sheet.js.map +1 -1
  217. package/dist/shortcut-recorder/index.d.ts +3 -0
  218. package/dist/shortcut-recorder/index.js +3 -0
  219. package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
  220. package/dist/shortcut-recorder/shortcut-model.js +206 -0
  221. package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
  222. package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
  223. package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
  224. package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
  225. package/dist/slider/index.d.ts +2 -2
  226. package/dist/slider/slider.d.ts +46 -14
  227. package/dist/slider/slider.js +151 -67
  228. package/dist/slider/slider.js.map +1 -1
  229. package/dist/slope-chart/index.d.ts +2 -0
  230. package/dist/slope-chart/index.js +2 -0
  231. package/dist/slope-chart/slope-chart.d.ts +54 -0
  232. package/dist/slope-chart/slope-chart.js +568 -0
  233. package/dist/slope-chart/slope-chart.js.map +1 -0
  234. package/dist/sparkline/index.d.ts +2 -0
  235. package/dist/sparkline/index.js +2 -0
  236. package/dist/sparkline/sparkline.d.ts +46 -0
  237. package/dist/sparkline/sparkline.js +390 -0
  238. package/dist/sparkline/sparkline.js.map +1 -0
  239. package/dist/split-bar/index.d.ts +2 -0
  240. package/dist/split-bar/index.js +2 -0
  241. package/dist/split-bar/split-bar.d.ts +62 -0
  242. package/dist/split-bar/split-bar.js +513 -0
  243. package/dist/split-bar/split-bar.js.map +1 -0
  244. package/dist/split-button/split-button.d.ts +1 -1
  245. package/dist/split-button/split-button.js +12 -11
  246. package/dist/split-button/split-button.js.map +1 -1
  247. package/dist/stat-card/index.d.ts +2 -2
  248. package/dist/stat-card/index.js +2 -2
  249. package/dist/stat-card/stat-card.d.ts +24 -4
  250. package/dist/stat-card/stat-card.js +114 -17
  251. package/dist/stat-card/stat-card.js.map +1 -1
  252. package/dist/stepper/index.d.ts +2 -0
  253. package/dist/stepper/index.js +2 -0
  254. package/dist/stepper/stepper.d.ts +51 -0
  255. package/dist/stepper/stepper.js +215 -0
  256. package/dist/stepper/stepper.js.map +1 -0
  257. package/dist/streamgraph/index.d.ts +2 -0
  258. package/dist/streamgraph/index.js +2 -0
  259. package/dist/streamgraph/streamgraph.d.ts +70 -0
  260. package/dist/streamgraph/streamgraph.js +613 -0
  261. package/dist/streamgraph/streamgraph.js.map +1 -0
  262. package/dist/styles.css +1 -1
  263. package/dist/swipe-actions/index.d.ts +2 -0
  264. package/dist/swipe-actions/index.js +2 -0
  265. package/dist/swipe-actions/swipe-actions.d.ts +67 -0
  266. package/dist/swipe-actions/swipe-actions.js +552 -0
  267. package/dist/swipe-actions/swipe-actions.js.map +1 -0
  268. package/dist/switch/switch.js +2 -2
  269. package/dist/switch/switch.js.map +1 -1
  270. package/dist/table/index.d.ts +2 -2
  271. package/dist/table/index.js +2 -2
  272. package/dist/table/table.d.ts +56 -4
  273. package/dist/table/table.js +340 -165
  274. package/dist/table/table.js.map +1 -1
  275. package/dist/tabs/tabs.js +2 -2
  276. package/dist/tabs/tabs.js.map +1 -1
  277. package/dist/tag-input/index.d.ts +2 -0
  278. package/dist/tag-input/index.js +2 -0
  279. package/dist/tag-input/tag-input.d.ts +40 -0
  280. package/dist/tag-input/tag-input.js +269 -0
  281. package/dist/tag-input/tag-input.js.map +1 -0
  282. package/dist/text-morph/index.d.ts +2 -0
  283. package/dist/text-morph/index.js +2 -0
  284. package/dist/text-morph/text-morph.d.ts +23 -0
  285. package/dist/text-morph/text-morph.js +21 -0
  286. package/dist/text-morph/text-morph.js.map +1 -0
  287. package/dist/text-reveal/index.d.ts +2 -0
  288. package/dist/text-reveal/index.js +2 -0
  289. package/dist/text-reveal/text-reveal.d.ts +27 -0
  290. package/dist/text-reveal/text-reveal.js +51 -0
  291. package/dist/text-reveal/text-reveal.js.map +1 -0
  292. package/dist/text-shimmer/index.d.ts +2 -0
  293. package/dist/text-shimmer/index.js +2 -0
  294. package/dist/text-shimmer/text-shimmer.d.ts +29 -0
  295. package/dist/text-shimmer/text-shimmer.js +29 -0
  296. package/dist/text-shimmer/text-shimmer.js.map +1 -0
  297. package/dist/time-picker/time-picker.js +2 -2
  298. package/dist/time-picker/time-picker.js.map +1 -1
  299. package/dist/toast/index.d.ts +2 -2
  300. package/dist/toast/index.js +2 -2
  301. package/dist/toast/toast.d.ts +6 -0
  302. package/dist/toast/toast.js +82 -40
  303. package/dist/toast/toast.js.map +1 -1
  304. package/dist/tooltip/tooltip.d.ts +9 -2
  305. package/dist/tooltip/tooltip.js +7 -7
  306. package/dist/tooltip/tooltip.js.map +1 -1
  307. package/dist/treemap/index.d.ts +2 -0
  308. package/dist/treemap/index.js +2 -0
  309. package/dist/treemap/treemap.d.ts +53 -0
  310. package/dist/treemap/treemap.js +699 -0
  311. package/dist/treemap/treemap.js.map +1 -0
  312. package/dist/usage-meter/index.d.ts +2 -0
  313. package/dist/usage-meter/index.js +2 -0
  314. package/dist/usage-meter/usage-meter.d.ts +50 -0
  315. package/dist/usage-meter/usage-meter.js +440 -0
  316. package/dist/usage-meter/usage-meter.js.map +1 -0
  317. package/dist/waffle-chart/index.d.ts +2 -0
  318. package/dist/waffle-chart/index.js +2 -0
  319. package/dist/waffle-chart/waffle-chart.d.ts +57 -0
  320. package/dist/waffle-chart/waffle-chart.js +426 -0
  321. package/dist/waffle-chart/waffle-chart.js.map +1 -0
  322. package/package.json +165 -3
@@ -0,0 +1,611 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { mergeRefs } from "../lib/merge-refs.js";
4
+ import { prefersReducedMotion, useIsomorphicLayoutEffect } from "../lib/motion.js";
5
+ import { SMOOTH, SNAPPY, tween } from "../lib/spring-tween.js";
6
+ import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/line-chart/line-chart.tsx
9
+ /** Room above the top gridline, and the gap between the crosshair and its tooltip. */
10
+ const TOP = 12;
11
+ const GAP = 14;
12
+ /** The first draw: 480ms x 1.75, slow at both ends. */
13
+ const LINE_CHART_DRAW_MS = 840;
14
+ const PALETTE = [
15
+ "var(--color-chart-1)",
16
+ "color-mix(in oklab, var(--color-foreground) 46%, var(--color-surface))",
17
+ "color-mix(in oklab, var(--color-foreground) 26%, var(--color-surface))"
18
+ ];
19
+ const GROUPED = new Intl.NumberFormat("en-US", { maximumFractionDigits: 1 });
20
+ const COMPACT = new Intl.NumberFormat("en-US", {
21
+ notation: "compact",
22
+ maximumFractionDigits: 1
23
+ });
24
+ const GRID = Array.from({ length: 241 }, (_, index) => index / 240);
25
+ const clamp = (value, low, high) => Math.min(high, Math.max(low, value));
26
+ const colorOf = (line, index) => line.color ?? PALETTE[Math.min(index, PALETTE.length - 1)];
27
+ const clean = (value) => Number(value.toPrecision(12));
28
+ /** Clean gridlines: the smallest step of 1, 2, 2.5 or 5 that covers the data in four rows or fewer, from zero unless the data dips below it. */
29
+ function niceScale(low, high) {
30
+ const lo = Math.min(0, low);
31
+ const hi = Math.max(high, lo + 1);
32
+ const magnitude = 10 ** Math.floor(Math.log10((hi - lo) / 4));
33
+ for (const factor of [
34
+ 1,
35
+ 2,
36
+ 2.5,
37
+ 5,
38
+ 10
39
+ ]) {
40
+ const step = factor * magnitude;
41
+ const bottom = Math.floor(lo / step) * step;
42
+ const top = Math.ceil(hi / step) * step;
43
+ if ((top - bottom) / step <= 4 + 1e-9) {
44
+ const ticks = [];
45
+ for (let value = bottom; value <= top + step / 2; value += step) ticks.push(clean(value));
46
+ return {
47
+ min: clean(bottom),
48
+ max: clean(top),
49
+ ticks
50
+ };
51
+ }
52
+ }
53
+ return {
54
+ min: lo,
55
+ max: hi,
56
+ ticks: [lo, hi]
57
+ };
58
+ }
59
+ /** The line through every value, sampled densely on x from 0 to 1 in data units. Monotone tangents keep a smooth curve from swinging past the data. */
60
+ function curveFor(values, smooth) {
61
+ if (values.length === 0) return [];
62
+ if (values.length === 1) return [[0, values[0]], [1, values[0]]];
63
+ const last = values.length - 1;
64
+ const step = 1 / last;
65
+ if (!smooth) return values.map((value, index) => [index * step, value]);
66
+ const slopes = values.slice(1).map((value, index) => (value - values[index]) / step);
67
+ const tangents = values.map((_, index) => index === 0 || index === last ? 0 : (Math.sign(slopes[index - 1]) + Math.sign(slopes[index])) * Math.min(Math.abs(slopes[index - 1]), Math.abs(slopes[index]), Math.abs(slopes[index - 1] + slopes[index]) / 4) || 0);
68
+ if (last > 1) {
69
+ tangents[0] = (3 * slopes[0] - tangents[1]) / 2;
70
+ tangents[last] = (3 * slopes[last - 1] - tangents[last - 1]) / 2;
71
+ } else {
72
+ tangents[0] = slopes[0];
73
+ tangents[1] = slopes[0];
74
+ }
75
+ const samples = Math.max(1, Math.min(16, Math.round(192 / last)));
76
+ const points = [];
77
+ for (let index = 0; index < last; index++) {
78
+ const low = Math.min(values[index], values[index + 1]);
79
+ const high = Math.max(values[index], values[index + 1]);
80
+ for (let sample = 0; sample < samples; sample++) {
81
+ const t = sample / samples;
82
+ const t2 = t * t;
83
+ const t3 = t2 * t;
84
+ const value = (2 * t3 - 3 * t2 + 1) * values[index] + (t3 - 2 * t2 + t) * step * tangents[index] + (3 * t2 - 2 * t3) * values[index + 1] + (t3 - t2) * step * tangents[index + 1];
85
+ points.push([(index + t) * step, clamp(value, low, high)]);
86
+ }
87
+ }
88
+ points.push([1, values[last]]);
89
+ return points;
90
+ }
91
+ /** Value of a sampled line at x, straight between samples. */
92
+ function valueAt(shape, x) {
93
+ if (!shape.length) return 0;
94
+ let low = 0;
95
+ let high = shape.length - 1;
96
+ if (x <= shape[low][0]) return shape[low][1];
97
+ if (x >= shape[high][0]) return shape[high][1];
98
+ while (high - low > 1) {
99
+ const middle = low + high >> 1;
100
+ if (shape[middle][0] < x) low = middle;
101
+ else high = middle;
102
+ }
103
+ const [x0, y0] = shape[low];
104
+ const [x1, y1] = shape[high];
105
+ return x1 === x0 ? y1 : y0 + (y1 - y0) * (x - x0) / (x1 - x0);
106
+ }
107
+ /** Axis labels that fit: at most one per 76 pixels, counted back from the latest so the newest always shows. */
108
+ function axisPicks(data, width) {
109
+ const labeled = data.flatMap((item, index) => item.axisLabel ? [index] : []);
110
+ if (!width || !labeled.length) return [];
111
+ const room = Math.max(2, Math.floor(width / 76));
112
+ const stride = Math.ceil(labeled.length / room);
113
+ return labeled.reverse().filter((_, rank) => rank % stride === 0).reverse();
114
+ }
115
+ const LineChart = forwardRef(function LineChart({ data, series, label, unit = "", height = 220, formatValue, formatTick = (value) => COMPACT.format(value), hiddenSeries, defaultHiddenSeries, onHiddenSeriesChange, onActiveChange, loading = false, emptyLabel = "No data for this range", legend, categoryLabel = "Date", curve = "smooth", className }, ref) {
116
+ const figure = useRef(null);
117
+ const refs = useMemo(() => mergeRefs(ref, figure), [ref]);
118
+ const plot = useRef(null);
119
+ const svg = useRef(null);
120
+ const tip = useRef(null);
121
+ const gutter = useRef(null);
122
+ const uid = `line${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
123
+ const last = data.length - 1;
124
+ const empty = data.length === 0;
125
+ const [ownHidden, setOwnHidden] = useState(defaultHiddenSeries ?? []);
126
+ const hidden = hiddenSeries ?? ownHidden;
127
+ const toggle = (key) => {
128
+ const next = hidden.includes(key) ? hidden.filter((item) => item !== key) : [...hidden, key];
129
+ if (hiddenSeries === void 0) setOwnHidden(next);
130
+ onHiddenSeriesChange?.(next);
131
+ };
132
+ const visible = series.filter((line) => !hidden.includes(line.key));
133
+ const showLegend = legend ?? series.length > 1;
134
+ const signature = `${curve}|${series.map((line) => line.key).join(",")}|${data.map((item) => `${item.key}:${series.map((line) => item.values[line.key] ?? 0).join(",")}`).join(";")}`;
135
+ const targets = useMemo(() => new Map(series.map((line) => [line.key, curveFor(data.map((item) => item.values[line.key] ?? 0), curve === "smooth")])), [signature]);
136
+ let low = Infinity;
137
+ let high = -Infinity;
138
+ for (const line of visible) for (const item of data) {
139
+ const value = item.values[line.key] ?? 0;
140
+ low = Math.min(low, value);
141
+ high = Math.max(high, value);
142
+ }
143
+ const range = Number.isFinite(low) ? niceScale(low, high) : null;
144
+ const [steady, setSteady] = useState(() => range ?? niceScale(0, 100));
145
+ if (range && (range.min !== steady.min || range.max !== steady.max)) setSteady(range);
146
+ const [active, setActive] = useState(null);
147
+ const [seen, setSeen] = useState(signature);
148
+ if (seen !== signature) {
149
+ setSeen(signature);
150
+ setActive(null);
151
+ }
152
+ const index = active === null || empty ? null : Math.min(active, last);
153
+ const scrubbing = index !== null;
154
+ const width = useRef(0);
155
+ const [plotWidth, setPlotWidth] = useState(0);
156
+ const [drawn, setDrawn] = useState(false);
157
+ const tracks = useRef(/* @__PURE__ */ new Map());
158
+ const scale = useRef({
159
+ min: steady.min,
160
+ max: steady.max
161
+ });
162
+ const cursor = useRef(1);
163
+ const live = useRef({
164
+ series,
165
+ hidden
166
+ });
167
+ useIsomorphicLayoutEffect(() => {
168
+ live.current = {
169
+ series,
170
+ hidden
171
+ };
172
+ });
173
+ /** One writer for every drawn attribute: morphs, toggles, the scale, the crosshair and the size all repaint through here. */
174
+ const paint = useCallback(() => {
175
+ const w = width.current;
176
+ const root = svg.current;
177
+ if (!w || !root) return;
178
+ const lo = scale.current.min;
179
+ const span = scale.current.max - lo || 1;
180
+ const toY = (value) => TOP + (1 - (value - lo) / span) * (height - TOP);
181
+ const at = cursor.current;
182
+ const dotYs = [];
183
+ for (const line of live.current.series) {
184
+ const track = tracks.current.get(line.key);
185
+ if (!track) continue;
186
+ const p = track.presence;
187
+ const y = (value) => toY(lo + (value - lo) * p);
188
+ const path = track.shape.length ? `M${track.shape.map(([x, value]) => `${(x * w).toFixed(1)},${y(value).toFixed(1)}`).join("L")}` : "";
189
+ const group = root.querySelector(`[data-series="${CSS.escape(line.key)}"]`);
190
+ group?.querySelector("[data-part=line]")?.setAttribute("d", path);
191
+ group?.querySelector("[data-part=area]")?.setAttribute("d", path ? `${path}L${w},${height}L0,${height}Z` : "");
192
+ if (group) group.style.opacity = String(p);
193
+ const dot = root.querySelector(`[data-dot="${CSS.escape(line.key)}"]`);
194
+ if (dot && track.shape.length) {
195
+ const cy = y(valueAt(track.shape, at));
196
+ dot.setAttribute("cx", (at * w).toFixed(1));
197
+ dot.setAttribute("cy", cy.toFixed(1));
198
+ if (p > .5) dotYs.push(cy);
199
+ }
200
+ }
201
+ for (const node of root.querySelectorAll("[data-grid]")) {
202
+ const yy = String(Math.round(toY(Number(node.dataset.grid))) + .5);
203
+ node.setAttribute("y1", yy);
204
+ node.setAttribute("y2", yy);
205
+ }
206
+ for (const node of gutter.current?.querySelectorAll("[data-tick]") ?? []) node.style.translate = `0 ${Math.round(toY(Number(node.dataset.tick)))}px`;
207
+ const cx = Math.round(at * w) + .5;
208
+ const cross = root.querySelector("[data-part=crosshair]");
209
+ cross?.setAttribute("x1", String(cx));
210
+ cross?.setAttribute("x2", String(cx));
211
+ const bubble = tip.current;
212
+ if (bubble) {
213
+ const tw = bubble.offsetWidth;
214
+ const th = bubble.offsetHeight;
215
+ let x = cx + GAP;
216
+ if (x + tw > w) x = cx - GAP - tw;
217
+ const middle = dotYs.length ? dotYs.reduce((sum, value) => sum + value, 0) / dotYs.length : height / 2;
218
+ bubble.style.translate = `${clamp(x, 0, Math.max(0, w - tw))}px ${clamp(middle - th / 2, 0, Math.max(0, height - th))}px`;
219
+ }
220
+ }, [height]);
221
+ useIsomorphicLayoutEffect(() => {
222
+ const node = plot.current;
223
+ if (!node) return;
224
+ const read = () => {
225
+ width.current = node.clientWidth;
226
+ setPlotWidth(node.clientWidth);
227
+ paint();
228
+ };
229
+ read();
230
+ if (typeof ResizeObserver === "undefined") return;
231
+ const observer = new ResizeObserver(read);
232
+ observer.observe(node);
233
+ return () => observer.disconnect();
234
+ }, [paint]);
235
+ useIsomorphicLayoutEffect(paint);
236
+ useEffect(() => {
237
+ const moves = [];
238
+ for (const [key, target] of targets) {
239
+ const track = tracks.current.get(key);
240
+ if (!track) {
241
+ tracks.current.set(key, {
242
+ shape: target,
243
+ presence: live.current.hidden.includes(key) ? 0 : 1,
244
+ stop: null
245
+ });
246
+ continue;
247
+ }
248
+ if (!track.shape.length || !target.length) {
249
+ track.shape = target;
250
+ continue;
251
+ }
252
+ const xs = [.../* @__PURE__ */ new Set([...GRID, ...target.map(([x]) => x)])].sort((a, b) => a - b);
253
+ moves.push({
254
+ track,
255
+ xs,
256
+ from: xs.map((x) => valueAt(track.shape, x)),
257
+ to: xs.map((x) => valueAt(target, x)),
258
+ target
259
+ });
260
+ }
261
+ if (!moves.length) {
262
+ paint();
263
+ return;
264
+ }
265
+ return tween(SMOOTH, (p) => {
266
+ for (const move of moves) move.track.shape = move.xs.map((x, at) => [x, move.from[at] + (move.to[at] - move.from[at]) * p]);
267
+ paint();
268
+ }, () => {
269
+ for (const move of moves) move.track.shape = move.target;
270
+ paint();
271
+ });
272
+ }, [paint, targets]);
273
+ const hiddenKey = hidden.join("|");
274
+ useEffect(() => {
275
+ for (const [key, track] of tracks.current) {
276
+ const to = hidden.includes(key) ? 0 : 1;
277
+ if (track.presence === to) continue;
278
+ track.stop?.();
279
+ const from = track.presence;
280
+ track.stop = tween(SMOOTH, (p) => {
281
+ track.presence = from + (to - from) * p;
282
+ paint();
283
+ });
284
+ }
285
+ paint();
286
+ }, [hiddenKey, paint]);
287
+ useEffect(() => {
288
+ const from = { ...scale.current };
289
+ if (from.min === steady.min && from.max === steady.max) return;
290
+ return tween(SMOOTH, (p) => {
291
+ scale.current = {
292
+ min: from.min + (steady.min - from.min) * p,
293
+ max: from.max + (steady.max - from.max) * p
294
+ };
295
+ paint();
296
+ });
297
+ }, [paint, steady]);
298
+ useEffect(() => {
299
+ if (empty || drawn) return;
300
+ const node = figure.current;
301
+ if (!node || prefersReducedMotion() || typeof IntersectionObserver === "undefined") {
302
+ setDrawn(true);
303
+ return;
304
+ }
305
+ const observer = new IntersectionObserver(([entry]) => {
306
+ if (!entry?.isIntersecting) return;
307
+ observer.disconnect();
308
+ setDrawn(true);
309
+ }, { threshold: .3 });
310
+ observer.observe(node);
311
+ return () => observer.disconnect();
312
+ }, [drawn, empty]);
313
+ const wasScrubbing = useRef(false);
314
+ useEffect(() => {
315
+ if (index === null) {
316
+ wasScrubbing.current = false;
317
+ return;
318
+ }
319
+ const to = last > 0 ? index / last : .5;
320
+ const from = cursor.current;
321
+ if (!wasScrubbing.current) {
322
+ wasScrubbing.current = true;
323
+ cursor.current = to;
324
+ paint();
325
+ return;
326
+ }
327
+ return tween(SNAPPY, (p) => {
328
+ cursor.current = from + (to - from) * p;
329
+ paint();
330
+ });
331
+ }, [
332
+ index,
333
+ last,
334
+ paint
335
+ ]);
336
+ const onActive = useRef(onActiveChange);
337
+ useIsomorphicLayoutEffect(() => {
338
+ onActive.current = onActiveChange;
339
+ });
340
+ useEffect(() => {
341
+ onActive.current?.(index, index === null ? null : data[index] ?? null);
342
+ }, [index]);
343
+ const pointAt = (clientX) => {
344
+ const rect = plot.current?.getBoundingClientRect();
345
+ if (!rect?.width || last < 1) return Math.max(0, last);
346
+ return clamp(Math.round((clientX - rect.left) / rect.width * last), 0, last);
347
+ };
348
+ const onKeyDown = (event) => {
349
+ if (event.key === "Escape" && scrubbing) {
350
+ event.preventDefault();
351
+ setActive(null);
352
+ return;
353
+ }
354
+ const from = index ?? last + 1;
355
+ const page = Math.max(1, Math.round(data.length / 6));
356
+ const next = {
357
+ ArrowLeft: from - 1,
358
+ ArrowDown: from - 1,
359
+ ArrowRight: index === null ? last : from + 1,
360
+ ArrowUp: index === null ? last : from + 1,
361
+ PageDown: from - page,
362
+ PageUp: from + page,
363
+ Home: 0,
364
+ End: last
365
+ }[event.key];
366
+ if (next === void 0 || empty) return;
367
+ event.preventDefault();
368
+ setActive(clamp(next, 0, last));
369
+ };
370
+ const onPointerDown = (event) => {
371
+ if (empty) return;
372
+ if (event.pointerType !== "mouse") event.currentTarget.setPointerCapture?.(event.pointerId);
373
+ setActive(pointAt(event.clientX));
374
+ };
375
+ const onPointerMove = (event) => {
376
+ if (!empty && (event.pointerType === "mouse" || event.buttons)) setActive(pointAt(event.clientX));
377
+ };
378
+ const format = (value, line) => `${formatValue ? formatValue(value, line) : GROUPED.format(value)}${unit ? ` ${unit}` : ""}`;
379
+ const reading = index === null ? null : data[index];
380
+ const shown = reading ?? data[last];
381
+ const valueText = reading ? `${reading.label}: ${visible.map((line) => `${line.label} ${format(reading.values[line.key] ?? 0, line)}`).join(", ")}` : empty ? emptyLabel : `${visible.length} of ${series.length} series shown`;
382
+ const summary = empty ? `${label}. ${emptyLabel}.` : `${label}, ${data[0].label} to ${data[last].label}. ${visible.map((line) => `${line.label}, latest ${format(data[last].values[line.key] ?? 0, line)}`).join(". ")}.`;
383
+ const picks = axisPicks(data, plotWidth);
384
+ const allHidden = !empty && visible.length === 0;
385
+ return /* @__PURE__ */ jsxs("figure", {
386
+ ref: refs,
387
+ "data-slot": "line-chart",
388
+ "aria-label": label,
389
+ "aria-busy": loading || void 0,
390
+ className: cn("m-0 grid min-w-0 gap-4 box-border font-sans text-foreground", className),
391
+ children: [
392
+ showLegend ? /* @__PURE__ */ jsx("div", {
393
+ role: "group",
394
+ "aria-label": `${label} series`,
395
+ className: "-mx-2 flex min-w-0 flex-wrap gap-x-1 gap-y-0.5",
396
+ children: series.map((line, at) => /* @__PURE__ */ jsxs("button", {
397
+ type: "button",
398
+ "data-slot": "line-chart-toggle",
399
+ "aria-pressed": !hidden.includes(line.key),
400
+ onClick: () => toggle(line.key),
401
+ style: { "--series": colorOf(line, at) },
402
+ className: "chart-legend-toggle",
403
+ children: [/* @__PURE__ */ jsx("span", {
404
+ "aria-hidden": "true",
405
+ "data-dashed": line.dashed || void 0,
406
+ className: "chart-swatch"
407
+ }), /* @__PURE__ */ jsx("span", {
408
+ className: "chart-toggle-label",
409
+ children: line.label
410
+ })]
411
+ }, line.key))
412
+ }) : null,
413
+ /* @__PURE__ */ jsxs("div", {
414
+ "data-slot": "line-chart-chart",
415
+ "data-scrubbing": scrubbing || void 0,
416
+ "data-loading": loading || void 0,
417
+ "data-drawn": drawn || void 0,
418
+ className: "line-chart-frame",
419
+ children: [
420
+ /* @__PURE__ */ jsxs("div", {
421
+ ref: plot,
422
+ "data-slot": "line-chart-plot",
423
+ role: "slider",
424
+ tabIndex: empty ? -1 : 0,
425
+ "aria-label": `${label}, explore by ${categoryLabel.toLowerCase()}`,
426
+ "aria-orientation": "horizontal",
427
+ "aria-valuemin": 1,
428
+ "aria-valuemax": Math.max(1, data.length),
429
+ "aria-valuenow": (index ?? last) + 1,
430
+ "aria-valuetext": valueText,
431
+ onPointerDown,
432
+ onPointerMove,
433
+ onPointerUp: (event) => {
434
+ if (event.pointerType !== "mouse") setActive(null);
435
+ },
436
+ onPointerCancel: () => setActive(null),
437
+ onPointerLeave: (event) => {
438
+ if (event.pointerType === "mouse") setActive(null);
439
+ },
440
+ onKeyDown,
441
+ onBlur: () => setActive(null),
442
+ onFocus: (event) => {
443
+ if (event.currentTarget.matches(":focus-visible") && !empty) setActive((current) => current ?? last);
444
+ },
445
+ className: "line-chart-plot",
446
+ style: { height },
447
+ children: [
448
+ /* @__PURE__ */ jsxs("svg", {
449
+ ref: svg,
450
+ width: "100%",
451
+ height,
452
+ "aria-hidden": "true",
453
+ focusable: "false",
454
+ className: "block overflow-visible",
455
+ children: [
456
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", {
457
+ id: `${uid}-draw`,
458
+ clipPathUnits: "userSpaceOnUse",
459
+ children: /* @__PURE__ */ jsx("rect", {
460
+ className: "line-chart-reveal",
461
+ x: -8,
462
+ y: -16,
463
+ height: height + 32,
464
+ style: { width: drawn ? plotWidth + 16 : 0 }
465
+ })
466
+ }) }),
467
+ steady.ticks.slice(1).map((value) => /* @__PURE__ */ jsx("line", {
468
+ "data-grid": value,
469
+ className: "line-chart-grid",
470
+ x1: 0,
471
+ x2: "100%"
472
+ }, value)),
473
+ /* @__PURE__ */ jsx("line", {
474
+ className: "line-chart-baseline",
475
+ x1: 0,
476
+ x2: "100%",
477
+ y1: height - .5,
478
+ y2: height - .5
479
+ }),
480
+ /* @__PURE__ */ jsx("line", {
481
+ "data-part": "crosshair",
482
+ className: "line-chart-crosshair",
483
+ y1: 8,
484
+ y2: height
485
+ }),
486
+ /* @__PURE__ */ jsx("g", {
487
+ className: "line-chart-series",
488
+ clipPath: `url(#${uid}-draw)`,
489
+ children: series.map((line, at) => /* @__PURE__ */ jsxs("g", {
490
+ "data-series": line.key,
491
+ style: { "--series": colorOf(line, at) },
492
+ children: [line.area ?? at === 0 ? /* @__PURE__ */ jsx("path", {
493
+ "data-part": "area",
494
+ className: "line-chart-area"
495
+ }) : null, /* @__PURE__ */ jsx("path", {
496
+ "data-part": "line",
497
+ "data-dashed": line.dashed || void 0,
498
+ className: "line-chart-line"
499
+ })]
500
+ }, line.key))
501
+ }),
502
+ series.map((line, at) => /* @__PURE__ */ jsx("circle", {
503
+ "data-dot": line.key,
504
+ "data-hidden": hidden.includes(line.key) || void 0,
505
+ r: 4,
506
+ className: "line-chart-dot",
507
+ style: { "--series": colorOf(line, at) }
508
+ }, line.key))
509
+ ]
510
+ }),
511
+ /* @__PURE__ */ jsxs("div", {
512
+ ref: tip,
513
+ "aria-hidden": "true",
514
+ "data-slot": "line-chart-tooltip",
515
+ className: "line-chart-tooltip",
516
+ children: [/* @__PURE__ */ jsx("p", {
517
+ className: "m-0 mb-0.5 whitespace-nowrap text-xs tabular-nums text-text-secondary",
518
+ children: shown?.label ?? ""
519
+ }), visible.map((line) => /* @__PURE__ */ jsxs("p", {
520
+ className: "m-0 flex min-w-0 items-center gap-2 text-sm",
521
+ style: { "--series": colorOf(line, series.indexOf(line)) },
522
+ children: [
523
+ /* @__PURE__ */ jsx("span", {
524
+ "data-dashed": line.dashed || void 0,
525
+ className: "chart-tip-swatch"
526
+ }),
527
+ /* @__PURE__ */ jsx("span", {
528
+ className: "min-w-0 flex-1 truncate text-text-secondary",
529
+ children: line.label
530
+ }),
531
+ /* @__PURE__ */ jsx("span", {
532
+ className: "ms-2 flex-none whitespace-nowrap font-medium tabular-nums text-foreground",
533
+ children: format(shown?.values[line.key] ?? 0, line)
534
+ })
535
+ ]
536
+ }, line.key))]
537
+ }),
538
+ (empty || allHidden) && !loading ? /* @__PURE__ */ jsx("p", {
539
+ className: "absolute inset-0 m-0 grid place-items-center text-sm text-muted-foreground pointer-events-none motion-safe:animate-fade-in",
540
+ children: empty ? emptyLabel : "Choose a series to show"
541
+ }) : null,
542
+ loading && empty ? /* @__PURE__ */ jsx("span", {
543
+ "aria-hidden": "true",
544
+ className: "chart-skeleton"
545
+ }) : null
546
+ ]
547
+ }),
548
+ /* @__PURE__ */ jsx("div", {
549
+ ref: gutter,
550
+ "aria-hidden": "true",
551
+ className: "relative min-w-0 [grid-area:1/2]",
552
+ children: steady.ticks.map((value) => /* @__PURE__ */ jsx("span", {
553
+ "data-tick": value,
554
+ className: "line-chart-tick motion-safe:animate-fade-in",
555
+ children: /* @__PURE__ */ jsx("span", {
556
+ className: "block -translate-y-1/2 text-xs leading-none tabular-nums text-muted-foreground",
557
+ children: formatTick(value)
558
+ })
559
+ }, value))
560
+ }),
561
+ /* @__PURE__ */ jsx("div", {
562
+ "aria-hidden": "true",
563
+ className: "relative min-w-0 [grid-area:2/1]",
564
+ children: picks.map((at) => {
565
+ const share = last > 0 ? at / last : .5;
566
+ return /* @__PURE__ */ jsx("span", {
567
+ className: "absolute top-2 whitespace-nowrap text-xs leading-none tabular-nums text-muted-foreground motion-safe:animate-fade-in",
568
+ style: {
569
+ left: `${share * 100}%`,
570
+ translate: `${-share * 100}% 0`
571
+ },
572
+ children: data[at].axisLabel
573
+ }, `${data[at].key}:${data[at].axisLabel}`);
574
+ })
575
+ })
576
+ ]
577
+ }),
578
+ /* @__PURE__ */ jsx("p", {
579
+ className: "sr-only",
580
+ children: summary
581
+ }),
582
+ empty ? null : /* @__PURE__ */ jsx("div", {
583
+ className: "sr-only",
584
+ children: /* @__PURE__ */ jsxs("table", { children: [
585
+ /* @__PURE__ */ jsx("caption", { children: label }),
586
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("th", {
587
+ scope: "col",
588
+ children: categoryLabel
589
+ }), series.map((line) => /* @__PURE__ */ jsx("th", {
590
+ scope: "col",
591
+ children: line.label
592
+ }, line.key))] }) }),
593
+ /* @__PURE__ */ jsx("tbody", { children: data.map((item) => /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("th", {
594
+ scope: "row",
595
+ children: item.label
596
+ }), series.map((line) => /* @__PURE__ */ jsx("td", { children: format(item.values[line.key] ?? 0, line) }, line.key))] }, item.key)) })
597
+ ] })
598
+ }),
599
+ /* @__PURE__ */ jsx("p", {
600
+ className: "sr-only",
601
+ "aria-live": "polite",
602
+ "aria-atomic": "true",
603
+ children: loading ? "Loading" : ""
604
+ })
605
+ ]
606
+ });
607
+ });
608
+ //#endregion
609
+ export { LINE_CHART_DRAW_MS, LineChart };
610
+
611
+ //# sourceMappingURL=line-chart.js.map