feat(lightbox): 用户主页头像可预览,图箱支持缩放旋转反色
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
58
frontend/app/u/[id]/ProfileHeroAvatar.tsx
Normal file
58
frontend/app/u/[id]/ProfileHeroAvatar.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Avatar from "@/components/Avatar";
|
||||
import ImageLightbox from "@/components/markdown/ImageLightbox";
|
||||
|
||||
const RING_STYLE = {
|
||||
width: 80,
|
||||
height: 80,
|
||||
lineHeight: 0,
|
||||
boxShadow:
|
||||
"0 0 0 4px var(--panel), 0 0 0 6px color-mix(in srgb, var(--accent) 38%, transparent)",
|
||||
} as const;
|
||||
|
||||
const RING_CLASS =
|
||||
"inline-flex items-center justify-center shrink-0 rounded-full p-0";
|
||||
|
||||
/** 用户主页 Hero 头像:有实图时可点击,复用正文图箱预览 */
|
||||
export default function ProfileHeroAvatar({
|
||||
name,
|
||||
src,
|
||||
}: {
|
||||
name: string;
|
||||
src?: string;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const canPreview = !!src;
|
||||
const alt = `${name} 的头像`;
|
||||
|
||||
return (
|
||||
<>
|
||||
{canPreview ? (
|
||||
<button
|
||||
type="button"
|
||||
className={`${RING_CLASS} border-0 bg-transparent cursor-zoom-in outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--panel)]`}
|
||||
style={RING_STYLE}
|
||||
aria-label="查看头像"
|
||||
data-tip="查看头像"
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<Avatar name={name} src={src} size={80} />
|
||||
</button>
|
||||
) : (
|
||||
<span className={RING_CLASS} style={RING_STYLE}>
|
||||
<Avatar name={name} src={src} size={80} />
|
||||
</span>
|
||||
)}
|
||||
{open && src ? (
|
||||
<ImageLightbox
|
||||
items={[{ src, alt }]}
|
||||
index={0}
|
||||
onClose={() => setOpen(false)}
|
||||
onIndexChange={() => {}}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import { getMeCached, getPublicSettingsCached } from "@/lib/serverData";
|
||||
import UserProfileContent from "./UserProfileContent";
|
||||
import ProfileForm from "./ProfileForm";
|
||||
import PasswordForm from "./PasswordForm";
|
||||
import Avatar from "@/components/Avatar";
|
||||
import ProfileHeroAvatar from "./ProfileHeroAvatar";
|
||||
import RoleBadge from "@/components/RoleBadge";
|
||||
import { roleMeta } from "@/lib/roles";
|
||||
import StartDMButton from "./StartDMButton";
|
||||
@@ -181,22 +181,10 @@ export default async function UserPage({ params, searchParams }: UserPageProps)
|
||||
{/* 身份信息:纯文档流,头像 + 右侧文字 baseline 对齐 */}
|
||||
<div className="relative z-10 px-5 sm:px-7 pt-5 sm:pt-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<span
|
||||
className="inline-flex items-center justify-center shrink-0 rounded-full p-0"
|
||||
style={{
|
||||
width: 80,
|
||||
height: 80,
|
||||
lineHeight: 0,
|
||||
boxShadow:
|
||||
"0 0 0 4px var(--panel), 0 0 0 6px color-mix(in srgb, var(--accent) 38%, transparent)",
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
name={u.nickname || u.username}
|
||||
src={u.avatar || undefined}
|
||||
size={80}
|
||||
/>
|
||||
</span>
|
||||
<ProfileHeroAvatar
|
||||
name={u.nickname || u.username}
|
||||
src={u.avatar || undefined}
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<h1
|
||||
|
||||
Reference in New Issue
Block a user