@stampui/blocks 1.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.
- package/dist/components/ai-chat-shell.d.ts +1 -0
- package/dist/components/ai-chat-shell.js +23 -0
- package/dist/components/prompt-input.d.ts +5 -0
- package/dist/components/prompt-input.js +47 -0
- package/dist/components/registry-card.d.ts +6 -0
- package/dist/components/registry-card.js +15 -0
- package/dist/components/registry-explorer.d.ts +8 -0
- package/dist/components/registry-explorer.js +38 -0
- package/dist/components/token-stream.d.ts +7 -0
- package/dist/components/token-stream.js +21 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +23 -0
- package/dist/manifests.d.ts +3 -0
- package/dist/manifests.js +1666 -0
- package/dist/types.d.ts +44 -0
- package/dist/types.js +2 -0
- package/package.json +28 -0
- package/src/components/blocks/ai-chat-shell.tsx +97 -0
- package/src/components/blocks/auth-panel.tsx +203 -0
- package/src/components/blocks/feature-grid.tsx +122 -0
- package/src/components/blocks/hero-section.tsx +73 -0
- package/src/components/blocks/notification-center.tsx +185 -0
- package/src/components/blocks/onboarding-flow.tsx +230 -0
- package/src/components/blocks/pricing-section.tsx +135 -0
- package/src/components/blocks/project-command-center.tsx +188 -0
- package/src/components/blocks/prompt-input.tsx +81 -0
- package/src/components/blocks/registry-card.tsx +104 -0
- package/src/components/blocks/registry-explorer.tsx +78 -0
- package/src/components/blocks/settings-layout.tsx +178 -0
- package/src/components/blocks/stats-strip.tsx +100 -0
- package/src/components/blocks/token-stream.tsx +42 -0
- package/src/components/blocks/usage-card.tsx +116 -0
- package/src/components/core/accordion.tsx +58 -0
- package/src/components/core/alert-dialog.tsx +113 -0
- package/src/components/core/alert.tsx +48 -0
- package/src/components/core/animated-number.tsx +77 -0
- package/src/components/core/aspect-ratio.tsx +20 -0
- package/src/components/core/avatar-stack.tsx +61 -0
- package/src/components/core/avatar.tsx +90 -0
- package/src/components/core/badge.tsx +39 -0
- package/src/components/core/breadcrumb.tsx +63 -0
- package/src/components/core/button-group.tsx +37 -0
- package/src/components/core/button.tsx +110 -0
- package/src/components/core/calendar.tsx +143 -0
- package/src/components/core/card.tsx +60 -0
- package/src/components/core/carousel.tsx +170 -0
- package/src/components/core/chart.tsx +377 -0
- package/src/components/core/checkbox.tsx +64 -0
- package/src/components/core/collapsible.tsx +30 -0
- package/src/components/core/combobox.tsx +114 -0
- package/src/components/core/command-box.tsx +22 -0
- package/src/components/core/command.tsx +165 -0
- package/src/components/core/confirm-action.tsx +94 -0
- package/src/components/core/context-menu.tsx +139 -0
- package/src/components/core/copy-button.tsx +41 -0
- package/src/components/core/data-table.tsx +173 -0
- package/src/components/core/date-picker.tsx +73 -0
- package/src/components/core/dialog.tsx +83 -0
- package/src/components/core/drawer.tsx +87 -0
- package/src/components/core/dropdown-menu.tsx +147 -0
- package/src/components/core/empty.tsx +34 -0
- package/src/components/core/field.tsx +39 -0
- package/src/components/core/file-upload.tsx +143 -0
- package/src/components/core/hover-card.tsx +31 -0
- package/src/components/core/inline-edit.tsx +104 -0
- package/src/components/core/input-group.tsx +47 -0
- package/src/components/core/input-otp.tsx +108 -0
- package/src/components/core/input.tsx +37 -0
- package/src/components/core/kbd.tsx +47 -0
- package/src/components/core/label.tsx +28 -0
- package/src/components/core/marquee.tsx +61 -0
- package/src/components/core/menubar.tsx +120 -0
- package/src/components/core/multi-select.tsx +145 -0
- package/src/components/core/native-select.tsx +27 -0
- package/src/components/core/navigation-menu.tsx +130 -0
- package/src/components/core/number-stepper.tsx +80 -0
- package/src/components/core/pagination.tsx +80 -0
- package/src/components/core/password-input.tsx +90 -0
- package/src/components/core/popover.tsx +34 -0
- package/src/components/core/progress.tsx +63 -0
- package/src/components/core/radio-group.tsx +77 -0
- package/src/components/core/resizable.tsx +250 -0
- package/src/components/core/scroll-area.tsx +38 -0
- package/src/components/core/select.tsx +128 -0
- package/src/components/core/separator.tsx +47 -0
- package/src/components/core/sheet.tsx +118 -0
- package/src/components/core/sidebar.tsx +129 -0
- package/src/components/core/skeleton.tsx +32 -0
- package/src/components/core/slider.tsx +97 -0
- package/src/components/core/sonner.tsx +29 -0
- package/src/components/core/spinner.tsx +60 -0
- package/src/components/core/status-pulse.tsx +67 -0
- package/src/components/core/stepper.tsx +111 -0
- package/src/components/core/switch.tsx +72 -0
- package/src/components/core/table.tsx +104 -0
- package/src/components/core/tabs.tsx +55 -0
- package/src/components/core/tag-input.tsx +93 -0
- package/src/components/core/textarea.tsx +44 -0
- package/src/components/core/timeline.tsx +81 -0
- package/src/components/core/toggle-group.tsx +56 -0
- package/src/components/core/toggle.tsx +66 -0
- package/src/components/core/tooltip.tsx +31 -0
- package/src/components/core/typing-indicator.tsx +51 -0
- package/src/index.ts +8 -0
- package/src/manifests.ts +1682 -0
- package/src/types.ts +58 -0
- package/src/ui.ts +13 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as SwitchPrimitive from "@radix-ui/react-switch"
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
|
+
import { cx } from "@/lib/cx"
|
|
7
|
+
|
|
8
|
+
const switchStyles = cva(
|
|
9
|
+
[
|
|
10
|
+
"peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent",
|
|
11
|
+
"transition-all duration-200 ease-out outline-none",
|
|
12
|
+
"focus-visible:ring-1 focus-visible:ring-border-strong focus-visible:ring-offset-1 focus-visible:ring-offset-background",
|
|
13
|
+
"disabled:cursor-not-allowed disabled:opacity-40",
|
|
14
|
+
"data-[state=unchecked]:bg-surface-3",
|
|
15
|
+
],
|
|
16
|
+
{
|
|
17
|
+
variants: {
|
|
18
|
+
size: {
|
|
19
|
+
sm: "h-4 w-7",
|
|
20
|
+
md: "h-5 w-9",
|
|
21
|
+
lg: "h-6 w-11",
|
|
22
|
+
},
|
|
23
|
+
color: {
|
|
24
|
+
default: "data-[state=checked]:bg-foreground",
|
|
25
|
+
success: "data-[state=checked]:bg-green-600",
|
|
26
|
+
danger: "data-[state=checked]:bg-red-600",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
size: "md",
|
|
31
|
+
color: "default",
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const thumbStyles = cva(
|
|
37
|
+
[
|
|
38
|
+
"pointer-events-none block rounded-full bg-background shadow-sm ring-0",
|
|
39
|
+
"transition-transform duration-200 ease-out",
|
|
40
|
+
"data-[state=unchecked]:translate-x-0",
|
|
41
|
+
],
|
|
42
|
+
{
|
|
43
|
+
variants: {
|
|
44
|
+
size: {
|
|
45
|
+
sm: "h-3 w-3 data-[state=checked]:translate-x-3",
|
|
46
|
+
md: "h-4 w-4 data-[state=checked]:translate-x-4",
|
|
47
|
+
lg: "h-5 w-5 data-[state=checked]:translate-x-5",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
defaultVariants: { size: "md" },
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
export interface SwitchProps
|
|
55
|
+
extends Omit<React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>, "color">,
|
|
56
|
+
VariantProps<typeof switchStyles> {}
|
|
57
|
+
|
|
58
|
+
const Switch = React.forwardRef<
|
|
59
|
+
React.ElementRef<typeof SwitchPrimitive.Root>,
|
|
60
|
+
SwitchProps
|
|
61
|
+
>(({ className, size, color, ...props }, ref) => (
|
|
62
|
+
<SwitchPrimitive.Root
|
|
63
|
+
className={cx(switchStyles({ size, color }), className)}
|
|
64
|
+
{...props}
|
|
65
|
+
ref={ref}
|
|
66
|
+
>
|
|
67
|
+
<SwitchPrimitive.Thumb className={thumbStyles({ size })} />
|
|
68
|
+
</SwitchPrimitive.Root>
|
|
69
|
+
))
|
|
70
|
+
Switch.displayName = "Switch"
|
|
71
|
+
|
|
72
|
+
export { Switch }
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cx } from "@/lib/cx"
|
|
3
|
+
|
|
4
|
+
export function Table({
|
|
5
|
+
className,
|
|
6
|
+
...props
|
|
7
|
+
}: React.ComponentPropsWithoutRef<"table">) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="w-full overflow-x-auto rounded-xl border border-border">
|
|
10
|
+
<table
|
|
11
|
+
className={cx("w-full text-sm", className)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function TableHeader({
|
|
19
|
+
className,
|
|
20
|
+
...props
|
|
21
|
+
}: React.ComponentPropsWithoutRef<"thead">) {
|
|
22
|
+
return (
|
|
23
|
+
<thead
|
|
24
|
+
className={cx("bg-surface-2 border-b border-border", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function TableBody({
|
|
31
|
+
className,
|
|
32
|
+
...props
|
|
33
|
+
}: React.ComponentPropsWithoutRef<"tbody">) {
|
|
34
|
+
return <tbody className={cx("divide-y divide-border", className)} {...props} />
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function TableFooter({
|
|
38
|
+
className,
|
|
39
|
+
...props
|
|
40
|
+
}: React.ComponentPropsWithoutRef<"tfoot">) {
|
|
41
|
+
return (
|
|
42
|
+
<tfoot
|
|
43
|
+
className={cx(
|
|
44
|
+
"border-t border-border bg-surface-2 font-medium text-foreground",
|
|
45
|
+
className
|
|
46
|
+
)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function TableRow({
|
|
53
|
+
className,
|
|
54
|
+
...props
|
|
55
|
+
}: React.ComponentPropsWithoutRef<"tr">) {
|
|
56
|
+
return (
|
|
57
|
+
<tr
|
|
58
|
+
className={cx("transition-colors hover:bg-surface-2/60", className)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function TableHead({
|
|
65
|
+
className,
|
|
66
|
+
...props
|
|
67
|
+
}: React.ComponentPropsWithoutRef<"th">) {
|
|
68
|
+
return (
|
|
69
|
+
<th
|
|
70
|
+
className={cx(
|
|
71
|
+
"px-4 py-3 text-left text-xs font-medium text-muted-foreground",
|
|
72
|
+
className
|
|
73
|
+
)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function TableCell({
|
|
80
|
+
className,
|
|
81
|
+
...props
|
|
82
|
+
}: React.ComponentPropsWithoutRef<"td">) {
|
|
83
|
+
return (
|
|
84
|
+
<td
|
|
85
|
+
className={cx("px-4 py-3 text-sm text-foreground", className)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function TableCaption({
|
|
92
|
+
className,
|
|
93
|
+
...props
|
|
94
|
+
}: React.ComponentPropsWithoutRef<"caption">) {
|
|
95
|
+
return (
|
|
96
|
+
<caption
|
|
97
|
+
className={cx(
|
|
98
|
+
"mt-3 text-xs text-muted-foreground text-center",
|
|
99
|
+
className
|
|
100
|
+
)}
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as RadixTabs from "@radix-ui/react-tabs"
|
|
5
|
+
import { cx } from "@/lib/cx"
|
|
6
|
+
|
|
7
|
+
export const Tabs = RadixTabs.Root
|
|
8
|
+
|
|
9
|
+
export const TabsList = React.forwardRef<
|
|
10
|
+
React.ElementRef<typeof RadixTabs.List>,
|
|
11
|
+
React.ComponentPropsWithoutRef<typeof RadixTabs.List>
|
|
12
|
+
>(({ className, ...props }, ref) => (
|
|
13
|
+
<RadixTabs.List
|
|
14
|
+
ref={ref}
|
|
15
|
+
className={cx(
|
|
16
|
+
"flex flex-wrap items-center gap-2 bg-transparent",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
))
|
|
22
|
+
TabsList.displayName = "TabsList"
|
|
23
|
+
|
|
24
|
+
export const TabsTrigger = React.forwardRef<
|
|
25
|
+
React.ElementRef<typeof RadixTabs.Trigger>,
|
|
26
|
+
React.ComponentPropsWithoutRef<typeof RadixTabs.Trigger>
|
|
27
|
+
>(({ className, ...props }, ref) => (
|
|
28
|
+
<RadixTabs.Trigger
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cx(
|
|
31
|
+
"relative flex items-center justify-center whitespace-nowrap rounded-lg px-4 py-2 text-sm font-medium transition-all outline-none border border-transparent",
|
|
32
|
+
"text-muted-foreground hover:bg-surface-2 hover:text-foreground",
|
|
33
|
+
"data-[state=active]:bg-surface-raised data-[state=active]:text-foreground data-[state=active]:border-border-strong data-[state=active]:font-medium",
|
|
34
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
35
|
+
className
|
|
36
|
+
)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
))
|
|
40
|
+
TabsTrigger.displayName = "TabsTrigger"
|
|
41
|
+
|
|
42
|
+
export const TabsContent = React.forwardRef<
|
|
43
|
+
React.ElementRef<typeof RadixTabs.Content>,
|
|
44
|
+
React.ComponentPropsWithoutRef<typeof RadixTabs.Content>
|
|
45
|
+
>(({ className, ...props }, ref) => (
|
|
46
|
+
<RadixTabs.Content
|
|
47
|
+
ref={ref}
|
|
48
|
+
className={cx(
|
|
49
|
+
"mt-4 outline-none focus-visible:ring-1 focus-visible:ring-border-strong focus-visible:ring-offset-1 focus-visible:ring-offset-background",
|
|
50
|
+
className
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
))
|
|
55
|
+
TabsContent.displayName = "TabsContent"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { X } from "lucide-react"
|
|
5
|
+
import { cx } from "@/lib/cx"
|
|
6
|
+
|
|
7
|
+
export interface TagInputProps {
|
|
8
|
+
value?: string[]
|
|
9
|
+
onChange?: (tags: string[]) => void
|
|
10
|
+
placeholder?: string
|
|
11
|
+
max?: number
|
|
12
|
+
disabled?: boolean
|
|
13
|
+
className?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function TagInput({
|
|
17
|
+
value,
|
|
18
|
+
onChange,
|
|
19
|
+
placeholder = "Add tag…",
|
|
20
|
+
max,
|
|
21
|
+
disabled = false,
|
|
22
|
+
className,
|
|
23
|
+
}: TagInputProps) {
|
|
24
|
+
const [tags, setTags] = React.useState<string[]>(value ?? [])
|
|
25
|
+
const [input, setInput] = React.useState("")
|
|
26
|
+
const inputRef = React.useRef<HTMLInputElement>(null)
|
|
27
|
+
|
|
28
|
+
const update = (next: string[]) => {
|
|
29
|
+
setTags(next)
|
|
30
|
+
onChange?.(next)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const add = () => {
|
|
34
|
+
const trimmed = input.trim()
|
|
35
|
+
if (!trimmed || tags.includes(trimmed) || (max !== undefined && tags.length >= max)) return
|
|
36
|
+
update([...tags, trimmed])
|
|
37
|
+
setInput("")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const remove = (index: number) => {
|
|
41
|
+
update(tags.filter((_, i) => i !== index))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
45
|
+
if (e.key === "Enter" || e.key === ",") {
|
|
46
|
+
e.preventDefault()
|
|
47
|
+
add()
|
|
48
|
+
} else if (e.key === "Backspace" && !input && tags.length > 0) {
|
|
49
|
+
remove(tags.length - 1)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
className={cx(
|
|
56
|
+
"flex flex-wrap gap-1.5 min-h-10 w-full rounded-lg border border-border bg-input px-3 py-2 text-sm transition-colors",
|
|
57
|
+
"focus-within:border-border-strong",
|
|
58
|
+
disabled && "opacity-50 cursor-not-allowed",
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
onClick={() => inputRef.current?.focus()}
|
|
62
|
+
>
|
|
63
|
+
{tags.map((tag, i) => (
|
|
64
|
+
<span
|
|
65
|
+
key={i}
|
|
66
|
+
className="inline-flex items-center gap-1 rounded-md bg-surface-2 border border-border px-2 py-0.5 text-xs font-medium text-foreground"
|
|
67
|
+
>
|
|
68
|
+
{tag}
|
|
69
|
+
{!disabled && (
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
onClick={(e) => { e.stopPropagation(); remove(i) }}
|
|
73
|
+
className="text-muted-foreground hover:text-foreground transition-colors"
|
|
74
|
+
aria-label={`Remove ${tag}`}
|
|
75
|
+
>
|
|
76
|
+
<X className="h-3 w-3" />
|
|
77
|
+
</button>
|
|
78
|
+
)}
|
|
79
|
+
</span>
|
|
80
|
+
))}
|
|
81
|
+
<input
|
|
82
|
+
ref={inputRef}
|
|
83
|
+
value={input}
|
|
84
|
+
onChange={(e) => setInput(e.target.value)}
|
|
85
|
+
onKeyDown={onKeyDown}
|
|
86
|
+
onBlur={add}
|
|
87
|
+
placeholder={tags.length === 0 ? placeholder : ""}
|
|
88
|
+
disabled={disabled || (max !== undefined && tags.length >= max)}
|
|
89
|
+
className="flex-1 min-w-20 bg-transparent outline-none placeholder:text-muted-foreground/50 text-foreground disabled:cursor-not-allowed"
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cx } from "@/lib/cx"
|
|
4
|
+
|
|
5
|
+
const textareaStyles = cva(
|
|
6
|
+
"flex w-full rounded-lg text-sm transition-all outline-none focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 resize-none",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
surface:
|
|
11
|
+
"bg-input border border-border px-3 py-2.5 text-foreground placeholder:text-muted-foreground focus:border-border-strong focus:bg-surface-3",
|
|
12
|
+
minimal:
|
|
13
|
+
"bg-transparent border-b border-border px-0 py-2 text-foreground placeholder:text-muted-foreground/50 rounded-none focus:outline-none focus:border-border-strong",
|
|
14
|
+
},
|
|
15
|
+
resize: {
|
|
16
|
+
none: "resize-none",
|
|
17
|
+
vertical: "resize-y",
|
|
18
|
+
both: "resize",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
variant: "surface",
|
|
23
|
+
resize: "none",
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
export interface TextareaProps
|
|
29
|
+
extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, "resize">,
|
|
30
|
+
VariantProps<typeof textareaStyles> {}
|
|
31
|
+
|
|
32
|
+
export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
33
|
+
({ className, variant, resize, rows = 4, ...props }, ref) => {
|
|
34
|
+
return (
|
|
35
|
+
<textarea
|
|
36
|
+
rows={rows}
|
|
37
|
+
className={cx(textareaStyles({ variant, resize }), className)}
|
|
38
|
+
ref={ref}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
Textarea.displayName = "Textarea"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cx } from "@/lib/cx"
|
|
3
|
+
|
|
4
|
+
// ── Types ─────────────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export interface TimelineItem {
|
|
7
|
+
title: string
|
|
8
|
+
description?: string
|
|
9
|
+
date?: string
|
|
10
|
+
icon?: React.ReactNode
|
|
11
|
+
dot?: "default" | "filled" | "outline" | "ring"
|
|
12
|
+
children?: React.ReactNode
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TimelineProps {
|
|
16
|
+
items: TimelineItem[]
|
|
17
|
+
className?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── Timeline ──────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
export function Timeline({ items, className }: TimelineProps) {
|
|
23
|
+
return (
|
|
24
|
+
<div className={cx("relative", className)}>
|
|
25
|
+
{items.map((item, i) => {
|
|
26
|
+
const isLast = i === items.length - 1
|
|
27
|
+
return (
|
|
28
|
+
<div key={i} className="flex gap-4">
|
|
29
|
+
{/* Left column: dot + connector */}
|
|
30
|
+
<div className="flex flex-col items-center">
|
|
31
|
+
<TimelineDot dot={item.dot} icon={item.icon} />
|
|
32
|
+
{!isLast && <div className="w-px flex-1 bg-border my-1" />}
|
|
33
|
+
</div>
|
|
34
|
+
{/* Content */}
|
|
35
|
+
<div className={cx("pb-7 min-w-0 flex-1", isLast && "pb-0")}>
|
|
36
|
+
<div className="flex items-baseline justify-between gap-3 mb-0.5">
|
|
37
|
+
<p className="text-sm font-medium text-foreground leading-snug">{item.title}</p>
|
|
38
|
+
{item.date && <span className="shrink-0 text-xs text-muted-foreground">{item.date}</span>}
|
|
39
|
+
</div>
|
|
40
|
+
{item.description && (
|
|
41
|
+
<p className="text-sm text-muted-foreground leading-relaxed">{item.description}</p>
|
|
42
|
+
)}
|
|
43
|
+
{item.children && <div className="mt-2">{item.children}</div>}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
)
|
|
47
|
+
})}
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Dot ───────────────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
function TimelineDot({ dot = "default", icon }: { dot?: TimelineItem["dot"]; icon?: React.ReactNode }) {
|
|
55
|
+
if (icon) {
|
|
56
|
+
return (
|
|
57
|
+
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full border border-border bg-surface-2 text-muted-foreground">
|
|
58
|
+
{icon}
|
|
59
|
+
</div>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
return (
|
|
63
|
+
<div className={cx(
|
|
64
|
+
"mt-1.5 h-3 w-3 shrink-0 rounded-full transition-colors",
|
|
65
|
+
dot === "default" && "bg-border-strong",
|
|
66
|
+
dot === "filled" && "bg-foreground",
|
|
67
|
+
dot === "outline" && "border-2 border-foreground bg-transparent",
|
|
68
|
+
dot === "ring" && "border-2 border-foreground bg-card ring-4 ring-surface-2",
|
|
69
|
+
)} />
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Sub-exports for composing custom content ──────────────────────────────────
|
|
74
|
+
|
|
75
|
+
export function TimelineCard({ children, className }: { children: React.ReactNode; className?: string }) {
|
|
76
|
+
return (
|
|
77
|
+
<div className={cx("rounded-xl border border-border bg-surface-2 px-4 py-3 text-sm", className)}>
|
|
78
|
+
{children}
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
|
|
5
|
+
import { type VariantProps } from "class-variance-authority"
|
|
6
|
+
import { cx } from "@/lib/cx"
|
|
7
|
+
import { toggleVariants } from "@/components/core/toggle"
|
|
8
|
+
|
|
9
|
+
const ToggleGroupContext = React.createContext<VariantProps<typeof toggleVariants>>({
|
|
10
|
+
variant: "outline",
|
|
11
|
+
size: "md",
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
type ToggleGroupProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
|
|
15
|
+
VariantProps<typeof toggleVariants>
|
|
16
|
+
|
|
17
|
+
function ToggleGroup({ className, variant, size, children, ...props }: ToggleGroupProps) {
|
|
18
|
+
return (
|
|
19
|
+
<ToggleGroupPrimitive.Root
|
|
20
|
+
className={cx("flex items-center", className)}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
<ToggleGroupContext.Provider value={{ variant, size }}>
|
|
24
|
+
{children}
|
|
25
|
+
</ToggleGroupContext.Provider>
|
|
26
|
+
</ToggleGroupPrimitive.Root>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
|
|
30
|
+
|
|
31
|
+
type ToggleGroupItemProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
|
|
32
|
+
VariantProps<typeof toggleVariants>
|
|
33
|
+
|
|
34
|
+
const ToggleGroupItem = React.forwardRef<
|
|
35
|
+
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
|
|
36
|
+
ToggleGroupItemProps
|
|
37
|
+
>(({ className, variant, size, ...props }, ref) => {
|
|
38
|
+
const context = React.useContext(ToggleGroupContext)
|
|
39
|
+
return (
|
|
40
|
+
<ToggleGroupPrimitive.Item
|
|
41
|
+
ref={ref}
|
|
42
|
+
className={cx(
|
|
43
|
+
toggleVariants({ variant: variant ?? context.variant, size: size ?? context.size }),
|
|
44
|
+
// connected group: collapse borders between siblings
|
|
45
|
+
"rounded-none first:rounded-l-lg last:rounded-r-lg",
|
|
46
|
+
"border-r-0 last:border-r",
|
|
47
|
+
"[&[data-size=sm]]:first:rounded-l-md [&[data-size=sm]]:last:rounded-r-md",
|
|
48
|
+
className,
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName
|
|
55
|
+
|
|
56
|
+
export { ToggleGroup, ToggleGroupItem }
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as TogglePrimitive from "@radix-ui/react-toggle"
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
|
+
import { cx } from "@/lib/cx"
|
|
7
|
+
|
|
8
|
+
const toggleVariants = cva(
|
|
9
|
+
[
|
|
10
|
+
"inline-flex items-center justify-center gap-2 rounded-lg text-sm font-medium",
|
|
11
|
+
"transition-all duration-[150ms] ease-out outline-none",
|
|
12
|
+
"focus-visible:ring-1 focus-visible:ring-border-strong focus-visible:ring-offset-1 focus-visible:ring-offset-background",
|
|
13
|
+
"disabled:pointer-events-none disabled:opacity-40",
|
|
14
|
+
"[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
15
|
+
],
|
|
16
|
+
{
|
|
17
|
+
variants: {
|
|
18
|
+
variant: {
|
|
19
|
+
outline: [
|
|
20
|
+
"border border-border bg-transparent text-muted-foreground",
|
|
21
|
+
"hover:bg-surface-2 hover:text-foreground hover:border-border-strong",
|
|
22
|
+
"data-[state=on]:bg-surface-2 data-[state=on]:text-foreground data-[state=on]:border-border-strong",
|
|
23
|
+
],
|
|
24
|
+
ghost: [
|
|
25
|
+
"bg-transparent text-muted-foreground",
|
|
26
|
+
"hover:bg-surface-2 hover:text-foreground",
|
|
27
|
+
"data-[state=on]:bg-surface-2 data-[state=on]:text-foreground",
|
|
28
|
+
],
|
|
29
|
+
solid: [
|
|
30
|
+
"border border-border bg-transparent text-muted-foreground",
|
|
31
|
+
"hover:bg-surface-2 hover:text-foreground",
|
|
32
|
+
"data-[state=on]:bg-foreground data-[state=on]:text-background data-[state=on]:border-foreground",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
size: {
|
|
36
|
+
sm: "h-8 px-3 text-xs rounded-md",
|
|
37
|
+
md: "h-9 px-4",
|
|
38
|
+
lg: "h-10 px-5 text-base",
|
|
39
|
+
icon: "h-9 w-9",
|
|
40
|
+
"icon-sm": "h-8 w-8 rounded-md",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
defaultVariants: {
|
|
44
|
+
variant: "outline",
|
|
45
|
+
size: "md",
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
export interface ToggleProps
|
|
51
|
+
extends React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root>,
|
|
52
|
+
VariantProps<typeof toggleVariants> {}
|
|
53
|
+
|
|
54
|
+
const Toggle = React.forwardRef<
|
|
55
|
+
React.ElementRef<typeof TogglePrimitive.Root>,
|
|
56
|
+
ToggleProps
|
|
57
|
+
>(({ className, variant, size, ...props }, ref) => (
|
|
58
|
+
<TogglePrimitive.Root
|
|
59
|
+
ref={ref}
|
|
60
|
+
className={cx(toggleVariants({ variant, size }), className)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
))
|
|
64
|
+
Toggle.displayName = TogglePrimitive.Root.displayName
|
|
65
|
+
|
|
66
|
+
export { Toggle, toggleVariants }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as RadixTooltip from "@radix-ui/react-tooltip"
|
|
5
|
+
import { cx } from "@/lib/cx"
|
|
6
|
+
|
|
7
|
+
export const TooltipProvider = RadixTooltip.Provider
|
|
8
|
+
export const Tooltip = RadixTooltip.Root
|
|
9
|
+
export const TooltipTrigger = RadixTooltip.Trigger
|
|
10
|
+
|
|
11
|
+
export const TooltipContent = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof RadixTooltip.Content>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof RadixTooltip.Content>
|
|
14
|
+
>(({ className, sideOffset = 6, ...props }, ref) => (
|
|
15
|
+
<RadixTooltip.Portal>
|
|
16
|
+
<RadixTooltip.Content
|
|
17
|
+
ref={ref}
|
|
18
|
+
sideOffset={sideOffset}
|
|
19
|
+
className={cx(
|
|
20
|
+
"z-50 max-w-xs overflow-hidden rounded-xl border border-border bg-card px-3 py-1.5 text-xs text-foreground shadow-md",
|
|
21
|
+
"animate-in fade-in-0 zoom-in-95",
|
|
22
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
23
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
24
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
</RadixTooltip.Portal>
|
|
30
|
+
))
|
|
31
|
+
TooltipContent.displayName = "TooltipContent"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cx } from "@/lib/cx"
|
|
4
|
+
|
|
5
|
+
const wrapperStyles = cva(
|
|
6
|
+
"inline-flex items-center gap-1",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
bubble: "rounded-xl bg-surface-2 border border-border px-3 py-2",
|
|
11
|
+
bare: "",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
defaultVariants: { variant: "bubble" },
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const dotClass = "rounded-full bg-muted-foreground animate-bounce"
|
|
19
|
+
|
|
20
|
+
const dotSizes = { sm: "h-1 w-1", md: "h-1.5 w-1.5", lg: "h-2 w-2" }
|
|
21
|
+
|
|
22
|
+
export interface TypingIndicatorProps
|
|
23
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
24
|
+
VariantProps<typeof wrapperStyles> {
|
|
25
|
+
size?: "sm" | "md" | "lg"
|
|
26
|
+
label?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function TypingIndicator({ variant, size = "md", label = "Typing…", className, ...props }: TypingIndicatorProps) {
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
role="status"
|
|
33
|
+
aria-label={label}
|
|
34
|
+
className={cx(wrapperStyles({ variant }), className)}
|
|
35
|
+
{...props}
|
|
36
|
+
>
|
|
37
|
+
<span
|
|
38
|
+
className={cx(dotClass, dotSizes[size])}
|
|
39
|
+
style={{ animationDelay: "0ms", animationDuration: "900ms" }}
|
|
40
|
+
/>
|
|
41
|
+
<span
|
|
42
|
+
className={cx(dotClass, dotSizes[size])}
|
|
43
|
+
style={{ animationDelay: "160ms", animationDuration: "900ms" }}
|
|
44
|
+
/>
|
|
45
|
+
<span
|
|
46
|
+
className={cx(dotClass, dotSizes[size])}
|
|
47
|
+
style={{ animationDelay: "320ms", animationDuration: "900ms" }}
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
)
|
|
51
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Data and type exports — compiled by tsc into dist/
|
|
2
|
+
export * from "./types"
|
|
3
|
+
export * from "./manifests"
|
|
4
|
+
|
|
5
|
+
// UI component exports — consumed directly by Next.js via transpilePackages.
|
|
6
|
+
// These are NOT compiled by tsc (they import from web app internals).
|
|
7
|
+
// Import paths for UI: "@stampui/blocks/src/components/registry-card"
|
|
8
|
+
// or via the web app's tsconfig path alias "@stampui/blocks" -> src/index.ts
|