1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-21 00:43:21 +01:00
metamask-extension/ui/app/pages/swaps/dropdown-search-list/dropdown-search-list.js

201 lines
5.7 KiB
JavaScript
Raw Normal View History

2020-11-03 00:41:28 +01:00
import React, {
useState,
useCallback,
useEffect,
useContext,
useRef,
} from 'react'
2020-10-06 20:28:38 +02:00
import PropTypes from 'prop-types'
import classnames from 'classnames'
import { isEqual } from 'lodash'
import { I18nContext } from '../../../contexts/i18n'
import SearchableItemList from '../searchable-item-list'
import PulseLoader from '../../../components/ui/pulse-loader'
import UrlIcon from '../../../components/ui/url-icon'
2020-11-03 00:41:28 +01:00
export default function DropdownSearchList({
2020-10-06 20:28:38 +02:00
searchListClassName,
itemsToSearch,
selectPlaceHolderText,
fuseSearchKeys,
defaultToAll,
maxListItems,
onSelect,
startingItem,
onOpen,
onClose,
className = '',
externallySelectedItem,
selectorClosedClassName,
loading,
hideRightLabels,
hideItemIf,
listContainerClassName,
}) {
2020-10-23 22:01:50 +02:00
const t = useContext(I18nContext)
const [isOpen, setIsOpen] = useState(false)
const [selectedItem, setSelectedItem] = useState(startingItem)
2020-10-23 23:08:46 +02:00
const close = useCallback(() => {
setIsOpen(false)
2020-11-05 16:35:58 +01:00
onClose?.()
2020-10-23 23:08:46 +02:00
}, [onClose])
2020-11-03 00:41:28 +01:00
const onClickItem = useCallback(
(item) => {
2020-11-05 16:35:58 +01:00
onSelect?.(item)
2020-11-03 00:41:28 +01:00
setSelectedItem(item)
close()
},
[onSelect, close],
)
2020-10-06 20:28:38 +02:00
const onClickSelector = useCallback(() => {
if (!isOpen) {
setIsOpen(true)
2020-11-05 16:35:58 +01:00
onOpen?.()
2020-10-06 20:28:38 +02:00
}
}, [isOpen, onOpen])
const prevExternallySelectedItemRef = useRef()
useEffect(() => {
prevExternallySelectedItemRef.current = externallySelectedItem
})
const prevExternallySelectedItem = prevExternallySelectedItemRef.current
useEffect(() => {
2020-11-03 00:41:28 +01:00
if (
externallySelectedItem &&
!isEqual(externallySelectedItem, selectedItem)
) {
2020-10-06 20:28:38 +02:00
setSelectedItem(externallySelectedItem)
} else if (prevExternallySelectedItem && !externallySelectedItem) {
setSelectedItem(null)
}
}, [externallySelectedItem, selectedItem, prevExternallySelectedItem])
const onKeyUp = (e) => {
if (e.key === 'Escape') {
close()
} else if (e.key === 'Enter') {
onClickSelector(e)
}
}
2020-10-06 20:28:38 +02:00
return (
<div
2020-10-06 20:28:38 +02:00
className={classnames('dropdown-search-list', className)}
onClick={onClickSelector}
onKeyUp={onKeyUp}
tabIndex="0"
2020-10-06 20:28:38 +02:00
>
{!isOpen && (
<div
2020-11-03 00:41:28 +01:00
className={classnames(
'dropdown-search-list__selector-closed-container',
selectorClosedClassName,
)}
2020-10-06 20:28:38 +02:00
>
<div className="dropdown-search-list__selector-closed">
2020-11-03 00:41:28 +01:00
{selectedItem?.iconUrl && (
<UrlIcon
url={selectedItem.iconUrl}
className="dropdown-search-list__selector-closed-icon"
name={selectedItem?.symbol}
/>
)}
{!selectedItem?.iconUrl && (
<div className="dropdown-search-list__default-dropdown-icon" />
)}
2020-10-06 20:28:38 +02:00
<div className="dropdown-search-list__labels">
<div className="dropdown-search-list__item-labels">
<span
2020-11-03 00:41:28 +01:00
className={classnames(
'dropdown-search-list__closed-primary-label',
{
'dropdown-search-list__select-default': !selectedItem?.symbol,
},
)}
>
{selectedItem?.symbol || selectPlaceHolderText}
2020-10-06 20:28:38 +02:00
</span>
</div>
</div>
</div>
<i className="fa fa-caret-down fa-lg dropdown-search-list__caret" />
</div>
)}
{isOpen && (
<>
<SearchableItemList
itemsToSearch={loading ? [] : itemsToSearch}
2020-11-03 00:41:28 +01:00
Placeholder={({ searchQuery }) =>
loading ? (
2020-10-06 20:28:38 +02:00
<div className="dropdown-search-list__loading-item">
<PulseLoader />
<div className="dropdown-search-list__loading-item-text-container">
2020-11-03 00:41:28 +01:00
<span className="dropdown-search-list__loading-item-text">
{t('swapFetchingTokens')}
</span>
2020-10-06 20:28:38 +02:00
</div>
</div>
2020-11-03 00:41:28 +01:00
) : (
2020-10-06 20:28:38 +02:00
<div className="dropdown-search-list__placeholder">
{t('swapBuildQuotePlaceHolderText', [searchQuery])}
</div>
)
2020-11-03 00:41:28 +01:00
}
2020-10-06 20:28:38 +02:00
searchPlaceholderText={t('swapSearchForAToken')}
fuseSearchKeys={fuseSearchKeys}
defaultToAll={defaultToAll}
onClickItem={onClickItem}
maxListItems={maxListItems}
2020-11-03 00:41:28 +01:00
className={classnames(
'dropdown-search-list__token-container',
searchListClassName,
{
'dropdown-search-list--open': isOpen,
},
)}
2020-10-06 20:28:38 +02:00
hideRightLabels={hideRightLabels}
hideItemIf={hideItemIf}
listContainerClassName={listContainerClassName}
/>
<div
className="simple-dropdown__close-area"
onClick={(event) => {
event.stopPropagation()
setIsOpen(false)
2020-11-05 16:35:58 +01:00
onClose?.()
2020-10-06 20:28:38 +02:00
}}
/>
</>
)}
</div>
2020-10-06 20:28:38 +02:00
)
}
DropdownSearchList.propTypes = {
itemsToSearch: PropTypes.array,
onSelect: PropTypes.func,
searchListClassName: PropTypes.string,
2020-11-03 00:41:28 +01:00
fuseSearchKeys: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string,
weight: PropTypes.number,
}),
),
2020-10-06 20:28:38 +02:00
defaultToAll: PropTypes.bool,
maxListItems: PropTypes.number,
startingItem: PropTypes.object,
onOpen: PropTypes.func,
onClose: PropTypes.func,
className: PropTypes.string,
externallySelectedItem: PropTypes.object,
loading: PropTypes.bool,
selectPlaceHolderText: PropTypes.string,
selectorClosedClassName: PropTypes.string,
hideRightLabels: PropTypes.bool,
hideItemIf: PropTypes.func,
listContainerClassName: PropTypes.string,
}