feat: enhance dropdown components by integrating useClickOutside hook for improved user experience and accessibility in NewWorkshopDropdown and WorkshopTabs
This commit is contained in:
@@ -1,24 +1,63 @@
|
||||
import { forwardRef, SelectHTMLAttributes } from 'react';
|
||||
|
||||
interface SelectOption {
|
||||
export interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'children'> {
|
||||
const SIZE_STYLES = {
|
||||
xs: 'px-2 py-1 pr-7 text-xs',
|
||||
sm: 'px-2 py-2 pr-8 text-sm',
|
||||
md: 'px-4 py-2.5 pr-10 text-sm',
|
||||
lg: 'px-4 py-2.5 pr-10 text-base',
|
||||
} as const;
|
||||
|
||||
const ICON_SIZES = {
|
||||
xs: 'h-3 w-3',
|
||||
sm: 'h-4 w-4',
|
||||
md: 'h-5 w-5',
|
||||
lg: 'h-5 w-5',
|
||||
} as const;
|
||||
|
||||
const ICON_POSITION = {
|
||||
xs: 'right-2',
|
||||
sm: 'right-2',
|
||||
md: 'right-3',
|
||||
lg: 'right-3',
|
||||
} as const;
|
||||
|
||||
interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'children' | 'size'> {
|
||||
label?: string;
|
||||
error?: string;
|
||||
options: SelectOption[];
|
||||
placeholder?: string;
|
||||
size?: keyof typeof SIZE_STYLES;
|
||||
wrapperClassName?: string;
|
||||
}
|
||||
|
||||
export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
||||
({ className = '', label, error, id, options, placeholder, ...props }, ref) => {
|
||||
(
|
||||
{
|
||||
className = '',
|
||||
label,
|
||||
error,
|
||||
id,
|
||||
options,
|
||||
placeholder,
|
||||
size = 'md',
|
||||
wrapperClassName = '',
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const selectId = id || props.name;
|
||||
const sizeStyles = SIZE_STYLES[size];
|
||||
const iconSize = ICON_SIZES[size];
|
||||
const iconPosition = ICON_POSITION[size];
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className={wrapperClassName || 'w-full'}>
|
||||
{label && (
|
||||
<label htmlFor={selectId} className="mb-2 block text-sm font-medium text-foreground">
|
||||
{label}
|
||||
@@ -29,11 +68,13 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
||||
ref={ref}
|
||||
id={selectId}
|
||||
className={`
|
||||
w-full appearance-none rounded-lg border bg-input px-4 py-2.5 pr-10 text-foreground
|
||||
w-full appearance-none rounded-lg border bg-input text-foreground
|
||||
placeholder:text-muted-foreground
|
||||
focus:outline-none focus:ring-2 focus:ring-primary/20
|
||||
disabled:cursor-not-allowed disabled:opacity-50
|
||||
${error ? 'border-destructive focus:border-destructive' : 'border-input-border focus:border-primary'}
|
||||
border-input-border focus:border-primary
|
||||
${sizeStyles}
|
||||
${error ? 'border-destructive focus:border-destructive' : ''}
|
||||
${className}
|
||||
`}
|
||||
{...props}
|
||||
@@ -49,14 +90,8 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{/* Custom arrow icon */}
|
||||
<div className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2">
|
||||
<svg
|
||||
className="h-5 w-5 text-muted-foreground"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<div className={`pointer-events-none absolute ${iconPosition} top-1/2 -translate-y-1/2 text-muted-foreground`}>
|
||||
<svg className={iconSize} 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>
|
||||
@@ -68,4 +103,3 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
||||
);
|
||||
|
||||
Select.displayName = 'Select';
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ export { ParticipantInput } from './ParticipantInput';
|
||||
export { Modal, ModalFooter } from './Modal';
|
||||
export { RocketIcon } from './RocketIcon';
|
||||
export { Select } from './Select';
|
||||
export type { SelectOption } from './Select';
|
||||
export { Textarea } from './Textarea';
|
||||
export { ToggleGroup } from './ToggleGroup';
|
||||
export type { ToggleOption } from './ToggleGroup';
|
||||
Reference in New Issue
Block a user