From e085ae5c1c6f0eecfb6f088877738498cb9ad9e2 Mon Sep 17 00:00:00 2001 From: Jun Ma Date: Tue, 9 May 2023 10:31:49 +0800 Subject: [PATCH] feat: support new customized assets --- .../components/SUDTMigrateDialog/index.tsx | 64 ++-- .../sUDTMigrateDialog.module.scss | 33 +- .../SUDTMigrateToExistAccountDialog/index.tsx | 42 +-- ...UDTMigrateToExistAccountDialog.module.scss | 31 +- .../SUDTMigrateToNewAccountDialog/hooks.ts | 3 + .../SUDTMigrateToNewAccountDialog/index.tsx | 69 ++-- .../sUDTMigrateToNewAccountDialog.module.scss | 14 +- .../src/components/SpecialAsset/index.tsx | 219 ------------- .../SpecialAsset/specialAsset.module.scss | 123 ------- .../src/components/SpecialAssetList/hooks.ts | 131 ++++++-- .../src/components/SpecialAssetList/index.tsx | 307 +++++++++++++----- .../specialAssetList.module.scss | 48 +-- packages/neuron-ui/src/locales/en.json | 3 + packages/neuron-ui/src/locales/zh-tw.json | 3 + packages/neuron-ui/src/locales/zh.json | 3 + .../src/stories/SpecialAsset.stories.tsx | 206 ------------ packages/neuron-ui/src/utils/const.ts | 2 + packages/neuron-ui/src/utils/hooks/index.ts | 5 +- .../src/widgets/InputSelect/index.tsx | 6 +- .../InputSelect/input-select.module.scss | 160 +++++---- 20 files changed, 602 insertions(+), 870 deletions(-) delete mode 100644 packages/neuron-ui/src/components/SpecialAsset/index.tsx delete mode 100644 packages/neuron-ui/src/components/SpecialAsset/specialAsset.module.scss delete mode 100644 packages/neuron-ui/src/stories/SpecialAsset.stories.tsx diff --git a/packages/neuron-ui/src/components/SUDTMigrateDialog/index.tsx b/packages/neuron-ui/src/components/SUDTMigrateDialog/index.tsx index 98fbd7f9b4..4dd70e3f6b 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTMigrateDialog/index.tsx @@ -1,7 +1,8 @@ -import React, { useMemo } from 'react' +import React, { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { SpecialAssetCell } from 'components/SpecialAssetList' import { MIN_CKB_REQUIRED_BY_NORMAL_SUDT, SHANNON_CKB_RATIO } from 'utils/const' +import Dialog from 'widgets/Dialog' import styles from './sUDTMigrateDialog.module.scss' const items = [ @@ -21,31 +22,56 @@ const leastSUDTAcccountCapacity = BigInt(MIN_CKB_REQUIRED_BY_NORMAL_SUDT) * BigI const SUDTMigrateDialog = ({ cell, + isDialogOpen, + onCancel, openDialog, }: { cell: SpecialAssetCell - openDialog?: (e: React.SyntheticEvent) => void + isDialogOpen: boolean + onCancel: () => void + openDialog?: (type: string) => void }) => { const [t] = useTranslation() const isNewSUDTAccountDisabled = useMemo(() => BigInt(cell.capacity) < leastSUDTAcccountCapacity, [cell.capacity]) + const [type, setType] = useState('') + + const handleClick = (e: React.SyntheticEvent) => setType(e.currentTarget.dataset.type!) + const handleCancel = () => { + setType('') + onCancel?.() + } + return ( -
-

{t('migrate-sudt.title')}

- {items.map((v, idx) => ( -
{}} - role="button" - tabIndex={idx} - > -
{t(v.title)}
-
{t(v.subTitle)}
-
- ))} -
+ openDialog?.(type) }} + disabled={!type} + > + <> +
{t('migrate-sudt.choose-title')}
+ {items.map((v, idx) => ( +
{}} + role="button" + tabIndex={idx} + > +
{t(v.title)}
+
{t(v.subTitle)}
+
+ ))} + +
) } diff --git a/packages/neuron-ui/src/components/SUDTMigrateDialog/sUDTMigrateDialog.module.scss b/packages/neuron-ui/src/components/SUDTMigrateDialog/sUDTMigrateDialog.module.scss index a1d3aa90cf..cf3fa64f5b 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateDialog/sUDTMigrateDialog.module.scss +++ b/packages/neuron-ui/src/components/SUDTMigrateDialog/sUDTMigrateDialog.module.scss @@ -1,22 +1,45 @@ @import '../../styles/mixin.scss'; .container { - padding: 12px 48px; + width: 680px; + + .chooseTitle { + margin: -4px 0 8px 0; + color: var(--secondary-text-color); + } + .title { + color: var(--main-text-color); + font-weight: 500; + font-size: 14px; + margin-bottom: 8px; + } } .actionContainer { - border: 1px solid #000; - padding: 12px; + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 16px; margin-bottom: 12px; font-size: 14px; cursor: pointer !important; + &:hover, + &.active { + &, + .title, + .subTitle { + color: var(--primary-color); + border-color: var(--primary-color); + } + } + * { cursor: pointer !important; } .subTitle { - font-size: 12px; + font-size: 14px; + color: var(--third-text-color); } &.disabled { @@ -30,4 +53,4 @@ .dialog { @include dialog-container; -} \ No newline at end of file +} diff --git a/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/index.tsx b/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/index.tsx index 2b27e7a47d..0a35429c4a 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/index.tsx @@ -1,8 +1,8 @@ import React, { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { SpecialAssetCell } from 'components/SpecialAssetList' -import Button from 'widgets/Button' import TextField from 'widgets/TextField' +import Dialog from 'widgets/Dialog' import { AnyoneCanPayLockInfoOnAggron, getSUDTAmount, isSuccessResponse, validateSpecificAddress } from 'utils' import InputSelect from 'widgets/InputSelect' import { generateSudtMigrateAcpTx } from 'services/remote' @@ -12,20 +12,22 @@ import styles from './sUDTMigrateToExistAccountDialog.module.scss' const SUDTMigrateToExistAccountDialog = ({ cell, - closeDialog, tokenInfo, sUDTAccounts, isMainnet, walletID, isLightClient, + isDialogOpen, + onCancel, }: { cell: SpecialAssetCell - closeDialog: () => void tokenInfo?: Controller.GetTokenInfoList.TokenInfo sUDTAccounts: State.SUDTAccount[] isMainnet: boolean walletID: string isLightClient: boolean + isDialogOpen: boolean + onCancel: () => void }) => { const [t] = useTranslation() const [address, setAddress] = useState('') @@ -54,7 +56,7 @@ const SUDTMigrateToExistAccountDialog = ({ outPoint: cell.outPoint, acpAddress: address, }).then(res => { - closeDialog() + onCancel() if (isSuccessResponse(res)) { if (res.result) { dispatch({ @@ -82,13 +84,22 @@ const SUDTMigrateToExistAccountDialog = ({ }) } }) - }, [cell.outPoint, address, t, closeDialog, dispatch, walletID]) + }, [cell.outPoint, address, t, onCancel, dispatch, walletID]) + return ( -
-

{t('migrate-sudt.transfer-to-exist-account.title')}

-
+ + <>
-
{`${t('migrate-sudt.address')} *`}
+
{t('migrate-sudt.address')}
({ label: v, value: v }))} onChange={onAddressChange} @@ -105,17 +116,8 @@ const SUDTMigrateToExistAccountDialog = ({ required disabled /> -
-
-
-
+ + ) } diff --git a/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/sUDTMigrateToExistAccountDialog.module.scss b/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/sUDTMigrateToExistAccountDialog.module.scss index 6c6d2be4e6..24f52dcbb8 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/sUDTMigrateToExistAccountDialog.module.scss +++ b/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/sUDTMigrateToExistAccountDialog.module.scss @@ -1,32 +1,25 @@ @import '../../styles/mixin.scss'; -.actions { - display: flex; - justify-content: flex-end; - margin-top: 8px; - - & > button { - margin-left: 4px; - } +.container { + width: 680px; } .addressContainer { - width: 424px; - margin-bottom: 8px; + margin-bottom: 20px; + + .addressLabel::after { + display: inline; + content: '*'; + padding-left: 10px; + } & > div:first-child { font-size: 0.875rem; line-height: 1.125rem; - color: #000000; - margin-bottom: 2px; + color: #8da394; + margin-bottom: 10px; display: block; } - - .addressInputSelect { - & > div:first-child { - height: 26px; - } - } } .error { @@ -34,4 +27,4 @@ font-size: 12px; width: 100%; word-break: break-all; -} \ No newline at end of file +} diff --git a/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/hooks.ts b/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/hooks.ts index 41476f109c..3def20fd36 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/hooks.ts +++ b/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/hooks.ts @@ -5,6 +5,7 @@ import { useSUDTAccountInfoErrors } from 'utils' export type TokenInfoType = Omit & { tokenId: string accountName: string + balance?: string } export const useTokenInfo = ({ @@ -24,6 +25,7 @@ export const useTokenInfo = ({ symbol: '', tokenName: '', decimal: '', + balance: '', }) useEffect(() => { if (findTokenInfo) { @@ -33,6 +35,7 @@ export const useTokenInfo = ({ symbol: findTokenInfo.symbol, tokenName: findTokenInfo.tokenName, decimal: findTokenInfo.decimal, + balance: '', }) } }, [findTokenInfo]) diff --git a/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/index.tsx b/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/index.tsx index 5b45f6ca87..e170ac192a 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/index.tsx @@ -1,8 +1,8 @@ import React, { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { SpecialAssetCell } from 'components/SpecialAssetList' -import Button from 'widgets/Button' import TextField from 'widgets/TextField' +import Dialog from 'widgets/Dialog' import { getSUDTAmount, isSuccessResponse } from 'utils' import { generateSudtMigrateAcpTx } from 'services/remote' import { AppActions, useDispatch } from 'states' @@ -10,8 +10,9 @@ import { useTokenInfo, TokenInfoType } from './hooks' import styles from './sUDTMigrateToNewAccountDialog.module.scss' const fields: { key: keyof TokenInfoType; label: string }[] = [ - { key: 'accountName', label: 'account-name' }, { key: 'tokenId', label: 'token-id' }, + { key: 'balance', label: 'balance' }, + { key: 'accountName', label: 'account-name' }, { key: 'tokenName', label: 'token-name' }, { key: 'symbol', label: 'symbol' }, { key: 'decimal', label: 'decimal' }, @@ -19,16 +20,18 @@ const fields: { key: keyof TokenInfoType; label: string }[] = [ const SUDTMigrateToNewAccountDialog = ({ cell, - closeDialog, tokenInfo: findTokenInfo, sUDTAccounts, walletID, + isDialogOpen, + onCancel, }: { cell: SpecialAssetCell - closeDialog: () => void tokenInfo?: Controller.GetTokenInfoList.TokenInfo sUDTAccounts: State.SUDTAccount[] walletID: string + isDialogOpen: boolean + onCancel: () => void }) => { const [t] = useTranslation() const dispatch = useDispatch() @@ -47,7 +50,7 @@ const SUDTMigrateToNewAccountDialog = ({ generateSudtMigrateAcpTx({ outPoint: cell.outPoint, }).then(res => { - closeDialog() + onCancel() if (isSuccessResponse(res)) { if (res.result) { dispatch({ @@ -84,31 +87,39 @@ const SUDTMigrateToNewAccountDialog = ({ }) } }) - }, [cell, t, closeDialog, walletID, tokenInfo, dispatch, sudtAmount]) + }, [cell, t, onCancel, walletID, tokenInfo, dispatch, sudtAmount]) + + const renderList = fields.map(field => { + return field.key === 'balance' ? ( + + ) : ( + + ) + }) + return ( -
-

{t('migrate-sudt.turn-into-new-account.title')}

-
- {fields.map(field => ( - - ))} - -
-
-
-
+ +
{renderList}
+
) } diff --git a/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/sUDTMigrateToNewAccountDialog.module.scss b/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/sUDTMigrateToNewAccountDialog.module.scss index a53492b92b..6c6a150747 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/sUDTMigrateToNewAccountDialog.module.scss +++ b/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/sUDTMigrateToNewAccountDialog.module.scss @@ -1,9 +1,9 @@ -.actions { - display: flex; - justify-content: flex-end; - margin-top: 8px; +.container { + width: 680px; - & > button { - margin-left: 4px; + .filedWrap { + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 24px; } -} \ No newline at end of file +} diff --git a/packages/neuron-ui/src/components/SpecialAsset/index.tsx b/packages/neuron-ui/src/components/SpecialAsset/index.tsx deleted file mode 100644 index cf55d39de9..0000000000 --- a/packages/neuron-ui/src/components/SpecialAsset/index.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import React, { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import Button from 'widgets/Button' -import CopyZone from 'widgets/CopyZone' -import { openExternal } from 'services/remote' -import { ckbCore } from 'services/chain' -import { - ConnectionStatus, - uniformTimeFormatter, - shannonToCKBFormatter, - getExplorerUrl, - PresetScript, - epochParser, - toUint128Le, - sudtValueToAmount, - nftFormatter, - getSUDTAmount, -} from 'utils' -import styles from './specialAsset.module.scss' - -const MS_PER_EPOCHS = 4 * 60 * 60 * 1000 - -interface LocktimeAssetInfo { - data: string - lock: PresetScript.Locktime - type: string -} - -interface ChequeAssetInfo { - data: 'claimable' | 'withdraw-able' - lock: PresetScript.Cheque - type: '' -} - -enum NFTType { - NFT = 'NFT', - NFTClass = 'NFTClass', - NFTIssuer = 'NFTIssuer', -} - -interface NFTAssetInfo { - data: '' | 'transferable' - lock: '' - type: NFTType -} - -type UnknownAssetInfo = Record<'data' | 'lock' | 'type', string> - -export type AssetInfo = LocktimeAssetInfo | ChequeAssetInfo | UnknownAssetInfo | NFTAssetInfo - -export interface SpecialAssetProps { - cell: CKBComponents.CellOutput & { - outPoint: CKBComponents.OutPoint - data: string - } - datetime: number - isMainnet: boolean - epoch: string - assetInfo: AssetInfo - onAction: any - connectionStatus: State.ConnectionStatus - bestKnownBlockTimestamp: number - tokenInfoList: Array -} - -const SpecialAsset = ({ - cell: { - outPoint: { txHash, index }, - capacity, - lock, - type, - data, - }, - datetime, - isMainnet, - epoch, - assetInfo, - onAction, - connectionStatus, - bestKnownBlockTimestamp, - tokenInfoList, -}: SpecialAssetProps) => { - const [t] = useTranslation() - const [date, time] = uniformTimeFormatter(datetime).split(' ') - let targetTime: undefined | number - let status: - | 'user-defined-asset' - | 'locked-asset' - | 'claim-asset' - | 'withdraw-asset' - | 'transfer-nft' - | 'user-defined-token' = 'user-defined-asset' - let epochsInfo: Record<'target' | 'current', number> | undefined - let amount: string = `${shannonToCKBFormatter(capacity)} CKB` - let amountToCopy: string = shannonToCKBFormatter(capacity, false, '') - - switch (assetInfo.lock) { - case PresetScript.Locktime: { - const targetEpochInfo = epochParser(ckbCore.utils.toUint64Le(`0x${lock.args.slice(-16)}`)) - const currentEpochInfo = epochParser(epoch) - const targetEpochFraction = - Number(targetEpochInfo.length) > 0 ? Number(targetEpochInfo.index) / Number(targetEpochInfo.length) : 1 - epochsInfo = { - target: Number(targetEpochInfo.number) + Math.min(targetEpochFraction, 1), - current: Number(currentEpochInfo.number) + Number(currentEpochInfo.index) / Number(currentEpochInfo.length), - } - targetTime = bestKnownBlockTimestamp + (epochsInfo.target - epochsInfo.current) * MS_PER_EPOCHS - if (epochsInfo.target - epochsInfo.current > 0) { - status = 'locked-asset' - } else { - status = 'claim-asset' - } - break - } - case PresetScript.Cheque: { - status = (assetInfo as ChequeAssetInfo).data === 'claimable' ? 'claim-asset' : 'withdraw-asset' - - if (status === 'withdraw-asset') { - const DAY = 86_400_000 - targetTime = datetime + DAY - } - - try { - const tokenInfo = tokenInfoList.find(info => info.tokenID === type?.args) - if (!tokenInfo) { - throw new Error('Token info not found') - } - const balance = BigInt(toUint128Le(data)).toString() - amount = `${sudtValueToAmount(balance, tokenInfo.decimal)} ${tokenInfo.symbol}` - amountToCopy = sudtValueToAmount(balance, tokenInfo.decimal, false, '') - } catch { - amount = 'sUDT Token' - amountToCopy = 'sUDT Token' - } - break - } - case PresetScript.SUDT: { - status = 'user-defined-token' - const tokenInfo = tokenInfoList.find(info => info.tokenID === type?.args) - const amountInfo = getSUDTAmount({ tokenInfo, data }) - amount = amountInfo.amount - amountToCopy = amountInfo.amountToCopy - break - } - default: { - // ignore - } - } - - const onViewDetail = useCallback(() => { - const explorerUrl = getExplorerUrl(isMainnet) - openExternal(`${explorerUrl}/transaction/${txHash}#${index}`) - }, [isMainnet, txHash, index]) - - const isLockedCheque = status === 'withdraw-asset' && Date.now() < targetTime! - const isNFTTransferable = assetInfo.type === NFTType.NFT && assetInfo.data === 'transferable' - const isNFTClassOrIssuer = assetInfo.type === NFTType.NFTClass || assetInfo.type === NFTType.NFTIssuer - - if (assetInfo.type === NFTType.NFT) { - amount = nftFormatter(type?.args) - status = 'transfer-nft' - } else if (isNFTClassOrIssuer || assetInfo.type === 'Unknown') { - amount = t('special-assets.unknown-asset') - } - - return ( -
-
- {date} - {time} -
- - {amount} - -
- {isNFTClassOrIssuer || (assetInfo.type === NFTType.NFT && !isNFTTransferable) ? null : ( -
-
- ) -} - -SpecialAsset.displayName = 'SpecialAsset' -export default SpecialAsset diff --git a/packages/neuron-ui/src/components/SpecialAsset/specialAsset.module.scss b/packages/neuron-ui/src/components/SpecialAsset/specialAsset.module.scss deleted file mode 100644 index 603d1013d2..0000000000 --- a/packages/neuron-ui/src/components/SpecialAsset/specialAsset.module.scss +++ /dev/null @@ -1,123 +0,0 @@ -@import '../../styles/mixin.scss'; - -@mixin tooltip { - &:before { - position: absolute; - display: none; - content: attr(data-tooltip); - bottom: calc(100% + 8px); - left: 50%; - transform: translateX(-50%); - font-size: 0.5625rem; - line-height: 2em; - letter-spacing: 0.45px; - background: #fff; - color: #000000; - text-align: center; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - opacity: 0; - padding: 3px 9px; - } - - &:after { - display: none; - position: absolute; - content: ''; - border: 9px solid transparent; - border-top-color: #fff; - bottom: calc(100% - 8px); - left: 50%; - transform: translateX(-50%); - filter: drop-shadow(0 2px 1px rgba(0, 0, 0, 0.1)); - opacity: 0; - } - - &:hover { - &:before, - &:after { - display: block; - animation: popup 0.2s ease-in forwards; - } - } -} - -.container { - display: grid; - grid-template: - 'datetime capacity actions' 1fr / - 120px minmax(100px, 1fr) 300px; - grid-column-gap: 15px; - box-sizing: border-box; - border-bottom: 1px solid #b3b3b3; - height: 50px; - align-items: center; - box-sizing: border-box; -} - -.datetime { - grid-area: datetime; - display: flex; - flex-direction: column; - justify-content: space-between; - font-size: 0.875rem; - letter-spacing: 0.7px; - color: #000; - height: calc(100% - 0.875rem); - - span:last-child { - color: #666; - font-size: 0.625rem; - } -} - -.capacity { - grid-area: capacity; - @include text-overflow-ellipsis; -} - -.actions { - grid-area: actions; - text-align: right; - button { - height: 1.5rem; - width: 150px; - font-size: 0.8125rem; - padding: 0; - pointer-events: unset !important; - - &:disabled { - opacity: 1 !important; - background-color: #3cc68a80 !important; - color: #ffffff80 !important; - } - - &.hasTooltip[data-tooltip] { - @include tooltip; - position: relative; - - &:before { - width: 200px; - text-align: left; - box-sizing: border-box; - } - } - } -} - -.detailBtn { - width: 80px; - border: none !important; - background: none !important; - text-decoration: underline; - color: #626262 !important; -} - -@keyframes popup { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} diff --git a/packages/neuron-ui/src/components/SpecialAssetList/hooks.ts b/packages/neuron-ui/src/components/SpecialAssetList/hooks.ts index 5bcc2399a3..126e2713e6 100644 --- a/packages/neuron-ui/src/components/SpecialAssetList/hooks.ts +++ b/packages/neuron-ui/src/components/SpecialAssetList/hooks.ts @@ -1,7 +1,22 @@ import React, { useCallback, useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { ckbCore } from 'services/chain' import { getSUDTAccountList } from 'services/remote' import { NeuronWalletActions, useDispatch } from 'states' -import { isSuccessResponse, useDialogWrapper, useDidMount } from 'utils' +import { + epochParser, + getSUDTAmount, + isSuccessResponse, + nftFormatter, + PresetScript, + shannonToCKBFormatter, + sudtValueToAmount, + toUint128Le, + useDialogWrapper, + useDidMount, +} from 'utils' +import { MS_PER_EPOCHS } from 'utils/const' +import { AssetInfo, ChequeAssetInfo, NFTType } from '.' export const useMigrate = () => { const { openDialog, dialogRef, closeDialog } = useDialogWrapper() @@ -24,24 +39,6 @@ export const useMigrate = () => { } } -export const useMigrateToNewAccount = () => { - const { openDialog, dialogRef, closeDialog } = useDialogWrapper() - return { - openDialog, - dialogRef, - closeDialog, - } -} - -export const useMigrateToExistAccount = () => { - const { openDialog, dialogRef, closeDialog } = useDialogWrapper() - return { - openDialog, - dialogRef, - closeDialog, - } -} - export const useClickMigrate = ({ closeMigrateDialog, openMigrateToNewAccountDialog, @@ -91,3 +88,99 @@ export const useGetAssetAccounts = (walletID: string) => { .catch((err: Error) => console.error(err)) }, [walletID, dispatch]) } + +interface SpecialAssetColumnInfoProps { + cell: CKBComponents.CellOutput & { + data: string + } + datetime: number + epoch: string + assetInfo: AssetInfo + bestKnownBlockTimestamp: number + tokenInfoList: Array +} + +export const useGetSpecialAssetColumnInfo = ({ + cell: { capacity, lock, type, data }, + datetime, + epoch, + assetInfo, + bestKnownBlockTimestamp, + tokenInfoList, +}: SpecialAssetColumnInfoProps) => { + const [t] = useTranslation() + + let targetTime: undefined | number + let status: + | 'user-defined-asset' + | 'locked-asset' + | 'claim-asset' + | 'withdraw-asset' + | 'transfer-nft' + | 'user-defined-token' = 'user-defined-asset' + let epochsInfo: Record<'target' | 'current', number> | undefined + let amount: string = `${shannonToCKBFormatter(capacity)} CKB` + + switch (assetInfo.lock) { + case PresetScript.Locktime: { + const targetEpochInfo = epochParser(ckbCore.utils.toUint64Le(`0x${lock.args.slice(-16)}`)) + const currentEpochInfo = epochParser(epoch) + const targetEpochFraction = + Number(targetEpochInfo.length) > 0 ? Number(targetEpochInfo.index) / Number(targetEpochInfo.length) : 1 + epochsInfo = { + target: Number(targetEpochInfo.number) + Math.min(targetEpochFraction, 1), + current: Number(currentEpochInfo.number) + Number(currentEpochInfo.index) / Number(currentEpochInfo.length), + } + targetTime = bestKnownBlockTimestamp + (epochsInfo.target - epochsInfo.current) * MS_PER_EPOCHS + if (epochsInfo.target - epochsInfo.current > 0) { + status = 'locked-asset' + } else { + status = 'claim-asset' + } + break + } + case PresetScript.Cheque: { + status = (assetInfo as ChequeAssetInfo).data === 'claimable' ? 'claim-asset' : 'withdraw-asset' + + if (status === 'withdraw-asset') { + const DAY = 86_400_000 + targetTime = datetime + DAY + } + + try { + const tokenInfo = tokenInfoList.find(info => info.tokenID === type?.args) + if (!tokenInfo) { + throw new Error('Token info not found') + } + const balance = BigInt(toUint128Le(data)).toString() + amount = `${sudtValueToAmount(balance, tokenInfo.decimal)} ${tokenInfo.symbol}` + } catch { + amount = 'sUDT Token' + } + break + } + case PresetScript.SUDT: { + status = 'user-defined-token' + const tokenInfo = tokenInfoList.find(info => info.tokenID === type?.args) + const amountInfo = getSUDTAmount({ tokenInfo, data }) + amount = amountInfo.amount + break + } + default: { + // ignore + } + } + + const isLockedCheque = status === 'withdraw-asset' && Date.now() < targetTime! + const isNFTTransferable = assetInfo.type === NFTType.NFT && assetInfo.data === 'transferable' + const isNFTClassOrIssuer = assetInfo.type === NFTType.NFTClass || assetInfo.type === NFTType.NFTIssuer + + if (assetInfo.type === NFTType.NFT) { + amount = nftFormatter(type?.args) + status = 'transfer-nft' + } else if (isNFTClassOrIssuer || assetInfo.type === 'Unknown') { + amount = t('special-assets.unknown-asset') + } + + return { amount, status, targetTime, isLockedCheque, isNFTTransferable, isNFTClassOrIssuer, epochsInfo } +} diff --git a/packages/neuron-ui/src/components/SpecialAssetList/index.tsx b/packages/neuron-ui/src/components/SpecialAssetList/index.tsx index 1ecba8bc00..9521c2b3fa 100644 --- a/packages/neuron-ui/src/components/SpecialAssetList/index.tsx +++ b/packages/neuron-ui/src/components/SpecialAssetList/index.tsx @@ -1,14 +1,16 @@ import React, { useState, useEffect, useMemo, useCallback } from 'react' +import { useState as useGlobalState, useDispatch, AppActions } from 'states' import { useTranslation } from 'react-i18next' import { useNavigate, useLocation } from 'react-router-dom' import Pagination from 'widgets/Pagination' -import SpecialAsset, { AssetInfo } from 'components/SpecialAsset' -import Experimental from 'widgets/ExperimentalRibbon' +import Table from 'widgets/Table' +import Button from 'widgets/Button' import { unlockSpecialAsset, getSpecialAssets, generateWithdrawChequeTransaction, generateClaimChequeTransaction, + openExternal, } from 'services/remote' import { CONSTANTS, @@ -19,25 +21,51 @@ import { useFetchTokenInfoList, PresetScript, nftFormatter, + uniformTimeFormatter, + getExplorerUrl, + ConnectionStatus, } from 'utils' -import { useState as useGlobalState, useDispatch, AppActions } from 'states' +import { LIGHT_NETWORK_TYPE, HIDE_BALANCE } from 'utils/const' +import useGetCountDownAndFeeRateStats from 'utils/hooks/useGetCountDownAndFeeRateStats' import { ControllerResponse } from 'services/remote/remoteApiWrapper' import SUDTUpdateDialog, { SUDTUpdateDialogProps } from 'components/SUDTUpdateDialog' import { TokenInfo } from 'components/SUDTCreateDialog' import SUDTMigrateDialog from 'components/SUDTMigrateDialog' import SUDTMigrateToNewAccountDialog from 'components/SUDTMigrateToNewAccountDialog' import SUDTMigrateToExistAccountDialog from 'components/SUDTMigrateToExistAccountDialog' -import useGetCountDownAndFeeRateStats from 'utils/hooks/useGetCountDownAndFeeRateStats' -import { LIGHT_NETWORK_TYPE } from 'utils/const' -import { - useMigrate, - useClickMigrate, - useMigrateToNewAccount, - useMigrateToExistAccount, - useGetAssetAccounts, -} from './hooks' +import PageContainer from 'components/PageContainer' +import { useGetAssetAccounts, useGetSpecialAssetColumnInfo } from './hooks' + import styles from './specialAssetList.module.scss' +export interface LocktimeAssetInfo { + data: string + lock: PresetScript.Locktime + type: string +} + +export interface ChequeAssetInfo { + data: 'claimable' | 'withdraw-able' + lock: PresetScript.Cheque + type: '' +} + +export enum NFTType { + NFT = 'NFT', + NFTClass = 'NFTClass', + NFTIssuer = 'NFTIssuer', +} + +export interface NFTAssetInfo { + data: '' | 'transferable' + lock: '' + type: NFTType +} + +type UnknownAssetInfo = Record<'data' | 'lock' | 'type', string> + +export type AssetInfo = LocktimeAssetInfo | ChequeAssetInfo | UnknownAssetInfo | NFTAssetInfo + const { PAGE_SIZE } = CONSTANTS export interface SpecialAssetCell { @@ -78,31 +106,44 @@ const SpecialAssetList = () => { tx: any } | null>(null) const [migrateCell, setMigrateCell] = useState() + const [isMigrateDialogOpen, setIsMigrateDialogOpen] = useState(false) + const [isNewAccountDialogOpen, setIsNewAccountDialogOpen] = useState(false) + const [isExistAccountDialogOpen, setIsExistAccountDialogOpen] = useState(false) const [migrateTokenInfo, setMigrateTokenInfo] = useState() - const { dialogRef, openDialog, closeDialog } = useMigrate() - const { - dialogRef: newAccountDialog, - openDialog: openNewAccount, - closeDialog: closeNewAccount, - } = useMigrateToNewAccount() - const { - dialogRef: existAccountDialog, - openDialog: openExistAccount, - closeDialog: closeExistAccount, - } = useMigrateToExistAccount() - const onClickMigrate = useClickMigrate({ - closeMigrateDialog: closeDialog, - openMigrateToNewAccountDialog: openNewAccount, - openMigrateToExistAccountDialog: openExistAccount, - }) - const onCloseDialog = useCallback(() => { - closeExistAccount() - closeNewAccount() - setMigrateCell(undefined) - }, [closeNewAccount, closeExistAccount, setMigrateCell]) + + const onClickMigrate = useCallback( + (type: string) => { + setIsMigrateDialogOpen(false) + switch (type) { + case 'new-account': + setIsNewAccountDialogOpen(true) + break + case 'exist-account': + setIsExistAccountDialogOpen(true) + break + default: + break + } + }, + [setIsMigrateDialogOpen, setIsNewAccountDialogOpen, setIsExistAccountDialogOpen] + ) + + const onCloseDialog = useCallback( + (type?: string) => { + if (type === 'existAccount') { + setIsExistAccountDialogOpen(false) + setIsMigrateDialogOpen(true) + } else { + setIsNewAccountDialogOpen(false) + setIsExistAccountDialogOpen(false) + setMigrateCell(undefined) + } + }, + [setIsNewAccountDialogOpen, setIsExistAccountDialogOpen, setMigrateCell] + ) const { - app: { epoch, globalDialog }, + app: { epoch, globalDialog, pageNotice }, wallet: { id }, settings: { networks }, chain: { @@ -161,6 +202,33 @@ const SpecialAssetList = () => { useGetAssetAccounts(id) + const handleGetSpecialAssetColumnInfo = useCallback( + (item: SpecialAssetCell) => { + const { timestamp, customizedAssetInfo, capacity, lock, type, data } = item + + return useGetSpecialAssetColumnInfo({ + cell: { capacity, lock, type, data }, + datetime: +timestamp, + epoch, + assetInfo: customizedAssetInfo, + bestKnownBlockTimestamp, + tokenInfoList, + }) + }, + [epoch, bestKnownBlockTimestamp, tokenInfoList, useGetSpecialAssetColumnInfo] + ) + + const onViewDetail = useCallback( + (item: SpecialAssetCell) => { + const { + outPoint: { txHash, index }, + } = item + const explorerUrl = getExplorerUrl(isMainnet) + openExternal(`${explorerUrl}/transaction/${txHash}#${index}`) + }, + [isMainnet] + ) + useEffect(() => { dispatch({ type: AppActions.ClearSendState }) }, [dispatch]) @@ -290,9 +358,10 @@ const SpecialAssetList = () => { break } case PresetScript.SUDT: { - openDialog() + setIsMigrateDialogOpen(true) setMigrateCell(cell) const findTokenInfo = tokenInfoList.find(info => info.tokenID === cell.type?.args) + setMigrateTokenInfo(findTokenInfo) break } @@ -301,40 +370,95 @@ const SpecialAssetList = () => { } } }, - [cells, id, dispatch, setAccountToClaim, navigate, openDialog, tokenInfoList] + [cells, id, dispatch, setAccountToClaim, navigate, setIsMigrateDialogOpen, tokenInfoList] ) - const list = useMemo(() => { - return cells.map(({ outPoint, timestamp, customizedAssetInfo, data, lock, type, capacity }) => { - return ( - - ) - }) - }, [cells, epoch, isMainnet, handleAction, connectionStatus, bestKnownBlockTimestamp, tokenInfoList]) - return ( -
- -
{t('special-assets.title')}
+ {totalCount ? ( -
-
- {t('special-assets.date')} - {t('special-assets.assets')} -
- {list} -
+ { + const [date, time] = uniformTimeFormatter(item.timestamp).split(' ') + return `${date} ${time}` + }, + }, + { + title: t('special-assets.assets'), + dataIndex: 'capacity', + align: 'left', + isBalance: true, + minWidth: '200px', + render(_, __, item, show) { + const { amount } = handleGetSpecialAssetColumnInfo(item) + return show ? amount : HIDE_BALANCE + }, + }, + { + title: '', + dataIndex: '#', + align: 'right', + render(_, __, item) { + const { + outPoint: { txHash, index }, + customizedAssetInfo, + } = item + + const { status, targetTime, isLockedCheque, isNFTTransferable, isNFTClassOrIssuer, epochsInfo } = + handleGetSpecialAssetColumnInfo(item) + + return ( +
+ {isNFTClassOrIssuer || (customizedAssetInfo.type === NFTType.NFT && !isNFTTransferable) ? null : ( +
+ ) + }, + }, + ]} + dataSource={cells} + noDataContent={t('overview.no-recent-activities')} + /> ) : null} {totalCount || !loaded ? null :
{t('special-assets.no-special-assets')}
} @@ -350,37 +474,42 @@ const SpecialAssetList = () => { /> ) : null} + {updateAccountDialogProps ? : null} + {migrateCell && ( - - - + setIsMigrateDialogOpen(false)} + /> )} + {migrateCell && ( - - - + )} + {migrateCell && ( - - - + onCloseDialog('existAccount')} + isLightClient={isLightClient} + /> )} - + ) } diff --git a/packages/neuron-ui/src/components/SpecialAssetList/specialAssetList.module.scss b/packages/neuron-ui/src/components/SpecialAssetList/specialAssetList.module.scss index dd66d5fdcb..06e36ce65b 100644 --- a/packages/neuron-ui/src/components/SpecialAssetList/specialAssetList.module.scss +++ b/packages/neuron-ui/src/components/SpecialAssetList/specialAssetList.module.scss @@ -1,38 +1,8 @@ @import '../../styles/mixin.scss'; .container { - position: relative; - .title { - @include page-title; - margin-bottom: 20px; - } - - .listContainer { - background-color: #fff; - padding: 2px 15px 48px; - - .listHeader { - @include semi-bold-text; - display: flex; - justify-content: flex-start; - align-items: center; - height: 55px; - font-size: 1rem; - letter-spacing: 0.7px; - border-bottom: 1px solid #b3b3b3; - - & > span { - flex-basis: 135px; - } - } - } - .pagination { - margin-top: 20px; - display: flex; - flex: 1; - flex-direction: column; - justify-content: flex-end; + padding: 24px 0; } } @@ -51,3 +21,19 @@ padding: 24px 48px; width: 520px; } + +.actionBtnBox { + margin-right: 16px; + display: flex; + justify-content: flex-end; + gap: 16px; + + .actionBtn { + width: 104px; + height: 40px; + min-width: unset; + border-radius: 12px; + font-weight: 500; + font-size: 14px; + } +} diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index 1237b697ed..0ad2228016 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -880,6 +880,9 @@ }, "migrate-sudt": { "title": "Migrate to a sUDT Asset Account", + "choose-title": "Migration mode", + "next": "Next", + "back": "Back", "turn-into-new-account": { "title": "Turn into a new sUDT Asset Account", "sub-title": "Turn the sUDT Asset into a new sUDT Account, occupies at least 142 CKBytes", diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json index 4336a801b9..a2d9f51480 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -850,6 +850,9 @@ }, "migrate-sudt": { "title": "遷移至 sUDT 資產賬戶", + "choose-title": "選擇遷移方式", + "next": "下一步", + "back": "上一步", "turn-into-new-account": { "title": "轉換成新的 sUDT 資產賬戶", "sub-title": "將 sUDT 資產轉換成 sUDT 資產賬戶, 至少占用 142 CKBytes", diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index 39a362fa9c..186b95ecb5 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -873,6 +873,9 @@ }, "migrate-sudt": { "title": "迁移至 sUDT 资产账户", + "choose-title": "选择迁移方式", + "next": "下一步", + "back": "上一步", "turn-into-new-account": { "title": "转换成新的 sUDT 资产账户", "sub-title": "将 sUDT 资产转换成 sUDT 资产账户, 至少占用 142 CKBytes", diff --git a/packages/neuron-ui/src/stories/SpecialAsset.stories.tsx b/packages/neuron-ui/src/stories/SpecialAsset.stories.tsx deleted file mode 100644 index 4b354650ff..0000000000 --- a/packages/neuron-ui/src/stories/SpecialAsset.stories.tsx +++ /dev/null @@ -1,206 +0,0 @@ -import React from 'react' -import { storiesOf } from '@storybook/react' -import SpecialAsset, { SpecialAssetProps } from 'components/SpecialAsset' - -const props: { - [name: string]: Omit -} = { - 'Type and Data': { - cell: { - outPoint: { - txHash: '', - index: '', - }, - capacity: '123456789012345678', - lock: { - args: '0xd2393e52df67cc0c231ef8cb04418bfeabb7b5e8b402009b00f00020', - codeHash: '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8', - hashType: 'type', - }, - type: undefined, - data: '0x', - }, - epoch: '0x3dd011d0004fb', - datetime: new Date().getTime(), - isMainnet: true, - connectionStatus: 'online', - bestKnownBlockTimestamp: Date.now(), - tokenInfoList: [], - assetInfo: { - data: '', - lock: 'SingleMultiSign', - type: '', - }, - }, - Type: { - cell: { - outPoint: { - txHash: '', - index: '', - }, - capacity: '123456789012345678', - lock: { - args: '0xd2393e52df67cc0c231ef8cb04418bfeabb7b5e8b402009b00f00020', - codeHash: '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8', - hashType: 'type', - }, - type: undefined, - data: '0x', - }, - epoch: '0x3dd011d0004fb', - datetime: new Date().getTime(), - isMainnet: true, - connectionStatus: 'online', - bestKnownBlockTimestamp: Date.now(), - tokenInfoList: [], - assetInfo: { - data: '', - lock: 'SingleMultiSign', - type: '', - }, - }, - Data: { - cell: { - outPoint: { - txHash: '', - index: '', - }, - - capacity: '123456789012345678', - lock: { - args: '0xd2393e52df67cc0c231ef8cb04418bfeabb7b5e8b402009b00f00020', - codeHash: '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8', - hashType: 'type', - }, - type: undefined, - data: '0x', - }, - epoch: '0x3dd011d0004fb', - datetime: new Date().getTime(), - isMainnet: true, - connectionStatus: 'online', - bestKnownBlockTimestamp: Date.now(), - tokenInfoList: [], - assetInfo: { - data: '', - lock: 'SingleMultiSign', - type: '', - }, - }, - 'User defined asset': { - cell: { - capacity: '123456789012345678', - outPoint: { - txHash: '', - index: '', - }, - lock: { - args: '0xd2393e52df67cc0c231ef8cb04418bfeabb7b5e8b402009b00f00020', - codeHash: '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8', - hashType: 'type', - }, - type: undefined, - data: '0x', - }, - epoch: '0x3dd011d0004fb', - datetime: new Date().getTime(), - isMainnet: true, - connectionStatus: 'online', - bestKnownBlockTimestamp: Date.now(), - tokenInfoList: [], - assetInfo: { - data: '', - lock: 'SingleMultiSign', - type: '', - }, - }, - Locked: { - cell: { - capacity: '123456789012345678', - outPoint: { - txHash: '', - index: '', - }, - lock: { - args: '0xd2393e52df67cc0c231ef8cb04418bfeabb7b5e8b402009b00f00020', - codeHash: '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8', - hashType: 'type', - }, - type: undefined, - data: '0x', - }, - epoch: '0x3dd011d0004fb', - datetime: new Date().getTime(), - isMainnet: true, - connectionStatus: 'online', - bestKnownBlockTimestamp: Date.now(), - tokenInfoList: [], - assetInfo: { - data: '', - lock: 'SingleMultiSign', - type: '', - }, - }, - Claim: { - cell: { - capacity: '123456789012345678', - outPoint: { - txHash: '', - index: '', - }, - lock: { - args: '0xd2393e52df67cc0c231ef8cb04418bfeabb7b5e8b402009b00f00020', - codeHash: '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8', - hashType: 'type', - }, - type: undefined, - data: '0x', - }, - epoch: '0x3dd011d0004fb', - datetime: new Date().getTime(), - isMainnet: true, - connectionStatus: 'online', - bestKnownBlockTimestamp: Date.now(), - tokenInfoList: [], - assetInfo: { - data: '', - lock: 'SingleMultiSign', - type: '', - }, - }, - Offline: { - cell: { - capacity: '123456789012345678', - outPoint: { - txHash: '', - index: '', - }, - lock: { - args: '0xd2393e52df67cc0c231ef8cb04418bfeabb7b5e8b402009b00f00020', - codeHash: '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8', - hashType: 'type', - }, - type: undefined, - data: '0x', - }, - epoch: '0x3dd011d0004fb', - datetime: new Date().getTime(), - isMainnet: true, - connectionStatus: 'offline', - bestKnownBlockTimestamp: Date.now(), - tokenInfoList: [], - assetInfo: { - data: '', - lock: 'SingleMultiSign', - type: '', - }, - }, -} - -const stories = storiesOf('Special Asset', module) - -Object.keys(props).forEach(name => { - stories.add(name, () => { - return - }) -}) diff --git a/packages/neuron-ui/src/utils/const.ts b/packages/neuron-ui/src/utils/const.ts index c85f228a34..3ba3dcb8c7 100644 --- a/packages/neuron-ui/src/utils/const.ts +++ b/packages/neuron-ui/src/utils/const.ts @@ -68,3 +68,5 @@ export const HIDE_BALANCE = '******' export const LIGHT_CLIENT_TESTNET = 'light_client_testnet' export const LIGHT_NETWORK_TYPE = 2 export const METHOD_NOT_FOUND = -32601 + +export const MS_PER_EPOCHS = 4 * 60 * 60 * 1000 diff --git a/packages/neuron-ui/src/utils/hooks/index.ts b/packages/neuron-ui/src/utils/hooks/index.ts index 8db7be4657..a5478e8e04 100644 --- a/packages/neuron-ui/src/utils/hooks/index.ts +++ b/packages/neuron-ui/src/utils/hooks/index.ts @@ -258,7 +258,7 @@ export const useExitOnWalletChange = () => { } export const useSUDTAccountInfoErrors = ({ - info: { accountName, tokenName, tokenId, symbol, decimal }, + info: { accountName, tokenName, tokenId, symbol, decimal, balance }, existingAccountNames, isCKB, t, @@ -269,6 +269,7 @@ export const useSUDTAccountInfoErrors = ({ tokenId: string symbol: string decimal: string + balance?: string } existingAccountNames: string[] isCKB: boolean @@ -281,6 +282,7 @@ export const useSUDTAccountInfoErrors = ({ tokenName: '', symbol: '', decimal: '', + balance: '', } const dataToValidate = { @@ -292,6 +294,7 @@ export const useSUDTAccountInfoErrors = ({ tokenId: { params: { tokenId, isCKB }, validator: validateTokenId }, tokenName: { params: { tokenName, isCKB }, validator: validateTokenName }, decimal: { params: { decimal }, validator: validateDecimal }, + balance: { params: { balance }, validator: validateDecimal }, } Object.entries(dataToValidate).forEach(([name, { params, validator }]: [string, any]) => { diff --git a/packages/neuron-ui/src/widgets/InputSelect/index.tsx b/packages/neuron-ui/src/widgets/InputSelect/index.tsx index c70e14fea9..c9f08d8a71 100644 --- a/packages/neuron-ui/src/widgets/InputSelect/index.tsx +++ b/packages/neuron-ui/src/widgets/InputSelect/index.tsx @@ -1,5 +1,7 @@ import React, { useRef, useState, useEffect, useCallback } from 'react' import { useDidMount, useForceUpdate } from 'utils' +import { ReactComponent as Arrow } from 'widgets/Icons/Arrow.svg' + import styles from './input-select.module.scss' export interface SelectOptions { @@ -86,7 +88,7 @@ const Select = ({ value, options, placeholder, disabled, onChange, className, in aria-selected={isSelected ? 'true' : 'false'} aria-hidden="true" > - {label} + {typeof label === 'string' && label?.length > 68 ? `${label.slice(0, 34)}...${label.slice(-34)}` : label} ) }, @@ -128,7 +130,7 @@ const Select = ({ value, options, placeholder, disabled, onChange, className, in onChange={onInputChange} value={value ?? innerValue} /> -
+
{openRef.current ? (
diff --git a/packages/neuron-ui/src/widgets/InputSelect/input-select.module.scss b/packages/neuron-ui/src/widgets/InputSelect/input-select.module.scss index 2703b909d8..3a2da1de2e 100644 --- a/packages/neuron-ui/src/widgets/InputSelect/input-select.module.scss +++ b/packages/neuron-ui/src/widgets/InputSelect/input-select.module.scss @@ -1,102 +1,100 @@ .root { position: relative; font-size: 0.75rem; -} + --active-color: #00c891; -.control { - font-family: inherit; - grid-area: input; - color: #434343; - border: solid 1px #000000; - display: flex; - justify-content: flex-start; - align-items: stretch; - box-sizing: border-box; - height: 32px; + .control { + font-family: inherit; + grid-area: input; + color: var(--main-text-color); + display: flex; + justify-content: flex-start; + align-items: stretch; + box-sizing: border-box; + height: 56px; + border: 1px solid var(--divide-line-color); + border-radius: 8px; - &[data-open="true"] { - border: 1px solid #000; - } + .placeholder { + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 500; + font-size: 14px; + } - &:hover { - border: 1px solid #000; - } + &:focus, + &:hover, + &[data-open='true'] { + border: 1px solid var(--active-color); + } - & > input { - border: none; - width: 100%; - padding: 4px 24px 4px 12px; + & > input { + border: none; + width: 100%; + padding: 4px 0 4px 12px; + background: unset; + caret-color: var(--active-color); + color: var(--main-text-color); + } } -} - -.arrow { - border-color: #999 transparent transparent; - border-style: solid; - border-width: 5px 5px 0; - position: absolute; - right: 8px; - top: 50%; - transform: translateY(-50%); -} -.isOpen .arrow { - border-color: transparent transparent #999; - border-width: 0 5px 5px; -} + .menu { + background-color: var(--third-background-color); + border: 1px solid var(--input-border-color); + box-shadow: 0px 1px 4px 0px var(--main-shadow-color); + box-sizing: border-box; + border-top: none; + overflow-y: auto; + position: absolute; + width: 100%; + z-index: 1000; + -webkit-overflow-scrolling: touch; -.menu { - background-color: white; - border: 1px solid #ccc; - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); - box-sizing: border-box; - border-top: none; - max-height: 200px; - overflow-y: auto; - position: absolute; - top: 100%; - width: 100%; - z-index: 1000; - -webkit-overflow-scrolling: touch; -} + top: calc(100% + 8px); + max-height: 170px; + border-radius: 8px; + } -.menu .group > .title{ - padding: 8px 10px; - color: rgba(51, 51, 51, 1); - font-weight: bold; - text-transform: capitalize; -} + .option { + box-sizing: border-box; + cursor: pointer; + display: block; + color: var(--main-text-color); + margin: 0 16px; + padding: 19px 0px; + border-bottom: 1px solid var(--input-border-color); + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 500; + font-size: 14px; -.option { - box-sizing: border-box; - color: rgba(51, 51, 51, 0.8); - cursor: pointer; - display: block; - padding: 8px 10px; + &:last-child { + border: unset; + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; + } - &[aria-selected="true"] { - background-color: #ccc; + &.is-select, + &:hover, + &[aria-selected='true'] { + background-color: inherit; + color: var(--active-color); + } } - &.is-select { - background-color: #eee; + .arrow { + transition: transform 0.3s; + padding: 0 16px; + line-height: 56px; + height: 56px; } - &:last-child { - border-bottom-right-radius: 2px; - border-bottom-left-radius: 2px; - } + .isOpen .arrow { + transform: rotate(180deg); - &:hover { - background-color: #eee; - color: #333; + path { + stroke: var(--active-color); + } } } - -.noresults { - box-sizing: border-box; - color: #ccc; - cursor: default; - display: block; - padding: 8px 10px; -}