674df49e24
Match the Dart lib/ui/sidebar/sidebar_screen.dart + profile_view.dart
exactly:
- White inner shell with 24px padding
- Back button row (arrow icon + "Back" text, no "Profile" header)
- Single "Other" section with one "Log out" row
- Drop profile card, Account/Data/Session sections, and Sign-in CTA
Match Flutter row visuals in the shared SettingsSection:
- Gray (#f5f5f5) rounded row pill on a white card
- 20px chevron, 1px rgba(61,49,74,0.5) divider
- Destructive color #ff6b6b, 14px/600 black section title
Add tokens: --color-settings-card-background, --color-settings-row-background,
--color-settings-divider, --color-chevron-icon, --color-destructive.
Wire the BlocConsumer listener equivalent: on currentUser transitioning to
null, dispatch ChatAuthStatusChanged + AuthReset and router.replace("/chat").
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
83 lines
2.3 KiB
TypeScript
83 lines
2.3 KiB
TypeScript
"use client";
|
|
/**
|
|
* 设置项列表 Section
|
|
*
|
|
* 原始 Dart: lib/ui/core/settings_section.dart
|
|
*
|
|
* 渲染一组 SettingItem:图标 + 标题 + 副标题/值 + 右侧 chevron + 可选 divider。
|
|
*/
|
|
import { type ReactNode } from "react";
|
|
|
|
import styles from "./settings-section.module.css";
|
|
|
|
export interface SettingsItemModel {
|
|
id: string;
|
|
title: string;
|
|
subtitle?: string;
|
|
trailing?: string;
|
|
icon?: ReactNode;
|
|
onClick?: () => void;
|
|
destructive?: boolean;
|
|
}
|
|
|
|
export interface SettingsSectionProps {
|
|
title?: string;
|
|
items: SettingsItemModel[];
|
|
}
|
|
|
|
export function SettingsSection({ title, items }: SettingsSectionProps) {
|
|
return (
|
|
<section className={styles.section}>
|
|
{title ? <h3 className={styles.title}>{title}</h3> : null}
|
|
<ul className={styles.list}>
|
|
{items.map((item, idx) => (
|
|
<li key={item.id}>
|
|
<button
|
|
type="button"
|
|
onClick={item.onClick}
|
|
className={[
|
|
styles.row,
|
|
item.destructive ? styles.destructive : "",
|
|
]
|
|
.filter(Boolean)
|
|
.join(" ")}
|
|
>
|
|
{item.icon ? (
|
|
<span className={styles.icon}>{item.icon}</span>
|
|
) : null}
|
|
<span className={styles.body}>
|
|
<span className={styles.rowTitle}>{item.title}</span>
|
|
{item.subtitle ? (
|
|
<span className={styles.subtitle}>{item.subtitle}</span>
|
|
) : null}
|
|
</span>
|
|
{item.trailing ? (
|
|
<span className={styles.trailing}>{item.trailing}</span>
|
|
) : null}
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
aria-hidden="true"
|
|
className={styles.chevron}
|
|
>
|
|
<path
|
|
d="M9 6l6 6-6 6"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
{idx < items.length - 1 ? (
|
|
<div className={styles.divider} />
|
|
) : null}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</section>
|
|
);
|
|
}
|