FlotillasGPS - Sistema completo de monitoreo de flotillas GPS
Sistema completo para monitoreo y gestion de flotas de vehiculos con: - Backend FastAPI con PostgreSQL/TimescaleDB - Frontend React con TypeScript y TailwindCSS - App movil React Native con Expo - Soporte para dispositivos GPS, Meshtastic y celulares - Video streaming en vivo con MediaMTX - Geocercas, alertas, viajes y reportes - Autenticacion JWT y WebSockets en tiempo real Documentacion completa y guias de usuario incluidas.
This commit is contained in:
134
frontend/src/components/ui/Input.tsx
Normal file
134
frontend/src/components/ui/Input.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import { forwardRef, InputHTMLAttributes, ReactNode } from 'react'
|
||||
import clsx from 'clsx'
|
||||
|
||||
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
label?: string
|
||||
error?: string
|
||||
helperText?: string
|
||||
leftIcon?: ReactNode
|
||||
rightIcon?: ReactNode
|
||||
fullWidth?: boolean
|
||||
}
|
||||
|
||||
const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
label,
|
||||
error,
|
||||
helperText,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
fullWidth = true,
|
||||
type = 'text',
|
||||
id,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const inputId = id || label?.toLowerCase().replace(/\s+/g, '-')
|
||||
|
||||
return (
|
||||
<div className={clsx(fullWidth && 'w-full')}>
|
||||
{label && (
|
||||
<label
|
||||
htmlFor={inputId}
|
||||
className="block text-sm font-medium text-slate-300 mb-1.5"
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<div className="relative">
|
||||
{leftIcon && (
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-slate-400">
|
||||
{leftIcon}
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
ref={ref}
|
||||
type={type}
|
||||
id={inputId}
|
||||
className={clsx(
|
||||
'w-full px-4 py-2.5 bg-background-800 border rounded-lg',
|
||||
'text-white placeholder-slate-500',
|
||||
'focus:outline-none focus:ring-2 focus:border-transparent',
|
||||
'transition-all duration-200',
|
||||
error
|
||||
? 'border-error-500 focus:ring-error-500'
|
||||
: 'border-slate-700 focus:ring-accent-500',
|
||||
leftIcon && 'pl-10',
|
||||
rightIcon && 'pr-10',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
{rightIcon && (
|
||||
<div className="absolute inset-y-0 right-0 pr-3 flex items-center text-slate-400">
|
||||
{rightIcon}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{error && <p className="mt-1.5 text-sm text-error-500">{error}</p>}
|
||||
{helperText && !error && (
|
||||
<p className="mt-1.5 text-sm text-slate-500">{helperText}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Input.displayName = 'Input'
|
||||
|
||||
export default Input
|
||||
|
||||
// Textarea component
|
||||
export interface TextareaProps
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
label?: string
|
||||
error?: string
|
||||
helperText?: string
|
||||
fullWidth?: boolean
|
||||
}
|
||||
|
||||
export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
(
|
||||
{ className, label, error, helperText, fullWidth = true, id, ...props },
|
||||
ref
|
||||
) => {
|
||||
const inputId = id || label?.toLowerCase().replace(/\s+/g, '-')
|
||||
|
||||
return (
|
||||
<div className={clsx(fullWidth && 'w-full')}>
|
||||
{label && (
|
||||
<label
|
||||
htmlFor={inputId}
|
||||
className="block text-sm font-medium text-slate-300 mb-1.5"
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<textarea
|
||||
ref={ref}
|
||||
id={inputId}
|
||||
className={clsx(
|
||||
'w-full px-4 py-2.5 bg-background-800 border rounded-lg',
|
||||
'text-white placeholder-slate-500',
|
||||
'focus:outline-none focus:ring-2 focus:border-transparent',
|
||||
'transition-all duration-200 resize-none',
|
||||
error
|
||||
? 'border-error-500 focus:ring-error-500'
|
||||
: 'border-slate-700 focus:ring-accent-500',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
{error && <p className="mt-1.5 text-sm text-error-500">{error}</p>}
|
||||
{helperText && !error && (
|
||||
<p className="mt-1.5 text-sm text-slate-500">{helperText}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Textarea.displayName = 'Textarea'
|
||||
Reference in New Issue
Block a user