Componentpickers1.34 KB gzipZero dependencies

Currency Select

Currency select dropdown with symbols and codes.

Preview

Live Preview

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add currency-select

Or install the full npm package:

npm install bigbullui
import { CurrencySelect } from "@/components/ui/currency-select";

<CurrencySelect />

Usage guidance

Use Currency Select when a ticket stub surface needs Currency select dropdown with symbols and codes. Keep props minimal and prefer composition with sibling stubs.

Avoid Currency Select for long form flows or dense data grids where a dedicated layout block fits better.

Source code

currency-select.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export interface CurrencyOption {
  code: string;
  symbol: string;
  label: string;
}

export const CURRENCIES: CurrencyOption[] = [
  { code: "TRY", symbol: "₺", label: "Turkish Lira" },
  { code: "USD", symbol: "$", label: "US Dollar" },
  { code: "EUR", symbol: "€", label: "Euro" },
  { code: "GBP", symbol: "£", label: "Sterlin" },
];

export interface CurrencySelectProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
  value?: string;
  defaultValue?: string;
  onValueChange?: (code: string) => void;
}

/** Currency selector dropdown with symbols and currency codes. */
export function CurrencySelect({ value, defaultValue, onValueChange, className, ...props }: CurrencySelectProps) {
  const [internal, setInternal] = React.useState(defaultValue ?? "TRY");
  const [open, setOpen] = React.useState(false);
  const active = CURRENCIES.find((c) => c.code === (value ?? internal)) ?? CURRENCIES[0];

  return (
    <div className={cn("relative", className)} {...props}>
      <style>{`@keyframes curIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }`}</style>
      <button
        onClick={() => setOpen((o) => !o)}
        aria-expanded={open}
        aria-haspopup="listbox"
        className={cn(
          "flex w-full items-center gap-2.5 rounded-md border border-input bg-background px-3 py-2 text-sm",
          "transition-colors motion-reduce:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
          "hover:border-foreground/40"
        )}
      >
        <span className="font-mono text-base font-bold text-accent">{active.symbol}</span>
        <span className="flex-1 text-start">{active.label}</span>
        <span className="font-mono text-[10px] text-muted-foreground">{active.code}</span>
        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={cn("shrink-0 text-muted-foreground transition-transform duration-200 motion-reduce:transition-none", open && "rotate-180")} aria-hidden="true"><path d="M6 9l6 6 6-6" /></svg>
      </button>
      {open && (
        <>
          <div className="fixed inset-0 z-20" onClick={() => setOpen(false)} aria-hidden="true" />
          <ul role="listbox" className="absolute z-30 mt-1 w-full rounded-md border border-border bg-card p-1 shadow-lg animate-[curIn_0.2s_ease-out_both] motion-reduce:animate-none">
            {CURRENCIES.map((c) => (
              <li key={c.code}>
                <button
                  role="option"
                  aria-selected={c.code === active.code}
                  onClick={() => { setInternal(c.code); onValueChange?.(c.code); setOpen(false); }}
                  className={cn(
                    "flex w-full items-center gap-2.5 rounded-sm px-2 py-1.5 text-sm transition-colors motion-reduce:transition-none focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
                    c.code === active.code ? "bg-accent/10 text-accent" : "hover:bg-secondary"
                  )}
                >
                  <span className="font-mono font-bold">{c.symbol}</span>
                  <span className="flex-1 text-start">{c.label}</span>
                  <span className="font-mono text-[10px] text-muted-foreground">{c.code}</span>
                </button>
              </li>
            ))}
          </ul>
        </>
      )}
    </div>
  );
}

Props

PropTypeDescription
valuestring
defaultValuestring
onValueChange(code: string) => void