import { useState, useEffect } from 'react' import _ from 'lodash' import { BridgeableToken } from 'types' import usePopover from '@/hooks/usePopoverRef' import { TokenBalance } from '@/utils/actions/fetchTokenBalances' import { DownArrow } from '@/components/icons/DownArrow' import { SearchInput } from '@/components/ui/SearchInput' import { TokenOption } from '@/components/ui/TokenOption' type PopoverSelectProps = { options: BridgeableToken[] remaining: BridgeableToken[] balances: TokenBalance[] onSelect: (selected: BridgeableToken) => void selected: BridgeableToken isOrigin: boolean } export const TokenPopoverSelect = ({ options, remaining, balances, onSelect, selected, isOrigin, }: PopoverSelectProps) => { const { popoverRef, isOpen, togglePopover, closePopover } = usePopover() const handleSelect = (option: BridgeableToken) => { onSelect(option) closePopover() } /** Merge options with balances to sort */ const optionsWithBalances: TokenBalance[] = mergeTokenOptionsWithBalances( options, balances ) /** Merge remaining with balances to sort */ const remainingWithBalances: TokenBalance[] = mergeTokenOptionsWithBalances( remaining, balances ) const sortedOptionsWithBalances: TokenBalance[] = _.orderBy( optionsWithBalances, ['parsedBalance', 'token.priorityRank'], ['desc', 'asc'] ) const sortedRemainingWithBalances: TokenBalance[] = _.orderBy( remainingWithBalances, ['parsedBalance', 'token.priorityRank'], ['desc', 'asc'] ) const { filterValue, setFilterValue, filteredOptions: filteredSortedOptionsWithBalances, filteredRemaining: filteredSortedRemainingWithBalances, hasFilteredRemaining, hasFilteredResults, } = useTokenInputFilter( sortedOptionsWithBalances, sortedRemainingWithBalances, isOpen ) return (
togglePopover()} style={{ background: 'var(--synapse-select-bg)' }} className={` flex px-2.5 py-1.5 gap-2 items-center rounded text-[--synapse-select-text] whitespace-nowrap border border-solid border-[--synapse-select-border] cursor-pointer hover:border-[--synapse-focus] `} > {selected?.imgUrl && ( {`${selected.symbol} )} {selected?.symbol || 'Token'}
{isOpen && (
{hasFilteredResults ? ( ) : (
No tokens found
matching '{filterValue}'.
)}
)}
) } const useTokenInputFilter = ( options: TokenBalance[], remaining: TokenBalance[], isActive: boolean ) => { const [filterValue, setFilterValue] = useState('') useEffect(() => { if (!isActive) { setFilterValue('') } }, [isActive]) const filterTokens = (tokens: TokenBalance[], filter: string) => { const lowerFilter = filter.toLowerCase() return _.filter(tokens, (option) => { const symbol = option.token.symbol.toLowerCase() return symbol.includes(lowerFilter) || symbol === lowerFilter }) } const filteredOptions = filterTokens(options, filterValue) const filteredRemaining = filterTokens(remaining, filterValue) const hasFilteredOptions = !_.isEmpty(filteredOptions) const hasFilteredRemaining = !_.isEmpty(filteredRemaining) const hasFilteredResults = hasFilteredOptions || hasFilteredRemaining return { filterValue, setFilterValue, filteredOptions, filteredRemaining, hasFilteredOptions, hasFilteredRemaining, hasFilteredResults, } } const mergeTokenOptionsWithBalances = ( tokens: BridgeableToken[], balances: TokenBalance[] ) => { return tokens?.map((token) => { /** If token balance does not exist, set balance to null */ if (_.isArray(balances) && _.isEmpty(balances)) { return { token, balance: null, parsedBalance: null, } } else { const matchedTokenBalance: TokenBalance = balances?.find( (currentToken: TokenBalance) => currentToken.token === token ) return { token, balance: matchedTokenBalance?.balance, parsedBalance: matchedTokenBalance?.parsedBalance, } } }) }