@heyramzi/ui 6.1.0 → 7.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 (98) hide show
  1. package/COMPONENTS.md +6 -25
  2. package/README.md +6 -4
  3. package/package.json +7 -15
  4. package/scripts/sync-shadcn.mjs +12 -3
  5. package/src/ai/index.ts +0 -18
  6. package/src/animations/README.md +3 -3
  7. package/src/lib/sliding-pill.ts +68 -0
  8. package/src/lib/theme-transition.ts +4 -2
  9. package/src/shadcn/code-block/code-block-actions.svelte +23 -0
  10. package/src/shadcn/code-block/code-block-content.svelte +55 -0
  11. package/src/shadcn/code-block/code-block-copy-button.svelte +59 -0
  12. package/src/shadcn/code-block/code-block-footer.svelte +20 -0
  13. package/src/shadcn/code-block/code-block-header.svelte +23 -0
  14. package/src/shadcn/code-block/code-block-line.svelte +71 -0
  15. package/src/shadcn/code-block/code-block-title.svelte +23 -0
  16. package/src/shadcn/code-block/code-block.svelte +47 -0
  17. package/src/shadcn/code-block/index.ts +42 -0
  18. package/src/shadcn/hooks/highlighter.ts +132 -0
  19. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +98 -0
  20. package/src/shadcn/kbd/index.ts +10 -0
  21. package/src/shadcn/kbd/kbd-group.svelte +20 -0
  22. package/src/shadcn/kbd/kbd.svelte +23 -0
  23. package/src/shadcn/message-scroller/index.ts +28 -0
  24. package/src/shadcn/message-scroller/message-scroller-button.svelte +52 -0
  25. package/src/shadcn/message-scroller/message-scroller-content.svelte +17 -0
  26. package/src/shadcn/message-scroller/message-scroller-item.svelte +22 -0
  27. package/src/shadcn/message-scroller/message-scroller-provider.svelte +7 -0
  28. package/src/shadcn/message-scroller/message-scroller-viewport.svelte +20 -0
  29. package/src/shadcn/message-scroller/message-scroller.svelte +20 -0
  30. package/src/shadcn/tabs/tabs-list.svelte +13 -2
  31. package/src/shadcn/tabs/tabs-trigger.svelte +1 -1
  32. package/src/shadcn/trace/index.ts +29 -0
  33. package/src/shadcn/trace/trace-collapsible-content.svelte +29 -0
  34. package/src/shadcn/trace/trace-collapsible-trigger.svelte +29 -0
  35. package/src/shadcn/trace/trace-collapsible.svelte +20 -0
  36. package/src/shadcn/trace/trace-description.svelte +20 -0
  37. package/src/shadcn/trace/trace-icon.svelte +24 -0
  38. package/src/shadcn/trace/trace-line.svelte +31 -0
  39. package/src/shadcn/trace/trace-title.svelte +20 -0
  40. package/src/shadcn/trace/trace.svelte +23 -0
  41. package/src/showcase/AiSection.svelte +0 -7
  42. package/src/showcase/demos/shadcn/code-block.svelte +80 -0
  43. package/src/showcase/demos/shadcn/index.ts +24 -0
  44. package/src/showcase/demos/shadcn/message-scroller.svelte +44 -0
  45. package/src/showcase/demos/shadcn/trace.svelte +51 -0
  46. package/src/showcase/kit.ts +1 -2
  47. package/src/ai/conversation/Conversation.svelte +0 -28
  48. package/src/ai/conversation/ConversationContent.svelte +0 -35
  49. package/src/ai/conversation/ConversationEmptyState.svelte +0 -46
  50. package/src/ai/conversation/ConversationScrollButton.svelte +0 -51
  51. package/src/ai/conversation/index.ts +0 -33
  52. package/src/ai/conversation/stick-to-bottom-context.svelte.ts +0 -131
  53. package/src/charts/animated-value.css +0 -7
  54. package/src/charts/animated-value.svelte +0 -110
  55. package/src/charts/area-chart.css +0 -29
  56. package/src/charts/area-chart.svelte +0 -195
  57. package/src/charts/bar-chart.css +0 -119
  58. package/src/charts/bar-chart.svelte +0 -269
  59. package/src/charts/charts.css +0 -201
  60. package/src/charts/crosshair.svelte +0 -35
  61. package/src/charts/donut-chart.css +0 -56
  62. package/src/charts/donut-chart.svelte +0 -158
  63. package/src/charts/edge-fade.svelte +0 -43
  64. package/src/charts/edge-fade.ts +0 -16
  65. package/src/charts/figure.css +0 -100
  66. package/src/charts/figure.svelte +0 -122
  67. package/src/charts/gauge-chart.css +0 -94
  68. package/src/charts/gauge-chart.svelte +0 -303
  69. package/src/charts/hover-band.test.ts +0 -30
  70. package/src/charts/hover-band.ts +0 -21
  71. package/src/charts/hover-focus.svelte +0 -85
  72. package/src/charts/index.ts +0 -30
  73. package/src/charts/legend.css +0 -131
  74. package/src/charts/legend.svelte +0 -112
  75. package/src/charts/line-chart.css +0 -26
  76. package/src/charts/line-chart.svelte +0 -256
  77. package/src/charts/motion.ts +0 -19
  78. package/src/charts/number-chart.css +0 -168
  79. package/src/charts/number-chart.svelte +0 -177
  80. package/src/charts/primitives/chart-container.svelte +0 -57
  81. package/src/charts/primitives/chart-style.svelte +0 -36
  82. package/src/charts/primitives/chart-tooltip.svelte +0 -161
  83. package/src/charts/primitives/chart-utils.ts +0 -54
  84. package/src/charts/primitives/index.ts +0 -11
  85. package/src/charts/reveal.svelte +0 -61
  86. package/src/charts/ring-chart.css +0 -96
  87. package/src/charts/ring-chart.svelte +0 -158
  88. package/src/charts/sparkline.css +0 -45
  89. package/src/charts/sparkline.svelte +0 -140
  90. package/src/charts/stat-card.css +0 -103
  91. package/src/charts/stat-card.svelte +0 -267
  92. package/src/charts/time-series-chart.css +0 -28
  93. package/src/charts/time-series-chart.svelte +0 -179
  94. package/src/charts/trend-pill.css +0 -38
  95. package/src/charts/trend-pill.svelte +0 -48
  96. package/src/lib/hooks/is-mobile.svelte.ts +0 -11
  97. package/src/showcase/ChartsSection.svelte +0 -458
  98. package/src/showcase/demos/ai/conversation.svelte +0 -59
@@ -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
- }