Skip to content

Commit 582d1cc

Browse files
committed
clean up
1 parent f2ad500 commit 582d1cc

File tree

2 files changed

+125
-36
lines changed

2 files changed

+125
-36
lines changed

packages/ui/src/components/common/TokenSelector/ChainFilter.tsx

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
useRef,
77
useCallback
88
} from 'react'
9-
import { Dropdown, DropdownMenuItem } from '../../primitives/Dropdown.js'
9+
import { Dropdown } from '../../primitives/Dropdown.js'
1010
import { Button, Flex, Text, Box } from '../../primitives/index.js'
1111
import ChainIcon from '../../primitives/ChainIcon.js'
1212
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
@@ -163,7 +163,7 @@ const ChainFilter: FC<Props> = ({
163163
>
164164
{filteredChains ? (
165165
filteredChains.length > 0 ? (
166-
filteredChains.map((chain, idx) => {
166+
filteredChains.map((chain) => {
167167
const tag = 'tags' in chain ? chain.tags?.[0] : undefined
168168
return (
169169
<Flex
@@ -200,21 +200,27 @@ const ChainFilter: FC<Props> = ({
200200
) : (
201201
<>
202202
{allChainsOption && (
203-
<>
204-
<DropdownMenuItem
205-
onClick={() => {
206-
setOpen(false)
207-
onSelect(allChainsOption)
208-
setChainSearchInput('')
209-
}}
210-
css={{ p: '2' }}
211-
>
212-
<ChainFilterRow
213-
chain={allChainsOption}
214-
onAnalyticEvent={onAnalyticEvent}
215-
/>
216-
</DropdownMenuItem>
217-
</>
203+
<Flex
204+
onClick={() => {
205+
setOpen(false)
206+
onSelect(allChainsOption)
207+
setChainSearchInput('')
208+
}}
209+
css={{
210+
padding: '8px',
211+
borderRadius: 4,
212+
cursor: 'pointer',
213+
backgroundColor: 'modal-background',
214+
_hover: {
215+
backgroundColor: 'gray3'
216+
}
217+
}}
218+
>
219+
<ChainFilterRow
220+
chain={allChainsOption}
221+
onAnalyticEvent={onAnalyticEvent}
222+
/>
223+
</Flex>
218224
)}
219225

220226
{starredChains.length > 0 && (
@@ -387,7 +393,7 @@ const ChainFilterRow: FC<ChainFilterRowProps> = ({
387393
}
388394

389395
// Long press handlers for mobile
390-
const handleTouchStart = (e: React.TouchEvent) => {
396+
const handleTouchStart = (_e: React.TouchEvent) => {
391397
if (!chain.id) return
392398
const timer = setTimeout(() => {
393399
// Provide haptic feedback on long press

packages/ui/src/components/common/TokenSelector/CompactChainFilter.tsx

Lines changed: 101 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { type FC, useState, useMemo, forwardRef } from 'react'
1+
import {
2+
type FC,
3+
useState,
4+
useMemo,
5+
forwardRef,
6+
useRef,
7+
useEffect,
8+
useCallback
9+
} from 'react'
210
import { Flex, Text, Box, Button, ChainIcon } from '../../primitives/index.js'
311
import { Dropdown, DropdownMenuItem } from '../../primitives/Dropdown.js'
412
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
@@ -33,7 +41,11 @@ const ChainFilterTrigger = forwardRef<
3341
cursor: 'pointer',
3442
backgroundColor: 'dropdown-background',
3543
borderRadius: 'dropdown-border-radius',
36-
flexShrink: 0
44+
flexShrink: 0,
45+
'--focusColor': 'colors.focus-color',
46+
_focusVisible: {
47+
boxShadow: 'inset 0 0 0 2px var(--focusColor)'
48+
}
3749
}}
3850
>
3951
{value.id ? (
@@ -81,6 +93,7 @@ export const CompactChainFilter: FC<CompactChainFilterProps> = ({
8193
}) => {
8294
const [open, setOpen] = useState(false)
8395
const [chainSearchInput, setChainSearchInput] = useState('')
96+
const searchInputRef = useRef<HTMLInputElement | null>(null)
8497
const chainFuse = new Fuse(options, {
8598
includeScore: true,
8699
includeMatches: true,
@@ -107,6 +120,44 @@ export const CompactChainFilter: FC<CompactChainFilterProps> = ({
107120
return Array.from(uniqueChains.values())
108121
}, [chainSearchInput, chainFuse])
109122

123+
useEffect(() => {
124+
if (open) {
125+
requestAnimationFrame(() => {
126+
searchInputRef.current?.focus()
127+
})
128+
} else {
129+
setChainSearchInput('')
130+
}
131+
}, [open])
132+
133+
const focusDropdownItem = useCallback((position: 'first' | 'last') => {
134+
const container = searchInputRef.current?.closest(
135+
'[data-chain-dropdown]'
136+
) as HTMLElement | null
137+
if (!container) return
138+
139+
const items = Array.from(
140+
container.querySelectorAll<HTMLElement>('[data-chain-dropdown-item]')
141+
)
142+
if (items.length === 0) return
143+
144+
const target = position === 'first' ? items[0] : items[items.length - 1]
145+
target.focus()
146+
}, [])
147+
148+
const handleSearchKeyDown = useCallback(
149+
(event: React.KeyboardEvent<HTMLInputElement>) => {
150+
if (event.key === 'ArrowDown') {
151+
event.preventDefault()
152+
focusDropdownItem('first')
153+
} else if (event.key === 'ArrowUp') {
154+
event.preventDefault()
155+
focusDropdownItem('last')
156+
}
157+
},
158+
[focusDropdownItem]
159+
)
160+
110161
return (
111162
<Dropdown
112163
open={open}
@@ -123,11 +174,15 @@ export const CompactChainFilter: FC<CompactChainFilterProps> = ({
123174
}
124175
}}
125176
>
126-
<Flex direction="column" css={{ p: '2' }}>
177+
<Flex direction="column" css={{ p: '2' }} data-chain-dropdown="true">
127178
<ChainSearchInput
179+
ref={searchInputRef}
128180
value={chainSearchInput}
129181
onChange={setChainSearchInput}
130-
onKeyDown={(event) => event.stopPropagation()}
182+
onKeyDown={(event) => {
183+
event.stopPropagation()
184+
handleSearchKeyDown(event)
185+
}}
131186
/>
132187
<Flex
133188
direction="column"
@@ -143,20 +198,25 @@ export const CompactChainFilter: FC<CompactChainFilterProps> = ({
143198
filteredChains.map((chain) => {
144199
const tag = 'tags' in chain ? chain.tags?.[0] : undefined
145200
return (
146-
<Flex
201+
<DropdownMenuItem
147202
key={chain.id?.toString() ?? 'all-chains'}
148-
onClick={() => {
149-
setOpen(false)
203+
data-chain-dropdown-item
204+
onSelect={() => {
150205
onSelect(chain)
206+
setOpen(false)
151207
setChainSearchInput('')
152208
}}
153209
css={{
154210
padding: '8px',
155211
borderRadius: 4,
156212
cursor: 'pointer',
157213
backgroundColor: 'modal-background',
214+
outline: 'none',
158215
_hover: {
159216
backgroundColor: 'gray3'
217+
},
218+
_focus: {
219+
backgroundColor: 'gray3'
160220
}
161221
}}
162222
>
@@ -166,7 +226,7 @@ export const CompactChainFilter: FC<CompactChainFilterProps> = ({
166226
onToggleStar={onChainStarToggle}
167227
onAnalyticEvent={onAnalyticEvent}
168228
/>
169-
</Flex>
229+
</DropdownMenuItem>
170230
)
171231
})
172232
) : (
@@ -178,12 +238,25 @@ export const CompactChainFilter: FC<CompactChainFilterProps> = ({
178238
<>
179239
{allChainsOption && (
180240
<DropdownMenuItem
181-
onClick={() => {
241+
data-chain-dropdown-item
242+
onSelect={() => {
182243
setOpen(false)
183244
onSelect(allChainsOption)
184245
setChainSearchInput('')
185246
}}
186-
css={{ p: '2' }}
247+
css={{
248+
padding: '8px',
249+
borderRadius: 4,
250+
cursor: 'pointer',
251+
backgroundColor: 'modal-background',
252+
outline: 'none',
253+
_hover: {
254+
backgroundColor: 'gray3'
255+
},
256+
_focus: {
257+
backgroundColor: 'gray3'
258+
}
259+
}}
187260
>
188261
<ChainFilterRow
189262
chain={allChainsOption}
@@ -205,20 +278,25 @@ export const CompactChainFilter: FC<CompactChainFilterProps> = ({
205278
{starredChains.map((chain: ChainFilterValue) => {
206279
const tag = 'tags' in chain ? chain.tags?.[0] : undefined
207280
return (
208-
<Flex
281+
<DropdownMenuItem
209282
key={chain.id?.toString() ?? 'all-chains'}
210-
onClick={() => {
211-
setOpen(false)
283+
data-chain-dropdown-item
284+
onSelect={() => {
212285
onSelect(chain)
286+
setOpen(false)
213287
setChainSearchInput('')
214288
}}
215289
css={{
216290
padding: '8px',
217291
borderRadius: 4,
218292
cursor: 'pointer',
219293
backgroundColor: 'modal-background',
294+
outline: 'none',
220295
_hover: {
221296
backgroundColor: 'gray3'
297+
},
298+
_focus: {
299+
backgroundColor: 'gray3'
222300
}
223301
}}
224302
>
@@ -229,7 +307,7 @@ export const CompactChainFilter: FC<CompactChainFilterProps> = ({
229307
showStar={false}
230308
onAnalyticEvent={onAnalyticEvent}
231309
/>
232-
</Flex>
310+
</DropdownMenuItem>
233311
)
234312
})}
235313
</>
@@ -241,20 +319,25 @@ export const CompactChainFilter: FC<CompactChainFilterProps> = ({
241319
{alphabeticalChains.map((chain: ChainFilterValue) => {
242320
const tag = 'tags' in chain ? chain.tags?.[0] : undefined
243321
return (
244-
<Flex
322+
<DropdownMenuItem
245323
key={chain.id?.toString() ?? 'all-chains'}
246-
onClick={() => {
247-
setOpen(false)
324+
data-chain-dropdown-item
325+
onSelect={() => {
248326
onSelect(chain)
327+
setOpen(false)
249328
setChainSearchInput('')
250329
}}
251330
css={{
252331
padding: '8px',
253332
borderRadius: 4,
254333
cursor: 'pointer',
255334
backgroundColor: 'modal-background',
335+
outline: 'none',
256336
_hover: {
257337
backgroundColor: 'gray3'
338+
},
339+
_focus: {
340+
backgroundColor: 'gray3'
258341
}
259342
}}
260343
>
@@ -264,7 +347,7 @@ export const CompactChainFilter: FC<CompactChainFilterProps> = ({
264347
onToggleStar={onChainStarToggle}
265348
onAnalyticEvent={onAnalyticEvent}
266349
/>
267-
</Flex>
350+
</DropdownMenuItem>
268351
)
269352
})}
270353
</>

0 commit comments

Comments
 (0)