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" ;
13
2
import { useEffect } from "react" ;
14
3
import {
15
4
type ArrayPath ,
@@ -25,7 +14,16 @@ import {
25
14
type WatchObserver ,
26
15
} from "react-hook-form" ;
27
16
import 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" ;
29
27
30
28
type OptionalPropertiesInput = {
31
29
[ key : string ] : string | number ;
@@ -46,17 +44,15 @@ interface IPropertiesFormControlProps<
46
44
client : ThirdwebClient ;
47
45
}
48
46
49
- export const PropertiesFormControl = <
47
+ export function PropertiesFormControl <
50
48
TFieldValues extends IPropertyFieldValues ,
51
49
> ( {
52
50
control,
53
- register,
54
51
watch,
55
52
errors,
56
53
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 ( {
60
56
control,
61
57
name : "attributes" as ArrayPath < TFieldValues > ,
62
58
} ) ;
@@ -72,122 +68,116 @@ export const PropertiesFormControl = <
72
68
73
69
return (
74
70
< 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 >
88
72
{ fields . map ( ( field , index ) => {
89
73
// biome-ignore lint/suspicious/noExplicitAny: FIXME
90
74
const keyError = ( errors as any ) ?. attributes ?. [ index ] ?. trait_type
91
75
?. message as string | undefined ;
92
76
// biome-ignore lint/suspicious/noExplicitAny: FIXME
93
77
const valueError = ( errors as any ) ?. attributes ?. [ index ] ?. value
94
78
?. message as string | undefined ;
95
- const isInvalid = ! ! ( keyError || valueError ) ;
79
+ const _isInvalid = ! ! ( keyError || valueError ) ;
96
80
97
81
return (
98
82
< 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
+ ) }
133
97
/>
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 >
141
149
) }
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 >
163
153
) }
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"
171
161
onClick = { ( ) => remove ( index ) }
172
- size = "xs"
173
- variant = "ghost"
174
- / >
162
+ >
163
+ < XIcon className = "size-4" />
164
+ </ Button >
175
165
</ div >
176
166
) ;
177
167
} ) }
178
168
< div className = "flex flex-row gap-2" >
179
169
< Button
180
- colorScheme = "purple"
181
- leftIcon = { < PlusIcon className = "size-5" /> }
170
+ className = "rounded-full"
182
171
onClick = { ( ) =>
183
172
// biome-ignore lint/suspicious/noExplicitAny: FIXME
184
173
append ( { trait_type : undefined , value : undefined } as any )
185
174
}
186
175
size = "sm"
187
176
>
177
+ < PlusIcon className = "size-4 mr-2" />
188
178
Add Row
189
179
</ Button >
190
180
</ div >
191
181
</ div >
192
182
) ;
193
- } ;
183
+ }
0 commit comments