@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,126 +0,0 @@
1
- <script lang="ts" module>
2
- export interface ShowcaseImportProps {
3
- /** The line a consumer pastes, e.g. `@heyramzi/ui/shadcn/button`. */
4
- path: string;
5
- /** Accessible name suffix, so two rows on a page do not both read "Copy import". */
6
- label?: string;
7
- class?: string;
8
- }
9
- </script>
10
-
11
- <script lang="ts">
12
- import { cn } from "@heyramzi/utils";
13
- import checkIcon from "./check.svg?raw";
14
- import copyIcon from "./copy.svg?raw";
15
-
16
- let { path, label, class: className }: ShowcaseImportProps = $props();
17
-
18
- let copied = $state(false);
19
- let timer: ReturnType<typeof setTimeout> | undefined;
20
-
21
- async function copy() {
22
- try {
23
- await navigator.clipboard.writeText(path);
24
- } catch {
25
- // A denied clipboard is not worth an error state on a dev-only surface;
26
- // the path is on screen and selectable either way.
27
- return;
28
- }
29
- copied = true;
30
- clearTimeout(timer);
31
- timer = setTimeout(() => (copied = false), 1600);
32
- }
33
- </script>
34
-
35
- <button
36
- type="button"
37
- class={cn("sc-import", className)}
38
- data-copied={copied ? "true" : undefined}
39
- onclick={copy}
40
- aria-label="Copy import for {label ?? path}"
41
- >
42
- <code>{path}</code>
43
- <span class="sc-import-state" aria-hidden="true">
44
- {#if copied}
45
- {@html checkIcon}
46
- {:else}
47
- {@html copyIcon}
48
- {/if}
49
- </span>
50
- </button>
51
-
52
- <style>
53
- .sc-import {
54
- display: flex;
55
- align-items: center;
56
- justify-content: space-between;
57
- gap: 0.75rem;
58
- width: 100%;
59
- min-width: 0;
60
- padding: 0.375rem 0.5rem 0.375rem 0.625rem;
61
- border: 1px solid transparent;
62
- border-radius: 0.5rem;
63
- background: transparent;
64
- color: var(--color-muted-foreground, var(--muted-foreground));
65
- cursor: pointer;
66
- text-align: start;
67
- transition:
68
- color 160ms ease,
69
- background-color 160ms ease,
70
- border-color 160ms ease;
71
- }
72
-
73
- .sc-import:hover {
74
- color: var(--color-foreground, var(--foreground));
75
- background: color-mix(in oklab, var(--color-muted, var(--muted)) 70%, transparent);
76
- }
77
-
78
- .sc-import:focus-visible {
79
- outline: 2px solid var(--color-ring, var(--ring));
80
- outline-offset: 2px;
81
- }
82
-
83
- .sc-import[data-copied] {
84
- color: var(--color-foreground, var(--foreground));
85
- border-color: color-mix(in oklab, var(--color-ring, var(--ring)) 45%, transparent);
86
- }
87
-
88
- .sc-import code {
89
- font-family: var(--font-mono, ui-monospace, "SF Mono", "JetBrains Mono", monospace);
90
- font-size: 0.6875rem;
91
- letter-spacing: -0.01em;
92
- overflow: hidden;
93
- text-overflow: ellipsis;
94
- white-space: nowrap;
95
- }
96
-
97
- .sc-import-state {
98
- display: grid;
99
- place-items: center;
100
- flex-shrink: 0;
101
- width: 0.875rem;
102
- height: 0.875rem;
103
- }
104
-
105
- .sc-import-state :global(svg) {
106
- width: 100%;
107
- height: 100%;
108
- animation: sc-icon-in 200ms cubic-bezier(0.22, 1, 0.36, 1);
109
- }
110
-
111
- @keyframes sc-icon-in {
112
- from {
113
- opacity: 0;
114
- transform: scale(0.6);
115
- }
116
- }
117
-
118
- @media (prefers-reduced-motion: reduce) {
119
- .sc-import {
120
- transition: none;
121
- }
122
- .sc-import-state :global(svg) {
123
- animation: none;
124
- }
125
- }
126
- </style>
@@ -1,132 +0,0 @@
1
- <script lang="ts" module>
2
- export interface ShowcaseListItem {
3
- /** Component name, e.g. "Context Menu". */
4
- title: string;
5
- /** Import line for the copy control. */
6
- importPath: string;
7
- /** Right-hand detail: a file count, a parts count, a source path. */
8
- meta?: string;
9
- /** Provenance beside the name, e.g. "shadcn" on a primitive the kit took as is. */
10
- tag?: string;
11
- }
12
-
13
- export interface ShowcaseListProps {
14
- items: ShowcaseListItem[];
15
- /** Heading above the rows. Omit inside a section that is list-only. */
16
- caption?: string;
17
- }
18
- </script>
19
-
20
- <script lang="ts">
21
- import ShowcaseImport from "./ShowcaseImport.svelte";
22
- import "./showcase-shell.css";
23
-
24
- let { items, caption }: ShowcaseListProps = $props();
25
- </script>
26
-
27
- <!-- WHY a row and not a stage: a component with no demo has one useful fact, its
28
- import line. Given a card each, the undemoed majority buries the demos. -->
29
- <div class="sc-list">
30
- {#if caption}
31
- <p class="sc-list-caption">{caption}</p>
32
- {/if}
33
- <ul>
34
- {#each items as item (item.importPath)}
35
- <li>
36
- <span class="sc-list-title">
37
- {item.title}
38
- {#if item.tag}
39
- <span class="sc-tag">{item.tag}</span>
40
- {/if}
41
- </span>
42
- <ShowcaseImport path={item.importPath} label={item.title} class="sc-list-import" />
43
- {#if item.meta}
44
- <span class="sc-list-meta">{item.meta}</span>
45
- {/if}
46
- </li>
47
- {/each}
48
- </ul>
49
- </div>
50
-
51
- <style>
52
- .sc-list {
53
- display: flex;
54
- flex-direction: column;
55
- gap: 0.625rem;
56
- }
57
-
58
- .sc-list-caption {
59
- margin: 0;
60
- font-size: 0.6875rem;
61
- font-weight: 500;
62
- letter-spacing: 0.06em;
63
- text-transform: uppercase;
64
- color: var(--color-muted-foreground, var(--muted-foreground));
65
- }
66
-
67
- .sc-list ul {
68
- list-style: none;
69
- margin: 0;
70
- padding: 0;
71
- border: 1px solid var(--color-border, var(--border));
72
- border-radius: calc(var(--radius, 0.625rem) + 0.375rem);
73
- background: var(--color-card, var(--card));
74
- overflow: hidden;
75
- }
76
-
77
- .sc-list li {
78
- position: relative;
79
- display: grid;
80
- grid-template-columns: minmax(7rem, 12rem) minmax(0, 1fr) auto;
81
- align-items: center;
82
- gap: 0.75rem;
83
- padding: 0.1875rem 0.75rem 0.1875rem 1rem;
84
- min-height: 2.5rem;
85
- }
86
-
87
- /* Inset to the title column, so a scan down the names is not cut by a full-bleed
88
- rule on every row. */
89
- .sc-list li + li::before {
90
- content: "";
91
- position: absolute;
92
- inset-block-start: 0;
93
- inset-inline: 1rem 0;
94
- height: 1px;
95
- background: var(--color-border, var(--border));
96
- }
97
-
98
- .sc-list-title {
99
- font-size: 0.8125rem;
100
- font-weight: 500;
101
- color: var(--color-card-foreground, var(--color-foreground, var(--foreground)));
102
- overflow: hidden;
103
- text-overflow: ellipsis;
104
- white-space: nowrap;
105
- }
106
-
107
- .sc-list-meta {
108
- font-size: 0.6875rem;
109
- font-variant-numeric: tabular-nums;
110
- color: var(--color-muted-foreground, var(--muted-foreground));
111
- white-space: nowrap;
112
- }
113
-
114
- @media (max-width: 40rem) {
115
- .sc-list li {
116
- grid-template-columns: minmax(0, 1fr) auto;
117
- grid-template-areas:
118
- "title meta"
119
- "import import";
120
- padding-block: 0.5rem;
121
- }
122
- .sc-list-title {
123
- grid-area: title;
124
- }
125
- .sc-list-meta {
126
- grid-area: meta;
127
- }
128
- .sc-list :global(.sc-list-import) {
129
- grid-area: import;
130
- }
131
- }
132
- </style>
@@ -1,247 +0,0 @@
1
- <script lang="ts" module>
2
- import type { ShowcaseSectionMeta } from "./types.js";
3
-
4
- export interface ShowcaseNavProps {
5
- sections: ShowcaseSectionMeta[];
6
- }
7
- </script>
8
-
9
- <script lang="ts">
10
- import { onMount } from "svelte";
11
-
12
- let { sections }: ShowcaseNavProps = $props();
13
-
14
- let active = $state(sections[0]?.id ?? "");
15
- // Nothing is written back until the reader's place has been restored, or the
16
- // observer's first call would overwrite it with whatever the top of the page is.
17
- let restored = $state(false);
18
-
19
- const storeKey = () => `sc-rail-active:${location.pathname}`;
20
-
21
- // Consecutive sections sharing a group are drawn under one heading.
22
-
23
- // Grouping by run rather than by key means the page's own order is the rail's order, and a section can be moved without also moving it in a second list.
24
- const runs = $derived(
25
- sections.reduce<{ group?: string; items: ShowcaseSectionMeta[] }[]>((acc, section) => {
26
- const last = acc.at(-1);
27
- if (last && last.group === section.group) last.items.push(section);
28
- else acc.push({ group: section.group, items: [section] });
29
- return acc;
30
- }, []),
31
- );
32
-
33
- // The rail follows the reader rather than the last click: an anchor jump and a
34
- // scroll both end in the same place, so only one source of truth is needed.
35
- $effect(() => {
36
- const targets = sections
37
- .map((section) => document.getElementById(section.id))
38
- .filter((element): element is HTMLElement => element !== null);
39
- if (targets.length === 0) return;
40
-
41
- const observer = new IntersectionObserver(
42
- (entries) => {
43
- const visible = entries
44
- .filter((entry) => entry.isIntersecting)
45
- .toSorted((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
46
- if (visible[0]) active = visible[0].target.id;
47
- },
48
- { rootMargin: "-12% 0px -75% 0px", threshold: 0 },
49
- );
50
- for (const target of targets) observer.observe(target);
51
- return () => observer.disconnect();
52
- });
53
-
54
- // The band the observer watches sits near the top of the viewport, and the last section is often too short to reach it,
55
-
56
- // so the rail stopped one entry early at the foot of the page. The bottom of the scroll names the last section.
57
- $effect(() => {
58
- const onScroll = () => {
59
- const last = sections.at(-1)?.id;
60
- if (last && window.innerHeight + window.scrollY >= document.body.scrollHeight - 2) {
61
- active = last;
62
- }
63
- };
64
- window.addEventListener("scroll", onScroll, { passive: true });
65
- return () => window.removeEventListener("scroll", onScroll);
66
- });
67
-
68
- // A reload put the reader back at the top.
69
-
70
- // The browser does restore a scroll offset, but the charts, images and fonts on this page settle after it, so the offset it restores is no longer the section it
71
-
72
- // was taken from. The section id survives instead: the hash when the reader followed one, the last section read otherwise.
73
- onMount(() => {
74
- let wanted = decodeURIComponent(location.hash.slice(1));
75
- if (!wanted) {
76
- try {
77
- wanted = sessionStorage.getItem(storeKey()) ?? "";
78
- } catch {
79
- wanted = "";
80
- }
81
- }
82
- if (!wanted || !sections.some((section) => section.id === wanted)) {
83
- restored = true;
84
- return;
85
- }
86
-
87
- active = wanted;
88
- // Two frames: the first paints the sections, the second lets what is inside
89
- // them take its height, so the anchor lands on the heading and not above it.
90
- requestAnimationFrame(() =>
91
- requestAnimationFrame(() => {
92
- document.getElementById(wanted)?.scrollIntoView({ block: "start" });
93
- restored = true;
94
- }),
95
- );
96
- });
97
-
98
- $effect(() => {
99
- if (!restored || !active) return;
100
- try {
101
- sessionStorage.setItem(storeKey(), active);
102
- } catch {
103
- // A private window refuses the write. The rail still follows the reader.
104
- }
105
- });
106
- </script>
107
-
108
- <nav aria-label="Component sections">
109
- {#each runs as run, index (run.group ?? index)}
110
- {#if run.group}
111
- <p class="sc-rail-group">{run.group}</p>
112
- {/if}
113
- <ul>
114
- {#each run.items as section (section.id)}
115
- <li>
116
- <a
117
- href="#{section.id}"
118
- data-active={active === section.id ? "true" : undefined}
119
- aria-current={active === section.id ? "page" : undefined}
120
- >
121
- <span class="sc-rail-label">{section.label}</span>
122
- <span class="sc-rail-count">{section.demos || section.listed}</span>
123
- </a>
124
- </li>
125
- {/each}
126
- </ul>
127
- {/each}
128
- </nav>
129
-
130
- <style>
131
- /*
132
- * WHY a rail and not a row of pills: the row was one line of chips that ran to
133
- */
134
-
135
- /*
136
- * nine sections and wrapped, so the reader's place in a long page was a chip somewhere in a block of chips. A vertical list holds a stable order, has room
137
- */
138
-
139
- /*
140
- * for group headings, and leaves the whole content column to the components.
141
- */
142
- nav {
143
- position: sticky;
144
- top: var(--showcase-nav-top, 0px);
145
- display: flex;
146
- flex-direction: column;
147
- gap: 0.125rem;
148
- max-height: calc(100dvh - var(--showcase-nav-top, 0px));
149
- padding: 1.25rem 0.75rem 2rem 0.4375rem;
150
- overflow-y: auto;
151
- /* WHY only x: `contain` on y stopped this zero-overflow rail chaining the wheel to the page. */
152
- overscroll-behavior-x: contain;
153
- scrollbar-width: thin;
154
- }
155
-
156
- .sc-rail-group {
157
- margin: 1.125rem 0 0.375rem 0.5rem;
158
- font-size: 0.625rem;
159
- font-weight: 600;
160
- letter-spacing: 0.08em;
161
- text-transform: uppercase;
162
- color: var(--color-muted-foreground, var(--muted-foreground));
163
- opacity: 0.75;
164
- }
165
-
166
- .sc-rail-group:first-child {
167
- margin-top: 0;
168
- }
169
-
170
- ul {
171
- display: flex;
172
- flex-direction: column;
173
- gap: 0.0625rem;
174
- margin: 0;
175
- padding: 0;
176
- list-style: none;
177
- }
178
-
179
- a {
180
- position: relative;
181
- display: flex;
182
- align-items: center;
183
- justify-content: space-between;
184
- gap: 0.5rem;
185
- padding: 0.3125rem 0.5rem 0.3125rem 0.6875rem;
186
- border-radius: var(--radius-lg, 8px);
187
- font-size: 0.78125rem;
188
- line-height: 1.35;
189
- color: var(--color-muted-foreground, var(--muted-foreground));
190
- text-decoration: none;
191
- transition:
192
- color var(--duration-quick, 150ms) var(--ease-smooth-out, ease),
193
- background-color var(--duration-quick, 150ms) var(--ease-smooth-out, ease);
194
- }
195
-
196
- a:hover {
197
- color: var(--color-foreground, var(--foreground));
198
- background: color-mix(in oklab, var(--color-muted, var(--muted)) 60%, transparent);
199
- }
200
-
201
- /*
202
- * The active row is a filled slot with a bar in the gutter. The bar is what the
203
- */
204
-
205
- /*
206
- * eye finds when it comes back to the rail after reading. A fill alone is too
207
- */
208
-
209
- /*
210
- * quiet at this weight, and a full-strength pill is too loud for a list this long.
211
- */
212
- a[data-active] {
213
- color: var(--color-foreground, var(--foreground));
214
- font-weight: 500;
215
- background: color-mix(in oklab, var(--color-muted, var(--muted)) 90%, transparent);
216
- }
217
-
218
- a[data-active]::before {
219
- content: "";
220
- position: absolute;
221
- inset-block: 0.3125rem;
222
- inset-inline-start: -0.375rem;
223
- width: 2px;
224
- border-radius: 999px;
225
- background: var(--color-ring, var(--ring));
226
- }
227
-
228
- .sc-rail-label {
229
- overflow: hidden;
230
- text-overflow: ellipsis;
231
- white-space: nowrap;
232
- }
233
-
234
- .sc-rail-count {
235
- flex-shrink: 0;
236
- font-size: 0.625rem;
237
- font-variant-numeric: tabular-nums;
238
- color: var(--color-muted-foreground, var(--muted-foreground));
239
- opacity: 0.7;
240
- }
241
-
242
- @media (prefers-reduced-motion: reduce) {
243
- a {
244
- transition: none;
245
- }
246
- }
247
- </style>
@@ -1,112 +0,0 @@
1
- <script lang="ts" module>
2
- import type { Snippet } from "svelte";
3
-
4
- export interface ShowcaseSectionProps {
5
- /** Anchor id. Must match the ShowcaseSectionMeta the nav rail was given. */
6
- id: string;
7
- label: string;
8
- /** What the section is for, in the reader's terms, not a list of its members. */
9
- lead?: string;
10
- /** Live demos. Stacked one per row unless `bare` is set. */
11
- children?: Snippet;
12
- /** Skip the stack, for content that brings a layout of its own. */
13
- bare?: boolean;
14
- /** Everything in the section with no demo, drawn as rows under the stack. */
15
- listed?: Snippet;
16
- }
17
- </script>
18
-
19
- <script lang="ts">
20
- let { id, label, lead, children, listed, bare = false }: ShowcaseSectionProps = $props();
21
- </script>
22
-
23
- <section {id}>
24
- <div class="sc-section-head">
25
- <h2>{label}</h2>
26
- {#if lead}
27
- <p>{lead}</p>
28
- {/if}
29
- </div>
30
-
31
- {#if children}
32
- {#if bare}
33
- {@render children()}
34
- {:else}
35
- <div class="sc-stack">
36
- {@render children()}
37
- </div>
38
- {/if}
39
- {/if}
40
-
41
- {#if listed}
42
- <div class="sc-section-listed">
43
- {@render listed()}
44
- </div>
45
- {/if}
46
- </section>
47
-
48
- <style>
49
- section {
50
- display: flex;
51
- flex-direction: column;
52
- /* Clears the sticky controls row, whose height the page declares once. */
53
- scroll-margin-top: calc(var(--showcase-nav-top, 0px) + 4.5rem);
54
- }
55
-
56
- /*
57
- * WHY the heading sits on its own rule rather than on whitespace: the page is
58
- */
59
-
60
- /*
61
- * one long column of stages now, and a gap alone did not read as a boundary once the stages themselves had air inside them.
62
- */
63
- .sc-section-head {
64
- display: flex;
65
- flex-direction: column;
66
- gap: 0.3125rem;
67
- margin-top: 2.25rem;
68
- padding: 2.25rem 0 1.25rem;
69
- border-top: 1px solid var(--color-border, var(--border));
70
- }
71
-
72
- section:first-child .sc-section-head {
73
- margin-top: 0;
74
- padding-top: 1.5rem;
75
- border-top: 0;
76
- }
77
-
78
- h2 {
79
- margin: 0;
80
- font-size: 1.125rem;
81
- font-weight: 600;
82
- line-height: 1.2;
83
- letter-spacing: -0.014em;
84
- color: var(--color-foreground, var(--foreground));
85
- }
86
-
87
- p {
88
- margin: 0;
89
- max-width: 62ch;
90
- font-size: 0.8125rem;
91
- line-height: 1.55;
92
- color: var(--color-muted-foreground, var(--muted-foreground));
93
- text-wrap: pretty;
94
- }
95
-
96
- /*
97
- * One stage per row. A grid of narrow cards made every component the size of the smallest one on the page, which is the opposite of what a showcase is
98
- */
99
-
100
- /*
101
- * for: a table and a button both got 20rem, and only one of them survived it.
102
- */
103
- .sc-stack {
104
- display: flex;
105
- flex-direction: column;
106
- gap: 0;
107
- }
108
-
109
- .sc-section-listed {
110
- padding-top: 1rem;
111
- }
112
- </style>
@@ -1,55 +0,0 @@
1
- <script lang="ts" module>
2
- import type { Snippet } from "svelte";
3
- import type { ShowcaseSectionMeta } from "./types.js";
4
-
5
- export interface ShowcaseShellProps {
6
- /** Every section the rail lists, in the order the page renders them. */
7
- sections: ShowcaseSectionMeta[];
8
- /** Search box, filters, anything that acts on the whole page. Spans both columns. */
9
- controls?: Snippet;
10
- /** Sits at the top of the rail, above the index. A title, a count, a legend. */
11
- railHead?: Snippet;
12
- /** The sections themselves. */
13
- children: Snippet;
14
- /** Rendered instead of the rail and the sections, e.g. a search result list. */
15
- override?: Snippet;
16
- }
17
- </script>
18
-
19
- <script lang="ts">
20
- import ShowcaseNav from "./ShowcaseNav.svelte";
21
- import "./showcase-shell.css";
22
-
23
- let { sections, controls, railHead, children, override }: ShowcaseShellProps = $props();
24
- </script>
25
-
26
- <div class="sc-shell">
27
- {#if controls}
28
- <div class="sc-shell-controls">
29
- {@render controls()}
30
- </div>
31
- {/if}
32
-
33
- {#if override}
34
- <div class="sc-shell-override">
35
- {@render override()}
36
- </div>
37
- {:else}
38
- <div class="sc-shell-body">
39
- <!-- A landmark of its own, because the index is a second way through the
40
- page rather than part of the page's content. -->
41
- <aside class="sc-shell-rail">
42
- {#if railHead}
43
- <div class="sc-shell-rail-head">
44
- {@render railHead()}
45
- </div>
46
- {/if}
47
- <ShowcaseNav {sections} />
48
- </aside>
49
-
50
- <div class="sc-shell-main">
51
- {@render children()}
52
- </div>
53
- </div>
54
- {/if}
55
- </div>