diff --git a/panda.config.ts b/panda.config.ts
index c2ed4de2c60..a078b7f55fc 100644
--- a/panda.config.ts
+++ b/panda.config.ts
@@ -4,6 +4,7 @@ import { breakpoints } from './theme/breakpoints';
import { globalCss } from './theme/global/global';
import { keyframes } from './theme/keyframes';
import { buttonRecipe } from './theme/recipes/button-recipe';
+import { itemRecipe } from './theme/recipes/item-recipe';
import { linkRecipe } from './theme/recipes/link-recipe';
import { semanticTokens } from './theme/semantic-tokens';
import { tokens } from './theme/tokens';
@@ -33,7 +34,7 @@ export default defineConfig({
keyframes,
textStyles,
breakpoints,
- recipes: { button: buttonRecipe, link: linkRecipe },
+ recipes: { button: buttonRecipe, item: itemRecipe, link: linkRecipe },
},
},
outdir: 'leather-styles',
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..8d4ecf3b2ac
--- /dev/null
+++ b/src/app/components/account/account-list-item.layout.tsx
@@ -0,0 +1,59 @@
+import { ComponentProps, ReactNode } from 'react';
+
+import { SettingsSelectors } from '@tests/selectors/settings.selectors';
+
+import { Item } from '@app/ui/components/item/item';
+import { ItemLayout } from '@app/ui/components/item/item.layout';
+import { Spinner } from '@app/ui/components/spinner';
+
+interface AccountListItemLayoutProps extends ComponentProps<'div'> {
+ 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..7aa511e1a2c 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,21 @@
+import { styled } from 'leather-styles/jsx';
+
import { createMoney } from '@shared/models/money.model';
+import { isDefined } from '@shared/utils';
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 { Item } from '@app/ui/components/item/item';
+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,23 +30,22 @@ 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..57e33537b45 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,12 @@
import { ReactNode } from 'react';
+import { styled } from 'leather-styles/jsx';
+
import { AllCryptoCurrencyAssetBalances } from '@shared/models/crypto-asset-balance.model';
+import { isDefined } from '@shared/utils';
-import { Item } from '@app/ui/components/items/item';
-import { ItemDefaultLayout } from '@app/ui/components/items/item-default.layout';
+import { Item } from '@app/ui/components/item/item';
+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 +17,6 @@ interface CryptoCurrencyAssetItemLayoutProps {
address?: string;
assetBalance: AllCryptoCurrencyAssetBalances;
icon: React.ReactNode;
- isPressable?: boolean;
onClick?(): void;
rightElement?: React.ReactNode;
usdBalance?: string;
@@ -25,7 +27,6 @@ export function CryptoCurrencyAssetItemLayout({
address = '',
assetBalance,
icon,
- isPressable,
onClick,
rightElement,
usdBalance,
@@ -34,40 +35,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..68697d59e56 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,52 @@
+import { styled } from 'leather-styles/jsx';
+
import { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model';
+import { isDefined } from '@shared/utils';
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 { Item } from '@app/ui/components/item/item';
+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..f5a89f13b6d 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 { Item } from '@app/ui/components/item/item';
+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..e57a9a29e4f 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 { Item } from '@app/ui/components/item/item';
+import { ItemWithButtonsLayout } from '@app/ui/components/item/item-with-buttons.layout';
import { truncateMiddle } from '@app/ui/utils/truncate-middle';
interface ReceiveItemProps {
@@ -26,32 +24,28 @@ export function ReceiveItem({
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..f58520c6684 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
@@ -3,8 +3,8 @@ 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 { Item } from '@app/ui/components/item/item';
+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';
@@ -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/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..08770e617e6
--- /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';
+
+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..8d1df8b34ae
--- /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';
+
+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..338005a49c2
--- /dev/null
+++ b/src/app/ui/components/item/item.stories.tsx
@@ -0,0 +1,74 @@
+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 { Item } from './item';
+import { ItemWithButtonsLayout } from './item-with-buttons.layout';
+import { ItemLayout } from './item.layout';
+
+const meta: Meta = {
+ component: Item.Root,
+ tags: ['autodocs'],
+ title: 'Item',
+};
+
+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 WithButton: Story = {
+ render: () => (
+
+ }
+ title="Label"
+ caption="Caption"
+ buttons={
+ <>
+ {}
+ {}
+ >
+ }
+ />
+
+ ),
+};
diff --git a/src/app/ui/components/item/item.tsx b/src/app/ui/components/item/item.tsx
new file mode 100644
index 00000000000..ec6c9f5952a
--- /dev/null
+++ b/src/app/ui/components/item/item.tsx
@@ -0,0 +1,45 @@
+import { forwardRef } from 'react';
+
+import { css } from 'leather-styles/css';
+import { styled } from 'leather-styles/jsx';
+import { type ItemVariantProps, item as itemRecipe } from 'leather-styles/recipes';
+import { HTMLStyledProps } from 'leather-styles/types';
+
+import { HasChildren } from '@app/common/has-children';
+
+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',
+});
+
+const ItemBase = forwardRef & ItemVariantProps>(
+ (props, ref) =>
+);
+
+const ItemInteractive = forwardRef & ItemVariantProps>(
+ (props, ref) => (
+
+ )
+);
+
+interface RootProps {
+ isDisabled?: boolean;
+ isInteractive?: boolean;
+ onClickItem?(): void;
+}
+function Root({ children, isDisabled, isInteractive, onClickItem }: RootProps & HasChildren) {
+ return isInteractive ? (
+
+ {children}
+
+ ) : (
+ {children}
+ );
+}
+
+export const Item = { Root };
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/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}
+
+ )
+);
diff --git a/theme/recipes/item-recipe.ts b/theme/recipes/item-recipe.ts
new file mode 100644
index 00000000000..b348e8b2e55
--- /dev/null
+++ b/theme/recipes/item-recipe.ts
@@ -0,0 +1,49 @@
+import { defineRecipe } from '@pandacss/dev';
+
+// ts-unused-exports:disable-next-line
+export const itemRecipe = defineRecipe({
+ description: 'The styles for the Item component',
+ className: 'item',
+ jsx: ['Item'],
+ base: {
+ bg: 'accent.background-primary',
+ color: 'accent.text-primary',
+ display: 'flex',
+ height: 'auto',
+ minHeight: '66px',
+ outline: 'none',
+ p: 'space.03',
+ userSelect: 'none',
+ width: '100%',
+ },
+ variants: {
+ interactive: {
+ true: {
+ _active: {
+ bg: 'accent.component-background-pressed',
+ },
+ _disabled: {
+ _active: { bg: 'unset' },
+ _focus: { border: 'unset' },
+ _hover: { bg: 'unset' },
+ color: 'accent.non-interactive',
+ cursor: 'not-allowed',
+ },
+ _focus: {
+ _before: {
+ content: '""',
+ pos: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ border: '2px solid {colors.focus}',
+ },
+ },
+ _hover: {
+ bg: 'accent.component-background-hover',
+ },
+ },
+ },
+ },
+});