diff --git a/src/components/common/BackHeader.tsx b/src/components/common/BackHeader.tsx new file mode 100644 index 00000000..cec071b4 --- /dev/null +++ b/src/components/common/BackHeader.tsx @@ -0,0 +1,44 @@ +import { useNavigate } from 'react-router'; +import { ArrowLeft } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +interface BackHeaderProps { + title: string; + subtitle?: string; + onBack?: () => void; + className?: string; +} + +const BackHeader: React.FC = ({ title, subtitle, onBack, className }) => { + const navigate = useNavigate(); + + const handleBack = () => { + if (onBack) { + onBack(); + } else { + navigate(-1); + } + }; + + return ( +
+ +
+

+ {title} +

+ {subtitle && ( +

{subtitle}

+ )} +
+
+ ); +}; + +export default BackHeader; diff --git a/src/components/common/FollowerCountPill.tsx b/src/components/common/FollowerCountPill.tsx new file mode 100644 index 00000000..23ea379d --- /dev/null +++ b/src/components/common/FollowerCountPill.tsx @@ -0,0 +1,37 @@ +import { Users } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +interface FollowerCountPillProps { + count?: number | null; + className?: string; +} + +function formatCount(count: number): string { + if (count >= 1_000_000) { + return `${(count / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`; + } + if (count >= 1_000) { + return `${(count / 1_000).toFixed(1).replace(/\.0$/, '')}K`; + } + return count.toString(); +} + +const FollowerCountPill: React.FC = ({ count, className }) => { + if (count == null || count < 0) { + return null; + } + + return ( + + + {formatCount(count)} + + ); +}; + +export default FollowerCountPill; diff --git a/src/components/common/TruncatedAddress.tsx b/src/components/common/TruncatedAddress.tsx new file mode 100644 index 00000000..91bb4ab4 --- /dev/null +++ b/src/components/common/TruncatedAddress.tsx @@ -0,0 +1,57 @@ +import { useState } from 'react'; +import { Copy, Check } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +interface TruncatedAddressProps { + address: string; + prefixChars?: number; + suffixChars?: number; + copyable?: boolean; + className?: string; +} + +function truncate(address: string, prefix: number, suffix: number): string { + if (address.length <= prefix + suffix + 3) { + return address; + } + return `${address.slice(0, prefix)}...${address.slice(-suffix)}`; +} + +const TruncatedAddress: React.FC = ({ + address, + prefixChars = 6, + suffixChars = 4, + copyable = false, + className, +}) => { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + await navigator.clipboard.writeText(address); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( + + {truncate(address, prefixChars, suffixChars)} + {copyable && ( + + )} + + ); +}; + +export default TruncatedAddress;