Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
frontend
Commits
10a1d410
Commit
10a1d410
authored
Dec 26, 2022
by
tom
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
one search bar for all cases and dummy dropdown
parent
e176fd03
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
241 additions
and
107 deletions
+241
-107
Popover.ts
theme/components/Popover.ts
+1
-1
search.ts
types/api/search.ts
+45
-0
SearchBar.tsx
ui/snippets/searchBar/SearchBar.tsx
+100
-16
SearchBarDesktop.tsx
ui/snippets/searchBar/SearchBarDesktop.tsx
+0
-43
SearchBarInput.tsx
ui/snippets/searchBar/SearchBarInput.tsx
+95
-0
SearchBarMobileHome.tsx
ui/snippets/searchBar/SearchBarMobileHome.tsx
+0
-47
No files found.
theme/components/Popover.ts
View file @
10a1d410
...
@@ -14,7 +14,7 @@ const $arrowBg = cssVar('popper-arrow-bg');
...
@@ -14,7 +14,7 @@ const $arrowBg = cssVar('popper-arrow-bg');
const
$arrowShadowColor
=
cssVar
(
'
popper-arrow-shadow-color
'
);
const
$arrowShadowColor
=
cssVar
(
'
popper-arrow-shadow-color
'
);
const
baseStylePopper
=
defineStyle
({
const
baseStylePopper
=
defineStyle
({
zIndex
:
20
,
zIndex
:
'
popover
'
,
});
});
const
baseStyleContent
=
defineStyle
((
props
)
=>
{
const
baseStyleContent
=
defineStyle
((
props
)
=>
{
...
...
types/api/search.ts
0 → 100644
View file @
10a1d410
export
type
SearchResultType
=
'
token
'
|
'
address
'
|
'
block
'
|
'
transaction
'
;
export
interface
SearchResultToken
{
type
:
'
token
'
;
name
:
string
;
symbol
:
string
;
address
:
string
;
token_url
:
string
;
address_url
:
string
;
}
export
interface
SearchResultAddress
{
type
:
'
address
'
;
name
:
string
;
address
:
string
;
url
:
string
;
}
export
interface
SearchResultBlock
{
type
:
'
block
'
;
block_number
:
number
;
block_hash
:
string
;
url
:
string
;
}
export
interface
SearchResultTx
{
type
:
'
transaction
'
;
tx_hash
:
string
;
url
:
string
;
}
export
interface
SearchResult
{
items
:
Array
<
SearchResultToken
|
SearchResultAddress
|
SearchResultBlock
|
SearchResultTx
>
;
next_page_params
:
{
'
address_hash
'
:
string
|
null
;
'
block_hash
'
:
string
|
null
;
'
holder_count
'
:
number
|
null
;
'
inserted_at
'
:
string
|
null
;
'
item_type
'
:
SearchResultType
;
'
items_count
'
:
number
;
'
name
'
:
string
;
'
q
'
:
string
;
'
tx_hash
'
:
string
|
null
;
};
}
ui/snippets/searchBar/SearchBar.tsx
View file @
10a1d410
import
type
{
ChangeEvent
,
FormEvent
}
from
'
react
'
;
import
{
Popover
,
PopoverTrigger
,
PopoverContent
,
PopoverBody
,
useDisclosure
,
Box
,
Text
}
from
'
@chakra-ui/react
'
;
import
_groupBy
from
'
lodash/groupBy
'
;
import
type
{
ChangeEvent
,
FormEvent
,
FocusEvent
}
from
'
react
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
link
from
'
lib/link/link
'
;
import
link
from
'
lib/link/link
'
;
import
SearchBarDesktop
from
'
./SearchBarDesktop
'
;
import
SearchBarInput
from
'
./SearchBarInput
'
;
import
SearchBarMobile
from
'
./SearchBarMobile
'
;
import
SearchBarMobileHome
from
'
./SearchBarMobileHome
'
;
type
Props
=
{
type
Props
=
{
withShadow
?:
boolean
;
withShadow
?:
boolean
;
isHomepage
?:
boolean
;
isHomepage
?:
boolean
;
}
}
const
data
=
[
{
address
:
'
0x377c5F2B300B25a534d4639177873b7fEAA56d4B
'
,
address_url
:
'
/address/0x377c5F2B300B25a534d4639177873b7fEAA56d4B
'
,
name
:
'
Toms NFT
'
,
symbol
:
'
TNT
'
,
token_url
:
'
/token/0x377c5F2B300B25a534d4639177873b7fEAA56d4B
'
,
type
:
'
token
'
,
},
{
address
:
'
0xC35Cc7223B0175245E9964f2E3119c261E8e21F9
'
,
address_url
:
'
/address/0xC35Cc7223B0175245E9964f2E3119c261E8e21F9
'
,
name
:
'
TomToken
'
,
symbol
:
'
pdE1B
'
,
token_url
:
'
/token/0xC35Cc7223B0175245E9964f2E3119c261E8e21F9
'
,
type
:
'
token
'
,
},
{
block_hash
:
'
0x1af31d7535dded06bab9a88eb40ee2f8d0529a60ab3b8a7be2ba69b008cacbd1
'
,
block_number
:
8198536
,
type
:
'
block
'
,
url
:
'
/block/0x1af31d7535dded06bab9a88eb40ee2f8d0529a60ab3b8a7be2ba69b008cacbd1
'
,
},
{
address
:
'
0xb64a30399f7F6b0C154c2E7Af0a3ec7B0A5b131a
'
,
name
:
null
,
type
:
'
address
'
,
url
:
'
/address/0xb64a30399f7F6b0C154c2E7Af0a3ec7B0A5b131a
'
,
},
{
tx_hash
:
'
0x349d4025d03c6faec117ee10ac0bce7c7a805dd2cbff7a9f101304d9a8a525dd
'
,
type
:
'
transaction
'
,
url
:
'
/tx/0x349d4025d03c6faec117ee10ac0bce7c7a805dd2cbff7a9f101304d9a8a525dd
'
,
},
];
const
SearchBar
=
({
isHomepage
,
withShadow
}:
Props
)
=>
{
const
SearchBar
=
({
isHomepage
,
withShadow
}:
Props
)
=>
{
const
[
value
,
setValue
]
=
React
.
useState
(
''
);
const
[
value
,
setValue
]
=
React
.
useState
(
''
);
const
{
isOpen
,
onClose
,
onOpen
}
=
useDisclosure
();
const
inputRef
=
React
.
useRef
<
HTMLFormElement
>
(
null
);
const
menuWidth
=
React
.
useRef
<
number
>
(
0
);
const
isMobile
=
useIsMobile
();
const
handleChange
=
React
.
useCallback
((
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
handleChange
=
React
.
useCallback
((
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
setValue
(
event
.
target
.
value
);
setValue
(
event
.
target
.
value
);
...
@@ -25,23 +66,66 @@ const SearchBar = ({ isHomepage, withShadow }: Props) => {
...
@@ -25,23 +66,66 @@ const SearchBar = ({ isHomepage, withShadow }: Props) => {
window
.
location
.
assign
(
url
);
window
.
location
.
assign
(
url
);
},
[
value
]);
},
[
value
]);
const
handleFocus
=
React
.
useCallback
(()
=>
{
onOpen
();
},
[
onOpen
]);
const
handleBlur
=
React
.
useCallback
((
event
:
FocusEvent
<
HTMLFormElement
>
)
=>
{
const
isFocusInMenu
=
event
.
relatedTarget
?.
classList
.
contains
(
'
chakra-popover__content
'
);
if
(
!
isFocusInMenu
)
{
onClose
();
}
},
[
onClose
]);
const
menuPaddingX
=
isMobile
&&
!
isHomepage
?
32
:
0
;
const
calculateMenuWidth
=
React
.
useCallback
(()
=>
{
menuWidth
.
current
=
(
inputRef
.
current
?.
getBoundingClientRect
().
width
||
0
)
-
menuPaddingX
;
},
[
menuPaddingX
]);
React
.
useEffect
(()
=>
{
calculateMenuWidth
();
window
.
addEventListener
(
'
resize
'
,
calculateMenuWidth
);
return
function
cleanup
()
{
window
.
removeEventListener
(
'
resize
'
,
calculateMenuWidth
);
};
},
[
calculateMenuWidth
]);
const
groupedData
=
_groupBy
(
data
,
'
type
'
);
return
(
return
(
<>
<
Popover
<
SearchBarDesktop
onChange=
{
handleChange
}
onSubmit=
{
handleSubmit
}
isHomepage=
{
isHomepage
}
/>
isOpen=
{
isOpen
}
{
!
isHomepage
&&
(
autoFocus=
{
false
}
<
SearchBarMobile
onClose=
{
onClose
}
placement=
"bottom-start"
offset=
{
isMobile
&&
!
isHomepage
?
[
16
,
-
12
]
:
undefined
}
>
<
PopoverTrigger
>
<
SearchBarInput
ref=
{
inputRef
}
onChange=
{
handleChange
}
onChange=
{
handleChange
}
onSubmit=
{
handleSubmit
}
onSubmit=
{
handleSubmit
}
onFocus=
{
handleFocus
}
onBlur=
{
handleBlur
}
isHomepage=
{
isHomepage
}
withShadow=
{
withShadow
}
withShadow=
{
withShadow
}
/>
/>
)
}
</
PopoverTrigger
>
{
isHomepage
&&
(
<
PopoverContent
<
SearchBarMobileHome
w=
{
`${ menuWidth.current }px`
}
onChange=
{
handleChange
}
>
onSubmit=
{
handleSubmit
}
<
PopoverBody
>
/>
{
Object
.
entries
(
groupedData
).
map
(([
group
,
data
])
=>
{
)
}
return
(
</>
<
Box
key=
{
group
}
>
<
Text
>
{
group
}
</
Text
>
{
data
.
map
((
item
,
index
)
=>
<
Box
key=
{
index
}
>
{
item
.
name
||
item
.
address
||
item
.
block_number
||
item
.
tx_hash
}
</
Box
>)
}
</
Box
>
);
})
}
</
PopoverBody
>
</
PopoverContent
>
</
Popover
>
);
);
};
};
...
...
ui/snippets/searchBar/SearchBarDesktop.tsx
deleted
100644 → 0
View file @
e176fd03
import
{
InputGroup
,
Input
,
InputLeftElement
,
Icon
,
chakra
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
ChangeEvent
,
FormEvent
}
from
'
react
'
;
import
searchIcon
from
'
icons/search.svg
'
;
interface
Props
{
onChange
:
(
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
void
;
onSubmit
:
(
event
:
FormEvent
<
HTMLFormElement
>
)
=>
void
;
isHomepage
?:
boolean
;
}
const
SearchBarDesktop
=
({
onChange
,
onSubmit
,
isHomepage
}:
Props
)
=>
{
return
(
<
chakra
.
form
noValidate
onSubmit=
{
onSubmit
}
display=
{
{
base
:
'
none
'
,
lg
:
'
block
'
}
}
w=
"100%"
backgroundColor=
{
isHomepage
?
'
white
'
:
'
none
'
}
borderRadius=
"base"
>
<
InputGroup
>
<
InputLeftElement
w=
{
6
}
ml=
{
4
}
>
<
Icon
as=
{
searchIcon
}
boxSize=
{
6
}
color=
{
useColorModeValue
(
'
blackAlpha.600
'
,
'
whiteAlpha.600
'
)
}
/>
</
InputLeftElement
>
<
Input
// paddingInlineStart="50px"
pl=
"50px"
placeholder=
"Search by addresses / transactions / block / token... "
ml=
"1px"
onChange=
{
onChange
}
border=
{
isHomepage
?
'
none
'
:
'
2px solid
'
}
borderColor=
{
useColorModeValue
(
'
blackAlpha.100
'
,
'
whiteAlpha.200
'
)
}
_focusWithin=
{
{
_placeholder
:
{
color
:
'
gray.300
'
}
}
}
color=
{
useColorModeValue
(
'
black
'
,
'
white
'
)
}
/>
</
InputGroup
>
</
chakra
.
form
>
);
};
export
default
React
.
memo
(
SearchBarDesktop
);
ui/snippets/searchBar/SearchBar
Mobile
.tsx
→
ui/snippets/searchBar/SearchBar
Input
.tsx
View file @
10a1d410
import
{
InputGroup
,
Input
,
InputLeftElement
,
Icon
,
useColorModeValue
,
chakra
}
from
'
@chakra-ui/react
'
;
import
{
InputGroup
,
Input
,
InputLeftElement
,
Icon
,
chakra
,
useColorModeValue
,
forwardRef
}
from
'
@chakra-ui/react
'
;
import
throttle
from
'
lodash/throttle
'
;
import
throttle
from
'
lodash/throttle
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
ChangeEvent
,
FormEvent
}
from
'
react
'
;
import
type
{
ChangeEvent
,
FormEvent
,
FocusEvent
}
from
'
react
'
;
import
searchIcon
from
'
icons/search.svg
'
;
import
searchIcon
from
'
icons/search.svg
'
;
import
{
useScrollDirection
}
from
'
lib/contexts/scrollDirection
'
;
import
{
useScrollDirection
}
from
'
lib/contexts/scrollDirection
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
const
TOP
=
55
;
interface
Props
{
interface
Props
{
onChange
:
(
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
void
;
onChange
:
(
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
void
;
onSubmit
:
(
event
:
FormEvent
<
HTMLFormElement
>
)
=>
void
;
onSubmit
:
(
event
:
FormEvent
<
HTMLFormElement
>
)
=>
void
;
onBlur
:
(
event
:
FocusEvent
<
HTMLFormElement
>
)
=>
void
;
onFocus
:
()
=>
void
;
isHomepage
?:
boolean
;
withShadow
?:
boolean
;
withShadow
?:
boolean
;
}
}
const
SearchBarMobile
=
({
onChange
,
onSubmit
,
withShadow
}:
Props
)
=>
{
const
SearchBarInput
=
({
onChange
,
onSubmit
,
isHomepage
,
onFocus
,
onBlur
,
withShadow
}:
Props
,
ref
:
React
.
ForwardedRef
<
HTMLFormElement
>
)
=>
{
const
[
isSticky
,
setIsSticky
]
=
React
.
useState
(
false
);
const
[
isSticky
,
setIsSticky
]
=
React
.
useState
(
false
);
const
scrollDirection
=
useScrollDirection
();
const
scrollDirection
=
useScrollDirection
();
const
isMobile
=
useIsMobile
();
const
handleScroll
=
React
.
useCallback
(()
=>
{
const
handleScroll
=
React
.
useCallback
(()
=>
{
if
(
window
.
pageYOffset
!==
0
)
{
if
(
window
.
pageYOffset
!==
0
)
{
...
@@ -28,6 +30,9 @@ const SearchBarMobile = ({ onChange, onSubmit, withShadow }: Props) => {
...
@@ -28,6 +30,9 @@ const SearchBarMobile = ({ onChange, onSubmit, withShadow }: Props) => {
},
[]);
},
[]);
React
.
useEffect
(()
=>
{
React
.
useEffect
(()
=>
{
if
(
!
isMobile
||
isHomepage
)
{
return
;
}
const
throttledHandleScroll
=
throttle
(
handleScroll
,
300
);
const
throttledHandleScroll
=
throttle
(
handleScroll
,
300
);
window
.
addEventListener
(
'
scroll
'
,
throttledHandleScroll
);
window
.
addEventListener
(
'
scroll
'
,
throttledHandleScroll
);
...
@@ -37,45 +42,54 @@ const SearchBarMobile = ({ onChange, onSubmit, withShadow }: Props) => {
...
@@ -37,45 +42,54 @@ const SearchBarMobile = ({ onChange, onSubmit, withShadow }: Props) => {
};
};
// replicate componentDidMount
// replicate componentDidMount
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[]);
},
[
isMobile
]);
const
searchIconColor
=
useColorModeValue
(
'
blackAlpha.600
'
,
'
whiteAlpha.600
'
);
const
inputBorderColor
=
useColorModeValue
(
'
blackAlpha.100
'
,
'
whiteAlpha.200
'
);
const
bgColor
=
useColorModeValue
(
'
white
'
,
'
black
'
);
const
bgColor
=
useColorModeValue
(
'
white
'
,
'
black
'
);
const
transformMobile
=
scrollDirection
!==
'
down
'
?
'
translateY(0)
'
:
'
translateY(-100%)
'
;
return
(
return
(
<
chakra
.
form
<
chakra
.
form
ref=
{
ref
}
noValidate
noValidate
onSubmit=
{
onSubmit
}
onSubmit=
{
onSubmit
}
paddingX=
{
4
}
onBlur=
{
onBlur
}
paddingTop=
{
1
}
onFocus=
{
onFocus
}
paddingBottom=
{
2
}
w=
"100%"
position=
"fixed"
backgroundColor=
{
isHomepage
?
'
white
'
:
bgColor
}
top=
{
`${ TOP }px`
}
borderRadius=
{
{
base
:
isHomepage
?
'
base
'
:
'
none
'
,
lg
:
'
base
'
}
}
position=
{
{
base
:
isHomepage
?
'
static
'
:
'
fixed
'
,
lg
:
'
static
'
}
}
top=
{
{
base
:
isHomepage
?
0
:
55
,
lg
:
0
}
}
left=
"0"
left=
"0"
zIndex=
"sticky1"
zIndex=
{
{
base
:
isHomepage
?
'
auto
'
:
'
sticky1
'
,
lg
:
'
auto
'
}
}
bgColor=
{
bgColor
}
paddingX=
{
{
base
:
isHomepage
?
0
:
4
,
lg
:
0
}
}
transform=
{
scrollDirection
!==
'
down
'
?
'
translateY(0)
'
:
'
translateY(-100%)
'
}
paddingTop=
{
{
base
:
isHomepage
?
0
:
1
,
lg
:
0
}
}
paddingBottom=
{
{
base
:
isHomepage
?
0
:
4
,
lg
:
0
}
}
boxShadow=
{
withShadow
&&
scrollDirection
!==
'
down
'
&&
isSticky
?
'
md
'
:
'
none
'
}
transform=
{
{
base
:
isHomepage
?
'
none
'
:
transformMobile
,
lg
:
'
none
'
}
}
transitionProperty=
"transform,box-shadow"
transitionProperty=
"transform,box-shadow"
transitionDuration=
"slow"
transitionDuration=
"slow"
display=
{
{
base
:
'
block
'
,
lg
:
'
none
'
}
}
w=
"100%"
boxShadow=
{
withShadow
&&
scrollDirection
!==
'
down
'
&&
isSticky
?
'
md
'
:
'
none
'
}
>
>
<
InputGroup
size=
"sm"
>
<
InputGroup
size=
{
{
base
:
isHomepage
?
'
md
'
:
'
sm
'
,
lg
:
'
md
'
}
}
>
<
InputLeftElement
>
<
InputLeftElement
w=
{
{
base
:
isHomepage
?
6
:
4
,
lg
:
6
}
}
ml=
{
{
base
:
isHomepage
?
4
:
3
,
lg
:
4
}
}
h=
"100%"
>
<
Icon
as=
{
searchIcon
}
boxSize=
{
4
}
color=
{
searchIconColor
}
/>
<
Icon
as=
{
searchIcon
}
boxSize=
{
{
base
:
isHomepage
?
6
:
4
,
lg
:
6
}
}
color=
{
useColorModeValue
(
'
blackAlpha.600
'
,
'
whiteAlpha.600
'
)
}
/>
</
InputLeftElement
>
</
InputLeftElement
>
<
Input
<
Input
paddingInlineStart=
"38px"
pl=
{
{
base
:
isHomepage
?
'
50px
'
:
'
38px
'
,
lg
:
'
50px
'
}
}
placeholder=
"Search by addresses / ... "
sx=
{
{
ml=
"1px"
'
@media screen and (max-width: 999px)
'
:
{
paddingLeft
:
isHomepage
?
'
50px
'
:
'
38px
'
,
},
}
}
placeholder=
{
isMobile
?
'
Search by addresses / ...
'
:
'
Search by addresses / transactions / block / token...
'
}
onChange=
{
onChange
}
onChange=
{
onChange
}
borderColor=
{
inputBorderColor
}
border=
{
isHomepage
?
'
none
'
:
'
2px solid
'
}
borderColor=
{
useColorModeValue
(
'
blackAlpha.100
'
,
'
whiteAlpha.200
'
)
}
_focusWithin=
{
{
_placeholder
:
{
color
:
'
gray.300
'
}
}
}
color=
{
useColorModeValue
(
'
black
'
,
'
white
'
)
}
/>
/>
</
InputGroup
>
</
InputGroup
>
</
chakra
.
form
>
</
chakra
.
form
>
);
);
};
};
export
default
React
.
memo
(
SearchBarMobile
);
export
default
React
.
memo
(
forwardRef
(
SearchBarInput
)
);
ui/snippets/searchBar/SearchBarMobileHome.tsx
deleted
100644 → 0
View file @
e176fd03
import
{
InputGroup
,
Input
,
InputLeftElement
,
Icon
,
LightMode
,
chakra
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
ChangeEvent
,
FormEvent
}
from
'
react
'
;
import
searchIcon
from
'
icons/search.svg
'
;
interface
Props
{
onChange
:
(
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
void
;
onSubmit
:
(
event
:
FormEvent
<
HTMLFormElement
>
)
=>
void
;
backgroundColor
?:
string
;
}
const
SearchBarMobileHome
=
({
onChange
,
onSubmit
}:
Props
)
=>
{
const
commonProps
=
{
noValidate
:
true
,
onSubmit
:
onSubmit
,
width
:
'
100%
'
,
display
:
{
base
:
'
block
'
,
lg
:
'
none
'
},
};
return
(
<
LightMode
>
<
chakra
.
form
{
...
commonProps
}
bgColor=
"white"
h=
"60px"
borderRadius=
"10px"
>
<
InputGroup
size=
"md"
>
<
InputLeftElement
>
<
Icon
as=
{
searchIcon
}
boxSize=
{
6
}
color=
"blackAlpha.600"
/>
</
InputLeftElement
>
<
Input
paddingInlineStart=
"38px"
placeholder=
"Search by addresses / ... "
ml=
"1px"
onChange=
{
onChange
}
border=
"none"
color=
"black"
/>
</
InputGroup
>
</
chakra
.
form
>
</
LightMode
>
);
};
export
default
SearchBarMobileHome
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment