Skip to content

Commit 1524c66

Browse files
committed
badge added
1 parent 08128e5 commit 1524c66

File tree

4 files changed

+231
-1
lines changed

4 files changed

+231
-1
lines changed

docs/components/ui/badge/index.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import React, { useState } from 'react';
2+
import {
3+
Pressable,
4+
Text,
5+
View,
6+
TouchableOpacityProps,
7+
StyleProp,
8+
ViewStyle,
9+
TextStyle,
10+
} from 'react-native';
11+
12+
import { cn } from '../../../lib/utils';
13+
14+
import { badgeClassNames, textClassNames } from './styles';
15+
16+
interface BadgeProps extends Omit<TouchableOpacityProps, 'style'> {
17+
variant?: 'default' | 'secondary' | 'destructive' | 'outline';
18+
className?: string;
19+
textClassName?: string;
20+
style?: StyleProp<ViewStyle>;
21+
textStyle?: StyleProp<TextStyle>;
22+
mode?: 'light' | 'dark';
23+
children: React.ReactNode;
24+
interactive?: boolean;
25+
}
26+
27+
const Badge: React.FC<BadgeProps> = ({
28+
children,
29+
variant = 'default',
30+
className = '',
31+
textClassName = '',
32+
style,
33+
mode = 'light',
34+
interactive = false,
35+
...props
36+
}) => {
37+
const [isPressed, setIsPressed] = useState(false);
38+
39+
const containerClasses = cn(
40+
badgeClassNames.base,
41+
badgeClassNames[`${mode}_variant_${variant}`],
42+
interactive && isPressed && badgeClassNames.pressed[variant],
43+
className
44+
);
45+
46+
const textClasses = cn(textClassNames.base, textClassNames[mode][variant], textClassName);
47+
48+
// Component selection based on interactive flag
49+
const Component = interactive ? Pressable : View;
50+
51+
// Only apply press handlers if interactive
52+
const interactiveProps = interactive
53+
? {
54+
onPressIn: () => setIsPressed(true),
55+
onPressOut: () => setIsPressed(false),
56+
android_ripple: { color: 'rgba(0, 0, 0, 0.1)' },
57+
...props,
58+
}
59+
: {};
60+
61+
return (
62+
<Component
63+
className={containerClasses}
64+
style={[
65+
style,
66+
interactive && isPressed && !badgeClassNames.pressed[variant] && { opacity: 0.95 },
67+
]}
68+
{...interactiveProps}
69+
>
70+
{typeof children === 'string' ? <Text className={textClasses}>{children}</Text> : children}
71+
</Component>
72+
);
73+
};
74+
75+
export { Badge };
76+
export default Badge;

docs/components/ui/badge/styles.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
export const badgeClassNames = {
2+
base: 'inline-flex items-center rounded-full px-2.5 py-0.5 border',
3+
4+
light_variant_default: 'border-transparent bg-primary',
5+
light_variant_secondary: 'border-transparent bg-secondary',
6+
light_variant_destructive: 'border-transparent bg-destructive',
7+
light_variant_outline: 'border-border',
8+
9+
dark_variant_default: 'border-transparent bg-dark-primary',
10+
dark_variant_secondary: 'border-transparent bg-dark-secondary',
11+
dark_variant_destructive: 'border-transparent bg-dark-destructive',
12+
dark_variant_outline: 'border-dark-border',
13+
14+
pressed: {
15+
default: 'opacity-80',
16+
secondary: 'opacity-80',
17+
destructive: 'opacity-80',
18+
outline: 'opacity-80',
19+
},
20+
};
21+
22+
export const textClassNames = {
23+
base: 'text-xs font-semibold',
24+
25+
light: {
26+
default: 'text-primary-foreground',
27+
secondary: 'text-secondary-foreground',
28+
destructive: 'text-white',
29+
outline: 'text-foreground',
30+
},
31+
32+
dark: {
33+
default: 'text-dark-primary-foreground',
34+
secondary: 'text-dark-secondary-foreground',
35+
destructive: 'text-white',
36+
outline: 'text-dark-foreground',
37+
},
38+
};

docs/pages/components/_meta.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"button": "Button",
55
"alert-dialog": "Alert Dialog",
66
"avatar": "Avatar",
7-
"skeleton": "Skeleton"
7+
"skeleton": "Skeleton",
8+
"badge": "Badge"
89
}

docs/pages/components/badge.mdx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
---
2+
title: "Badge Component | NativeCN UI"
3+
description: "Versatile badge component for React Native apps - display status indicators, labels, and interactive elements with customizable styles."
4+
---
5+
6+
# Badge Component
7+
8+
import { Badge } from '../../components/ui/badge'
9+
import ComponentPreview, { PreviewModeContext } from '../../components/ComponentPreview'
10+
import ComponentCode from '../../components/ComponentCode'
11+
import { useContext } from 'react'
12+
13+
export const BadgeWithMode = (props) => {
14+
const mode = useContext(PreviewModeContext) || 'light';
15+
return <Badge mode={mode} {...props} />;
16+
}
17+
18+
## Installation
19+
20+
The Badge component is used to highlight and display status, labels, or counts.
21+
22+
<ComponentCode
23+
language="bash"
24+
code="npx @nativecn/cli add badge"
25+
title="Installation Command"
26+
/>
27+
28+
## Basic Variants
29+
30+
<ComponentPreview
31+
title="Badge Variants"
32+
code={`// The mode prop will automatically be set to 'light' or 'dark' based on the preview container
33+
<Badge mode="..." variant="default">Default</Badge>
34+
<Badge mode="..." variant="secondary">Secondary</Badge>
35+
<Badge mode="..." variant="destructive">Destructive</Badge>
36+
<Badge mode="..." variant="outline">Outline</Badge>`}
37+
>
38+
<div style={{ display: 'flex', flexDirection: 'row', gap: '8px', flexWrap: 'wrap' }}>
39+
<BadgeWithMode>Default</BadgeWithMode>
40+
<BadgeWithMode variant="secondary">Secondary</BadgeWithMode>
41+
<BadgeWithMode variant="destructive">Destructive</BadgeWithMode>
42+
<BadgeWithMode variant="outline">Outline</BadgeWithMode>
43+
</div>
44+
</ComponentPreview>
45+
46+
## Interactive Badges
47+
48+
<ComponentPreview
49+
title="Interactive Badges"
50+
code={`<Badge mode="..." interactive>Interactive Default</Badge>
51+
<Badge mode="..." variant="secondary" interactive>Interactive Secondary</Badge>
52+
<Badge mode="..." variant="destructive" interactive>Interactive Destructive</Badge>
53+
<Badge mode="..." variant="outline" interactive>Interactive Outline</Badge>`}
54+
>
55+
<div style={{ display: 'flex', flexDirection: 'row', gap: '8px', flexWrap: 'wrap' }}>
56+
<BadgeWithMode interactive>Interactive Default</BadgeWithMode>
57+
<BadgeWithMode variant="secondary" interactive>Interactive Secondary</BadgeWithMode>
58+
<BadgeWithMode variant="destructive" interactive>Interactive Destructive</BadgeWithMode>
59+
<BadgeWithMode variant="outline" interactive>Interactive Outline</BadgeWithMode>
60+
</div>
61+
</ComponentPreview>
62+
63+
## Custom Styling
64+
65+
<ComponentPreview
66+
title="Custom Styled Badges"
67+
code={`<Badge mode="..." className="bg-blue-500">Custom Background</Badge>
68+
<Badge mode="..." textClassName="text-red-500">Custom Text Color</Badge>
69+
<Badge mode="..." className="border-2 border-green-500" variant="outline">Custom Border</Badge>`}
70+
>
71+
<div style={{ display: 'flex', flexDirection: 'row', gap: '8px', flexWrap: 'wrap' }}>
72+
<BadgeWithMode className="bg-blue-500">Custom Background</BadgeWithMode>
73+
<BadgeWithMode textClassName="text-red-500">Custom Text Color</BadgeWithMode>
74+
<BadgeWithMode className="border-2 border-green-500" variant="outline">Custom Border</BadgeWithMode>
75+
</div>
76+
</ComponentPreview>
77+
78+
## Usage Example
79+
80+
<ComponentCode
81+
title="Basic Badge Usage with Responsive Mode"
82+
code={`import { Badge } from '../components/ui/badge';
83+
import { useColorScheme } from 'react-native';
84+
85+
export function NotificationBadge({ count }) {
86+
const colorScheme = useColorScheme();
87+
88+
return (
89+
<Badge
90+
mode={colorScheme} // 'light' or 'dark' based on system preference
91+
variant="destructive"
92+
interactive
93+
>
94+
{count}
95+
</Badge>
96+
);
97+
}`}
98+
/>
99+
100+
## Reference
101+
102+
<ComponentCode
103+
title="Badge Props"
104+
language="typescript"
105+
code={`interface BadgeProps extends Omit<TouchableOpacityProps, 'style'> {
106+
variant?: 'default' | 'secondary' | 'destructive' | 'outline';
107+
className?: string;
108+
textClassName?: string;
109+
style?: StyleProp<ViewStyle>;
110+
textStyle?: StyleProp<TextStyle>;
111+
mode?: 'light' | 'dark'; // Controls light/dark appearance
112+
children: React.ReactNode;
113+
interactive?: boolean; // Makes the badge clickable
114+
}`}
115+
/>

0 commit comments

Comments
 (0)