@@ -8,11 +8,15 @@ import * as ShadcnSelect from '@/components/ui/select';
88import { Slider as ShadcnSlider } from '@/components/ui/slider' ;
99import { Switch as ShadcnSwitch } from '@/components/ui/switch' ;
1010import { Textarea as ShadcnTextarea } from '@/components/ui/textarea' ;
11+ import type { CheckedState } from '@radix-ui/react-checkbox' ;
1112import { Decimal } from '@sovryn/slayer-shared' ;
1213import { Loader2Icon } from 'lucide-react' ;
13- import { useState } from 'react' ;
14+ import { useEffect , useState , type ReactNode } from 'react' ;
1415import type { GetBalanceData } from 'wagmi/query' ;
16+ import { AmountRenderer } from './ui/amount-renderer' ;
17+ import { Checkbox } from './ui/checkbox' ;
1518import { Field , FieldDescription , FieldError , FieldLabel } from './ui/field' ;
19+ import { InputGroup , InputGroupAddon , InputGroupInput } from './ui/input-group' ;
1620
1721export function SubscribeButton ( { label } : { label : string } ) {
1822 const form = useFormContext ( ) ;
@@ -24,7 +28,7 @@ export function SubscribeButton({ label }: { label: string }) {
2428 < Button
2529 type = "submit"
2630 disabled = { isSubmitting || ! isFormValid }
27- form = { form . formId ( ) }
31+ form = { form . formId }
2832 >
2933 < Loader2Icon
3034 className = { `mr-2 h-4 w-4 animate-spin ${ isSubmitting ? '' : 'hidden' } ` }
@@ -60,7 +64,7 @@ export function TextField({
6064 placeholder,
6165 description,
6266} : {
63- label : string ;
67+ label : ReactNode ;
6468 placeholder ?: string ;
6569 description ?: string ;
6670} ) {
@@ -69,8 +73,9 @@ export function TextField({
6973
7074 return (
7175 < Field >
72- < FieldLabel htmlFor = { label } > { label } </ FieldLabel >
76+ < FieldLabel htmlFor = { field . name } > { label } </ FieldLabel >
7377 < Input
78+ id = { field . name }
7479 value = { field . state . value }
7580 placeholder = { placeholder }
7681 onBlur = { field . handleBlur }
@@ -87,7 +92,7 @@ export function TextArea({
8792 rows = 3 ,
8893 description,
8994} : {
90- label : string ;
95+ label : ReactNode ;
9196 rows ?: number ;
9297 description ?: string ;
9398} ) {
@@ -96,9 +101,9 @@ export function TextArea({
96101
97102 return (
98103 < Field >
99- < FieldLabel htmlFor = { label } > { label } </ FieldLabel >
104+ < FieldLabel htmlFor = { field . name } > { label } </ FieldLabel >
100105 < ShadcnTextarea
101- id = { label }
106+ id = { field . name }
102107 value = { field . state . value }
103108 onBlur = { field . handleBlur }
104109 rows = { rows }
@@ -116,7 +121,7 @@ export function Select({
116121 placeholder,
117122 description,
118123} : {
119- label : string ;
124+ label : ReactNode ;
120125 values : Array < { label : string ; value : string } > ;
121126 placeholder ?: string ;
122127 description ?: string ;
@@ -126,7 +131,7 @@ export function Select({
126131
127132 return (
128133 < Field >
129- < FieldLabel htmlFor = { label } > { label } </ FieldLabel >
134+ < FieldLabel htmlFor = { field . name } > { label } </ FieldLabel >
130135 < ShadcnSelect . Select
131136 name = { field . name }
132137 value = { field . state . value }
@@ -156,17 +161,17 @@ export function Slider({
156161 label,
157162 description,
158163} : {
159- label : string ;
164+ label : ReactNode ;
160165 description ?: string ;
161166} ) {
162167 const field = useFieldContext < number > ( ) ;
163168 const errors = useStore ( field . store , ( state ) => state . meta . errors ) ;
164169
165170 return (
166171 < Field >
167- < FieldLabel htmlFor = { label } > { label } </ FieldLabel >
172+ < FieldLabel htmlFor = { field . name } > { label } </ FieldLabel >
168173 < ShadcnSlider
169- id = { label }
174+ id = { field . name }
170175 onBlur = { field . handleBlur }
171176 value = { [ field . state . value ] }
172177 onValueChange = { ( value ) => field . handleChange ( value [ 0 ] ) }
@@ -181,7 +186,7 @@ export function Switch({
181186 label,
182187 description,
183188} : {
184- label : string ;
189+ label : ReactNode ;
185190 description ?: string ;
186191} ) {
187192 const field = useFieldContext < boolean > ( ) ;
@@ -191,19 +196,49 @@ export function Switch({
191196 < Field >
192197 < div className = "flex items-center gap-2" >
193198 < ShadcnSwitch
194- id = { label }
199+ id = { field . name }
195200 onBlur = { field . handleBlur }
196201 checked = { field . state . value }
197202 onCheckedChange = { ( checked ) => field . handleChange ( checked ) }
198203 />
199- < FieldLabel htmlFor = { label } > { label } </ FieldLabel >
204+ < FieldLabel htmlFor = { field . name } > { label } </ FieldLabel >
200205 </ div >
201206 { description && < FieldDescription > { description } </ FieldDescription > }
202207 { field . state . meta . isTouched && < ErrorMessages errors = { errors } /> }
203208 </ Field >
204209 ) ;
205210}
206211
212+ export function CheckBox ( {
213+ label,
214+ description,
215+ } : {
216+ label : ReactNode ;
217+ description ?: string ;
218+ } ) {
219+ const field = useFieldContext < CheckedState > ( ) ;
220+ const errors = useStore ( field . store , ( state ) => state . meta . errors ) ;
221+
222+ return (
223+ < Field >
224+ < div className = "flex items-start gap-3" >
225+ < Checkbox
226+ id = { field . name }
227+ checked = { field . state . value }
228+ onCheckedChange = { ( checked ) => field . handleChange ( checked ) }
229+ onBlur = { field . handleBlur }
230+ className = "mt-1"
231+ />
232+ < div className = "grid gap-2" >
233+ < FieldLabel htmlFor = { field . name } > { label } </ FieldLabel >
234+ { description && < FieldDescription > { description } </ FieldDescription > }
235+ </ div >
236+ </ div >
237+ { field . state . meta . isTouched && < ErrorMessages errors = { errors } /> }
238+ </ Field >
239+ ) ;
240+ }
241+
207242const tryDecimalValue = ( input : string ) : string => {
208243 try {
209244 if ( input ) {
@@ -221,11 +256,13 @@ export function AmountField({
221256 placeholder,
222257 description,
223258 balance,
259+ addonRight,
224260} : {
225- label : string ;
261+ label : ReactNode ;
226262 placeholder ?: string ;
227263 description ?: string ;
228- balance ?: GetBalanceData ;
264+ balance ?: Omit < GetBalanceData , 'formatted' > ;
265+ addonRight ?: ReactNode ;
229266} ) {
230267 const field = useFieldContext < string > ( ) ;
231268 const errors = useStore ( field . store , ( state ) => state . meta . errors ) ;
@@ -236,28 +273,71 @@ export function AmountField({
236273
237274 const handleChange = ( input : string ) => {
238275 setRenderedValue ( input ) ;
239- field . handleChange ( tryDecimalValue ( input ) ) ;
276+ field . setValue ( tryDecimalValue ( input ) as never , {
277+ dontRunListeners : true ,
278+ } ) ;
240279 } ;
241280
281+ useEffect ( ( ) => {
282+ const unsub = field . store . subscribe ( ( { prevVal, currentVal } ) => {
283+ if ( prevVal . value !== currentVal . value ) {
284+ setRenderedValue ( tryDecimalValue ( currentVal . value ) ) ;
285+ }
286+ } ) ;
287+
288+ return unsub ;
289+ } , [ ] ) ;
290+
242291 return (
243292 < Field >
244- < FieldLabel htmlFor = { label } >
245- { label }
246-
247- { balance && (
248- < span className = "ml-2 text-sm font-normal text-gray-400" >
249- (Balance:{ ' ' }
250- { Decimal . from ( balance . value , balance . decimals ) . toFormatted ( ) } { ' ' }
251- { balance . symbol } )
252- </ span >
293+ < FieldLabel htmlFor = { field . name } >
294+ { balance ? (
295+ < >
296+ < div className = "w-full flex flex-row gap-4 justify-between items-center" >
297+ < span > { label } </ span >
298+ < Button
299+ variant = "link"
300+ size = "sm"
301+ className = "p-0"
302+ onClick = { ( ) => {
303+ field . setValue (
304+ Decimal . from ( balance . value ) . toString ( balance . decimals ) ,
305+ ) ;
306+ } }
307+ >
308+ < span >
309+ (max:
310+ < AmountRenderer
311+ value = { Decimal . from (
312+ balance . value ,
313+ balance . decimals ,
314+ ) . toString ( ) }
315+ suffix = { balance . symbol }
316+ showApproxSign
317+ />
318+ )
319+ </ span >
320+ </ Button >
321+ </ div >
322+ </ >
323+ ) : (
324+ < > { label } </ >
253325 ) }
254326 </ FieldLabel >
255- < Input
256- value = { renderedValue }
257- placeholder = { placeholder }
258- onBlur = { field . handleBlur }
259- onChange = { ( e ) => handleChange ( e . target . value ) }
260- />
327+ < InputGroup >
328+ < InputGroupInput
329+ id = { field . name }
330+ value = { renderedValue }
331+ placeholder = { placeholder }
332+ onBlur = { field . handleBlur }
333+ onChange = { ( e ) => handleChange ( e . target . value ) }
334+ type = "number"
335+ step = "0.00001"
336+ />
337+ { addonRight && (
338+ < InputGroupAddon align = "inline-end" > { addonRight } </ InputGroupAddon >
339+ ) }
340+ </ InputGroup >
261341 { description && < FieldDescription > { description } </ FieldDescription > }
262342 { field . state . meta . isTouched && < ErrorMessages errors = { errors } /> }
263343 </ Field >
0 commit comments