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