forked from StellarSend/frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInput.tsx
More file actions
125 lines (114 loc) · 4.1 KB
/
Copy pathInput.tsx
File metadata and controls
125 lines (114 loc) · 4.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import React, { forwardRef } from 'react'
import { cn } from '@/lib/utils'
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string
error?: string
hint?: string
leftIcon?: React.ReactNode
rightElement?: React.ReactNode
fullWidth?: boolean
}
export const Input = forwardRef<HTMLInputElement, InputProps>(
(
{ label, error, hint, leftIcon, rightElement, fullWidth = false, className, id, ...props },
ref,
) => {
const inputId = id || label?.toLowerCase().replace(/\s+/g, '-')
return (
<div className={cn('flex flex-col gap-1.5', fullWidth && 'w-full')}>
{label && (
<label
htmlFor={inputId}
className="text-sm font-medium text-slate-300"
>
{label}
</label>
)}
<div className="relative flex items-center">
{leftIcon && (
<span className="absolute left-3 text-slate-400 pointer-events-none z-10">
{leftIcon}
</span>
)}
<input
ref={ref}
id={inputId}
className={cn(
'w-full bg-navy-800 border rounded-xl px-4 py-2.5 text-sm text-white placeholder:text-slate-500',
'transition-all duration-200 outline-none',
'focus:border-stellar-500 focus:ring-2 focus:ring-stellar-500/25',
'disabled:opacity-50 disabled:cursor-not-allowed',
'read-only:bg-navy-900 read-only:text-slate-400',
error
? 'border-danger-500 focus:border-danger-400 focus:ring-danger-500/25'
: 'border-navy-600 hover:border-navy-500',
leftIcon && 'pl-10',
rightElement && 'pr-24',
className,
)}
{...props}
/>
{rightElement && (
<div className="absolute right-2 flex items-center">{rightElement}</div>
)}
</div>
{error && (
<p className="text-xs text-danger-400 flex items-center gap-1 mt-0.5">
<span className="inline-block w-1 h-1 rounded-full bg-danger-400" />
{error}
</p>
)}
{hint && !error && (
<p className="text-xs text-slate-500">{hint}</p>
)}
</div>
)
},
)
Input.displayName = 'Input'
// ─── Select ───────────────────────────────────────────────────────────────────
interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
label?: string
error?: string
hint?: string
fullWidth?: boolean
options: { value: string; label: string }[]
}
export const Select = forwardRef<HTMLSelectElement, SelectProps>(
({ label, error, hint, fullWidth = false, options, className, id, ...props }, ref) => {
const selectId = id || label?.toLowerCase().replace(/\s+/g, '-')
return (
<div className={cn('flex flex-col gap-1.5', fullWidth && 'w-full')}>
{label && (
<label htmlFor={selectId} className="text-sm font-medium text-slate-300">
{label}
</label>
)}
<select
ref={ref}
id={selectId}
className={cn(
'w-full bg-navy-800 border rounded-xl px-4 py-2.5 text-sm text-white',
'transition-all duration-200 outline-none cursor-pointer',
'focus:border-stellar-500 focus:ring-2 focus:ring-stellar-500/25',
'disabled:opacity-50 disabled:cursor-not-allowed',
error
? 'border-danger-500'
: 'border-navy-600 hover:border-navy-500',
className,
)}
{...props}
>
{options.map((opt) => (
<option key={opt.value} value={opt.value} className="bg-navy-900">
{opt.label}
</option>
))}
</select>
{error && <p className="text-xs text-danger-400">{error}</p>}
{hint && !error && <p className="text-xs text-slate-500">{hint}</p>}
</div>
)
},
)
Select.displayName = 'Select'