diff --git a/src/app/components/account-total-balance.tsx b/src/app/components/account-total-balance.tsx index 4dfb1a5622f..efb430ded57 100644 --- a/src/app/components/account-total-balance.tsx +++ b/src/app/components/account-total-balance.tsx @@ -13,5 +13,9 @@ export const AccountTotalBalance = memo(({ btcAddress, stxAddress }: AccountTota if (!totalBalance) return null; - return {totalBalance.totalUsdBalance}; + return ( + + {totalBalance.totalUsdBalance} + + ); }); diff --git a/src/app/components/account/account-addresses.tsx b/src/app/components/account/account-addresses.tsx new file mode 100644 index 00000000000..f9c3c66b64e --- /dev/null +++ b/src/app/components/account/account-addresses.tsx @@ -0,0 +1,29 @@ +import { HStack } from 'leather-styles/jsx'; + +import { useViewportMinWidth } from '@app/common/hooks/use-media-query'; +import { BulletSeparator } from '@app/ui/components/bullet-separator/bullet-separator'; +import { Caption } from '@app/ui/components/typography/caption'; +import { truncateMiddle } from '@app/ui/utils/truncate-middle'; + +import { StacksAccountLoader } from '../loaders/stacks-account-loader'; +import { BitcoinNativeSegwitAccountLoader } from './bitcoin-account-loader'; + +interface AccountAddressesProps { + index: number; +} +export function AcccountAddresses({ index }: AccountAddressesProps) { + const isBreakpointSm = useViewportMinWidth('sm'); + + return ( + + + + {account => {truncateMiddle(account.address, isBreakpointSm ? 4 : 3)}} + + + {signer => {truncateMiddle(signer.address, 5)}} + + + + ); +} diff --git a/src/app/components/account/account-list-item-layout.tsx b/src/app/components/account/account-list-item-layout.tsx deleted file mode 100644 index c639cc12969..00000000000 --- a/src/app/components/account/account-list-item-layout.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { SettingsSelectors } from '@tests/selectors/settings.selectors'; -import { Flex, HStack, Stack, StackProps, styled } from 'leather-styles/jsx'; - -import { useViewportMinWidth } from '@app/common/hooks/use-media-query'; -import { BulletSeparator } from '@app/ui/components/bullet-separator/bullet-separator'; -import { Flag } from '@app/ui/components/flag/flag'; -import { CheckmarkIcon } from '@app/ui/components/icons/checkmark-icon'; -import { Spinner } from '@app/ui/components/spinner'; -import { truncateMiddle } from '@app/ui/utils/truncate-middle'; - -import { StacksAccountLoader } from '../loaders/stacks-account-loader'; -import { BitcoinNativeSegwitAccountLoader } from './bitcoin-account-loader'; - -interface AccountListItemLayoutProps extends StackProps { - isLoading: boolean; - isActive: boolean; - index: number; - accountName: React.ReactNode; - avatar: React.JSX.Element; - balanceLabel: React.ReactNode; - onSelectAccount(): void; -} -export function AccountListItemLayout(props: AccountListItemLayoutProps) { - const { - index, - isLoading, - isActive, - accountName, - avatar, - balanceLabel, - onSelectAccount, - children = null, - ...rest - } = props; - - const isBreakpointSm = useViewportMinWidth('sm'); - - return ( - - - - - - {accountName} - {isActive && } - - {isLoading ? ( - - ) : ( - balanceLabel - )} - - - - - {account => ( - - {truncateMiddle(account.address, isBreakpointSm ? 4 : 3)} - - )} - - - - {signer => ( - - {truncateMiddle(signer.address, 5)} - - )} - - - - - - {children} - - ); -} diff --git a/src/app/components/account/account-list-item.layout.tsx b/src/app/components/account/account-list-item.layout.tsx new file mode 100644 index 00000000000..f353437b7ec --- /dev/null +++ b/src/app/components/account/account-list-item.layout.tsx @@ -0,0 +1,58 @@ +import { ReactNode } from 'react'; + +import { SettingsSelectors } from '@tests/selectors/settings.selectors'; + +import { ItemInteractive } from '@app/ui/components/item/item-interactive'; +import { ItemLayout } from '@app/ui/components/item/item.layout'; +import { Spinner } from '@app/ui/components/spinner'; + +interface AccountListItemLayoutProps { + accountAddresses: ReactNode; + accountName: ReactNode; + avatar: ReactNode; + balanceLabel: ReactNode; + index: number; + isLoading: boolean; + isSelected: boolean; + onSelectAccount(): void; +} +export function AccountListItemLayout(props: AccountListItemLayoutProps) { + const { + accountAddresses, + accountName, + avatar, + balanceLabel, + index, + isLoading, + isSelected, + onSelectAccount, + } = props; + + return ( + + + ) : ( + balanceLabel + ) + } + captionLeft={accountAddresses} + /> + + ); +} diff --git a/src/app/components/account/account-name.tsx b/src/app/components/account/account-name.tsx index 67a07d4fdbf..7101f5d76d5 100644 --- a/src/app/components/account/account-name.tsx +++ b/src/app/components/account/account-name.tsx @@ -6,5 +6,7 @@ interface AccountNameLayoutProps { children: React.ReactNode; } export const AccountNameLayout = memo(({ children }: AccountNameLayoutProps) => ( - {children} + + {children} + )); diff --git a/src/app/components/bitcoin-transaction-item/bitcoin-transaction-item.tsx b/src/app/components/bitcoin-transaction-item/bitcoin-transaction-item.tsx index 227990cc93f..3838cd0a656 100644 --- a/src/app/components/bitcoin-transaction-item/bitcoin-transaction-item.tsx +++ b/src/app/components/bitcoin-transaction-item/bitcoin-transaction-item.tsx @@ -1,8 +1,6 @@ import { useMemo } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { BoxProps } from 'leather-styles/jsx'; - import { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model'; import { RouteUrls } from '@shared/route-urls'; @@ -15,7 +13,6 @@ import { isBitcoinTxInbound, } from '@app/common/transactions/bitcoin/utils'; import { openInNewTab } from '@app/common/utils/open-in-new-tab'; -import { usePressable } from '@app/components/item-hover'; import { IncreaseFeeButton } from '@app/components/stacks-transaction-item/increase-fee-button'; import { TransactionTitle } from '@app/components/transaction/transaction-title'; import { @@ -34,11 +31,10 @@ import { InscriptionIcon } from './bitcoin-transaction-inscription-icon'; import { BitcoinTransactionStatus } from './bitcoin-transaction-status'; import { BitcoinTransactionValue } from './bitcoin-transaction-value'; -interface BitcoinTransactionItemProps extends BoxProps { +interface BitcoinTransactionItemProps { transaction: BitcoinTx; } -export function BitcoinTransactionItem({ transaction, ...rest }: BitcoinTransactionItemProps) { - const [component, bind, { isHovered }] = usePressable(true); +export function BitcoinTransactionItem({ transaction }: BitcoinTransactionItemProps) { const { pathname } = useLocation(); const navigate = useNavigate(); @@ -92,7 +88,6 @@ export function BitcoinTransactionItem({ transaction, ...rest }: BitcoinTransact const increaseFeeButton = ( @@ -101,6 +96,7 @@ export function BitcoinTransactionItem({ transaction, ...rest }: BitcoinTransact return ( } txTitle={} txValue={txValue} - belowCaptionEl={increaseFeeButton} - {...bind} - {...rest} - > - {component} - + /> ); } diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx index 5bd77800863..80e06aa8328 100644 --- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx +++ b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx @@ -3,11 +3,13 @@ import { useNavigate } from 'react-router-dom'; import { RouteUrls } from '@shared/route-urls'; import { noop } from '@shared/utils'; -import { Brc20TokenAssetItem } from '@app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item'; import { useNativeSegwitBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; import { Brc20Token } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.query'; import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { Brc20TokenAssetItemLayout } from './components/brc20-token-asset-item.layout'; +import { Brc20AssetListLayout } from './components/brc20-token-asset-list.layout'; + export function Brc20TokenAssetList(props: { brc20Tokens?: Brc20Token[] }) { const navigate = useNavigate(); const currentAccountBtcAddress = useCurrentAccountNativeSegwitAddressIndexZero(); @@ -24,13 +26,17 @@ export function Brc20TokenAssetList(props: { brc20Tokens?: Brc20Token[] }) { } if (!props.brc20Tokens?.length) return null; - return props.brc20Tokens.map(token => ( - navigateToBrc20SendForm(token) : noop} - displayNotEnoughBalance={!hasPositiveBtcBalanceForFees} - key={token.tick} - /> - )); + + return ( + + {props.brc20Tokens?.map(token => ( + navigateToBrc20SendForm(token) : noop} + /> + ))} + + ); } diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.layout.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.layout.tsx index 027deed6f74..6232995762b 100644 --- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.layout.tsx +++ b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.layout.tsx @@ -1,69 +1,52 @@ -import { BoxProps, Flex, HStack, styled } from 'leather-styles/jsx'; +import { styled } from 'leather-styles/jsx'; -import type { Money } from '@shared/models/money.model'; +import { createMoney } from '@shared/models/money.model'; import { formatBalance } from '@app/common/format-balance'; -import { AssetCaption } from '@app/components/crypto-assets/components/asset-caption'; -import { usePressable } from '@app/components/item-hover'; -import { Flag } from '@app/ui/components/flag/flag'; +import { Brc20Token } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.query'; import { Brc20TokenIcon } from '@app/ui/components/icons/brc20-token-icon'; +import { ItemInteractive } from '@app/ui/components/item/item-interactive'; +import { ItemLayout } from '@app/ui/components/item/item.layout'; import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; -interface Brc20TokenAssetItemLayoutProps extends BoxProps { - balance: Money; - caption: string; - isPressable?: boolean; +interface Brc20TokenAssetItemLayoutProps { + token: Brc20Token; onClick?(): void; - title: string; displayNotEnoughBalance?: boolean; } export function Brc20TokenAssetItemLayout({ - balance, - caption, - isPressable, onClick, - title, displayNotEnoughBalance, + token, }: Brc20TokenAssetItemLayoutProps) { - const [component, bind] = usePressable(isPressable); - + const balance = createMoney(Number(token.overall_balance), token.tick, 0); const formattedBalance = formatBalance(balance.amount.toString()); return ( - - } spacing="space.04" width="100%"> - - - {title} - + + } + titleLeft={token.tick} + captionLeft="BRC-20" + titleRight={ - + {formattedBalance.value} - - - - - {component} - - + } + /> + ); } diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.tsx deleted file mode 100644 index 29820b11099..00000000000 --- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { createMoney } from '@shared/models/money.model'; - -import { Brc20Token } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.query'; - -import { Brc20TokenAssetItemLayout } from './brc20-token-asset-item.layout'; - -interface Brc20TokenAssetItemProps { - token: Brc20Token; - isPressable?: boolean; - onClick?(): void; - displayNotEnoughBalance?: boolean; -} -export function Brc20TokenAssetItem({ - token, - isPressable, - onClick, - displayNotEnoughBalance, -}: Brc20TokenAssetItemProps) { - return ( - - ); -} diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-list.layout.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-list.layout.tsx new file mode 100644 index 00000000000..41ea4db9c38 --- /dev/null +++ b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-list.layout.tsx @@ -0,0 +1,10 @@ +import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; +import { Stack, StackProps } from 'leather-styles/jsx'; + +export function Brc20AssetListLayout({ children }: StackProps) { + return ( + + {children} + + ); +} diff --git a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx index e98f899f166..4e790a4e35f 100644 --- a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx +++ b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx @@ -1,7 +1,6 @@ import type { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-asset-balance.model'; -import { CryptoCurrencyAssetItem } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item'; - +import { CryptoCurrencyAssetItemLayout } from '../crypto-currency-asset/crypto-currency-asset-item.layout'; import { CryptoCurrencyAssetIcon } from './crypto-currency-asset-icon'; import { FungibleTokenAssetItem } from './fungible-token-asset-item'; @@ -16,10 +15,9 @@ export function CryptoAssetListItem(props: CryptoAssetListItemProps) { switch (type) { case 'crypto-currency': return ( - } - isPressable onClick={onClick} /> ); diff --git a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.layout.tsx b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.layout.tsx index e1803d73421..c5c9418347b 100644 --- a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.layout.tsx +++ b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.layout.tsx @@ -3,12 +3,7 @@ import { Stack, StackProps } from 'leather-styles/jsx'; export function CryptoAssetListLayout({ children }: StackProps) { return ( - + {children} ); diff --git a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx index fd1fb2f0e0f..0605e67c3bf 100644 --- a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx +++ b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx @@ -8,7 +8,7 @@ import { Brc20TokensLoader } from '@app/components/brc20-tokens-loader'; import { Brc20TokenAssetList } from '@app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list'; import { BtcIcon } from '@app/ui/components/icons/btc-icon'; -import { CryptoCurrencyAssetItem } from '../crypto-currency-asset/crypto-currency-asset-item'; +import { CryptoCurrencyAssetItemLayout } from '../crypto-currency-asset/crypto-currency-asset-item.layout'; import { CryptoAssetListItem } from './crypto-asset-list-item'; import { CryptoAssetListLayout } from './crypto-asset-list.layout'; @@ -25,11 +25,10 @@ export function CryptoAssetList({ cryptoAssetBalances, onItemClick }: CryptoAsse {signer => ( {balance => ( - } onClick={() => onItemClick(balance)} - isPressable /> )} diff --git a/src/app/components/crypto-assets/choose-crypto-asset/fungible-token-asset-item.tsx b/src/app/components/crypto-assets/choose-crypto-asset/fungible-token-asset-item.tsx index 0ce6b98c4ff..7be8034c33e 100644 --- a/src/app/components/crypto-assets/choose-crypto-asset/fungible-token-asset-item.tsx +++ b/src/app/components/crypto-assets/choose-crypto-asset/fungible-token-asset-item.tsx @@ -2,7 +2,7 @@ import { FlexProps } from 'leather-styles/jsx'; import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; -import { StacksFungibleTokenAssetItem } from '@app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item'; +import { StacksFungibleTokenAssetItemLayout } from '../stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout'; interface FungibleTokenAssetItemProps extends FlexProps { assetBalance: StacksFungibleTokenAssetBalance; @@ -13,9 +13,7 @@ export function FungibleTokenAssetItem({ assetBalance, onClick }: FungibleTokenA switch (blockchain) { case 'stacks': - return ( - - ); + return ; default: return null; } diff --git a/src/app/components/crypto-assets/components/asset-caption.tsx b/src/app/components/crypto-assets/components/asset-caption.tsx deleted file mode 100644 index 36dd07d71d4..00000000000 --- a/src/app/components/crypto-assets/components/asset-caption.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Flex, styled } from 'leather-styles/jsx'; - -interface AssetCaptionProps { - caption: string; -} -export function AssetCaption({ caption }: AssetCaptionProps) { - return ( - - {caption}{' '} - - ); -} diff --git a/src/app/components/crypto-assets/components/asset-row-grid.tsx b/src/app/components/crypto-assets/components/asset-row-grid.tsx deleted file mode 100644 index 3508934ab32..00000000000 --- a/src/app/components/crypto-assets/components/asset-row-grid.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Grid, GridItem } from 'leather-styles/jsx'; - -interface AssetRowGridProps { - title: React.ReactNode; - balance: React.ReactNode; - caption: React.ReactNode; - usdBalance?: React.ReactNode; - rightElement?: React.ReactNode; -} -export function AssetRowGrid({ - title, - balance, - caption, - usdBalance, - rightElement, -}: AssetRowGridProps) { - const balanceItem = rightElement ? ( - - {rightElement} - - ) : ( - {balance} - ); - return ( - - - {title} - - {balanceItem} - {caption} - {usdBalance && {usdBalance}} - - ); -} diff --git a/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout.tsx b/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout.tsx index 5dc6cfadb58..0efb73b7727 100644 --- a/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout.tsx +++ b/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout.tsx @@ -1,98 +1,69 @@ -import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; -import { Flex, styled } from 'leather-styles/jsx'; +import { ReactNode } from 'react'; -import { CryptoCurrencies } from '@shared/models/currencies.model'; -import { Money } from '@shared/models/money.model'; +import { styled } from 'leather-styles/jsx'; -import { formatBalance } from '@app/common/format-balance'; -import { ftDecimals } from '@app/common/stacks-utils'; -import { usePressable } from '@app/components/item-hover'; -import { Flag } from '@app/ui/components/flag/flag'; +import { AllCryptoCurrencyAssetBalances } from '@shared/models/crypto-asset-balance.model'; + +import { ItemInteractive } from '@app/ui/components/item/item-interactive'; +import { ItemLayout } from '@app/ui/components/item/item.layout'; import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; -import { truncateMiddle } from '@app/ui/utils/truncate-middle'; +import { Caption } from '@app/ui/components/typography/caption'; -import { AssetRowGrid } from '../components/asset-row-grid'; +import { parseCryptoCurrencyAssetBalance } from './crypto-currency-asset.utils'; interface CryptoCurrencyAssetItemLayoutProps { - balance: Money; - caption: string; - icon: React.ReactNode; - copyIcon?: React.ReactNode; - isPressable?: boolean; - title: string; - usdBalance?: string; + additionalBalanceInfo?: ReactNode; + additionalUsdBalanceInfo?: ReactNode; address?: string; - isHovered?: boolean; - currency?: CryptoCurrencies; - additionalBalanceInfo?: React.ReactNode; - additionalUsdBalanceInfo?: React.ReactNode; - rightElement?: React.ReactNode; + assetBalance: AllCryptoCurrencyAssetBalances; + icon: React.ReactNode; onClick?(): void; + rightElement?: React.ReactNode; + usdBalance?: string; } export function CryptoCurrencyAssetItemLayout({ - balance, - caption, - icon, - copyIcon, - isPressable, - title, - usdBalance, - address = '', - isHovered = false, additionalBalanceInfo, additionalUsdBalanceInfo, - rightElement, + address = '', + assetBalance, + icon, onClick, + rightElement, + usdBalance, }: CryptoCurrencyAssetItemLayoutProps) { - const [component, bind] = usePressable(isPressable); - - const amount = balance.decimals - ? ftDecimals(balance.amount, balance.decimals) - : balance.amount.toString(); - const dataTestId = CryptoAssetSelectors.CryptoAssetListItem.replace( - '{symbol}', - balance.symbol.toLowerCase() - ); - const formattedBalance = formatBalance(amount); + const { balance, dataTestId, formattedBalance, title } = + parseCryptoCurrencyAssetBalance(assetBalance); return ( - - - - {isHovered ? truncateMiddle(address, 6) : title} - - } - balance={ + + - + {formattedBalance.value} {additionalBalanceInfo} - } - caption={ - - {caption} - - } - usdBalance={ - - {balance.amount.toNumber() > 0 && address ? ( - - {usdBalance} - - ) : null} + ) + } + captionRight={ + !rightElement && ( + <> + {balance.amount.toNumber() > 0 && address ? usdBalance : null} {additionalUsdBalanceInfo} - - } - rightElement={rightElement} - /> - - {component} - + + ) + } + /> + ); } diff --git a/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.tsx b/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.tsx deleted file mode 100644 index bddbae243ac..00000000000 --- a/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import type { AllCryptoCurrencyAssetBalances } from '@shared/models/crypto-asset-balance.model'; - -import { spamFilter } from '@app/common/utils/spam-filter'; - -import { CryptoCurrencyAssetItemLayout } from './crypto-currency-asset-item.layout'; - -interface CryptoCurrencyAssetItemProps { - assetBalance: AllCryptoCurrencyAssetBalances; - icon: React.ReactNode; - usdBalance?: string; - address?: string; - isPressable?: boolean; - additionalBalanceInfo?: React.ReactNode; - additionalUsdBalanceInfo?: React.ReactNode; - rightElement?: React.ReactNode; - onClick?(): void; -} -export function CryptoCurrencyAssetItem({ - additionalBalanceInfo, - additionalUsdBalanceInfo, - address, - assetBalance, - icon, - isPressable, - onClick, - rightElement, - usdBalance, -}: CryptoCurrencyAssetItemProps) { - const { balance, asset } = assetBalance; - - return ( - - ); -} diff --git a/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset.utils.ts b/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset.utils.ts new file mode 100644 index 00000000000..041ca288ea5 --- /dev/null +++ b/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset.utils.ts @@ -0,0 +1,28 @@ +import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; + +import { AllCryptoCurrencyAssetBalances } from '@shared/models/crypto-asset-balance.model'; + +import { formatBalance } from '@app/common/format-balance'; +import { ftDecimals } from '@app/common/stacks-utils'; +import { spamFilter } from '@app/common/utils/spam-filter'; + +export function parseCryptoCurrencyAssetBalance(assetBalance: AllCryptoCurrencyAssetBalances) { + const { asset, balance } = assetBalance; + + const amount = balance.decimals + ? ftDecimals(balance.amount, balance.decimals) + : balance.amount.toString(); + const dataTestId = CryptoAssetSelectors.CryptoAssetListItem.replace( + '{symbol}', + balance.symbol.toLowerCase() + ); + const formattedBalance = formatBalance(amount); + const title = spamFilter(asset.name); + + return { + balance, + dataTestId, + formattedBalance, + title, + }; +} diff --git a/src/app/components/crypto-assets/stacks/components/stacks-asset-avatar.tsx b/src/app/components/crypto-assets/stacks/components/stacks-asset-avatar.tsx index f731aea9a1f..08d771dd2d1 100644 --- a/src/app/components/crypto-assets/stacks/components/stacks-asset-avatar.tsx +++ b/src/app/components/crypto-assets/stacks/components/stacks-asset-avatar.tsx @@ -15,7 +15,7 @@ export function StacksAssetAvatar({ gradientString, imageCanonicalUri, isStx, - size = '36', + size = '40', ...props }: StacksAssetAvatarProps) { if (isStx) return ; diff --git a/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.tsx b/src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts similarity index 58% rename from src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.tsx rename to src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts index 62c76e02d1c..06336273364 100644 --- a/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.tsx +++ b/src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts @@ -1,46 +1,41 @@ import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; -import { FlexProps } from 'leather-styles/jsx'; import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; import { getImageCanonicalUri } from '@app/common/crypto-assets/stacks-crypto-asset.utils'; +import { formatBalance } from '@app/common/format-balance'; +import { ftDecimals } from '@app/common/stacks-utils'; import { formatContractId, getTicker } from '@app/common/utils'; import { spamFilter } from '@app/common/utils/spam-filter'; import { getAssetName } from '@app/ui/utils/get-asset-name'; -import { StacksFungibleTokenAssetItemLayout } from './stacks-fungible-token-asset-item.layout'; - -interface StacksFungibleTokenAssetItemProps extends FlexProps { - assetBalance: StacksFungibleTokenAssetBalance; - isPressable?: boolean; - onClick?(): void; -} -export function StacksFungibleTokenAssetItem({ - assetBalance, - isPressable, - onClick, -}: StacksFungibleTokenAssetItemProps) { +export function parseStacksFungibleTokenAssetBalance( + assetBalance: StacksFungibleTokenAssetBalance +) { const { asset, balance } = assetBalance; const { contractAddress, contractAssetName, contractName, name, symbol } = asset; + const amount = balance.decimals + ? ftDecimals(balance.amount, balance.decimals || 0) + : balance.amount.toString(); const avatar = `${formatContractId(contractAddress, contractName)}::${contractAssetName}`; const dataTestId = symbol && CryptoAssetSelectors.CryptoAssetListItem.replace('{symbol}', symbol.toLowerCase()); + const formattedBalance = formatBalance(amount); const friendlyName = name || (contractAssetName.includes('::') ? getAssetName(contractAssetName) : contractAssetName); const imageCanonicalUri = getImageCanonicalUri(asset.imageCanonicalUri, asset.name); const caption = symbol || getTicker(friendlyName); - return ( - - ); + const title = spamFilter(friendlyName); + + return { + amount, + avatar, + caption, + dataTestId, + formattedBalance, + imageCanonicalUri, + title, + }; } diff --git a/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx b/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx index d476b4db70c..79b0f6fc747 100644 --- a/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx +++ b/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx @@ -1,46 +1,29 @@ -import { Flex, FlexProps, styled } from 'leather-styles/jsx'; +import { styled } from 'leather-styles/jsx'; -import type { Money } from '@shared/models/money.model'; +import { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; -import { formatBalance } from '@app/common/format-balance'; -import { ftDecimals } from '@app/common/stacks-utils'; import { StacksAssetAvatar } from '@app/components/crypto-assets/stacks/components/stacks-asset-avatar'; -import { usePressable } from '@app/components/item-hover'; -import { Flag } from '@app/ui/components/flag/flag'; +import { ItemInteractive } from '@app/ui/components/item/item-interactive'; +import { ItemLayout } from '@app/ui/components/item/item.layout'; import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; -import { AssetCaption } from '../../components/asset-caption'; -import { AssetRowGrid } from '../../components/asset-row-grid'; +import { parseStacksFungibleTokenAssetBalance } from './fungible-token-asset.utils'; -interface StacksFungibleTokenAssetItemLayoutProps extends FlexProps { - avatar: string; - balance: Money; - caption: string; - imageCanonicalUri?: string; - isPressable?: boolean; - title: string; +interface StacksFungibleTokenAssetItemLayoutProps { + assetBalance: StacksFungibleTokenAssetBalance; onClick?(): void; } export function StacksFungibleTokenAssetItemLayout({ - avatar, - balance, - caption, - imageCanonicalUri, - isPressable, - title, + assetBalance, onClick, }: StacksFungibleTokenAssetItemLayoutProps) { - const [component, bind] = usePressable(isPressable); - - const amount = balance.decimals - ? ftDecimals(balance.amount, balance.decimals || 0) - : balance.amount.toString(); - const formattedBalance = formatBalance(amount); + const { amount, avatar, caption, dataTestId, formattedBalance, imageCanonicalUri, title } = + parseStacksFungibleTokenAssetBalance(assetBalance); return ( - - + } - spacing="space.04" - width="100%" - > - {title}} - balance={ - - - {formattedBalance.value} - - - } - caption={} - /> - {component} - - + titleLeft={title} + captionLeft={caption} + titleRight={ + + + {formattedBalance.value} + + + } + /> + ); } diff --git a/src/app/components/stacks-transaction-item/increase-fee-button.tsx b/src/app/components/stacks-transaction-item/increase-fee-button.tsx index 7cb383c8fef..cf924f4e988 100644 --- a/src/app/components/stacks-transaction-item/increase-fee-button.tsx +++ b/src/app/components/stacks-transaction-item/increase-fee-button.tsx @@ -4,13 +4,12 @@ import { ChevronsRightIcon } from '@app/ui/components/icons/chevrons-right-icon' interface IncreaseFeeButtonProps { isEnabled?: boolean; - isHovered: boolean; isSelected: boolean; onIncreaseFee(): void; } export function IncreaseFeeButton(props: IncreaseFeeButtonProps) { - const { isEnabled, isHovered, isSelected, onIncreaseFee } = props; - const isActive = isEnabled && isHovered && !isSelected; + const { isEnabled, isSelected, onIncreaseFee } = props; + const isActive = isEnabled && !isSelected; return ( @@ -99,16 +95,12 @@ export function StacksTransactionItem({ return ( } txValue={txValue} - belowCaptionEl={increaseFeeButton} - {...bind} - {...rest} - > - {component} - + /> ); } diff --git a/src/app/components/transaction-item/transaction-item.layout.tsx b/src/app/components/transaction-item/transaction-item.layout.tsx index bb961b93108..e778cf9591d 100644 --- a/src/app/components/transaction-item/transaction-item.layout.tsx +++ b/src/app/components/transaction-item/transaction-item.layout.tsx @@ -1,49 +1,53 @@ import { ReactNode } from 'react'; -import { Box, Flex, HStack } from 'leather-styles/jsx'; +import { styled } from 'leather-styles/jsx'; + +import { ItemInteractive } from '@app/ui/components/item/item-interactive'; +import { ItemLayout } from '@app/ui/components/item/item.layout'; interface TransactionItemLayoutProps { openTxLink(): void; + rightElement?: ReactNode; txCaption: ReactNode; txTitle: ReactNode; txValue: ReactNode; txIcon?: ReactNode; txStatus?: ReactNode; - belowCaptionEl?: ReactNode; children?: ReactNode; } export function TransactionItemLayout({ openTxLink, + rightElement, txCaption, txIcon, txStatus, txTitle, txValue, - belowCaptionEl, - children, - ...rest }: TransactionItemLayoutProps) { return ( - - - {txIcon && txIcon} - - - {txTitle} {txValue} - - - {txCaption} {txStatus && txStatus} - {belowCaptionEl ? belowCaptionEl : null} - - - - {children} - + // TODO: Revisit if needed styles position="relative" zIndex={99} + + + + {txCaption} + + {txStatus && txStatus} + + } + titleRight={ + rightElement ? ( + rightElement + ) : ( + + {txValue} + + ) + } + /> + ); } diff --git a/src/app/features/activity-list/components/pending-transaction-list/pending-transaction-list.layout.tsx b/src/app/features/activity-list/components/pending-transaction-list/pending-transaction-list.layout.tsx index 3d2fb787563..44acfa0e297 100644 --- a/src/app/features/activity-list/components/pending-transaction-list/pending-transaction-list.layout.tsx +++ b/src/app/features/activity-list/components/pending-transaction-list/pending-transaction-list.layout.tsx @@ -11,7 +11,7 @@ export function PendingTransactionListLayout({ children }: PendingTransactionLis Pending - + {children} diff --git a/src/app/features/activity-list/components/pending-transaction-list/pending-transaction-list.tsx b/src/app/features/activity-list/components/pending-transaction-list/pending-transaction-list.tsx index 6fa22d355a1..2fe58970c23 100644 --- a/src/app/features/activity-list/components/pending-transaction-list/pending-transaction-list.tsx +++ b/src/app/features/activity-list/components/pending-transaction-list/pending-transaction-list.tsx @@ -2,9 +2,9 @@ import { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; import { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model'; +import { BitcoinTransactionItem } from '@app/components/bitcoin-transaction-item/bitcoin-transaction-item'; import { StacksTransactionItem } from '@app/components/stacks-transaction-item/stacks-transaction-item'; -import { BitcoinTransactionItem } from '../../../../components/bitcoin-transaction-item/bitcoin-transaction-item'; import { PendingTransactionListLayout } from './pending-transaction-list.layout'; interface PendingTransactionListProps { diff --git a/src/app/features/activity-list/components/transaction-list/transaction-list.layout.tsx b/src/app/features/activity-list/components/transaction-list/transaction-list.layout.tsx index eed16bebc44..7d3f7c2e50a 100644 --- a/src/app/features/activity-list/components/transaction-list/transaction-list.layout.tsx +++ b/src/app/features/activity-list/components/transaction-list/transaction-list.layout.tsx @@ -6,9 +6,5 @@ interface TransactionListLayoutProps { children: ReactNode; } export function TransactionListLayout({ children }: TransactionListLayoutProps) { - return ( - - {children} - - ); + return {children}; } diff --git a/src/app/features/activity-list/components/transaction-list/transactions-by-date.layout.tsx b/src/app/features/activity-list/components/transaction-list/transactions-by-date.layout.tsx index 117d795d447..f9b3bde1d8d 100644 --- a/src/app/features/activity-list/components/transaction-list/transactions-by-date.layout.tsx +++ b/src/app/features/activity-list/components/transaction-list/transactions-by-date.layout.tsx @@ -17,9 +17,7 @@ export function TransactionsByDateLayout({ {displayDate} - - {children} - + {children} ); } diff --git a/src/app/features/asset-list/asset-list.tsx b/src/app/features/asset-list/asset-list.tsx index d6703e2c534..096f86f20b0 100644 --- a/src/app/features/asset-list/asset-list.tsx +++ b/src/app/features/asset-list/asset-list.tsx @@ -7,7 +7,7 @@ import { useBtcAssetBalance } from '@app/common/hooks/balance/btc/use-btc-balanc import { useWalletType } from '@app/common/use-wallet-type'; import { BitcoinContractEntryPoint } from '@app/components/bitcoin-contract-entry-point/bitcoin-contract-entry-point'; import { Brc20TokensLoader } from '@app/components/brc20-tokens-loader'; -import { CryptoCurrencyAssetItem } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item'; +import { CryptoCurrencyAssetItemLayout } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout'; import { CurrentStacksAccountLoader } from '@app/components/loaders/stacks-account-loader'; import { useHasBitcoinLedgerKeychain } from '@app/store/accounts/blockchain/bitcoin/bitcoin.ledger'; import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; @@ -31,10 +31,10 @@ export function AssetsList() { const { whenWallet } = useWalletType(); return ( - + {whenWallet({ software: ( - } @@ -42,7 +42,7 @@ export function AssetsList() { /> ), ledger: ( - } diff --git a/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx b/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx index a09c669a03f..ff0f0ed281a 100644 --- a/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx +++ b/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js'; -import { CryptoCurrencyAssetItem } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item'; +import { CryptoCurrencyAssetItemLayout } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout'; import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar'; import { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils'; @@ -8,7 +8,7 @@ import { ConnectLedgerAssetBtn } from './connect-ledger-asset-button'; export function AddStacksLedgerKeysItem() { return ( - } rightElement={} diff --git a/src/app/features/asset-list/components/bitcoin-fungible-tokens-asset-list.tsx b/src/app/features/asset-list/components/bitcoin-fungible-tokens-asset-list.tsx index c8ce70ef4b9..e71915b3ee3 100644 --- a/src/app/features/asset-list/components/bitcoin-fungible-tokens-asset-list.tsx +++ b/src/app/features/asset-list/components/bitcoin-fungible-tokens-asset-list.tsx @@ -1,6 +1,6 @@ import { Stack } from 'leather-styles/jsx'; -import { Brc20TokenAssetItem } from '@app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item'; +import { Brc20TokenAssetItemLayout } from '@app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.layout'; import { Brc20Token } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.query'; interface BitcoinFungibleTokenAssetListProps { @@ -12,7 +12,7 @@ export function BitcoinFungibleTokenAssetList({ brc20Tokens }: BitcoinFungibleTo return ( {brc20Tokens.map(token => ( - + ))} ); diff --git a/src/app/features/asset-list/components/stacks-asset-list.tsx b/src/app/features/asset-list/components/stacks-asset-list.tsx index 13019d672e4..a58c3a82834 100644 --- a/src/app/features/asset-list/components/stacks-asset-list.tsx +++ b/src/app/features/asset-list/components/stacks-asset-list.tsx @@ -2,7 +2,7 @@ import { styled } from 'leather-styles/jsx'; import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance'; import { ftDecimals } from '@app/common/stacks-utils'; -import { CryptoCurrencyAssetItem } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item'; +import { CryptoCurrencyAssetItemLayout } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout'; import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar'; import { useStacksFungibleTokenAssetBalancesWithMetadata } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models'; @@ -36,7 +36,7 @@ export function StacksAssetList({ account }: StacksAssetListProps) { return ( <> - + {assetBalances.map(assetBalance => ( - diff --git a/src/app/features/collectibles/components/collectibes.layout.tsx b/src/app/features/collectibles/components/collectibes.layout.tsx index ccdae07f19e..091fa5196c5 100644 --- a/src/app/features/collectibles/components/collectibes.layout.tsx +++ b/src/app/features/collectibles/components/collectibes.layout.tsx @@ -23,7 +23,7 @@ export function CollectiblesLayout({ }: CollectiblesLayoutProps) { return ( <> - + {title} {isLoading ? ( diff --git a/src/app/features/increase-fee-drawer/components/increase-btc-fee-form.tsx b/src/app/features/increase-fee-drawer/components/increase-btc-fee-form.tsx index c9520a69f7b..bb6bff62f2a 100644 --- a/src/app/features/increase-fee-drawer/components/increase-btc-fee-form.tsx +++ b/src/app/features/increase-fee-drawer/components/increase-btc-fee-form.tsx @@ -50,7 +50,7 @@ export function IncreaseBtcFeeForm({ btcTx }: IncreaseBtcFeeFormProps) { validationSchema={validationSchema} > - {btcTx && } + {btcTx && } diff --git a/src/app/features/increase-fee-drawer/components/increase-stx-fee-form.tsx b/src/app/features/increase-fee-drawer/components/increase-stx-fee-form.tsx index bd9a3ce5512..3d010093475 100644 --- a/src/app/features/increase-fee-drawer/components/increase-stx-fee-form.tsx +++ b/src/app/features/increase-fee-drawer/components/increase-stx-fee-form.tsx @@ -74,7 +74,7 @@ export function IncreaseStxFeeForm() { > {props => ( - {tx && } + {tx && } {balances?.stx.unlockedStx.amount && ( diff --git a/src/app/features/leather-intro-dialog/leather-intro-steps.tsx b/src/app/features/leather-intro-dialog/leather-intro-steps.tsx index 29b62738bfc..48d8f80131a 100644 --- a/src/app/features/leather-intro-dialog/leather-intro-steps.tsx +++ b/src/app/features/leather-intro-dialog/leather-intro-steps.tsx @@ -16,7 +16,7 @@ export function LeatherIntroDialog({ children }: HasChildren) { return ( e.preventDefault()} onInteractOutside={e => e.preventDefault()} className={css({ maxWidth: '500px', backgroundColor: 'accent.background-primary' })} diff --git a/src/app/features/switch-account-drawer/components/switch-account-list-item.tsx b/src/app/features/switch-account-drawer/components/switch-account-list-item.tsx index 761823e7bc1..384fb50567c 100644 --- a/src/app/features/switch-account-drawer/components/switch-account-list-item.tsx +++ b/src/app/features/switch-account-drawer/components/switch-account-list-item.tsx @@ -4,13 +4,13 @@ import { useAccountDisplayName } from '@app/common/hooks/account/use-account-nam import { useSwitchAccount } from '@app/common/hooks/account/use-switch-account'; import { useLoading } from '@app/common/hooks/use-loading'; import { AccountTotalBalance } from '@app/components/account-total-balance'; -import { AccountListItemLayout } from '@app/components/account/account-list-item-layout'; -import { usePressable } from '@app/components/item-hover'; +import { AcccountAddresses } from '@app/components/account/account-addresses'; +import { AccountListItemLayout } from '@app/components/account/account-list-item.layout'; +import { AccountNameLayout } from '@app/components/account/account-name'; import { useNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { AccountAvatarItem } from '../../../components/account/account-avatar-item'; -import { AccountNameLayout } from '../../../components/account/account-name'; interface SwitchAccountListItemProps { handleClose(): void; @@ -20,16 +20,15 @@ interface SwitchAccountListItemProps { export const SwitchAccountListItem = memo( ({ handleClose, currentAccountIndex, index }: SwitchAccountListItemProps) => { const stacksAccounts = useStacksAccounts(); - const stacksAddress = stacksAccounts[index]?.address || ''; + const stxAddress = stacksAccounts[index]?.address || ''; const bitcoinSigner = useNativeSegwitSigner(index); - const bitcoinAddress = bitcoinSigner?.(0).address || ''; + const btcAddress = bitcoinSigner?.(0).address || ''; const { isLoading, setIsLoading, setIsIdle } = useLoading( - 'SWITCH_ACCOUNTS' + stacksAddress || bitcoinAddress + 'SWITCH_ACCOUNTS' + stxAddress || btcAddress ); const { handleSwitchAccount } = useSwitchAccount(handleClose); - const [component, bind] = usePressable(true); - const name = useAccountDisplayName({ address: stacksAddress, index }); + const name = useAccountDisplayName({ address: stxAddress, index }); const handleClick = async () => { setIsLoading(); @@ -41,9 +40,8 @@ export const SwitchAccountListItem = memo( return ( } + accountName={{name}} avatar={ } + balanceLabel={} + index={index} + isLoading={isLoading} + isSelected={currentAccountIndex === index} onSelectAccount={handleClick} - accountName={{name}} - balanceLabel={ - - } - mt="space.05" - {...bind} - > - {component} - + /> ); } ); diff --git a/src/app/features/switch-account-drawer/components/switch-account-list.tsx b/src/app/features/switch-account-drawer/components/switch-account-list.tsx index 809dccd3804..0cafce7570f 100644 --- a/src/app/features/switch-account-drawer/components/switch-account-list.tsx +++ b/src/app/features/switch-account-drawer/components/switch-account-list.tsx @@ -23,7 +23,7 @@ export const SwitchAccountList = memo( style={{ paddingTop: '24px', height: '70vh' }} totalCount={addressesNum} itemContent={index => ( - + { - const [component, bind] = usePressable(true); const name = useAccountDisplayName(account); const btcAddress = useNativeSegwitAccountIndexAddressIndexZero(account.index); const accountSlug = useMemo(() => slugify(`Account ${account?.index + 1}`), [account?.index]); return ( - // Padding required on outer element to prevent jumpy list behaviours in - // virtualised list library + // Padding required on outer element to prevent jumpy virtualized list } accountName={ }> {name} @@ -65,13 +63,12 @@ const ChooseAccountItem = memo( balanceLabel={ } + data-testid={`account-${accountSlug}-${account.index}`} + index={account.index} isLoading={isLoading} + isSelected={false} onSelectAccount={() => onSelectAccount(account.index)} - data-testid={`account-${accountSlug}-${account.index}`} - {...bind} - > - {component} - + /> ); } diff --git a/src/app/pages/receive/components/receive-item.tsx b/src/app/pages/receive/components/receive-item.tsx index 043c75214a7..d4b79796505 100644 --- a/src/app/pages/receive/components/receive-item.tsx +++ b/src/app/pages/receive/components/receive-item.tsx @@ -1,9 +1,8 @@ -import { Flex, HStack, Stack, styled } from 'leather-styles/jsx'; - import { Button } from '@app/ui/components/button/button'; -import { Flag } from '@app/ui/components/flag/flag'; import { CopyIcon } from '@app/ui/components/icons/copy-icon'; import { QrCodeIcon } from '@app/ui/components/icons/qr-code-icon'; +import { ItemInteractive } from '@app/ui/components/item/item-interactive'; +import { ItemWithButtonsLayout } from '@app/ui/components/item/item-with-buttons.layout'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; interface ReceiveItemProps { @@ -24,23 +23,29 @@ export function ReceiveItem({ }: ReceiveItemProps) { if (!address) return null; return ( - - - - {title} - {truncateMiddle(address, 6)} - - - - {onClickQrCode && ( - - )} - - - + {onClickQrCode && ( + + )} + + } + /> + ); } diff --git a/src/app/pages/receive/components/receive-items.tsx b/src/app/pages/receive/components/receive-items.tsx index d1c2d674ae9..4f89ecee966 100644 --- a/src/app/pages/receive/components/receive-items.tsx +++ b/src/app/pages/receive/components/receive-items.tsx @@ -13,7 +13,7 @@ export function ReceiveItemList({ children, title }: ReceiveItemListProps) { )} - + {children} diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-accounts-drawer/account-list-item.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-accounts-drawer/account-list-item.tsx index 971e0aa5bb5..d59114ed9d0 100644 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-accounts-drawer/account-list-item.tsx +++ b/src/app/pages/send/send-crypto-asset-form/components/recipient-accounts-drawer/account-list-item.tsx @@ -6,10 +6,10 @@ import { BitcoinSendFormValues, StacksSendFormValues } from '@shared/models/form import { useAccountDisplayName } from '@app/common/hooks/account/use-account-names'; import { AccountTotalBalance } from '@app/components/account-total-balance'; +import { AcccountAddresses } from '@app/components/account/account-addresses'; import { AccountAvatarItem } from '@app/components/account/account-avatar-item'; -import { AccountListItemLayout } from '@app/components/account/account-list-item-layout'; +import { AccountListItemLayout } from '@app/components/account/account-list-item.layout'; import { AccountNameLayout } from '@app/components/account/account-name'; -import { usePressable } from '@app/components/item-hover'; import { useNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models'; @@ -22,7 +22,6 @@ export const AccountListItem = memo(({ index, stacksAccount, onClose }: AccountL const { setFieldValue, values, setFieldTouched } = useFormikContext< BitcoinSendFormValues | StacksSendFormValues >(); - const [component, bind] = usePressable(true); const stacksAddress = stacksAccount?.address || ''; const name = useAccountDisplayName({ address: stacksAddress, index }); @@ -38,6 +37,7 @@ export const AccountListItem = memo(({ index, stacksAccount, onClose }: AccountL return ( } accountName={{name}} avatar={ } index={index} - isActive={false} + isSelected={false} isLoading={false} - mt="space.05" onSelectAccount={onSelectAccount} - {...bind} - > - {component} - + /> ); }); diff --git a/src/app/pages/swap/swap-choose-asset/components/swap-asset-item.layout.tsx b/src/app/pages/swap/swap-choose-asset/components/swap-asset-item.layout.tsx deleted file mode 100644 index 075868fc2d8..00000000000 --- a/src/app/pages/swap/swap-choose-asset/components/swap-asset-item.layout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { Stack } from 'leather-styles/jsx'; - -import { HasChildren } from '@app/common/has-children'; -import { Flag } from '@app/ui/components/flag/flag'; - -export function SwapAssetItemLayout({ - children, - icon, - ...rest -}: HasChildren & { icon: React.JSX.Element }) { - return ( - - {children} - - ); -} diff --git a/src/app/pages/swap/swap-choose-asset/components/swap-asset-item.tsx b/src/app/pages/swap/swap-choose-asset/components/swap-asset-item.tsx index 06b608c7194..f0f9e632948 100644 --- a/src/app/pages/swap/swap-choose-asset/components/swap-asset-item.tsx +++ b/src/app/pages/swap/swap-choose-asset/components/swap-asset-item.tsx @@ -1,19 +1,20 @@ -import { HStack, styled } from 'leather-styles/jsx'; +import { SwapSelectors } from '@tests/selectors/swap.selectors'; +import { styled } from 'leather-styles/jsx'; import { formatMoneyWithoutSymbol } from '@app/common/money/format-money'; -import { usePressable } from '@app/components/item-hover'; import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query'; import { isFtAsset } from '@app/query/stacks/tokens/token-metadata.utils'; +import { ItemInteractive } from '@app/ui/components/item/item-interactive'; +import { ItemLayout } from '@app/ui/components/item/item.layout'; import { useAlexSdkBalanceAsFiat } from '../../hooks/use-alex-sdk-fiat-price'; import { SwapAsset } from '../../hooks/use-swap-form'; -import { SwapAssetItemLayout } from './swap-asset-item.layout'; interface SwapAssetItemProps { asset: SwapAsset; + onClick(): void; } -export function SwapAssetItem({ asset }: SwapAssetItemProps) { - const [component, bind] = usePressable(true); +export function SwapAssetItem({ asset, onClick }: SwapAssetItemProps) { const balanceAsFiat = useAlexSdkBalanceAsFiat(asset.balance, asset.price); const { data: ftMetadata } = useGetFungibleTokenMetadataQuery(asset.principal); @@ -21,21 +22,14 @@ export function SwapAssetItem({ asset }: SwapAssetItemProps) { const displayName = asset.displayName ?? ftMetadataName; return ( - } - {...bind} - > - - {displayName} - {formatMoneyWithoutSymbol(asset.balance)} - - - {asset.name} - - {balanceAsFiat} - - - {component} - + + } + titleLeft={displayName} + captionLeft={asset.name} + titleRight={formatMoneyWithoutSymbol(asset.balance)} + captionRight={balanceAsFiat} + /> + ); } diff --git a/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.layout.tsx b/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.layout.tsx index e7feee61700..eb40bfdcbf4 100644 --- a/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.layout.tsx +++ b/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.layout.tsx @@ -2,7 +2,7 @@ import { Stack, StackProps } from 'leather-styles/jsx'; export function SwapAssetListLayout({ children }: StackProps) { return ( - + {children} ); diff --git a/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.tsx b/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.tsx index bc2875175ff..32c2028fd89 100644 --- a/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.tsx +++ b/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.tsx @@ -1,9 +1,7 @@ import { useNavigate } from 'react-router-dom'; -import { SwapSelectors } from '@tests/selectors/swap.selectors'; import BigNumber from 'bignumber.js'; import { useFormikContext } from 'formik'; -import { styled } from 'leather-styles/jsx'; import { createMoney } from '@shared/models/money.model'; import { isUndefined } from '@shared/utils'; @@ -68,15 +66,11 @@ export function SwapAssetList({ assets }: SwapAssetList) { return ( {selectableAssets.map(asset => ( - onChooseAsset(asset)} - textAlign="left" - type="button" - > - - + /> ))} ); diff --git a/src/app/ui/components/dowpdown-menu/dropdown-menu.tsx b/src/app/ui/components/dowpdown-menu/dropdown-menu.tsx index 169d56ed1c7..38d0bbf67ea 100644 --- a/src/app/ui/components/dowpdown-menu/dropdown-menu.tsx +++ b/src/app/ui/components/dowpdown-menu/dropdown-menu.tsx @@ -3,6 +3,8 @@ import { ReactNode, forwardRef } from 'react'; import * as RadixDropdownMenu from '@radix-ui/react-dropdown-menu'; import { css } from 'leather-styles/css'; +import { itemBaseStyles, itemInteractiveStyles } from '../item/item-interactive'; + export interface DropdownMenuItem { iconLeft?: ReactNode; iconRight?: ReactNode; @@ -67,20 +69,12 @@ const Label: typeof RadixDropdownMenu.Label = forwardRef((props, ref) => ( )); -const dropdownMenuItemStyles = css({ - bg: 'accent.background-primary', - color: 'accent.text-primary', - height: 'auto', - outline: 'none', - userSelect: 'none', - p: 'space.03', - - '&[data-highlighted]': { - bg: 'accent.component-background-hover', - }, -}); const Item: typeof RadixDropdownMenu.Item = forwardRef((props, ref) => ( - + )); const dropdownMenuSeparatorStyles = css({ diff --git a/src/app/ui/components/dynamic-color-circle.tsx b/src/app/ui/components/dynamic-color-circle.tsx index e0d62e71db4..2b4d0f0e495 100644 --- a/src/app/ui/components/dynamic-color-circle.tsx +++ b/src/app/ui/components/dynamic-color-circle.tsx @@ -6,7 +6,7 @@ interface DynamicColorCircleProps extends CircleProps { } export function DynamicColorCircle({ children, - sizeParam = '36', + sizeParam = '40', value, ...props }: DynamicColorCircleProps) { diff --git a/src/app/ui/components/item/item-interactive.stories.tsx b/src/app/ui/components/item/item-interactive.stories.tsx new file mode 100644 index 00000000000..6bcc9274e00 --- /dev/null +++ b/src/app/ui/components/item/item-interactive.stories.tsx @@ -0,0 +1,72 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { Button } from '../button/button'; +import { BtcIcon } from '../icons/btc-icon'; +import { CopyIcon } from '../icons/copy-icon'; +import { QrCodeIcon } from '../icons/qr-code-icon'; +import { ItemInteractive as Component } from './item-interactive'; +import { ItemWithButtonsLayout } from './item-with-buttons.layout'; +import { ItemLayout } from './item.layout'; + +const meta: Meta = { + component: Component, + tags: ['autodocs'], + title: 'Item Interactive', + parameters: { + controls: { include: [] }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const ItemInteractive: Story = { + args: { + onClick: () => {}, + children: ( + } + titleLeft="Label" + captionLeft="Caption" + titleRight="1,000.00 ABC" + captionRight="$1,000.00" + /> + ), + }, +}; + +export const Disabled: Story = { + args: { + disabled: true, + onClick: () => {}, + children: ( + } + titleLeft="Label" + captionLeft="Caption" + titleRight="1,000.00 ABC" + captionRight="$1,000.00" + /> + ), + }, +}; + +export const WithButtons: Story = { + args: { + children: ( + } + title="Label" + caption="Caption" + buttons={ + <> + + + + } + /> + ), + }, +}; diff --git a/src/app/ui/components/item/item-interactive.tsx b/src/app/ui/components/item/item-interactive.tsx new file mode 100644 index 00000000000..9a4667b681d --- /dev/null +++ b/src/app/ui/components/item/item-interactive.tsx @@ -0,0 +1,100 @@ +import { forwardRef } from 'react'; + +import { type RecipeVariantProps, css, cva } from 'leather-styles/css'; +import { Box, BoxProps } from 'leather-styles/jsx'; + +import { isDefined } from '@shared/utils'; + +const basePseudoOutlineProps = { + content: '""', + rounded: 'xs', + position: 'absolute', + top: 0, + left: 0, + bottom: 0, + right: 0, +}; + +const focusVisibleStyles = { + _before: { + ...basePseudoOutlineProps, + border: '2px solid', + borderColor: 'lightModeBlue.500', + }, + _focusWithin: { outline: 'none' }, +}; + +export const itemBaseStyles = css.raw({ + bg: 'accent.background-primary', + color: 'accent.text-primary', + cursor: 'default', + display: 'flex', + height: 'auto', + outline: 'none', + p: 'space.03', + position: 'relative', + rounded: 'xs', + userSelect: 'none', + width: '100%', +}); + +export const itemInteractiveStyles = css.raw({ + cursor: 'pointer', + + '&:is(:active)': { + bg: 'accent.component-background-pressed', + }, + '&:is(:focus-visible)': { + ...focusVisibleStyles, + }, + '&:is(:disabled, [data-disabled])': { + _active: { bg: 'unset' }, + _focus: { border: 'unset' }, + _hover: { bg: 'unset' }, + color: 'accent.non-interactive', + cursor: 'not-allowed', + }, + '&:is(:hover, [data-highlighted])': { + _before: { borderColor: 'transparent' }, + bg: 'accent.component-background-hover', + }, +}); + +const itemRecipe = cva({ + base: itemBaseStyles, + variants: { + disabled: { true: {} }, + interactive: { + true: itemInteractiveStyles, + }, + }, +}); + +export const itemCaptionStyles = css({ + _groupDisabled: { color: 'accent.non-interactive' }, + color: 'accent.text-subdued', +}); + +export const itemChevronStyles = css({ + _groupDisabled: { color: 'accent.non-interactive' }, + color: 'accent.action-primary-default', +}); + +type ItemVariantProps = RecipeVariantProps; + +export const ItemInteractive = forwardRef( + (props, ref) => { + const { disabled, onClick, ...rest } = props; + const isInteractive = isDefined(onClick); + return ( + + ); + } +); diff --git a/src/app/ui/components/item/item-with-buttons.layout.tsx b/src/app/ui/components/item/item-with-buttons.layout.tsx new file mode 100644 index 00000000000..af00349f3f8 --- /dev/null +++ b/src/app/ui/components/item/item-with-buttons.layout.tsx @@ -0,0 +1,44 @@ +import { ReactNode } from 'react'; + +import { Flex, HStack, Stack, styled } from 'leather-styles/jsx'; + +import { Flag } from '../flag/flag'; +import { itemCaptionStyles } from './item-interactive'; + +interface ItemWithButtonsLayoutProps { + buttons: ReactNode; + caption: string; + flagImg: ReactNode; + title: string; +} +export function ItemWithButtonsLayout({ + buttons, + caption, + flagImg, + title, +}: ItemWithButtonsLayoutProps) { + return ( + + + + + {title} + + + {caption} + + + + {buttons} + + + + ); +} diff --git a/src/app/ui/components/item/item.layout.tsx b/src/app/ui/components/item/item.layout.tsx new file mode 100644 index 00000000000..da1dae0d8e2 --- /dev/null +++ b/src/app/ui/components/item/item.layout.tsx @@ -0,0 +1,80 @@ +import { ReactNode, isValidElement } from 'react'; + +import { Flex, HStack, Stack, styled } from 'leather-styles/jsx'; + +import { Flag } from '../flag/flag'; +import { CheckmarkIcon } from '../icons/checkmark-icon'; +import { ChevronUpIcon } from '../icons/chevron-up-icon'; +import { itemCaptionStyles, itemChevronStyles } from './item-interactive'; + +interface ItemLayoutProps { + captionLeft: ReactNode; + captionRight?: ReactNode; + flagImg: ReactNode; + isDisabled?: boolean; + isSelected?: boolean; + showChevron?: boolean; + titleLeft: ReactNode; + titleRight: ReactNode; +} +export function ItemLayout({ + captionLeft, + captionRight, + flagImg, + isSelected, + showChevron, + titleLeft, + titleRight, +}: ItemLayoutProps) { + return ( + + + + + {isValidElement(titleLeft) ? ( + titleLeft + ) : ( + + {titleLeft} + + )} + {isSelected && } + + {isValidElement(captionLeft) ? ( + captionLeft + ) : ( + + {captionLeft} + + )} + + + + {isValidElement(titleRight) ? ( + titleRight + ) : ( + + {titleRight} + + )} + {isValidElement(captionRight) ? ( + captionRight + ) : ( + + {captionRight} + + )} + + {showChevron && } + + + + ); +} diff --git a/src/app/ui/components/item/item.stories.tsx b/src/app/ui/components/item/item.stories.tsx new file mode 100644 index 00000000000..7a118342b18 --- /dev/null +++ b/src/app/ui/components/item/item.stories.tsx @@ -0,0 +1,25 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { Box, Circle } from 'leather-styles/jsx'; + +import { ItemLayout as Component } from './item.layout'; + +const meta: Meta = { + component: Component, + tags: ['autodocs'], + title: 'Layout/Item', +}; + +export default meta; +type Story = StoryObj; + +export const Item: Story = { + render: () => ( + } + titleLeft={} + captionLeft={} + titleRight={} + captionRight={} + /> + ), +}; diff --git a/src/app/ui/components/select/select.tsx b/src/app/ui/components/select/select.tsx index 9953bdce076..166fd0b70e0 100644 --- a/src/app/ui/components/select/select.tsx +++ b/src/app/ui/components/select/select.tsx @@ -3,6 +3,8 @@ import { ReactNode, forwardRef } from 'react'; import * as RadixSelect from '@radix-ui/react-select'; import { css } from 'leather-styles/css'; +import { itemBaseStyles, itemInteractiveStyles } from '../item/item-interactive'; + export interface SelectItem { iconLeft?: ReactNode; iconRight?: ReactNode; @@ -79,20 +81,8 @@ const Label: typeof RadixSelect.Label = forwardRef((props, ref) => ( )); -const selectItemStyles = css({ - bg: 'accent.background-primary', - color: 'accent.text-primary', - height: 'auto', - outline: 'none', - userSelect: 'none', - p: 'space.03', - - '&[data-highlighted]': { - bg: 'accent.component-background-hover', - }, -}); const Item: typeof RadixSelect.Item = forwardRef((props, ref) => ( - + )); const ItemText = RadixSelect.ItemText; diff --git a/src/app/ui/components/tooltip/basic-tooltip.tsx b/src/app/ui/components/tooltip/basic-tooltip.tsx index e10e2d61616..56fcead792f 100644 --- a/src/app/ui/components/tooltip/basic-tooltip.tsx +++ b/src/app/ui/components/tooltip/basic-tooltip.tsx @@ -11,7 +11,6 @@ interface BasicTooltipProps { side?: RadixTooltip.TooltipContentProps['side']; asChild?: boolean; } - export function BasicTooltip({ children, label, disabled, side, asChild }: BasicTooltipProps) { const isDisabled = !label || disabled; return ( diff --git a/src/app/ui/components/typography/caption.tsx b/src/app/ui/components/typography/caption.tsx index 96eb6c55b1b..d87df4d218e 100644 --- a/src/app/ui/components/typography/caption.tsx +++ b/src/app/ui/components/typography/caption.tsx @@ -1,9 +1,17 @@ import { forwardRef } from 'react'; -import { BoxProps, styled } from 'leather-styles/jsx'; +import { HTMLStyledProps, styled } from 'leather-styles/jsx'; -export const Caption = forwardRef(({ children, ...props }, ref) => ( - - {children} - -)); +export const Caption = forwardRef>( + ({ children, ...props }, ref) => ( + + {children} + + ) +); diff --git a/src/app/ui/components/typography/title.tsx b/src/app/ui/components/typography/title.tsx index 0129f184d97..c8eaac73eec 100644 --- a/src/app/ui/components/typography/title.tsx +++ b/src/app/ui/components/typography/title.tsx @@ -1,15 +1,18 @@ import { forwardRef } from 'react'; -import { BoxProps, styled } from 'leather-styles/jsx'; +import { HTMLStyledProps, styled } from 'leather-styles/jsx'; -export const Title = forwardRef(({ children, ...props }, ref) => ( - - {children} - -)); +export const Title = forwardRef>( + ({ children, ...props }, ref) => ( + + {children} + + ) +);