feat(auth): redesign auth UI to match Dart original

- Full-bleed bg_login.png background outside 500px MobileShell
- Facebook landing: logo + Facebook pill button + "Other Sign In Options" link
- Replace centered Dialog with true bottom sheet (drag handle, top radius 28px)
- Email panel: internal login/register toggle, no separate route
- Pink gradient primary button (#f96ADE → #f657A0)
- Pill input fields (46px, white, radius 24, pink 10% shadow)
- Pink circular checkbox + bold Privacy/Terms rich text (visual only, non-blocking)
- Floating 40x40 white back button (top:20/left:20)
- Wire AuthPanelModeChanged dispatch for panel switching
- New design tokens (--color-auth-*, --auth-*, --radius-bottom-sheet)
- New generic BottomSheet component
- Copy ic-logo-login.png (+@2x, +@3x) from Dart assets

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-10 14:27:06 +08:00
parent 4e27b4c512
commit 90bb711a45
30 changed files with 967 additions and 335 deletions
+56 -12
View File
@@ -1,30 +1,74 @@
"use client";
/**
* 认证面板:顶层 switchFacebook / Email
* 认证面板:顶层 switchFacebook / Email+ 悬浮返回按钮
*
* 原始 Dart: lib/ui/auth/widgets/auth_panel.dart
*
* 行为对齐:
* - 状态机:`authPanelMode`"facebook" | "email")由 XState 管理
* - 切换派发 `AuthPanelModeChanged`
* - 悬浮返回按钮(40×40 白圆,top:20, left:20):
* - facebook 模式:`router.back()`
* - email 模式:派发 `AuthPanelModeChanged(facebook)`
* - Email 面板的弹层 Facebook 按钮同样派发 `AuthPanelModeChanged(facebook)`
*/
import { useAuthState } from "@/stores/auth/auth-context";
import { useRouter } from "next/navigation";
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
import { AuthFacebookPanel } from "./auth-facebook-panel";
import { AuthEmailPanel } from "./auth-email-panel";
import { AuthFacebookPanel } from "./auth-facebook-panel";
import styles from "./auth-panel.module.css";
export function AuthPanel() {
const state = useAuthState();
const dispatch = useAuthDispatch();
const router = useRouter();
const switchToEmail = () =>
dispatch({ type: "AuthPanelModeChanged", mode: "email" });
const switchToFacebook = () =>
dispatch({ type: "AuthPanelModeChanged", mode: "facebook" });
const handleBack = () => {
if (state.authPanelMode === "email") {
switchToFacebook();
} else {
router.back();
}
};
return (
<div className={styles.panel}>
<div className={styles.panelShell}>
<button
type="button"
className={styles.backButton}
onClick={handleBack}
aria-label="Back"
>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M15 18l-6-6 6-6" />
</svg>
</button>
{state.authPanelMode === "facebook" ? (
<AuthFacebookPanel
onSwitchToEmail={() => {
// 切换模式由外部 dispatch 触发;这里不直接处理
// 因为 auth-panel 受 `authPanelMode` 控制,调用方需要 dispatch `AuthPanelModeChanged`
// 但我们目前 AuthFacebookPanel 内部 click "Continue with Email" 直接调用 onSwitchToEmail
// 实际上本组件直接渲染对应 panel
<AuthFacebookPanel onSwitchToEmail={switchToEmail} />
) : (
<AuthEmailPanel
onSwitchToFacebook={() => {
switchToFacebook();
}}
/>
) : (
<AuthEmailPanel onBack={() => undefined} />
)}
</div>
);