avatar

shade cn select field

Guest 178 14th Sep, 2026

import { useMemo } from "react"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import FieldErrorMessage from "@/components/common/FieldErrorMessage"
import FormLabel from "@/components/common/FormLabel"
import { cn } from "@/lib/utils"
import type { FormSelectFieldProps } from "@/type/commonType"
import { getFormSelectFieldState } from "@/utils/formFieldValidation"
import { dedupeSelectOptions } from "@/utils/search"

export function FormSelectField({
  label,
  name,
  value,
  onChange,
  options,
  placeholder,
  error,
  touched,
  isRequired = false,
  className,
  disabled = false,
  isLoading = false,
  loadingText = "Loading...",
  noValueFoundText = "No options found",
  valueType = "string",
}: FormSelectFieldProps) {
  const uniqueOptions = useMemo(() => dedupeSelectOptions(options), [options])
  const selectValue = value == null ? "" : String(value)

  const handleValueChange = (val: string) => {
    if (valueType === "number") {
      ;(onChange as (value: number) => void)(Number(val))
      return
    }

    ;(onChange as (value: string) => void)(val)
  }
  const fieldState = getFormSelectFieldState({
    name,
    touched,
    error,
    disabled,
    isLoading,
  })

  return (
    <div className={cn("mb-5 space-y-2", className)}>
      <FormLabel
        name={name}
        label={label}
        isRequired={isRequired}
        className="text-micurato-steelGray"
      />

      <Select
        value={selectValue}
        onValueChange={handleValueChange}
        disabled={fieldState.isDisabled}
        name={name}
      >
        <SelectTrigger
          id={name}
          className={cn("w-full", fieldState.hasError && "border-destructive")}
          disabled={fieldState.isDisabled}
          aria-invalid={fieldState.ariaInvalid}
          aria-describedby={fieldState.ariaDescribedBy}
          aria-busy={fieldState.isLoading || undefined}
        >
          <SelectValue
            placeholder={fieldState.isLoading ? loadingText : placeholder}
          />
        </SelectTrigger>

        <SelectContent>
          {fieldState.isLoading ? (
            <p className="px-2 py-1.5 text-sm text-muted-foreground" role="status">
              {loadingText}
            </p>
          ) : uniqueOptions.length === 0 ? (
            <p className="px-2 py-1.5 text-sm text-muted-foreground" role="status">
              {noValueFoundText}
            </p>
          ) : (
            uniqueOptions.map((item) => (
              <SelectItem key={item.value} value={item.value}>
                {item.label}
              </SelectItem>
            ))
          )}
        </SelectContent>
      </Select>

      <FieldErrorMessage
        id={fieldState.errorId}
        touched={touched}
        errors={error}
        className="min-h-[20px]"
      />
    </div>
  )
}
Description

No description

To share this paste please copy this url and send to your friends
RAW Paste Data
Recent Pastes