@stampui/blocks 1.1.0 → 2.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 (56) hide show
  1. package/dist/manifests.js +917 -170
  2. package/package.json +15 -10
  3. package/src/components/blocks/animated-counter.tsx +70 -0
  4. package/src/components/blocks/changelog-feed.tsx +3 -3
  5. package/src/components/blocks/gradient-text.tsx +39 -0
  6. package/src/components/blocks/grid-wave.tsx +40 -0
  7. package/src/components/blocks/loading-card.tsx +48 -0
  8. package/src/components/blocks/loading-dots.tsx +68 -0
  9. package/src/components/blocks/orbit-trail.tsx +30 -0
  10. package/src/components/blocks/progress-ring.tsx +72 -0
  11. package/src/components/blocks/registry-card.tsx +9 -10
  12. package/src/components/blocks/signal-arc.tsx +32 -0
  13. package/src/components/blocks/typewriter-text.tsx +62 -0
  14. package/src/components/blocks/waitlist-section.tsx +1 -1
  15. package/src/components/core/alert-dialog.tsx +2 -2
  16. package/src/components/core/avatar.tsx +8 -4
  17. package/src/components/core/button.tsx +1 -1
  18. package/src/components/core/checkbox.tsx +1 -1
  19. package/src/components/core/combobox.tsx +1 -1
  20. package/src/components/core/command.tsx +7 -4
  21. package/src/components/core/date-picker.tsx +1 -1
  22. package/src/components/core/dialog.tsx +1 -1
  23. package/src/components/core/drawer.tsx +1 -1
  24. package/src/components/core/input.tsx +2 -0
  25. package/src/components/core/label.tsx +1 -1
  26. package/src/components/core/multi-select.tsx +1 -1
  27. package/src/components/core/native-select.tsx +1 -1
  28. package/src/components/core/password-input.tsx +3 -0
  29. package/src/components/core/radio-group.tsx +1 -1
  30. package/src/components/core/resizable.tsx +1 -1
  31. package/src/components/core/select.tsx +1 -1
  32. package/src/components/core/sheet.tsx +1 -1
  33. package/src/components/core/slider.tsx +1 -1
  34. package/src/components/core/status-pulse.tsx +6 -0
  35. package/src/components/core/switch.tsx +1 -1
  36. package/src/components/core/table.tsx +7 -2
  37. package/src/components/core/tabs.tsx +1 -1
  38. package/src/components/core/toggle.tsx +1 -1
  39. package/src/components/core/typing-indicator.tsx +41 -27
  40. package/src/manifests.ts +932 -183
  41. package/src/components/blocks/ai-chat-shell.tsx +0 -97
  42. package/src/components/blocks/auth-panel.tsx +0 -203
  43. package/src/components/blocks/dashboard-shell.tsx +0 -135
  44. package/src/components/blocks/notification-center.tsx +0 -185
  45. package/src/components/blocks/onboarding-flow.tsx +0 -230
  46. package/src/components/blocks/project-command-center.tsx +0 -188
  47. package/src/components/blocks/prompt-input.tsx +0 -81
  48. package/src/components/blocks/settings-layout.tsx +0 -178
  49. package/src/components/blocks/token-stream.tsx +0 -42
  50. package/src/components/core/carousel.tsx +0 -170
  51. package/src/components/core/chart.tsx +0 -377
  52. package/src/components/core/data-table.tsx +0 -173
  53. package/src/components/core/file-upload.tsx +0 -143
  54. package/src/components/core/input-otp.tsx +0 -108
  55. package/src/components/core/stepper.tsx +0 -111
  56. package/src/components/core/timeline.tsx +0 -81
@@ -1,42 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import { cx } from "@/lib/cx"
5
-
6
- export interface TokenStreamProps extends React.HTMLAttributes<HTMLDivElement> {
7
- content: string
8
- speed?: number
9
- onComplete?: () => void
10
- }
11
-
12
- export function TokenStream({
13
- content,
14
- speed = 15,
15
- onComplete,
16
- className,
17
- ...props
18
- }: TokenStreamProps) {
19
- const [displayedContent, setDisplayedContent] = React.useState("")
20
- const [currentIndex, setCurrentIndex] = React.useState(0)
21
-
22
- React.useEffect(() => {
23
- if (currentIndex < content.length) {
24
- const timeout = setTimeout(() => {
25
- setDisplayedContent((prev) => prev + content[currentIndex])
26
- setCurrentIndex((prev) => prev + 1)
27
- }, speed)
28
- return () => clearTimeout(timeout)
29
- } else if (onComplete) {
30
- onComplete()
31
- }
32
- }, [content, currentIndex, speed, onComplete])
33
-
34
- return (
35
- <div className={cx("relative inline-block", className)} {...props}>
36
- <span className="whitespace-pre-wrap">{displayedContent}</span>
37
- {currentIndex < content.length && (
38
- <span className="ml-1 inline-block h-4 w-1 animate-pulse bg-primary align-middle" />
39
- )}
40
- </div>
41
- )
42
- }
@@ -1,170 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import { ChevronLeft, ChevronRight } from "lucide-react"
5
- import { cx } from "@/lib/cx"
6
-
7
- // ── Context ───────────────────────────────────────────────────────────────────
8
-
9
- interface CarouselContextValue {
10
- current: number
11
- count: number
12
- setCount: (n: number) => void
13
- canPrev: boolean
14
- canNext: boolean
15
- prev: () => void
16
- next: () => void
17
- goTo: (index: number) => void
18
- loop: boolean
19
- }
20
-
21
- const CarouselContext = React.createContext<CarouselContextValue | null>(null)
22
-
23
- function useCarousel() {
24
- const ctx = React.useContext(CarouselContext)
25
- if (!ctx) throw new Error("Must be inside Carousel")
26
- return ctx
27
- }
28
-
29
- // ── Root ──────────────────────────────────────────────────────────────────────
30
-
31
- export interface CarouselProps {
32
- children: React.ReactNode
33
- loop?: boolean
34
- autoPlay?: number
35
- className?: string
36
- }
37
-
38
- export function Carousel({ children, loop = false, autoPlay, className }: CarouselProps) {
39
- const [current, setCurrent] = React.useState(0)
40
- const [count, setCount] = React.useState(0)
41
-
42
- const canPrev = loop || current > 0
43
- const canNext = loop || current < count - 1
44
-
45
- function prev() {
46
- setCurrent((c) => loop ? (c - 1 + count) % count : Math.max(0, c - 1))
47
- }
48
- function next() {
49
- setCurrent((c) => loop ? (c + 1) % count : Math.min(count - 1, c + 1))
50
- }
51
- function goTo(index: number) {
52
- setCurrent(Math.max(0, Math.min(count - 1, index)))
53
- }
54
-
55
- React.useEffect(() => {
56
- if (!autoPlay || count === 0) return
57
- const t = setInterval(next, autoPlay)
58
- return () => clearInterval(t)
59
- }, [autoPlay, count, current])
60
-
61
- return (
62
- <CarouselContext.Provider value={{ current, count, setCount, canPrev, canNext, prev, next, goTo, loop }}>
63
- <div className={cx("relative", className)} data-carousel>
64
- {children}
65
- </div>
66
- </CarouselContext.Provider>
67
- )
68
- }
69
-
70
- // ── Content ───────────────────────────────────────────────────────────────────
71
-
72
- export function CarouselContent({ children, className }: { children: React.ReactNode; className?: string }) {
73
- const { current, setCount } = useCarousel()
74
- const items = React.Children.toArray(children)
75
-
76
- React.useEffect(() => {
77
- setCount(items.length)
78
- }, [items.length])
79
-
80
- return (
81
- <div className={cx("overflow-hidden", className)}>
82
- <div
83
- data-carousel-content
84
- className="flex transition-transform duration-300 ease-in-out"
85
- style={{ transform: `translateX(-${current * 100}%)` }}
86
- >
87
- {items.map((child, i) => (
88
- <div key={i} className="w-full shrink-0">
89
- {child}
90
- </div>
91
- ))}
92
- </div>
93
- </div>
94
- )
95
- }
96
-
97
- // ── Item ──────────────────────────────────────────────────────────────────────
98
-
99
- export function CarouselItem({ children, className }: { children: React.ReactNode; className?: string }) {
100
- return <div className={cx("w-full", className)}>{children}</div>
101
- }
102
-
103
- // ── Controls ──────────────────────────────────────────────────────────────────
104
-
105
- export function CarouselPrevious({ className }: { className?: string }) {
106
- const { prev, canPrev } = useCarousel()
107
- return (
108
- <button
109
- onClick={prev}
110
- disabled={!canPrev}
111
- className={cx(
112
- "flex h-8 w-8 items-center justify-center rounded-full border border-border bg-card shadow-sm transition-colors",
113
- "hover:bg-surface-2 disabled:opacity-40 disabled:pointer-events-none",
114
- className
115
- )}
116
- >
117
- <ChevronLeft className="h-4 w-4" />
118
- <span className="sr-only">Previous</span>
119
- </button>
120
- )
121
- }
122
-
123
- export function CarouselNext({ className }: { className?: string }) {
124
- const { next, canNext } = useCarousel()
125
- return (
126
- <button
127
- onClick={next}
128
- disabled={!canNext}
129
- className={cx(
130
- "flex h-8 w-8 items-center justify-center rounded-full border border-border bg-card shadow-sm transition-colors",
131
- "hover:bg-surface-2 disabled:opacity-40 disabled:pointer-events-none",
132
- className
133
- )}
134
- >
135
- <ChevronRight className="h-4 w-4" />
136
- <span className="sr-only">Next</span>
137
- </button>
138
- )
139
- }
140
-
141
- // ── Dots ──────────────────────────────────────────────────────────────────────
142
-
143
- export function CarouselDots({ className }: { className?: string }) {
144
- const { current, count, goTo } = useCarousel()
145
- return (
146
- <div className={cx("flex items-center gap-1.5", className)}>
147
- {Array.from({ length: count }).map((_, i) => (
148
- <button
149
- key={i}
150
- onClick={() => goTo(i)}
151
- className={cx(
152
- "h-1.5 rounded-full transition-all duration-200",
153
- i === current ? "w-4 bg-foreground" : "w-1.5 bg-border-strong hover:bg-muted-foreground"
154
- )}
155
- />
156
- ))}
157
- </div>
158
- )
159
- }
160
-
161
- // ── Counter ───────────────────────────────────────────────────────────────────
162
-
163
- export function CarouselCounter({ className }: { className?: string }) {
164
- const { current, count } = useCarousel()
165
- return (
166
- <span className={cx("text-xs text-muted-foreground", className)}>
167
- {current + 1} / {count}
168
- </span>
169
- )
170
- }
@@ -1,377 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import {
5
- LineChart as ReLineChart,
6
- Line,
7
- BarChart as ReBarChart,
8
- Bar,
9
- AreaChart as ReAreaChart,
10
- Area,
11
- PieChart as RePieChart,
12
- Pie,
13
- Cell,
14
- XAxis,
15
- YAxis,
16
- CartesianGrid,
17
- Tooltip,
18
- Legend,
19
- ResponsiveContainer,
20
- } from "recharts"
21
- import { cx } from "@/lib/cx"
22
-
23
- // ── Design tokens ─────────────────────────────────────────────────────────────
24
-
25
- export const CHART_COLORS = [
26
- "var(--color-foreground)",
27
- "var(--color-muted-foreground)",
28
- "var(--color-border-strong)",
29
- "var(--color-border)",
30
- "var(--color-surface-2)",
31
- ] as const
32
-
33
- // Explicit palette for charts that need distinct hues
34
- export const PALETTE = [
35
- "#18181b", // zinc-900
36
- "#71717a", // zinc-500
37
- "#a1a1aa", // zinc-400
38
- "#d4d4d8", // zinc-300
39
- "#e4e4e7", // zinc-200
40
- ]
41
-
42
- // ── Tooltip ───────────────────────────────────────────────────────────────────
43
-
44
- interface CustomTooltipProps {
45
- active?: boolean
46
- payload?: Array<{ name: string; value: number; color: string }>
47
- label?: string
48
- }
49
-
50
- function CustomTooltip({ active, payload, label }: CustomTooltipProps) {
51
- if (!active || !payload?.length) return null
52
- return (
53
- <div className="rounded-xl border border-border bg-card px-3 py-2.5 shadow-lg text-xs">
54
- {label && <p className="mb-1.5 font-medium text-foreground">{label}</p>}
55
- {payload.map((entry, i) => (
56
- <div key={i} className="flex items-center gap-2 text-muted-foreground">
57
- <span className="h-1.5 w-1.5 rounded-full shrink-0" style={{ backgroundColor: entry.color }} />
58
- <span>{entry.name}:</span>
59
- <span className="font-medium text-foreground">{entry.value}</span>
60
- </div>
61
- ))}
62
- </div>
63
- )
64
- }
65
-
66
- // ── Shared axis props ─────────────────────────────────────────────────────────
67
-
68
- const AXIS_STYLE = {
69
- tick: { fontSize: 11, fill: "var(--color-muted-foreground)" },
70
- axisLine: false as const,
71
- tickLine: false as const,
72
- }
73
-
74
- // ── LineChart ─────────────────────────────────────────────────────────────────
75
-
76
- export interface LineChartSeries {
77
- key: string
78
- label?: string
79
- color?: string
80
- }
81
-
82
- export interface LineChartProps {
83
- data: Record<string, unknown>[]
84
- xKey: string
85
- series: LineChartSeries[]
86
- height?: number
87
- curved?: boolean
88
- showDots?: boolean
89
- showGrid?: boolean
90
- showLegend?: boolean
91
- className?: string
92
- }
93
-
94
- export function LineChart({
95
- data,
96
- xKey,
97
- series,
98
- height = 280,
99
- curved = true,
100
- showDots = false,
101
- showGrid = true,
102
- showLegend = false,
103
- className,
104
- }: LineChartProps) {
105
- return (
106
- <div className={cx("w-full", className)} style={{ height }}>
107
- <ResponsiveContainer width="100%" height="100%">
108
- <ReLineChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
109
- {showGrid && <CartesianGrid strokeDasharray="3 3" stroke="var(--color-border)" vertical={false} />}
110
- <XAxis dataKey={xKey} {...AXIS_STYLE} />
111
- <YAxis {...AXIS_STYLE} />
112
- <Tooltip content={<CustomTooltip />} />
113
- {showLegend && <Legend wrapperStyle={{ fontSize: 11, color: "var(--color-muted-foreground)" }} />}
114
- {series.map((s, i) => (
115
- <Line
116
- key={s.key}
117
- type={curved ? "monotone" : "linear"}
118
- dataKey={s.key}
119
- name={s.label ?? s.key}
120
- stroke={s.color ?? PALETTE[i % PALETTE.length]}
121
- strokeWidth={2}
122
- dot={showDots ? { r: 3, fill: s.color ?? PALETTE[i % PALETTE.length], strokeWidth: 0 } : false}
123
- activeDot={{ r: 4, strokeWidth: 0 }}
124
- />
125
- ))}
126
- </ReLineChart>
127
- </ResponsiveContainer>
128
- </div>
129
- )
130
- }
131
-
132
- // ── BarChart ──────────────────────────────────────────────────────────────────
133
-
134
- export interface BarChartSeries {
135
- key: string
136
- label?: string
137
- color?: string
138
- }
139
-
140
- export interface BarChartProps {
141
- data: Record<string, unknown>[]
142
- xKey: string
143
- series: BarChartSeries[]
144
- height?: number
145
- horizontal?: boolean
146
- stacked?: boolean
147
- showGrid?: boolean
148
- showLegend?: boolean
149
- radius?: number
150
- className?: string
151
- }
152
-
153
- export function BarChart({
154
- data,
155
- xKey,
156
- series,
157
- height = 280,
158
- horizontal = false,
159
- stacked = false,
160
- showGrid = true,
161
- showLegend = false,
162
- radius = 4,
163
- className,
164
- }: BarChartProps) {
165
- return (
166
- <div className={cx("w-full", className)} style={{ height }}>
167
- <ResponsiveContainer width="100%" height="100%">
168
- <ReBarChart
169
- data={data}
170
- layout={horizontal ? "vertical" : "horizontal"}
171
- margin={{ top: 4, right: 4, left: horizontal ? 8 : -20, bottom: 0 }}
172
- >
173
- {showGrid && (
174
- <CartesianGrid
175
- strokeDasharray="3 3"
176
- stroke="var(--color-border)"
177
- vertical={!horizontal}
178
- horizontal={horizontal}
179
- />
180
- )}
181
- {horizontal ? (
182
- <>
183
- <XAxis type="number" {...AXIS_STYLE} />
184
- <YAxis type="category" dataKey={xKey} {...AXIS_STYLE} width={60} />
185
- </>
186
- ) : (
187
- <>
188
- <XAxis dataKey={xKey} {...AXIS_STYLE} />
189
- <YAxis {...AXIS_STYLE} />
190
- </>
191
- )}
192
- <Tooltip content={<CustomTooltip />} />
193
- {showLegend && <Legend wrapperStyle={{ fontSize: 11, color: "var(--color-muted-foreground)" }} />}
194
- {series.map((s, i) => (
195
- <Bar
196
- key={s.key}
197
- dataKey={s.key}
198
- name={s.label ?? s.key}
199
- fill={s.color ?? PALETTE[i % PALETTE.length]}
200
- stackId={stacked ? "stack" : undefined}
201
- radius={i === series.length - 1 || !stacked ? radius : 0}
202
- />
203
- ))}
204
- </ReBarChart>
205
- </ResponsiveContainer>
206
- </div>
207
- )
208
- }
209
-
210
- // ── AreaChart ─────────────────────────────────────────────────────────────────
211
-
212
- export interface AreaChartSeries {
213
- key: string
214
- label?: string
215
- color?: string
216
- }
217
-
218
- export interface AreaChartProps {
219
- data: Record<string, unknown>[]
220
- xKey: string
221
- series: AreaChartSeries[]
222
- height?: number
223
- curved?: boolean
224
- stacked?: boolean
225
- showGrid?: boolean
226
- showLegend?: boolean
227
- className?: string
228
- }
229
-
230
- export function AreaChart({
231
- data,
232
- xKey,
233
- series,
234
- height = 280,
235
- curved = true,
236
- stacked = false,
237
- showGrid = true,
238
- showLegend = false,
239
- className,
240
- }: AreaChartProps) {
241
- return (
242
- <div className={cx("w-full", className)} style={{ height }}>
243
- <ResponsiveContainer width="100%" height="100%">
244
- <ReAreaChart data={data} margin={{ top: 4, right: 4, left: -20, bottom: 0 }}>
245
- <defs>
246
- {series.map((s, i) => {
247
- const color = s.color ?? PALETTE[i % PALETTE.length]
248
- return (
249
- <linearGradient key={s.key} id={`grad-${s.key}`} x1="0" y1="0" x2="0" y2="1">
250
- <stop offset="5%" stopColor={color} stopOpacity={0.15} />
251
- <stop offset="95%" stopColor={color} stopOpacity={0} />
252
- </linearGradient>
253
- )
254
- })}
255
- </defs>
256
- {showGrid && <CartesianGrid strokeDasharray="3 3" stroke="var(--color-border)" vertical={false} />}
257
- <XAxis dataKey={xKey} {...AXIS_STYLE} />
258
- <YAxis {...AXIS_STYLE} />
259
- <Tooltip content={<CustomTooltip />} />
260
- {showLegend && <Legend wrapperStyle={{ fontSize: 11, color: "var(--color-muted-foreground)" }} />}
261
- {series.map((s, i) => {
262
- const color = s.color ?? PALETTE[i % PALETTE.length]
263
- return (
264
- <Area
265
- key={s.key}
266
- type={curved ? "monotone" : "linear"}
267
- dataKey={s.key}
268
- name={s.label ?? s.key}
269
- stroke={color}
270
- strokeWidth={2}
271
- fill={`url(#grad-${s.key})`}
272
- stackId={stacked ? "stack" : undefined}
273
- />
274
- )
275
- })}
276
- </ReAreaChart>
277
- </ResponsiveContainer>
278
- </div>
279
- )
280
- }
281
-
282
- // ── PieChart ──────────────────────────────────────────────────────────────────
283
-
284
- export interface PieSlice {
285
- name: string
286
- value: number
287
- color?: string
288
- }
289
-
290
- export interface PieChartProps {
291
- data: PieSlice[]
292
- height?: number
293
- donut?: boolean
294
- showLabels?: boolean
295
- showLegend?: boolean
296
- className?: string
297
- }
298
-
299
- interface PieLabelProps {
300
- cx?: number
301
- cy?: number
302
- midAngle?: number
303
- innerRadius?: number
304
- outerRadius?: number
305
- name?: string
306
- percent?: number
307
- }
308
-
309
- function PieLabel({ cx, cy, midAngle, innerRadius, outerRadius, name, percent }: PieLabelProps) {
310
- if (
311
- cx == null ||
312
- cy == null ||
313
- midAngle == null ||
314
- innerRadius == null ||
315
- outerRadius == null ||
316
- percent == null ||
317
- percent < 0.05
318
- ) {
319
- return null
320
- }
321
-
322
- const RADIAN = Math.PI / 180
323
- const radius = innerRadius + (outerRadius - innerRadius) * 0.5
324
- const x = cx + radius * Math.cos(-midAngle * RADIAN)
325
- const y = cy + radius * Math.sin(-midAngle * RADIAN)
326
- return (
327
- <text x={x} y={y} fill="var(--color-background)" textAnchor="middle" dominantBaseline="central" fontSize={11} fontWeight={500}>
328
- {name}
329
- </text>
330
- )
331
- }
332
-
333
- export function PieChart({
334
- data,
335
- height = 280,
336
- donut = false,
337
- showLabels = false,
338
- showLegend = true,
339
- className,
340
- }: PieChartProps) {
341
- const outerRadius = 100
342
- const innerRadius = donut ? 60 : 0
343
-
344
- return (
345
- <div className={cx("w-full", className)} style={{ height }}>
346
- <ResponsiveContainer width="100%" height="100%">
347
- <RePieChart>
348
- <Pie
349
- data={data}
350
- dataKey="value"
351
- nameKey="name"
352
- cx="50%"
353
- cy="50%"
354
- outerRadius={outerRadius}
355
- innerRadius={innerRadius}
356
- strokeWidth={2}
357
- stroke="var(--color-card)"
358
- labelLine={false}
359
- label={showLabels ? PieLabel : undefined}
360
- >
361
- {data.map((entry, i) => (
362
- <Cell key={i} fill={entry.color ?? PALETTE[i % PALETTE.length]} />
363
- ))}
364
- </Pie>
365
- <Tooltip content={<CustomTooltip />} />
366
- {showLegend && (
367
- <Legend
368
- wrapperStyle={{ fontSize: 11, color: "var(--color-muted-foreground)" }}
369
- iconType="circle"
370
- iconSize={8}
371
- />
372
- )}
373
- </RePieChart>
374
- </ResponsiveContainer>
375
- </div>
376
- )
377
- }