1+ import { useCallback , useEffect , useMemo , useState } from 'react' ;
2+ import { Flex , Text , Card , Box , IconButton , Tooltip , TextField } from '@radix-ui/themes' ;
3+ import { UpdateIcon , DividerHorizontalIcon , Cross2Icon , QuoteIcon , CubeIcon , TokensIcon , MagnifyingGlassIcon } from '@radix-ui/react-icons' ;
4+ import { useActiveWorkspaceId , useWebSocket } from '../../hooks' ;
5+ import type { Enum } from '../../types' ;
6+ import { errStr } from '../../utils' ;
7+ import HashIcon from '../HashIcon' ;
8+ // import EnumModal from './EnumModal';
9+
10+ interface EnumsPanelProps {
11+ scope : string ;
12+ }
13+
14+ export default function EnumsPanel ( { scope } : EnumsPanelProps ) {
15+ const { emit } = useWebSocket ( ) ;
16+ const activeId = useActiveWorkspaceId ( ) ;
17+ const [ enums , setEnums ] = useState < Enum [ ] > ( [ ] ) ;
18+ const [ isLoading , setIsLoading ] = useState ( false ) ;
19+ const [ fetchError , setFetchError ] = useState < string | null > ( null ) ;
20+ const [ searchQuery , setSearchQuery ] = useState ( '' ) ;
21+ const [ viewEnum , setViewEnum ] = useState < Enum | null > ( null ) ;
22+
23+ const fetchEnums = useCallback ( async ( ) => {
24+ setIsLoading ( true ) ;
25+ setFetchError ( null ) ;
26+ try {
27+ const response = await emit ( 'FETCH_ENUMS' , { id : activeId , scope } ) as Enum [ ] ;
28+ setEnums ( response || [ ] ) ;
29+ } catch ( err : unknown ) {
30+ console . error ( "Failed to fetch enums:" , err ) ;
31+ setFetchError ( errStr ( err , "Failed to fetch enums." ) ) ;
32+ } finally {
33+ setIsLoading ( false ) ;
34+ }
35+ } , [ activeId , emit , scope ] ) ;
36+
37+ useEffect ( ( ) => {
38+ queueMicrotask ( fetchEnums ) ;
39+ } , [ fetchEnums ] ) ;
40+
41+ const filtered = useMemo ( ( ) => {
42+ const cleanedQuery = searchQuery . trim ( ) . toLowerCase ( ) ;
43+ if ( ! cleanedQuery ) {
44+ return enums ;
45+ }
46+
47+ return enums . filter ( ( enu ) => {
48+ const nameMatch = enu . name ?. toLowerCase ( ) . includes ( cleanedQuery ) ;
49+
50+ return nameMatch ;
51+ } ) ;
52+ } , [ enums , searchQuery ] ) ;
53+
54+ return (
55+ < Flex direction = "column" gap = "3" >
56+ < Flex justify = "between" align = "center" mt = "2" >
57+ < Text size = "1" color = "gray" weight = "bold" mt = "2" >
58+ ENUMS ({ isLoading ? '...' : filtered . length } )
59+ </ Text >
60+ < Tooltip content = "Refresh enums list" >
61+ < IconButton
62+ size = "1"
63+ variant = "soft"
64+ color = "gray"
65+ disabled = { isLoading }
66+ onClick = { fetchEnums }
67+ style = { { cursor : isLoading ? 'not-allowed' : 'pointer' } }
68+ >
69+ < UpdateIcon width = "13" height = "13" className = { isLoading ? 'animate-spin' : '' } />
70+ </ IconButton >
71+ </ Tooltip >
72+ </ Flex >
73+
74+ { ( enums . length > 0 || searchQuery ) && (
75+ < TextField . Root
76+ placeholder = "Search name..."
77+ value = { searchQuery }
78+ onChange = { ( e ) => setSearchQuery ( e . target . value ) }
79+ size = "1"
80+ >
81+ < TextField . Slot >
82+ < MagnifyingGlassIcon height = "14" width = "14" />
83+ </ TextField . Slot >
84+ { searchQuery && (
85+ < TextField . Slot style = { { paddingRight : '4px' } } >
86+ < IconButton
87+ size = "1"
88+ variant = "ghost"
89+ color = "gray"
90+ onClick = { ( ) => setSearchQuery ( '' ) }
91+ style = { { cursor : 'pointer' , height : '16px' , width : '16px' } }
92+ >
93+ < Cross2Icon height = "12" width = "12" />
94+ </ IconButton >
95+ </ TextField . Slot >
96+ ) }
97+ </ TextField . Root >
98+ ) }
99+
100+ { fetchError && enums . length === 0 && ! isLoading && (
101+ < Box
102+ py = "3"
103+ px = "2"
104+ style = { {
105+ backgroundColor : 'var(--red-2)' ,
106+ borderRadius : 'var(--radius-2)' ,
107+ border : '1px dashed var(--red-4)' ,
108+ textAlign : 'center' ,
109+ } }
110+ >
111+ < Text size = "1" color = "red" > { fetchError } </ Text >
112+ </ Box >
113+ ) }
114+
115+ { ! fetchError && enums . length === 0 && ! isLoading && (
116+ < Box
117+ py = "3"
118+ px = "2"
119+ style = { {
120+ textAlign : 'center' ,
121+ border : '1px dashed var(--gray-5)' ,
122+ borderRadius : 'var(--radius-2)'
123+ } }
124+ >
125+ < Text size = "1" color = "gray" > No enums found in this scope.</ Text >
126+ </ Box >
127+ ) }
128+
129+ { ! fetchError && enums . length > 0 && filtered . length === 0 && (
130+ < Box
131+ py = "3"
132+ px = "2"
133+ style = { {
134+ textAlign : 'center' ,
135+ border : '1px dashed var(--gray-5)' ,
136+ borderRadius : 'var(--radius-2)'
137+ } }
138+ >
139+ < Text size = "1" color = "gray" > No matching enums match your query.</ Text >
140+ </ Box >
141+ ) }
142+
143+ { ! fetchError && isLoading && enums . length === 0 && (
144+ < Flex justify = "center" py = "2" >
145+ < Text size = "1" color = "gray" className = "animate-pulse" > Loading enums...</ Text >
146+ </ Flex >
147+ ) }
148+
149+ < Flex direction = "column" gap = "2" >
150+ { filtered . map ( ( enu ) => {
151+
152+ return (
153+ < Card
154+ key = { enu . name }
155+ size = "1"
156+ style = { {
157+ padding : '6px 8px' ,
158+ backgroundColor : 'var(--gray-2)' ,
159+ borderColor : 'var(--gray-4)' ,
160+ cursor : 'pointer' ,
161+ } }
162+ onClick = { ( ) => setViewEnum ( enu ) }
163+ >
164+ < Flex direction = "column" gap = "2" >
165+ < Flex align = "center" justify = "between" gap = "2" >
166+ < Tooltip content = { enu . name } >
167+ < Text
168+ size = "1"
169+ weight = "bold"
170+ truncate
171+ style = { {
172+ fontFamily : 'monospace' ,
173+ color : 'var(--gray-12)' ,
174+ minWidth : 0 ,
175+ flexGrow : 1
176+ } }
177+ >
178+ { enu . name }
179+ </ Text >
180+ </ Tooltip >
181+
182+ { enu . type === 'str' && < QuoteIcon color = 'var(--iris-5)' width = "14" height = "14" /> }
183+ { enu . type === 'int' && < HashIcon color = 'var(--iris-5)' width = "14" height = "14" /> }
184+ { enu . type === 'float' && < DividerHorizontalIcon color = 'var(--iris-5)' width = "14" height = "14" /> }
185+ { enu . type === 'bytes' && < TokensIcon color = 'var(--iris-5)' width = "14" height = "14" /> }
186+ { enu . type === 'thing' && < CubeIcon color = 'var(--iris-5)' width = "14" height = "14" /> }
187+ </ Flex >
188+ </ Flex >
189+ </ Card >
190+ ) ;
191+ } ) }
192+ </ Flex >
193+ { viewEnum && < > { null } </ > }
194+ { /* {viewEnum && (
195+ <EnumModal
196+ onClose={() => setViewEnum(null)}
197+ scope={scope}
198+ enum={viewEnum}
199+ />
200+ )} */ }
201+ </ Flex >
202+ ) ;
203+ }
0 commit comments