Skip to content

Commit 9ed4d33

Browse files
committed
feat: Add SignInScreen component
1 parent 2409893 commit 9ed4d33

File tree

11 files changed

+232
-178
lines changed

11 files changed

+232
-178
lines changed

packages/firebaseui-core/src/config.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
import { computed, map, MapStore } from 'nanostores';
22
import { FUIConfig } from './types';
33
import { fuiSignInAnonymously } from './auth';
4-
import { getAuth } from 'firebase/auth';
4+
import { getAuth, onAuthStateChanged } from 'firebase/auth';
55

66
export const $config = map<Record<string, MapStore<FUIConfig>>>({});
77

88
export function initializeUI(config: FUIConfig, name: string = '[DEFAULT]'): MapStore<FUIConfig> {
99
$config.setKey(name, map(config));
1010
if (config.enableAutoAnonymousLogin && config.app) {
11-
void fuiSignInAnonymously(getAuth(config.app));
11+
const unsubscribe = onAuthStateChanged(getAuth(config.app), (user) => {
12+
if (!user) {
13+
void fuiSignInAnonymously(getAuth(config.app));
14+
}
15+
unsubscribe();
16+
});
1217
}
1318
return $config.get()[name]!;
1419
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Card } from "../components/card";
2+
import { CardHeader } from "../components/card-header";
3+
import { CustomSignInScreen } from "./custom-sign-in-screen";
4+
import { EmailPasswordForm } from "./email-password-form";
5+
6+
interface SignInScreenProps {
7+
onForgotPasswordClick: () => void;
8+
onRegisterClick: () => void;
9+
}
10+
11+
export function SignInScreen({
12+
onForgotPasswordClick,
13+
onRegisterClick,
14+
}: SignInScreenProps) {
15+
return (
16+
<CustomSignInScreen>
17+
<Card>
18+
<CardHeader />
19+
<EmailPasswordForm
20+
onForgotPasswordClick={onForgotPasswordClick}
21+
onRegisterClick={onRegisterClick}
22+
/>
23+
</Card>
24+
</CustomSignInScreen>
25+
);
26+
}
Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,17 @@
1-
import { useConfig, useTranslations } from "~/hooks";
2-
import { getTranslation } from "@firebase-ui/core";
3-
import type { TranslationStrings } from "@firebase-ui/core";
4-
5-
type LabelKeys = keyof Required<TranslationStrings>["labels"];
6-
type PromptKeys = keyof Required<TranslationStrings>["prompts"];
1+
import { CardTitle } from "./card-title";
2+
import { CardSubtitle } from "./card-subtitle";
73

84
interface CardHeaderProps {
9-
titleKey: LabelKeys;
10-
subtitleKey: PromptKeys;
5+
title?: string;
6+
subtitle?: string;
7+
className?: string;
118
}
129

13-
export function CardHeader({ titleKey, subtitleKey }: CardHeaderProps) {
14-
const { language } = useConfig();
15-
const translations = useTranslations();
16-
10+
export function CardHeader({ title, subtitle, className }: CardHeaderProps) {
1711
return (
18-
<div className="fui-card__header">
19-
<h2 className="fui-card__title">
20-
{getTranslation("labels", titleKey, translations, language)}
21-
</h2>
22-
<p className="fui-card__subtitle">
23-
{getTranslation("prompts", subtitleKey, translations, language)}
24-
</p>
12+
<div className={`fui-card__header ${className || ""}`}>
13+
<CardTitle text={title} />
14+
<CardSubtitle text={subtitle} />
2515
</div>
2616
);
2717
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useConfig, useTranslations } from "~/hooks";
2+
import { getTranslation } from "@firebase-ui/core";
3+
4+
interface CardSubtitleProps {
5+
text?: string;
6+
className?: string;
7+
}
8+
9+
export function CardSubtitle({ text, className }: CardSubtitleProps) {
10+
const { language } = useConfig();
11+
const translations = useTranslations();
12+
13+
const subtitleText =
14+
text ||
15+
getTranslation("prompts", "signInToAccount", translations, language);
16+
17+
return (
18+
<p className={`fui-card__subtitle ${className || ""}`}>{subtitleText}</p>
19+
);
20+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useConfig, useTranslations } from "~/hooks";
2+
import { getTranslation } from "@firebase-ui/core";
3+
4+
interface CardTitleProps {
5+
text?: string;
6+
className?: string;
7+
}
8+
9+
export function CardTitle({ text, className }: CardTitleProps) {
10+
const { language } = useConfig();
11+
const translations = useTranslations();
12+
13+
const titleText =
14+
text || getTranslation("labels", "signIn", translations, language);
15+
16+
return <h2 className={`fui-card__title ${className || ""}`}>{titleText}</h2>;
17+
}

packages/firebaseui-react/src/components/card.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
interface CardProps {
22
children: React.ReactNode;
3+
className?: string;
34
}
45

5-
export function Card({ children }: CardProps) {
6+
export function Card({ children, className }: CardProps) {
67
return (
7-
<div className="fui-card">
8+
<div className={`fui-card ${className || ""}`}>
89
<div className="fui-card__container">{children}</div>
910
</div>
1011
);

packages/firebaseui-react/src/components/divider.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
export function Divider({ text }: { text?: string }) {
1+
interface DividerProps {
2+
text?: string;
3+
className?: string;
4+
}
5+
6+
export function Divider({ text, className }: DividerProps) {
27
if (!text) {
38
return (
4-
<div className="fui-divider">
9+
<div className={`fui-divider ${className || ""}`}>
510
<div className="fui-divider__line" />
611
</div>
712
);
813
}
914

1015
return (
11-
<div className="fui-divider">
16+
<div className={`fui-divider ${className || ""}`}>
1217
<div className="fui-divider__line" />
1318
<div className="fui-divider__text">{text}</div>
1419
<div className="fui-divider__line" />

packages/firebaseui-react/src/components/field-info.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import type { FieldApi } from "@tanstack/react-form";
22

3-
export function FieldInfo<TData>({ field }: { field: FieldApi<TData, any> }) {
3+
interface FieldInfoProps<TData> {
4+
field: FieldApi<TData, any>;
5+
className?: string;
6+
}
7+
8+
export function FieldInfo<TData>({ field, className }: FieldInfoProps<TData>) {
49
return (
510
<>
611
{field.state.meta.isTouched && field.state.meta.errors.length ? (
7-
<div className="fui-form__error">
12+
<div className={`fui-form__error ${className || ""}`}>
813
{field.state.meta.errors.join(", ")}
914
</div>
1015
) : null}

packages/firebaseui-react/src/examples/example_2.tsx

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { EmailPasswordForm } from "../auth/email-password-form";
22
import { RegisterForm } from "../auth/register-form";
33
import { useState } from "react";
4+
import { CustomSignInScreen } from "../auth/custom-sign-in-screen";
5+
import { Card } from "../components/card";
6+
import { CardHeader } from "../components/card-header";
47

58
export default function Example2() {
69
const [showRegister, setShowRegister] = useState(false);
@@ -43,43 +46,37 @@ export default function Example2() {
4346
style={{ animationDelay: "1s" }}
4447
></div>
4548

46-
<div className="relative rounded-xl border border-white/10 bg-black/40 p-8 shadow-2xl backdrop-blur-xl">
47-
{showRegister ? (
48-
<div className="space-y-6">
49-
<div>
50-
<h2 className="text-3xl font-bold text-white">
51-
Join DemoStyle
52-
</h2>
53-
<p className="mt-2 text-sm text-gray-400">
54-
Experience this demo styling
49+
<CustomSignInScreen className="!min-h-0 !bg-transparent !p-0">
50+
<Card className="relative rounded-xl border border-white/10 bg-black/40 shadow-2xl backdrop-blur-xl">
51+
<CardHeader
52+
title={showRegister ? "Join DemoStyle" : "Welcome Back"}
53+
subtitle={
54+
showRegister
55+
? "Experience this demo styling"
56+
: "Sign in to continue your journey"
57+
}
58+
/>
59+
{showRegister ? (
60+
<>
61+
<RegisterForm />
62+
<p className="text-center text-xs text-gray-500">
63+
By signing up, you agree to our Terms of Service
5564
</p>
56-
</div>
57-
<RegisterForm />
58-
<p className="text-center text-xs text-gray-500">
59-
By signing up, you agree to our Terms of Service
60-
</p>
61-
</div>
62-
) : (
63-
<div className="space-y-6">
64-
<div>
65-
<h2 className="text-3xl font-bold text-white">
66-
Welcome Back
67-
</h2>
68-
<p className="mt-2 text-sm text-gray-400">
69-
Sign in to continue your journey
70-
</p>
71-
</div>
72-
<EmailPasswordForm onForgotPasswordClick={() => {}} />
73-
<div className="flex items-center gap-4 pt-4">
74-
<div className="h-px flex-1 bg-white/10"></div>
75-
<span className="text-xs text-gray-500">
76-
Example Style Demo
77-
</span>
78-
<div className="h-px flex-1 bg-white/10"></div>
79-
</div>
80-
</div>
81-
)}
82-
</div>
65+
</>
66+
) : (
67+
<>
68+
<EmailPasswordForm onForgotPasswordClick={() => {}} />
69+
<div className="flex items-center gap-4 pt-4">
70+
<div className="h-px flex-1 bg-white/10"></div>
71+
<span className="text-xs text-gray-500">
72+
Example Style Demo
73+
</span>
74+
<div className="h-px flex-1 bg-white/10"></div>
75+
</div>
76+
</>
77+
)}
78+
</Card>
79+
</CustomSignInScreen>
8380
</div>
8481
</main>
8582

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { SignInScreen } from "../auth/sign-in-screen";
2+
3+
export default function Example4() {
4+
return (
5+
<SignInScreen onForgotPasswordClick={() => {}} onRegisterClick={() => {}} />
6+
);
7+
}

0 commit comments

Comments
 (0)