109 lines
3.3 KiB
TypeScript
109 lines
3.3 KiB
TypeScript
import type { ReactNode } from "react";
|
|
import { Dropdown } from "./Dropdown.js";
|
|
import { HeaderAvatar } from "./AppHeader.js";
|
|
import { Icon, type IconName } from "./Icon.js";
|
|
|
|
export interface UserProfileMenuAction {
|
|
id: string;
|
|
label: ReactNode;
|
|
icon?: IconName;
|
|
href?: string;
|
|
disabled?: boolean;
|
|
tone?: "default" | "danger";
|
|
onSelect?: () => void;
|
|
}
|
|
|
|
export interface UserProfileMenuProps {
|
|
displayName: string;
|
|
subtitle?: string;
|
|
avatarUrl?: string;
|
|
coverImageUrl?: string;
|
|
triggerLabel?: ReactNode;
|
|
actions: readonly UserProfileMenuAction[];
|
|
}
|
|
|
|
export function UserProfileMenu({
|
|
displayName,
|
|
subtitle,
|
|
avatarUrl,
|
|
coverImageUrl,
|
|
triggerLabel,
|
|
actions,
|
|
}: UserProfileMenuProps) {
|
|
const label = displayName || "NODE.DC";
|
|
return (
|
|
<Dropdown
|
|
placement="bottom-end"
|
|
width={296}
|
|
offset={10}
|
|
surfaceClassName="nodedc-user-profile-menu"
|
|
trigger={({ open, toggle, setTriggerRef, surfaceId }) => (
|
|
<button
|
|
ref={setTriggerRef}
|
|
type="button"
|
|
className="nodedc-user-profile-menu__trigger"
|
|
title={label}
|
|
aria-label={`Меню пользователя ${label}`}
|
|
aria-expanded={open}
|
|
aria-controls={surfaceId}
|
|
onClick={toggle}
|
|
>
|
|
{triggerLabel === null ? null : <span className="nodedc-user-profile-menu__trigger-label">{triggerLabel ?? label}</span>}
|
|
<HeaderAvatar label={label} imageUrl={avatarUrl} />
|
|
</button>
|
|
)}
|
|
>
|
|
{({ close }) => (
|
|
<div className="nodedc-user-profile-card">
|
|
<div
|
|
className="nodedc-user-profile-card__cover"
|
|
style={coverImageUrl ? { backgroundImage: `linear-gradient(180deg, rgb(0 0 0 / 0.08), rgb(0 0 0 / 0.52)), url(${JSON.stringify(coverImageUrl)})` } : undefined}
|
|
>
|
|
<HeaderAvatar label={label} imageUrl={avatarUrl} />
|
|
<strong>{label}</strong>
|
|
{subtitle ? <span>{subtitle}</span> : null}
|
|
</div>
|
|
<div className="nodedc-user-profile-card__actions">
|
|
{actions.map((action) => {
|
|
const content = (
|
|
<>
|
|
{action.icon ? <Icon name={action.icon} size={16} /> : <span />}
|
|
<span>{action.label}</span>
|
|
</>
|
|
);
|
|
if (action.href && !action.disabled) {
|
|
return (
|
|
<a
|
|
key={action.id}
|
|
className="nodedc-user-profile-card__action"
|
|
data-tone={action.tone === "danger" ? "danger" : undefined}
|
|
href={action.href}
|
|
onClick={() => close()}
|
|
>
|
|
{content}
|
|
</a>
|
|
);
|
|
}
|
|
return (
|
|
<button
|
|
key={action.id}
|
|
type="button"
|
|
className="nodedc-user-profile-card__action"
|
|
data-tone={action.tone === "danger" ? "danger" : undefined}
|
|
disabled={action.disabled}
|
|
onClick={() => {
|
|
action.onSelect?.();
|
|
close();
|
|
}}
|
|
>
|
|
{content}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Dropdown>
|
|
);
|
|
}
|