MOPC-App/src/components/ui/country-select.tsx

119 lines
3.9 KiB
TypeScript
Raw Normal View History

'use client'
import * as React from 'react'
import { CheckIcon, ChevronsUpDown } from 'lucide-react'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/components/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover'
import { ScrollArea } from '@/components/ui/scroll-area'
import { COUNTRIES } from '@/lib/countries'
// Build sorted country list from the canonical COUNTRIES source
const countryList = Object.entries(COUNTRIES)
.map(([code, info]) => ({ code, name: info.name }))
.sort((a, b) => a.name.localeCompare(b.name))
/** Renders a country flag image from flagcdn.com CDN */
function CountryFlagImg({ code, size = 20, className }: { code: string; size?: number; className?: string }) {
return (
<img
src={`https://flagcdn.com/w${size}/${code.toLowerCase()}.png`}
srcSet={`https://flagcdn.com/w${size * 2}/${code.toLowerCase()}.png 2x`}
width={size}
height={Math.round(size * 0.75)}
alt={code}
className={cn('inline-block rounded-[2px]', className)}
loading="lazy"
/>
)
}
export type Country = { code: string; name: string }
interface CountrySelectProps {
value?: string
onChange?: (value: string) => void
placeholder?: string
disabled?: boolean
className?: string
}
const CountrySelect = React.forwardRef<HTMLButtonElement, CountrySelectProps>(
({ value, onChange, placeholder = 'Select country...', disabled, className }, ref) => {
const [open, setOpen] = React.useState(false)
const selectedCountry = countryList.find((c) => c.code === value)
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
ref={ref}
variant="outline"
role="combobox"
aria-expanded={open}
className={cn('w-full justify-between font-normal', className)}
disabled={disabled}
>
{selectedCountry ? (
<span className="flex items-center gap-2">
<CountryFlagImg code={selectedCountry.code} size={20} />
<span>{selectedCountry.name}</span>
</span>
) : (
<span className="text-muted-foreground">{placeholder}</span>
)}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[--radix-popover-trigger-width] p-0" align="start">
<Command>
<CommandInput placeholder="Search country..." />
<CommandList>
<ScrollArea className="h-72">
<CommandEmpty>No country found.</CommandEmpty>
<CommandGroup>
{countryList.map((country) => (
<CommandItem
key={country.code}
value={country.name}
onSelect={() => {
onChange?.(country.code)
setOpen(false)
}}
className="gap-2"
>
<CountryFlagImg code={country.code} size={20} />
<span className="flex-1">{country.name}</span>
<CheckIcon
className={cn(
'ml-auto h-4 w-4',
value === country.code ? 'opacity-100' : 'opacity-0'
)}
/>
</CommandItem>
))}
</CommandGroup>
</ScrollArea>
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
)
CountrySelect.displayName = 'CountrySelect'
export { CountrySelect, countryList as countries, CountryFlagImg }