Skip to content

Commit

Permalink
u button
Browse files Browse the repository at this point in the history
  • Loading branch information
prichodko committed Sep 18, 2024
1 parent 5484770 commit 297284d
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 79 deletions.
77 changes: 45 additions & 32 deletions packages/components/src/_components/button/button.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,70 @@
import { PlaceholderIcon } from '@status-im/icons/20'

import { Button } from './button'

import type { Meta, StoryObj } from '@storybook/react'

const sizes = ['40', '32', '24'] as const

// eslint-disable-next-line react/display-name
const renderVariant = (variant: string) => (props: any) => (
<div className="flex items-center gap-4">
{sizes.map(size => (
<Button key={size} {...props} variant={variant} size={size} />
))}
</div>
)
type Story = StoryObj<typeof Button>

const meta = {
const meta: Meta<
typeof Button & { showIconBefore: boolean; showIconAfter: boolean }
> = {
component: Button,
title: 'Components/Button',
args: {
children: 'Button',
// isDisabled: false,
iconBefore: <PlaceholderIcon />,
iconAfter: <PlaceholderIcon />,
},

parameters: {
design: {
type: 'figma',
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=4%3A32&mode=dev',
},
},
argTypes: {
children: {
control: 'text',
},
disabled: {
control: 'boolean',
},
iconBefore: {
control: 'boolean',
},
iconAfter: {
control: 'boolean',
},
},

render: props => (
args: {
children: 'Button',
disabled: false,
// variant: 'primary',
// size: '40',
},
render: args => (
<div className="grid gap-4">
{renderVariant('primary')(props)}
{renderVariant('positive')(props)}
{renderVariant('grey')(props)}
{renderVariant('darkGrey')(props)}
{renderVariant('outline')(props)}
{renderVariant('ghost')(props)}
{renderVariant('danger')(props)}
{(
[
'primary',
'positive',
'grey',
'darkGrey',
'outline',
'ghost',
'danger',
] as const
).map(variant => (
<div key={variant} className="flex items-center gap-4">
{(['40', '32', '24'] as const).map(size => (
<Button key={size} {...args} variant={variant} size={size} />
))}
</div>
))}
</div>
),
} satisfies Meta<typeof Button>
}

type Story = StoryObj<typeof Button>
export default meta

export const Light: Story = {}

export const Dark: Story = {
parameters: {
backgrounds: { default: 'dark' },
},
}

export default meta
83 changes: 43 additions & 40 deletions packages/components/src/_components/button/button.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,70 @@
'use client'

import { cloneElement, forwardRef } from 'react'

import { cva } from 'cva'
import { Button as AriaButton, Link as AriaLink } from 'react-aria-components'

import type { IconComponent } from '../types'
import { useConfig } from '../provider'

import type { IconElement, Prettify } from '../types'
import type { VariantProps } from 'cva'
import type { Ref } from 'react'
import type { ButtonProps as AriaButtonProps } from 'react-aria-components'

type Variants = VariantProps<typeof styles>

type Props = {
size?: Variants['size']
variant?: Variants['variant']
onClick?: () => void
onPress?: AriaButtonProps['onPress']
disabled?: boolean
// isDisabled?: boolean
onPress?: () => void
} & (
| {
children: React.ReactNode
iconBefore?: IconComponent
iconAfter?: IconComponent
iconBefore?: IconElement
iconAfter?: IconElement
}
| {
icon: IconComponent
ariaLabel: string
icon: IconElement
'aria-label': string
children?: never
}
)

const Button = (props: Props, ref: Ref<HTMLButtonElement>) => {
const {
size = '40',
variant = 'primary',
onClick: onPress,
...buttonProps
} = props
type ButtonProps = Prettify<
Omit<React.ComponentPropsWithoutRef<'button'>, 'children'> & { href?: never }
>

type LinkProps = Prettify<
Omit<React.ComponentPropsWithoutRef<'a'>, 'children'> & { href: string }
>

function Button(
props: Props & (ButtonProps | LinkProps),
ref: React.Ref<HTMLButtonElement | HTMLAnchorElement>,
) {
const { size = '40', variant = 'primary' } = props

const { link } = useConfig()

const Element = props.href ? link : 'button'

// icon only
if ('icon' in props) {
const { icon: Icon } = props
const { icon: Icon, ...buttonProps } = props
return (
<AriaButton
onPress={onPress}
<Element
{...buttonProps}
ref={ref}
className={styles({ variant, size, iconOnly: true })}
>
{cloneElement(Icon, {
className: iconStyles({ size, variant }),
})}
</AriaButton>
</Element>
)
}

const { children, iconBefore, iconAfter } = props
const { children, iconBefore, iconAfter, ...buttonProps } = props

return (
<AriaButton
onPress={onPress}
{...buttonProps}
ref={ref}
className={styles({ variant, size })}
>
<Element {...buttonProps} ref={ref} className={styles({ variant, size })}>
{iconBefore && (
<span className={iconStyles({ size, placement: 'before', variant })}>
{iconBefore}
Expand All @@ -74,16 +76,15 @@ const Button = (props: Props, ref: Ref<HTMLButtonElement>) => {
{iconAfter}
</span>
)}
</AriaButton>
</Element>
)
}

const styles = cva({
base: [
'inline-flex shrink-0 cursor-pointer items-center justify-center gap-1 font-medium transition-all',
'outline-none focus:outline-none focus-visible:ring-2 focus-visible:ring-customisation-50 focus-visible:ring-offset-2',
'disabled:cursor-default disabled:opacity-30',
// 'flex cursor-pointer items-center gap-3 px-4 py-[10px] text-13 text-white-100 transition-all',
'outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-customisation-50 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-neutral-100',
'disabled:pointer-events-none disabled:cursor-default disabled:opacity-30',
],
variants: {
variant: {
Expand All @@ -97,12 +98,15 @@ const styles = cva({
],
positive: [
'bg-success-50 text-white-100 hover:bg-success-60 focus-visible:ring-success-50 disabled:bg-success-50/30',
'dark:bg-success-60 dark:hover:bg-success-50 dark:pressed:bg-success-50/90',
],
grey: [
'bg-neutral-10 text-neutral-100 hover:bg-neutral-20 focus-visible:ring-neutral-80 pressed:bg-neutral-30',
'dark:bg-neutral-80 dark:text-white-100 dark:hover:bg-neutral-60 dark:pressed:bg-neutral-50',
],
darkGrey: [
'bg-neutral-20 text-neutral-100 hover:bg-neutral-30 focus-visible:ring-neutral-80 pressed:bg-neutral-40',
'dark:bg-neutral-90 dark:text-white-100 dark:hover:bg-neutral-60 dark:pressed:bg-neutral-50',
],
outline: [
'border border-neutral-30 text-neutral-100 hover:border-neutral-40 focus-visible:ring-neutral-80 pressed:border-neutral-50',
Expand All @@ -113,8 +117,8 @@ const styles = cva({
'dark:text-white-100 dark:hover:bg-neutral-80 dark:pressed:bg-neutral-70',
],
danger: [
'bg-danger-50 text-white-100 hover:bg-danger-60 focus-visible:ring-danger-50',
'dark:bg-danger-60 dark:hover:bg-danger-50',
'bg-danger-50 text-white-100 hover:bg-danger-60 focus-visible:ring-danger-50 pressed:bg-danger-60/90',
'dark:bg-danger-60 dark:hover:bg-danger-50 dark:pressed:bg-danger-50/90',
],
},
size: {
Expand All @@ -129,7 +133,7 @@ const styles = cva({
})

const iconStyles = cva({
base: 'shrink-0',
base: ['shrink-0', '[&>svg]:size-full'],
variants: {
variant: {
primary: 'text-blur-white/70',
Expand Down Expand Up @@ -176,5 +180,4 @@ const iconStyles = cva({

const _Button = forwardRef(Button)

export { _Button as Button, styles as buttonStyles }
export type { Props as ButtonProps }
export { _Button as Button }
3 changes: 1 addition & 2 deletions packages/components/src/_components/button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export type { ButtonProps } from './button'
export { Button, buttonStyles } from './button'
export { Button } from './button'
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ export default {
children: {
control: 'text',
},
isDisabled: {
disabled: {
control: 'boolean',
},
},
args: {
children: 'Dropdown',
isDisabled: false,
disabled: false,
},

render: args => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { cva } from 'cva'

import { Button } from '../button'

import type { ButtonProps } from '../button'
import type { Ref } from 'react'
type ButtonProps = React.ComponentPropsWithoutRef<typeof Button>

type Props = ButtonProps & {
variant?: Extract<
Expand All @@ -17,7 +16,7 @@ type Props = ButtonProps & {
children: React.ReactNode
}

const DropdownButton = (props: Props, ref: Ref<HTMLButtonElement>) => {
const DropdownButton = (props: Props, ref: React.Ref<HTMLButtonElement>) => {
const { size = '40', variant = 'primary', children, ...buttonProps } = props

return (
Expand Down
4 changes: 4 additions & 0 deletions packages/components/src/_components/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export type IconElement = React.ReactElement<
React.ComponentPropsWithoutRef<'svg'>
>

export type Prettify<T> = {
[K in keyof T]: T[K]
} & {} // eslint-disable-line @typescript-eslint/ban-types

0 comments on commit 297284d

Please sign in to comment.