diff --git a/packages/ui/src/Button/helpers.ts b/packages/ui/src/Button/helpers.ts index 52e60b9711..0899d87bf6 100644 --- a/packages/ui/src/Button/helpers.ts +++ b/packages/ui/src/Button/helpers.ts @@ -1,5 +1,5 @@ import { DefaultTheme } from 'styled-components'; -import { Variant, ActionType } from './types'; +import { Variant, ActionType } from '../utils/button'; export const getBackgroundColor = ( actionType: ActionType, diff --git a/packages/ui/src/Button/index.tsx b/packages/ui/src/Button/index.tsx index 5cab0001d7..c5b02e7715 100644 --- a/packages/ui/src/Button/index.tsx +++ b/packages/ui/src/Button/index.tsx @@ -1,7 +1,7 @@ import { MouseEventHandler } from 'react'; import styled, { css, DefaultTheme } from 'styled-components'; import { asTransientProps } from '../utils/asTransientProps'; -import { Size, Variant, ActionType } from './types'; +import { Size, Variant, ActionType } from '../utils/button'; import { getBackgroundColor } from './helpers'; import { button } from '../utils/typography'; import { LucideIcon } from 'lucide-react'; diff --git a/packages/ui/src/ButtonGroup/index.stories.tsx b/packages/ui/src/ButtonGroup/index.stories.tsx new file mode 100644 index 0000000000..a3d9e55011 --- /dev/null +++ b/packages/ui/src/ButtonGroup/index.stories.tsx @@ -0,0 +1,36 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ButtonGroup } from '.'; +import { Ban, HandCoins, Send } from 'lucide-react'; + +const meta: Meta = { + component: ButtonGroup, + tags: ['autodocs', '!dev'], + argTypes: { + buttons: { control: false }, + }, +}; +export default meta; + +type Story = StoryObj; + +export const Basic: Story = { + args: { + actionType: 'default', + size: 'sparse', + buttons: [ + { + label: 'Delegate', + icon: Send, + }, + { + label: 'Undelegate', + icon: HandCoins, + }, + { + label: 'Cancel', + icon: Ban, + }, + ], + }, +}; diff --git a/packages/ui/src/ButtonGroup/index.tsx b/packages/ui/src/ButtonGroup/index.tsx new file mode 100644 index 0000000000..ebd1538886 --- /dev/null +++ b/packages/ui/src/ButtonGroup/index.tsx @@ -0,0 +1,76 @@ +import { LucideIcon } from 'lucide-react'; +import { MouseEventHandler } from 'react'; +import { ActionType, Size } from '../utils/button'; +import { Button } from '../Button'; +import styled from 'styled-components'; +import { media } from '../utils/media'; + +const Root = styled.div<{ $size: Size }>` + display: flex; + flex-direction: ${props => (props.$size === 'sparse' ? 'column' : 'row')}; + gap: ${props => props.theme.spacing(2)}; + + ${props => media.tablet` + flex-direction: row; + gap: ${props.theme.spacing(props.$size === 'sparse' ? 4 : 2)}; + `} +`; + +const ButtonWrapper = styled.div<{ $size: Size }>` + flex-grow: ${props => (props.$size === 'sparse' ? 1 : 0)}; + flex-shrink: ${props => (props.$size === 'sparse' ? 1 : 0)}; +`; + +interface ButtonDescription { + label: string; + icon?: LucideIcon; + onClick?: MouseEventHandler; +} + +export interface ButtonGroupProps { + /** + * An array of objects, each describing a button to render. The first will be + * rendered with the `primary` variant, the rest with the `secondary` variant. + * + * Minimum length: 1. Maximum length: 3. + */ + buttons: + | [ButtonDescription] + | [ButtonDescription, ButtonDescription] + | [ButtonDescription, ButtonDescription, ButtonDescription]; + /** + * The action type of the button group. Will be used for all buttons in the + * group. + */ + actionType?: ActionType; + /** Will be used for all buttons in the group. */ + size?: Size; +} + +/** + * Use a `` to render multiple buttons in a group with the same + * `actionType` and `size`. + * + * When rendering multiple Penumbra UI buttons together, always use a `` rather than individual ` + + ))} + +); diff --git a/packages/ui/src/Button/types.ts b/packages/ui/src/utils/button.ts similarity index 100% rename from packages/ui/src/Button/types.ts rename to packages/ui/src/utils/button.ts