Skip to content

Commit c203967

Browse files
committed
Searchable Dropdown on chains, setSelectedChain, add fuse.js
1 parent 39ffcb5 commit c203967

File tree

9 files changed

+121
-13
lines changed

9 files changed

+121
-13
lines changed

apps/contract-verification/src/app/AppContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ export const AppContext = React.createContext({
77
console.log('Calling Set Theme Type')
88
},
99
chains: [],
10+
selectedChain: null,
11+
setSelectedChain: (chain: string) => {},
1012
})

apps/contract-verification/src/app/app.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const plugin = new ContractVerificationPluginClient()
1414
const App = () => {
1515
const [themeType, setThemeType] = useState<ThemeType>('dark')
1616
const [chains, setChains] = useState([]) // State to hold the chains data
17+
const [selectedChain, setSelectedChain] = useState(null)
1718

1819
useEffect(() => {
1920
// Fetch chains.json and update state
@@ -24,7 +25,7 @@ const App = () => {
2425
}, [])
2526

2627
return (
27-
<AppContext.Provider value={{themeType, setThemeType, chains}}>
28+
<AppContext.Provider value={{themeType, setThemeType, chains, selectedChain, setSelectedChain}}>
2829
<DisplayRoutes />
2930
</AppContext.Provider>
3031
)

apps/contract-verification/src/app/components/Input/Dropdown/index.tsx renamed to apps/contract-verification/src/app/components/Dropdown.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react'
22

33
interface DropdownItem {
44
value: string
5-
text: string
5+
name: string
66
}
77

88
interface DropdownProps {
@@ -18,7 +18,7 @@ export const Dropdown: React.FC<DropdownProps> = ({label, items, id}) => {
1818
<select className="form-control custom-select pr-4" id={id}>
1919
{items.map((item, index) => (
2020
<option value={item.value} key={index}>
21-
{item.text}
21+
{item.name}
2222
</option>
2323
))}
2424
</select>
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import React, {useState, useEffect, useRef} from 'react'
2+
import Fuse from 'fuse.js'
3+
4+
interface DropdownItem {
5+
value: string
6+
name: string
7+
}
8+
9+
interface DropdownProps {
10+
label: string
11+
options: DropdownItem[]
12+
id: string
13+
value: string
14+
onChange: (value: string) => void
15+
}
16+
17+
export const SearchableDropdown: React.FC<DropdownProps> = ({options, label, id, value, onChange}) => {
18+
const [searchTerm, setSearchTerm] = useState('')
19+
const [selectedOption, setSelectedOption] = useState<DropdownItem | null>(null)
20+
const [isOpen, setIsOpen] = useState(false)
21+
const [filteredOptions, setFilteredOptions] = useState<DropdownItem[]>(options)
22+
const dropdownRef = useRef<HTMLDivElement>(null)
23+
24+
const fuse = new Fuse(options, {
25+
keys: ['name'],
26+
threshold: 0.3,
27+
})
28+
29+
useEffect(() => {
30+
if (searchTerm === '') {
31+
setFilteredOptions(options)
32+
} else {
33+
const result = fuse.search(searchTerm)
34+
setFilteredOptions(result.map(({item}) => item))
35+
}
36+
}, [searchTerm, options])
37+
38+
// Close dropdown when user clicks outside
39+
useEffect(() => {
40+
const handleClickOutside = (event: MouseEvent) => {
41+
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
42+
setIsOpen(false)
43+
}
44+
}
45+
document.addEventListener('mousedown', handleClickOutside)
46+
return () => {
47+
document.removeEventListener('mousedown', handleClickOutside)
48+
}
49+
}, [])
50+
51+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
52+
setSearchTerm(e.target.value)
53+
onChange(e.target.value)
54+
setIsOpen(true)
55+
}
56+
57+
const handleOptionClick = (option: DropdownItem) => {
58+
setSelectedOption(option)
59+
setSearchTerm(option.name)
60+
setIsOpen(false)
61+
}
62+
63+
const openDropdown = () => {
64+
setIsOpen(true)
65+
setSearchTerm('')
66+
}
67+
68+
if (!options || options.length === 0) {
69+
return (
70+
<div className="dropdown">
71+
<label htmlFor={id}>{label}</label>
72+
<div>Loading chains...</div>
73+
</div>
74+
)
75+
}
76+
77+
return (
78+
<div className="dropdown" ref={dropdownRef}>
79+
{' '}
80+
{/* Add ref here */}
81+
<label htmlFor={id}>{label}</label>
82+
<input type="text" value={searchTerm} onChange={handleInputChange} onClick={openDropdown} placeholder="Select a chain" className="form-control" />
83+
{isOpen && (
84+
<ul className="dropdown-menu show w-100" style={{maxHeight: '400px', overflowY: 'auto'}}>
85+
{filteredOptions.map((option) => (
86+
<li key={option.value} onClick={() => handleOptionClick(option)} className={`dropdown-item ${selectedOption === option ? 'active' : ''}`} style={{cursor: 'pointer', whiteSpace: 'normal'}}>
87+
{option.name}
88+
</li>
89+
))}
90+
</ul>
91+
)}
92+
</div>
93+
)
94+
}

apps/contract-verification/src/app/components/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export {NavMenu} from './NavMenu'
2+
export {Dropdown} from './Dropdown'
3+
export {SearchableDropdown} from './SearchableDropdown'

apps/contract-verification/src/app/views/HomeView.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import React from 'react'
22

33
import {AppContext} from '../AppContext'
4-
import {Dropdown} from '../components/Input/Dropdown'
4+
import {Dropdown} from '../components'
5+
import {SearchableDropdown} from '../components'
56

67
export const HomeView = () => {
7-
const {chains} = React.useContext(AppContext)
8+
const {chains, selectedChain, setSelectedChain} = React.useContext(AppContext)
89

9-
const ethereumChainIds = [1, 11155111, 17000]
10+
const ethereumChainIds = [1, 3, 4, 5, 11155111, 17000]
1011

1112
// Add Ethereum chains to the head of the chains list. Sort the rest alphabetically
1213
const dropdownChains = chains
13-
.map((chain) => ({value: chain.chainId, text: `${chain.name} (${chain.chainId})`}))
14+
.map((chain) => ({value: chain.chainId, name: `${chain.title || chain.name} (${chain.chainId})`}))
1415
.sort((a, b) => {
1516
const isAInEthereum = ethereumChainIds.includes(a.value)
1617
const isBInEthereum = ethereumChainIds.includes(b.value)
@@ -19,7 +20,7 @@ export const HomeView = () => {
1920
if (!isAInEthereum && isBInEthereum) return 1
2021
if (isAInEthereum && isBInEthereum) return ethereumChainIds.indexOf(a.value) - ethereumChainIds.indexOf(b.value)
2122

22-
return a.text.localeCompare(b.text)
23+
return a.name.localeCompare(b.name)
2324
})
2425

2526
return (
@@ -31,17 +32,19 @@ export const HomeView = () => {
3132
</p>
3233
</div>
3334
<div>
34-
<Dropdown label="Network" items={dropdownChains} id="network-dropdown" />
35+
<SearchableDropdown label="Contract Chain" options={dropdownChains} id="network-dropdown" value={selectedChain} onChange={setSelectedChain} />
36+
3537
<div className="form-group">
3638
<label htmlFor="contract-address">Contract Address</label>
3739
<input type="text" className="form-control" id="contract-address" placeholder="0x2738d13E81e..." />
3840
</div>
41+
3942
<Dropdown
4043
label="Contract Name"
4144
items={[
42-
{value: 'ERC20', text: 'ERC20'},
43-
{value: 'ERC721', text: 'ERC721'},
44-
{value: 'ERC1155', text: 'ERC1155'},
45+
{value: 'ERC20', name: 'ERC20'},
46+
{value: 'ERC721', name: 'ERC721'},
47+
{value: 'ERC1155', name: 'ERC1155'},
4548
]}
4649
id="contract-name-dropdown"
4750
/>

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
"formik": "^2.4.5",
149149
"from-exponential": "1.1.1",
150150
"fs-extra": "^3.0.1",
151+
"fuse.js": "^7.0.0",
151152
"ganache": "^7.9.1",
152153
"html-react-parser": "^3.0.4",
153154
"http-server": "^14.1.1",

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15047,6 +15047,11 @@ functions-have-names@^1.2.2, functions-have-names@^1.2.3:
1504715047
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
1504815048
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
1504915049

15050+
fuse.js@^7.0.0:
15051+
version "7.0.0"
15052+
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.0.0.tgz#6573c9fcd4c8268e403b4fc7d7131ffcf99a9eb2"
15053+
integrity sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==
15054+
1505015055
galactus@^0.2.1:
1505115056
version "0.2.1"
1505215057
resolved "https://registry.yarnpkg.com/galactus/-/galactus-0.2.1.tgz#cbed2d20a40c1f5679a35908e2b9415733e78db9"

0 commit comments

Comments
 (0)