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:
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 底部弹层样式
|
||||
*
|
||||
* 原始 Dart: showModalBottomSheet(透明背景,底部白卡 + 顶部圆角 radius28)
|
||||
*/
|
||||
.scrim {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 50;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
background: var(--color-auth-surface);
|
||||
color: var(--color-auth-text-primary);
|
||||
border-radius: var(--radius-bottom-sheet) var(--radius-bottom-sheet) 0 0;
|
||||
box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.18);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
animation: slideUp 0.18s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
"use client";
|
||||
/**
|
||||
* 通用底部弹层(Bottom Sheet)
|
||||
*
|
||||
* 原始 Dart: 散落在各处的 `showModalBottomSheet`(auth_other_options_dialog 等)
|
||||
*
|
||||
* 设计目标:
|
||||
* - portal 渲染到 body
|
||||
* - 固定底部、最大宽度 500px、顶部圆角 `--radius-bottom-sheet`
|
||||
* - ESC 关闭 / 点击 scrim 关闭 / 打开时锁定 body 滚动
|
||||
* - 简单 slide-up 动画(与原 Dart 行为接近)
|
||||
* - 调用方只关心内容布局
|
||||
*/
|
||||
import { type ReactNode, useEffect } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
import styles from "./bottom-sheet.module.css";
|
||||
|
||||
export interface BottomSheetProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
children: ReactNode;
|
||||
/** 蒙层透明度(0-1)。默认 0.45。 */
|
||||
scrimOpacity?: number;
|
||||
/** 禁用点击外部 / ESC 关闭。 */
|
||||
persistent?: boolean;
|
||||
/** ARIA 标签。 */
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export function BottomSheet({
|
||||
open,
|
||||
onClose,
|
||||
children,
|
||||
scrimOpacity = 0.45,
|
||||
persistent = false,
|
||||
ariaLabel,
|
||||
}: BottomSheetProps) {
|
||||
useEffect(() => {
|
||||
if (!open || typeof document === "undefined") return;
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape" && !persistent) onClose();
|
||||
};
|
||||
document.addEventListener("keydown", onKey);
|
||||
const prevOverflow = document.body.style.overflow;
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
document.removeEventListener("keydown", onKey);
|
||||
document.body.style.overflow = prevOverflow;
|
||||
};
|
||||
}, [open, onClose, persistent]);
|
||||
|
||||
if (!open || typeof document === "undefined") return null;
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
className={styles.scrim}
|
||||
style={{ background: `rgba(0, 0, 0, ${scrimOpacity})` }}
|
||||
onClick={(e) => {
|
||||
if (!persistent && e.target === e.currentTarget) onClose();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label={ariaLabel}
|
||||
className={styles.panel}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user