@@ -5,11 +5,13 @@ import React from 'react';
55
66import type { AdvancedFilterParams } from 'types/api/advancedFilter' ;
77
8+ import { Field } from 'toolkit/chakra/field' ;
89import { Input } from 'toolkit/chakra/input' ;
910import { InputGroup } from 'toolkit/chakra/input-group' ;
1011import { Select } from 'toolkit/chakra/select' ;
1112import AddButton from 'toolkit/components/buttons/AddButton' ;
1213import { ClearButton } from 'toolkit/components/buttons/ClearButton' ;
14+ import { ADDRESS_REGEXP } from 'toolkit/utils/regexp' ;
1315import TableColumnFilter from 'ui/shared/filters/TableColumnFilter' ;
1416
1517const FILTER_PARAM_TO_INCLUDE = 'to_address_hashes_to_include' ;
@@ -42,8 +44,10 @@ type InputProps = {
4244 isLast : boolean ;
4345 onModeChange : ( { value } : { value : Array < string > } ) => void ;
4446 onChange : ( event : ChangeEvent < HTMLInputElement > ) => void ;
47+ onBlur : ( ) => void ;
4548 onClear : ( ) => void ;
4649 onAddFieldClick : ( ) => void ;
50+ isInvalid : boolean ;
4751} ;
4852
4953type AddressFilter = {
@@ -55,9 +59,9 @@ function addressFilterToKey(filter: AddressFilter) {
5559 return `${ filter . address . toLowerCase ( ) } -${ filter . mode } ` ;
5660}
5761
58- const AddressFilterInput = ( { address, mode, onModeChange, onChange, onClear, isLast, onAddFieldClick } : InputProps ) => {
62+ const AddressFilterInput = ( { address, mode, onModeChange, onChange, onBlur , onClear, isLast, onAddFieldClick, isInvalid } : InputProps ) => {
5963 return (
60- < Flex alignItems = "center " w = "100%" >
64+ < Flex alignItems = "flex-start " w = "100%" >
6165 < Select
6266 collection = { collection }
6367 placeholder = "Select mode"
@@ -68,12 +72,17 @@ const AddressFilterInput = ({ address, mode, onModeChange, onChange, onClear, is
6872 flexShrink = { 0 }
6973 mr = { 3 }
7074 />
71- < InputGroup
75+ < Field
7276 flexGrow = { 1 }
73- endElement = { < ClearButton onClick = { onClear } mx = { 2 } disabled = { ! address } /> }
77+ invalid = { isInvalid }
78+ errorText = "Invalid address format"
7479 >
75- < Input value = { address } onChange = { onChange } placeholder = "Smart contract / Address (0x...)*" size = "sm" autoComplete = "off" />
76- </ InputGroup >
80+ < InputGroup
81+ endElement = { < ClearButton onClick = { onClear } mx = { 2 } disabled = { ! address } /> }
82+ >
83+ < Input value = { address } onChange = { onChange } onBlur = { onBlur } placeholder = "Smart contract / Address (0x...)*" size = "sm" autoComplete = "off" />
84+ </ InputGroup >
85+ </ Field >
7786 { isLast && (
7887 < AddButton
7988 ml = { 2 }
@@ -89,6 +98,7 @@ const emptyItem = { address: '', mode: 'include' as AddressFilterMode };
8998const AddressFilter = ( { type, value = [ ] , handleFilterChange } : Props ) => {
9099 const [ currentValue , setCurrentValue ] =
91100 React . useState < Array < AddressFilter > > ( [ ...value , emptyItem ] ) ;
101+ const [ touched , setTouched ] = React . useState < Array < boolean > > ( value . map ( ( ) => true ) . concat ( false ) ) ;
92102
93103 const handleModeSelectChange = React . useCallback ( ( index : number ) => ( { value } : { value : Array < string > } ) => {
94104 setCurrentValue ( prev => {
@@ -103,6 +113,11 @@ const AddressFilter = ({ type, value = [], handleFilterChange }: Props) => {
103113 newVal [ index ] = { ...newVal [ index ] , address : '' } ;
104114 return newVal ;
105115 } ) ;
116+ setTouched ( prev => {
117+ const newTouched = [ ...prev ] ;
118+ newTouched [ index ] = false ;
119+ return newTouched ;
120+ } ) ;
106121 } , [ ] ) ;
107122
108123 const handleAddressChange = React . useCallback ( ( index : number ) => ( event : React . ChangeEvent < HTMLInputElement > ) => {
@@ -115,11 +130,23 @@ const AddressFilter = ({ type, value = [], handleFilterChange }: Props) => {
115130 } ) ;
116131 } , [ ] ) ;
117132
133+ const handleAddressBlur = React . useCallback ( ( index : number ) => ( ) => {
134+ setTouched ( prev => {
135+ const newTouched = [ ...prev ] ;
136+ newTouched [ index ] = true ;
137+ return newTouched ;
138+ } ) ;
139+ } , [ ] ) ;
140+
118141 const onAddFieldClick = React . useCallback ( ( ) => {
119142 setCurrentValue ( prev => [ ...prev , emptyItem ] ) ;
143+ setTouched ( prev => [ ...prev , false ] ) ;
120144 } , [ ] ) ;
121145
122- const onReset = React . useCallback ( ( ) => setCurrentValue ( [ emptyItem ] ) , [ ] ) ;
146+ const onReset = React . useCallback ( ( ) => {
147+ setCurrentValue ( [ emptyItem ] ) ;
148+ setTouched ( [ false ] ) ;
149+ } , [ ] ) ;
123150
124151 const onFilter = React . useCallback ( ( ) => {
125152 const includeFilterParam = type === 'from' ? FILTER_PARAM_FROM_INCLUDE : FILTER_PARAM_TO_INCLUDE ;
@@ -131,11 +158,14 @@ const AddressFilter = ({ type, value = [], handleFilterChange }: Props) => {
131158 handleFilterChange ( excludeFilterParam , excludeValue . length ? excludeValue : undefined ) ;
132159 } , [ handleFilterChange , currentValue , type ] ) ;
133160
161+ const hasErrors = currentValue . some ( i => Boolean ( i . address ) && ! ADDRESS_REGEXP . test ( i . address ) ) ;
162+ const isTouched = ! isEqual ( currentValue . filter ( i => i . address ) . map ( addressFilterToKey ) . sort ( ) , value . map ( addressFilterToKey ) . sort ( ) ) ;
163+
134164 return (
135165 < TableColumnFilter
136166 title = { type === 'from' ? 'From address' : 'To address' }
137167 isFilled = { Boolean ( currentValue [ 0 ] . address ) }
138- isTouched = { ! isEqual ( currentValue . filter ( i => i . address ) . map ( addressFilterToKey ) . sort ( ) , value . map ( addressFilterToKey ) . sort ( ) ) }
168+ isTouched = { isTouched && ! hasErrors }
139169 onFilter = { onFilter }
140170 onReset = { onReset }
141171 hasReset
@@ -149,8 +179,10 @@ const AddressFilter = ({ type, value = [], handleFilterChange }: Props) => {
149179 isLast = { index === currentValue . length - 1 }
150180 onModeChange = { handleModeSelectChange ( index ) }
151181 onChange = { handleAddressChange ( index ) }
182+ onBlur = { handleAddressBlur ( index ) }
152183 onClear = { handleAddressClear ( index ) }
153184 onAddFieldClick = { onAddFieldClick }
185+ isInvalid = { Boolean ( touched [ index ] ) && Boolean ( item . address ) && ! ADDRESS_REGEXP . test ( item . address ) }
154186 />
155187 ) ) }
156188 </ VStack >
0 commit comments