feat(Badge): rewrite Badge component to new design
This commit is contained in:
@@ -9,8 +9,7 @@ const { Story } = defineMeta({
|
|||||||
parameters: {
|
parameters: {
|
||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
component:
|
component: 'Pill badge with border and optional status dot.',
|
||||||
'Small status indicator with color variants and size options. Use for displaying status, categories, or labels.',
|
|
||||||
},
|
},
|
||||||
story: { inline: false },
|
story: { inline: false },
|
||||||
},
|
},
|
||||||
@@ -19,58 +18,76 @@ const { Story } = defineMeta({
|
|||||||
argTypes: {
|
argTypes: {
|
||||||
variant: {
|
variant: {
|
||||||
control: 'select',
|
control: 'select',
|
||||||
options: ['default', 'success', 'warning', 'error', 'info'],
|
options: ['default', 'accent', 'success', 'warning', 'info'],
|
||||||
description: 'Color variant of the badge',
|
description: 'Badge variant',
|
||||||
|
defaultValue: 'default',
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
control: 'select',
|
control: 'select',
|
||||||
options: ['sm', 'md'],
|
options: ['xs', 'sm', 'md'],
|
||||||
description: 'Size of the badge',
|
description: 'Badge size',
|
||||||
|
defaultValue: 'xs',
|
||||||
|
},
|
||||||
|
dot: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Show status dot',
|
||||||
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<Story
|
||||||
import type { Snippet } from 'svelte';
|
name="Default variant"
|
||||||
</script>
|
args={{ variant: 'default', size: 'xs' }}
|
||||||
|
>
|
||||||
<Story name="Default">
|
{#snippet template()}
|
||||||
{#snippet template(args)}
|
<Badge variant="default" size="xs">Default</Badge>
|
||||||
<Badge variant="default" {...args}>Default</Badge>
|
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</Story>
|
</Story>
|
||||||
|
|
||||||
<Story name="Variants">
|
<Story
|
||||||
{#snippet template(args)}
|
name="Accent variant"
|
||||||
<div class="flex gap-2">
|
args={{ variant: 'accent', size: 'xs' }}
|
||||||
<Badge variant="default" {...args}>Default</Badge>
|
>
|
||||||
<Badge variant="success" {...args}>Success</Badge>
|
{#snippet template()}
|
||||||
<Badge variant="warning" {...args}>Warning</Badge>
|
<Badge variant="accent" size="xs">Accent</Badge>
|
||||||
<Badge variant="error" {...args}>Error</Badge>
|
|
||||||
<Badge variant="info" {...args}>Info</Badge>
|
|
||||||
</div>
|
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</Story>
|
</Story>
|
||||||
|
|
||||||
<Story name="Sizes">
|
<Story
|
||||||
{#snippet template(args)}
|
name="Success variant"
|
||||||
<div class="flex items-center gap-2">
|
args={{ variant: 'success', size: 'xs' }}
|
||||||
<Badge size="sm" {...args}>Small</Badge>
|
>
|
||||||
<Badge size="md" {...args}>Medium</Badge>
|
{#snippet template()}
|
||||||
</div>
|
<Badge variant="success" size="xs">Success</Badge>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</Story>
|
</Story>
|
||||||
|
|
||||||
<Story name="All Combinations">
|
<Story
|
||||||
{#snippet template(args)}
|
name="Warning variant"
|
||||||
<div class="space-y-2">
|
args={{ variant: 'warning', size: 'xs' }}
|
||||||
{#each ['default', 'success', 'warning', 'error', 'info'] as variant}
|
>
|
||||||
<div class="flex gap-2">
|
{#snippet template()}
|
||||||
<Badge {variant} size="sm" {...args}>{variant} sm</Badge>
|
<Badge variant="warning" size="xs">Warning</Badge>
|
||||||
<Badge {variant} size="md" {...args}>{variant} md</Badge>
|
{/snippet}
|
||||||
</div>
|
</Story>
|
||||||
{/each}
|
|
||||||
|
<Story
|
||||||
|
name="Info variant"
|
||||||
|
args={{ variant: 'info', size: 'xs' }}
|
||||||
|
>
|
||||||
|
{#snippet template()}
|
||||||
|
<Badge variant="info" size="xs">Info</Badge>
|
||||||
|
{/snippet}
|
||||||
|
</Story>
|
||||||
|
|
||||||
|
<Story name="All sizes">
|
||||||
|
{#snippet template()}
|
||||||
|
<div class="flex gap-3 items-center">
|
||||||
|
<Badge variant="default" size="xs">XS</Badge>
|
||||||
|
<Badge variant="default" size="sm">SM</Badge>
|
||||||
|
<Badge variant="default" size="md">MD</Badge>
|
||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</Story>
|
</Story>
|
||||||
|
|||||||
@@ -1,40 +1,56 @@
|
|||||||
<!--
|
<!--
|
||||||
Component: Badge
|
Component: Badge
|
||||||
Small status indicator with color variants and size options
|
Pill badge with border and optional status dot.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||||
|
import {
|
||||||
|
type LabelSize,
|
||||||
|
labelSizeConfig,
|
||||||
|
} from '$shared/ui/Label/config';
|
||||||
|
import type { Snippet } from 'svelte';
|
||||||
|
|
||||||
|
type BadgeVariant = 'default' | 'accent' | 'success' | 'warning' | 'info';
|
||||||
|
|
||||||
|
const badgeVariantConfig: Record<BadgeVariant, string> = {
|
||||||
|
default: 'border-black/10 dark:border-white/10 text-neutral-500',
|
||||||
|
accent: 'bg-brand/10 border-brand/20 text-brand',
|
||||||
|
success: 'bg-green-500/10 border-green-500/20 text-green-600 dark:text-green-400',
|
||||||
|
warning: 'bg-yellow-500/10 border-yellow-500/20 text-yellow-600 dark:text-yellow-400',
|
||||||
|
info: 'bg-blue-500/10 border-blue-500/20 text-blue-600 dark:text-blue-400',
|
||||||
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/**
|
variant?: BadgeVariant;
|
||||||
* Color variant of the badge
|
size?: LabelSize;
|
||||||
* @default default
|
/** Renders a small filled circle before the text. */
|
||||||
*/
|
dot?: boolean;
|
||||||
variant?: 'default' | 'success' | 'warning' | 'error' | 'info';
|
children?: Snippet;
|
||||||
/**
|
class?: string;
|
||||||
* Size of the badge
|
|
||||||
* @default md
|
|
||||||
*/
|
|
||||||
size?: 'sm' | 'md';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { variant = 'default', size = 'md' }: Props = $props();
|
let {
|
||||||
|
variant = 'default',
|
||||||
const baseClasses = 'inline-flex items-center justify-center rounded-full font-medium';
|
size = 'xs',
|
||||||
|
dot = false,
|
||||||
const variantClasses = $derived(
|
children,
|
||||||
variant === 'success'
|
class: className,
|
||||||
? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'
|
}: Props = $props();
|
||||||
: variant === 'warning'
|
|
||||||
? 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400'
|
|
||||||
: variant === 'error'
|
|
||||||
? 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'
|
|
||||||
: variant === 'info'
|
|
||||||
? 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400'
|
|
||||||
: 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300',
|
|
||||||
);
|
|
||||||
|
|
||||||
const sizeClasses = $derived(size === 'sm' ? 'px-2 py-0.5 text-xs' : 'px-2.5 py-0.5 text-sm');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span class="{baseClasses} {variantClasses} {sizeClasses}">
|
<span
|
||||||
<slot />
|
class={cn(
|
||||||
|
'inline-flex items-center gap-1 px-2 py-0.5 border rounded-full',
|
||||||
|
'font-mono uppercase tracking-wide',
|
||||||
|
labelSizeConfig[size],
|
||||||
|
badgeVariantConfig[variant],
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{#if dot}
|
||||||
|
<span class="w-1 h-1 rounded-full bg-current"></span>
|
||||||
|
{/if}
|
||||||
|
{#if children}
|
||||||
|
{@render children()}
|
||||||
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user