From 1cfd17f704b2dcf6d147f3f65569b385bdb19fd9 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 | 67 +++- .../sUDTMigrateDialog.module.scss | 33 +- .../SUDTMigrateToExistAccountDialog/index.tsx | 42 ++- ...UDTMigrateToExistAccountDialog.module.scss | 19 +- .../SUDTMigrateToNewAccountDialog/index.tsx | 71 ++-- .../sUDTMigrateToNewAccountDialog.module.scss | 14 +- .../src/components/SpecialAsset/index.tsx | 39 +- .../src/components/SpecialAssetList/hooks.ts | 136 ++++++- .../src/components/SpecialAssetList/index.tsx | 339 +++++++++++++----- .../specialAssetList.module.scss | 48 +-- packages/neuron-ui/src/locales/en.json | 2 + packages/neuron-ui/src/locales/zh-tw.json | 2 + packages/neuron-ui/src/locales/zh.json | 2 + .../src/stories/SpecialAsset.stories.tsx | 14 +- .../neuron-ui/src/widgets/Dialog/index.tsx | 17 +- .../src/widgets/InputSelect/index.tsx | 14 +- .../InputSelect/input-select.module.scss | 221 ++++++++---- .../src/widgets/NewUIInputSelect/index.tsx | 151 ++++++++ .../NewUIInputSelect/input-select.module.scss | 102 ++++++ .../neuron-ui/src/widgets/Table/index.tsx | 5 +- .../widgets/TextField/textField.module.scss | 2 +- 21 files changed, 1012 insertions(+), 328 deletions(-) create mode 100644 packages/neuron-ui/src/widgets/NewUIInputSelect/index.tsx create mode 100644 packages/neuron-ui/src/widgets/NewUIInputSelect/input-select.module.scss diff --git a/packages/neuron-ui/src/components/SUDTMigrateDialog/index.tsx b/packages/neuron-ui/src/components/SUDTMigrateDialog/index.tsx index 98fbd7f9b4..f20a56dafe 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTMigrateDialog/index.tsx @@ -1,7 +1,9 @@ -import React, { useMemo } from 'react' +/* eslint-disable no-console */ +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 +23,58 @@ 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 06082136ca..5551c546ba 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/index.tsx @@ -1,29 +1,32 @@ +/* eslint-disable no-console */ 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 { AnyoneCanPayLockInfoOnAggron, getSUDTAmount, isSuccessResponse, validateSpecificAddress } from 'utils' import InputSelect from 'widgets/InputSelect' import { generateSudtMigrateAcpTx } from 'services/remote' import { AppActions, useDispatch } from 'states' import { isErrorWithI18n } from 'exceptions' +import Dialog from 'widgets/Dialog' import styles from './sUDTMigrateToExistAccountDialog.module.scss' const SUDTMigrateToExistAccountDialog = ({ cell, - closeDialog, tokenInfo, sUDTAccounts, isMainnet, walletID, + isDialogOpen, + onCancel, }: { cell: SpecialAssetCell - closeDialog: () => void tokenInfo?: Controller.GetTokenInfoList.TokenInfo sUDTAccounts: State.SUDTAccount[] isMainnet: boolean walletID: string + isDialogOpen: boolean + onCancel: () => void }) => { const [t] = useTranslation() const [address, setAddress] = useState('') @@ -52,7 +55,7 @@ const SUDTMigrateToExistAccountDialog = ({ outPoint: cell.outPoint, acpAddress: address, }).then(res => { - closeDialog() + onCancel() if (isSuccessResponse(res)) { if (res.result) { dispatch({ @@ -80,11 +83,20 @@ 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')} *`}
{addressError &&
{addressError}
}
@@ -102,17 +115,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..fd90bf5b5d 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/sUDTMigrateToExistAccountDialog.module.scss +++ b/packages/neuron-ui/src/components/SUDTMigrateToExistAccountDialog/sUDTMigrateToExistAccountDialog.module.scss @@ -1,17 +1,10 @@ @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; & > div:first-child { @@ -21,12 +14,6 @@ margin-bottom: 2px; display: block; } - - .addressInputSelect { - & > div:first-child { - height: 26px; - } - } } .error { @@ -34,4 +21,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/index.tsx b/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/index.tsx index 141c054ae7..12c60aab16 100644 --- a/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTMigrateToNewAccountDialog/index.tsx @@ -1,17 +1,19 @@ +/* eslint-disable no-console */ import React, { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { SpecialAssetCell } from 'components/SpecialAssetList' -import Button from 'widgets/Button' +// import Button from 'widgets/Button' import TextField from 'widgets/TextField' import { getSUDTAmount, isSuccessResponse } from 'utils' import { generateSudtMigrateAcpTx } from 'services/remote' import { AppActions, useDispatch } from 'states' +import Dialog from 'widgets/Dialog' 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: 'accountName', label: 'account-name' }, { key: 'tokenName', label: 'token-name' }, { key: 'symbol', label: 'symbol' }, { key: 'decimal', label: 'decimal' }, @@ -19,16 +21,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 +51,7 @@ const SUDTMigrateToNewAccountDialog = ({ generateSudtMigrateAcpTx({ outPoint: cell.outPoint, }).then(res => { - closeDialog() + onCancel() if (isSuccessResponse(res)) { if (res.result) { dispatch({ @@ -84,31 +88,40 @@ const SUDTMigrateToNewAccountDialog = ({ }) } }) - }, [cell, t, closeDialog, walletID, tokenInfo, dispatch, sudtAmount]) + }, [cell, t, onCancel, walletID, tokenInfo, dispatch, sudtAmount]) + + const renderList = fields.map(field => ( + + )) + renderList.splice( + 1, + 0, + + ) + 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 index cf55d39de9..ead7bc0bb3 100644 --- a/packages/neuron-ui/src/components/SpecialAsset/index.tsx +++ b/packages/neuron-ui/src/components/SpecialAsset/index.tsx @@ -1,14 +1,14 @@ -import React, { useCallback } from 'react' +import React from 'react' import { useTranslation } from 'react-i18next' import Button from 'widgets/Button' import CopyZone from 'widgets/CopyZone' -import { openExternal } from 'services/remote' +// import { openExternal } from 'services/remote' import { ckbCore } from 'services/chain' import { ConnectionStatus, - uniformTimeFormatter, + // uniformTimeFormatter, shannonToCKBFormatter, - getExplorerUrl, + // getExplorerUrl, PresetScript, epochParser, toUint128Le, @@ -26,19 +26,19 @@ interface LocktimeAssetInfo { type: string } -interface ChequeAssetInfo { +export interface ChequeAssetInfo { data: 'claimable' | 'withdraw-able' lock: PresetScript.Cheque type: '' } -enum NFTType { +export enum NFTType { NFT = 'NFT', NFTClass = 'NFTClass', NFTIssuer = 'NFTIssuer', } -interface NFTAssetInfo { +export interface NFTAssetInfo { data: '' | 'transferable' lock: '' type: NFTType @@ -54,7 +54,7 @@ export interface SpecialAssetProps { data: string } datetime: number - isMainnet: boolean + // isMainnet: boolean epoch: string assetInfo: AssetInfo onAction: any @@ -72,7 +72,7 @@ const SpecialAsset = ({ data, }, datetime, - isMainnet, + // isMainnet, epoch, assetInfo, onAction, @@ -81,7 +81,7 @@ const SpecialAsset = ({ tokenInfoList, }: SpecialAssetProps) => { const [t] = useTranslation() - const [date, time] = uniformTimeFormatter(datetime).split(' ') + // const [date, time] = uniformTimeFormatter(datetime).split(' ') let targetTime: undefined | number let status: | 'user-defined-asset' @@ -91,6 +91,7 @@ const SpecialAsset = ({ | '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, '') @@ -147,10 +148,10 @@ const SpecialAsset = ({ } } - const onViewDetail = useCallback(() => { - const explorerUrl = getExplorerUrl(isMainnet) - openExternal(`${explorerUrl}/transaction/${txHash}#${index}`) - }, [isMainnet, txHash, index]) + // 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' @@ -165,10 +166,8 @@ const SpecialAsset = ({ return (
-
- {date} - {time} -
+
{/* {date} + {time} */}
{amount} @@ -204,12 +203,12 @@ const SpecialAsset = ({ } /> )} -
) diff --git a/packages/neuron-ui/src/components/SpecialAssetList/hooks.ts b/packages/neuron-ui/src/components/SpecialAssetList/hooks.ts index 5bcc2399a3..9018c7815b 100644 --- a/packages/neuron-ui/src/components/SpecialAssetList/hooks.ts +++ b/packages/neuron-ui/src/components/SpecialAssetList/hooks.ts @@ -1,7 +1,21 @@ +import { AssetInfo, ChequeAssetInfo, NFTType } from 'components/SpecialAsset' 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' export const useMigrate = () => { const { openDialog, dialogRef, closeDialog } = useDialogWrapper() @@ -24,24 +38,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 +87,105 @@ 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() + const MS_PER_EPOCHS = 4 * 60 * 60 * 1000 + + 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 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 8983bc337c..eecaf2546a 100644 --- a/packages/neuron-ui/src/components/SpecialAssetList/index.tsx +++ b/packages/neuron-ui/src/components/SpecialAssetList/index.tsx @@ -1,14 +1,15 @@ +/* eslint-disable no-console */ import React, { useState, useEffect, useMemo, useCallback } from 'react' 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 { AssetInfo, NFTType } from 'components/SpecialAsset' import { unlockSpecialAsset, getSpecialAssets, generateWithdrawChequeTransaction, generateClaimChequeTransaction, + openExternal, } from 'services/remote' import { CONSTANTS, @@ -19,6 +20,12 @@ import { useFetchTokenInfoList, PresetScript, nftFormatter, + uniformTimeFormatter, + getExplorerUrl, + ConnectionStatus, + // sUDTAmountFormatter, + // sudtValueToAmount, + // shannonToCKBFormatter, } from 'utils' import { useState as useGlobalState, useDispatch, AppActions } from 'states' import { ControllerResponse } from 'services/remote/remoteApiWrapper' @@ -27,14 +34,21 @@ import { TokenInfo } from 'components/SUDTCreateDialog' import SUDTMigrateDialog from 'components/SUDTMigrateDialog' import SUDTMigrateToNewAccountDialog from 'components/SUDTMigrateToNewAccountDialog' import SUDTMigrateToExistAccountDialog from 'components/SUDTMigrateToExistAccountDialog' +import Table from 'widgets/Table' +import Button from 'widgets/Button' +import { HIDE_BALANCE } from 'utils/const' +import PageContainer from 'components/PageContainer' import { - useMigrate, - useClickMigrate, - useMigrateToNewAccount, - useMigrateToExistAccount, + // useMigrate, + // useClickMigrate, + // useMigrateToNewAccount, + // useMigrateToExistAccount, useGetAssetAccounts, + useGetSpecialAssetColumnInfo, } from './hooks' import styles from './specialAssetList.module.scss' +// import { UANTonkenSymbol } from 'components/UANDisplay' +// import { HIDE_BALANCE } from 'utils/const' const { PAGE_SIZE, MEDIUM_FEE_RATE } = CONSTANTS @@ -51,7 +65,7 @@ export interface SpecialAssetCell { hashType: 'type' | 'data' } lockHash: string - multiSignBlake160: string + multiSignBlake160: string | null outPoint: { index: string txHash: string @@ -76,31 +90,59 @@ 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 { openDialog, closeDialog } = useMigrate() + + // const { + // dialogRef: newAccountDialog, + // openDialog: openNewAccount, + // closeDialog: closeNewAccount, + // } = useMigrateToNewAccount() + + // const { + // dialogRef: existAccountDialog, + // openDialog: openExistAccount, + // closeDialog: closeExistAccount, + // } = useMigrateToExistAccount() + + // const onClickMigrate1 = useClickMigrate({ + // closeMigrateDialog: closeDialog, + // openMigrateToNewAccountDialog: openNewAccount, + // openMigrateToExistAccountDialog: openExistAccount, + // }) + + const onClickMigrate = useCallback( + (type: string) => { + console.log(type, 'type') + + setIsMigrateDialogOpen(false) + switch (type) { + case 'new-account': + setIsNewAccountDialogOpen(true) + break + case 'exist-account': + setIsExistAccountDialogOpen(true) + break + default: + break + } + }, + [setIsMigrateDialogOpen, setIsNewAccountDialogOpen, setIsExistAccountDialogOpen] + ) + const onCloseDialog = useCallback(() => { - closeExistAccount() - closeNewAccount() + setIsNewAccountDialogOpen(false) + setIsExistAccountDialogOpen(false) setMigrateCell(undefined) - }, [closeNewAccount, closeExistAccount, setMigrateCell]) + }, [setIsNewAccountDialogOpen, setIsExistAccountDialogOpen, setMigrateCell]) const { - app: { epoch, globalDialog }, + app: { epoch, globalDialog, pageNotice }, wallet: { id }, settings: { networks }, chain: { @@ -154,6 +196,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]) @@ -169,6 +238,37 @@ const SpecialAssetList = () => { if (isSuccessResponse(res)) { const { items, totalCount: count } = res.result as { items: SpecialAssetCell[]; totalCount: string } setCells(items) + // console.log(items, 'items') + + // setCells([ + // { + // blockHash: '0xc596b980135657ba709aef0e6a4705b7dbc203b506476db444a76072244900e4', + // blockNumber: '7539913', + // capacity: '14400000000', + // customizedAssetInfo: { lock: 'SUDT', type: 'SUDT', data: '' }, + // daoData: null, + // data: '0x000010632d5ec76b0500000000000000', + // lock: { + // args: '0xac4bad95dd72500f8d11d68fc23e07b8c8435221', + // codeHash: '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8', + // hashType: 'type', + // }, + // lockHash: '0x4b7093e89aec17b5d619a66bb173d81e6d75211fbec0aea2302984b17aca6f6b', + // multiSignBlake160: null, + // outPoint: { + // txHash: '0x5cc94ded5d0edccbe7c7065880073230208d97d54bc55d3f2d21d3a522342e98', + // index: '0', + // }, + // status: 'live', + // timestamp: '1669982526760', + // type: { + // args: '0x58bef38794236b315b7c23fd8132d7f42676228d659b291936e8c6c7ba9f064e', + // codeHash: '0xc5e5dcf215925f7ef4dfaf5f4b4f105bc321c02776d6e7d52a1db3fcd9d011a4', + // hashType: 'type', + // }, + // // typeHash: '0xdac0c53c572f451e56c092fdb520aec82f5f4bf8a5c02e1c4843f40c15f84c55', + // }, + // ]) setTotalCount(+count) } else { dispatch({ @@ -222,6 +322,7 @@ const SpecialAssetList = () => { }) return } + const handleRes = (actionType: 'unlock' | 'withdraw-cheque' | 'claim-cheque') => ( res: ControllerResponse ) => { @@ -243,6 +344,9 @@ const SpecialAssetList = () => { }) } } + + console.log(cell.customizedAssetInfo, 'customizedAssetInfo') + switch (cell.customizedAssetInfo.lock) { case PresetScript.Locktime: { unlockSpecialAsset({ @@ -284,9 +388,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 } @@ -295,40 +400,101 @@ 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')}
} @@ -344,36 +510,47 @@ const SpecialAssetList = () => { /> ) : null} + {updateAccountDialogProps ? : null} + {migrateCell && ( - - - + // + setIsMigrateDialogOpen(false)} + /> + // )} + {migrateCell && ( - - - + // + + // )} + {migrateCell && ( - - - + // + + // )} - + ) } 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 8020a4f258..bb21a52462 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -860,6 +860,8 @@ }, "migrate-sudt": { "title": "Migrate to a sUDT Asset Account", + "choose-title": "Migration mode", + "next": "Next", "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 2c4a6ded5d..2fe6ffc52d 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -831,6 +831,8 @@ }, "migrate-sudt": { "title": "遷移至 sUDT 資產賬戶", + "choose-title": "選擇遷移方式", + "next": "下一步", "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 6b79473394..7e454b8eaf 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -855,6 +855,8 @@ }, "migrate-sudt": { "title": "迁移至 sUDT 资产账户", + "choose-title": "选择迁移方式", + "next": "下一步", "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 index 4b354650ff..ee114813d5 100644 --- a/packages/neuron-ui/src/stories/SpecialAsset.stories.tsx +++ b/packages/neuron-ui/src/stories/SpecialAsset.stories.tsx @@ -22,7 +22,7 @@ const props: { }, epoch: '0x3dd011d0004fb', datetime: new Date().getTime(), - isMainnet: true, + // isMainnet: true, connectionStatus: 'online', bestKnownBlockTimestamp: Date.now(), tokenInfoList: [], @@ -49,7 +49,7 @@ const props: { }, epoch: '0x3dd011d0004fb', datetime: new Date().getTime(), - isMainnet: true, + // isMainnet: true, connectionStatus: 'online', bestKnownBlockTimestamp: Date.now(), tokenInfoList: [], @@ -77,7 +77,7 @@ const props: { }, epoch: '0x3dd011d0004fb', datetime: new Date().getTime(), - isMainnet: true, + // isMainnet: true, connectionStatus: 'online', bestKnownBlockTimestamp: Date.now(), tokenInfoList: [], @@ -104,7 +104,7 @@ const props: { }, epoch: '0x3dd011d0004fb', datetime: new Date().getTime(), - isMainnet: true, + // isMainnet: true, connectionStatus: 'online', bestKnownBlockTimestamp: Date.now(), tokenInfoList: [], @@ -131,7 +131,7 @@ const props: { }, epoch: '0x3dd011d0004fb', datetime: new Date().getTime(), - isMainnet: true, + // isMainnet: true, connectionStatus: 'online', bestKnownBlockTimestamp: Date.now(), tokenInfoList: [], @@ -158,7 +158,7 @@ const props: { }, epoch: '0x3dd011d0004fb', datetime: new Date().getTime(), - isMainnet: true, + // isMainnet: true, connectionStatus: 'online', bestKnownBlockTimestamp: Date.now(), tokenInfoList: [], @@ -185,7 +185,7 @@ const props: { }, epoch: '0x3dd011d0004fb', datetime: new Date().getTime(), - isMainnet: true, + // isMainnet: true, connectionStatus: 'offline', bestKnownBlockTimestamp: Date.now(), tokenInfoList: [], diff --git a/packages/neuron-ui/src/widgets/Dialog/index.tsx b/packages/neuron-ui/src/widgets/Dialog/index.tsx index 80226b79be..45af33df23 100644 --- a/packages/neuron-ui/src/widgets/Dialog/index.tsx +++ b/packages/neuron-ui/src/widgets/Dialog/index.tsx @@ -56,6 +56,15 @@ const Dialog = ({ } }, [show]) + const onDialogClicked = useCallback( + (e: React.MouseEvent) => { + if (e.target instanceof HTMLDialogElement && e.target.tagName === 'DIALOG') { + onCancel?.() + } + }, + [onCancel] + ) + const onSubmit = useCallback( (e: React.FormEvent) => { e.preventDefault() @@ -72,7 +81,13 @@ const Dialog = ({ } return ( - + {}} + role="none" + > {showHeader ? (
diff --git a/packages/neuron-ui/src/widgets/InputSelect/index.tsx b/packages/neuron-ui/src/widgets/InputSelect/index.tsx index 5d924b64d1..3f4ab7fac4 100644 --- a/packages/neuron-ui/src/widgets/InputSelect/index.tsx +++ b/packages/neuron-ui/src/widgets/InputSelect/index.tsx @@ -1,5 +1,8 @@ +/* eslint-disable no-console */ 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 { @@ -16,6 +19,7 @@ export interface InputSelectProps { onChange?: (value: string, arg?: SelectOptions) => void value?: string placeholder?: string + isNewUI?: boolean } function parseValue(value: string, options: SelectOptions[]) { @@ -23,7 +27,7 @@ function parseValue(value: string, options: SelectOptions[]) { return option?.value || value } -const Select = ({ value, options, placeholder, disabled, onChange, className }: InputSelectProps) => { +const Select = ({ value, options, placeholder, disabled, onChange, className, isNewUI }: InputSelectProps) => { const mounted = useRef(true) const root = useRef(null) const openRef = useRef(false) @@ -34,7 +38,7 @@ const Select = ({ value, options, placeholder, disabled, onChange, className }: const onDocumentClick = useCallback( (e: any) => { - if (mounted.current && !root.current!.contains(e.target) && openRef.current) { + if (mounted.current && !root.current?.contains(e.target) && openRef.current) { setOpen(false) } }, @@ -85,7 +89,7 @@ const Select = ({ value, options, placeholder, disabled, onChange, className }: aria-selected={isSelected ? 'true' : 'false'} aria-hidden="true" > - {label} + {typeof label === 'string' && label?.length > 68 ? `${label.slice(0, 34)}...${label.slice(-34)}` : label}
) }, @@ -112,7 +116,7 @@ const Select = ({ value, options, placeholder, disabled, onChange, className }: const placeholderClass = `${styles.placeholder} ${innerValue !== '' ? styles.isSelected : ''}` return ( -
+
-
+ {isNewUI ? :
}
{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..f8bdbefc8a 100644 --- a/packages/neuron-ui/src/widgets/InputSelect/input-select.module.scss +++ b/packages/neuron-ui/src/widgets/InputSelect/input-select.module.scss @@ -1,32 +1,167 @@ .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: #434343; + border: solid 1px #000000; + display: flex; + justify-content: flex-start; + align-items: stretch; + box-sizing: border-box; + height: 32px; - &[data-open="true"] { - border: 1px solid #000; - } + &[data-open='true'] { + border: 1px solid #000; + } - &:hover { - border: 1px solid #000; + &:hover { + border: 1px solid #000; + } + + & > input { + border: none; + width: 100%; + padding: 4px 24px 4px 12px; + } } - & > input { - border: none; + .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%; - padding: 4px 24px 4px 12px; + z-index: 1000; + -webkit-overflow-scrolling: touch; + } + + .menu .group > .title { + padding: 8px 10px; + color: rgba(51, 51, 51, 1); + font-weight: bold; + text-transform: capitalize; + } + + .option { + box-sizing: border-box; + color: rgba(51, 51, 51, 0.8); + cursor: pointer; + display: block; + padding: 8px 10px; + + &[aria-selected='true'] { + background-color: #ccc; + } + + &.is-select { + background-color: #eee; + } + + &:last-child { + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; + } + + &:hover { + background-color: #eee; + color: #333; + } + } + + &[data-is-new-ui='true'] { + .control { + height: 56px; + border: 1px solid #e5e5e5; + border-radius: 8px; + + .placeholder { + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 500; + font-size: 14px; + } + + &:focus, + &:hover, + &[data-open='true'] { + border-color: var(--active-color); + } + + & > input { + border: none; + width: 100%; + padding: 4px 0 4px 12px; + background: unset; + caret-color: var(--active-color); + } + } + + .arrowForNewUI { + transition: transform 0.3s; + padding: 0 16px; + line-height: 56px; + height: 56px; + } + + .isOpen .arrowForNewUI { + transform: rotate(180deg); + + path { + stroke: var(--active-color); + } + } + + .menu { + top: calc(100% + 8px); + max-height: 170px; + box-shadow: 0px 1px 4px #eeeeee; + border-radius: 8px; + border: unset; + } + + .option { + color: var(--main-text-color); + margin: 8px 36px 8px 10px; + padding: unset; + height: 56px; + line-height: 56px; + border-bottom: 1px solid #f0f0f0; + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 500; + font-size: 14px; + + &:last-child { + border: unset; + } + + &[aria-selected='true'], + &.is-select { + background-color: unset; + } + + &:last-child { + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; + } + + &.is-select, + &:hover, + &[aria-selected='true'] { + background-color: inherit; + color: var(--active-color); + } + } } } @@ -45,54 +180,6 @@ border-width: 0 5px 5px; } -.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; -} - -.menu .group > .title{ - padding: 8px 10px; - color: rgba(51, 51, 51, 1); - font-weight: bold; - text-transform: capitalize; -} - -.option { - box-sizing: border-box; - color: rgba(51, 51, 51, 0.8); - cursor: pointer; - display: block; - padding: 8px 10px; - - &[aria-selected="true"] { - background-color: #ccc; - } - - &.is-select { - background-color: #eee; - } - - &:last-child { - border-bottom-right-radius: 2px; - border-bottom-left-radius: 2px; - } - - &:hover { - background-color: #eee; - color: #333; - } -} - .noresults { box-sizing: border-box; color: #ccc; diff --git a/packages/neuron-ui/src/widgets/NewUIInputSelect/index.tsx b/packages/neuron-ui/src/widgets/NewUIInputSelect/index.tsx new file mode 100644 index 0000000000..eae2af6a6c --- /dev/null +++ b/packages/neuron-ui/src/widgets/NewUIInputSelect/index.tsx @@ -0,0 +1,151 @@ +import React, { useRef, useState, useEffect, useCallback } from 'react' +import { useDidMount, useForceUpdate } from 'utils' +import styles from './input-select.module.scss' + +export interface SelectOptions { + label: React.ReactNode + value: string + data?: any + className?: string +} + +export interface InputSelectProps { + options: SelectOptions[] + className?: string + inputClassName?: string + inputBoxClassName?: string + disabled?: boolean + onChange?: (value: string, arg?: SelectOptions) => void + value?: string + placeholder?: string +} + +function parseValue(value: string, options: SelectOptions[]) { + const option = options.find(o => o.value === value) + return option?.value || value +} + +const Select = ({ + value, + options, + placeholder, + disabled, + onChange, + className, + inputClassName, + inputBoxClassName, +}: InputSelectProps) => { + const mounted = useRef(true) + const root = useRef(null) + const openRef = useRef(false) + const setOpen = useForceUpdate((isOpen: boolean) => { + openRef.current = isOpen + }) + const [innerValue, setInnerValue] = useState('') + + const onDocumentClick = useCallback( + (e: any) => { + if (mounted.current && !root.current!.contains(e.target) && openRef.current) { + setOpen(false) + } + }, + [openRef, setOpen] + ) + + const onMouseDown = useCallback(() => { + if (!disabled) { + setOpen(!openRef.current) + } + }, [openRef, disabled, setOpen]) + + const setValue = useCallback( + (option: SelectOptions) => { + if (onChange) { + onChange(option.value, option) + } + + setInnerValue(option.value) + setOpen(false) + }, + [onChange, setInnerValue, setOpen] + ) + + const onInputChange = useCallback( + (e: React.ChangeEvent) => { + if (onChange) { + onChange(e.target.value) + } + + setInnerValue(e.target.value) + setOpen(false) + }, + [setInnerValue, setOpen, onChange] + ) + const renderOption = useCallback( + (option: SelectOptions) => { + const { value: val } = option + const label: React.ReactNode = option.label || option.value + const isSelected = val === innerValue + + return ( + + ) + }, + [innerValue, setValue] + ) + + useDidMount(() => { + document.addEventListener('click', onDocumentClick, false) + return () => document.removeEventListener('click', onDocumentClick, false) + }) + + useEffect(() => { + if (value !== undefined) { + const nextSelected = parseValue(value, options) + setInnerValue(nextSelected) + } else { + setInnerValue('') + } + }, [value, placeholder, options]) + + const disabledClass = disabled ? styles.disabled : '' + const dropdownClass = `${className} ${styles.root} ${openRef.current ? styles.isOpen : ''}` + const inputBoxClass = `${styles.inputBox} ${inputBoxClassName} ${disabledClass} ${ + openRef.current ? styles.isOpen : '' + }` + const placeholderClass = `${styles.placeholder} ${inputClassName} ${innerValue !== '' ? styles.isSelected : ''}` + + return ( +
+
+ +
+
+ {openRef.current ? ( +
+ {options.map(option => renderOption(option))} +
+ ) : null} +
+ ) +} + +Select.displayName = 'Select' + +export default Select diff --git a/packages/neuron-ui/src/widgets/NewUIInputSelect/input-select.module.scss b/packages/neuron-ui/src/widgets/NewUIInputSelect/input-select.module.scss new file mode 100644 index 0000000000..3dbcbeae66 --- /dev/null +++ b/packages/neuron-ui/src/widgets/NewUIInputSelect/input-select.module.scss @@ -0,0 +1,102 @@ +.root { + position: relative; + font-size: 0.75rem; +} + + +.inputBox { + 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; + + &[data-open="true"] { + border: 1px solid #000; + } + + &:hover { + border: 1px solid #000; + } + + & > input { + border: none; + width: 100%; + padding: 4px 24px 4px 12px; + } +} + +.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: 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; +} + +.menu .group > .title{ + padding: 8px 10px; + color: rgba(51, 51, 51, 1); + font-weight: bold; + text-transform: capitalize; +} + +.option { + box-sizing: border-box; + color: rgba(51, 51, 51, 0.8); + cursor: pointer; + display: block; + padding: 8px 10px; + + &[aria-selected="true"] { + background-color: #ccc; + } + + &.is-select { + background-color: #eee; + } + + &:last-child { + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; + } + + &:hover { + background-color: #eee; + color: #333; + } +} + +.noresults { + box-sizing: border-box; + color: #ccc; + cursor: default; + display: block; + padding: 8px 10px; +} diff --git a/packages/neuron-ui/src/widgets/Table/index.tsx b/packages/neuron-ui/src/widgets/Table/index.tsx index 6011b7b806..2168512e4c 100755 --- a/packages/neuron-ui/src/widgets/Table/index.tsx +++ b/packages/neuron-ui/src/widgets/Table/index.tsx @@ -12,6 +12,8 @@ export type TableProps = { key?: string isBalance?: boolean render?: (v: any, idx: number, item: T, showBalance: boolean) => React.ReactNode + width?: string + minWidth?: string align?: 'left' | 'right' | 'center' className?: string tdClassName?: string @@ -39,7 +41,7 @@ const Table = >(props: TableProps) => {
- {columns.map(({ title, dataIndex, key, isBalance, align, className: headClassName }) => { + {columns.map(({ title, dataIndex, key, isBalance, align, width, minWidth, className: headClassName }) => { return (
>(props: TableProps) => { data-field={dataIndex} align={align ?? 'left'} className={headClassName} + style={{ width, minWidth }} > {!!dataSource.length && isBalance ? (
diff --git a/packages/neuron-ui/src/widgets/TextField/textField.module.scss b/packages/neuron-ui/src/widgets/TextField/textField.module.scss index 20ffaa71b6..5254656a0c 100644 --- a/packages/neuron-ui/src/widgets/TextField/textField.module.scss +++ b/packages/neuron-ui/src/widgets/TextField/textField.module.scss @@ -43,7 +43,7 @@ background: #ffffff; border: 1px solid var(--divide-line-color); border-radius: 8px; - width: 480px; + width: 100%; height: 56px; padding: 0 16px;