diff --git a/src/app/components/account/account-addresses.tsx b/src/app/components/account/account-addresses.tsx
new file mode 100644
index 00000000000..9a2fda9ec52
--- /dev/null
+++ b/src/app/components/account/account-addresses.tsx
@@ -0,0 +1,28 @@
+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 { 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 82cc25d2a59..00000000000
--- a/src/app/components/account/account-list-item-layout.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { ComponentProps, ReactNode } from 'react';
-
-import { SettingsSelectors } from '@tests/selectors/settings.selectors';
-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 { CheckmarkIcon } from '@app/ui/components/icons/checkmark-icon';
-import { Item } from '@app/ui/components/items/item';
-import { ItemDefaultLayout } from '@app/ui/components/items/item-default.layout';
-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 ComponentProps<'div'> {
- accountName: ReactNode;
- avatar: ReactNode;
- balanceLabel: ReactNode;
- index: number;
- isLoading: boolean;
- isPressable?: boolean;
- isSelected: boolean;
- onSelectAccount(): void;
-}
-export function AccountListItemLayout(props: AccountListItemLayoutProps) {
- const {
- accountName,
- avatar,
- balanceLabel,
- index,
- isLoading,
- isPressable,
- isSelected,
- onSelectAccount,
- } = props;
-
- const isBreakpointSm = useViewportMinWidth('sm');
-
- return (
-
-
-
- {accountName}
- {isSelected && }
-
- }
- contentLeftBottom={
-
-
-
- {account => (
-
- {truncateMiddle(account.address, isBreakpointSm ? 4 : 3)}
-
- )}
-
-
- {signer => (
-
- {truncateMiddle(signer.address, 5)}
-
- )}
-
-
-
- }
- contentRightTop={
- isLoading ? (
-
- ) : (
- {balanceLabel}
- )
- }
- />
-
-
- );
-}
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/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 8652feb9670..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
@@ -34,7 +34,6 @@ export function Brc20TokenAssetList(props: { brc20Tokens?: Brc20Token[] }) {
key={token.tick}
displayNotEnoughBalance={!hasPositiveBtcBalanceForFees}
token={token}
- isPressable={hasPositiveBtcBalanceForFees}
onClick={hasPositiveBtcBalanceForFees ? () => 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 750b729abc4..fe66492d136 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,20 +1,20 @@
+import { styled } from 'leather-styles/jsx';
+
import { createMoney } from '@shared/models/money.model';
import { formatBalance } from '@app/common/format-balance';
import { Brc20Token } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.query';
import { Brc20TokenIcon } from '@app/ui/components/icons/brc20-token-icon';
-import { Item } from '@app/ui/components/items/item';
-import { ItemDefaultLayout } from '@app/ui/components/items/item-default.layout';
+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 {
token: Brc20Token;
- isPressable?: boolean;
onClick?(): void;
displayNotEnoughBalance?: boolean;
}
export function Brc20TokenAssetItemLayout({
- isPressable,
onClick,
displayNotEnoughBalance,
token,
@@ -29,24 +29,23 @@ export function Brc20TokenAssetItemLayout({
label={'Not enough BTC in balance'}
side="top"
>
-
-
- }
- contentLeftTop={{token.tick}}
- contentLeftBottom={{'BRC-20'}}
- contentRightTop={
-
- {formattedBalance.value}
-
- }
- />
-
-
+
+ }
+ titleLeft={token.tick}
+ captionLeft={'BRC-20'}
+ titleRight={
+
+
+ {formattedBalance.value}
+
+
+ }
+ />
+
);
}
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 81eb70a1ecc..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
@@ -18,7 +18,6 @@ export function CryptoAssetListItem(props: CryptoAssetListItemProps) {
}
- isPressable
onClick={onClick}
/>
);
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 d40c64ede1f..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
@@ -29,7 +29,6 @@ export function CryptoAssetList({ cryptoAssetBalances, onItemClick }: CryptoAsse
assetBalance={balance}
icon={}
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 8d7389ea14f..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
@@ -13,13 +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/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 d5ff281458e..f603857fd12 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,9 +1,11 @@
import { ReactNode } from 'react';
+import { styled } from 'leather-styles/jsx';
+
import { AllCryptoCurrencyAssetBalances } from '@shared/models/crypto-asset-balance.model';
-import { Item } from '@app/ui/components/items/item';
-import { ItemDefaultLayout } from '@app/ui/components/items/item-default.layout';
+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 { parseCryptoCurrencyAssetBalance } from './crypto-currency-asset.utils';
@@ -14,7 +16,6 @@ interface CryptoCurrencyAssetItemLayoutProps {
address?: string;
assetBalance: AllCryptoCurrencyAssetBalances;
icon: React.ReactNode;
- isPressable?: boolean;
onClick?(): void;
rightElement?: React.ReactNode;
usdBalance?: string;
@@ -25,7 +26,6 @@ export function CryptoCurrencyAssetItemLayout({
address = '',
assetBalance,
icon,
- isPressable,
onClick,
rightElement,
usdBalance,
@@ -34,40 +34,35 @@ export function CryptoCurrencyAssetItemLayout({
parseCryptoCurrencyAssetBalance(assetBalance);
return (
-
-
- {title}}
- contentLeftBottom={{balance.symbol}}
- contentRightTop={
- rightElement ? (
- rightElement
- ) : (
-
-
- {formattedBalance.value} {additionalBalanceInfo}
-
-
- )
- }
- contentRightBottom={
- !rightElement && (
- <>
- {balance.amount.toNumber() > 0 && address ? (
- {usdBalance}
- ) : null}
- {additionalUsdBalanceInfo}
- >
- )
- }
- />
-
-
+
+
+
+ {formattedBalance.value} {additionalBalanceInfo}
+
+
+ )
+ }
+ captionRight={
+ !rightElement && (
+ <>
+ {balance.amount.toNumber() > 0 && address ? usdBalance : null}
+ {additionalUsdBalanceInfo}
+ >
+ )
+ }
+ />
+
);
}
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 d47bb580151..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,52 +1,51 @@
+import { styled } from 'leather-styles/jsx';
+
import { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model';
import { StacksAssetAvatar } from '@app/components/crypto-assets/stacks/components/stacks-asset-avatar';
-import { Item } from '@app/ui/components/items/item';
-import { ItemDefaultLayout } from '@app/ui/components/items/item-default.layout';
+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 { parseStacksFungibleTokenAssetBalance } from './fungible-token-asset.utils';
interface StacksFungibleTokenAssetItemLayoutProps {
assetBalance: StacksFungibleTokenAssetBalance;
- isPressable?: boolean;
onClick?(): void;
}
export function StacksFungibleTokenAssetItemLayout({
assetBalance,
- isPressable,
onClick,
}: StacksFungibleTokenAssetItemLayoutProps) {
const { amount, avatar, caption, dataTestId, formattedBalance, imageCanonicalUri, title } =
parseStacksFungibleTokenAssetBalance(assetBalance);
return (
-
-
-
- {title[0]}
-
- }
- contentLeftTop={{title}}
- contentLeftBottom={{caption}}
- contentRightTop={
-
- {formattedBalance.value}
-
- }
- />
-
-
+
+
+ {title[0]}
+
+ }
+ titleLeft={title}
+ captionLeft={caption}
+ titleRight={
+
+
+ {formattedBalance.value}
+
+
+ }
+ />
+
);
}
diff --git a/src/app/components/transaction-item/transaction-item.layout.tsx b/src/app/components/transaction-item/transaction-item.layout.tsx
index 4248f843e04..e778cf9591d 100644
--- a/src/app/components/transaction-item/transaction-item.layout.tsx
+++ b/src/app/components/transaction-item/transaction-item.layout.tsx
@@ -1,7 +1,9 @@
import { ReactNode } from 'react';
-import { Item } from '@app/ui/components/items/item';
-import { ItemDefaultLayout } from '@app/ui/components/items/item-default.layout';
+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;
@@ -23,21 +25,29 @@ export function TransactionItemLayout({
txValue,
}: TransactionItemLayoutProps) {
return (
-
-
- {txTitle}}
- contentLeftBottom={
- <>
- {txCaption}
- {txStatus && txStatus}
- >
- }
- contentRightTop={rightElement ? rightElement : {txValue}}
- />
-
-
+ // TODO: Revisit if needed styles position="relative" zIndex={99}
+
+
+
+ {txCaption}
+
+ {txStatus && txStatus}
+ >
+ }
+ titleRight={
+ rightElement ? (
+ rightElement
+ ) : (
+
+ {txValue}
+
+ )
+ }
+ />
+
);
}
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 d5e77122983..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,12 +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 { 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;
@@ -19,15 +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 name = useAccountDisplayName({ address: stacksAddress, index });
+ const name = useAccountDisplayName({ address: stxAddress, index });
const handleClick = async () => {
setIsLoading();
@@ -39,6 +40,7 @@ export const SwitchAccountListItem = memo(
return (
}
accountName={{name}}
avatar={
}
- balanceLabel={
-
- }
+ balanceLabel={}
index={index}
isLoading={isLoading}
- isPressable
isSelected={currentAccountIndex === index}
onSelectAccount={handleClick}
/>
diff --git a/src/app/pages/choose-account/components/accounts.tsx b/src/app/pages/choose-account/components/accounts.tsx
index f22b61e946d..793396e2589 100644
--- a/src/app/pages/choose-account/components/accounts.tsx
+++ b/src/app/pages/choose-account/components/accounts.tsx
@@ -12,8 +12,9 @@ import { useCreateAccount } from '@app/common/hooks/account/use-create-account';
import { useWalletType } from '@app/common/use-wallet-type';
import { slugify } from '@app/common/utils';
import { AccountTotalBalance } from '@app/components/account-total-balance';
+import { AcccountAddresses } from '@app/components/account/account-addresses';
import { AccountAvatar } from '@app/components/account/account-avatar';
-import { AccountListItemLayout } from '@app/components/account/account-list-item-layout';
+import { AccountListItemLayout } from '@app/components/account/account-list-item.layout';
import { usePressable } from '@app/components/item-hover';
import { useNativeSegwitAccountIndexAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
@@ -45,6 +46,7 @@ const ChooseAccountItem = memo(
// Padding required on outer element to prevent jumpy virtualized list
}
accountName={
}>
{name}
diff --git a/src/app/pages/receive/components/receive-item.tsx b/src/app/pages/receive/components/receive-item.tsx
index a6d7e11f078..d4b79796505 100644
--- a/src/app/pages/receive/components/receive-item.tsx
+++ b/src/app/pages/receive/components/receive-item.tsx
@@ -1,10 +1,8 @@
-import { HStack } from 'leather-styles/jsx';
-
import { Button } from '@app/ui/components/button/button';
import { CopyIcon } from '@app/ui/components/icons/copy-icon';
import { QrCodeIcon } from '@app/ui/components/icons/qr-code-icon';
-import { Item } from '@app/ui/components/items/item';
-import { ItemWithButtonsLayout } from '@app/ui/components/items/item-with-buttons.layout';
+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 {
@@ -25,33 +23,29 @@ export function ReceiveItem({
}: ReceiveItemProps) {
if (!address) return null;
return (
-
-
- {title}}
- contentLeftBottom={
- {truncateMiddle(address, 6)}
- }
- contentRightTop={
-
-
- }
- />
-
-
+ )}
+ >
+ }
+ />
+
);
}
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 c55652b6522..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,8 +6,9 @@ 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 { useNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
@@ -36,6 +37,7 @@ export const AccountListItem = memo(({ index, stacksAccount, onClose }: AccountL
return (
}
accountName={{name}}
avatar={
}
index={index}
isSelected={false}
- isPressable
isLoading={false}
onSelectAccount={onSelectAccount}
/>
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 2ef1aecb9df..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,20 +1,20 @@
+import { SwapSelectors } from '@tests/selectors/swap.selectors';
import { styled } from 'leather-styles/jsx';
import { formatMoneyWithoutSymbol } from '@app/common/money/format-money';
import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query';
import { isFtAsset } from '@app/query/stacks/tokens/token-metadata.utils';
-import { Item } from '@app/ui/components/items/item';
-import { ItemDefaultLayout } from '@app/ui/components/items/item-default.layout';
+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';
interface SwapAssetItemProps {
asset: SwapAsset;
- dataTestId: string;
onClick(): void;
}
-export function SwapAssetItem({ asset, dataTestId, onClick }: SwapAssetItemProps) {
+export function SwapAssetItem({ asset, onClick }: SwapAssetItemProps) {
const balanceAsFiat = useAlexSdkBalanceAsFiat(asset.balance, asset.price);
const { data: ftMetadata } = useGetFungibleTokenMetadataQuery(asset.principal);
@@ -22,17 +22,14 @@ export function SwapAssetItem({ asset, dataTestId, onClick }: SwapAssetItemProps
const displayName = asset.displayName ?? ftMetadataName;
return (
-
-
- }
- contentLeftTop={{displayName}}
- contentLeftBottom={{asset.name}}
- contentRightTop={{formatMoneyWithoutSymbol(asset.balance)}}
- contentRightBottom={{balanceAsFiat}}
- />
-
-
+
+ }
+ 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.tsx b/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.tsx
index 5419a1c2629..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,6 +1,5 @@
import { useNavigate } from 'react-router-dom';
-import { SwapSelectors } from '@tests/selectors/swap.selectors';
import BigNumber from 'bignumber.js';
import { useFormikContext } from 'formik';
@@ -69,7 +68,6 @@ export function SwapAssetList({ assets }: SwapAssetList) {
{selectableAssets.map(asset => (
onChooseAsset(asset)}
/>
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/item/item-interactive.stories.tsx b/src/app/ui/components/item/item-interactive.stories.tsx
new file mode 100644
index 00000000000..5a91cb3ce09
--- /dev/null
+++ b/src/app/ui/components/item/item-interactive.stories.tsx
@@ -0,0 +1,75 @@
+import { Meta, StoryObj } from '@storybook/react';
+import { Box } from 'leather-styles/jsx';
+
+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',
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const ItemInteractive: Story = {
+ render: () => (
+ {}}>
+
+
+ ),
+};
+
+export const Default: Story = {
+ render: () => (
+ {}}>
+ }
+ titleLeft="Label"
+ captionLeft="Caption"
+ titleRight="1,000.00 ABC"
+ captionRight="$1,000.00"
+ />
+
+ ),
+};
+
+export const Disabled: Story = {
+ render: () => (
+ {}}>
+ }
+ titleLeft="Label"
+ captionLeft="Caption"
+ titleRight="1,000.00 ABC"
+ captionRight="$1,000.00"
+ />
+
+ ),
+};
+
+export const WithButtons: Story = {
+ render: () => (
+
+ }
+ 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..5e0b28af3b7
--- /dev/null
+++ b/src/app/ui/components/item/item-interactive.tsx
@@ -0,0 +1,99 @@
+import { forwardRef } from 'react';
+
+import { type RecipeVariantProps, css, cva } from 'leather-styles/css';
+import { styled } from 'leather-styles/jsx';
+import { HTMLStyledProps } from 'leather-styles/types';
+
+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: {
+ 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<
+ HTMLButtonElement,
+ HTMLStyledProps<'button'> & ItemVariantProps
+>((props, ref) => {
+ const { 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..f1809838107
--- /dev/null
+++ b/src/app/ui/components/item/item-with-buttons.layout.tsx
@@ -0,0 +1,45 @@
+import { ReactNode } from 'react';
+
+import { Flex, HStack, Stack, styled } from 'leather-styles/jsx';
+
+import { Flag } from '@app/components/layout/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..f21a68519a5
--- /dev/null
+++ b/src/app/ui/components/item/item.layout.tsx
@@ -0,0 +1,81 @@
+import { ReactNode, isValidElement } from 'react';
+
+import { Flex, HStack, Stack, styled } from 'leather-styles/jsx';
+
+import { Flag } from '@app/components/layout/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/items/item-default.layout.tsx b/src/app/ui/components/items/item-default.layout.tsx
deleted file mode 100644
index ad9b7b0e7f5..00000000000
--- a/src/app/ui/components/items/item-default.layout.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { ReactNode } from 'react';
-
-import { Grid, GridItem } from 'leather-styles/jsx';
-
-import { ChevronUpIcon } from '../icons/chevron-up-icon';
-
-interface ItemDefaultLayoutProps {
- contentLeftTop: ReactNode;
- contentLeftBottom?: ReactNode;
- contentRightTop: ReactNode;
- contentRightBottom?: ReactNode;
- flagImg?: ReactNode;
- isDisabled?: boolean;
- isPressable?: boolean;
-}
-export function ItemDefaultLayout({
- contentLeftTop,
- contentLeftBottom,
- contentRightTop,
- contentRightBottom,
- flagImg,
- isDisabled,
- isPressable,
-}: ItemDefaultLayoutProps) {
- return (
-
-
- {flagImg}
-
-
- {contentLeftTop}
-
-
- {contentRightTop}
-
-
- {isPressable && (
-
- )}
-
- {/* Using scroll here for now bc possibly shows addresses */}
-
- {contentLeftBottom}
-
- {contentRightBottom && {contentRightBottom}}
-
- );
-}
diff --git a/src/app/ui/components/items/item-with-buttons.layout.tsx b/src/app/ui/components/items/item-with-buttons.layout.tsx
deleted file mode 100644
index 7900957c59e..00000000000
--- a/src/app/ui/components/items/item-with-buttons.layout.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { ReactNode } from 'react';
-
-import { Grid, GridItem } from 'leather-styles/jsx';
-
-interface ItemWithButtonsLayoutProps {
- contentLeftTop: ReactNode;
- contentLeftBottom?: ReactNode;
- contentRightTop: ReactNode;
- contentRightBottom?: ReactNode;
- flagImg?: ReactNode;
- isDisabled?: boolean;
- isPressable?: boolean;
-}
-export function ItemWithButtonsLayout({
- contentLeftTop,
- contentLeftBottom,
- contentRightTop,
- flagImg,
-}: ItemWithButtonsLayoutProps) {
- return (
-
-
- {flagImg}
-
-
- {contentLeftTop}
-
-
- {contentRightTop}
-
- {/* Using scroll here for now bc possibly shows addresses */}
-
- {contentLeftBottom}
-
-
- );
-}
diff --git a/src/app/ui/components/items/item.stories.tsx b/src/app/ui/components/items/item.stories.tsx
deleted file mode 100644
index f739c1ce5b9..00000000000
--- a/src/app/ui/components/items/item.stories.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { HStack } from 'leather-styles/jsx';
-
-import { Button } from '../button/button';
-import { BtcIcon } from '../icons/btc-icon';
-import { CheckmarkIcon } from '../icons/checkmark-icon';
-import { CopyIcon } from '../icons/copy-icon';
-import { QrCodeIcon } from '../icons/qr-code-icon';
-import { Item } from './item';
-import { ItemDefaultLayout } from './item-default.layout';
-import { ItemWithButtonsLayout } from './item-with-buttons.layout';
-
-const meta: Meta = {
- component: Item.Root,
- tags: ['autodocs'],
- title: 'Item',
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- render: () => (
- // eslint-disable-next-line no-console
-
-
- }
- contentLeftTop={Label}
- contentLeftBottom={Caption}
- contentRightTop={1,000.00 ABC}
- contentRightBottom={$1,000.00}
- />
-
-
- ),
-};
-
-export const DefaultPressable: Story = {
- render: () => (
- // eslint-disable-next-line no-console
- console.log(`I'm interactive`)}>
-
- }
- contentLeftTop={Label}
- contentLeftBottom={Caption}
- contentRightTop={1,000.00 ABC}
- contentRightBottom={$1,000.00}
- />
-
-
- ),
-};
-
-export const Selected: Story = {
- render: () => (
- // eslint-disable-next-line no-console
- console.log(`I'm interactive`)}>
-
- }
- contentLeftTop={
-
- Label
-
-
- }
- contentLeftBottom={Caption}
- contentRightTop={1,000.00 ABC}
- contentRightBottom={$1,000.00}
- />
-
-
- ),
-};
-
-export const Disabled: Story = {
- render: () => (
- // eslint-disable-next-line no-console
- console.log(`I'm interactive`)}>
-
- }
- contentLeftTop={Label}
- contentLeftBottom={Caption}
- contentRightTop={1,000.00 ABC}
- contentRightBottom={$1,000.00}
- />
-
-
- ),
-};
-
-export const WithButtons: Story = {
- render: () => (
- // eslint-disable-next-line no-console
-
-
- }
- contentLeftTop={Label}
- contentLeftBottom={Caption}
- contentRightTop={
-
-
-
-
-
-
-
-
- }
- />
-
-
- ),
-};
-
-export const WithButtonsDisabled: Story = {
- render: () => (
- // eslint-disable-next-line no-console
-
-
- }
- contentLeftTop={Label}
- contentLeftBottom={Caption}
- contentRightTop={
-
-
-
-
-
-
-
-
- }
- />
-
-
- ),
-};
diff --git a/src/app/ui/components/items/item.tsx b/src/app/ui/components/items/item.tsx
deleted file mode 100644
index eec33764e1e..00000000000
--- a/src/app/ui/components/items/item.tsx
+++ /dev/null
@@ -1,137 +0,0 @@
-import { type ComponentProps, KeyboardEvent } from 'react';
-
-import { sva } from 'leather-styles/css';
-import { BoxProps, styled } from 'leather-styles/jsx';
-
-import { createStyleContext } from '@app/ui/utils/style-context';
-
-const item = sva({
- slots: ['root', 'content', 'text', 'caption'],
- base: {
- root: {
- bg: 'accent.background-primary',
- height: 'auto',
- userSelect: 'none',
- p: 'space.03',
- width: '100%',
- },
- content: {
- width: '100%',
- },
- text: {
- color: 'accent.text-primary',
- fontWeight: 500,
- textStyle: 'label.02',
- },
- caption: {
- color: 'accent.text-subdued',
- },
- },
- variants: {
- isDisabled: {
- true: {
- root: {
- _active: { bg: 'unset' },
- _focus: { border: 'unset' },
- _hover: { bg: 'unset' },
- cursor: 'not-allowed',
- },
- content: {},
- text: {
- color: 'accent.non-interactive',
- },
- caption: {
- color: 'accent.non-interactive',
- },
- },
- },
- isPressable: {
- true: {
- root: {
- _active: {
- bg: 'accent.component-background-pressed',
- },
- _focus: {
- border: '2px solid {focus}',
- },
- _hover: {
- bg: 'accent.component-background-hover',
- },
- },
- },
- },
- isSelected: {
- true: {
- root: {
- _active: {
- bg: 'unset',
- },
- _focus: {
- border: 'unset',
- },
- _hover: {
- bg: 'unset',
- },
- },
- },
- },
- },
- compoundVariants: [
- {
- isDisabled: true,
- isPressable: true,
- css: {
- root: {
- _active: { bg: 'unset' },
- _focus: { border: 'unset' },
- _hover: { bg: 'unset' },
- cursor: 'not-allowed',
- },
- },
- },
- ],
-});
-
-const { withProvider, withContext } = createStyleContext(item);
-
-const RootBase = withProvider('div', 'root');
-
-interface RootProps extends ComponentProps<'div'> {
- isDisabled?: boolean;
- isPressable?: boolean;
- isSelected?: boolean;
- onClick?(): void;
-}
-function Root({ isDisabled, isPressable, isSelected, onClick, ...props }: RootProps & BoxProps) {
- const isRoleButton = !!onClick;
-
- function onKeyDown(e: KeyboardEvent) {
- if (!isRoleButton) return;
- if (e.key === 'enter' || e.key === ' ') {
- e.preventDefault();
- onClick();
- }
- }
-
- return (
-
- );
-}
-
-const Content = withContext(styled('div'), 'content');
-
-const Text = withContext(styled('span'), 'text');
-
-const Caption = withContext(styled('span'), 'caption');
-
-export const Item = { Root, Content, Text, Caption };
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/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..744ace4763e 100644
--- a/src/app/ui/components/typography/title.tsx
+++ b/src/app/ui/components/typography/title.tsx
@@ -1,15 +1,17 @@
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}
+
+ )
+);