index.tsx raw
1 import { cn } from '@/lib/utils'
2 import { TRelaySet } from '@/types'
3 import { ChevronDown, FolderClosed } from 'lucide-react'
4 import { useState } from 'react'
5 import { useTranslation } from 'react-i18next'
6 import RelayIcon from '../RelayIcon'
7
8 export default function RelaySetCard({
9 relaySet,
10 select,
11 onSelectChange
12 }: {
13 relaySet: TRelaySet
14 select: boolean
15 onSelectChange: (select: boolean) => void
16 }) {
17 const { t } = useTranslation()
18 const [expand, setExpand] = useState(false)
19
20 return (
21 <div
22 className={cn(
23 'group relative w-full border rounded-lg px-3 py-2.5 transition-all duration-200',
24 select
25 ? 'border-primary bg-primary/5 shadow-sm'
26 : 'border-border hover:border-primary/50 hover:bg-accent/50 clickable'
27 )}
28 onClick={() => onSelectChange(!select)}
29 >
30 <div className="flex justify-between items-center gap-2">
31 <div className="flex gap-3 items-center flex-1 min-w-0">
32 <div className="flex justify-center items-center size-6 shrink-0">
33 <FolderClosed className="size-5" />
34 </div>
35 <div className="font-medium select-none truncate">{relaySet.name}</div>
36 </div>
37 <div className="flex gap-1 items-center shrink-0">
38 <RelayUrlsExpandToggle expand={expand} onExpandChange={setExpand}>
39 {t('n relays', { n: relaySet.relayUrls.length })}
40 </RelayUrlsExpandToggle>
41 </div>
42 </div>
43 {expand && <RelayUrls urls={relaySet.relayUrls} />}
44 </div>
45 )
46 }
47
48 function RelayUrlsExpandToggle({
49 children,
50 expand,
51 onExpandChange
52 }: {
53 children: React.ReactNode
54 expand: boolean
55 onExpandChange: (expand: boolean) => void
56 }) {
57 return (
58 <div
59 className="text-xs text-muted-foreground flex items-center gap-0.5 cursor-pointer hover:text-foreground transition-colors"
60 onClick={(e) => {
61 e.stopPropagation()
62 onExpandChange(!expand)
63 }}
64 >
65 <div className="select-none font-medium">{children}</div>
66 <ChevronDown
67 size={14}
68 className={cn('transition-transform duration-200', expand && 'rotate-180')}
69 />
70 </div>
71 )
72 }
73
74 function RelayUrls({ urls }: { urls: string[] }) {
75 if (!urls) return null
76
77 return (
78 <div className="mt-2.5 pt-2.5 border-t space-y-1.5">
79 {urls.map((url) => (
80 <div key={url} className="flex items-center gap-2.5 pl-1">
81 <RelayIcon url={url} className="size-4 shrink-0" classNames={{ fallback: 'size-3' }} />
82 <div className="text-muted-foreground text-xs truncate">{url}</div>
83 </div>
84 ))}
85 </div>
86 )
87 }
88