diff --git a/src/app/components/network-mode-badge.tsx b/src/app/components/network-mode-badge.tsx index 0e535ffd9fb..d8067de7392 100644 --- a/src/app/components/network-mode-badge.tsx +++ b/src/app/components/network-mode-badge.tsx @@ -2,11 +2,12 @@ import { memo, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { ChainID } from '@stacks/transactions'; -import { Flex, FlexProps, styled } from 'leather-styles/jsx'; +import { Flex, FlexProps } from 'leather-styles/jsx'; import { RouteUrls } from '@shared/route-urls'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; +import { Tag } from '@app/ui/components/tag/tag'; export const NetworkModeBadge = memo((props: FlexProps) => { const navigate = useNavigate(); @@ -21,19 +22,12 @@ export const NetworkModeBadge = memo((props: FlexProps) => { return ( navigate(RouteUrls.SelectNetwork, { relative: 'path' })} - px="space.03" position="relative" zIndex={999} {...props} > - - {name} - + ); }); diff --git a/src/app/components/pill.tsx b/src/app/components/pill.tsx deleted file mode 100644 index f63281d02a5..00000000000 --- a/src/app/components/pill.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Box, BoxProps, styled } from 'leather-styles/jsx'; - -import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; - -interface PillProps extends BoxProps { - hoverLabel?: string; - label: string; -} -export function Pill({ hoverLabel, label, ...props }: PillProps) { - return ( - - - {label} - - - ); -} diff --git a/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-input-list/components/psbt-input-item.tsx b/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-input-list/components/psbt-input-item.tsx index 2709de06da4..e93cf35756b 100644 --- a/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-input-list/components/psbt-input-item.tsx +++ b/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-input-list/components/psbt-input-item.tsx @@ -1,8 +1,8 @@ import { createMoney } from '@shared/models/money.model'; import { formatMoney } from '@app/common/money/format-money'; -import { Pill } from '@app/components/pill'; import { PsbtInput } from '@app/features/psbt-signer/hooks/use-parsed-inputs'; +import { TagWithTooltip } from '@app/ui/components/tag/tag-with-tooltip'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; import { PsbtInputOutputItemLayout } from '../../psbt-input-output-item.layout'; @@ -15,7 +15,11 @@ export function PsbtInputItem({ utxo }: { utxo: PsbtInput }) { address={truncateMiddle(utxo.address)} addressHoverLabel={utxo.address} amount={formatMoney(createMoney(utxo.value, 'BTC'))} - label={utxo.toSign ? : undefined} + label={ + utxo.toSign ? ( + + ) : undefined + } txId={truncateMiddle(utxo.txid)} txIdHoverLabel={utxo.txid} /> diff --git a/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-input-output-item.layout.tsx b/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-input-output-item.layout.tsx index 8eb50055d8c..3dd279b4295 100644 --- a/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-input-output-item.layout.tsx +++ b/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-input-output-item.layout.tsx @@ -39,9 +39,9 @@ export function PsbtInputOutputItemLayout({ side="bottom" > - - {addressHoverLabel ? : null} - + + {addressHoverLabel ? : null} + {label} diff --git a/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-output-list/components/psbt-output-item.tsx b/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-output-list/components/psbt-output-item.tsx index e8ddb099cb4..bad99075d52 100644 --- a/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-output-list/components/psbt-output-item.tsx +++ b/src/app/features/psbt-signer/components/psbt-inputs-and-outputs/components/psbt-output-list/components/psbt-output-item.tsx @@ -1,8 +1,8 @@ import { createMoney } from '@shared/models/money.model'; import { formatMoney } from '@app/common/money/format-money'; -import { Pill } from '@app/components/pill'; import { PsbtOutput } from '@app/features/psbt-signer/hooks/use-parsed-outputs'; +import { TagWithTooltip } from '@app/ui/components/tag/tag-with-tooltip'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; import { PsbtInputOutputItemLayout } from '../../psbt-input-output-item.layout'; @@ -19,7 +19,11 @@ export function PsbtOutputItem({ output }: { output: PsbtOutput }) { address={truncateMiddle(output.address)} addressHoverLabel={output.address} amount={formatMoney(createMoney(Number(output.value), 'BTC'))} - label={output.toSign ? : undefined} + label={ + output.toSign ? ( + + ) : undefined + } /> ); } diff --git a/src/app/features/psbt-signer/components/psbt-request-details-header.tsx b/src/app/features/psbt-signer/components/psbt-request-details-header.tsx index 11069544b9e..bc4dfb2956c 100644 --- a/src/app/features/psbt-signer/components/psbt-request-details-header.tsx +++ b/src/app/features/psbt-signer/components/psbt-request-details-header.tsx @@ -1,8 +1,7 @@ -import { Box, HStack, styled } from 'leather-styles/jsx'; -import { token } from 'leather-styles/tokens'; +import { HStack, styled } from 'leather-styles/jsx'; import { usePsbtSignerContext } from '@app/features/psbt-signer/psbt-signer.context'; -import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; +import { TagWithTooltip } from '@app/ui/components/tag/tag-with-tooltip'; import { LockIcon } from '@app/ui/icons/lock-icon'; import { UnlockIcon } from '@app/ui/icons/unlock-icon'; @@ -13,37 +12,17 @@ const uncertainLabel = export function PsbtRequestDetailsHeader() { const { isPsbtMutable } = usePsbtSignerContext(); - const tokenLabelColor = isPsbtMutable - ? token('colors.yellow.action-primary-default') - : token('colors.ink.text-subdued'); return ( Transaction - - - - {isPsbtMutable ? ( - - ) : ( - - )} - - - {isPsbtMutable ? 'Uncertain' : 'Certain'} - - - + : } + label={isPsbtMutable ? 'Uncertain' : 'Certain'} + transparent + variant={isPsbtMutable ? 'warning' : 'default'} + /> ); } diff --git a/src/app/pages/fund/components/fast-checkout-badge.tsx b/src/app/pages/fund/components/fast-checkout-badge.tsx deleted file mode 100644 index 423c0660e07..00000000000 --- a/src/app/pages/fund/components/fast-checkout-badge.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { HStack, styled } from 'leather-styles/jsx'; - -import { ZapIcon } from '@app/ui/icons/zap-icon'; - -export function FastCheckoutBadge() { - return ( - - - - Fast checkout - - - ); -} diff --git a/src/app/pages/fund/components/fiat-provider-item.tsx b/src/app/pages/fund/components/fiat-provider-item.tsx index b94bdc268a6..31f11affb2d 100644 --- a/src/app/pages/fund/components/fiat-provider-item.tsx +++ b/src/app/pages/fund/components/fiat-provider-item.tsx @@ -1,10 +1,10 @@ import { FundPageSelectors } from '@tests/selectors/fund.selectors'; import { AvailableRegions } from '@app/query/common/remote-config/remote-config.query'; +import { Tag } from '@app/ui/components/tag/tag'; +import { StarIcon, ZapIcon } from '@app/ui/icons'; -import { FastCheckoutBadge } from './fast-checkout-badge'; import { FundAccountTile } from './fund-account-tile'; -import { ZeroPercentFeesBadge } from './zero-percent-fees-badge'; const availableInsideUnitedStatesDescription = 'Available only inside of the US'; const availableOutsideUnitedStatesDescription = 'Available only outside of the US'; @@ -37,8 +37,12 @@ export function FiatProviderItem(props: FiatProviderProps) { const Attributes = ( <> - {hasFastCheckoutProcess && } - {!hasTradingFees && } + {hasFastCheckoutProcess && ( + } label="Fast checkout" variant="success" /> + )} + {!hasTradingFees && ( + } label="0 % Fees" variant="warning" /> + )} ); diff --git a/src/app/pages/fund/components/zero-percent-fees-badge.tsx b/src/app/pages/fund/components/zero-percent-fees-badge.tsx deleted file mode 100644 index 9659155f7fa..00000000000 --- a/src/app/pages/fund/components/zero-percent-fees-badge.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { HStack, styled } from 'leather-styles/jsx'; - -import { StarIcon } from '@app/ui/icons/star-icon'; - -export function ZeroPercentFeesBadge() { - return ( - - - - 0 % Fees - - - ); -} diff --git a/src/app/ui/components/callout/callout.tsx b/src/app/ui/components/callout/callout.tsx index 553c650fc1b..7ba98707b97 100644 --- a/src/app/ui/components/callout/callout.tsx +++ b/src/app/ui/components/callout/callout.tsx @@ -1,44 +1,51 @@ import type { ReactNode } from 'react'; -import { css } from 'leather-styles/css'; +import { type RecipeVariantProps, cva } from 'leather-styles/css'; import { Box, type BoxProps, Stack, styled } from 'leather-styles/jsx'; import { Flag } from '../flag/flag'; import { getIconVariant } from './callout.utils'; -type CalloutVariant = 'default' | 'error' | 'info' | 'success' | 'warning'; - -const calloutStyles = css({ - color: 'ink.text-primary', - textAlign: 'left', - width: '100%', - - '&[data-variant="default"]': { - backgroundColor: 'ink.non-interactive', - }, - '&[data-variant="error"]': { - backgroundColor: 'red.background-secondary', +const calloutRecipe = cva({ + base: { + color: 'ink.text-primary', + textAlign: 'left', + width: '100%', }, - '&[data-variant="info"]': { - backgroundColor: 'blue.background-secondary', + variants: { + variant: { + default: { + bg: 'ink.non-interactive', + }, + error: { + bg: 'red.background-secondary', + }, + info: { + bg: 'blue.background-secondary', + }, + success: { + bg: 'green.background-secondary', + }, + warning: { + bg: 'yellow.background-secondary', + }, + }, }, - '&[data-variant="success"]': { - backgroundColor: 'green.background-secondary', - }, - '&[data-variant="warning"]': { - backgroundColor: 'yellow.background-secondary', + defaultVariants: { + variant: 'default', }, }); +type CalloutVariants = RecipeVariantProps; + interface CalloutProps extends BoxProps { icon?: ReactNode; title?: string; - variant?: CalloutVariant; } -export function Callout(props: CalloutProps) { +export function Callout(props: CalloutProps & CalloutVariants) { const { children, icon, title, variant = 'default', ...rest } = props; return ( - + + + + ); +} diff --git a/src/app/ui/components/tag/tag.stories.tsx b/src/app/ui/components/tag/tag.stories.tsx new file mode 100644 index 00000000000..0a4444facc5 --- /dev/null +++ b/src/app/ui/components/tag/tag.stories.tsx @@ -0,0 +1,32 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ErrorCircleIcon } from '@app/ui/icons'; + +import { Tag as Component } from './tag'; + +const meta: Meta = { + component: Component, + tags: ['autodocs'], + title: 'Tag', + parameters: { + controls: { include: ['transparent', 'variant'] }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Tag: Story = { + args: { + label: 'Label', + variant: 'default', + }, +}; + +export const WithIcon: Story = { + args: { + icon: , + label: 'Label', + variant: 'default', + }, +}; diff --git a/src/app/ui/components/tag/tag.tsx b/src/app/ui/components/tag/tag.tsx new file mode 100644 index 00000000000..39490283474 --- /dev/null +++ b/src/app/ui/components/tag/tag.tsx @@ -0,0 +1,64 @@ +import { type RecipeVariantProps, cva } from 'leather-styles/css'; +import { HStack, type HTMLStyledProps, styled } from 'leather-styles/jsx'; + +const tagRecipe = cva({ + base: { + borderRadius: 'xs', + fontWeight: 500, + maxWidth: 'fit-content', + maxHeight: 'fit-content', + p: 'space.01', + textStyle: 'label.03', + }, + variants: { + variant: { + default: { + bg: 'ink.background-secondary', + border: '1px solid {colors.ink.border-transparent}', + color: 'ink.text-subdued', + }, + error: { + bg: 'red.background-primary', + border: '1px solid {colors.red.border}', + color: 'red.action-primary-default', + }, + info: { + bg: 'blue.background-primary', + border: '1px solid {colors.blue.border}', + color: 'blue.action-primary-default', + }, + success: { + bg: 'green.background-primary', + border: '1px solid {colors.green.border}', + color: 'green.action-primary-default', + }, + warning: { + bg: 'yellow.background-primary', + border: '1px solid {colors.yellow.border}', + color: 'yellow.action-primary-default', + }, + }, + + transparent: { true: { bg: 'transparent' } }, + }, + defaultVariants: { + variant: 'default', + }, +}); + +export type TagVariants = RecipeVariantProps; + +export interface TagProps extends HTMLStyledProps<'div'> { + icon?: React.JSX.Element; + label: string; +} +export function Tag({ icon, label, transparent, variant, ...props }: TagProps & TagVariants) { + return ( + + + {icon && icon} + {label} + + + ); +}