This repository has been archived by the owner on Oct 10, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- [x] `AssetSelect2` component + story - [x] Update `i18n`
- Loading branch information
Showing
10 changed files
with
269 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
src/renderer/components/uielements/assets/assetSelect/AssetSelect2.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { useState } from 'react' | ||
|
||
import { ComponentMeta } from '@storybook/react' | ||
import { | ||
Asset, | ||
AssetBCH, | ||
AssetBNB, | ||
AssetBTC, | ||
AssetDOGE, | ||
AssetETH, | ||
AssetLTC, | ||
AssetRuneNative | ||
} from '@xchainjs/xchain-util' | ||
|
||
import { Network } from '../../../../../shared/api/types' | ||
import { AssetBUSDBD1 } from '../../../../const' | ||
import * as AT from '../../../../storybook/argTypes' | ||
import { AssetSelect2 as Component } from './AssetSelect2' | ||
|
||
const assets = [AssetBTC, AssetBNB, AssetRuneNative, AssetETH, AssetLTC, AssetBCH, AssetDOGE, AssetBUSDBD1] | ||
|
||
type Args = { | ||
network: Network | ||
dialogHeadline: string | ||
onSelect: (asset: Asset) => void | ||
} | ||
|
||
const Template = ({ network, onSelect, dialogHeadline }: Args) => { | ||
const [asset, setAsset] = useState<Asset>(AssetBNB) | ||
return ( | ||
<Component | ||
asset={asset} | ||
assets={assets} | ||
onSelect={(asset) => { | ||
onSelect(asset) | ||
setAsset(asset) | ||
}} | ||
dialogHeadline={dialogHeadline} | ||
network={network} | ||
/> | ||
) | ||
} | ||
export const Default = Template.bind({}) | ||
|
||
const meta: ComponentMeta<typeof Template> = { | ||
component: Template, | ||
title: 'Components/Assets/AssetSelect2', | ||
argTypes: { | ||
network: AT.network, | ||
onSelect: { | ||
action: 'onSelect' | ||
} | ||
}, | ||
args: { network: 'mainnet', dialogHeadline: 'Change asset' }, | ||
decorators: [ | ||
(Story) => ( | ||
<div className="flex min-h-full w-full flex-col items-center justify-center bg-white"> | ||
<h1 className="uppercase text-gray2">Random headline</h1> | ||
<p className="uppercase text-gray1">Some random text</p> | ||
<Story /> | ||
</div> | ||
) | ||
] | ||
} | ||
|
||
export default meta |
186 changes: 186 additions & 0 deletions
186
src/renderer/components/uielements/assets/assetSelect/AssetSelect2.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
import React, { useCallback, useState, useMemo, useRef } from 'react' | ||
|
||
import { Dialog } from '@headlessui/react' | ||
import { ArchiveBoxXMarkIcon, ChevronDownIcon, XMarkIcon } from '@heroicons/react/24/outline' | ||
import { Asset, assetToString } from '@xchainjs/xchain-util' | ||
import * as A from 'fp-ts/lib/Array' | ||
import * as FP from 'fp-ts/lib/function' | ||
import * as NEA from 'fp-ts/lib/NonEmptyArray' | ||
import * as O from 'fp-ts/lib/Option' | ||
import { useIntl } from 'react-intl' | ||
|
||
import { Network } from '../../../../../shared/api/types' | ||
import { emptyString } from '../../../../helpers/stringHelper' | ||
import { BaseButton } from '../../button' | ||
import { InputSearch } from '../../input' | ||
import { AssetData } from '../assetData' | ||
|
||
export type Props = { | ||
asset: Asset | ||
assets: Asset[] | ||
onSelect: (_: Asset) => void | ||
className?: string | ||
showAssetName?: boolean | ||
dialogHeadline?: string | ||
disabled?: boolean | ||
network: Network | ||
} | ||
|
||
export const AssetSelect2: React.FC<Props> = (props): JSX.Element => { | ||
const { | ||
asset, | ||
assets = [], | ||
onSelect = (_: Asset) => {}, | ||
className = '', | ||
dialogHeadline = emptyString, | ||
showAssetName = true, | ||
disabled = false, | ||
network | ||
} = props | ||
|
||
const [openMenu, setOpenMenu] = useState<boolean>(false) | ||
|
||
const [searchValue, setSearchValue] = useState<string>(emptyString) | ||
|
||
const clearSearchValue = useCallback(() => { | ||
setSearchValue(emptyString) | ||
}, []) | ||
|
||
const intl = useIntl() | ||
|
||
const handleDropdownButtonClicked = (e: React.MouseEvent) => { | ||
e.stopPropagation() | ||
setOpenMenu(!openMenu) | ||
} | ||
|
||
const handleChangeAsset = useCallback( | ||
async (asset: Asset) => { | ||
onSelect(asset) | ||
setOpenMenu(false) | ||
clearSearchValue() | ||
}, | ||
[clearSearchValue, onSelect] | ||
) | ||
|
||
const filteredAssets = useMemo( | ||
() => | ||
FP.pipe( | ||
assets, | ||
A.filter((asset) => | ||
// filter assets depending on search input | ||
searchValue ? assetToString(asset).toLowerCase().includes(searchValue.toLowerCase()) : true | ||
) | ||
), | ||
[assets, searchValue] | ||
) | ||
const renderAssets = useMemo( | ||
() => | ||
FP.pipe( | ||
filteredAssets, | ||
NEA.fromArray, | ||
O.fold( | ||
() => ( | ||
<div className="flex h-full w-full flex-col items-center justify-center px-20px py-50px"> | ||
<h2 className="mb-10px text-[14px] uppercase text-gray1 dark:text-gray1d"> | ||
{intl.formatMessage({ id: 'common.noResult' })} | ||
</h2> | ||
<ArchiveBoxXMarkIcon className="h-[75px] w-[75px] text-gray0 dark:text-gray0d" /> | ||
</div> | ||
), | ||
(assets) => ( | ||
<div className="w-full overflow-y-auto"> | ||
{FP.pipe( | ||
assets, | ||
NEA.map((asset) => ( | ||
<BaseButton | ||
key={assetToString(asset)} | ||
onClick={() => handleChangeAsset(asset)} | ||
className="w-full !justify-start hover:bg-gray0 hover:dark:bg-gray0d"> | ||
<AssetData asset={asset} network={network} className="" /> | ||
</BaseButton> | ||
)) | ||
)} | ||
</div> | ||
) | ||
) | ||
), | ||
[filteredAssets, handleChangeAsset, intl, network] | ||
) | ||
|
||
const searchHandler = useCallback(({ target }: React.ChangeEvent<HTMLInputElement>) => { | ||
const { value } = target | ||
setSearchValue(value.replace(/\s/g, '')) | ||
}, []) | ||
|
||
const onCloseMenu = useCallback(() => { | ||
setOpenMenu(false) | ||
clearSearchValue() | ||
}, [clearSearchValue]) | ||
|
||
// Ref to `InputSearch` - needed for intial focus in dialog | ||
// @see https://headlessui.com/react/dialog#managing-initial-focus | ||
const inputSearchRef = useRef(null) | ||
|
||
const renderMenu = useMemo( | ||
() => ( | ||
<Dialog as="div" className="relative z-10" initialFocus={inputSearchRef} open={openMenu} onClose={onCloseMenu}> | ||
{/* backdrop */} | ||
<div className="fixed inset-0 bg-bg0/80 dark:bg-bg0d/80" aria-hidden="true" /> | ||
|
||
{/* container to center the panel */} | ||
<div className="fixed inset-0 flex items-center justify-center p-4"> | ||
{/* dialog panel */} | ||
<Dialog.Panel | ||
className="relative mx-auto flex h-[50%] min-h-[350px] max-w-[250px] | ||
flex-col items-center rounded-[10px] | ||
bg-bg0 px-20px | ||
pb-20px pt-30px shadow-lg dark:bg-bg0d | ||
"> | ||
<BaseButton | ||
className="absolute right-[15px] top-10px !p-0 text-gray1 hover:text-gray2 dark:text-gray1d hover:dark:text-gray2d" | ||
onClick={() => setOpenMenu(false)}> | ||
<XMarkIcon className="h-20px w-20px text-inherit" /> | ||
</BaseButton> | ||
{dialogHeadline && ( | ||
<h1 className="!my-5px text-center font-mainSemiBold text-[17px] uppercase text-text2 dark:text-text2d"> | ||
{dialogHeadline} | ||
</h1> | ||
)} | ||
<InputSearch | ||
ref={inputSearchRef} | ||
className="my-10px" | ||
size="large" | ||
onChange={searchHandler} | ||
onCancel={clearSearchValue} | ||
placeholder={intl.formatMessage({ id: 'common.search' })} | ||
/> | ||
{renderAssets} | ||
</Dialog.Panel> | ||
</div> | ||
</Dialog> | ||
), | ||
[openMenu, onCloseMenu, dialogHeadline, searchHandler, clearSearchValue, intl, renderAssets] | ||
) | ||
|
||
const hideButton = !assets.length | ||
const disableButton = disabled || hideButton | ||
|
||
return ( | ||
<div> | ||
<BaseButton | ||
className={`group py-[2px] px-10px focus:outline-none ${className}`} | ||
disabled={disableButton} | ||
onClick={handleDropdownButtonClicked}> | ||
<AssetData noTicker={!showAssetName} className="" asset={asset} network={network} /> | ||
{!hideButton && ( | ||
<ChevronDownIcon | ||
className={`ease h-20px w-20px text-turquoise ${openMenu ? 'rotate-180' : 'rotate-0'} | ||
group-hover:rotate-180 | ||
`} | ||
/> | ||
)} | ||
</BaseButton> | ||
{renderMenu} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters