diff --git a/src/locales/en.json b/src/locales/en.json index 9313a0b1b..b28d14216 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -417,7 +417,16 @@ "leap_out_tip": "This RGB++ transaction is marked as \"L2 → L1\", indicating that the number of cells with RGB++ lock in the input exceeds the number of cells with RGB++ lock in the output.", "view_in_btc_explorer": "View in Bitcoin Explorer", "view-as-asset-items": "View Asset Distribution", - "view-as-merged-assets": "View Asset Amounts(CKB excluded)" + "view-as-merged-assets": "View Asset Amounts(CKB excluded)", + "block-height": "Block Height", + "out-point": "OutPoint", + "capacity": "Capacity(CKB)", + "type": "Type", + "amount": "Amount", + "detail": "Detail", + "asset": "Asset", + "view-asset": "View Asset", + "cell-info": "Cell Info" }, "rgbpp": { "transaction": { @@ -895,7 +904,9 @@ }, "sort": { "time": "Sort by time", - "capacity": "Sort by capacity" + "capacity": "Sort by capacity", + "card": "Show as card view", + "list": "Show as list view" }, "decoder": { "view-data-as": "View data as", diff --git a/src/locales/zh.json b/src/locales/zh.json index 5464d7c65..2bfd6d163 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -417,7 +417,16 @@ "leap_out_tip": "本笔 RGB++ 交易被标记为\"L2 → L1\",即存在 RGB++ 资产转化为一般 CKB 资产。", "view_in_btc_explorer": "在 Bitcoin 浏览器查看", "view-as-asset-items": "查看资产分布", - "view-as-merged-assets": "查看资产合计(不含 CKB)" + "view-as-merged-assets": "查看资产合计(不含 CKB)", + "block-height": "区块高度", + "out-point": "OutPoint", + "capacity": "Capacity", + "type": "类型", + "amount": "数量", + "detail": "详情", + "asset": "资产", + "view-asset": "查看资产", + "cell-info": "Cell详情" }, "rgbpp": { "transaction": { @@ -896,7 +905,9 @@ }, "sort": { "time": "按时间排序", - "capacity": "按 Capacity 排序" + "capacity": "按 Capacity 排序", + "card": "按卡片样式展示", + "list": "按列表样式展示" }, "decoder": { "view-data-as": "选择解码格式", diff --git a/src/pages/Address/AddressComp.tsx b/src/pages/Address/AddressComp.tsx index 11d5953f2..f9df1f4a5 100644 --- a/src/pages/Address/AddressComp.tsx +++ b/src/pages/Address/AddressComp.tsx @@ -47,15 +47,8 @@ import { Address, UDTAccount } from '../../models/Address' import { Card, CardCellInfo, CardCellsLayout } from '../../components/Card' import { CardHeader } from '../../components/Card/CardHeader' import Cells from './Cells' -import { - AddressCoTAComp, - AddressOmigaInscriptionComp, - AddressMNFTComp, - AddressSporeComp, - AddressSudtComp, - AddressXudtComp, - AddressNRC721Comp, -} from './AddressAssetComp' +import DefinedTokens from './DefinedTokens' +import { AddressOmigaInscriptionComp } from './AddressAssetComp' enum AssetInfo { UDT = 1, @@ -255,41 +248,7 @@ export const AddressOverviewCard: FC<{ address: Address }> = ({ address }) => { key={AssetInfo.UDT} >
- {udts.map(udt => { - switch (udt.udtType) { - case 'xudt': - return - - case 'sudt': - return - - case 'spore_cell': - return - - case 'm_nft_token': - return - - case 'nrc_721_token': - return - - default: - return null - } - })} - {cotaList?.map(cota => ( - - )) ?? null} +
)} diff --git a/src/pages/Address/Cells.tsx b/src/pages/Address/Cells.tsx index 872c56ddd..fe355e9a6 100644 --- a/src/pages/Address/Cells.tsx +++ b/src/pages/Address/Cells.tsx @@ -1,12 +1,11 @@ import { type FC, useState, useRef, useEffect } from 'react' -import { useTranslation } from 'react-i18next' +import { TFunction, useTranslation } from 'react-i18next' import BigNumber from 'bignumber.js' import { useInfiniteQuery } from '@tanstack/react-query' import { Tooltip } from 'antd' import { explorerService, LiveCell } from '../../services/ExplorerService' import SUDTTokenIcon from '../../assets/sudt_token.png' import CKBTokenIcon from './ckb_token_icon.png' -import { ReactComponent as CopyIcon } from './copy.svg' import { ReactComponent as TypeHashIcon } from './type_script.svg' import { ReactComponent as DataIcon } from './data.svg' import { ReactComponent as SporeCluterIcon } from './spore_cluster.svg' @@ -18,10 +17,11 @@ import { ReactComponent as ListIcon } from './list.svg' import { ReactComponent as GridIcon } from './grid.svg' import { parseUDTAmount } from '../../utils/number' import { shannonToCkb } from '../../utils/util' -import { useSetToast } from '../../components/Toast' -import { PAGE_SIZE } from '../../constants/common' +import { parseSimpleDateNoSecond } from '../../utils/date' import styles from './cells.module.scss' import SmallLoading from '../../components/Loading/SmallLoading' +import { TransactionCellInfo } from '../Transaction/TransactionCell' +import { CellBasicInfo } from '../../utils/transformer' import { sliceNftName } from '../../utils/string' enum Sort { @@ -53,31 +53,13 @@ const initialPageParams = { size: 10, sort: 'capacity.desc' } const ATTRIBUTE_LENGTH = 18 -const Cell: FC<{ cell: LiveCell }> = ({ cell }) => { - const setToast = useSetToast() - const { t } = useTranslation() - - const handleCopy = (e: React.MouseEvent) => { - e.stopPropagation() - e.preventDefault() - const { detail } = e.currentTarget.dataset - if (!detail) return - navigator.clipboard.writeText(detail).then(() => { - setToast({ message: t('common.copied') }) - }) - } - +const getCellDetails = (cell: LiveCell, t: TFunction) => { const ckb = new BigNumber(shannonToCkb(+cell.capacity)).toFormat() - const title = `${cell.txHash.slice(0, 8)}...${cell.txHash.slice(-8)}#${cell.cellIndex}` - const link = `/transaction/${cell.txHash}?${new URLSearchParams({ - page_of_outputs: Math.ceil((+cell.cellIndex + 1) / PAGE_SIZE).toString(), - })}` const assetType: string = cell.extraInfo?.type ?? cell.cellType let icon: string | React.ReactElement | null = null let assetName = null let attribute = null let detailInfo = null - switch (assetType) { case 'ckb': { if (cell.typeHash) { @@ -183,44 +165,105 @@ const Cell: FC<{ cell: LiveCell }> = ({ cell }) => { attribute = '-' } } - const outPoint = { - tx_hash: cell.txHash, - index: `0x${cell.cellIndex.toString(16)}`, + + const outPointStr = `${cell.txHash.slice(0, 8)}...${cell.txHash.slice(-8)}#${cell.cellIndex}` + const parsedBlockCreateAt = parseSimpleDateNoSecond(cell.blockTimestamp) + const title = `${cell.cellId}: ${ckb} ` + const cellInfo = { + ...cell, + id: Number(cell.cellId), + isGenesisOutput: Number(cell.blockNumber) === 0, + } as CellBasicInfo + + return { + ckb, + outPointStr, + icon, + assetName, + attribute, + detailInfo, + title, + parsedBlockCreateAt, + cellInfo, } +} + +const Cell: FC<{ cell: LiveCell }> = ({ cell }) => { + const { t } = useTranslation() + + const { title, parsedBlockCreateAt, icon, assetName, attribute, detailInfo, cellInfo } = getCellDetails(cell, t) return (
  • -
    - {title} + + +
    + {title} + CKB ({parsedBlockCreateAt}) +
    +
    - - {`${ckb} CKB`} -
    -
    - {typeof icon === 'string' ? {assetName : null} - {icon && typeof icon !== 'string' ? icon : null} -
    -
    {assetName}
    -
    -
    {attribute}
    - {detailInfo ? ( - - ) : null} +
    + {typeof icon === 'string' ? {assetName : null} + {icon && typeof icon !== 'string' ? icon : null} +
    +
    {assetName}
    +
    + {attribute} +
    -
    +
  • ) } +const CellTable: FC<{ cells: LiveCell[] }> = ({ cells }) => { + const { t } = useTranslation() + const headers = getTableHeaders(t) + + return ( +
    + + + + {headers.map(header => ( + + ))} + + + + {cells.map((cell, index) => { + const { ckb, outPointStr, assetName, attribute, cellInfo } = getCellDetails(cell, t) + + return ( + + + + + + + + + + ) + })} + +
    {header.title}
    {index + 1}{cell.blockNumber}{outPointStr}{ckb}{cell.extraInfo?.type} + {attribute} {attribute === 'Unknown amount' ? '' : assetName} + + + {t('address.detail')} + +
    +
    + ) +} + const Cells: FC<{ address: string; count: number }> = ({ address, count }) => { const { t } = useTranslation() const [params, setParams] = useState(initialPageParams) - const [isDisplayedAsList, setIsDisplayedAsList] = useState(true) + const [isDisplayedAsList, setIsDisplayedAsList] = useState(false) const loadMoreRef = useRef(null) const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery( @@ -279,9 +322,6 @@ const Cells: FC<{ address: string; count: number }> = ({ address, count }) => { data-sort={params.sort === Sort.TimeAsc ? Sort.TimeDesc : Sort.TimeAsc} onClick={handleSortChange} data-is-active={params.sort === Sort.TimeAsc || params.sort === Sort.TimeDesc} - style={{ - display: 'none', // FIXME: wait for api fix - }} > {params.sort === Sort.TimeAsc ? : } @@ -296,35 +336,56 @@ const Cells: FC<{ address: string; count: number }> = ({ address, count }) => { - + + + -
      - {cells.map(cell => ( - - ))} -
    - {isFetchingNextPage ? ( - - - - ) : null} - {!hasNextPage || isFetchingNextPage ? null : ( -
    - -
    - )} + +
    + {isDisplayedAsList ? ( + + ) : ( +
      + {cells.map(cell => ( + + ))} +
    + )} + + {isFetchingNextPage ? ( + + + + ) : null} + {!hasNextPage || isFetchingNextPage ? null : ( +
    + +
    + )} +
    ) } export default Cells + +const getTableHeaders = (t: TFunction): TableHeader[] => { + return [ + { title: '#', key: 'index' }, + { title: t('address.block-height'), key: 'block-number' }, + { title: t('address.out-point'), key: 'out-point' }, + { title: t('address.capacity'), key: 'capacity' }, + { title: t('address.type'), key: 'type' }, + { title: t('address.amount'), key: 'amount' }, + { title: '', key: 'action' }, + ] +} + +interface TableHeader { + title: string + key: string +} diff --git a/src/pages/Address/DefinedTokens.tsx b/src/pages/Address/DefinedTokens.tsx new file mode 100644 index 000000000..51e2f3d82 --- /dev/null +++ b/src/pages/Address/DefinedTokens.tsx @@ -0,0 +1,195 @@ +import { type FC, useState, useMemo } from 'react' +import { TFunction, useTranslation } from 'react-i18next' +import { Tooltip } from 'antd' +import { ReactComponent as ListIcon } from './list.svg' +import { ReactComponent as GridIcon } from './grid.svg' +import { parseUDTAmount } from '../../utils/number' +import { formatNftDisplayId } from '../../utils/util' +import styles from './definedTokens.module.scss' +import { sliceNftName } from '../../utils/string' +import { NFTItem } from '../../services/ExplorerService/fetcher' +import { UDTAccount } from '../../models/Address' +import { + AddressCoTAComp, + AddressMNFTComp, + AddressSporeComp, + AddressSudtComp, + AddressXudtComp, + AddressNRC721Comp, +} from './AddressAssetComp' + +const TokenTable: FC<{ udts: UDTAccount[]; cotaList: NFTItem[] }> = ({ udts, cotaList }) => { + const { t } = useTranslation() + const headers = getTableHeaders(t) + + return ( +
    + + + + {headers.map(header => ( + + ))} + + + + {udts.map((udt, index) => { + let type = '' + let key = udt.symbol + udt.udtType + let href = '' + let asset = '' + let amount = '' + switch (udt.udtType) { + case 'xudt': + type = 'xUDT' + key += udt.amount + href = `/xudt/${udt.typeHash}` + asset = udt.uan || udt.symbol + amount = `${parseUDTAmount(udt.amount, udt.decimal)} ${asset}` + break + case 'sudt': + type = 'sUDT' + key += udt.amount + href = `/sudt/${udt.typeHash}` + asset = udt.uan || udt.symbol + amount = `${parseUDTAmount(udt.amount, udt.decimal)} ${asset}` + break + case 'spore_cell': + type = 'DOB' + key += udt.amount + href = `/nft-collections/${udt.collection?.typeHash}` + asset = sliceNftName(udt.symbol) || '' + // eslint-disable-next-line no-case-declarations + const id = formatNftDisplayId(udt.amount, 'spore') + amount = `id: ${id.slice(0, 8)}...${id.slice(-8)}` + break + case 'm_nft_token': + type = 'm-NFT' + key += udt.amount + href = `/nft-collections/${udt.collection?.typeHash}` + asset = sliceNftName(udt.symbol) || '' + amount = `#${udt.amount}` + break + case 'nrc_721_token': + type = 'NRC 721' + key += udt.amount + href = `/nft-collections/${udt.collection?.typeHash}` + asset = !udt.symbol ? '?' : sliceNftName(`${udt.symbol} #${udt.collection.typeHash.slice(2, 5)}`) || '' + amount = `id: ${udt.amount.slice(0, 5)}...${udt.amount.slice(-4)}` + break + default: + break + } + + return ( + + + + + + + + ) + })} + {cotaList.map((cota, index) => { + return ( + + + + + + + + ) + })} + +
    {header.title}
    {index + 1}{type}{asset || '/'}{amount} + {t('address.view-asset')} +
    {udts.length + index + 1}CoTA{sliceNftName(cota.collection.name)}{`#${Number(cota.token_id)}`} + {t('address.view-asset')} +
    +
    + ) +} + +const DefinedTokens: FC<{ udts: UDTAccount[]; cotaList?: NFTItem[] }> = ({ udts, cotaList = [] }) => { + const { t } = useTranslation() + const [isDisplayedAsList, setIsDisplayedAsList] = useState(false) + + const count = useMemo(() => udts.length + cotaList.length, [udts, cotaList]) + + return ( +
    +
    +
    Count: {count.toLocaleString('en')}
    +
    + + + +
    +
    + +
    + {isDisplayedAsList ? ( + + ) : ( +
      + {udts.map(udt => { + switch (udt.udtType) { + case 'xudt': + return + + case 'sudt': + return + + case 'spore_cell': + return + + case 'm_nft_token': + return + + case 'nrc_721_token': + return + + default: + return null + } + })} + {cotaList?.map(cota => ( + + )) ?? null} +
    + )} +
    +
    + ) +} +export default DefinedTokens + +const getTableHeaders = (t: TFunction): TableHeader[] => { + return [ + { title: '#', key: 'index' }, + { title: t('address.type'), key: 'type' }, + { title: t('address.asset'), key: 'asset' }, + { title: t('address.amount'), key: 'amount' }, + { title: '', key: 'action' }, + ] +} + +interface TableHeader { + title: string + key: string +} diff --git a/src/pages/Address/RgbppAssets.tsx b/src/pages/Address/RgbppAssets.tsx index 213d6f25e..6add7a4c3 100644 --- a/src/pages/Address/RgbppAssets.tsx +++ b/src/pages/Address/RgbppAssets.tsx @@ -1,8 +1,10 @@ import { type FC, useState, useRef, useEffect } from 'react' -import { useTranslation } from 'react-i18next' +import { TFunction, useTranslation } from 'react-i18next' import BigNumber from 'bignumber.js' import { useInfiniteQuery } from '@tanstack/react-query' import { Tooltip } from 'antd' +import { ReactComponent as ListIcon } from './list.svg' +import { ReactComponent as GridIcon } from './grid.svg' import { explorerService, LiveCell } from '../../services/ExplorerService' import SUDTTokenIcon from '../../assets/sudt_token.png' import CKBTokenIcon from './ckb_token_icon.png' @@ -14,6 +16,7 @@ import { ReactComponent as SporeCellIcon } from './spore_cell.svg' import { ReactComponent as MergedAssetIcon } from './merged_assets.svg' import { ReactComponent as AssetItemsIcon } from './asset_items.svg' import SmallLoading from '../../components/Loading/SmallLoading' +import { TransactionCellInfo } from '../Transaction/TransactionCell' import { parseUDTAmount } from '../../utils/number' import { getContractHashTag, shannonToCkb } from '../../utils/util' import { useSetToast } from '../../components/Toast' @@ -21,6 +24,7 @@ import { PAGE_SIZE } from '../../constants/common' import styles from './rgbppAssets.module.scss' import MergedAssetList from './MergedAssetList' import { UDTAccount } from '../../models/Address' +import { CellBasicInfo } from '../../utils/transformer' const fetchCells = async ({ address, @@ -44,20 +48,7 @@ const initialPageParams = { size: 10, sort: 'capacity.desc' } const ATTRIBUTE_LENGTH = 18 -const AssetItem: FC<{ cell: LiveCell }> = ({ cell }) => { - const setToast = useSetToast() - const { t } = useTranslation() - - const handleCopy = (e: React.MouseEvent) => { - e.stopPropagation() - e.preventDefault() - const { detail } = e.currentTarget.dataset - if (!detail) return - navigator.clipboard.writeText(detail).then(() => { - setToast({ message: t('common.copied') }) - }) - } - +const getCellDetails = (cell: LiveCell, t: TFunction) => { const ckb = new BigNumber(shannonToCkb(+cell.capacity)).toFormat() const link = `/transaction/${cell.txHash}?${new URLSearchParams({ page_of_outputs: Math.ceil((+cell.cellIndex + 1) / PAGE_SIZE).toString(), @@ -68,6 +59,8 @@ const AssetItem: FC<{ cell: LiveCell }> = ({ cell }) => { let attribute = null let detailInfo = null + const utxo = `${cell.txHash.slice(0, 4)}...${cell.txHash.slice(-3)}:${cell.cellIndex}` + switch (assetType) { case 'ckb': { if (cell.typeHash) { @@ -172,6 +165,41 @@ const AssetItem: FC<{ cell: LiveCell }> = ({ cell }) => { } } + const cellInfo = { + ...cell, + id: Number(cell.cellId), + isGenesisOutput: false, + } as CellBasicInfo + + return { + link, + assetType, + ckb, + detailInfo, + icon, + assetName, + attribute, + cellInfo, + utxo, + } +} + +const AssetItem: FC<{ cell: LiveCell }> = ({ cell }) => { + const setToast = useSetToast() + const { t } = useTranslation() + + const handleCopy = (e: React.MouseEvent) => { + e.stopPropagation() + e.preventDefault() + const { detail } = e.currentTarget.dataset + if (!detail) return + navigator.clipboard.writeText(detail).then(() => { + setToast({ message: t('common.copied') }) + }) + } + + const { link, assetType, ckb, detailInfo, icon, assetName, attribute } = getCellDetails(cell, t) + return (
  • = ({ cell }) => { {`${ckb} CKB`}
    -
    +
    {typeof icon === 'string' ? {assetName : null} {icon && typeof icon !== 'string' ? icon : null}
    @@ -203,7 +231,54 @@ const AssetItem: FC<{ cell: LiveCell }> = ({ cell }) => { ) } -const RgbAssetItems: FC<{ address: string; count: number }> = ({ address, count }) => { +const CellTable: FC<{ cells: LiveCell[] }> = ({ cells }) => { + const { t } = useTranslation() + const headers = getTableHeaders(t) + + return ( +
    + + + + {headers.map(header => ( + + ))} + + + + {cells.map((cell, index) => { + const { utxo, link, assetType, ckb, assetName, attribute, cellInfo } = getCellDetails(cell, t) + return ( + + + + + + + + + + ) + })} + +
    {header.title}
    {index + 1} + {utxo} + {assetType}{assetName} + {attribute} {assetName} + {ckb} + + {t('address.cell-info')} + +
    +
    + ) +} + +const RgbAssetItems: FC<{ address: string; count: number; isDisplayedAsList: boolean }> = ({ + address, + count, + isDisplayedAsList, +}) => { const [params] = useState(initialPageParams) const loadMoreRef = useRef(null) @@ -260,11 +335,15 @@ const RgbAssetItems: FC<{ address: string; count: number }> = ({ address, count return ( <> -
      - {cells.map(cell => ( - - ))} -
    + {isDisplayedAsList ? ( + + ) : ( +
      + {cells.map(cell => ( + + ))} +
    + )} {isFetchingNextPage ? ( @@ -289,6 +368,7 @@ const RgbAssets: FC<{ address: string; count: number; udts: UDTAccount[]; inscri }) => { const [isMerged, setIsMerged] = useState(true) const { t } = useTranslation() + const [isDisplayedAsList, setIsDisplayedAsList] = useState(false) return (
    @@ -296,19 +376,62 @@ const RgbAssets: FC<{ address: string; count: number; udts: UDTAccount[]; inscri
    {t(`address.${isMerged ? 'view-as-merged-assets' : 'view-as-asset-items'}`)}
    - + + +
    - {isMerged ? ( - - ) : ( - - )} + +
    + {isDisplayedAsList ? ( + + ) : ( + <> + {isMerged ? ( + + ) : ( + + )} + + )} +
    ) } export default RgbAssets + +const getTableHeaders = (t: TFunction): TableHeader[] => { + return [ + { title: '#', key: 'index' }, + { title: 'UTXO', key: 'utxo' }, + { title: t('address.type'), key: 'type' }, + { title: t('address.asset'), key: 'asset' }, + { title: t('address.amount'), key: 'amount' }, + { title: t('address.capacity'), key: 'capacity' }, + { title: '', key: 'action' }, + ] +} + +interface TableHeader { + title: string + key: string +} diff --git a/src/pages/Address/cells.module.scss b/src/pages/Address/cells.module.scss index fac4d70ce..72b115119 100644 --- a/src/pages/Address/cells.module.scss +++ b/src/pages/Address/cells.module.scss @@ -1,3 +1,5 @@ +@import '../../styles/variables.module'; + .container { width: 100%; overflow: scroll; @@ -49,6 +51,18 @@ } } + .content { + overflow-x: scroll; + + @media (width >= $extraLargeBreakPoint) { + max-height: 220px; + } + + @media (width <= $extraLargeBreakPoint) { + max-height: 310px; + } + } + ul { list-style: none; padding: 0; @@ -66,50 +80,26 @@ h5 { display: flex; - justify-content: space-between; align-items: center; background: var(--primary-color); color: #fff; height: 1.875rem; padding: 8px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - a { - &:hover { - font-weight: bold; - color: #fff; - text-decoration: underline; + span { + &:first-child { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } - } - - .copy { - appearance: none; - border: none; - background: none; - width: 14px; - cursor: pointer; - margin-right: 8px; - height: 14px; - svg { - pointer-events: none; - height: 14px; + &:last-child { + flex-shrink: 0; } } - - span { - display: block; - overflow: hidden; - text-overflow: ellipsis; - cursor: default; - flex: 1; - text-align: right; - } } - .content { + .cellContent { padding: 8px; display: flex; line-height: 1; @@ -160,6 +150,47 @@ } } +.tableContainer { + table { + width: 100%; + background: #fff; + border: 1px solid #e5e5e5; + + thead tr th { + background: #fff; + position: sticky; + top: -1px; + } + + th, + td { + border: 1px solid #e5e5e5; + text-align: center; + padding: 0 24px; + font-size: 14px; + color: #333; + } + + th { + height: 40px; + } + + td { + height: 48px; + } + } +} + +.detail { + text-decoration: underline; + cursor: pointer; + color: #666; + + &:hover { + color: var(--primary-color); + } +} + .loading { display: flex; justify-content: center; diff --git a/src/pages/Address/definedTokens.module.scss b/src/pages/Address/definedTokens.module.scss new file mode 100644 index 000000000..1c5a31c58 --- /dev/null +++ b/src/pages/Address/definedTokens.module.scss @@ -0,0 +1,108 @@ +@import '../../styles/variables.module'; + +.container { + width: 100%; + overflow: scroll; + + .toolbar { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 1rem; + margin-bottom: 1rem; + + .filters { + display: flex; + align-items: center; + gap: 24px; + + button { + width: 20px; + height: 20px; + appearance: none; + border: none; + cursor: pointer; + color: #999; + + &:hover { + color: var(--primary-color); + } + + svg { + pointer-events: none; + width: 100%; + height: 100%; + } + } + + .capacitySortIcon { + &[data-current-sort='capacity.asc'] { + path:last-of-type { + fill: var(--primary-color); + } + } + + &[data-current-sort='capacity.desc'] { + path:first-of-type { + fill: var(--primary-color); + } + } + } + } + } + + .content { + overflow-x: scroll; + + @media (width >= $extraLargeBreakPoint) { + max-height: 220px; + } + + @media (width <= $extraLargeBreakPoint) { + max-height: 310px; + } + } + + ul { + list-style: none; + padding: 0; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(248px, 1fr)); + gap: 1rem; + } +} + +.tableContainer { + table { + width: 100%; + background: #fff; + border: 1px solid #e5e5e5; + + thead tr th { + background: #fff; + position: sticky; + top: -1px; + } + + th, + td { + border: 1px solid #e5e5e5; + text-align: center; + padding: 0 24px; + font-size: 14px; + color: #333; + } + + th { + height: 40px; + } + + td { + height: 48px; + } + } + + a { + text-decoration: underline; + } +} diff --git a/src/pages/Address/rgbppAssets.module.scss b/src/pages/Address/rgbppAssets.module.scss index 961d1c3d6..a1d3ecd57 100644 --- a/src/pages/Address/rgbppAssets.module.scss +++ b/src/pages/Address/rgbppAssets.module.scss @@ -1,3 +1,5 @@ +@import '../../styles/variables.module'; + .container { width: 100%; overflow: scroll; @@ -49,6 +51,18 @@ } } + .content { + overflow-x: scroll; + + @media (width >= $extraLargeBreakPoint) { + max-height: 220px; + } + + @media (width <= $extraLargeBreakPoint) { + max-height: 310px; + } + } + ul { list-style: none; padding: 0; @@ -110,7 +124,7 @@ } } - .content { + .itemContent { padding: 8px; display: flex; line-height: 1; @@ -155,6 +169,46 @@ } } +.tableContainer { + table { + width: 100%; + background: #fff; + border: 1px solid #e5e5e5; + + thead tr th { + background: #fff; + position: sticky; + top: -1px; + } + + th, + td { + border: 1px solid #e5e5e5; + text-align: center; + padding: 0 24px; + font-size: 14px; + color: #333; + } + + th { + height: 40px; + } + + td { + height: 48px; + } + } + + .cellInfo { + text-decoration: underline; + cursor: pointer; + + &:hover { + color: var(--primary-color); + } + } +} + .loading { display: flex; justify-content: center; diff --git a/src/pages/Address/styles.module.scss b/src/pages/Address/styles.module.scss index 59317476a..b43d82631 100644 --- a/src/pages/Address/styles.module.scss +++ b/src/pages/Address/styles.module.scss @@ -285,17 +285,9 @@ margin-top: 10px; background-color: #f1f1f1; padding: 6px 25px; + border-radius: 4px; width: 100%; display: flex; flex-flow: row wrap; - overflow-y: scroll; gap: 1rem; - - @media (width >= $extraLargeBreakPoint) { - max-height: 220px; - } - - @media (width <= $extraLargeBreakPoint) { - max-height: 310px; - } } diff --git a/src/pages/Transaction/TransactionCell/index.tsx b/src/pages/Transaction/TransactionCell/index.tsx index 1d6e9f4d7..28fe1226e 100644 --- a/src/pages/Transaction/TransactionCell/index.tsx +++ b/src/pages/Transaction/TransactionCell/index.tsx @@ -43,6 +43,7 @@ import { useDASAccount } from '../../../hooks/useDASAccount' import styles from './styles.module.scss' import AddressText from '../../../components/AddressText' import { Cell } from '../../../models/Cell' +import { CellBasicInfo } from '../../../utils/transformer' export const Addr: FC<{ address: string; isCellBase: boolean }> = ({ address, isCellBase }) => { const alias = useDASAccount(address) @@ -171,7 +172,7 @@ const useParseNftInfo = (cell: Cell) => { } } -const TransactionCellDetail = ({ cell }: { cell: Cell }) => { +export const TransactionCellDetail = ({ cell }: { cell: Cell }) => { const { t } = useTranslation() let detailTitle = t('transaction.ckb_capacity') let detailIcon @@ -266,12 +267,20 @@ const TransactionCellDetail = ({ cell }: { cell: Cell }) => { ) } -const TransactionCellInfo = ({ cell, children }: { cell: Cell; children: string | ReactNode }) => { +export const TransactionCellInfo = ({ + cell, + children, + isDefaultStyle = true, +}: { + cell: CellBasicInfo + children: string | ReactNode + isDefaultStyle?: boolean +}) => { const [showModal, setShowModal] = useState(false) return ( { setShowModal(true) }} @@ -279,6 +288,7 @@ const TransactionCellInfo = ({ cell, children }: { cell: Cell; children: string
    {children}
    + setShowModal(false)} /> diff --git a/src/services/ExplorerService/fetcher.ts b/src/services/ExplorerService/fetcher.ts index 46b853505..2ebc1171c 100644 --- a/src/services/ExplorerService/fetcher.ts +++ b/src/services/ExplorerService/fetcher.ts @@ -49,7 +49,7 @@ const v1GetUnwrappedPagedList = (...args: Parameters) => v1Get[]>(...args).then(res => { assert(res.meta, 'Unexpected paged list response') return { - data: res.data.map(wrapper => wrapper.attributes), + data: res.data.map(wrapper => ({ ...wrapper.attributes, cellId: wrapper.id })), ...res.meta, } }) diff --git a/src/services/ExplorerService/types.ts b/src/services/ExplorerService/types.ts index 403a2f54f..1a9e2c68b 100644 --- a/src/services/ExplorerService/types.ts +++ b/src/services/ExplorerService/types.ts @@ -349,6 +349,7 @@ export interface Script { } export interface LiveCell { + cellId: string cellType: string txHash: string cellIndex: number