UNPKG

bigblocks

Version:

Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React

104 lines (103 loc) 11.6 kB
"use client"; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { CheckIcon, CopyIcon, DotsHorizontalIcon, EnvelopeClosedIcon, ExternalLinkIcon, GlobeIcon, HomeIcon, TokensIcon, } from "@radix-ui/react-icons"; import React from "react"; import { HEIGHTS } from "../../lib/layout-constants.js"; import { BitcoinImage } from "../ui-components/index.js"; import { Badge } from "../ui/badge.js"; import { Button } from "../ui/button.js"; import { Card } from "../ui/card.js"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "../ui/dropdown-menu.js"; import { ScrollArea } from "../ui/scroll-area.js"; import { Separator } from "../ui/separator.js"; /** * ProfileCard supporting full schema.org Person/Organization structure * * Features: * - Complete schema.org field support (name, email, url, location, etc.) * - Person vs Organization type detection * - Rich contact information display * - Location and address handling * - Bitcoin-specific fields (paymail, address) * - Beautiful avatar with BitcoinImage wrapper * - Scrollable bio section * - Responsive design with multiple variants */ export function ProfileCard({ profile, showActions = true, onEdit, onSwitch, onViewDetails, menuItems, status, compact = false, variant = "surface", size = "2", showFullDetails = false, className = "", style, noWrapper = false, }) { const [copied, setCopied] = React.useState(null); const handleCopy = async (text, type) => { try { await navigator.clipboard.writeText(text); setCopied(type); setTimeout(() => setCopied(null), 2000); } catch (error) { console.error("Failed to copy:", error); } }; const getInitials = (name) => { if (!name) return "?"; return name .split(" ") .map((word) => word[0]) .join("") .toUpperCase() .slice(0, 2); }; const getDisplayName = () => { // Support schema.org structure: alternateName, givenName+familyName, or fallback if (profile.alternateName) return profile.alternateName; if (profile.givenName && profile.familyName) { return `${profile.givenName} ${profile.familyName}`; } if (profile.givenName) return profile.givenName; if (profile.name) return profile.name; if (profile.legalName) return profile.legalName; return "Unnamed Profile"; }; const getProfileType = () => { // Detect if it's a Person or Organization based on schema.org fields if (profile.legalName || profile.logo) return "Organization"; if (profile.givenName || profile.familyName || profile.homeLocation) return "Person"; return "Person"; // Default }; const profileType = getProfileType(); const displayName = getDisplayName(); const avatarImage = profileType === "Organization" ? profile.logo || profile.image : profile.image; // Use custom status or default to published/draft const statusBadge = status || { label: profile.isPublished ? "Published" : "Draft", color: profile.isPublished ? "green" : "orange", variant: "soft", }; // Compact view for tight spaces if (compact) { return (_jsx(Card, { className: `p-3 ${className}`, style: style, children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(BitcoinImage, { src: avatarImage, alt: displayName, fallback: getInitials(displayName), className: "w-8 h-8 rounded-md" }), _jsxs("div", { className: "flex flex-col gap-1 flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("p", { className: "text-sm font-bold truncate", children: displayName }), _jsx(Badge, { variant: statusBadge.variant === "soft" ? "secondary" : "default", className: "text-xs", children: statusBadge.label })] }), profile.description && (_jsx("p", { className: "text-xs text-muted-foreground truncate", children: profile.description }))] }), onSwitch && (_jsx(Button, { size: "sm", variant: "ghost", onClick: onSwitch, children: _jsx(CheckIcon, { className: "h-4 w-4" }) }))] }) })); } // Full profile card view const content = (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-start justify-between w-full", children: [_jsxs("div", { className: "flex items-center gap-4 flex-1 min-w-0", children: [_jsx(BitcoinImage, { src: avatarImage, alt: displayName, fallback: getInitials(displayName), className: "w-16 h-16 rounded-md" }), _jsxs("div", { className: "flex flex-col gap-2 flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-3 w-full", children: [_jsx("h3", { className: "text-lg font-bold flex-1 min-w-0", children: displayName }), _jsx(Badge, { variant: statusBadge.variant === "soft" ? "secondary" : "default", className: "text-xs", children: statusBadge.label })] }), profileType === "Organization" && profile.legalName && profile.legalName !== displayName && (_jsx("p", { className: "text-sm text-muted-foreground", children: profile.legalName })), profileType === "Person" && profile.givenName && profile.familyName && !profile.alternateName && (_jsxs("p", { className: "text-sm text-muted-foreground", children: [profile.givenName, " ", profile.familyName] })), _jsxs("div", { className: "flex items-center gap-2 w-full", children: [_jsxs("p", { className: "text-xs text-muted-foreground font-mono flex-1 min-w-0", children: [profile.address.slice(0, 8), "...", profile.address.slice(-6)] }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => handleCopy(profile.address, "address"), "aria-label": "Copy address", children: copied === "address" ? (_jsx(CheckIcon, { className: "h-3 w-3" })) : (_jsx(CopyIcon, { className: "h-3 w-3" })) }), _jsx(Button, { size: "sm", variant: "ghost", asChild: true, "aria-label": "View on blockchain", children: _jsx("a", { href: `https://whatsonchain.com/address/${profile.address}`, target: "_blank", rel: "noopener noreferrer", children: _jsx(ExternalLinkIcon, { className: "h-3 w-3" }) }) })] })] })] }), menuItems && menuItems.length > 0 && (_jsx("div", { className: "flex-shrink-0 ml-3", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { size: "sm", variant: "ghost", "aria-label": "More options", children: _jsx(DotsHorizontalIcon, { className: "h-4 w-4" }) }) }), _jsx(DropdownMenuContent, { align: "end", children: menuItems.map((item, index) => (_jsxs(DropdownMenuItem, { onClick: item.onClick, children: [item.icon && _jsx("span", { className: "mr-2", children: item.icon }), item.label] }, `profile-card-menu-item-${index + 1}`))) })] }) }))] }), profile.description && (_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium mb-2", children: "About" }), _jsx(ScrollArea, { className: "", style: { height: showFullDetails ? "auto" : "60px", maxHeight: showFullDetails ? HEIGHTS.CARD_MIN : "60px", }, children: _jsx("p", { className: "text-sm leading-6", children: profile.description }) })] })), showFullDetails && (_jsxs("div", { className: "flex flex-col gap-3 w-full", children: [profile.email && (_jsxs("div", { className: "flex items-center justify-between w-full", children: [_jsxs("div", { className: "flex items-center gap-2 w-20 flex-shrink-0", children: [_jsx(EnvelopeClosedIcon, { className: "h-4 w-4" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Email" })] }), _jsx("div", { className: "flex-1 text-right pr-2", children: _jsx("a", { href: `mailto:${profile.email}`, className: "text-sm text-blue-600 hover:underline", children: profile.email }) }), _jsx("div", { className: "w-10 flex justify-end", children: _jsx(Button, { size: "sm", variant: "ghost", onClick: () => handleCopy(profile.email || "", "email"), children: copied === "email" ? (_jsx(CheckIcon, { className: "h-3 w-3" })) : (_jsx(CopyIcon, { className: "h-3 w-3" })) }) })] })), profile.url && (_jsxs("div", { className: "flex items-center justify-between w-full", children: [_jsxs("div", { className: "flex items-center gap-2 w-20 flex-shrink-0", children: [_jsx(GlobeIcon, { className: "h-4 w-4" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Website" })] }), _jsx("div", { className: "flex-1 text-right pr-2", children: _jsx("p", { className: "text-sm", children: profile.url.replace(/^https?:\/\//, "") }) }), _jsx("div", { className: "w-10 flex justify-end", children: _jsx(Button, { size: "sm", variant: "ghost", asChild: true, children: _jsx("a", { href: profile.url, target: "_blank", rel: "noopener noreferrer", children: _jsx(ExternalLinkIcon, { className: "h-3 w-3" }) }) }) })] })), (profile.location || profile.homeLocation) && (_jsxs("div", { className: "flex items-center justify-between w-full", children: [_jsxs("div", { className: "flex items-center gap-2 w-20 flex-shrink-0", children: [profileType === "Organization" ? (_jsx(TokensIcon, { className: "h-4 w-4" })) : (_jsx(HomeIcon, { className: "h-4 w-4" })), _jsx("p", { className: "text-sm text-muted-foreground", children: "Location" })] }), _jsx("div", { className: "flex-1 text-right pr-2", children: _jsx("p", { className: "text-sm", children: profileType === "Organization" ? profile.location : profile.homeLocation }) }), _jsx("div", { className: "w-10" })] })), profile.paymail && (_jsxs("div", { className: "flex items-center justify-between w-full", children: [_jsxs("div", { className: "flex items-center gap-2 w-20 flex-shrink-0", children: [_jsx("span", { className: "text-sm", children: "\u20BF" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Paymail" })] }), _jsx("div", { className: "flex-1 text-right pr-2", children: _jsx("p", { className: "text-sm font-mono", children: profile.paymail }) }), _jsx("div", { className: "w-10 flex justify-end", children: _jsx(Button, { size: "sm", variant: "ghost", onClick: () => handleCopy(profile.paymail || "", "paymail"), children: copied === "paymail" ? (_jsx(CheckIcon, { className: "h-3 w-3" })) : (_jsx(CopyIcon, { className: "h-3 w-3" })) }) })] }))] })), showActions && (onEdit || onSwitch || onViewDetails) && (_jsxs(_Fragment, { children: [_jsx(Separator, { className: "my-4" }), _jsxs("div", { className: "flex flex-col sm:flex-row gap-2 sm:gap-3 justify-center sm:justify-end", children: [onViewDetails && (_jsx(Button, { size: "sm", variant: "outline", onClick: onViewDetails, children: "Details" })), onEdit && (_jsx(Button, { size: "sm", variant: "outline", onClick: onEdit, children: "Edit" })), onSwitch && (_jsxs(Button, { size: "sm", variant: "default", onClick: onSwitch, children: [_jsx(CheckIcon, { className: "h-4 w-4 mr-1" }), _jsx("span", { className: "hidden sm:inline", children: "Switch to Profile" }), _jsx("span", { className: "inline sm:hidden", children: "Switch" })] }))] })] }))] })); // Conditionally wrap in Card if (noWrapper) { return content; } return (_jsx(Card, { className: `p-6 ${className}`, style: style, children: content })); }