diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx index 225df87..0bc5a5e 100644 --- a/src/components/ui/Input.tsx +++ b/src/components/ui/Input.tsx @@ -83,8 +83,7 @@ export const Input = React.forwardRef( hasError ? "text-rose-600 dark:text-rose-400" : "text-slate-700 dark:text-slate-300" }`}> {label} - - {required && *} + {required} )} diff --git a/src/components/ui/Select.tsx b/src/components/ui/Select.tsx index 40f80ea..c5025b4 100644 --- a/src/components/ui/Select.tsx +++ b/src/components/ui/Select.tsx @@ -1,6 +1,9 @@ "use client"; -import React from 'react'; -import { FiChevronDown, FiAlertCircle } from 'react-icons/fi'; + +import React from "react"; +import { Select as HeroSelect, Label, Description, FieldError, ListBox, ListBoxItem } from "@heroui/react"; +import type { Key } from "@heroui/react"; +import { FiChevronDown, FiAlertCircle } from "react-icons/fi"; export interface SelectOption { value: string | number; @@ -8,158 +11,190 @@ export interface SelectOption { disabled?: boolean; } -export interface SelectProps extends Omit, 'size'> { +export interface SelectProps { label?: string; helperText?: string; errorMessage?: string; leftIcon?: React.ReactNode; options?: SelectOption[]; - sizeVariant?: 'sm' | 'md' | 'lg'; + sizeVariant?: "sm" | "md" | "lg"; fullWidth?: boolean; + id?: string; + name?: string; + disabled?: boolean; + required?: boolean; + value?: string | number; + defaultValue?: string | number; + onChange?: (value: string | null) => void; + className?: string; + children?: React.ReactNode; } const sizeStyles = { sm: { - select: 'py-2 text-xs rounded-lg', - icon: 'text-sm', - leftPad: 'pl-8', - rightPad: 'pr-8', + select: "py-2 text-xs rounded-lg", + icon: "text-sm", + leftPad: "pl-8", + rightPad: "pr-8", }, md: { - select: 'py-2.5 text-sm rounded-xl', - icon: 'text-base', - leftPad: 'pl-10', - rightPad: 'pr-10', + select: "py-2.5 text-sm rounded-xl", + icon: "text-base", + leftPad: "pl-10", + rightPad: "pr-10", }, lg: { - select: 'py-3.5 text-base rounded-2xl', - icon: 'text-lg', - leftPad: 'pl-12', - rightPad: 'pr-12', + select: "py-3.5 text-base rounded-2xl", + icon: "text-lg", + leftPad: "pl-12", + rightPad: "pr-12", }, }; -export const Select = React.forwardRef( +export const Select = React.forwardRef( ( { label, helperText, errorMessage, leftIcon, - options, - sizeVariant = 'md', + options = [], + sizeVariant = "md", fullWidth = true, id, name, disabled, required, - className = '', + className = "", + value, + defaultValue, + onChange, children, - ...props }, - ref + ref, ) => { - const selectId = id || name || (label ? label.toLowerCase().replace(/\s+/g, '-') : undefined); + const selectId = id || name || (label ? label.toLowerCase().replace(/\s+/g, "-") : undefined); + const hasError = Boolean(errorMessage); const currentSize = sizeStyles[sizeVariant]; + const selectedKey = value !== undefined && value !== null && value !== "" ? String(value) : undefined; + + const defaultSelectedKey = + defaultValue !== undefined && defaultValue !== null && defaultValue !== "" ? String(defaultValue) : undefined; + + const handleSelectionChange = (key: Key | null) => { + if (!onChange) return; + onChange(key !== null ? String(key) : null); + }; + return ( -
- {/* Label */} - {label && ( -
-
- )} - - {/* Select Wrapper */} -
- {/* Left Icon */} - {leftIcon && ( -
- {leftIcon} -
+ )} - {/* Core Select Element */} - + `}> + {leftIcon && ( + + {leftIcon} + + )} - {/* Right Chevron Icon */} -
- -
-
+ - {/* Error or Helper Message */} - {hasError ? ( -

- - {errorMessage} -

- ) : helperText ? ( -

{helperText}

- ) : null} + + + + + + + + {options.length > 0 + ? options.map((option) => ( + + {option.label} + + )) + : (children as any)} + + + + {hasError ? ( + + + {errorMessage} + + ) : helperText ? ( + {helperText} + ) : null} +
); - } + }, ); -Select.displayName = 'Select'; +Select.displayName = "Select";