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
7025b758
Commit
7025b758
authored
Mar 11, 2025
by
tom
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
advanced filters page
parent
7e97c811
Changes
29
Show whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
319 additions
and
286 deletions
+319
-286
eslint.config.mjs
eslint.config.mjs
+1
-0
advanced-filter.tsx
pages/advanced-filter.tsx
+2
-2
[id].tsx
pages/validators/[id].tsx
+7
-7
semanticTokens.ts
theme/foundations/semanticTokens.ts
+1
-1
checkbox.tsx
toolkit/chakra/checkbox.tsx
+2
-1
select.tsx
toolkit/chakra/select.tsx
+31
-1
borders.ts
toolkit/theme/foundations/borders.ts
+4
-2
zIndex.ts
toolkit/theme/foundations/zIndex.ts
+4
-2
checkbox.recipe.ts
toolkit/theme/recipes/checkbox.recipe.ts
+1
-0
ColumnsButton.tsx
ui/advancedFilter/ColumnsButton.tsx
+23
-30
ExportCSV.tsx
ui/advancedFilter/ExportCSV.tsx
+5
-10
FilterByColumn.tsx
ui/advancedFilter/FilterByColumn.tsx
+8
-8
ItemByColumn.tsx
ui/advancedFilter/ItemByColumn.tsx
+9
-9
constants.ts
ui/advancedFilter/constants.ts
+1
-1
AddressFilter.tsx
ui/advancedFilter/filters/AddressFilter.tsx
+29
-23
AddressRelationFilter.tsx
ui/advancedFilter/filters/AddressRelationFilter.tsx
+8
-8
AgeFilter.tsx
ui/advancedFilter/filters/AgeFilter.tsx
+14
-11
AmountFilter.tsx
ui/advancedFilter/filters/AmountFilter.tsx
+22
-20
AssetFilter.tsx
ui/advancedFilter/filters/AssetFilter.tsx
+33
-35
MethodFilter.tsx
ui/advancedFilter/filters/MethodFilter.tsx
+17
-21
TypeFilter.tsx
ui/advancedFilter/filters/TypeFilter.tsx
+22
-21
AdvancedFilter.tsx
ui/pages/AdvancedFilter.tsx
+53
-54
RawInputData.tsx
ui/shared/RawInputData.tsx
+1
-1
AddressIconDelegated.tsx
ui/shared/entities/address/AddressIconDelegated.tsx
+3
-1
ValidatorEntity.tsx
ui/shared/entities/validator/ValidatorEntity.tsx
+4
-6
TableColumnFilter.tsx
ui/shared/filters/TableColumnFilter.tsx
+1
-1
TableColumnFilterWrapper.tsx
ui/shared/filters/TableColumnFilterWrapper.tsx
+2
-8
TagGroupSelect.tsx
ui/shared/tagGroupSelect/TagGroupSelect.tsx
+2
-2
Tag.tsx
ui/showcases/Tag.tsx
+9
-0
No files found.
eslint.config.mjs
View file @
7025b758
...
...
@@ -37,6 +37,7 @@ const RESTRICTED_MODULES = {
'
Tag
'
,
'
Switch
'
,
'
Image
'
,
'
Popover
'
,
'
PopoverTrigger
'
,
'
PopoverContent
'
,
'
PopoverBody
'
,
'
PopoverFooter
'
,
'
DrawerRoot
'
,
'
DrawerBody
'
,
'
DrawerContent
'
,
'
DrawerOverlay
'
,
'
DrawerBackdrop
'
,
'
DrawerTrigger
'
,
'
Drawer
'
,
'
Alert
'
,
'
AlertIcon
'
,
'
AlertTitle
'
,
'
AlertDescription
'
,
'
Select
'
,
'
SelectRoot
'
,
'
SelectControl
'
,
'
SelectContent
'
,
'
SelectItem
'
,
'
SelectValueText
'
,
'
Heading
'
,
'
Badge
'
,
'
Tabs
'
,
'
Show
'
,
'
Hide
'
,
'
Checkbox
'
,
'
CheckboxGroup
'
,
'
Table
'
,
'
TableRoot
'
,
'
TableBody
'
,
'
TableHeader
'
,
'
TableRow
'
,
'
TableCell
'
,
'
Menu
'
,
'
MenuRoot
'
,
'
MenuTrigger
'
,
'
MenuContent
'
,
'
MenuItem
'
,
'
MenuTriggerItem
'
,
'
MenuRadioItemGroup
'
,
'
MenuContextTrigger
'
,
...
...
pages/advanced-filter.tsx
View file @
7025b758
...
...
@@ -3,12 +3,12 @@ import React from 'react';
import
PageNextJs
from
'
nextjs/PageNextJs
'
;
//
import AdvancedFilter from 'ui/pages/AdvancedFilter';
import
AdvancedFilter
from
'
ui/pages/AdvancedFilter
'
;
const
Page
:
NextPage
=
()
=>
{
return
(
<
PageNextJs
pathname=
"/advanced-filter"
>
{
/* <AdvancedFilter/> */
}
<
AdvancedFilter
/>
</
PageNextJs
>
);
};
...
...
pages/validators/[id].tsx
View file @
7025b758
...
...
@@ -9,18 +9,18 @@ import config from 'configs/app';
const
validatorsFeature
=
config
.
features
.
validators
;
//
const ValidatorDetails = dynamic(() => {
//
if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'zilliqa') {
//
return import('ui/pages/ValidatorZilliqa');
//
}
const
ValidatorDetails
=
dynamic
(()
=>
{
if
(
validatorsFeature
.
isEnabled
&&
validatorsFeature
.
chainType
===
'
zilliqa
'
)
{
return
import
(
'
ui/pages/ValidatorZilliqa
'
);
}
//
throw new Error('Validators feature is not enabled.');
//
}, { ssr: false });
throw
new
Error
(
'
Validators feature is not enabled.
'
);
},
{
ssr
:
false
});
const
Page
:
NextPage
<
Props
>
=
(
props
)
=>
{
return
(
<
PageNextJs
pathname=
"/validators/[id]"
query=
{
props
.
query
}
>
{
/* <ValidatorDetails/> */
}
<
ValidatorDetails
/>
</
PageNextJs
>
);
};
...
...
theme/foundations/semanticTokens.ts
View file @
7025b758
...
...
@@ -31,7 +31,7 @@ const semanticTokens = {
'
default
'
:
'
red.500
'
,
_dark
:
'
red.500
'
,
},
dialog
.
bg
:
{
dialog
_
bg
:
{
'
default
'
:
'
white
'
,
_dark
:
'
gray.900
'
,
},
...
...
toolkit/chakra/checkbox.tsx
View file @
7025b758
import
type
{
Checkbox
as
ArkCheckbox
}
from
'
@ark-ui/react/checkbox
'
;
import
type
{
HTMLChakraProps
}
from
'
@chakra-ui/react
'
;
import
{
Checkbox
as
ChakraCheckbox
,
CheckboxGroup
as
ChakraCheckboxGroup
}
from
'
@chakra-ui/react
'
;
import
*
as
React
from
'
react
'
;
...
...
@@ -25,7 +26,7 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
},
);
export
interface
CheckboxGroupProps
extends
ArkCheckbox
.
GroupProps
{
export
interface
CheckboxGroupProps
extends
HTMLChakraProps
<
'
div
'
,
ArkCheckbox
.
GroupProps
>
{
orientation
?:
'
vertical
'
|
'
horizontal
'
;
}
...
...
toolkit/chakra/select.tsx
View file @
7025b758
'
use client
'
;
import
type
{
CollectionItem
}
from
'
@chakra-ui/react
'
;
import
type
{
CollectionItem
,
ListCollection
}
from
'
@chakra-ui/react
'
;
import
{
Select
as
ChakraSelect
,
Portal
,
useSelectContext
}
from
'
@chakra-ui/react
'
;
import
*
as
React
from
'
react
'
;
...
...
@@ -173,3 +173,33 @@ export const SelectItemGroup = React.forwardRef<
export
const
SelectLabel
=
ChakraSelect
.
Label
;
export
const
SelectItemText
=
ChakraSelect
.
ItemText
;
export
interface
SelectProps
extends
SelectRootProps
{
collection
:
ListCollection
<
CollectionItem
>
;
placeholder
:
string
;
portalled
?:
boolean
;
}
// TODO @tom2drum refactor selects
export
const
Select
=
React
.
forwardRef
<
HTMLDivElement
,
SelectProps
>
((
props
,
ref
)
=>
{
const
{
collection
,
placeholder
,
portalled
=
true
,
...
rest
}
=
props
;
return
(
<
SelectRoot
ref=
{
ref
}
collection=
{
collection
}
variant=
"outline"
{
...
rest
}
>
<
SelectControl
>
<
SelectValueText
placeholder=
{
placeholder
}
/>
</
SelectControl
>
<
SelectContent
portalled=
{
portalled
}
>
{
collection
.
items
.
map
((
item
)
=>
(
<
SelectItem
item=
{
item
}
key=
{
item
.
value
}
>
{
item
.
label
}
</
SelectItem
>
))
}
</
SelectContent
>
</
SelectRoot
>
);
});
toolkit/theme/foundations/borders.ts
View file @
7025b758
import
type
{
T
okenDefinition
}
from
'
@chakra-ui/react/dist/types/styled-system/types
'
;
import
type
{
T
hemingConfig
}
from
'
@chakra-ui/react
'
;
export
const
radii
:
TokenDefinition
[
'
radii
'
]
=
{
import
type
{
ExcludeUndefined
}
from
'
types/utils
'
;
export
const
radii
:
ExcludeUndefined
<
ThemingConfig
[
'
tokens
'
]
>
[
'
radii
'
]
=
{
none
:
{
value
:
'
0
'
},
sm
:
{
value
:
'
4px
'
},
base
:
{
value
:
'
8px
'
},
...
...
toolkit/theme/foundations/zIndex.ts
View file @
7025b758
import
type
{
T
okenDefinition
}
from
'
@chakra-ui/react/dist/types/styled-system/types
'
;
import
type
{
T
hemingConfig
}
from
'
@chakra-ui/react
'
;
export
const
zIndex
:
TokenDefinition
[
'
zIndex
'
]
=
{
import
type
{
ExcludeUndefined
}
from
'
types/utils
'
;
export
const
zIndex
:
ExcludeUndefined
<
ThemingConfig
[
'
tokens
'
]
>
[
'
zIndex
'
]
=
{
hide
:
{
value
:
-
1
},
auto
:
{
value
:
'
auto
'
},
base
:
{
value
:
0
},
...
...
toolkit/theme/recipes/checkbox.recipe.ts
View file @
7025b758
...
...
@@ -26,6 +26,7 @@ export const recipe = defineSlotRecipe({
label
:
{
fontWeight
:
'
normal
'
,
userSelect
:
'
none
'
,
flexGrow
:
1
,
_disabled
:
{
opacity
:
'
control.disabled
'
,
},
...
...
ui/advancedFilter/ColumnsButton.tsx
View file @
7025b758
import
{
Button
,
Grid
,
PopoverTrigger
,
PopoverContent
,
PopoverBody
,
useDisclosure
,
Checkbox
,
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
ChangeEvent
}
from
'
react
'
;
import
{
Button
}
from
'
toolkit/chakra/button
'
;
import
{
Checkbox
,
CheckboxGroup
}
from
'
toolkit/chakra/checkbox
'
;
import
{
PopoverBody
,
PopoverContent
,
PopoverRoot
,
PopoverTrigger
}
from
'
toolkit/chakra/popover
'
;
import
type
{
ColumnsIds
}
from
'
ui/advancedFilter/constants
'
;
import
{
TABLE_COLUMNS
}
from
'
ui/advancedFilter/constants
'
;
import
Popover
from
'
ui/shared/chakra/Popover
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
interface
Props
{
...
...
@@ -21,46 +13,47 @@ interface Props {
}
const
ColumnsButton
=
({
columns
,
onChange
}:
Props
)
=>
{
const
{
isOpen
,
onToggle
,
onClose
}
=
useDisclosure
();
const
onCheckboxClick
=
React
.
useCallback
((
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
newCols
=
{
...
columns
};
const
id
=
event
.
target
.
id
as
ColumnsIds
;
newCols
[
id
]
=
event
.
target
.
checked
;
const
handleValueChange
=
React
.
useCallback
((
value
:
Array
<
string
>
)
=>
{
const
newCols
=
value
.
reduce
((
acc
,
key
)
=>
{
acc
[
key
as
ColumnsIds
]
=
true
;
return
acc
;
},
{}
as
Record
<
ColumnsIds
,
boolean
>
);
onChange
(
newCols
);
},
[
onChange
,
columns
]);
},
[
onChange
]);
return
(
<
Popover
isOpen=
{
isOpen
}
onClose=
{
onClose
}
placement=
"bottom-start"
isLazy
>
<
Popover
Root
>
<
PopoverTrigger
>
<
Button
onClick=
{
onToggle
}
variant=
"outline"
colorScheme=
"gray"
variant=
"dropdown"
size=
"sm"
leftIcon=
{
<
IconSvg
name=
"columns"
boxSize=
{
5
}
color=
"inherit"
/>
}
>
<
IconSvg
name=
"columns"
boxSize=
{
5
}
color=
"inherit"
/>
Columns
</
Button
>
</
PopoverTrigger
>
<
PopoverContent
>
<
PopoverBody
px=
{
4
}
py=
{
6
}
display=
"flex"
flexDir=
"column"
rowGap=
{
5
}
>
<
Grid
gridTemplateColumns=
"160px 160px"
gap=
{
3
}
>
<
CheckboxGroup
defaultValue=
{
Object
.
keys
(
columns
).
filter
((
key
)
=>
columns
[
key
as
ColumnsIds
])
}
onValueChange=
{
handleValueChange
}
display=
"grid"
gridTemplateColumns=
"160px 160px"
gap=
{
3
}
>
{
TABLE_COLUMNS
.
map
(
col
=>
(
<
Checkbox
key=
{
col
.
id
}
defaultChecked=
{
columns
[
col
.
id
]
}
onChange=
{
onCheckboxClick
}
id=
{
col
.
id
}
size=
"lg"
value=
{
col
.
id
}
size=
"md"
>
{
col
.
id
===
'
or_and
'
?
'
And/Or
'
:
col
.
name
}
</
Checkbox
>
))
}
</
Grid
>
</
CheckboxGroup
>
</
PopoverBody
>
</
PopoverContent
>
</
Popover
>
</
Popover
Root
>
);
};
...
...
ui/advancedFilter/ExportCSV.tsx
View file @
7025b758
import
{
Button
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
AdvancedFilterParams
}
from
'
types/api/advancedFilter
'
;
...
...
@@ -7,7 +6,8 @@ import config from 'configs/app';
import
buildUrl
from
'
lib/api/buildUrl
'
;
import
dayjs
from
'
lib/date/dayjs
'
;
import
downloadBlob
from
'
lib/downloadBlob
'
;
import
useToast
from
'
lib/hooks/useToast
'
;
import
{
Button
}
from
'
toolkit/chakra/button
'
;
import
{
toaster
}
from
'
toolkit/chakra/toaster
'
;
import
ReCaptcha
from
'
ui/shared/reCaptcha/ReCaptcha
'
;
import
useReCaptcha
from
'
ui/shared/reCaptcha/useReCaptcha
'
;
...
...
@@ -17,7 +17,6 @@ type Props = {
const
ExportCSV
=
({
filters
}:
Props
)
=>
{
const
recaptcha
=
useReCaptcha
();
const
toast
=
useToast
();
const
[
isLoading
,
setIsLoading
]
=
React
.
useState
(
false
);
const
handleExportCSV
=
React
.
useCallback
(
async
()
=>
{
...
...
@@ -49,18 +48,14 @@ const ExportCSV = ({ filters }: Props) => {
downloadBlob
(
blob
,
fileName
);
}
catch
(
error
)
{
toast
({
position
:
'
top-right
'
,
toaster
.
error
({
title
:
'
Error
'
,
description
:
(
error
as
Error
)?.
message
||
'
Something went wrong. Try again later.
'
,
status
:
'
error
'
,
variant
:
'
subtle
'
,
isClosable
:
true
,
});
}
finally
{
setIsLoading
(
false
);
}
},
[
toast
,
filters
,
recaptcha
]);
},
[
filters
,
recaptcha
]);
if
(
!
config
.
services
.
reCaptchaV2
.
siteKey
)
{
return
null
;
...
...
@@ -71,7 +66,7 @@ const ExportCSV = ({ filters }: Props) => {
<
Button
onClick=
{
handleExportCSV
}
variant=
"outline"
isL
oading=
{
isLoading
}
l
oading=
{
isLoading
}
size=
"sm"
mr=
{
3
}
>
...
...
ui/advancedFilter/FilterByColumn.tsx
View file @
7025b758
...
...
@@ -34,7 +34,7 @@ const FilterByColumn = ({ column, filters, columnName, handleFilterChange, searc
<
TableColumnFilterWrapper
columnName=
"Type"
isLoading=
{
isLoading
}
isActive
=
{
Boolean
(
value
&&
value
.
length
)
}
selected
=
{
Boolean
(
value
&&
value
.
length
)
}
>
<
TypeFilter
{
...
commonProps
}
value=
{
value
}
/>
</
TableColumnFilterWrapper
>
...
...
@@ -46,7 +46,7 @@ const FilterByColumn = ({ column, filters, columnName, handleFilterChange, searc
<
TableColumnFilterWrapper
columnName=
"Method"
isLoading=
{
isLoading
}
isActive
=
{
Boolean
(
value
&&
value
.
length
)
}
selected
=
{
Boolean
(
value
&&
value
.
length
)
}
w=
"350px"
>
<
MethodFilter
{
...
commonProps
}
value=
{
value
}
/>
...
...
@@ -59,7 +59,7 @@ const FilterByColumn = ({ column, filters, columnName, handleFilterChange, searc
<
TableColumnFilterWrapper
columnName=
"Age"
isLoading=
{
isLoading
}
isActive
=
{
Boolean
(
value
.
from
||
value
.
to
||
value
.
age
)
}
selected
=
{
Boolean
(
value
.
from
||
value
.
to
||
value
.
age
)
}
w=
"382px"
>
<
AgeFilter
{
...
commonProps
}
value=
{
value
}
/>
...
...
@@ -71,7 +71,7 @@ const FilterByColumn = ({ column, filters, columnName, handleFilterChange, searc
<
TableColumnFilterWrapper
columnName=
"And/Or"
isLoading=
{
isLoading
}
isActive
=
{
false
}
selected
=
{
false
}
w=
"106px"
value=
{
filters
.
address_relation
===
'
and
'
?
'
AND
'
:
'
OR
'
}
>
...
...
@@ -88,7 +88,7 @@ const FilterByColumn = ({ column, filters, columnName, handleFilterChange, searc
<
TableColumnFilterWrapper
columnName=
"Address from"
isLoading=
{
isLoading
}
isActive
=
{
Boolean
(
value
.
length
)
}
selected
=
{
Boolean
(
value
.
length
)
}
w=
"480px"
>
<
AddressFilter
{
...
commonProps
}
type=
"from"
value=
{
value
}
/>
...
...
@@ -105,7 +105,7 @@ const FilterByColumn = ({ column, filters, columnName, handleFilterChange, searc
<
TableColumnFilterWrapper
columnName=
"Address to"
isLoading=
{
isLoading
}
isActive
=
{
Boolean
(
value
.
length
)
}
selected
=
{
Boolean
(
value
.
length
)
}
w=
"480px"
>
<
AddressFilter
{
...
commonProps
}
type=
"to"
value=
{
value
}
/>
...
...
@@ -118,7 +118,7 @@ const FilterByColumn = ({ column, filters, columnName, handleFilterChange, searc
<
TableColumnFilterWrapper
columnName=
"Amount"
isLoading=
{
isLoading
}
isActive
=
{
Boolean
(
value
.
from
||
value
.
to
)
}
selected
=
{
Boolean
(
value
.
from
||
value
.
to
)
}
w=
"382px"
>
<
AmountFilter
{
...
commonProps
}
value=
{
value
}
/>
...
...
@@ -145,7 +145,7 @@ const FilterByColumn = ({ column, filters, columnName, handleFilterChange, searc
<
TableColumnFilterWrapper
columnName=
"Asset"
isLoading=
{
isLoading
}
isActive
=
{
Boolean
(
value
.
length
)
}
selected
=
{
Boolean
(
value
.
length
)
}
w=
"382px"
>
<
AssetFilter
{
...
commonProps
}
value=
{
value
}
/>
...
...
ui/advancedFilter/ItemByColumn.tsx
View file @
7025b758
...
...
@@ -5,10 +5,10 @@ import type { AdvancedFilterResponseItem } from 'types/api/advancedFilter';
import
config
from
'
configs/app
'
;
import
getCurrencyValue
from
'
lib/getCurrencyValue
'
;
import
{
Badge
}
from
'
toolkit/chakra/badge
'
;
import
{
Skeleton
}
from
'
toolkit/chakra/skeleton
'
;
import
type
{
ColumnsIds
}
from
'
ui/advancedFilter/constants
'
;
import
AddressFromToIcon
from
'
ui/shared/address/AddressFromToIcon
'
;
import
Skeleton
from
'
ui/shared/chakra/Skeleton
'
;
import
Tag
from
'
ui/shared/chakra/Tag
'
;
import
AddressEntity
from
'
ui/shared/entities/address/AddressEntity
'
;
import
TokenEntity
from
'
ui/shared/entities/token/TokenEntity
'
;
import
TxEntity
from
'
ui/shared/entities/tx/TxEntity
'
;
...
...
@@ -31,10 +31,10 @@ const ItemByColumn = ({ item, column, isLoading }: Props) => {
if
(
!
type
)
{
return
null
;
}
return
<
Tag
isLoading=
{
isLoading
}
>
{
type
.
name
}
</
Tag
>;
return
<
Badge
loading=
{
isLoading
}
>
{
type
.
name
}
</
Badge
>;
}
case
'
method
'
:
return
item
.
method
?
<
Tag
isLoading=
{
isLoading
}
isTruncated
colorScheme=
"gray"
>
{
item
.
method
}
</
Tag
>
:
null
;
return
item
.
method
?
<
Badge
loading=
{
isLoading
}
truncated
>
{
item
.
method
}
</
Badge
>
:
null
;
case
'
age
'
:
return
<
TimeAgoWithTooltip
timestamp=
{
item
.
timestamp
}
isLoading=
{
isLoading
}
color=
"text_secondary"
fontWeight=
{
400
}
/>;
case
'
from
'
:
...
...
@@ -63,18 +63,18 @@ const ItemByColumn = ({ item, column, isLoading }: Props) => {
);
case
'
amount
'
:
{
if
(
item
.
token
?.
type
===
'
ERC-721
'
)
{
return
<
Skeleton
isLoaded=
{
!
isLoading
}
>
1
</
Skeleton
>;
return
<
Skeleton
loading=
{
isLoading
}
>
1
</
Skeleton
>;
}
if
(
item
.
total
)
{
return
(
<
Skeleton
isLoaded=
{
!
isLoading
}
>
<
Skeleton
loading=
{
isLoading
}
>
{
getCurrencyValue
({
value
:
item
.
total
?.
value
,
decimals
:
item
.
total
.
decimals
,
accuracy
:
8
}).
valueStr
}
</
Skeleton
>
);
}
if
(
item
.
value
)
{
return
(
<
Skeleton
isLoaded=
{
!
isLoading
}
>
<
Skeleton
loading=
{
isLoading
}
>
{
getCurrencyValue
({
value
:
item
.
value
,
decimals
:
config
.
chain
.
currency
.
decimals
.
toString
(),
accuracy
:
8
}).
valueStr
}
</
Skeleton
>
);
...
...
@@ -84,9 +84,9 @@ const ItemByColumn = ({ item, column, isLoading }: Props) => {
case
'
asset
'
:
return
item
.
token
?
<
TokenEntity
token=
{
item
.
token
}
isLoading=
{
isLoading
}
fontWeight=
{
700
}
onlySymbol
noCopy
/>
:
<
Skeleton
isLoaded=
{
!
isLoading
}
fontWeight=
{
700
}
>
{
config
.
chain
.
currency
.
symbol
}
</
Skeleton
>;
<
Skeleton
loading=
{
isLoading
}
fontWeight=
{
700
}
>
{
config
.
chain
.
currency
.
symbol
}
</
Skeleton
>;
case
'
fee
'
:
return
<
Skeleton
isLoaded=
{
!
isLoading
}
>
{
item
.
fee
?
getCurrencyValue
({
value
:
item
.
fee
,
accuracy
:
8
}).
valueStr
:
'
-
'
}
</
Skeleton
>;
return
<
Skeleton
loading=
{
isLoading
}
>
{
item
.
fee
?
getCurrencyValue
({
value
:
item
.
fee
,
accuracy
:
8
}).
valueStr
:
'
-
'
}
</
Skeleton
>;
default
:
return
null
;
}
...
...
ui/advancedFilter/constants.ts
View file @
7025b758
...
...
@@ -40,7 +40,7 @@ export const TABLE_COLUMNS: Array<TxTableColumn> = [
{
id
:
'
or_and
'
,
name
:
''
,
width
:
'
6
0
px
'
,
width
:
'
6
5
px
'
,
},
{
id
:
'
to
'
,
...
...
ui/advancedFilter/filters/AddressFilter.tsx
View file @
7025b758
import
{
Flex
,
Select
,
Input
,
InputGroup
,
InputRightElement
,
VStack
,
IconButton
}
from
'
@chakra-ui/react
'
;
import
{
createListCollection
,
Flex
,
VStack
}
from
'
@chakra-ui/react
'
;
import
{
isEqual
}
from
'
es-toolkit
'
;
import
type
{
ChangeEvent
}
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
AdvancedFilterParams
}
from
'
types/api/advancedFilter
'
;
import
{
IconButton
}
from
'
toolkit/chakra/icon-button
'
;
import
{
Input
}
from
'
toolkit/chakra/input
'
;
import
{
InputGroup
}
from
'
toolkit/chakra/input-group
'
;
import
{
Select
}
from
'
toolkit/chakra/select
'
;
import
ClearButton
from
'
ui/shared/ClearButton
'
;
import
TableColumnFilter
from
'
ui/shared/filters/TableColumnFilter
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
...
...
@@ -16,6 +20,13 @@ const FILTER_PARAM_FROM_EXCLUDE = 'from_address_hashes_to_exclude';
export
type
AddressFilterMode
=
'
include
'
|
'
exclude
'
;
const
collection
=
createListCollection
({
items
:
[
{
label
:
'
Include
'
,
value
:
'
include
'
},
{
label
:
'
Exclude
'
,
value
:
'
exclude
'
},
],
});
type
Value
=
Array
<
{
address
:
string
;
mode
:
AddressFilterMode
}
>
;
type
Props
=
{
...
...
@@ -24,14 +35,13 @@ type Props = {
columnName
:
string
;
type
:
'
from
'
|
'
to
'
;
isLoading
?:
boolean
;
onClose
?:
()
=>
void
;
};
type
InputProps
=
{
address
?:
string
;
mode
?:
AddressFilterMode
;
isLast
:
boolean
;
onModeChange
:
(
event
:
ChangeEvent
<
HTMLSelectElement
>
)
=>
void
;
onModeChange
:
(
{
value
}:
{
value
:
Array
<
string
>
}
)
=>
void
;
onChange
:
(
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
void
;
onClear
:
()
=>
void
;
onAddFieldClick
:
()
=>
void
;
...
...
@@ -50,22 +60,19 @@ const AddressFilterInput = ({ address, mode, onModeChange, onChange, onClear, is
return
(
<
Flex
alignItems=
"center"
w=
"100%"
>
<
Select
size=
"xs"
borderRadius=
"bas
e"
value=
{
mode
||
'
include
'
}
onChange=
{
onModeChange
}
minW=
"105px"
collection=
{
collection
}
placeholder=
"Select mod
e"
defaultValue=
{
[
mode
||
'
include
'
]
}
on
Value
Change=
{
onModeChange
}
portalled=
{
false
}
w=
"105px"
mr=
{
3
}
/>
<
InputGroup
flexGrow=
{
1
}
endElement=
{
<
ClearButton
onClick=
{
onClear
}
isDisabled=
{
!
address
}
/>
}
>
<
option
value=
"include"
>
Include
</
option
>
<
option
value=
"exclude"
>
Exclude
</
option
>
</
Select
>
<
InputGroup
size=
"xs"
flexGrow=
{
1
}
>
<
Input
value=
{
address
}
onChange=
{
onChange
}
placeholder=
"Smart contract / Address (0x...)*"
size=
"xs"
autoComplete=
"off"
/>
<
InputRightElement
>
<
ClearButton
onClick=
{
onClear
}
isDisabled=
{
!
address
}
/>
</
InputRightElement
>
<
Input
value=
{
address
}
onChange=
{
onChange
}
placeholder=
"Smart contract / Address (0x...)*"
size=
"sm"
autoComplete=
"off"
/>
</
InputGroup
>
{
isLast
&&
(
<
IconButton
...
...
@@ -76,8 +83,9 @@ const AddressFilterInput = ({ address, mode, onModeChange, onChange, onClear, is
h=
"30px"
ml=
{
2
}
onClick=
{
onAddFieldClick
}
icon=
{
<
IconSvg
name=
"plus"
w=
"20px"
h=
"20px"
/>
}
/>
>
<
IconSvg
name=
"plus"
w=
"20px"
h=
"20px"
/>
</
IconButton
>
)
}
</
Flex
>
);
...
...
@@ -85,14 +93,13 @@ const AddressFilterInput = ({ address, mode, onModeChange, onChange, onClear, is
const
emptyItem
=
{
address
:
''
,
mode
:
'
include
'
as
AddressFilterMode
};
const
AddressFilter
=
({
type
,
value
=
[],
handleFilterChange
,
onClose
}:
Props
)
=>
{
const
AddressFilter
=
({
type
,
value
=
[],
handleFilterChange
}:
Props
)
=>
{
const
[
currentValue
,
setCurrentValue
]
=
React
.
useState
<
Array
<
AddressFilter
>>
([
...
value
,
emptyItem
]);
const
handleModeSelectChange
=
React
.
useCallback
((
index
:
number
)
=>
(
event
:
React
.
ChangeEvent
<
HTMLSelectElement
>
)
=>
{
const
value
=
event
.
target
.
value
as
AddressFilterMode
;
const
handleModeSelectChange
=
React
.
useCallback
((
index
:
number
)
=>
({
value
}:
{
value
:
Array
<
string
>
})
=>
{
setCurrentValue
(
prev
=>
{
prev
[
index
]
=
{
...
prev
[
index
],
mode
:
value
};
prev
[
index
]
=
{
...
prev
[
index
],
mode
:
value
[
0
]
as
AddressFilterMode
};
return
[
...
prev
];
});
},
[]);
...
...
@@ -138,7 +145,6 @@ const AddressFilter = ({ type, value = [], handleFilterChange, onClose }: Props)
isTouched=
{
!
isEqual
(
currentValue
.
filter
(
i
=>
i
.
address
).
map
(
addressFilterToKey
).
sort
(),
value
.
map
(
addressFilterToKey
).
sort
())
}
onFilter=
{
onFilter
}
onReset=
{
onReset
}
onClose=
{
onClose
}
hasReset
>
<
VStack
gap=
{
2
}
>
...
...
ui/advancedFilter/filters/AddressRelationFilter.tsx
View file @
7025b758
import
{
Radio
,
RadioGroup
,
Stack
,
Box
}
from
'
@chakra-ui/react
'
;
import
{
Box
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
{
type
AdvancedFilterParams
}
from
'
types/api/advancedFilter
'
;
import
{
Radio
,
RadioGroup
}
from
'
toolkit/chakra/radio
'
;
const
FILTER_PARAM
=
'
address_relation
'
;
type
Value
=
'
or
'
|
'
and
'
;
...
...
@@ -18,18 +20,16 @@ type Props = {
};
const
AddressRelationFilter
=
({
value
=
DEFAULT_VALUE
,
handleFilterChange
,
onClose
}:
Props
)
=>
{
const
onFilter
=
React
.
useCallback
((
val
:
Value
)
=>
{
const
onFilter
=
React
.
useCallback
((
{
value
}:
{
value
:
string
}
)
=>
{
onClose
&&
onClose
();
handleFilterChange
(
FILTER_PARAM
,
val
);
handleFilterChange
(
FILTER_PARAM
,
val
ue
as
Value
);
},
[
handleFilterChange
,
onClose
]);
return
(
<
Box
w=
"120px"
>
<
RadioGroup
onChange=
{
onFilter
}
value=
{
value
}
>
<
Stack
direction=
"column"
>
<
RadioGroup
onValueChange=
{
onFilter
}
value=
{
value
}
orientation=
"vertical"
>
<
Radio
value=
"or"
>
OR
</
Radio
>
<
Radio
value=
"and"
>
AND
</
Radio
>
</
Stack
>
</
RadioGroup
>
</
Box
>
);
...
...
ui/advancedFilter/filters/AgeFilter.tsx
View file @
7025b758
import
{
Flex
,
Input
,
Text
}
from
'
@chakra-ui/react
'
;
import
{
Flex
,
Text
}
from
'
@chakra-ui/react
'
;
import
{
isEqual
}
from
'
es-toolkit
'
;
import
type
{
ChangeEvent
}
from
'
react
'
;
import
React
from
'
react
'
;
...
...
@@ -7,6 +7,8 @@ import { ADVANCED_FILTER_AGES, type AdvancedFilterAge, type AdvancedFilterParams
import
dayjs
from
'
lib/date/dayjs
'
;
import
{
ndash
}
from
'
lib/html-entities
'
;
import
{
Input
}
from
'
toolkit/chakra/input
'
;
import
{
PopoverCloseTriggerWrapper
}
from
'
toolkit/chakra/popover
'
;
import
TableColumnFilter
from
'
ui/shared/filters/TableColumnFilter
'
;
import
TagGroupSelect
from
'
ui/shared/tagGroupSelect/TagGroupSelect
'
;
...
...
@@ -48,8 +50,8 @@ const AgeFilter = ({ value = defaultValue, handleFilterChange, onClose }: Props)
const
to
=
dayjs
().
toISOString
();
handleFilterChange
(
FILTER_PARAM_TO
,
to
);
handleFilterChange
(
FILTER_PARAM_AGE
,
age
);
onClose
&&
onClose
();
},
[
onClose
,
handleFilterChang
e
]);
onClose
?.
();
},
[
handleFilterChange
,
onClos
e
]);
const
onReset
=
React
.
useCallback
(()
=>
setCurrentValue
(
defaultValue
),
[]);
...
...
@@ -76,15 +78,16 @@ const AgeFilter = ({ value = defaultValue, handleFilterChange, onClose }: Props)
isTouched=
{
value
.
age
?
value
.
age
!==
currentValue
.
age
:
!
isEqual
(
currentValue
,
value
)
}
onFilter=
{
onFilter
}
onReset=
{
onReset
}
onClose=
{
onClose
}
hasReset
>
<
Flex
gap=
{
3
}
>
<
PopoverCloseTriggerWrapper
>
<
TagGroupSelect
<
AdvancedFilterAge
>
items=
{
ADVANCED_FILTER_AGES
.
map
(
val
=>
({
id
:
val
,
title
:
val
}))
}
onChange=
{
onPresetChange
}
value=
{
currentValue
.
age
||
undefined
}
/
>
</
PopoverCloseTriggerWrapper
>
</
Flex
>
<
Flex
mt=
{
3
}
>
<
Input
...
...
@@ -92,7 +95,7 @@ const AgeFilter = ({ value = defaultValue, handleFilterChange, onClose }: Props)
onChange=
{
handleFromChange
}
placeholder=
"From"
type=
"date"
size=
"
xs
"
size=
"
sm
"
/>
<
Text
mx=
{
3
}
>
{
ndash
}
</
Text
>
<
Input
...
...
@@ -100,7 +103,7 @@ const AgeFilter = ({ value = defaultValue, handleFilterChange, onClose }: Props)
onChange=
{
handleToChange
}
placeholder=
"To"
type=
"date"
size=
"
xs
"
size=
"
sm
"
/>
</
Flex
>
</
TableColumnFilter
>
...
...
ui/advancedFilter/filters/AmountFilter.tsx
View file @
7025b758
import
{
Flex
,
Input
,
Tag
,
Text
}
from
'
@chakra-ui/react
'
;
import
{
Flex
,
Text
}
from
'
@chakra-ui/react
'
;
import
{
isEqual
}
from
'
es-toolkit
'
;
import
type
{
ChangeEvent
}
from
'
react
'
;
import
React
from
'
react
'
;
...
...
@@ -6,6 +6,9 @@ import React from 'react';
import
type
{
AdvancedFilterParams
}
from
'
types/api/advancedFilter
'
;
import
{
ndash
}
from
'
lib/html-entities
'
;
import
{
Input
}
from
'
toolkit/chakra/input
'
;
import
{
PopoverCloseTriggerWrapper
}
from
'
toolkit/chakra/popover
'
;
import
{
Tag
}
from
'
toolkit/chakra/tag
'
;
import
TableColumnFilter
from
'
ui/shared/filters/TableColumnFilter
'
;
const
FILTER_PARAM_FROM
=
'
amount_from
'
;
...
...
@@ -44,10 +47,9 @@ type AmountValue = { from?: string; to?: string };
type
Props
=
{
value
?:
AmountValue
;
handleFilterChange
:
(
filed
:
keyof
AdvancedFilterParams
,
value
?:
string
)
=>
void
;
onClose
?:
()
=>
void
;
};
const
AmountFilter
=
({
value
=
{},
handleFilterChange
,
onClose
}:
Props
)
=>
{
const
AmountFilter
=
({
value
=
{},
handleFilterChange
}:
Props
)
=>
{
const
[
currentValue
,
setCurrentValue
]
=
React
.
useState
<
AmountValue
>
(
value
||
defaultValue
);
const
handleFromChange
=
React
.
useCallback
((
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
...
...
@@ -69,8 +71,7 @@ const AmountFilter = ({ value = {}, handleFilterChange, onClose }: Props) => {
const
to
=
(
event
.
currentTarget
as
HTMLDivElement
).
getAttribute
(
'
data-id
'
)
as
string
;
handleFilterChange
(
FILTER_PARAM_FROM
,
''
);
handleFilterChange
(
FILTER_PARAM_TO
,
to
);
onClose
&&
onClose
();
},
[
handleFilterChange
,
onClose
]);
},
[
handleFilterChange
]);
return
(
<
TableColumnFilter
...
...
@@ -79,9 +80,9 @@ const AmountFilter = ({ value = {}, handleFilterChange, onClose }: Props) => {
isTouched=
{
!
isEqual
(
currentValue
,
value
)
}
onFilter=
{
onFilter
}
onReset=
{
onReset
}
onClose=
{
onClose
}
hasReset
>
<
PopoverCloseTriggerWrapper
>
<
Flex
gap=
{
3
}
>
{
PRESETS
.
map
(
preset
=>
(
<
Tag
...
...
@@ -94,10 +95,11 @@ const AmountFilter = ({ value = {}, handleFilterChange, onClose }: Props) => {
</
Tag
>
))
}
</
Flex
>
</
PopoverCloseTriggerWrapper
>
<
Flex
mt=
{
3
}
alignItems=
"center"
>
<
Input
value=
{
currentValue
.
from
}
onChange=
{
handleFromChange
}
placeholder=
"From"
type=
"number"
size=
"
xs
"
/>
<
Input
value=
{
currentValue
.
from
}
onChange=
{
handleFromChange
}
placeholder=
"From"
type=
"number"
size=
"
sm
"
/>
<
Text
mx=
{
3
}
>
{
ndash
}
</
Text
>
<
Input
value=
{
currentValue
.
to
}
onChange=
{
handleToChange
}
placeholder=
"To"
type=
"number"
size=
"
xs
"
/>
<
Input
value=
{
currentValue
.
to
}
onChange=
{
handleToChange
}
placeholder=
"To"
type=
"number"
size=
"
sm
"
/>
</
Flex
>
</
TableColumnFilter
>
);
...
...
ui/advancedFilter/filters/AssetFilter.tsx
View file @
7025b758
import
{
Flex
,
Checkbox
,
CheckboxGroup
,
Text
,
Spinner
,
Select
}
from
'
@chakra-ui/react
'
;
import
{
Flex
,
Text
,
Spinner
,
createListCollection
}
from
'
@chakra-ui/react
'
;
import
{
isEqual
}
from
'
es-toolkit
'
;
import
React
from
'
react
'
;
...
...
@@ -7,7 +7,9 @@ import type { TokenInfo } from 'types/api/token';
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
useDebounce
from
'
lib/hooks/useDebounce
'
;
import
Tag
from
'
ui/shared/chakra/Tag
'
;
import
{
Checkbox
,
CheckboxGroup
}
from
'
toolkit/chakra/checkbox
'
;
import
{
Select
}
from
'
toolkit/chakra/select
'
;
import
{
Tag
}
from
'
toolkit/chakra/tag
'
;
import
ClearButton
from
'
ui/shared/ClearButton
'
;
import
*
as
TokenEntity
from
'
ui/shared/entities/token/TokenEntity
'
;
import
FilterInput
from
'
ui/shared/filters/FilterInput
'
;
...
...
@@ -23,6 +25,13 @@ const NAME_PARAM_EXCLUDE = 'token_contract_symbols_to_exclude';
export
type
AssetFilterMode
=
'
include
'
|
'
exclude
'
;
const
collection
=
createListCollection
({
items
:
[
{
label
:
'
Include
'
,
value
:
'
include
'
},
{
label
:
'
Exclude
'
,
value
:
'
exclude
'
},
],
});
// add native token
type
Value
=
Array
<
{
token
:
TokenInfo
;
mode
:
AssetFilterMode
}
>
;
...
...
@@ -31,10 +40,9 @@ type Props = {
handleFilterChange
:
(
filed
:
keyof
AdvancedFilterParams
,
val
:
Array
<
string
>
)
=>
void
;
columnName
:
string
;
isLoading
?:
boolean
;
onClose
?:
()
=>
void
;
};
const
AssetFilter
=
({
value
=
[],
handleFilterChange
,
onClose
}:
Props
)
=>
{
const
AssetFilter
=
({
value
=
[],
handleFilterChange
}:
Props
)
=>
{
const
[
currentValue
,
setCurrentValue
]
=
React
.
useState
<
Value
>
([
...
value
]);
const
[
searchTerm
,
setSearchTerm
]
=
React
.
useState
<
string
>
(
''
);
const
debouncedSearchTerm
=
useDebounce
(
searchTerm
,
300
);
...
...
@@ -43,11 +51,10 @@ const AssetFilter = ({ value = [], handleFilterChange, onClose }: Props) => {
setSearchTerm
(
value
);
},
[]);
const
handleModeSelectChange
=
React
.
useCallback
((
index
:
number
)
=>
(
event
:
React
.
ChangeEvent
<
HTMLSelectElement
>
)
=>
{
const
value
=
event
.
target
.
value
as
AssetFilterMode
;
const
handleModeSelectChange
=
React
.
useCallback
((
index
:
number
)
=>
({
value
}:
{
value
:
Array
<
string
>
})
=>
{
setCurrentValue
(
prev
=>
{
const
newValue
=
[
...
prev
];
newValue
[
index
]
=
{
...
prev
[
index
],
mode
:
value
};
newValue
[
index
]
=
{
...
prev
[
index
],
mode
:
value
[
0
]
as
AssetFilterMode
};
return
newValue
;
});
},
[]);
...
...
@@ -88,11 +95,10 @@ const AssetFilter = ({ value = [], handleFilterChange, onClose }: Props) => {
isTouched=
{
!
isEqual
(
currentValue
.
map
(
i
=>
JSON
.
stringify
(
i
)).
sort
(),
value
.
map
(
i
=>
JSON
.
stringify
(
i
)).
sort
())
}
onFilter=
{
onFilter
}
onReset=
{
onReset
}
onClose=
{
onClose
}
hasReset
>
<
FilterInput
size=
"
xs
"
size=
"
sm
"
onChange=
{
onSearchChange
}
placeholder=
"Token name or symbol"
initialValue=
{
searchTerm
}
...
...
@@ -100,17 +106,15 @@ const AssetFilter = ({ value = [], handleFilterChange, onClose }: Props) => {
{
!
searchTerm
&&
currentValue
.
map
((
item
,
index
)
=>
(
<
Flex
key=
{
item
.
token
.
address
}
alignItems=
"center"
>
<
Select
size=
"xs"
borderRadius=
"base"
value=
{
item
.
mode
}
onChange=
{
handleModeSelectChange
(
index
)
}
size=
"sm"
value=
{
[
item
.
mode
]
}
onValueChange=
{
handleModeSelectChange
(
index
)
}
collection=
{
collection
}
placeholder=
"Select mode"
minW=
"105px"
w=
"105px"
mr=
{
3
}
>
<
option
value=
"include"
>
Include
</
option
>
<
option
value=
"exclude"
>
Exclude
</
option
>
</
Select
>
/>
<
TokenEntity
.
default
token=
{
item
.
token
}
noLink
noCopy
flexGrow=
{
1
}
/>
<
ClearButton
onClick=
{
handleRemove
(
index
)
}
/>
</
Flex
>
...
...
@@ -139,25 +143,19 @@ const AssetFilter = ({ value = [], handleFilterChange, onClose }: Props) => {
{
searchTerm
&&
tokensQuery
.
data
&&
!
tokensQuery
.
data
?.
items
.
length
&&
<
Text
>
No tokens found
</
Text
>
}
{
searchTerm
&&
tokensQuery
.
data
&&
Boolean
(
tokensQuery
.
data
?.
items
.
length
)
&&
(
<
Flex
display=
"flex"
flexDir=
"column"
rowGap=
{
3
}
maxH=
"250px"
overflowY=
"scroll"
mt=
{
3
}
ml=
"-4px"
>
<
CheckboxGroup
value=
{
currentValue
.
map
(
i
=>
i
.
token
.
address
)
}
>
<
CheckboxGroup
value=
{
currentValue
.
map
(
i
=>
i
.
token
.
address
)
}
orientation=
"vertical"
>
{
tokensQuery
.
data
.
items
.
map
(
token
=>
(
<
Flex
key=
{
token
.
address
}
>
<
Checkbox
key=
{
token
.
address
}
value=
{
token
.
address
}
id=
{
token
.
address
}
onChange=
{
onTokenClick
(
token
)
}
overflow=
"hidden"
w=
"100%"
pl=
{
1
}
sx=
{
{
'
.chakra-checkbox__label
'
:
{
flexGrow
:
1
,
},
}
}
>
<
TokenEntity
.
default
token=
{
token
}
noLink
noCopy
/>
</
Checkbox
>
</
Flex
>
))
}
</
CheckboxGroup
>
</
Flex
>
...
...
ui/advancedFilter/filters/MethodFilter.tsx
View file @
7025b758
import
{
Flex
,
Checkbox
,
CheckboxGroup
,
Spinner
,
chakra
}
from
'
@chakra-ui/react
'
;
import
{
Flex
,
Spinner
,
chakra
}
from
'
@chakra-ui/react
'
;
import
{
isEqual
,
differenceBy
}
from
'
es-toolkit
'
;
import
type
{
ChangeEvent
}
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
AdvancedFilterMethodInfo
,
AdvancedFilterParams
}
from
'
types/api/advancedFilter
'
;
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
useDebounce
from
'
lib/hooks/useDebounce
'
;
import
Tag
from
'
ui/shared/chakra/Tag
'
;
import
{
Badge
}
from
'
toolkit/chakra/badge
'
;
import
{
Checkbox
,
CheckboxGroup
}
from
'
toolkit/chakra/checkbox
'
;
import
FilterInput
from
'
ui/shared/filters/FilterInput
'
;
import
TableColumnFilter
from
'
ui/shared/filters/TableColumnFilter
'
;
...
...
@@ -19,10 +19,9 @@ const NAMES_PARAM = 'methods_names';
type
Props
=
{
value
?:
Array
<
AdvancedFilterMethodInfo
>
;
handleFilterChange
:
(
filed
:
keyof
AdvancedFilterParams
,
val
:
Array
<
string
>
)
=>
void
;
onClose
?:
()
=>
void
;
};
const
MethodFilter
=
({
value
=
[],
handleFilterChange
,
onClose
}:
Props
)
=>
{
const
MethodFilter
=
({
value
=
[],
handleFilterChange
}:
Props
)
=>
{
const
[
currentValue
,
setCurrentValue
]
=
React
.
useState
<
Array
<
AdvancedFilterMethodInfo
>>
([
...
value
]);
const
[
searchTerm
,
setSearchTerm
]
=
React
.
useState
<
string
>
(
''
);
const
debouncedSearchTerm
=
useDebounce
(
searchTerm
,
300
);
...
...
@@ -43,9 +42,10 @@ const MethodFilter = ({ value = [], handleFilterChange, onClose }: Props) => {
}
},
[
methodsQuery
.
data
,
value
,
methodsList
]);
const
handleChange
=
React
.
useCallback
((
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
checked
=
event
.
target
.
checked
;
const
id
=
event
.
target
.
id
as
string
|
typeof
RESET_VALUE
;
const
handleChange
:
React
.
FormEventHandler
<
HTMLLabelElement
>
=
React
.
useCallback
((
event
)
=>
{
const
checked
=
(
event
.
target
as
HTMLInputElement
).
checked
;
const
id
=
event
.
currentTarget
.
getAttribute
(
'
data-id
'
);
if
(
id
===
RESET_VALUE
)
{
setCurrentValue
([]);
setMethodsList
(
methodsQuery
.
data
||
[]);
...
...
@@ -75,11 +75,10 @@ const MethodFilter = ({ value = [], handleFilterChange, onClose }: Props) => {
isTouched=
{
!
isEqual
(
currentValue
.
map
(
i
=>
JSON
.
stringify
(
i
)).
sort
(),
value
.
map
(
i
=>
JSON
.
stringify
(
i
)).
sort
())
}
onFilter=
{
onFilter
}
onReset=
{
onReset
}
onClose=
{
onClose
}
hasReset
>
<
FilterInput
size=
"
xs
"
size=
"
sm
"
onChange=
{
onSearchChange
}
placeholder=
"Find by function name/ method ID"
mb=
{
3
}
...
...
@@ -89,26 +88,23 @@ const MethodFilter = ({ value = [], handleFilterChange, onClose }: Props) => {
{
Boolean
(
searchTerm
)
&&
methodsQuery
.
data
?.
length
===
0
&&
<
span
>
No results found.
</
span
>
}
{
methodsQuery
.
data
&&
(
// added negative margin because of checkbox focus styles & overflow hidden
<
Flex
display=
"flex"
flexDir=
"column"
rowGap=
{
3
}
maxH=
"250px"
overflowY=
"scroll"
ml=
"-4px"
>
<
CheckboxGroup
value=
{
currentValue
.
length
?
currentValue
.
map
(
i
=>
i
.
method_id
)
:
[
RESET_VALUE
]
}
>
<
Flex
display=
"flex"
flexDir=
"column"
rowGap=
{
3
}
maxH=
"250px"
overflowY=
"scroll"
>
<
CheckboxGroup
value=
{
currentValue
.
length
?
currentValue
.
map
(
i
=>
i
.
method_id
)
:
[
]
}
orientation=
"vertical"
>
{
(
searchTerm
?
methodsQuery
.
data
:
(
methodsList
||
[])).
map
(
method
=>
(
<
Checkbox
key=
{
method
.
method_id
}
value=
{
method
.
method_id
}
id=
{
method
.
method_id
}
data
-
id=
{
method
.
method_id
}
onChange=
{
handleChange
}
pl=
{
1
}
sx=
{
{
'
.chakra-checkbox__label
'
:
{
flexGrow
:
1
,
},
}
}
>
<
Flex
justifyContent=
"space-between"
alignItems=
"center"
id=
{
method
.
method_id
}
>
<
chakra
.
span
overflow=
"hidden"
whiteSpace=
"nowrap"
textOverflow=
"ellipsis"
>
{
method
.
name
||
method
.
method_id
}
</
chakra
.
span
>
<
Tag
colorScheme=
"gray"
isTruncated
ml=
{
2
}
>
<
Badge
colorPalette=
"gray"
truncated
ml=
"auto"
>
{
method
.
method_id
}
</
Tag
>
</
Badge
>
</
Flex
>
</
Checkbox
>
))
}
...
...
ui/advancedFilter/filters/TypeFilter.tsx
View file @
7025b758
import
{
Flex
,
Checkbox
,
CheckboxGroup
}
from
'
@chakra-ui/react
'
;
import
{
Flex
}
from
'
@chakra-ui/react
'
;
import
{
isEqual
,
without
}
from
'
es-toolkit
'
;
import
type
{
ChangeEvent
}
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
AdvancedFilterParams
,
AdvancedFilterType
}
from
'
types/api/advancedFilter
'
;
import
{
Checkbox
,
CheckboxGroup
}
from
'
toolkit/chakra/checkbox
'
;
import
TableColumnFilter
from
'
ui/shared/filters/TableColumnFilter
'
;
import
{
ADVANCED_FILTER_TYPES_WITH_ALL
}
from
'
../constants
'
;
...
...
@@ -14,48 +14,49 @@ const RESET_VALUE = 'all';
const
FILTER_PARAM
=
'
transaction_types
'
;
type
Props
=
{
value
?:
Array
<
AdvancedFilterType
>
;
value
?:
Array
<
AdvancedFilterType
|
typeof
RESET_VALUE
>
;
handleFilterChange
:
(
filed
:
keyof
AdvancedFilterParams
,
value
:
Array
<
AdvancedFilterType
>
)
=>
void
;
onClose
?:
()
=>
void
;
};
const
TypeFilter
=
({
value
=
[
],
handleFilterChange
,
onClos
e
}:
Props
)
=>
{
const
[
currentValue
,
setCurrentValue
]
=
React
.
useState
<
Array
<
AdvancedFilterType
>>
([
...
value
]);
const
TypeFilter
=
({
value
=
[
RESET_VALUE
],
handleFilterChang
e
}:
Props
)
=>
{
const
[
currentValue
,
setCurrentValue
]
=
React
.
useState
<
Array
<
AdvancedFilterType
|
typeof
RESET_VALUE
>>
([
...
value
]);
const
handleChange
=
React
.
useCallback
((
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
checked
=
event
.
target
.
checked
;
const
id
=
event
.
target
.
id
as
AdvancedFilterType
|
typeof
RESET_VALUE
;
if
(
id
===
RESET_VALUE
)
{
setCurrentValue
([]);
}
else
{
setCurrentValue
(
prev
=>
checked
?
[
...
prev
,
id
]
:
without
(
prev
,
id
));
const
handleChange
=
React
.
useCallback
((
value
:
Array
<
string
>
)
=>
{
setCurrentValue
((
prev
)
=>
{
if
(
value
.
length
===
0
)
{
return
[
RESET_VALUE
];
}
const
diff
=
value
.
filter
(
item
=>
!
prev
.
includes
(
item
));
if
(
diff
.
includes
(
RESET_VALUE
))
{
return
[
RESET_VALUE
];
}
return
without
(
value
as
Array
<
AdvancedFilterType
>
,
RESET_VALUE
);
});
},
[]);
const
onReset
=
React
.
useCallback
(()
=>
setCurrentValue
([]),
[]);
const
onReset
=
React
.
useCallback
(()
=>
setCurrentValue
([
RESET_VALUE
]),
[]);
const
onFilter
=
React
.
useCallback
(()
=>
{
handleFilterChange
(
FILTER_PARAM
,
currentValue
);
handleFilterChange
(
FILTER_PARAM
,
currentValue
.
filter
(
item
=>
item
!==
RESET_VALUE
)
);
},
[
handleFilterChange
,
currentValue
]);
return
(
<
TableColumnFilter
title=
"Type of transfer"
isFilled=
{
currentValue
.
length
>
0
}
isFilled=
{
!
(
currentValue
.
length
===
1
&&
currentValue
[
0
]
===
RESET_VALUE
)
}
isTouched=
{
!
isEqual
(
currentValue
.
sort
(),
value
.
sort
())
}
onFilter=
{
onFilter
}
onReset=
{
onReset
}
onClose=
{
onClose
}
hasReset
>
<
Flex
display=
"flex"
flexDir=
"column"
rowGap=
{
3
}
>
<
CheckboxGroup
value=
{
currentValue
.
length
?
currentValue
:
[
RESET_VALUE
]
}
>
<
CheckboxGroup
value=
{
currentValue
}
onValueChange=
{
handleChange
}
orientation=
"vertical"
>
{
ADVANCED_FILTER_TYPES_WITH_ALL
.
map
(
type
=>
(
<
Checkbox
key=
{
type
.
id
}
value=
{
type
.
id
}
id=
{
type
.
id
}
onChange=
{
handleChange
}
>
{
type
.
name
}
</
Checkbox
>
...
...
ui/pages/AdvancedFilter.tsx
View file @
7025b758
import
{
Table
,
Tbody
,
Tr
,
Th
,
Td
,
Thead
,
Box
,
Text
,
Tag
,
TagCloseButton
,
chakra
,
Flex
,
TagLabel
,
HStack
,
Link
,
}
from
'
@chakra-ui/react
'
;
import
{
omit
}
from
'
es-toolkit
'
;
import
{
useRouter
}
from
'
next/router
'
;
...
...
@@ -31,6 +21,9 @@ import getValuesArrayFromQuery from 'lib/getValuesArrayFromQuery';
import
getQueryParamString
from
'
lib/router/getQueryParamString
'
;
import
{
ADVANCED_FILTER_ITEM
}
from
'
stubs/advancedFilter
'
;
import
{
generateListStub
}
from
'
stubs/utils
'
;
import
{
Link
}
from
'
toolkit/chakra/link
'
;
import
{
TableBody
,
TableCell
,
TableColumnHeader
,
TableHeaderSticky
,
TableRoot
,
TableRow
}
from
'
toolkit/chakra/table
'
;
import
{
Tag
}
from
'
toolkit/chakra/tag
'
;
import
ColumnsButton
from
'
ui/advancedFilter/ColumnsButton
'
;
import
type
{
ColumnsIds
}
from
'
ui/advancedFilter/constants
'
;
import
{
TABLE_COLUMNS
}
from
'
ui/advancedFilter/constants
'
;
...
...
@@ -145,12 +138,12 @@ const AdvancedFilter = () => {
const content = (
<
AddressHighlightProvider
>
<
Box
maxW=
"100%"
overflowX=
"scroll"
whiteSpace=
"nowrap"
>
<
Table
style=
{
{
tableLayout
:
'
fixed
'
}
}
minWidth=
"950px"
w=
"100%"
>
<
T
head
w=
"100%"
display=
"table"
>
<
T
r
>
<
Table
Root
tableLayout=
"fixed"
minWidth=
"950px"
w=
"100%"
>
<
T
ableHeaderSticky
>
<
T
ableRow
>
{
columnsToShow
.
map
(
column
=>
{
return
(
<
T
h
<
T
ableColumnHeader
key=
{
column
.
id
}
isNumeric=
{
column
.
isNumeric
}
minW=
{
column
.
width
}
...
...
@@ -167,16 +160,27 @@ const AdvancedFilter = () => {
searchParams=
{
data
?.
search_params
}
isLoading=
{
isPlaceholderData
}
/>
</
T
h
>
</
T
ableColumnHeader
>
);
})
}
</
T
r
>
</
T
head
>
<
T
body
w=
"100%"
display=
"table"
>
</
T
ableRow
>
</
T
ableHeaderSticky
>
<
T
ableBody
>
{
data
?.
items
.
map
((
item
,
index
)
=>
(
<
Tr
key=
{
item
.
hash
+
String
(
index
)
}
>
{
columnsToShow
.
map
(
column
=>
(
<
Td
<
TableRow
key=
{
item
.
hash
+
String
(
index
)
}
>
{
columnsToShow
.
map
(
column
=>
{
const
textAlign
=
(()
=>
{
if
(
column
.
id
===
'
or_and
'
)
{
return
'
center
'
;
}
if
(
column
.
isNumeric
)
{
return
'
right
'
;
}
return
'
start
'
;
})();
return
(
<
TableCell
key=
{
item
.
hash
+
column
.
id
}
isNumeric=
{
column
.
isNumeric
}
minW=
{
column
.
width
}
...
...
@@ -185,15 +189,16 @@ const AdvancedFilter = () => {
wordBreak=
"break-word"
whiteSpace=
"nowrap"
overflow=
"hidden"
textAlign=
{
column
.
id
===
'
or_and
'
?
'
center
'
:
'
start
'
}
textAlign=
{
textAlign
}
>
<
ItemByColumn
item=
{
item
}
column=
{
column
.
id
}
isLoading=
{
isPlaceholderData
}
/>
</
Td
>
))
}
</
Tr
>
</
TableCell
>
);
})
}
</
TableRow
>
))
}
</
T
b
ody
>
</
Table
>
</
T
ableB
ody
>
</
Table
Root
>
</
Box
>
</
AddressHighlightProvider
>
);
...
...
@@ -223,42 +228,36 @@ const AdvancedFilter = () => {
</
Flex
>
<
HStack
gap=
{
2
}
flexWrap=
"wrap"
mb=
{
6
}
>
{
filterTags
.
map
(
t
=>
(
<
Tag
key=
{
t
.
name
}
colorScheme=
"blue"
display=
"inline-flex"
>
<
TagLabel
>
<
Tag
key=
{
t
.
name
}
colorScheme=
"blue"
display=
"inline-flex"
onClose=
{
onClearFilter
(
t
.
key
)
}
closable
>
<
chakra
.
span
color=
"text_secondary"
>
{
t
.
name
}
:
</
chakra
.
span
>
<
chakra
.
span
color=
"text"
>
{
t
.
value
}
</
chakra
.
span
>
</
TagLabel
>
<
TagCloseButton
onClick=
{
onClearFilter
(
t
.
key
)
}
/>
</
Tag
>
))
}
{
filterTags
.
length
===
0
&&
(
<>
<
Tag
colorScheme=
"blue"
display=
"inline-flex"
>
<
TagLabel
>
<
chakra
.
span
color=
"text_secondary"
>
Type:
</
chakra
.
span
>
<
chakra
.
span
color=
"text"
>
All
</
chakra
.
span
>
</
TagLabel
>
</
Tag
>
<
Tag
colorScheme=
"blue"
display=
"inline-flex"
>
<
TagLabel
>
<
chakra
.
span
color=
"text_secondary"
>
Age:
</
chakra
.
span
>
<
chakra
.
span
color=
"text"
>
7d
</
chakra
.
span
>
</
TagLabel
>
</
Tag
>
</>
)
}
</
HStack
>
<
DataListDisplay
isError=
{
isError
}
items
=
{
data
?.
items
}
items
Num=
{
data
?.
items
.
length
}
emptyText=
"There are no transactions."
content=
{
content
}
actionBar=
{
actionBar
}
filterProps=
{
{
hasActiveFilters
:
Object
.
values
(
filters
).
some
(
Boolean
),
emptyFilteredText
:
'
No match found for current filter
'
,
}
}
/>
>
{
content
}
</
DataListDisplay
>
</>
);
};
...
...
ui/shared/RawInputData.tsx
View file @
7025b758
...
...
@@ -43,7 +43,7 @@ const RawInputData = ({ hex, rightSlot: rightSlotProp, defaultDataType = 'Hex',
mr=
"auto"
>
<
SelectControl
loading=
{
isLoading
}
>
<
SelectValueText
placeholder=
"Select
framework
"
/>
<
SelectValueText
placeholder=
"Select
type
"
/>
</
SelectControl
>
<
SelectContent
>
{
collection
.
items
.
map
((
item
)
=>
(
...
...
ui/shared/entities/address/AddressIconDelegated.tsx
View file @
7025b758
import
{
Box
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
{
Box
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
{
useColorModeValue
}
from
'
toolkit/chakra/color-mode
'
;
interface
Props
{
isVerified
:
boolean
;
}
...
...
ui/shared/entities/validator/ValidatorEntity.tsx
View file @
7025b758
import
type
{
As
}
from
'
@chakra-ui/react
'
;
import
{
chakra
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
...
...
@@ -60,21 +59,20 @@ export interface EntityProps extends EntityBase.EntityBaseProps {
id
:
string
;
}
const
UserOp
Entity
=
(
props
:
EntityProps
)
=>
{
const
Validator
Entity
=
(
props
:
EntityProps
)
=>
{
const
partsProps
=
distributeEntityProps
(
props
);
const
content
=
<
Content
{
...
partsProps
.
content
}
/>;
return
(
<
Container
{
...
partsProps
.
container
}
>
<
Icon
{
...
partsProps
.
icon
}
/>
<
Link
{
...
partsProps
.
link
}
>
<
Content
{
...
partsProps
.
content
}
/>
</
Link
>
{
props
.
noLink
?
content
:
<
Link
{
...
partsProps
.
link
}
>
{
content
}
</
Link
>
}
<
Copy
{
...
partsProps
.
copy
}
/>
</
Container
>
);
};
export
default
React
.
memo
(
chakra
<
As
,
EntityProps
>
(
UserOp
Entity
));
export
default
React
.
memo
(
chakra
(
Validator
Entity
));
export
{
Container
,
...
...
ui/shared/filters/TableColumnFilter.tsx
View file @
7025b758
...
...
@@ -24,7 +24,7 @@ const TableColumnFilter = ({ title, isFilled, isTouched, hasReset, onFilter, onR
},
[
onFilter
]);
return
(
<>
<
Flex
alignItems=
"center"
justifyContent=
"space-between"
>
<
Flex
alignItems=
"center"
justifyContent=
"space-between"
columnGap=
{
6
}
>
<
Text
color=
"text.secondary"
fontWeight=
"600"
>
{
title
}
</
Text
>
{
hasReset
&&
(
<
Button
...
...
ui/shared/filters/TableColumnFilterWrapper.tsx
View file @
7025b758
...
...
@@ -19,6 +19,7 @@ const TableColumnFilterWrapper = ({ columnName, className, children, isLoading,
<
PopoverRoot
>
<
PopoverTrigger
>
<
Button
display=
"inline-flex"
aria
-
label=
{
`filter by ${ columnName }`
}
variant=
"dropdown"
borderWidth=
"0"
...
...
@@ -28,16 +29,9 @@ const TableColumnFilterWrapper = ({ columnName, className, children, isLoading,
selected=
{
selected
}
borderRadius=
"4px"
size=
"sm"
textStyle=
"sm"
fontWeight=
{
500
}
padding=
{
0
}
css=
{
{
'
span:only-child
'
:
{
mx
:
0
,
},
'
span:not(:only-child)
'
:
{
mr
:
'
2px
'
,
},
}
}
>
<
IconSvg
name=
"filter"
w=
"19px"
h=
"19px"
/>
{
Boolean
(
value
)
&&
<
chakra
.
span
>
{
value
}
</
chakra
.
span
>
}
...
...
ui/shared/tagGroupSelect/TagGroupSelect.tsx
View file @
7025b758
...
...
@@ -19,7 +19,7 @@ type Props<T extends string> = {
}
);
const
TagGroupSelect
=
<
T
extends
string
>
(
{
items
,
value
,
isMulti
,
onChange
,
tagSize
}
: Props
<
T
>
) =
>
{
const
TagGroupSelect
=
<
T
extends
string
>
(
{
items
,
value
,
isMulti
,
onChange
,
tagSize
,
...
rest
}
: Props
<
T
>
) =
>
{
const
onItemClick
=
React
.
useCallback
((
event
:
React
.
SyntheticEvent
)
=>
{
const
itemValue
=
(
event
.
currentTarget
as
HTMLDivElement
).
getAttribute
(
'
data-id
'
)
as
T
;
if
(
isMulti
)
{
...
...
@@ -36,7 +36,7 @@ const TagGroupSelect = <T extends string>({ items, value, isMulti, onChange, tag
},
[
isMulti
,
onChange
,
value
]);
return
(
<
HStack
>
<
HStack
{
...
rest
}
>
{
items
.
map
(
item
=>
{
const
isSelected
=
isMulti
?
value
.
includes
(
item
.
id
)
:
value
===
item
.
id
;
return
(
...
...
ui/showcases/Tag.tsx
View file @
7025b758
...
...
@@ -47,6 +47,15 @@ const TagShowcase = () => {
</
SamplesStack
>
</
Section
>
<
Section
>
<
SectionHeader
>
Closable
</
SectionHeader
>
<
SamplesStack
>
<
Sample
label=
"closable: true"
>
<
Tag
closable
>
My tag
</
Tag
>
</
Sample
>
</
SamplesStack
>
</
Section
>
<
Section
>
<
SectionHeader
>
Loading
</
SectionHeader
>
<
SamplesStack
>
...
...
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