1
+ import {
2
+ FloatingPortal ,
3
+ offset ,
4
+ useClick ,
5
+ useDismiss ,
6
+ useFloating ,
7
+ useHover ,
8
+ useInteractions ,
9
+ useTransitionStyles ,
10
+ } from "@floating-ui/react" ;
1
11
import { TokenCategory , TokenSymbol } from "../types" ;
2
12
import { getTokenLogoSrc } from "../utils" ;
3
- import { useState } from "react" ;
13
+ import { useMemo , useState } from "react" ;
14
+ import { safePolygon } from "@floating-ui/react" ;
15
+ import { useMediaQuery } from "../hooks" ;
4
16
5
17
interface Value {
6
18
logo : string ;
@@ -17,13 +29,17 @@ interface Props {
17
29
export default function TransferTokenSelect ( { value, options, onChange } : Props ) {
18
30
const [ hoveIndex , setHoverIndex ] = useState ( - 1 ) ;
19
31
32
+ const isPC = useMediaQuery ( "lg" ) ;
33
+ const nonMoreTokensAmount = useMemo ( ( ) => ( isPC ? 5 : 4 ) , [ isPC ] ) ;
34
+
20
35
return (
21
36
< div className = "gap-medium px-medium flex items-center" >
22
37
< TokenImage token = { value } active />
23
38
< span className = "text-base font-bold text-white" > { value . symbol } </ span >
24
39
< div className = "gap-medium group ml-2 flex items-center" >
25
40
{ options
26
41
. filter ( ( option ) => option . symbol !== value . symbol )
42
+ . slice ( 0 , nonMoreTokensAmount )
27
43
. map ( ( option , index ) => (
28
44
< TokenImage
29
45
key = { option . symbol }
@@ -34,6 +50,12 @@ export default function TransferTokenSelect({ value, options, onChange }: Props)
34
50
onHoverChange = { setHoverIndex }
35
51
/>
36
52
) ) }
53
+ { nonMoreTokensAmount + 1 < options . length && (
54
+ < MoreTokens
55
+ options = { options . filter ( ( option ) => option . symbol !== value . symbol ) . slice ( nonMoreTokensAmount ) }
56
+ onClick = { onChange }
57
+ />
58
+ ) }
37
59
</ div >
38
60
</ div >
39
61
) ;
@@ -79,3 +101,59 @@ function TokenImage({
79
101
/>
80
102
) ;
81
103
}
104
+
105
+ function MoreTokens ( { options, onClick = ( ) => undefined } : { options : Value [ ] ; onClick ?: ( token : Value ) => void } ) {
106
+ const [ isOpen , setIsOpen ] = useState ( false ) ;
107
+
108
+ const { refs, context, floatingStyles } = useFloating ( {
109
+ open : isOpen ,
110
+ onOpenChange : setIsOpen ,
111
+ middleware : [ offset ( 4 ) ] ,
112
+ } ) ;
113
+ const { styles, isMounted } = useTransitionStyles ( context , {
114
+ initial : { transform : "translateY(-10px)" , opacity : 0 } ,
115
+ open : { transform : "translateY(0)" , opacity : 1 } ,
116
+ close : { transform : "translateY(-10px)" , opacity : 0 } ,
117
+ } ) ;
118
+ const hover = useHover ( context , { handleClose : safePolygon ( ) } ) ;
119
+ const click = useClick ( context ) ;
120
+ const dismiss = useDismiss ( context ) ;
121
+ const { getReferenceProps, getFloatingProps } = useInteractions ( [ hover , click , dismiss ] ) ;
122
+
123
+ return (
124
+ < >
125
+ < img
126
+ width = { 36 }
127
+ height = { 36 }
128
+ src = { getTokenLogoSrc ( "more.svg" ) }
129
+ alt = "More tokens"
130
+ className = { `transition-opacity duration-200 hover:cursor-pointer hover:opacity-80 ${ isOpen ? "opacity-80" : "opacity-50" } ` }
131
+ ref = { refs . setReference }
132
+ { ...getReferenceProps ( ) }
133
+ />
134
+
135
+ { isMounted && (
136
+ < FloatingPortal >
137
+ < div style = { floatingStyles } ref = { refs . setFloating } { ...getFloatingProps ( ) } className = "z-20" >
138
+ < div
139
+ className = "bg-app-bg flex flex-col gap-2 rounded-xl border border-white/20 px-3 py-2"
140
+ style = { styles }
141
+ onClick = { ( ) => setIsOpen ( false ) }
142
+ >
143
+ { options . map ( ( option ) => (
144
+ < img
145
+ key = { option . symbol }
146
+ width = { 36 }
147
+ height = { 36 }
148
+ src = { getTokenLogoSrc ( option . logo ) }
149
+ className = "rounded-full opacity-60 transition-opacity duration-200 hover:cursor-pointer hover:opacity-80"
150
+ onClick = { ( ) => onClick ( option ) }
151
+ />
152
+ ) ) }
153
+ </ div >
154
+ </ div >
155
+ </ FloatingPortal >
156
+ ) }
157
+ </ >
158
+ ) ;
159
+ }
0 commit comments