11import * as Tooltip from '@radix-ui/react-tooltip'
22import { noop } from 'lodash'
33import Link from 'next/link'
4- import { ReactNode } from 'react'
5- import { Alert , Button , Input , Listbox } from 'ui'
4+ import { ReactNode , useState } from 'react'
5+ import {
6+ AlertDescription_Shadcn_ ,
7+ AlertTitle_Shadcn_ ,
8+ Alert_Shadcn_ ,
9+ Button ,
10+ CommandEmpty_Shadcn_ ,
11+ CommandGroup_Shadcn_ ,
12+ CommandInput_Shadcn_ ,
13+ CommandItem_Shadcn_ ,
14+ CommandList_Shadcn_ ,
15+ Command_Shadcn_ ,
16+ CriticalIcon ,
17+ Input ,
18+ PopoverContent_Shadcn_ ,
19+ PopoverTrigger_Shadcn_ ,
20+ Popover_Shadcn_ ,
21+ ScrollArea ,
22+ cn ,
23+ } from 'ui'
624
725import type { EnumeratedType } from 'data/enumerated-types/enumerated-types-query'
8- import { Calendar , Circle , ExternalLink , Hash , ListPlus , ToggleRight , Type } from 'lucide-react'
26+ import {
27+ Calendar ,
28+ Check ,
29+ ChevronsUpDown ,
30+ ExternalLink ,
31+ Hash ,
32+ ListPlus ,
33+ ToggleRight ,
34+ Type ,
35+ } from 'lucide-react'
36+
937import {
1038 POSTGRES_DATA_TYPES ,
1139 POSTGRES_DATA_TYPE_OPTIONS ,
@@ -16,8 +44,6 @@ import type { PostgresDataTypeOption } from '../SidePanelEditor.types'
1644interface ColumnTypeProps {
1745 value : string
1846 enumTypes : EnumeratedType [ ]
19- size ?: 'tiny' | 'small' | 'medium' | 'large' | 'xlarge'
20- layout ?: 'vertical' | 'horizontal'
2147 className ?: string
2248 error ?: any
2349 disabled ?: boolean
@@ -30,9 +56,6 @@ interface ColumnTypeProps {
3056const ColumnType = ( {
3157 value,
3258 enumTypes = [ ] ,
33- className,
34- size = 'medium' ,
35- layout,
3659 error,
3760 disabled = false ,
3861 showLabel = true ,
@@ -44,25 +67,43 @@ const ColumnType = ({
4467 const availableTypes = POSTGRES_DATA_TYPES . concat ( enumTypes . map ( ( type ) => type . name ) )
4568 const isAvailableType = value ? availableTypes . includes ( value ) : true
4669 const recommendation = RECOMMENDED_ALTERNATIVE_DATA_TYPE [ value ]
70+ const [ open , setOpen ] = useState ( false )
71+ console . log ( { availableTypes } )
72+
73+ const getOptionByName = ( name : string ) => {
74+ // handle built in types
75+ const pgOption = POSTGRES_DATA_TYPE_OPTIONS . find ( ( option ) => option . name === name )
76+ if ( pgOption ) return pgOption
77+
78+ // handle custom enums
79+ const enumType = enumTypes . find ( ( type ) => type . name === name )
80+ return enumType ? { ...enumType , type : 'enum' } : undefined
81+ }
4782
4883 const inferIcon = ( type : string ) => {
4984 switch ( type ) {
5085 case 'number' :
51- return < Hash size = { 16 } className = "text-foreground" strokeWidth = { 1.5 } />
86+ return < Hash size = { 14 } className = "text-foreground" strokeWidth = { 1.5 } />
5287 case 'time' :
53- return < Calendar size = { 16 } className = "text-foreground" strokeWidth = { 1.5 } />
88+ return < Calendar size = { 14 } className = "text-foreground" strokeWidth = { 1.5 } />
5489 case 'text' :
55- return < Type size = { 16 } className = "text-foreground" strokeWidth = { 1.5 } />
90+ return < Type size = { 14 } className = "text-foreground" strokeWidth = { 1.5 } />
5691 case 'json' :
5792 return (
5893 < div className = "text-foreground" style = { { padding : '0px 1px' } } >
5994 { '{ }' }
6095 </ div >
6196 )
97+ case 'jsonb' :
98+ return (
99+ < div className = "text-foreground" style = { { padding : '0px 1px' } } >
100+ { '{ }' }
101+ </ div >
102+ )
62103 case 'bool' :
63- return < ToggleRight size = { 16 } className = "text-foreground" strokeWidth = { 1.5 } />
104+ return < ToggleRight size = { 14 } className = "text-foreground" strokeWidth = { 1.5 } />
64105 default :
65- return < Circle size = { 16 } className = "text-foreground p-0.5 " strokeWidth = { 1.5 } />
106+ return < ListPlus size = { 16 } className = "text-foreground" strokeWidth = { 1.5 } />
66107 }
67108 }
68109
@@ -119,7 +160,6 @@ const ColumnType = ({
119160 layout = { showLabel ? 'horizontal' : undefined }
120161 className = "md:gap-x-0"
121162 size = "small"
122- icon = { inferIcon ( POSTGRES_DATA_TYPE_OPTIONS . find ( ( x ) => x . name === value ) ?. type ?? '' ) }
123163 value = { value }
124164 />
125165 </ Tooltip . Trigger >
@@ -143,105 +183,126 @@ const ColumnType = ({
143183 }
144184
145185 return (
146- < div className = "space-y-2" >
147- < Listbox
148- label = { showLabel ? 'Type' : '' }
149- layout = { layout || ( showLabel ? 'horizontal' : 'vertical' ) }
150- value = { value }
151- size = { size }
152- error = { error }
153- disabled = { disabled }
154- // @ts -ignore
155- descriptionText = { description }
156- className = { `${ className } ${ disabled ? 'column-type-disabled' : '' } rounded-md` }
157- onChange = { ( value : string ) => onOptionSelect ( value ) }
158- optionsWidth = { 480 }
159- >
160- < Listbox . Option key = "empty" value = "" label = "---" >
161- ---
162- </ Listbox . Option >
163-
164- { /*
165- Weird issue with Listbox here
166- 1. Can't do render conditionally (&&) within Listbox hence why using Fragment
167- 2. Can't wrap these 2 components within a Fragment conditional (enumTypes.length)
168- as selecting the enumType option will not render it in the Listbox component
169- */ }
170- { enumTypes . length > 0 ? (
171- < Listbox . Option disabled key = "header-1" value = "header-1" label = "header-1" >
172- Other Data Types
173- </ Listbox . Option >
174- ) : (
175- < > </ >
176- ) }
177-
178- { enumTypes . length > 0 ? (
179- // @ts -ignore
180- enumTypes . map ( ( enumType : PostgresType ) => (
181- < Listbox . Option
182- key = { enumType . name }
183- value = { enumType . name }
184- label = { enumType . name }
185- addOnBefore = { ( ) => {
186- return < ListPlus size = { 16 } className = "text-foreground" strokeWidth = { 1.5 } />
187- } }
188- >
189- < div className = "flex items-center space-x-4" >
190- < p className = "text-foreground" > { enumType . name } </ p >
191- { enumType . comment !== undefined && (
192- < p className = "text-foreground-lighter" > { enumType . comment } </ p >
193- ) }
186+ < div >
187+ < Popover_Shadcn_ open = { open } onOpenChange = { setOpen } >
188+ < PopoverTrigger_Shadcn_ asChild >
189+ < Button
190+ type = "default"
191+ role = "combobox"
192+ size = { 'small' }
193+ aria-expanded = { open }
194+ className = "w-full justify-between"
195+ iconRight = { < ChevronsUpDown className = "ml-2 h-4 w-4 shrink-0 opacity-50" /> }
196+ >
197+ { value ? (
198+ < div className = "flex gap-2 items-center" >
199+ < span > { inferIcon ( getOptionByName ( value ) ?. type ?? '' ) } </ span >
200+ { value }
194201 </ div >
195- </ Listbox . Option >
196- ) )
197- ) : (
198- < > </ >
199- ) }
202+ ) : (
203+ 'Choose a column type...'
204+ ) }
205+ </ Button >
206+ </ PopoverTrigger_Shadcn_ >
207+ < PopoverContent_Shadcn_ className = "w-[460px] p-0" side = "bottom" align = "center" >
208+ < ScrollArea className = "h-[335px]" >
209+ < Command_Shadcn_ >
210+ < CommandInput_Shadcn_ placeholder = "Search types..." />
211+ < CommandEmpty_Shadcn_ > Type not found.</ CommandEmpty_Shadcn_ >
200212
201- < Listbox . Option disabled value = "header-2" label = "header-2" >
202- PostgreSQL Data Types
203- </ Listbox . Option >
213+ < CommandList_Shadcn_ >
214+ < CommandGroup_Shadcn_ >
215+ { POSTGRES_DATA_TYPE_OPTIONS . map ( ( option : PostgresDataTypeOption ) => (
216+ < CommandItem_Shadcn_
217+ key = { option . name }
218+ value = { option . name }
219+ className = { cn ( 'relative' , option . name === value ? 'bg-surface-200' : '' ) }
220+ onSelect = { ( value : string ) => {
221+ onOptionSelect ( value )
222+ setOpen ( false )
223+ } }
224+ >
225+ < div className = "flex items-center gap-2 pr-6" >
226+ < span > { inferIcon ( option . type ) } </ span >
227+ < span className = "text-foreground" > { option . name } </ span >
228+ < span className = "text-foreground-lighter" > { option . description } </ span >
229+ </ div >
230+ < span className = "absolute right-3 top-2" >
231+ { option . name === value ? (
232+ < Check className = "text-brand-500" size = { 14 } />
233+ ) : (
234+ ''
235+ ) }
236+ </ span >
237+ </ CommandItem_Shadcn_ >
238+ ) ) }
239+ </ CommandGroup_Shadcn_ >
240+ { enumTypes . length > 0 && (
241+ < >
242+ < CommandItem_Shadcn_ > Other types</ CommandItem_Shadcn_ >
243+ < CommandGroup_Shadcn_ >
244+ { enumTypes . map ( ( option : any ) => (
245+ < CommandItem_Shadcn_
246+ key = { option . name }
247+ value = { option . name }
248+ className = { cn ( 'relative' , option . name === value ? 'bg-surface-200' : '' ) }
249+ onSelect = { ( value : string ) => {
250+ onOptionSelect ( value )
251+ setOpen ( false )
252+ } }
253+ >
254+ < div className = "flex items-center gap-2" >
255+ < div >
256+ < ListPlus size = { 16 } className = "text-foreground" strokeWidth = { 1.5 } />
257+ </ div >
258+ < span className = "text-foreground" > { option . name } </ span >
259+ { option . comment !== undefined && (
260+ < span title = { option . comment } className = "text-foreground-lighter" >
261+ { option . comment }
262+ </ span >
263+ ) }
264+ < span className = "flex items-center gap-1.5" >
265+ { option . name === value ? < Check size = { 13 } /> : '' }
266+ </ span >
267+ </ div >
268+ </ CommandItem_Shadcn_ >
269+ ) ) }
270+ </ CommandGroup_Shadcn_ >
271+ </ >
272+ ) }
273+ </ CommandList_Shadcn_ >
274+ </ Command_Shadcn_ >
275+ </ ScrollArea >
276+ </ PopoverContent_Shadcn_ >
277+ </ Popover_Shadcn_ >
204278
205- { POSTGRES_DATA_TYPE_OPTIONS . map ( ( option : PostgresDataTypeOption ) => (
206- < Listbox . Option
207- key = { option . name }
208- value = { option . name }
209- label = { option . name }
210- addOnBefore = { ( ) => inferIcon ( option . type ) }
211- >
212- < div className = "flex items-center space-x-4" >
213- < span className = "text-foreground" > { option . name } </ span >
214- < span className = "text-foreground-lighter" > { option . description } </ span >
215- </ div >
216- </ Listbox . Option >
217- ) ) }
218- </ Listbox >
219279 { showRecommendation && recommendation !== undefined && (
220- < Alert
221- withIcon
222- variant = "warning"
223- title = {
224- < >
225- It is recommended to use < code className = "text-xs" > { recommendation . alternative } </ code > { ' ' }
226- instead
227- </ >
228- }
229- >
230- < p >
231- Postgres recommends against using the data type < code className = "text-xs" > { value } </ code > { ' ' }
232- unless you have a very specific use case.
233- </ p >
234- < div className = "flex items-center space-x-2 mt-3" >
235- < Button asChild type = "default" icon = { < ExternalLink /> } >
236- < Link href = { recommendation . reference } target = "_blank" rel = "noreferrer" >
237- Read more
238- </ Link >
239- </ Button >
240- < Button type = "primary" onClick = { ( ) => onOptionSelect ( recommendation . alternative ) } >
241- Use { recommendation . alternative }
242- </ Button >
243- </ div >
244- </ Alert >
280+ < Alert_Shadcn_ variant = "warning" >
281+ < CriticalIcon />
282+ < AlertTitle_Shadcn_ >
283+ { ' ' }
284+ It is recommended to use < code className = "text-xs" >
285+ { recommendation . alternative }
286+ </ code > { ' ' }
287+ insteadn
288+ </ AlertTitle_Shadcn_ >
289+ < AlertDescription_Shadcn_ >
290+ < p >
291+ Postgres recommends against using the data type{ ' ' }
292+ < code className = "text-xs" > { value } </ code > unless you have a very specific use case.
293+ </ p >
294+ < div className = "flex items-center space-x-2 mt-3" >
295+ < Button asChild type = "default" icon = { < ExternalLink /> } >
296+ < Link href = { recommendation . reference } target = "_blank" rel = "noreferrer" >
297+ Read more
298+ </ Link >
299+ </ Button >
300+ < Button type = "primary" onClick = { ( ) => onOptionSelect ( recommendation . alternative ) } >
301+ Use { recommendation . alternative }
302+ </ Button >
303+ </ div >
304+ </ AlertDescription_Shadcn_ >
305+ </ Alert_Shadcn_ >
245306 ) }
246307 </ div >
247308 )
0 commit comments