Skip to content

Commit

Permalink
feat(ui): #2053: implement AssetGroup component
Browse files Browse the repository at this point in the history
  • Loading branch information
VanishMax committed Feb 19, 2025
1 parent 59a15d9 commit 7c0885c
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 3 deletions.
33 changes: 33 additions & 0 deletions packages/ui/src/AssetIcon/group.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Meta, StoryObj } from '@storybook/react';

import {
PENUMBRA_METADATA,
PIZZA_METADATA,
USDC_METADATA,
} from '../utils/bufs';
import { AssetGroup } from '.';

const meta: Meta<typeof AssetGroup> = {
title: 'AssetIcon/Group',
component: AssetGroup,
tags: ['autodocs', '!dev'],
};
export default meta;

type Story = StoryObj<typeof AssetGroup>;

export const Overlay: Story = {
args: {
size: 'md',
variant: 'overlay',
assets: [PENUMBRA_METADATA, PIZZA_METADATA, USDC_METADATA],
},
};

export const Split: Story = {
args: {
size: 'md',
variant: 'split',
assets: [PENUMBRA_METADATA, USDC_METADATA],
},
};
48 changes: 48 additions & 0 deletions packages/ui/src/AssetIcon/group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Metadata } from '@penumbra-zone/protobuf/penumbra/core/asset/v1/asset_pb';
import { ElementType } from 'react';
import cn from 'clsx';
import { Size, AssetIcon } from './index';

export interface AssetGroupProps {
assets?: Metadata[];
as?: ElementType;
size?: Size;
variant?: 'overlay' | 'split';
}

const OVERLAY_SIZE_MAP: Record<Size, string> = {
lg: '[&>*:not(:first-child)]:-ml-4',
md: '[&>*:not(:first-child)]:-ml-3',
sm: '[&>*:not(:first-child)]:-ml-2',
};

const SPLIT_SIZE_MAP: Record<Size, string> = {
lg: 'w-4 max-w-4',
md: 'w-3 max-w-3',
sm: 'w-2 max-w-2',
};

export const AssetGroup = ({ assets, as: Container = 'div', size = 'md', variant = 'overlay' }: AssetGroupProps) => {
if (variant === 'split') {
return (<Container className={cn('relative flex items-center gap-[1px]')}>
{assets?.[0] && (
<div className={cn(SPLIT_SIZE_MAP[size], 'overflow-hidden')}>
<AssetIcon metadata={assets[0]} size={size} />
</div>
)}
{assets?.[1] && (
<div className={cn(SPLIT_SIZE_MAP[size], 'overflow-hidden [&>*]:-translate-x-1/2')}>
<AssetIcon metadata={assets[1]} size={size} />
</div>
)}
</Container>);
}

return (
<Container className={cn('relative flex items-center', OVERLAY_SIZE_MAP[size])}>
{assets?.map((asset, index) => (
<AssetIcon metadata={asset} key={index} size={size} zIndex={assets.length - index} />
))}
</Container>
)
};
1 change: 1 addition & 0 deletions packages/ui/src/AssetIcon/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '../utils/bufs';

const meta: Meta<typeof AssetIcon> = {
title: 'AssetIcon/Single',
component: AssetIcon,
tags: ['autodocs', '!dev'],
argTypes: {
Expand Down
10 changes: 7 additions & 3 deletions packages/ui/src/AssetIcon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import { DelegationTokenIcon } from './DelegationTokenIcon';
import { UnbondingTokenIcon } from './UnbondingTokenIcon';
import cn from 'clsx';

type Size = 'lg' | 'md' | 'sm';
export { AssetGroup, type AssetGroupProps } from './group';

export type Size = 'lg' | 'md' | 'sm';

const sizeMap: Record<Size, string> = {
lg: cn('w-8 h-8'),
Expand All @@ -18,9 +20,11 @@ const sizeMap: Record<Size, string> = {
export interface AssetIconProps {
size?: Size;
metadata?: Metadata;
/** Technical property, needed for `AssetGroup` component only */
zIndex?: number;
}

export const AssetIcon = ({ metadata, size = 'md' }: AssetIconProps) => {
export const AssetIcon = ({ metadata, size = 'md', zIndex }: AssetIconProps) => {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- possibly empty string
const icon = metadata?.images[0]?.png || metadata?.images[0]?.svg;
const display = getDisplay.optional(metadata);
Expand All @@ -43,7 +47,7 @@ export const AssetIcon = ({ metadata, size = 'md' }: AssetIconProps) => {
}

return (
<div className={cn(sizeMap[size], 'rounded-full overflow-hidden', '[&>*]:w-full [&>*]:h-full')}>
<div style={{ zIndex }} className={cn(sizeMap[size], 'rounded-full overflow-hidden', '[&>*]:w-full [&>*]:h-full')} title={metadata?.symbol}>
{assetIcon}
</div>
);
Expand Down

0 comments on commit 7c0885c

Please sign in to comment.