1- import {
2- Flex ,
3- FormControl ,
4- IconButton ,
5- Input ,
6- InputGroup ,
7- InputRightElement ,
8- Tooltip ,
9- } from "@chakra-ui/react" ;
10- import { Button } from "chakra/button" ;
11- import { FormErrorMessage , FormLabel } from "chakra/form" ;
12- import { BanIcon , PlusIcon , TrashIcon , UploadIcon , XIcon } from "lucide-react" ;
1+ import { PlusIcon , TrashIcon , XIcon } from "lucide-react" ;
132import { useEffect } from "react" ;
143import {
154 type ArrayPath ,
@@ -25,7 +14,16 @@ import {
2514 type WatchObserver ,
2615} from "react-hook-form" ;
2716import type { ThirdwebClient } from "thirdweb" ;
28- import { FileInput } from "@/components/blocks/FileInput" ;
17+ import { Button } from "@/components/ui/button" ;
18+ import {
19+ FormControl ,
20+ FormField ,
21+ FormItem ,
22+ FormLabel ,
23+ FormMessage ,
24+ } from "@/components/ui/form" ;
25+ import { Input } from "@/components/ui/input" ;
26+ import { cn } from "@/lib/utils" ;
2927
3028type OptionalPropertiesInput = {
3129 [ key : string ] : string | number ;
@@ -46,17 +44,15 @@ interface IPropertiesFormControlProps<
4644 client : ThirdwebClient ;
4745}
4846
49- export const PropertiesFormControl = <
47+ export function PropertiesFormControl <
5048 TFieldValues extends IPropertyFieldValues ,
5149> ( {
5250 control,
53- register,
5451 watch,
5552 errors,
5653 setValue,
57- client,
58- } : React . PropsWithChildren < IPropertiesFormControlProps < TFieldValues > > ) => {
59- const { fields, append, remove, replace } = useFieldArray ( {
54+ } : React . PropsWithChildren < IPropertiesFormControlProps < TFieldValues > > ) {
55+ const { fields, append, remove } = useFieldArray ( {
6056 control,
6157 name : "attributes" as ArrayPath < TFieldValues > ,
6258 } ) ;
@@ -72,122 +68,116 @@ export const PropertiesFormControl = <
7268
7369 return (
7470 < div className = "flex flex-col gap-4" >
75- < Flex align = "center" direction = "row" justify = "space-between" >
76- < FormLabel m = { 0 } > Attributes</ FormLabel >
77- < Button
78- colorScheme = "red"
79- // biome-ignore lint/suspicious/noExplicitAny: FIXME
80- onClick = { ( ) => replace ( [ { trait_type : "" , value : "" } as any ] ) }
81- rightIcon = { < BanIcon className = "size-4" /> }
82- size = "xs"
83- variant = "outline"
84- >
85- Reset
86- </ Button >
87- </ Flex >
71+ < FormLabel > Attributes</ FormLabel >
8872 { fields . map ( ( field , index ) => {
8973 // biome-ignore lint/suspicious/noExplicitAny: FIXME
9074 const keyError = ( errors as any ) ?. attributes ?. [ index ] ?. trait_type
9175 ?. message as string | undefined ;
9276 // biome-ignore lint/suspicious/noExplicitAny: FIXME
9377 const valueError = ( errors as any ) ?. attributes ?. [ index ] ?. value
9478 ?. message as string | undefined ;
95- const isInvalid = ! ! ( keyError || valueError ) ;
79+ const _isInvalid = ! ! ( keyError || valueError ) ;
9680
9781 return (
9882 < div className = "flex flex-row items-center gap-2" key = { field . id } >
99- < FormControl
100- className = "flex flex-row items-start gap-3"
101- isInvalid = { isInvalid }
102- >
103- < FormControl isInvalid = { ! ! keyError } >
104- < Input
105- { ...register (
106- `attributes.${ index } .trait_type` as Path < TFieldValues > ,
107- ) }
108- placeholder = "trait_type"
109- />
110- < FormErrorMessage > { keyError } </ FormErrorMessage >
111- </ FormControl >
112- < FormControl isInvalid = { ! ! valueError } >
113- { watch (
114- `attributes.${ index } .value` as unknown as WatchObserver < TFieldValues > ,
115- ) instanceof File ? (
116- < InputGroup >
117- < Input
118- isDisabled
119- value = {
120- watch ( `attributes.${ index } .value` as Path < TFieldValues > )
121- . name
122- }
123- />
124- < InputRightElement >
125- < TrashIcon
126- className = "size-4 cursor-pointer text-red-300 hover:text-red-200"
127- onClick = { ( ) =>
128- setValue (
129- `attributes.${ index } .value` as Path < TFieldValues > ,
130- "" as PathValue < TFieldValues , Path < TFieldValues > > ,
131- )
132- }
83+ < div className = "flex flex-row items-start gap-3 flex-1" >
84+ < FormField
85+ control = { control }
86+ name = { `attributes.${ index } .trait_type` as Path < TFieldValues > }
87+ render = { ( { field : traitField } ) => (
88+ < FormItem className = "flex-1" >
89+ < FormControl >
90+ < Input
91+ { ...traitField }
92+ placeholder = "trait_type"
93+ className = { cn (
94+ "bg-card" ,
95+ keyError && "border-destructive" ,
96+ ) }
13397 />
134- </ InputRightElement >
135- </ InputGroup >
136- ) : (
137- < InputGroup >
138- < Input
139- { ...register (
140- `attributes.${ index } .value` as Path < TFieldValues > ,
98+ </ FormControl >
99+ { keyError && < FormMessage > { keyError } </ FormMessage > }
100+ </ FormItem >
101+ ) }
102+ />
103+
104+ < FormField
105+ control = { control }
106+ name = { `attributes.${ index } .value` as Path < TFieldValues > }
107+ render = { ( { field : valueField } ) => (
108+ < FormItem className = "flex-1" >
109+ < FormControl >
110+ { watch (
111+ `attributes.${ index } .value` as unknown as WatchObserver < TFieldValues > ,
112+ ) instanceof File ? (
113+ < div className = "relative" >
114+ < Input
115+ disabled
116+ value = {
117+ watch (
118+ `attributes.${ index } .value` as Path < TFieldValues > ,
119+ ) . name
120+ }
121+ className = "pr-10 bg-card"
122+ />
123+ < Button
124+ type = "button"
125+ variant = "ghost"
126+ size = "sm"
127+ className = "absolute right-0 top-0 h-full px-3 hover:bg-transparent"
128+ onClick = { ( ) =>
129+ setValue (
130+ `attributes.${ index } .value` as Path < TFieldValues > ,
131+ "" as PathValue <
132+ TFieldValues ,
133+ Path < TFieldValues >
134+ > ,
135+ )
136+ }
137+ >
138+ < TrashIcon className = "size-4 text-muted-foreground hover:text-destructive" />
139+ </ Button >
140+ </ div >
141+ ) : (
142+ < div className = "relative" >
143+ < Input
144+ { ...valueField }
145+ placeholder = "value"
146+ className = { `pr-10 bg-card ${ valueError ? "border-destructive" : "" } ` }
147+ />
148+ </ div >
141149 ) }
142- placeholder = "value"
143- />
144- < InputRightElement >
145- < Tooltip label = "Upload file" shouldWrapChildren >
146- < FileInput
147- client = { client }
148- setValue = { ( file ) => {
149- setValue (
150- `attributes.${ index } .value` as Path < TFieldValues > ,
151- file as PathValue <
152- TFieldValues ,
153- Path < TFieldValues >
154- > ,
155- ) ;
156- } }
157- >
158- < UploadIcon className = "size-4 text-muted-foreground" />
159- </ FileInput >
160- </ Tooltip >
161- </ InputRightElement >
162- </ InputGroup >
150+ </ FormControl >
151+ { valueError && < FormMessage > { valueError } </ FormMessage > }
152+ </ FormItem >
163153 ) }
164- < FormErrorMessage > { valueError } </ FormErrorMessage >
165- </ FormControl >
166- </ FormControl >
167- < IconButton
168- aria-label = "remove key value pair "
169- colorScheme = "red "
170- icon = { < XIcon /> }
154+ / >
155+ </ div >
156+ < Button
157+ type = "button"
158+ variant = "outline "
159+ size = "sm "
160+ className = "rounded-full p-0 size-10 bg-card"
171161 onClick = { ( ) => remove ( index ) }
172- size = "xs"
173- variant = "ghost"
174- / >
162+ >
163+ < XIcon className = "size-4" />
164+ </ Button >
175165 </ div >
176166 ) ;
177167 } ) }
178168 < div className = "flex flex-row gap-2" >
179169 < Button
180- colorScheme = "purple"
181- leftIcon = { < PlusIcon className = "size-5" /> }
170+ className = "rounded-full"
182171 onClick = { ( ) =>
183172 // biome-ignore lint/suspicious/noExplicitAny: FIXME
184173 append ( { trait_type : undefined , value : undefined } as any )
185174 }
186175 size = "sm"
187176 >
177+ < PlusIcon className = "size-4 mr-2" />
188178 Add Row
189179 </ Button >
190180 </ div >
191181 </ div >
192182 ) ;
193- } ;
183+ }
0 commit comments