11"use client" ;
22
3+ import { XIcon } from "lucide-react" ;
4+ import { AbiItem } from "viem" ;
5+ import { useDeferredValue , useMemo , useState } from "react" ;
36import JsonView from "react18-json-view" ;
47import { CopyButton } from "../../../../../../components/CopyButton" ;
8+ import { Checkbox } from "../../../../../../components/ui/Checkbox" ;
9+ import { Input } from "../../../../../../components/ui/Input" ;
510import { Skeleton } from "../../../../../../components/ui/Skeleton" ;
611import { useWorldAbiQuery } from "../../../../queries/useWorldAbiQuery" ;
712
13+ const filterTypes = [ "function" , "event" , "error" ] as const ;
14+
815export function AbiExplorer ( ) {
916 const { data, isLoading, isError } = useWorldAbiQuery ( ) ;
17+ const [ filterValue , setFilterValue ] = useState ( "" ) ;
18+ const [ selectedTypes , setSelectedTypes ] = useState < Set < AbiItem [ "type" ] > > ( new Set ( filterTypes ) ) ;
19+ const deferredFilterValue = useDeferredValue ( filterValue ) ;
20+
21+ const hasActiveFilters = filterValue || selectedTypes . size !== filterTypes . length ;
22+
23+ const filteredAbi = useMemo ( ( ) => {
24+ if ( ! data ?. abi ) return [ ] ;
25+
26+ const filtered = data . abi . filter ( ( item : AbiItem ) => {
27+ if ( ! selectedTypes . has ( item . type ) ) return false ;
28+
29+ if ( deferredFilterValue ) {
30+ const itemString = JSON . stringify ( item ) . toLowerCase ( ) ;
31+ return itemString . includes ( deferredFilterValue . toLowerCase ( ) ) ;
32+ }
33+ return true ;
34+ } ) ;
35+
36+ return filtered ;
37+ } , [ data ?. abi , deferredFilterValue , selectedTypes ] ) ;
38+
39+ const handleTypeToggle = ( type : AbiItem [ "type" ] ) => {
40+ const newSelectedTypes = new Set ( selectedTypes ) ;
41+ if ( newSelectedTypes . has ( type ) ) {
42+ newSelectedTypes . delete ( type ) ;
43+ } else {
44+ newSelectedTypes . add ( type ) ;
45+ }
46+ setSelectedTypes ( newSelectedTypes ) ;
47+ } ;
48+
49+ const handleClearFilters = ( ) => {
50+ setFilterValue ( "" ) ;
51+ setSelectedTypes ( new Set ( filterTypes ) ) ;
52+ } ;
1053
1154 if ( isLoading ) {
1255 return < Skeleton className = "h-[106px] w-full" /> ;
@@ -17,9 +60,38 @@ export function AbiExplorer() {
1760 return (
1861 < div className = "space-y-4" >
1962 < h4 className = "font-semibold uppercase" > World ABI</ h4 >
63+
64+ < div className = "space-y-3" >
65+ < Input
66+ type = "search"
67+ placeholder = "Filter ABI…"
68+ value = { filterValue }
69+ onChange = { ( evt ) => setFilterValue ( evt . target . value ) }
70+ />
71+
72+ < div className = "flex flex-wrap gap-4" >
73+ { filterTypes . map ( ( type ) => (
74+ < label key = { type } className = "flex items-center gap-2 text-sm" >
75+ < Checkbox checked = { selectedTypes . has ( type ) } onCheckedChange = { ( ) => handleTypeToggle ( type ) } />
76+ < span className = "capitalize" > { `${ type } s` } </ span >
77+ </ label >
78+ ) ) }
79+
80+ { hasActiveFilters && (
81+ < button
82+ type = "button"
83+ onClick = { handleClearFilters }
84+ className = "flex items-center gap-1 text-sm text-muted-foreground"
85+ >
86+ < XIcon className = "h-4 w-4" /> Clear filters
87+ </ button >
88+ ) }
89+ </ div >
90+ </ div >
91+
2092 < pre className = "text-md relative mb-4 rounded border border-white/20 p-3 text-sm" >
21- < JsonView src = { data ?. abi } theme = "a11y" />
22- < CopyButton value = { JSON . stringify ( data ?. abi , null , 2 ) } className = "absolute right-1.5 top-1.5" />
93+ < JsonView src = { filteredAbi } theme = "a11y" />
94+ < CopyButton value = { JSON . stringify ( filteredAbi , null , 2 ) } className = "absolute right-1.5 top-1.5" />
2395 </ pre >
2496 </ div >
2597 ) ;
0 commit comments