Skip to content

Commit

Permalink
feat: rework Badge add new colour themes and overflow types
Browse files Browse the repository at this point in the history
  • Loading branch information
LimeWub committed Jul 28, 2023
1 parent 71aa5ed commit 15cabe0
Show file tree
Hide file tree
Showing 7 changed files with 1,276 additions and 153 deletions.
80 changes: 72 additions & 8 deletions documentation/content/components.feedback.badge.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,77 @@ tabs:
<CodeBlock live={true} preview={true} code={`<Badge>New Data</Badge>`} language={"tsx"} />
## Theme
## Text overflow
<CodeBlock live={true} preview={true} code={`<Tooltip.Provider>
<Badge css={{width: 100}} overflow="ellipsis">
<Icon is={Wifi} /> New Data New Data New Data New Data New Data</Badge>
</Tooltip.Provider>`} language={"tsx"} />
These are the available `theme`s for this component: `success`, `warning`, `danger`, `neutral`, `info` and `purple`. The default is `info`
## Emphasis
<CodeBlock live={true} preview={true} code={`<Stack gap={3}>
<Badge>Info</Badge>
<Badge theme="neutral">Neutral</Badge>
<Badge theme="success">Success</Badge>
<Badge theme="warning">Warning</Badge>
<Badge theme="danger">Danger</Badge>
<Badge theme="purple">Purple</Badge>
<Badge emphasis="subtle">Badge</Badge>
<Badge emphasis="bold">Badge</Badge>
</Stack>`} language={"tsx"} />
## Theme
The following `theme`s semantic themes are available: `success`, `warning`, `danger`, `neutral`, `info`.
Moreover, any colour that has been set up as a ColorScheme accent is also available to use. Currently the available non-semantic colours are `grey`, `blue`, `purple`, `cyan`, `green`, `magenta`, `red`, `teal`, `orange`, `yellow`, `lime`.
If NO theme is passed in. The badge will attempt to pick up a colour based on the accent of the closest parent with a `ColorScheme`.
<CodeBlock live={true} preview={true} code={`<Stack gap={3} direction="column" align="center">
<Badge>Picks up from ColorScheme</Badge>
<Stack gap={3}>
<Badge theme="info">Info</Badge>
<Badge theme="neutral">Neutral</Badge>
<Badge theme="success">Success</Badge>
<Badge theme="warning">Warning</Badge>
<Badge theme="danger">Danger</Badge>
</Stack>
<Stack gap={3}>
<Badge theme="grey">Grey</Badge>
<Badge theme="blue">Blue</Badge>
<Badge theme="purple">Purple</Badge>
<Badge theme="cyan">Cyan</Badge>
<Badge theme="green">Green</Badge>
<Badge theme="magenta">Magenta</Badge>
<Badge theme="red">Red</Badge>
<Badge theme="teal">Teal</Badge>
<Badge theme="orange">Orange</Badge>
<Badge theme="yellow">Yellow</Badge>
<Badge theme="lime">Lime</Badge>
</Stack>
<Badge emphasis="bold">Picks up from ColorScheme</Badge>
<Stack gap={3}>
<Badge theme="info" emphasis="bold">Info</Badge>
<Badge theme="neutral" emphasis="bold">Neutral</Badge>
<Badge theme="success" emphasis="bold">Success</Badge>
<Badge theme="warning" emphasis="bold">Warning</Badge>
<Badge theme="danger" emphasis="bold">Danger</Badge>
</Stack>
<Stack gap={3}>
<Badge theme="grey" emphasis="bold">Grey</Badge>
<Badge theme="blue" emphasis="bold">Blue</Badge>
<Badge theme="purple" emphasis="bold">Purple</Badge>
<Badge theme="cyan" emphasis="bold">Cyan</Badge>
<Badge theme="green" emphasis="bold">Green</Badge>
<Badge theme="magenta" emphasis="bold">Magenta</Badge>
<Badge theme="red" emphasis="bold">Red</Badge>
<Badge theme="teal" emphasis="bold">Teal</Badge>
<Badge theme="orange" emphasis="bold">Orange</Badge>
<Badge theme="yellow" emphasis="bold">Yellow</Badge>
<Badge theme="lime" emphasis="bold">Lime</Badge>
</Stack>
</Stack>`} language={"tsx"} />
Expand Down Expand Up @@ -55,6 +113,12 @@ tabs:
<ComponentProps component="Badge" />
<ComponentProps component="Badge.Icon" />
<ComponentProps component="Badge.Text" />
- title: Visual
content: >-
## Structure
Expand Down
29 changes: 29 additions & 0 deletions lib/src/components/badge/Badge.context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from 'react'

import type { TBadgeProps } from './Badge'

type TBadgeProviderProps = {
size?: TBadgeProps['size']
overflow?: TBadgeProps['overflow']
}

type TBadgeContext = TBadgeProviderProps & {
isOverflowing?: boolean
setIsOverflowing?: React.Dispatch<React.SetStateAction<boolean>>
}

export const BadgeContext = React.createContext<TBadgeContext>({})

export const BadgeProvider: React.FC<TBadgeProviderProps> = ({
size,
overflow,
children
}) => {
const [isOverflowing, setIsOverflowing] = React.useState(false)

const value = React.useMemo<TBadgeContext>(
() => ({ size, overflow, isOverflowing, setIsOverflowing }),
[size, overflow, isOverflowing, setIsOverflowing]
)
return <BadgeContext.Provider value={value}>{children}</BadgeContext.Provider>
}
166 changes: 96 additions & 70 deletions lib/src/components/badge/Badge.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import * as React from 'react'

import { Flex } from '~/components/flex'
import { Icon } from '~/components/icon'
import { ColorScheme, TcolorScheme } from '~/experiments/color-scheme'
import { styled } from '~/stitches'
import { OptionalTooltipWrapper } from '~/utilities/optional-tooltip-wrapper'
import { useCallbackRefState } from '~/utilities/hooks/useCallbackRef'
import { BadgeText } from './BadgeText'
import { BadgeIcon } from './BadgeIcon'
import { BadgeContext, BadgeProvider } from './Badge.context'

import { Box } from '../box'
import { Flex } from '../flex'
import { Icon } from '../icon'
import { colorSchemes as badgeColorSchemes } from './stitches.badge.colorscheme.config'

const StyledBadge = styled(Flex, {
justifyContent: 'center',
Expand All @@ -17,90 +23,110 @@ const StyledBadge = styled(Flex, {
mr: '$1'
},
variants: {
theme: {
success: {
bg: '$successLight',
color: '$successMid'
emphasis: {
subtle: {
color: '$textSubtle',
background: '$backgroundSubtle'
},
warning: {
bg: '$warningLight',
color: '$warningText'
},
danger: {
bg: '$dangerLight',
color: '$dangerMid'
},
neutral: {
bg: '$tonal50',
color: '$tonal400'
},
info: {
bg: '$primaryLight',
color: '$primaryMid'
},
purple: {
bg: '$purple200',
color: '$purple1000'
bold: {
color: '$textBold',
background: '$backgroundBold'
}
},
size: {
xs: {
fontSize: '$sm',
px: '$1',
height: '$2'
px: '$1'
},
sm: {
fontSize: '$md',
px: '$1',
height: '$3'
py: '$0'
},
md: {
fontSize: '$md',
px: '$2',
height: '$4'
py: '$1'
}
}
}
})

type BadgeProps = React.ComponentProps<typeof StyledBadge>

export const Badge: React.FC<BadgeProps> = ({
theme = 'info',
size = 'sm',
children,
...rest
}) => {
return (
<StyledBadge role="status" theme={theme} size={size} {...rest}>
{React.Children.map(children, (child) => {
if (typeof child === 'string' || typeof child === 'number') {
return (
<Box
css={{
whiteSpace: 'nowrap',
overflowX: 'hidden',
textOverflow: 'ellipsis',
py: '$0'
}}
>
{child}
</Box>
)
}
export type TBadgeProps = React.ComponentProps<typeof StyledBadge> & {
theme?: keyof typeof badgeColorSchemes
overflow?: React.ComponentProps<typeof BadgeText>['overflow']
colorScheme?: TcolorScheme
}

if (React.isValidElement(child) && child.type === Icon) {
return React.cloneElement(
child as React.ReactElement<React.ComponentProps<typeof Icon>>,
{
size: 'sm',
css: { ...child.props.css, flexShrink: 0 }
}
)
}
})}
</StyledBadge>
)
type TBadge = React.ForwardRefExoticComponent<TBadgeProps> & {
Icon: typeof BadgeIcon
Text: typeof BadgeText
}

type TBadgeInnerProps = Omit<Omit<TBadgeProps, 'size'>, 'overflow'>

const BadgeInner = React.forwardRef<HTMLDivElement, TBadgeInnerProps>(
({ theme = 'non-semantic', emphasis = 'subtle', children, ...rest }, ref) => {
const { size, overflow, isOverflowing } = React.useContext(BadgeContext)
const [badgeElRef, setBadgeElRef] = useCallbackRefState()
React.useImperativeHandle(ref, () => badgeElRef as HTMLInputElement)

const isSemanticTheme = [
'info',
'success',
'neutral',
'warning',
'danger'
].includes(theme)

const label = badgeElRef?.textContent

return (
<OptionalTooltipWrapper
hasTooltip={overflow === 'ellipsis' && isOverflowing}
label={label}
>
<ColorScheme
className={
badgeColorSchemes[isSemanticTheme ? theme : 'non-semantic']
}
accent={
isSemanticTheme || !theme
? undefined
: (`${theme}2` as TcolorScheme['accent'])
}
asChild
>
<StyledBadge
role="status"
emphasis={emphasis}
size={size}
{...rest}
ref={setBadgeElRef}
>
{React.Children.map(children, (child) => {
if (typeof child === 'string' || typeof child === 'number') {
return <BadgeText>{child}</BadgeText>
}
if (React.isValidElement(child) && child.type === Icon) {
return <BadgeIcon {...child.props} />
}
return child
})}
</StyledBadge>
</ColorScheme>
</OptionalTooltipWrapper>
)
}
)

export const Badge = React.forwardRef<HTMLDivElement, TBadgeProps>(
({ size = 'sm', overflow = 'wrap', ...rest }, ref) => {
return (
<BadgeProvider size={size} overflow={overflow}>
<BadgeInner {...rest} ref={ref} />
</BadgeProvider>
)
}
) as TBadge

Badge.displayName = 'Badge'
Badge.Icon = BadgeIcon
Badge.Text = BadgeText
9 changes: 9 additions & 0 deletions lib/src/components/badge/BadgeIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as React from 'react'

import { Icon } from '~/components/icon'
import { styled } from '~/stitches'

const StyledBadgeIcon = styled(Icon, { flexShrink: 0 })
export const BadgeIcon: React.FC<
React.ComponentProps<typeof StyledBadgeIcon>
> = (props) => <StyledBadgeIcon size="sm" {...props} />
Loading

0 comments on commit 15cabe0

Please sign in to comment.