@srcroot/ui 0.0.61 → 0.0.62
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/index.js +1 -1
- package/package.json +1 -1
- package/src/registry/analytics/google-analytics.tsx +25 -23
- package/src/registry/analytics/google-tag-manager.tsx +54 -49
- package/src/registry/analytics/meta-pixel.tsx +29 -25
- package/src/registry/analytics/microsoft-clarity.tsx +19 -17
- package/src/registry/analytics/tiktok-pixel.tsx +15 -13
- package/src/registry/ui/alert.tsx +50 -48
- package/src/registry/ui/aspect-ratio.tsx +27 -25
- package/src/registry/ui/badge.tsx +40 -37
- package/src/registry/ui/breadcrumb.tsx +106 -106
- package/src/registry/ui/button-group.tsx +52 -46
- package/src/registry/ui/card.tsx +68 -58
- package/src/registry/ui/container.tsx +34 -31
- package/src/registry/ui/empty-state.tsx +32 -30
- package/src/registry/ui/form-field.tsx +79 -68
- package/src/registry/ui/image.tsx +128 -120
- package/src/registry/ui/input-group.tsx +78 -72
- package/src/registry/ui/kbd.tsx +46 -44
- package/src/registry/ui/loading-spinner.tsx +84 -80
- package/src/registry/ui/marquee.tsx +43 -45
- package/src/registry/ui/native-select.tsx +42 -40
- package/src/registry/ui/pagination.tsx +115 -101
- package/src/registry/ui/radio.tsx +96 -66
- package/src/registry/ui/skeleton.tsx +15 -14
- package/src/registry/ui/slot.tsx +56 -55
- package/src/registry/ui/text.tsx +42 -41
package/src/registry/ui/kbd.tsx
CHANGED
|
@@ -1,60 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
3
5
|
|
|
4
6
|
interface KbdProps extends React.HTMLAttributes<HTMLElement> {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
/** Array of keys to display (e.g., ["Ctrl", "K"]) */
|
|
8
|
+
keys?: string[];
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Kbd - Keyboard key display component
|
|
11
|
-
*
|
|
13
|
+
*
|
|
12
14
|
* Usage:
|
|
13
15
|
* <Kbd>⌘</Kbd>
|
|
14
16
|
* <Kbd keys={["Ctrl", "Shift", "P"]} />
|
|
15
17
|
*/
|
|
16
18
|
const Kbd = React.forwardRef<HTMLElement, KbdProps>(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
className={cn(
|
|
27
|
-
"pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground",
|
|
28
|
-
className
|
|
29
|
-
)}
|
|
30
|
-
{...props}
|
|
31
|
-
>
|
|
32
|
-
{key}
|
|
33
|
-
</kbd>
|
|
34
|
-
{index < keys.length - 1 && (
|
|
35
|
-
<span className="text-muted-foreground text-xs">+</span>
|
|
36
|
-
)}
|
|
37
|
-
</React.Fragment>
|
|
38
|
-
))}
|
|
39
|
-
</span>
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Single key rendering
|
|
44
|
-
return (
|
|
45
|
-
<kbd
|
|
46
|
-
ref={ref}
|
|
19
|
+
({ className, children, keys, ...props }, ref) => {
|
|
20
|
+
// If keys array is provided, render each key
|
|
21
|
+
if (keys && keys.length > 0) {
|
|
22
|
+
return (
|
|
23
|
+
<span className="inline-flex items-center gap-1">
|
|
24
|
+
{keys.map((key, index) => (
|
|
25
|
+
<React.Fragment key={index}>
|
|
26
|
+
<kbd
|
|
27
|
+
ref={index === 0 ? ref : undefined}
|
|
47
28
|
className={cn(
|
|
48
|
-
|
|
49
|
-
|
|
29
|
+
"pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground",
|
|
30
|
+
className,
|
|
50
31
|
)}
|
|
51
32
|
{...props}
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
|
|
55
|
-
|
|
33
|
+
>
|
|
34
|
+
{key}
|
|
35
|
+
</kbd>
|
|
36
|
+
{index < keys.length - 1 && (
|
|
37
|
+
<span className="text-muted-foreground text-xs">+</span>
|
|
38
|
+
)}
|
|
39
|
+
</React.Fragment>
|
|
40
|
+
))}
|
|
41
|
+
</span>
|
|
42
|
+
);
|
|
56
43
|
}
|
|
57
|
-
)
|
|
58
|
-
Kbd.displayName = "Kbd"
|
|
59
44
|
|
|
60
|
-
|
|
45
|
+
// Single key rendering
|
|
46
|
+
return (
|
|
47
|
+
<kbd
|
|
48
|
+
ref={ref}
|
|
49
|
+
className={cn(
|
|
50
|
+
"pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground",
|
|
51
|
+
className,
|
|
52
|
+
)}
|
|
53
|
+
{...props}
|
|
54
|
+
>
|
|
55
|
+
{children}
|
|
56
|
+
</kbd>
|
|
57
|
+
);
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
Kbd.displayName = "Kbd";
|
|
61
|
+
|
|
62
|
+
export { Kbd };
|
|
@@ -1,108 +1,112 @@
|
|
|
1
|
-
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
-
import { cn } from "@/lib/utils"
|
|
1
|
+
"use client";
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
variants: {
|
|
9
|
-
size: {
|
|
10
|
-
xs: "h-3 w-3",
|
|
11
|
-
sm: "h-4 w-4",
|
|
12
|
-
default: "h-6 w-6",
|
|
13
|
-
lg: "h-8 w-8",
|
|
14
|
-
xl: "h-12 w-12",
|
|
15
|
-
},
|
|
16
|
-
variant: {
|
|
17
|
-
default: "text-primary",
|
|
18
|
-
muted: "text-muted-foreground",
|
|
19
|
-
white: "text-white",
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
defaultVariants: {
|
|
23
|
-
size: "default",
|
|
24
|
-
variant: "default",
|
|
25
|
-
},
|
|
26
|
-
}
|
|
27
|
-
)
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
28
6
|
|
|
29
|
-
|
|
7
|
+
const spinnerVariants = cva("animate-spin", {
|
|
8
|
+
variants: {
|
|
9
|
+
size: {
|
|
10
|
+
xs: "h-3 w-3",
|
|
11
|
+
sm: "h-4 w-4",
|
|
12
|
+
default: "h-6 w-6",
|
|
13
|
+
lg: "h-8 w-8",
|
|
14
|
+
xl: "h-12 w-12",
|
|
15
|
+
},
|
|
16
|
+
variant: {
|
|
17
|
+
default: "text-primary",
|
|
18
|
+
muted: "text-muted-foreground",
|
|
19
|
+
white: "text-white",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
size: "default",
|
|
24
|
+
variant: "default",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
type SpinnerVariants = VariantProps<typeof spinnerVariants>;
|
|
30
29
|
|
|
31
30
|
interface LoadingSpinnerProps extends SpinnerVariants {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
className?: string;
|
|
32
|
+
/** Accessible label for screen readers */
|
|
33
|
+
label?: string;
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
/**
|
|
38
37
|
* Loading spinner with size and color variants
|
|
39
|
-
*
|
|
38
|
+
*
|
|
40
39
|
* @example
|
|
41
40
|
* <LoadingSpinner />
|
|
42
41
|
* <LoadingSpinner size="lg" variant="muted" />
|
|
43
42
|
* <LoadingSpinner size="sm" label="Submitting..." />
|
|
44
43
|
*/
|
|
45
44
|
const LoadingSpinner = React.forwardRef<SVGSVGElement, LoadingSpinnerProps>(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
72
|
-
LoadingSpinner.displayName = "LoadingSpinner"
|
|
45
|
+
({ className, size, variant, label = "Loading" }, ref) => (
|
|
46
|
+
<svg
|
|
47
|
+
ref={ref}
|
|
48
|
+
className={cn(spinnerVariants({ size, variant, className }))}
|
|
49
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
50
|
+
fill="none"
|
|
51
|
+
viewBox="0 0 24 24"
|
|
52
|
+
role="status"
|
|
53
|
+
aria-label={label}
|
|
54
|
+
>
|
|
55
|
+
<circle
|
|
56
|
+
className="opacity-25"
|
|
57
|
+
cx="12"
|
|
58
|
+
cy="12"
|
|
59
|
+
r="10"
|
|
60
|
+
stroke="currentColor"
|
|
61
|
+
strokeWidth="4"
|
|
62
|
+
/>
|
|
63
|
+
<path
|
|
64
|
+
className="opacity-75"
|
|
65
|
+
fill="currentColor"
|
|
66
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
67
|
+
/>
|
|
68
|
+
</svg>
|
|
69
|
+
),
|
|
70
|
+
);
|
|
71
|
+
LoadingSpinner.displayName = "LoadingSpinner";
|
|
73
72
|
|
|
74
73
|
interface LoadingOverlayProps extends SpinnerVariants {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
className?: string;
|
|
75
|
+
children?: React.ReactNode;
|
|
76
|
+
/** Whether to show the loading state */
|
|
77
|
+
loading?: boolean;
|
|
78
|
+
/** Text to display below spinner */
|
|
79
|
+
text?: string;
|
|
81
80
|
}
|
|
82
81
|
|
|
83
82
|
/**
|
|
84
83
|
* Full overlay loading state
|
|
85
|
-
*
|
|
84
|
+
*
|
|
86
85
|
* @example
|
|
87
86
|
* <LoadingOverlay loading={isLoading}>
|
|
88
87
|
* <YourContent />
|
|
89
88
|
* </LoadingOverlay>
|
|
90
89
|
*/
|
|
91
90
|
const LoadingOverlay = React.forwardRef<HTMLDivElement, LoadingOverlayProps>(
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
(
|
|
92
|
+
{ className, children, loading = true, text, size = "lg", variant },
|
|
93
|
+
ref,
|
|
94
|
+
) => {
|
|
95
|
+
if (!loading) return <>{children}</>;
|
|
94
96
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
)
|
|
106
|
-
|
|
97
|
+
return (
|
|
98
|
+
<div ref={ref} className={cn("relative", className)}>
|
|
99
|
+
{children && (
|
|
100
|
+
<div className="opacity-50 pointer-events-none">{children}</div>
|
|
101
|
+
)}
|
|
102
|
+
<div className="absolute inset-0 flex flex-col items-center justify-center bg-background/80">
|
|
103
|
+
<LoadingSpinner size={size} variant={variant} />
|
|
104
|
+
{text && <p className="mt-2 text-sm text-muted-foreground">{text}</p>}
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
LoadingOverlay.displayName = "LoadingOverlay";
|
|
107
111
|
|
|
108
|
-
export { LoadingSpinner, LoadingOverlay, spinnerVariants }
|
|
112
|
+
export { LoadingSpinner, LoadingOverlay, spinnerVariants };
|
|
@@ -1,55 +1,53 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
1
|
"use client";
|
|
4
2
|
|
|
5
3
|
import { cn } from "@/lib/utils";
|
|
6
4
|
|
|
7
5
|
interface MarqueeProps {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
className?: string;
|
|
7
|
+
reverse?: boolean;
|
|
8
|
+
pauseOnHover?: boolean;
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
vertical?: boolean;
|
|
11
|
+
repeat?: number;
|
|
12
|
+
[key: string]: any;
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
export default function Marquee({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
className,
|
|
17
|
+
reverse,
|
|
18
|
+
pauseOnHover = false,
|
|
19
|
+
children,
|
|
20
|
+
vertical = false,
|
|
21
|
+
repeat = 4,
|
|
22
|
+
...props
|
|
25
23
|
}: MarqueeProps) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
{...props}
|
|
27
|
+
className={cn(
|
|
28
|
+
"group flex overflow-hidden p-2 [--duration:40s] [--gap:1rem] [gap:var(--gap)]",
|
|
29
|
+
{
|
|
30
|
+
"flex-row": !vertical,
|
|
31
|
+
"flex-col": vertical,
|
|
32
|
+
},
|
|
33
|
+
className,
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
{Array(repeat)
|
|
37
|
+
.fill(0)
|
|
38
|
+
.map((_, i) => (
|
|
39
|
+
<div
|
|
40
|
+
key={i}
|
|
41
|
+
className={cn("flex shrink-0 justify-around [gap:var(--gap)]", {
|
|
42
|
+
"animate-marquee flex-row": !vertical,
|
|
43
|
+
"animate-marquee-vertical flex-col": vertical,
|
|
44
|
+
"group-hover:[animation-play-state:paused]": pauseOnHover,
|
|
45
|
+
"[animation-direction:reverse]": reverse,
|
|
46
|
+
})}
|
|
47
|
+
>
|
|
48
|
+
{children}
|
|
49
|
+
</div>
|
|
50
|
+
))}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
55
53
|
}
|
|
@@ -1,49 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
import { cn } from "@/lib/utils"
|
|
1
|
+
"use client";
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
export interface NativeSelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {}
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* NativeSelect - Styled browser-native select element
|
|
8
|
-
*
|
|
10
|
+
*
|
|
9
11
|
* Uses the browser's native <select> for accessibility and mobile UX,
|
|
10
12
|
* with custom styling to match the design system.
|
|
11
13
|
*/
|
|
12
14
|
const NativeSelect = React.forwardRef<HTMLSelectElement, NativeSelectProps>(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
)
|
|
47
|
-
NativeSelect.displayName = "NativeSelect"
|
|
15
|
+
({ className, children, ...props }, ref) => {
|
|
16
|
+
return (
|
|
17
|
+
<div className="relative">
|
|
18
|
+
<select
|
|
19
|
+
ref={ref}
|
|
20
|
+
className={cn(
|
|
21
|
+
"flex h-10 w-full appearance-none rounded-md border border-input bg-background px-3 py-2 pr-8 text-sm ring-offset-background",
|
|
22
|
+
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
23
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</select>
|
|
30
|
+
{/* Custom chevron icon */}
|
|
31
|
+
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
|
32
|
+
<svg
|
|
33
|
+
className="h-4 w-4 opacity-50"
|
|
34
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
35
|
+
viewBox="0 0 24 24"
|
|
36
|
+
fill="none"
|
|
37
|
+
stroke="currentColor"
|
|
38
|
+
strokeWidth="2"
|
|
39
|
+
strokeLinecap="round"
|
|
40
|
+
strokeLinejoin="round"
|
|
41
|
+
>
|
|
42
|
+
<path d="m6 9 6 6 6-6" />
|
|
43
|
+
</svg>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
NativeSelect.displayName = "NativeSelect";
|
|
48
50
|
|
|
49
|
-
export { NativeSelect }
|
|
51
|
+
export { NativeSelect };
|