@srcroot/ui 0.0.61 → 0.0.63

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.
@@ -1,82 +1,88 @@
1
- import * as React from "react"
2
- import { cn } from "@/lib/utils"
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { cn } from "@/lib/utils";
3
5
 
4
6
  const InputGroup = React.forwardRef<
5
- HTMLDivElement,
6
- React.HTMLAttributes<HTMLDivElement> & { error?: boolean }
7
+ HTMLDivElement,
8
+ React.HTMLAttributes<HTMLDivElement> & { error?: boolean }
7
9
  >(({ className, children, error, ...props }, ref) => {
8
- return (
9
- <div
10
- ref={ref}
11
- className={cn(
12
- "flex w-full items-center rounded-md",
13
- "focus-within:ring-1 focus-within:ring-ring",
14
- error && "focus-within:ring-destructive focus-within:border-destructive",
15
- // First child: rounded-l only, remove right border if not last
16
- "[&>*:first-child]:rounded-r-none",
17
- // Last child: rounded-r only, remove left border if not first
18
- "[&>*:last-child]:rounded-l-none",
19
- // Middle children: no rounding
20
- "[&>*:not(:first-child):not(:last-child)]:rounded-none",
21
- // Negative margin to merge borders
22
- "[&>*:not(:first-child)]:-ml-px",
23
- // Bring hovered element to front
24
- "[&>*:hover]:z-10",
25
- className
26
- )}
27
- {...props}
28
- >
29
- {React.Children.map(children, (child) => {
30
- if (!React.isValidElement(child)) return child
31
- const element = child as React.ReactElement<{ className?: string, error?: boolean }>
10
+ return (
11
+ <div
12
+ ref={ref}
13
+ className={cn(
14
+ "flex w-full items-center rounded-md",
15
+ "focus-within:ring-1 focus-within:ring-ring",
16
+ error &&
17
+ "focus-within:ring-destructive focus-within:border-destructive",
18
+ // First child: rounded-l only, remove right border if not last
19
+ "[&>*:first-child]:rounded-r-none",
20
+ // Last child: rounded-r only, remove left border if not first
21
+ "[&>*:last-child]:rounded-l-none",
22
+ // Middle children: no rounding
23
+ "[&>*:not(:first-child):not(:last-child)]:rounded-none",
24
+ // Negative margin to merge borders
25
+ "[&>*:not(:first-child)]:-ml-px",
26
+ // Bring hovered element to front
27
+ "[&>*:hover]:z-10",
28
+ className,
29
+ )}
30
+ {...props}
31
+ >
32
+ {React.Children.map(children, (child) => {
33
+ if (!React.isValidElement(child)) return child;
34
+ const element = child as React.ReactElement<{
35
+ className?: string;
36
+ error?: boolean;
37
+ }>;
32
38
 
33
- // Only pass error to custom components, not DOM elements
34
- const additionalProps: { className: string; error?: boolean } = {
35
- className: cn(
36
- element.props.className,
37
- "focus-visible:ring-0 focus-visible:ring-offset-0",
38
- error && "border-destructive focus-visible:ring-destructive"
39
- )
40
- }
39
+ // Only pass error to custom components, not DOM elements
40
+ const additionalProps: { className: string; error?: boolean } = {
41
+ className: cn(
42
+ element.props.className,
43
+ "focus-visible:ring-0 focus-visible:ring-offset-0",
44
+ error && "border-destructive focus-visible:ring-destructive",
45
+ ),
46
+ };
41
47
 
42
- if (typeof element.type !== "string") {
43
- additionalProps.error = error
44
- }
48
+ if (typeof element.type !== "string") {
49
+ additionalProps.error = error;
50
+ }
45
51
 
46
- return React.cloneElement(element, additionalProps)
47
- })}
48
- </div>
49
- )
50
- })
51
- InputGroup.displayName = "InputGroup"
52
+ return React.cloneElement(element, additionalProps);
53
+ })}
54
+ </div>
55
+ );
56
+ });
57
+ InputGroup.displayName = "InputGroup";
52
58
 
53
59
  const InputAddon = React.forwardRef<
54
- HTMLDivElement,
55
- React.HTMLAttributes<HTMLDivElement> & { error?: boolean }
60
+ HTMLDivElement,
61
+ React.HTMLAttributes<HTMLDivElement> & { error?: boolean }
56
62
  >(({ className, children, error, ...props }, ref) => {
57
- return (
58
- <div
59
- ref={ref}
60
- className={cn(
61
- "flex h-9 items-center justify-center rounded-md border border-input bg-muted px-3 text-sm text-muted-foreground shadow-sm",
62
- error && "border-destructive",
63
- className
64
- )}
65
- {...props}
66
- >
67
- {React.Children.map(children, (child: React.ReactNode) => {
68
- if (!React.isValidElement(child)) return child
69
- const element = child as React.ReactElement<{ className?: string }>
70
- return React.cloneElement(element, {
71
- className: cn(
72
- element.props.className,
73
- "focus-visible:ring-0 focus-visible:ring-offset-0"
74
- ),
75
- })
76
- })}
77
- </div>
78
- )
79
- })
80
- InputAddon.displayName = "InputAddon"
63
+ return (
64
+ <div
65
+ ref={ref}
66
+ className={cn(
67
+ "flex h-9 items-center justify-center rounded-md border border-input bg-muted px-3 text-sm text-muted-foreground shadow-sm",
68
+ error && "border-destructive",
69
+ className,
70
+ )}
71
+ {...props}
72
+ >
73
+ {React.Children.map(children, (child: React.ReactNode) => {
74
+ if (!React.isValidElement(child)) return child;
75
+ const element = child as React.ReactElement<{ className?: string }>;
76
+ return React.cloneElement(element, {
77
+ className: cn(
78
+ element.props.className,
79
+ "focus-visible:ring-0 focus-visible:ring-offset-0",
80
+ ),
81
+ });
82
+ })}
83
+ </div>
84
+ );
85
+ });
86
+ InputAddon.displayName = "InputAddon";
81
87
 
82
- export { InputGroup, InputAddon }
88
+ export { InputGroup, InputAddon };
@@ -1,60 +1,62 @@
1
- import * as React from "react"
2
- import { cn } from "@/lib/utils"
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
- /** Array of keys to display (e.g., ["Ctrl", "K"]) */
6
- keys?: string[]
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
- ({ className, children, keys, ...props }, ref) => {
18
- // If keys array is provided, render each key
19
- if (keys && keys.length > 0) {
20
- return (
21
- <span className="inline-flex items-center gap-1">
22
- {keys.map((key, index) => (
23
- <React.Fragment key={index}>
24
- <kbd
25
- ref={index === 0 ? ref : undefined}
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
- "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",
49
- className
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
- {children}
54
- </kbd>
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
- export { Kbd }
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
- import * as React from "react"
2
- import { cva, type VariantProps } from "class-variance-authority"
3
- import { cn } from "@/lib/utils"
1
+ "use client";
4
2
 
5
- const spinnerVariants = cva(
6
- "animate-spin",
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
- type SpinnerVariants = VariantProps<typeof spinnerVariants>
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
- className?: string
33
- /** Accessible label for screen readers */
34
- label?: string
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
- ({ className, size, variant, label = "Loading" }, ref) => (
47
- <svg
48
- ref={ref}
49
- className={cn(spinnerVariants({ size, variant, className }))}
50
- xmlns="http://www.w3.org/2000/svg"
51
- fill="none"
52
- viewBox="0 0 24 24"
53
- role="status"
54
- aria-label={label}
55
- >
56
- <circle
57
- className="opacity-25"
58
- cx="12"
59
- cy="12"
60
- r="10"
61
- stroke="currentColor"
62
- strokeWidth="4"
63
- />
64
- <path
65
- className="opacity-75"
66
- fill="currentColor"
67
- 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"
68
- />
69
- </svg>
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
- className?: string
76
- children?: React.ReactNode
77
- /** Whether to show the loading state */
78
- loading?: boolean
79
- /** Text to display below spinner */
80
- text?: string
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
- ({ className, children, loading = true, text, size = "lg", variant }, ref) => {
93
- if (!loading) return <>{children}</>
91
+ (
92
+ { className, children, loading = true, text, size = "lg", variant },
93
+ ref,
94
+ ) => {
95
+ if (!loading) return <>{children}</>;
94
96
 
95
- return (
96
- <div ref={ref} className={cn("relative", className)}>
97
- {children && <div className="opacity-50 pointer-events-none">{children}</div>}
98
- <div className="absolute inset-0 flex flex-col items-center justify-center bg-background/80">
99
- <LoadingSpinner size={size} variant={variant} />
100
- {text && <p className="mt-2 text-sm text-muted-foreground">{text}</p>}
101
- </div>
102
- </div>
103
- )
104
- }
105
- )
106
- LoadingOverlay.displayName = "LoadingOverlay"
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
- className?: string;
9
- reverse?: boolean;
10
- pauseOnHover?: boolean;
11
- children?: React.ReactNode;
12
- vertical?: boolean;
13
- repeat?: number;
14
- [key: string]: any;
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
- className,
19
- reverse,
20
- pauseOnHover = false,
21
- children,
22
- vertical = false,
23
- repeat = 4,
24
- ...props
16
+ className,
17
+ reverse,
18
+ pauseOnHover = false,
19
+ children,
20
+ vertical = false,
21
+ repeat = 4,
22
+ ...props
25
23
  }: MarqueeProps) {
26
- return (
27
- <div
28
- {...props}
29
- className={cn(
30
- "group flex overflow-hidden p-2 [--duration:40s] [--gap:1rem] [gap:var(--gap)]",
31
- {
32
- "flex-row": !vertical,
33
- "flex-col": vertical,
34
- },
35
- className,
36
- )}
37
- >
38
- {Array(repeat)
39
- .fill(0)
40
- .map((_, i) => (
41
- <div
42
- key={i}
43
- className={cn("flex shrink-0 justify-around [gap:var(--gap)]", {
44
- "animate-marquee flex-row": !vertical,
45
- "animate-marquee-vertical flex-col": vertical,
46
- "group-hover:[animation-play-state:paused]": pauseOnHover,
47
- "[animation-direction:reverse]": reverse,
48
- })}
49
- >
50
- {children}
51
- </div>
52
- ))}
53
- </div>
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
- import * as React from "react"
2
- import { cn } from "@/lib/utils"
1
+ "use client";
3
2
 
4
- export interface NativeSelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> { }
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
- ({ className, children, ...props }, ref) => {
14
- return (
15
- <div className="relative">
16
- <select
17
- ref={ref}
18
- className={cn(
19
- "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",
20
- "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
21
- "disabled:cursor-not-allowed disabled:opacity-50",
22
- className
23
- )}
24
- {...props}
25
- >
26
- {children}
27
- </select>
28
- {/* Custom chevron icon */}
29
- <div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
30
- <svg
31
- className="h-4 w-4 opacity-50"
32
- xmlns="http://www.w3.org/2000/svg"
33
- viewBox="0 0 24 24"
34
- fill="none"
35
- stroke="currentColor"
36
- strokeWidth="2"
37
- strokeLinecap="round"
38
- strokeLinejoin="round"
39
- >
40
- <path d="m6 9 6 6 6-6" />
41
- </svg>
42
- </div>
43
- </div>
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 };