Skip to content

Commit d864ca7

Browse files
committed
uds components fetch
1 parent 5b6ac38 commit d864ca7

File tree

9 files changed

+544
-3
lines changed

9 files changed

+544
-3
lines changed

package-lock.json

Lines changed: 48 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
},
1616
"dependencies": {
1717
"@auth0/auth0-acul-js": "^0.1.0-beta.5",
18+
"@radix-ui/react-label": "^2.1.7",
1819
"@radix-ui/react-slot": "^1.2.3",
1920
"class-variance-authority": "^0.7.1",
2021
"clsx": "^2.1.1",

src/components/ui/button.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"use client";
2+
3+
import { Slot } from "@radix-ui/react-slot";
4+
import { cva, type VariantProps } from "class-variance-authority";
5+
import * as React from "react";
6+
import { cn } from "@/lib/utils";
7+
8+
const buttonVariants = cva(
9+
"focus-visible:ring-ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive theme-default:active:scale-[0.99] relative box-border inline-flex shrink-0 items-center justify-center gap-2 overflow-hidden text-sm font-medium whitespace-nowrap transition-all duration-150 ease-in-out outline-none focus-visible:ring-4 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
10+
{
11+
variants: {
12+
variant: {
13+
primary:
14+
"shadow-button-resting hover:shadow-button-hover hover:border-primary/50 border-primary bg-primary text-primary-foreground hover:bg-primary/90 theme-default:before:from-primary-foreground/0 theme-default:before:to-primary-foreground/15 theme-default:before:absolute theme-default:before:top-0 theme-default:before:left-0 theme-default:before:block theme-default:before:h-full theme-default:before:w-full theme-default:before:bg-gradient-to-t theme-default:before:content-[''] border",
15+
outline:
16+
"dark:bg-muted/50 hover:text-accent-foreground shadow-button-outlined-resting hover:shadow-button-outlined-hover hover:border-accent bg-background hover:bg-muted text-primary border-primary/35 theme-default:before:from-primary/5 theme-default:before:to-primary/0 theme-default:before:absolute theme-default:before:top-0 theme-default:before:left-0 theme-default:before:block theme-default:before:h-full theme-default:before:w-full theme-default:before:bg-gradient-to-t theme-default:before:content-[''] border",
17+
ghost: "hover:bg-muted text-primary bg-transparent",
18+
destructive:
19+
"bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-button-destructive-resting hover:shadow-button-destructive-hover border-destructive-border/25 hover:border-destructive-border/50 theme-default:before:to-primary-foreground/50 theme-default:before:absolute theme-default:before:top-0 theme-default:before:left-0 theme-default:before:block theme-default:before:h-full theme-default:before:w-full theme-default:before:bg-gradient-to-t theme-default:before:content-[''] theme-default:border",
20+
link: "text-foreground underline-offset-4 hover:underline",
21+
},
22+
size: {
23+
default: "h-10 rounded-2xl px-4 py-2.5 has-[>svg]:px-3",
24+
xs: "h-7 rounded-md px-2 py-1.5 text-xs has-[>svg]:px-2",
25+
sm: "h-8 gap-1.5 rounded-xl px-3 py-2 text-xs has-[>svg]:px-2.5",
26+
lg: "h-12 rounded-3xl px-6 py-3 text-base has-[>svg]:px-4",
27+
icon: "size-7 rounded-xl",
28+
},
29+
},
30+
defaultVariants: {
31+
variant: "primary",
32+
size: "default",
33+
},
34+
},
35+
);
36+
37+
export interface ButtonProps
38+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
39+
VariantProps<typeof buttonVariants> {
40+
as?: boolean;
41+
}
42+
43+
function Button({
44+
className,
45+
variant,
46+
size,
47+
as,
48+
...props
49+
}: React.ComponentProps<"button"> & ButtonProps) {
50+
const Comp = as ? Slot : "button";
51+
52+
return (
53+
<Comp
54+
className={cn(buttonVariants({ variant, size }), className)}
55+
{...props}
56+
/>
57+
);
58+
}
59+
60+
export { Button, buttonVariants };

src/components/ui/card.tsx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import * as React from "react";
2+
import { cn } from "@/lib/utils";
3+
4+
function Card({ className, ...props }: React.ComponentProps<"div">) {
5+
return (
6+
<div
7+
data-slot="card"
8+
className={cn(
9+
"bg-card text-card-foreground shadow-bevel-2xl flex flex-col gap-6 rounded-4xl py-6",
10+
className,
11+
)}
12+
{...props}
13+
/>
14+
);
15+
}
16+
17+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
18+
return (
19+
<div
20+
data-slot="card-header"
21+
className={cn(
22+
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
23+
className,
24+
)}
25+
{...props}
26+
/>
27+
);
28+
}
29+
30+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
31+
return (
32+
<div
33+
data-slot="card-title"
34+
className={cn("leading-none font-semibold", className)}
35+
{...props}
36+
/>
37+
);
38+
}
39+
40+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
41+
return (
42+
<div
43+
data-slot="card-description"
44+
className={cn("text-muted-foreground text-sm", className)}
45+
{...props}
46+
/>
47+
);
48+
}
49+
50+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
51+
return (
52+
<div
53+
data-slot="card-action"
54+
className={cn(
55+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
56+
className,
57+
)}
58+
{...props}
59+
/>
60+
);
61+
}
62+
63+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
64+
return (
65+
<div
66+
data-slot="card-content"
67+
className={cn("px-6", className)}
68+
{...props}
69+
/>
70+
);
71+
}
72+
73+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
74+
return (
75+
<div
76+
data-slot="card-footer"
77+
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
78+
{...props}
79+
/>
80+
);
81+
}
82+
83+
export {
84+
Card,
85+
CardHeader,
86+
CardFooter,
87+
CardTitle,
88+
CardAction,
89+
CardDescription,
90+
CardContent,
91+
};

src/components/ui/form-field.tsx

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { cva, VariantProps } from "class-variance-authority";
2+
import * as React from "react";
3+
import { cn } from "@/lib/utils";
4+
5+
const formFieldVariants = cva(
6+
"bg-input aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive theme-default:active:scale-[0.99] relative box-border inline-flex w-full shrink-0 cursor-text items-center justify-center gap-2 overflow-hidden text-sm transition-[color,box-shadow] duration-150 ease-in-out outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
7+
{
8+
variants: {
9+
variant: {
10+
default:
11+
"text-input-foreground shadow-input-resting hover:shadow-input-hover focus-within:ring-ring/15 focus-within:ring-4",
12+
error:
13+
"text-destructive-foreground shadow-input-destructive-resting hover:shadow-input-destructive-hover focus-within:ring-destructive-border/15 focus-within:ring-4",
14+
},
15+
size: {
16+
default: "h-14 rounded-2xl",
17+
sm: "h-12 rounded-2xl",
18+
lg: "h-16 rounded-2xl",
19+
},
20+
},
21+
defaultVariants: {
22+
variant: "default",
23+
size: "default",
24+
},
25+
},
26+
);
27+
28+
export interface FormFieldProps
29+
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
30+
label: string;
31+
error?: boolean;
32+
helperText?: string;
33+
size?: VariantProps<typeof formFieldVariants>["size"];
34+
variant?: VariantProps<typeof formFieldVariants>["variant"];
35+
startAdornment?: React.ReactNode;
36+
endAdornment?: React.ReactNode;
37+
}
38+
39+
const FormField = React.forwardRef<HTMLInputElement, FormFieldProps>(
40+
(
41+
{
42+
className,
43+
variant,
44+
size,
45+
error,
46+
helperText,
47+
label,
48+
startAdornment,
49+
endAdornment,
50+
...props
51+
},
52+
ref,
53+
) => {
54+
const [focused, setFocused] = React.useState(false);
55+
const [hasValue, setHasValue] = React.useState(
56+
Boolean(props.value || props.defaultValue),
57+
);
58+
const isDisabled = props.disabled;
59+
60+
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
61+
setFocused(true);
62+
props.onFocus?.(e);
63+
};
64+
65+
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
66+
setFocused(false);
67+
setHasValue(Boolean(e.target.value));
68+
props.onBlur?.(e);
69+
};
70+
71+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
72+
setHasValue(Boolean(e.target.value));
73+
props.onChange?.(e);
74+
};
75+
76+
const isLabelFloating = focused || hasValue;
77+
78+
return (
79+
<div>
80+
<div
81+
className={cn(
82+
formFieldVariants({ variant: error ? "error" : variant, size }),
83+
"group relative items-end gap-0.5",
84+
isDisabled &&
85+
"bg-input-muted text-input-foreground cursor-not-allowed opacity-50",
86+
isDisabled && variant === "default" && "bg-input-muted",
87+
startAdornment && "pl-[5px]",
88+
endAdornment && "pr-[5px]",
89+
className,
90+
)}
91+
>
92+
<label
93+
htmlFor={props.id}
94+
className={cn(
95+
"transition- pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-sm duration-150 ease-in-out",
96+
startAdornment && "left-0",
97+
isLabelFloating && "top-4 text-xs",
98+
isLabelFloating && size === "sm" && "top-3.5 text-xs",
99+
error ? "text-destructive-foreground" : "text-muted-foreground",
100+
focused && !error && "text-primary",
101+
)}
102+
>
103+
{label}
104+
</label>
105+
{startAdornment && (
106+
<div className="[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
107+
{startAdornment}
108+
</div>
109+
)}
110+
<div className="relative flex-1">
111+
<input
112+
className={cn(
113+
"h-14 w-full flex-1 bg-transparent px-3 pt-6 pb-1 outline-none file:border-0 file:bg-transparent file:text-sm file:font-medium",
114+
isDisabled &&
115+
"bg-input-muted text-input-foreground cursor-not-allowed opacity-50",
116+
startAdornment ? "pl-0" : "pl-3",
117+
endAdornment ? "pr-0" : "pr-3",
118+
size === "sm" && "h-12 pb-2 text-sm",
119+
size === "lg" && "h-16 pt-4 pb-0 text-base",
120+
)}
121+
ref={ref}
122+
onFocus={handleFocus}
123+
onBlur={handleBlur}
124+
onChange={handleChange}
125+
{...props}
126+
/>
127+
</div>
128+
{endAdornment && (
129+
<div className="[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
130+
{endAdornment}
131+
</div>
132+
)}
133+
</div>
134+
{helperText && (
135+
<p
136+
className={cn(
137+
"mt-1.5 text-xs",
138+
error ? "text-destructive-foreground" : "text-muted-foreground",
139+
)}
140+
>
141+
{helperText}
142+
</p>
143+
)}
144+
</div>
145+
);
146+
},
147+
);
148+
149+
FormField.displayName = "FormField";
150+
151+
export { FormField, formFieldVariants };

0 commit comments

Comments
 (0)