This commit is contained in:
2025-12-01 17:21:38 +08:00
parent 32fee2b8ab
commit fab8c13cb3
7511 changed files with 996300 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
@tailwind components;
@layer components {
.badge {
@apply inline-flex justify-center items-center text-text-tertiary border border-divider-deep
}
.badge-l {
@apply rounded-md gap-1 min-w-6
}
/* m is for the regular button */
.badge-m {
@apply rounded-md gap-[3px] min-w-5
}
.badge-s {
@apply rounded-[5px] gap-0.5 min-w-[18px]
}
.badge.badge-warning {
@apply text-text-warning border border-text-warning
}
.badge.badge-accent {
@apply text-text-accent-secondary border border-text-accent-secondary
}
}

View File

@@ -0,0 +1,73 @@
import type { Meta, StoryObj } from '@storybook/nextjs'
import Badge from '../badge'
const meta = {
title: 'Base/Data Display/Badge',
component: Badge,
parameters: {
docs: {
description: {
component: 'Compact label used for statuses and counts. Supports uppercase styling and optional red corner marks.',
},
source: {
language: 'tsx',
code: `
<Badge text="beta" />
`.trim(),
},
},
},
tags: ['autodocs'],
args: {
text: 'beta',
uppercase: true,
},
} satisfies Meta<typeof Badge>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {}
export const WithCornerMark: Story = {
args: {
text: 'new',
hasRedCornerMark: true,
},
parameters: {
docs: {
source: {
language: 'tsx',
code: `
<Badge text="new" hasRedCornerMark />
`.trim(),
},
},
},
}
export const CustomContent: Story = {
render: args => (
<Badge {...args} uppercase={false}>
<span className="flex items-center gap-1">
<span className="h-2 w-2 rounded-full bg-emerald-400" />
Production
</span>
</Badge>
),
parameters: {
docs: {
source: {
language: 'tsx',
code: `
<Badge uppercase={false}>
<span className="flex items-center gap-1">
<span className="h-2 w-2 rounded-full bg-emerald-400" />
Production
</span>
</Badge>
`.trim(),
},
},
},
}

View File

@@ -0,0 +1,81 @@
import type { CSSProperties, ReactNode } from 'react'
import React from 'react'
import { type VariantProps, cva } from 'class-variance-authority'
import classNames from '@/utils/classnames'
import './index.css'
enum BadgeState {
Warning = 'warning',
Accent = 'accent',
Default = '',
}
const BadgeVariants = cva(
'badge',
{
variants: {
size: {
s: 'badge-s',
m: 'badge-m',
l: 'badge-l',
},
},
defaultVariants: {
size: 'm',
},
},
)
type BadgeProps = {
size?: 's' | 'm' | 'l'
iconOnly?: boolean
uppercase?: boolean
state?: BadgeState
styleCss?: CSSProperties
children?: ReactNode
} & React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof BadgeVariants>
function getBadgeState(state: BadgeState) {
switch (state) {
case BadgeState.Warning:
return 'badge-warning'
case BadgeState.Accent:
return 'badge-accent'
default:
return ''
}
}
const Badge: React.FC<BadgeProps> = ({
className,
size,
state = BadgeState.Default,
iconOnly = false,
uppercase = false,
styleCss,
children,
...props
}) => {
return (
<div
className={classNames(
BadgeVariants({ size, className }),
getBadgeState(state),
size === 's'
? (iconOnly ? 'p-[3px]' : 'px-[5px] py-[3px]')
: size === 'l'
? (iconOnly ? 'p-1.5' : 'px-2 py-1')
: (iconOnly ? 'p-1' : 'px-[5px] py-[2px]'),
uppercase ? 'system-2xs-medium-uppercase' : 'system-2xs-medium',
)}
style={styleCss}
{...props}
>
{children}
</div>
)
}
Badge.displayName = 'Badge'
export default Badge
export { Badge, BadgeState, BadgeVariants }