Spaces:
Running
Running
| import React from 'react'; | |
| import { Copy } from 'lucide-react'; | |
| import { Label } from './Label'; | |
| export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> { | |
| label?: string; | |
| error?: string; | |
| helperText?: string; | |
| onCopyClick?: () => void; | |
| copyLabel?: string; | |
| rightElement?: React.ReactNode; | |
| labelRightElement?: React.ReactNode; | |
| options?: { value: string; label: string }[]; | |
| } | |
| export const Input: React.FC<InputProps> = ({ | |
| label, | |
| error, | |
| helperText, | |
| className = '', | |
| id, | |
| onCopyClick, | |
| copyLabel, | |
| rightElement, | |
| labelRightElement, | |
| options, | |
| ...props | |
| }) => { | |
| const inputId = id || `input-${Date.now()}`; | |
| return ( | |
| <div className="space-y-1"> | |
| {label && ( | |
| <div className="flex items-center justify-between mb-1"> | |
| <Label htmlFor={inputId} className="!mb-0"> | |
| {label} | |
| </Label> | |
| {labelRightElement && ( | |
| <div className="flex items-center gap-1.5"> | |
| {labelRightElement} | |
| </div> | |
| )} | |
| </div> | |
| )} | |
| <div className="relative flex items-center w-full"> | |
| {props.type === 'select' ? ( | |
| <select | |
| id={inputId} | |
| className={`w-full px-3 py-2 text-sm border ${error | |
| ? 'border-red-500 focus:ring-red-500/10 focus:border-red-500' | |
| : 'border-slate-200 focus:ring-2 focus:ring-orange-500/10 focus:border-orange-500' | |
| } rounded-lg outline-none bg-white transition-all cursor-pointer ${className}`} | |
| {...(props as unknown as React.SelectHTMLAttributes<HTMLSelectElement>)} | |
| > | |
| {options?.map((opt) => ( | |
| <option key={opt.value} value={opt.value}> | |
| {opt.label} | |
| </option> | |
| ))} | |
| </select> | |
| ) : ( | |
| <input | |
| id={inputId} | |
| className={`w-full px-3 py-2 text-sm border ${error | |
| ? 'border-red-500 focus:ring-red-500/10 focus:border-red-500' | |
| : 'border-slate-200 focus:ring-2 focus:ring-orange-500/10 focus:border-orange-500' | |
| } rounded-lg outline-none transition-all ${onCopyClick || rightElement ? 'pr-24' : ''} ${className}`} | |
| {...props} | |
| /> | |
| )} | |
| {onCopyClick && !rightElement && ( | |
| <button | |
| type="button" | |
| onClick={onCopyClick} | |
| className="absolute right-2 px-2 py-1 text-xs font-semibold text-orange-600 hover:text-orange-700 flex items-center gap-1 bg-orange-50 hover:bg-orange-100 rounded transition-colors cursor-pointer" | |
| title={copyLabel || '복사'} | |
| > | |
| <Copy className="w-3 h-3" /> | |
| <span>{copyLabel || '복사'}</span> | |
| </button> | |
| )} | |
| {rightElement && ( | |
| <div className="absolute right-2 flex items-center gap-1.5"> | |
| {rightElement} | |
| </div> | |
| )} | |
| </div> | |
| {error && <p className="text-[11px] text-red-500 font-semibold">{error}</p>} | |
| {!error && helperText && <p className="text-[11px] text-slate-400">{helperText}</p>} | |
| </div> | |
| ); | |
| }; | |