98 lines
2.6 KiB
TypeScript
98 lines
2.6 KiB
TypeScript
import { forwardRef, SelectHTMLAttributes } from 'react';
|
|
import { Box } from './Box';
|
|
import { Text } from './Text';
|
|
|
|
export interface SelectOption {
|
|
value: string | number;
|
|
label: string;
|
|
}
|
|
|
|
export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'size'> {
|
|
label?: string;
|
|
options: SelectOption[];
|
|
error?: string;
|
|
hint?: string;
|
|
fullWidth?: boolean;
|
|
size?: 'sm' | 'md' | 'lg';
|
|
pl?: number;
|
|
}
|
|
|
|
export const Select = forwardRef<HTMLSelectElement, SelectProps>(({
|
|
label,
|
|
options,
|
|
error,
|
|
hint,
|
|
fullWidth = false,
|
|
size = 'md',
|
|
pl,
|
|
...props
|
|
}, ref) => {
|
|
const sizeClasses = {
|
|
sm: 'px-3 py-1.5 text-xs',
|
|
md: 'px-4 py-2 text-sm',
|
|
lg: 'px-4 py-3 text-base'
|
|
};
|
|
|
|
const baseClasses = 'bg-[var(--ui-color-bg-surface)] border border-[var(--ui-color-border-default)] text-[var(--ui-color-text-high)] focus:outline-none focus:border-[var(--ui-color-intent-primary)] transition-colors appearance-none';
|
|
const errorClasses = error ? 'border-[var(--ui-color-intent-critical)]' : '';
|
|
const widthClasses = fullWidth ? 'w-full' : '';
|
|
|
|
const classes = [
|
|
baseClasses,
|
|
sizeClasses[size],
|
|
errorClasses,
|
|
widthClasses,
|
|
].filter(Boolean).join(' ');
|
|
|
|
const style: React.CSSProperties = {
|
|
...(pl !== undefined ? { paddingLeft: `${pl * 0.25}rem` } : {}),
|
|
};
|
|
|
|
return (
|
|
<Box width={fullWidth ? '100%' : undefined}>
|
|
{label && (
|
|
<Box marginBottom={1.5}>
|
|
<Text as="label" size="xs" weight="bold" variant="low">
|
|
{label}
|
|
</Text>
|
|
</Box>
|
|
)}
|
|
<div className="relative">
|
|
<select
|
|
ref={ref}
|
|
className={classes}
|
|
style={style}
|
|
{...props}
|
|
>
|
|
{options.map((option) => (
|
|
<option key={option.value} value={option.value}>
|
|
{option.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
<div className="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none">
|
|
<svg className="w-4 h-4 text-[var(--ui-color-text-low)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
{error && (
|
|
<Box marginTop={1}>
|
|
<Text size="xs" variant="critical">
|
|
{error}
|
|
</Text>
|
|
</Box>
|
|
)}
|
|
{hint && !error && (
|
|
<Box marginTop={1}>
|
|
<Text size="xs" variant="low">
|
|
{hint}
|
|
</Text>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
);
|
|
});
|
|
|
|
Select.displayName = 'Select';
|