bharti-axa/src/components/core/SelectField.tsx

92 lines
2.7 KiB
TypeScript

import { useMemo, useState } from 'react';
import { PickerShell } from './PickerShell';
export interface SelectFieldOption {
value: string;
label: string;
}
export interface SelectFieldProps {
label?: string;
required?: boolean;
hint?: string;
className?: string;
/** Either strings or {value,label} objects. */
options?: Array<string | SelectFieldOption>;
value: string;
onChange: (value: string) => void;
placeholder?: string;
/** Force the search box on/off. Defaults to on when there are >7 options. */
searchable?: boolean;
/** Allow clearing back to ''. Defaults to true for non-required fields. */
clearable?: boolean;
disabled?: boolean;
}
const norm = (o: string | SelectFieldOption): SelectFieldOption =>
typeof o === 'string' ? { value: o, label: o } : o;
/** Single-select that renders through the shared PickerShell, so it matches
* LookupField exactly. Static options, searched/filtered locally. An empty-
* value option (e.g. {value:'', label:'Choose…'}) is treated as the
* placeholder rather than a list row, and option values are de-duplicated. */
export function SelectField({
label,
required,
hint,
className,
options = [],
value,
onChange,
placeholder,
searchable,
clearable,
disabled,
}: SelectFieldProps) {
const [search, setSearch] = useState('');
// Normalize, lift any empty-value option's label to the placeholder, dedupe.
const { opts, ph } = useMemo(() => {
const all = options.map(norm);
const empty = all.find((o) => o.value === '');
const seen = new Set<string>();
const opts = all.filter((o) => {
if (o.value === '' || seen.has(o.value)) return false;
seen.add(o.value);
return true;
});
return { opts, ph: placeholder ?? empty?.label };
}, [options, placeholder]);
const items = useMemo(() => {
const q = search.trim().toLowerCase();
return q ? opts.filter((o) => o.label.toLowerCase().includes(q)) : opts;
}, [opts, search]);
const display = opts.find((o) => o.value === value)?.label ?? '';
const canSearch = searchable ?? opts.length > 7;
const canClear = (clearable ?? !required) && value !== '';
return (
<PickerShell<SelectFieldOption>
label={label}
required={required}
hint={hint}
className={className}
displayLabel={display}
placeholder={ph}
items={items}
renderItem={(o) => o.label}
itemKey={(o) => o.value}
onPick={(o) => onChange(o.value)}
onClear={canClear ? () => onChange('') : undefined}
searchable={canSearch}
search={search}
onSearch={setSearch}
onOpenChange={(open) => !open && setSearch('')}
emptyText="No options."
disabled={disabled}
/>
);
}