Skip to content

Commit 1cd1049

Browse files
authored
Merge branch '@invertase/shadcn-registry' into @invertase/shadcn-components
2 parents d6b61c4 + 9b32d5a commit 1cd1049

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+6800
-74
lines changed

examples/shadcn/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<title>shadcn</title>
8+
<link rel="stylesheet" href="src/index.css" />
89
</head>
910
<body>
1011
<div id="root"></div>

examples/shadcn/package.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,54 @@
99
"dependencies": {
1010
"@firebase-ui/core": "workspace:*",
1111
"@firebase-ui/react": "workspace:*",
12+
"@firebase-ui/styles": "workspace:*",
1213
"@hookform/resolvers": "^5.2.2",
14+
"@radix-ui/react-accordion": "^1.2.12",
15+
"@radix-ui/react-alert-dialog": "^1.1.15",
16+
"@radix-ui/react-aspect-ratio": "^1.1.7",
17+
"@radix-ui/react-avatar": "^1.1.10",
18+
"@radix-ui/react-checkbox": "^1.3.3",
19+
"@radix-ui/react-collapsible": "^1.1.12",
20+
"@radix-ui/react-context-menu": "^2.2.16",
21+
"@radix-ui/react-dialog": "^1.1.15",
22+
"@radix-ui/react-dropdown-menu": "^2.1.16",
23+
"@radix-ui/react-hover-card": "^1.1.15",
1324
"@radix-ui/react-label": "^2.1.7",
25+
"@radix-ui/react-menubar": "^1.1.16",
26+
"@radix-ui/react-navigation-menu": "^1.2.14",
27+
"@radix-ui/react-popover": "^1.1.15",
28+
"@radix-ui/react-progress": "^1.1.7",
29+
"@radix-ui/react-radio-group": "^1.3.8",
30+
"@radix-ui/react-scroll-area": "^1.2.10",
31+
"@radix-ui/react-select": "^2.2.6",
32+
"@radix-ui/react-separator": "^1.1.7",
33+
"@radix-ui/react-slider": "^1.3.6",
1434
"@radix-ui/react-slot": "^1.2.3",
35+
"@radix-ui/react-switch": "^1.2.6",
36+
"@radix-ui/react-tabs": "^1.1.13",
37+
"@radix-ui/react-toggle": "^1.1.10",
38+
"@radix-ui/react-toggle-group": "^1.1.11",
39+
"@radix-ui/react-tooltip": "^1.2.8",
1540
"class-variance-authority": "^0.7.1",
1641
"clsx": "^2.1.1",
42+
"cmdk": "^1.1.1",
43+
"date-fns": "^4.1.0",
44+
"embla-carousel-react": "^8.6.0",
45+
"firebase": "catalog:",
46+
"input-otp": "^1.4.2",
1747
"lucide-react": "^0.544.0",
48+
"next-themes": "^0.4.6",
1849
"react": "catalog:",
50+
"react-day-picker": "^9.11.1",
1951
"react-dom": "catalog:",
2052
"react-hook-form": "^7.64.0",
53+
"react-resizable-panels": "^3.0.6",
54+
"react-router": "^7.9.3",
55+
"react-router-dom": "^6.28.0",
56+
"recharts": "2.15.4",
57+
"sonner": "^2.0.7",
2158
"tailwind-merge": "^3.3.1",
59+
"vaul": "^1.1.2",
2260
"zod": "catalog:"
2361
},
2462
"devDependencies": {

examples/shadcn/src/App.tsx

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,91 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { NavLink } from "react-router";
18+
import { useUser } from "./firebase/hooks";
19+
120
function App() {
2-
return <>TODO</>;
21+
const user = useUser();
22+
23+
return (
24+
<div className="p-8 ">
25+
<h1 className="text-3xl font-bold mb-6">Firebase UI Demo</h1>
26+
<div className="mb-6">{user && <div>Welcome: {user.email || user.phoneNumber}</div>}</div>
27+
<div>
28+
<h2 className="text-2xl font-bold mb-4">Auth Screens</h2>
29+
<ul className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
30+
<li>
31+
<NavLink to="/screens/sign-in-auth-screen" className="text-blue-500 hover:underline">
32+
Sign In Auth Screen
33+
</NavLink>
34+
</li>
35+
<li>
36+
<NavLink to="/screens/sign-in-auth-screen-w-handlers" className="text-blue-500 hover:underline">
37+
Sign In Auth Screen with Handlers
38+
</NavLink>
39+
</li>
40+
<li>
41+
<NavLink to="/screens/sign-in-auth-screen-w-oauth" className="text-blue-500 hover:underline">
42+
Sign In Auth Screen with OAuth
43+
</NavLink>
44+
</li>
45+
<li>
46+
<NavLink to="/screens/email-link-auth-screen" className="text-blue-500 hover:underline">
47+
Email Link Auth Screen
48+
</NavLink>
49+
</li>
50+
<li>
51+
<NavLink to="/screens/email-link-auth-screen-w-oauth" className="text-blue-500 hover:underline">
52+
Email Link Auth Screen with OAuth
53+
</NavLink>
54+
</li>
55+
<li>
56+
<NavLink to="/screens/phone-auth-screen" className="text-blue-500 hover:underline">
57+
Phone Auth Screen
58+
</NavLink>
59+
</li>
60+
<li>
61+
<NavLink to="/screens/phone-auth-screen-w-oauth" className="text-blue-500 hover:underline">
62+
Phone Auth Screen with OAuth
63+
</NavLink>
64+
</li>
65+
<li>
66+
<NavLink to="/screens/sign-up-auth-screen" className="text-blue-500 hover:underline">
67+
Sign Up Auth Screen
68+
</NavLink>
69+
</li>
70+
<li>
71+
<NavLink to="/screens/sign-up-auth-screen-w-oauth" className="text-blue-500 hover:underline">
72+
Sign Up Auth Screen with OAuth
73+
</NavLink>
74+
</li>
75+
<li>
76+
<NavLink to="/screens/oauth-screen" className="text-blue-500 hover:underline">
77+
OAuth Screen
78+
</NavLink>
79+
</li>
80+
<li>
81+
<NavLink to="/screens/password-reset-screen" className="text-blue-500 hover:underline">
82+
Password Reset Screen
83+
</NavLink>
84+
</li>
85+
</ul>
86+
</div>
87+
</div>
88+
);
389
}
490

591
export default App;
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"use client";
2+
3+
import type { EmailLinkAuthFormSchema } from "@firebase-ui/core";
4+
import {
5+
useEmailLinkAuthFormAction,
6+
useEmailLinkAuthFormCompleteSignIn,
7+
useEmailLinkAuthFormSchema,
8+
useUI,
9+
type EmailLinkAuthFormProps,
10+
} from "@firebase-ui/react";
11+
12+
import { FirebaseUIError, getTranslation } from "@firebase-ui/core";
13+
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
14+
import { useState } from "react";
15+
import { useForm } from "react-hook-form";
16+
17+
import { Button } from "@/components/ui/button";
18+
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
19+
import { Input } from "@/components/ui/input";
20+
import { Policies } from "./policies";
21+
22+
export type { EmailLinkAuthFormProps };
23+
24+
export function EmailLinkAuthForm(props: EmailLinkAuthFormProps) {
25+
const { onEmailSent, onSignIn } = props;
26+
const ui = useUI();
27+
const schema = useEmailLinkAuthFormSchema();
28+
const action = useEmailLinkAuthFormAction();
29+
const [emailSent, setEmailSent] = useState(false);
30+
31+
const form = useForm<EmailLinkAuthFormSchema>({
32+
resolver: standardSchemaResolver(schema),
33+
defaultValues: {
34+
email: "",
35+
},
36+
});
37+
38+
useEmailLinkAuthFormCompleteSignIn(onSignIn);
39+
40+
async function onSubmit(values: EmailLinkAuthFormSchema) {
41+
try {
42+
await action(values);
43+
setEmailSent(true);
44+
onEmailSent?.();
45+
} catch (error) {
46+
const message = error instanceof FirebaseUIError ? error.message : String(error);
47+
form.setError("root", { message });
48+
}
49+
}
50+
51+
if (emailSent) {
52+
return (
53+
<div className="text-center space-y-4">
54+
<div className="text-green-600 dark:text-green-400">{getTranslation(ui, "messages", "signInLinkSent")}</div>
55+
</div>
56+
);
57+
}
58+
59+
return (
60+
<Form {...form}>
61+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2">
62+
<FormField
63+
control={form.control}
64+
name="email"
65+
render={({ field }) => (
66+
<FormItem>
67+
<FormLabel>{getTranslation(ui, "labels", "emailAddress")}</FormLabel>
68+
<FormControl>
69+
<Input {...field} type="email" />
70+
</FormControl>
71+
<FormMessage />
72+
</FormItem>
73+
)}
74+
/>
75+
<Policies />
76+
<Button type="submit" disabled={ui.state !== "idle"}>
77+
{getTranslation(ui, "labels", "sendSignInLink")}
78+
</Button>
79+
{form.formState.errors.root && <FormMessage>{form.formState.errors.root.message}</FormMessage>}
80+
</form>
81+
</Form>
82+
);
83+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"use client";
2+
3+
import { getTranslation } from "@firebase-ui/core";
4+
import { useUI, type EmailLinkAuthScreenProps } from "@firebase-ui/react";
5+
6+
import { EmailLinkAuthForm } from "@/components/email-link-auth-form";
7+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
8+
import { Separator } from "@/components/ui/separator";
9+
10+
export type { EmailLinkAuthScreenProps };
11+
12+
export function EmailLinkAuthScreen({ children, ...props }: EmailLinkAuthScreenProps) {
13+
const ui = useUI();
14+
15+
const titleText = getTranslation(ui, "labels", "signIn");
16+
const subtitleText = getTranslation(ui, "prompts", "signInToAccount");
17+
18+
return (
19+
<div className="max-w-md mx-auto">
20+
<Card>
21+
<CardHeader>
22+
<CardTitle>{titleText}</CardTitle>
23+
<CardDescription>{subtitleText}</CardDescription>
24+
</CardHeader>
25+
<CardContent>
26+
<EmailLinkAuthForm {...props} />
27+
{children ? (
28+
<>
29+
<Separator>{getTranslation(ui, "messages", "dividerOr")}</Separator>
30+
<div className="space-y-2">{children}</div>
31+
</>
32+
) : null}
33+
</CardContent>
34+
</Card>
35+
</div>
36+
);
37+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"use client";
2+
3+
import type { ForgotPasswordAuthFormSchema } from "@firebase-ui/core";
4+
import {
5+
useForgotPasswordAuthFormAction,
6+
useForgotPasswordAuthFormSchema,
7+
useUI,
8+
type ForgotPasswordAuthFormProps,
9+
} from "@firebase-ui/react";
10+
import { useForm } from "react-hook-form";
11+
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
12+
import { FirebaseUIError, getTranslation } from "@firebase-ui/core";
13+
import { useState } from "react";
14+
15+
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
16+
import { Input } from "@/components/ui/input";
17+
import { Button } from "@/components/ui/button";
18+
import { Policies } from "./policies";
19+
20+
export type { ForgotPasswordAuthFormProps };
21+
22+
export function ForgotPasswordAuthForm(props: ForgotPasswordAuthFormProps) {
23+
const ui = useUI();
24+
const schema = useForgotPasswordAuthFormSchema();
25+
const action = useForgotPasswordAuthFormAction();
26+
const [emailSent, setEmailSent] = useState(false);
27+
28+
const form = useForm<ForgotPasswordAuthFormSchema>({
29+
resolver: standardSchemaResolver(schema),
30+
defaultValues: {
31+
email: "",
32+
},
33+
});
34+
35+
async function onSubmit(values: ForgotPasswordAuthFormSchema) {
36+
try {
37+
await action(values);
38+
setEmailSent(true);
39+
props.onPasswordSent?.();
40+
} catch (error) {
41+
const message = error instanceof FirebaseUIError ? error.message : String(error);
42+
form.setError("root", { message });
43+
}
44+
}
45+
46+
if (emailSent) {
47+
return (
48+
<div className="text-center space-y-4">
49+
<div className="text-green-600 dark:text-green-400">{getTranslation(ui, "messages", "checkEmailForReset")}</div>
50+
</div>
51+
);
52+
}
53+
54+
return (
55+
<Form {...form}>
56+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2">
57+
<FormField
58+
control={form.control}
59+
name="email"
60+
render={({ field }) => (
61+
<FormItem>
62+
<FormLabel>{getTranslation(ui, "labels", "emailAddress")}</FormLabel>
63+
<FormControl>
64+
<Input {...field} type="email" />
65+
</FormControl>
66+
<FormMessage />
67+
</FormItem>
68+
)}
69+
/>
70+
<Policies />
71+
<Button type="submit" disabled={ui.state !== "idle"}>
72+
{getTranslation(ui, "labels", "resetPassword")}
73+
</Button>
74+
{form.formState.errors.root && <FormMessage>{form.formState.errors.root.message}</FormMessage>}
75+
{props.onBackToSignInClick ? (
76+
<Button type="button" variant="secondary" onClick={props.onBackToSignInClick}>
77+
{getTranslation(ui, "labels", "backToSignIn")}
78+
</Button>
79+
) : null}
80+
</form>
81+
</Form>
82+
);
83+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"use client";
2+
3+
import { getTranslation } from "@firebase-ui/core";
4+
import { useUI, type ForgotPasswordAuthScreenProps } from "@firebase-ui/react";
5+
6+
import { ForgotPasswordAuthForm } from "@/components/forgot-password-auth-form";
7+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
8+
9+
export type { ForgotPasswordAuthScreenProps };
10+
11+
export function ForgotPasswordAuthScreen(props: ForgotPasswordAuthScreenProps) {
12+
const ui = useUI();
13+
14+
const titleText = getTranslation(ui, "labels", "resetPassword");
15+
const subtitleText = getTranslation(ui, "prompts", "enterEmailToReset");
16+
17+
return (
18+
<div className="max-w-md mx-auto">
19+
<Card>
20+
<CardHeader>
21+
<CardTitle>{titleText}</CardTitle>
22+
<CardDescription>{subtitleText}</CardDescription>
23+
</CardHeader>
24+
<CardContent>
25+
<ForgotPasswordAuthForm {...props} />
26+
</CardContent>
27+
</Card>
28+
</div>
29+
);
30+
}

0 commit comments

Comments
 (0)