@heyramzi/ui 6.1.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. package/COMPONENTS.md +32 -40
  2. package/README.md +10 -4
  3. package/package.json +31 -29
  4. package/scripts/sync-shadcn.mjs +29 -16
  5. package/scripts/sync-transitions.mjs +11 -11
  6. package/src/ai/ai-button/AiButton.svelte +86 -0
  7. package/src/ai/ai-button/index.ts +4 -0
  8. package/src/ai/confirmation/Confirmation.svelte +8 -8
  9. package/src/ai/confirmation/confirmation-context.svelte.ts +2 -2
  10. package/src/ai/generation-progress/GenerationProgress.svelte +354 -0
  11. package/src/ai/generation-progress/index.ts +7 -0
  12. package/src/ai/index.ts +16 -24
  13. package/src/ai/prompt-input/PromptInput.svelte +19 -1
  14. package/src/ai/prompt-input/PromptInputSubmit.svelte +9 -3
  15. package/src/ai/reasoning/Reasoning.svelte +11 -8
  16. package/src/ambient.d.ts +4 -0
  17. package/src/animations/README.md +17 -17
  18. package/src/animations/index.css +3 -14
  19. package/src/animations/upstream-coverage.test.ts +1 -1
  20. package/src/components/copy/clipboard.test.ts +206 -0
  21. package/src/components/copy/clipboard.ts +49 -0
  22. package/src/components/copy/copied.svelte.ts +39 -0
  23. package/src/components/copy/copy-button.svelte +175 -0
  24. package/src/components/copy/copy-button.test.ts +110 -0
  25. package/src/components/copy/index.ts +3 -0
  26. package/src/components/data-table/data-table.test.ts +134 -0
  27. package/src/components/data-table/data-table.ts +77 -0
  28. package/src/components/data-table/index.ts +20 -0
  29. package/src/components/data-table/infinite-scroll.svelte +69 -0
  30. package/src/components/data-table/pagination.svelte +62 -0
  31. package/src/components/data-table/row-arrow.svelte +28 -0
  32. package/src/components/data-table/row-context-menu.svelte +106 -0
  33. package/src/components/data-table/row-context-menu.test.ts +18 -0
  34. package/src/components/data-table/sortable-head.svelte +56 -0
  35. package/src/components/data-table/table-skeleton.svelte +83 -0
  36. package/src/components/hold-to-confirm/hold-to-confirm.svelte +232 -0
  37. package/src/components/hold-to-confirm/index.ts +1 -0
  38. package/src/components/icon/icon.svelte +23 -0
  39. package/src/components/icon/index.ts +1 -0
  40. package/src/components/language-flag/index.ts +1 -0
  41. package/src/components/language-flag/language-flag.svelte +60 -0
  42. package/src/components/list-page/detail-empty.svelte +50 -0
  43. package/src/components/list-page/detail-header.svelte +107 -0
  44. package/src/components/list-page/detail-panel.svelte +90 -0
  45. package/src/components/list-page/detail-row.svelte +36 -0
  46. package/src/components/list-page/detail-stack.svelte +33 -0
  47. package/src/components/list-page/empty-state.svelte +91 -0
  48. package/src/components/list-page/filter-item.svelte +49 -0
  49. package/src/components/list-page/filter-popover.svelte +88 -0
  50. package/src/components/list-page/filter-section.ts +12 -0
  51. package/src/components/list-page/grouped-list.svelte +116 -0
  52. package/src/components/list-page/index.ts +28 -0
  53. package/src/components/list-page/list-page-shell.svelte +195 -0
  54. package/src/components/list-page/list-table.ts +19 -0
  55. package/src/components/list-page/master-detail.svelte +206 -0
  56. package/src/components/list-page/master-detail.test.ts +37 -0
  57. package/src/components/list-page/notice-bar.svelte +68 -0
  58. package/src/components/list-page/prev-next.svelte +104 -0
  59. package/src/components/list-page/queue-rail.ts +25 -0
  60. package/src/components/list-page/rail-body.svelte +29 -0
  61. package/src/components/list-page/rail-empty.svelte +51 -0
  62. package/src/components/list-page/rail-head.svelte +49 -0
  63. package/src/components/list-page/rail-row.svelte +163 -0
  64. package/src/components/list-page/section-host.ts +24 -0
  65. package/src/components/list-page/section-label.svelte +39 -0
  66. package/src/components/list-page/step.test.ts +21 -0
  67. package/src/components/list-page/step.ts +10 -0
  68. package/src/components/list-page/toolbar-search.svelte +63 -0
  69. package/src/components/list-page/toolbar-trigger.ts +23 -0
  70. package/src/components/list-page/types.ts +1 -0
  71. package/src/components/locale-switch/index.ts +1 -0
  72. package/src/components/locale-switch/locale-switch.svelte +59 -0
  73. package/src/components/mark/index.ts +1 -0
  74. package/src/components/mark/mark.svelte +70 -0
  75. package/src/components/open-in/index.ts +6 -0
  76. package/src/components/open-in/open-in.svelte +106 -0
  77. package/src/components/picker/index.ts +4 -0
  78. package/src/components/picker/mark-picker.svelte +84 -0
  79. package/src/components/picker/picker.svelte +143 -0
  80. package/src/components/picker/trigger.ts +42 -0
  81. package/src/components/picker/types.ts +26 -0
  82. package/src/components/segmented/animated-tabs.svelte +175 -0
  83. package/src/components/segmented/collapse.test.ts +49 -0
  84. package/src/components/segmented/collapse.ts +39 -0
  85. package/src/components/segmented/index.ts +41 -0
  86. package/src/components/segmented/section-runs.svelte +79 -0
  87. package/src/components/segmented/segment.svelte +106 -0
  88. package/src/components/segmented/segmented-filter.svelte +125 -0
  89. package/src/components/segmented/segmented-nav.svelte +82 -0
  90. package/src/components/segmented/segmented-overflow.svelte +85 -0
  91. package/src/components/segmented/segmented-track.svelte +181 -0
  92. package/src/components/segmented/segmented.test.ts +20 -0
  93. package/src/components/segmented/segmented.ts +80 -0
  94. package/src/components/star-rating-input/index.ts +9 -0
  95. package/src/components/star-rating-input/star-rating-input.svelte +256 -0
  96. package/src/components/svg/index.ts +1 -0
  97. package/src/components/svg/svg.svelte +9 -0
  98. package/src/effects/border-beam/BorderBeam.svelte +1 -1
  99. package/src/effects/image-comparison/ImageComparison.css +42 -8
  100. package/src/effects/image-comparison/ImageComparison.svelte +138 -21
  101. package/src/effects/image-comparison/compare.test.ts +17 -0
  102. package/src/effects/image-comparison/compare.ts +5 -0
  103. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +338 -0
  104. package/src/effects/magic-card/MagicCard.css +47 -0
  105. package/src/effects/magic-card/MagicCard.svelte +57 -0
  106. package/src/effects/marquee/Marquee.css +27 -1
  107. package/src/effects/marquee/Marquee.svelte +67 -3
  108. package/src/effects/reveal/index.ts +2 -1
  109. package/src/effects/reveal/observe.test.ts +60 -0
  110. package/src/effects/reveal/observe.ts +33 -1
  111. package/src/effects/reveal/types.ts +7 -0
  112. package/src/effects/theme-toggle/ThemeToggle.css +199 -6
  113. package/src/effects/theme-toggle/ThemeToggle.svelte +51 -26
  114. package/src/gallery.test.ts +52 -0
  115. package/src/index.ts +5 -1
  116. package/src/lib/panel.ts +7 -1
  117. package/src/lib/sliding-pill.ts +68 -0
  118. package/src/lib/theme-transition.ts +41 -13
  119. package/src/lib/tone.ts +48 -0
  120. package/src/shadcn/accordion/accordion-trigger.svelte +15 -6
  121. package/src/shadcn/alert/alert.svelte +3 -1
  122. package/src/shadcn/badge/badge.svelte +8 -7
  123. package/src/shadcn/breadcrumb/breadcrumb-ellipsis.svelte +2 -2
  124. package/src/shadcn/breadcrumb/breadcrumb-separator.svelte +2 -2
  125. package/src/shadcn/calendar/calendar-month-select.svelte +2 -2
  126. package/src/shadcn/calendar/calendar-next-button.svelte +2 -2
  127. package/src/shadcn/calendar/calendar-prev-button.svelte +2 -2
  128. package/src/shadcn/calendar/calendar-year-select.svelte +2 -2
  129. package/src/shadcn/calendar/calendar.svelte +6 -0
  130. package/src/shadcn/code-block/code-block-actions.svelte +23 -0
  131. package/src/shadcn/code-block/code-block-content.svelte +55 -0
  132. package/src/shadcn/code-block/code-block-copy-button.svelte +10 -0
  133. package/src/shadcn/code-block/code-block-footer.svelte +20 -0
  134. package/src/shadcn/code-block/code-block-header.svelte +23 -0
  135. package/src/shadcn/code-block/code-block-line.svelte +71 -0
  136. package/src/shadcn/code-block/code-block-title.svelte +23 -0
  137. package/src/shadcn/code-block/code-block.svelte +47 -0
  138. package/src/shadcn/code-block/index.ts +42 -0
  139. package/src/shadcn/command/index.ts +3 -0
  140. package/src/shadcn/field/index.ts +3 -0
  141. package/src/shadcn/field/simple-field.svelte +71 -0
  142. package/src/shadcn/field/simple-field.test.ts +47 -0
  143. package/src/shadcn/hooks/highlighter.ts +132 -0
  144. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +98 -0
  145. package/src/shadcn/kbd/index.ts +10 -0
  146. package/src/shadcn/kbd/kbd-group.svelte +20 -0
  147. package/src/shadcn/kbd/kbd.svelte +23 -0
  148. package/src/shadcn/message-scroller/index.ts +28 -0
  149. package/src/shadcn/message-scroller/message-scroller-button.svelte +52 -0
  150. package/src/shadcn/message-scroller/message-scroller-content.svelte +17 -0
  151. package/src/shadcn/message-scroller/message-scroller-item.svelte +22 -0
  152. package/src/shadcn/message-scroller/message-scroller-provider.svelte +7 -0
  153. package/src/shadcn/message-scroller/message-scroller-viewport.svelte +20 -0
  154. package/src/shadcn/message-scroller/message-scroller.svelte +20 -0
  155. package/src/shadcn/radio-group/radio-group-item.svelte +3 -2
  156. package/src/shadcn/sheet/sheet-content.svelte +1 -0
  157. package/src/shadcn/sheet/sheet.css +48 -0
  158. package/src/shadcn/sonner/sonner.svelte +7 -10
  159. package/src/shadcn/tabs/tabs-list.svelte +13 -2
  160. package/src/shadcn/tabs/tabs-trigger.svelte +1 -1
  161. package/src/shadcn/toggle-group/toggle-group.svelte +10 -3
  162. package/src/shadcn/trace/index.ts +29 -0
  163. package/src/shadcn/trace/trace-collapsible-content.svelte +29 -0
  164. package/src/shadcn/trace/trace-collapsible-trigger.svelte +29 -0
  165. package/src/shadcn/trace/trace-collapsible.svelte +20 -0
  166. package/src/shadcn/trace/trace-description.svelte +20 -0
  167. package/src/shadcn/trace/trace-icon.svelte +24 -0
  168. package/src/shadcn/trace/trace-line.svelte +31 -0
  169. package/src/shadcn/trace/trace-title.svelte +20 -0
  170. package/src/shadcn/trace/trace.svelte +23 -0
  171. package/src/ai/composer/Composer.css +0 -108
  172. package/src/ai/composer/Composer.svelte +0 -111
  173. package/src/ai/composer/index.ts +0 -10
  174. package/src/ai/conversation/Conversation.svelte +0 -28
  175. package/src/ai/conversation/ConversationContent.svelte +0 -35
  176. package/src/ai/conversation/ConversationEmptyState.svelte +0 -46
  177. package/src/ai/conversation/ConversationScrollButton.svelte +0 -51
  178. package/src/ai/conversation/index.ts +0 -33
  179. package/src/ai/conversation/stick-to-bottom-context.svelte.ts +0 -131
  180. package/src/animations/accordion.css +0 -58
  181. package/src/animations/icon-swap.css +0 -43
  182. package/src/animations/notification-badge.css +0 -81
  183. package/src/animations/panel.css +0 -86
  184. package/src/animations/reasoning-stream.css +0 -49
  185. package/src/animations/skeleton-reveal.css +0 -97
  186. package/src/animations/streaming-text.css +0 -31
  187. package/src/animations/tabs-sliding.css +0 -83
  188. package/src/animations/texts-reveal.css +0 -70
  189. package/src/animations/tooltip.css +0 -72
  190. package/src/animations/view-transition-reveal.ts +0 -74
  191. package/src/charts/animated-value.css +0 -7
  192. package/src/charts/animated-value.svelte +0 -110
  193. package/src/charts/area-chart.css +0 -29
  194. package/src/charts/area-chart.svelte +0 -195
  195. package/src/charts/bar-chart.css +0 -119
  196. package/src/charts/bar-chart.svelte +0 -269
  197. package/src/charts/charts.css +0 -201
  198. package/src/charts/crosshair.svelte +0 -35
  199. package/src/charts/donut-chart.css +0 -56
  200. package/src/charts/donut-chart.svelte +0 -158
  201. package/src/charts/edge-fade.svelte +0 -43
  202. package/src/charts/edge-fade.ts +0 -16
  203. package/src/charts/figure.css +0 -100
  204. package/src/charts/figure.svelte +0 -122
  205. package/src/charts/gauge-chart.css +0 -94
  206. package/src/charts/gauge-chart.svelte +0 -303
  207. package/src/charts/hover-band.test.ts +0 -30
  208. package/src/charts/hover-band.ts +0 -21
  209. package/src/charts/hover-focus.svelte +0 -85
  210. package/src/charts/index.ts +0 -30
  211. package/src/charts/legend.css +0 -131
  212. package/src/charts/legend.svelte +0 -112
  213. package/src/charts/line-chart.css +0 -26
  214. package/src/charts/line-chart.svelte +0 -256
  215. package/src/charts/motion.ts +0 -19
  216. package/src/charts/number-chart.css +0 -168
  217. package/src/charts/number-chart.svelte +0 -177
  218. package/src/charts/primitives/chart-container.svelte +0 -57
  219. package/src/charts/primitives/chart-style.svelte +0 -36
  220. package/src/charts/primitives/chart-tooltip.svelte +0 -161
  221. package/src/charts/primitives/chart-utils.ts +0 -54
  222. package/src/charts/primitives/index.ts +0 -11
  223. package/src/charts/reveal.svelte +0 -61
  224. package/src/charts/ring-chart.css +0 -96
  225. package/src/charts/ring-chart.svelte +0 -158
  226. package/src/charts/sparkline.css +0 -45
  227. package/src/charts/sparkline.svelte +0 -140
  228. package/src/charts/stat-card.css +0 -103
  229. package/src/charts/stat-card.svelte +0 -267
  230. package/src/charts/time-series-chart.css +0 -28
  231. package/src/charts/time-series-chart.svelte +0 -179
  232. package/src/charts/trend-pill.css +0 -38
  233. package/src/charts/trend-pill.svelte +0 -48
  234. package/src/lib/hooks/is-mobile.svelte.ts +0 -11
  235. package/src/lib/utils.ts +0 -45
  236. package/src/showcase/AiSection.svelte +0 -157
  237. package/src/showcase/AnimationsSection.svelte +0 -1658
  238. package/src/showcase/ChartsSection.svelte +0 -458
  239. package/src/showcase/EffectsSection.svelte +0 -152
  240. package/src/showcase/ShadcnSection.svelte +0 -27
  241. package/src/showcase/ShowcaseImport.svelte +0 -126
  242. package/src/showcase/ShowcaseList.svelte +0 -132
  243. package/src/showcase/ShowcaseNav.svelte +0 -247
  244. package/src/showcase/ShowcaseSection.svelte +0 -112
  245. package/src/showcase/ShowcaseShell.svelte +0 -55
  246. package/src/showcase/ShowcaseStage.svelte +0 -342
  247. package/src/showcase/ShowcaseVariant.svelte +0 -109
  248. package/src/showcase/assets/image-reveal/art-1.jpg +0 -0
  249. package/src/showcase/assets/image-reveal/art-2.jpg +0 -0
  250. package/src/showcase/assets/image-reveal/art-3.jpg +0 -0
  251. package/src/showcase/check.svg +0 -1
  252. package/src/showcase/chevron-arms.svg +0 -2
  253. package/src/showcase/chevron-down.svg +0 -1
  254. package/src/showcase/copy.svg +0 -2
  255. package/src/showcase/demos/ai/confirmation.svelte +0 -59
  256. package/src/showcase/demos/ai/conversation.svelte +0 -59
  257. package/src/showcase/demos/ai/loading-state.svelte +0 -19
  258. package/src/showcase/demos/ai/prompt-input.svelte +0 -57
  259. package/src/showcase/demos/ai/reasoning.svelte +0 -19
  260. package/src/showcase/demos/ai/thinking-dots.svelte +0 -15
  261. package/src/showcase/demos/ai/voice-waveform.svelte +0 -24
  262. package/src/showcase/demos/effects/animated-beam.svelte +0 -50
  263. package/src/showcase/demos/effects/animated-group.svelte +0 -47
  264. package/src/showcase/demos/effects/border-beam-default.svelte +0 -20
  265. package/src/showcase/demos/effects/border-beam-line.svelte +0 -20
  266. package/src/showcase/demos/effects/border-beam-mono.svelte +0 -20
  267. package/src/showcase/demos/effects/border-beam-sunset.svelte +0 -20
  268. package/src/showcase/demos/effects/image-comparison.svelte +0 -25
  269. package/src/showcase/demos/effects/image-reveal.svelte +0 -49
  270. package/src/showcase/demos/effects/in-view.svelte +0 -41
  271. package/src/showcase/demos/effects/integrations-hub.svelte +0 -82
  272. package/src/showcase/demos/effects/magnetic.svelte +0 -31
  273. package/src/showcase/demos/effects/marquee.svelte +0 -33
  274. package/src/showcase/demos/effects/progressive-blur.svelte +0 -47
  275. package/src/showcase/demos/effects/spotlight.svelte +0 -40
  276. package/src/showcase/demos/effects/text-effect.svelte +0 -55
  277. package/src/showcase/demos/shadcn/accordion.svelte +0 -45
  278. package/src/showcase/demos/shadcn/alert-dialog.svelte +0 -23
  279. package/src/showcase/demos/shadcn/alert.svelte +0 -30
  280. package/src/showcase/demos/shadcn/attachment.svelte +0 -71
  281. package/src/showcase/demos/shadcn/avatar.svelte +0 -30
  282. package/src/showcase/demos/shadcn/badge.svelte +0 -26
  283. package/src/showcase/demos/shadcn/breadcrumb.svelte +0 -34
  284. package/src/showcase/demos/shadcn/bubble.svelte +0 -36
  285. package/src/showcase/demos/shadcn/button.svelte +0 -11
  286. package/src/showcase/demos/shadcn/calendar.svelte +0 -8
  287. package/src/showcase/demos/shadcn/card.svelte +0 -39
  288. package/src/showcase/demos/shadcn/checkbox.svelte +0 -39
  289. package/src/showcase/demos/shadcn/collapsible.svelte +0 -20
  290. package/src/showcase/demos/shadcn/command.svelte +0 -48
  291. package/src/showcase/demos/shadcn/context-menu.svelte +0 -54
  292. package/src/showcase/demos/shadcn/demo-harness.svelte +0 -12
  293. package/src/showcase/demos/shadcn/demos.test.ts +0 -20
  294. package/src/showcase/demos/shadcn/dialog.svelte +0 -38
  295. package/src/showcase/demos/shadcn/dropdown-menu.svelte +0 -59
  296. package/src/showcase/demos/shadcn/empty.svelte +0 -29
  297. package/src/showcase/demos/shadcn/field.svelte +0 -115
  298. package/src/showcase/demos/shadcn/hover-card.svelte +0 -32
  299. package/src/showcase/demos/shadcn/index.ts +0 -358
  300. package/src/showcase/demos/shadcn/input-otp.svelte +0 -19
  301. package/src/showcase/demos/shadcn/input.svelte +0 -5
  302. package/src/showcase/demos/shadcn/label.svelte +0 -11
  303. package/src/showcase/demos/shadcn/marker.svelte +0 -30
  304. package/src/showcase/demos/shadcn/message.svelte +0 -75
  305. package/src/showcase/demos/shadcn/popover.svelte +0 -36
  306. package/src/showcase/demos/shadcn/progress.svelte +0 -13
  307. package/src/showcase/demos/shadcn/radio-group.svelte +0 -19
  308. package/src/showcase/demos/shadcn/scroll-area.svelte +0 -18
  309. package/src/showcase/demos/shadcn/select.svelte +0 -29
  310. package/src/showcase/demos/shadcn/separator.svelte +0 -12
  311. package/src/showcase/demos/shadcn/sheet.svelte +0 -48
  312. package/src/showcase/demos/shadcn/skeleton.svelte +0 -11
  313. package/src/showcase/demos/shadcn/slider.svelte +0 -7
  314. package/src/showcase/demos/shadcn/sonner.svelte +0 -18
  315. package/src/showcase/demos/shadcn/spinner.svelte +0 -11
  316. package/src/showcase/demos/shadcn/switch.svelte +0 -9
  317. package/src/showcase/demos/shadcn/table.svelte +0 -76
  318. package/src/showcase/demos/shadcn/tabs.svelte +0 -62
  319. package/src/showcase/demos/shadcn/textarea.svelte +0 -5
  320. package/src/showcase/demos/shadcn/toggle-group.svelte +0 -18
  321. package/src/showcase/demos/shadcn/toggle.svelte +0 -14
  322. package/src/showcase/demos/shadcn/tooltip.svelte +0 -13
  323. package/src/showcase/heart.svg +0 -1
  324. package/src/showcase/index.ts +0 -38
  325. package/src/showcase/kit.test.ts +0 -72
  326. package/src/showcase/kit.ts +0 -17
  327. package/src/showcase/plus.svg +0 -1
  328. package/src/showcase/showcase-shell.css +0 -91
  329. package/src/showcase/stacked-section.css +0 -15
  330. package/src/showcase/success-check.svg +0 -1
  331. package/src/showcase/types.ts +0 -29
  332. package/src/svg-raw.d.ts +0 -7
@@ -1,158 +0,0 @@
1
- <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import "./ring-chart.css";
6
- import { cn } from "@heyramzi/utils";
7
- import AnimatedValue from "./animated-value.svelte";
8
-
9
- // =========================
10
- // TYPES
11
- // =========================
12
- type RingData = {
13
- label: string;
14
- value: number;
15
- /** Denominator for the progress arc. */
16
- maxValue: number;
17
- /** CSS color or var. Falls back to --chart-1..5. */
18
- color?: string;
19
- };
20
-
21
- type Props = {
22
- data: RingData[];
23
- /** Fixed square size in px. Omit to fill the parent (responsive). */
24
- size?: number;
25
- /** Ring thickness in viewBox units. */
26
- strokeWidth?: number;
27
- /** Gap between concentric rings in viewBox units. */
28
- ringGap?: number;
29
- /** Inner radius of the innermost ring in viewBox units. */
30
- baseInnerRadius?: number;
31
- lineCap?: "round" | "butt";
32
- /** Center label when nothing is hovered. */
33
- defaultLabel?: string;
34
- formatValue?: (v: number) => string;
35
- /** Animate arcs on mount. */
36
- animate?: boolean;
37
- /** Glow the hovered ring. */
38
- showGlow?: boolean;
39
- /** Hovered ring index, bindable for legend hover-sync. */
40
- hoveredIndex?: number | null;
41
- onHoverChange?: (index: number | null) => void;
42
- minWidth?: number;
43
- ariaLabel?: string;
44
- class?: string;
45
- };
46
-
47
- // =========================
48
- // PROPS
49
- // =========================
50
- let {
51
- data,
52
- size,
53
- strokeWidth = 11,
54
- ringGap = 6,
55
- baseInnerRadius = 48,
56
- lineCap = "round",
57
- defaultLabel = "Total",
58
- formatValue = (v) => v.toLocaleString(),
59
- animate = true,
60
- showGlow = true,
61
- hoveredIndex = $bindable(null),
62
- onHoverChange,
63
- minWidth = 170,
64
- ariaLabel,
65
- class: className,
66
- }: Props = $props();
67
-
68
- // =========================
69
- // DERIVED
70
- // =========================
71
- const rings = $derived.by(() =>
72
- data.map((d, i) => {
73
- const radius = baseInnerRadius + i * (strokeWidth + ringGap) + strokeWidth / 2;
74
- const progress = d.maxValue > 0 ? Math.max(0, Math.min(1, d.value / d.maxValue)) : 0;
75
- return {
76
- ...d,
77
- index: i,
78
- radius,
79
- progress,
80
- color: d.color ?? `var(--chart-${(i % 5) + 1})`,
81
- };
82
- }),
83
- );
84
- const maxRadius = $derived(
85
- rings.length ? rings[rings.length - 1].radius + strokeWidth / 2 : baseInnerRadius,
86
- );
87
- const vb = $derived(maxRadius * 2 + 4);
88
- const center = $derived(vb / 2);
89
- const total = $derived(data.reduce((sum, d) => sum + d.value, 0));
90
- const hoveredRing = $derived(hoveredIndex != null ? (rings[hoveredIndex] ?? null) : null);
91
-
92
- // =========================
93
- // FUNCTIONS
94
- // =========================
95
- function setHover(index: number | null) {
96
- hoveredIndex = index;
97
- onHoverChange?.(index);
98
- }
99
- </script>
100
-
101
- <!-- =========================== -->
102
- <!-- MARKUP -->
103
- <!-- =========================== -->
104
- <div
105
- class={cn("hr-ring", className)}
106
- class:glow={showGlow}
107
- style:width={size ? `${size}px` : "100%"}
108
- style:min-width="{minWidth}px"
109
- role="img"
110
- aria-label={ariaLabel ?? `${defaultLabel}: ${formatValue(total)}`}
111
- >
112
- <!-- no-inline-svg-ignore -->
113
- <svg viewBox="0 0 {vb} {vb}" aria-hidden="true">
114
- <g transform="rotate(-90 {center} {center})">
115
- {#each rings as ring (ring.index)}
116
- <circle
117
- class="track"
118
- cx={center}
119
- cy={center}
120
- r={ring.radius}
121
- fill="none"
122
- stroke={ring.color}
123
- stroke-width={strokeWidth}
124
- />
125
- {/each}
126
- {#each rings as ring (ring.index)}
127
- {@const dimmed = hoveredIndex !== null && hoveredIndex !== ring.index}
128
- <circle
129
- class="fill"
130
- class:animate
131
- cx={center}
132
- cy={center}
133
- r={ring.radius}
134
- pathLength="100"
135
- fill="none"
136
- stroke={ring.color}
137
- stroke-width={strokeWidth}
138
- stroke-linecap={lineCap}
139
- style:stroke-dashoffset={100 - ring.progress * 100}
140
- style:opacity={dimmed ? 0.25 : 1}
141
- style:animation-delay="{160 + ring.index * 120}ms"
142
- style:--ring-color={ring.color}
143
- onmouseenter={() => setHover(ring.index)}
144
- onmouseleave={() => setHover(null)}
145
- ></circle>
146
- {/each}
147
- </g>
148
- </svg>
149
-
150
- <div class="center">
151
- <span class="value">
152
- <AnimatedValue value={hoveredRing ? hoveredRing.value : total} format={formatValue} />
153
- </span>
154
- {#key hoveredRing?.label ?? defaultLabel}
155
- <span class="label">{hoveredRing ? hoveredRing.label : defaultLabel}</span>
156
- {/key}
157
- </div>
158
- </div>
@@ -1,45 +0,0 @@
1
- /* Styles for Sparkline. Plain stylesheet (see area-chart.css for why).
2
- Selectors are namespaced under `.hr-sparkline`. */
3
-
4
- .hr-sparkline {
5
- display: flex;
6
- align-items: center;
7
- gap: 0.625rem;
8
- width: 100%;
9
- }
10
- .hr-sparkline-chart {
11
- flex: 1 1 auto;
12
- min-width: 0;
13
- }
14
- /* Bare is an ornament inside another control: no inline value, so no gap. */
15
- .hr-sparkline[data-bare] {
16
- gap: 0;
17
- }
18
- .hr-sparkline-value {
19
- flex-shrink: 0;
20
- font-size: 0.8125rem;
21
- font-weight: 600;
22
- font-feature-settings: "tnum";
23
- font-family: ui-monospace, SFMono-Regular, monospace;
24
- }
25
-
26
- .hr-sparkline .hr-sparkline-end {
27
- filter: drop-shadow(0 0 3px currentColor);
28
- opacity: 0.9;
29
- transform-box: fill-box;
30
- transform-origin: center;
31
- /* WHY: the dot lands as the reveal reaches the end of the line. */
32
- animation: hr-sparkline-pop var(--duration-fast, 250ms) var(--ease-smooth-out)
33
- calc(var(--chart-motion-reveal, 1100ms) * 0.85) both;
34
- }
35
- @keyframes hr-sparkline-pop {
36
- from {
37
- opacity: 0;
38
- transform: scale(0);
39
- }
40
- }
41
- @media (prefers-reduced-motion: reduce) {
42
- .hr-sparkline .hr-sparkline-end {
43
- animation: none;
44
- }
45
- }
@@ -1,140 +0,0 @@
1
- <script lang="ts" generics="TData extends Record<string, unknown>">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import "./sparkline.css";
6
- import { Area, Chart, Highlight, LinearGradient, Points, Spline, Svg, Tooltip } from "layerchart";
7
- import { curveMonotoneX } from "d3-shape";
8
- import Reveal from "./reveal.svelte";
9
-
10
- // =========================
11
- // TYPES
12
- // =========================
13
- type Props = {
14
- data: TData[];
15
- xKey: keyof TData & string;
16
- yKey: keyof TData & string;
17
- color?: string;
18
- height?: number;
19
- smooth?: boolean;
20
- /** Show a dot at the latest point */
21
- endCap?: boolean;
22
- /** Show the latest value as inline text */
23
- showLast?: boolean;
24
- /**
25
- * Ornament mode: no tooltip, no highlight, no end cap, and the plot runs to the edges of its box.
26
- */
27
-
28
- /*
29
- * For a line inside a control that already states the number and owns the pointer, such as a stat card that links.
30
- */
31
- bare?: boolean;
32
- formatValue?: (v: number) => string;
33
- class?: string;
34
- };
35
-
36
- // =========================
37
- // PROPS
38
- // =========================
39
- let {
40
- data,
41
- xKey,
42
- yKey,
43
- color = "var(--chart-1)",
44
- height = 44,
45
- smooth = true,
46
- endCap = true,
47
- showLast = false,
48
- bare = false,
49
- formatValue = (v) => v.toLocaleString(),
50
- class: className,
51
- }: Props = $props();
52
-
53
- // =========================
54
- // STATE
55
- // =========================
56
- let active = $state<TData | null>(null);
57
-
58
- // ========================= FUNCTIONS ========================= WHY: called from the tooltip header's value expression so it re-runs on every active-point change; an unread {@const} side effect only evaluates once.
59
-
60
- // The microtask defers the state write out of template evaluation.
61
- function trackActive(d: TData): string {
62
- queueMicrotask(() => {
63
- active = d;
64
- });
65
- return String(d[xKey]);
66
- }
67
-
68
- // =========================
69
- // DERIVED
70
- // =========================
71
- const uid = `hr-spark-${Math.random().toString(36).slice(2, 8)}`;
72
- const curve = $derived(smooth ? curveMonotoneX : undefined);
73
- const last = $derived(data.length > 0 ? data[data.length - 1] : null);
74
- const display = $derived(active ?? last);
75
- const dot = $derived(endCap && !bare);
76
- // Bare runs to the edges: the fill reaches the bottom of the box and the line keeps three pixels of head so its stroke is not cut at the series
77
-
78
- // maximum.
79
- const padding = $derived(
80
- bare
81
- ? { top: 3, bottom: 0, left: 0, right: 0 }
82
- : { top: 4, bottom: 4, left: 0, right: dot ? 6 : 0 },
83
- );
84
- </script>
85
-
86
- <!-- =========================== -->
87
- <!-- SPARKLINE -->
88
- <!-- =========================== -->
89
- <div
90
- class="hr-sparkline {className ?? ''}"
91
- role={bare ? undefined : "img"}
92
- aria-label={bare ? undefined : "Sparkline"}
93
- aria-hidden={bare ? "true" : undefined}
94
- data-bare={bare ? "" : undefined}
95
- onpointerleave={() => (active = null)}
96
- >
97
- {#if data.length > 0}
98
- <div class="hr-sparkline-chart" style:height="{height}px">
99
- <Chart {data} x={xKey} y={yKey} {padding} tooltipContext={{ mode: "bisect-x" }}>
100
- <Svg>
101
- <LinearGradient id={uid} vertical>
102
- {#snippet stopsContent()}
103
- <stop offset="0%" stop-color={color} stop-opacity="0.38" />
104
- <stop offset="100%" stop-color={color} stop-opacity="0" />
105
- {/snippet}
106
- </LinearGradient>
107
- <Reveal bleed={3}>
108
- <!-- WHY no `y0`: it defaults to the bottom of the plot, which is what
109
- a fill under a sparkline wants whatever its domain is. -->
110
- <Area y1={yKey} {curve} fill="url(#{uid})" />
111
- <Spline {curve} style="stroke: {color}; fill: none" stroke-width="1.5" />
112
- </Reveal>
113
- {#if dot && last}
114
- <!-- WHY: Points resolves its own data and hands the inner circle pixel coords; Circle's data mode clobbers the sibling Spline's data in -->
115
- <!-- this layerchart prerelease (and a bare object left the dot at the chart origin). -->
116
- <Points data={[last]} r={3} fill={color} class="hr-sparkline-end" />
117
- {/if}
118
- {#if !bare}
119
- <Highlight points lines={false} />
120
- {/if}
121
- </Svg>
122
- {#if !bare}
123
- <Tooltip.Root>
124
- {#snippet children({ data: d })}
125
- <Tooltip.Header value={trackActive(d as TData)} />
126
- <Tooltip.List>
127
- <Tooltip.Item label={String(yKey)} value={formatValue(Number(d[yKey]))} />
128
- </Tooltip.List>
129
- {/snippet}
130
- </Tooltip.Root>
131
- {/if}
132
- </Chart>
133
- </div>
134
- {#if showLast && display}
135
- <span class="hr-sparkline-value" style:color>
136
- {formatValue(Number(display[yKey]))}
137
- </span>
138
- {/if}
139
- {/if}
140
- </div>
@@ -1,103 +0,0 @@
1
- /* Styles for StatCard. Plain stylesheet (see area-chart.css for why). All
2
- selectors are already namespaced under `.hr-stat-card`. */
3
-
4
- .hr-stat-card {
5
- display: flex;
6
- flex-direction: column;
7
- gap: 0.375rem;
8
- padding: 1.25rem 1.5rem;
9
- border: 1px solid var(--border, oklch(0.92 0.004 286));
10
- border-radius: 1rem;
11
- background: var(--card, white);
12
- box-shadow: var(--chart-card-shadow);
13
- min-width: 0;
14
- /* WHY: clips the edge-to-edge chart to the card's rounded corners. */
15
- overflow: hidden;
16
- transition: border-color var(--chart-motion, 250ms) var(--chart-ease-out, ease);
17
- }
18
- .hr-stat-card:hover {
19
- border-color: color-mix(in srgb, var(--ring, var(--chart-1)) 28%, var(--border, transparent));
20
- }
21
- /* WHY: a bare figure, not a card. Dropping the chrome avoids a card-in-card
22
- when the figure is placed inside another surface, and lets the chart be the
23
- whole element. */
24
- .hr-stat-figure-only {
25
- padding: 0;
26
- gap: 0;
27
- border: 0;
28
- border-radius: 0;
29
- background: none;
30
- box-shadow: none;
31
- }
32
-
33
- /* WHY: the gauge variant sits the figure and a radial gauge side by side
34
- instead of bleeding a chart to the bottom edge; no negative margins here. */
35
- .hr-stat-gauge-row {
36
- display: flex;
37
- flex-wrap: wrap;
38
- align-items: center;
39
- gap: 1rem;
40
- }
41
- /* WHY the floor: below the width a gauge and a figure share, the row wraps the
42
- gauge to its own line rather than squeezing the figure to nothing. */
43
- .hr-stat-gauge-row .hr-stat-gauge-figure {
44
- flex: 1 1 auto;
45
- min-width: 6rem;
46
- }
47
- .hr-stat-gauge-row .hr-gauge {
48
- flex: 0 0 auto;
49
- }
50
-
51
- /* WHY: the chart bleeds past the card padding to the left/right/bottom edges,
52
- matching bklit's edge-to-edge stat-card chart; overflow:hidden on the card
53
- clips the corners to the radius. */
54
- .hr-stat-chart {
55
- margin: 0.5rem -1.5rem -1.25rem;
56
- width: auto;
57
- }
58
- .hr-stat-figure-only .hr-stat-chart {
59
- margin: 0;
60
- }
61
-
62
- .hr-stat-card .hr-stat-spline {
63
- transition: opacity var(--chart-motion-fast, 150ms) ease;
64
- }
65
- .hr-stat-card .hr-stat-bars {
66
- transform-box: fill-box;
67
- transform-origin: bottom;
68
- animation: hr-stat-grow var(--chart-motion-reveal, 1100ms)
69
- var(--chart-ease-reveal, cubic-bezier(0.85, 0, 0.15, 1)) both;
70
- }
71
- .hr-stat-card .hr-stat-bars:nth-of-type(2) {
72
- animation-delay: var(--chart-stagger, 30ms);
73
- }
74
- .hr-stat-card .hr-stat-bars:nth-of-type(3) {
75
- animation-delay: calc(var(--chart-stagger, 30ms) * 2);
76
- }
77
- .hr-stat-card .hr-stat-bars:nth-of-type(4) {
78
- animation-delay: calc(var(--chart-stagger, 30ms) * 3);
79
- }
80
- .hr-stat-card .hr-stat-bars:nth-of-type(5) {
81
- animation-delay: calc(var(--chart-stagger, 30ms) * 4);
82
- }
83
- .hr-stat-card .hr-stat-bars:nth-of-type(6) {
84
- animation-delay: calc(var(--chart-stagger, 30ms) * 5);
85
- }
86
- .hr-stat-card .hr-stat-bars:nth-of-type(n + 7) {
87
- animation-delay: calc(var(--chart-stagger, 30ms) * 6);
88
- }
89
- @keyframes hr-stat-grow {
90
- from {
91
- transform: scaleY(0);
92
- }
93
- }
94
- .hr-stat-card .hr-stat-bar-active {
95
- filter: brightness(1.08);
96
- pointer-events: none;
97
- }
98
-
99
- @media (prefers-reduced-motion: reduce) {
100
- .hr-stat-card .hr-stat-bars {
101
- animation: none;
102
- }
103
- }