@beyondcorp/beyond-ui-native 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/index.js +78 -61
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +78 -61
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +11 -5
  6. package/src/components/accordion/accordion.tsx +245 -0
  7. package/src/components/accordion/index.ts +1 -0
  8. package/src/components/alert/alert.tsx +118 -0
  9. package/src/components/alert/index.ts +1 -0
  10. package/src/components/avatar/avatar.tsx +125 -0
  11. package/src/components/avatar/index.ts +1 -0
  12. package/src/components/badge/badge.tsx +89 -0
  13. package/src/components/badge/index.ts +1 -0
  14. package/src/components/bottom-sheet/bottom-sheet.tsx +336 -0
  15. package/src/components/bottom-sheet/index.ts +1 -0
  16. package/src/components/button/button.tsx +172 -0
  17. package/src/components/button/index.ts +1 -0
  18. package/src/components/card/card.tsx +121 -0
  19. package/src/components/card/index.ts +1 -0
  20. package/src/components/checkbox/checkbox.tsx +100 -0
  21. package/src/components/checkbox/index.ts +1 -0
  22. package/src/components/data-table/data-table.tsx +276 -0
  23. package/src/components/data-table/index.ts +1 -0
  24. package/src/components/dialog/dialog.tsx +278 -0
  25. package/src/components/dialog/index.ts +1 -0
  26. package/src/components/input/index.ts +1 -0
  27. package/src/components/input/input.tsx +130 -0
  28. package/src/components/radio/index.ts +1 -0
  29. package/src/components/radio/radio.tsx +117 -0
  30. package/src/components/select/index.ts +1 -0
  31. package/src/components/select/select.tsx +377 -0
  32. package/src/components/separator/index.ts +1 -0
  33. package/src/components/separator/separator.tsx +28 -0
  34. package/src/components/skeleton/index.ts +1 -0
  35. package/src/components/skeleton/skeleton.tsx +43 -0
  36. package/src/components/spinner/index.ts +1 -0
  37. package/src/components/spinner/spinner.tsx +46 -0
  38. package/src/components/stats-card/index.ts +1 -0
  39. package/src/components/stats-card/stats-card.tsx +92 -0
  40. package/src/components/switch/index.ts +1 -0
  41. package/src/components/switch/switch.tsx +80 -0
  42. package/src/components/tabs/index.ts +1 -0
  43. package/src/components/tabs/tabs.tsx +172 -0
  44. package/src/components/text/heading.tsx +50 -0
  45. package/src/components/text/index.ts +2 -0
  46. package/src/components/text/text.tsx +62 -0
  47. package/src/components/textarea/index.ts +1 -0
  48. package/src/components/textarea/textarea.tsx +87 -0
  49. package/src/components/toast/index.ts +2 -0
  50. package/src/components/toast/toast.tsx +136 -0
  51. package/src/components/toast/types.ts +32 -0
  52. package/src/index.ts +42 -0
  53. package/src/primitives/box.tsx +25 -0
  54. package/src/primitives/index.ts +2 -0
  55. package/src/primitives/pressable.tsx +49 -0
  56. package/src/providers/BeyondUIProvider.tsx +119 -0
  57. package/src/templates/auth/ForgotPasswordTemplate.tsx +132 -0
  58. package/src/templates/auth/SignInTemplate.tsx +213 -0
  59. package/src/templates/auth/SignUpTemplate.tsx +233 -0
  60. package/src/templates/auth/index.ts +3 -0
  61. package/src/templates/dashboard/DashboardTemplate.tsx +204 -0
  62. package/src/templates/dashboard/index.ts +1 -0
  63. package/src/templates/index.ts +3 -0
  64. package/src/templates/settings/SettingsTemplate.tsx +226 -0
  65. package/src/templates/settings/index.ts +1 -0
  66. package/src/theme/preset.ts +45 -0
  67. package/src/theme/tokens.ts +144 -0
  68. package/src/types/nativewind-env.d.ts +26 -0
  69. package/src/utilities/accessibility.ts +43 -0
  70. package/src/utilities/cn.ts +9 -0
  71. package/src/utilities/index.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beyondcorp/beyond-ui-native",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Accessible, NativeWind-first React Native component library for Beyond Softwares",
5
5
  "author": "Beyond Corp",
6
6
  "license": "MIT",
@@ -28,12 +28,15 @@
28
28
  ],
29
29
  "main": "./dist/index.js",
30
30
  "module": "./dist/index.mjs",
31
+ "react-native": "./src/index.ts",
31
32
  "types": "./dist/index.d.ts",
32
33
  "exports": {
33
34
  ".": {
34
35
  "types": "./dist/index.d.ts",
35
- "import": "./dist/index.mjs",
36
- "require": "./dist/index.js"
36
+ "react-native": "./src/index.ts",
37
+ "import": "./src/index.ts",
38
+ "require": "./dist/index.js",
39
+ "default": "./src/index.ts"
37
40
  },
38
41
  "./preset": {
39
42
  "types": "./dist/preset.d.ts",
@@ -42,12 +45,15 @@
42
45
  },
43
46
  "./utilities": {
44
47
  "types": "./dist/utilities.d.ts",
45
- "import": "./dist/utilities.mjs",
46
- "require": "./dist/utilities.js"
48
+ "react-native": "./src/utilities/index.ts",
49
+ "import": "./src/utilities/index.ts",
50
+ "require": "./dist/utilities.js",
51
+ "default": "./src/utilities/index.ts"
47
52
  }
48
53
  },
49
54
  "files": [
50
55
  "dist",
56
+ "src",
51
57
  "README.md",
52
58
  "LICENSE"
53
59
  ],
@@ -0,0 +1,245 @@
1
+ import * as React from "react";
2
+ import {
3
+ View,
4
+ Pressable,
5
+ Animated,
6
+ type ViewProps,
7
+ type PressableProps,
8
+ } from "react-native";
9
+ import { cn } from "../../utilities/cn";
10
+ import { Text } from "../text/text";
11
+
12
+ type AccordionType = "single" | "multiple";
13
+
14
+ interface AccordionContextValue {
15
+ type: AccordionType;
16
+ value: string[];
17
+ toggleItem: (itemValue: string) => void;
18
+ collapsible: boolean;
19
+ }
20
+
21
+ const AccordionContext = React.createContext<AccordionContextValue | null>(null);
22
+
23
+ function useAccordionContext() {
24
+ const context = React.useContext(AccordionContext);
25
+ if (!context) {
26
+ throw new Error("Accordion components must be used within an Accordion");
27
+ }
28
+ return context;
29
+ }
30
+
31
+ export interface AccordionProps extends ViewProps {
32
+ type?: AccordionType;
33
+ value?: string | string[];
34
+ defaultValue?: string | string[];
35
+ onValueChange?: (value: string | string[]) => void;
36
+ collapsible?: boolean;
37
+ className?: string;
38
+ children: React.ReactNode;
39
+ }
40
+
41
+ export const Accordion = React.forwardRef<View, AccordionProps>(
42
+ (
43
+ {
44
+ type = "single",
45
+ value: controlledValue,
46
+ defaultValue,
47
+ onValueChange,
48
+ collapsible = true,
49
+ className,
50
+ children,
51
+ ...props
52
+ },
53
+ ref
54
+ ) => {
55
+ const toArray = (v?: string | string[]): string[] => {
56
+ if (!v) return [];
57
+ return Array.isArray(v) ? v : [v];
58
+ };
59
+
60
+ const [uncontrolledValue, setUncontrolledValue] = React.useState<string[]>(
61
+ toArray(defaultValue)
62
+ );
63
+
64
+ const isControlled = controlledValue !== undefined;
65
+ const currentValues = isControlled ? toArray(controlledValue) : uncontrolledValue;
66
+
67
+ const toggleItem = React.useCallback(
68
+ (itemValue: string) => {
69
+ let nextValues: string[];
70
+ const isCurrentOpen = currentValues.includes(itemValue);
71
+
72
+ if (type === "single") {
73
+ if (isCurrentOpen) {
74
+ nextValues = collapsible ? [] : [itemValue];
75
+ } else {
76
+ nextValues = [itemValue];
77
+ }
78
+ } else {
79
+ if (isCurrentOpen) {
80
+ nextValues = currentValues.filter((v) => v !== itemValue);
81
+ } else {
82
+ nextValues = [...currentValues, itemValue];
83
+ }
84
+ }
85
+
86
+ if (!isControlled) {
87
+ setUncontrolledValue(nextValues);
88
+ }
89
+
90
+ if (onValueChange) {
91
+ onValueChange(type === "single" ? nextValues[0] ?? "" : nextValues);
92
+ }
93
+ },
94
+ [currentValues, type, collapsible, isControlled, onValueChange]
95
+ );
96
+
97
+ return (
98
+ <AccordionContext.Provider
99
+ value={{
100
+ type,
101
+ value: currentValues,
102
+ toggleItem,
103
+ collapsible,
104
+ }}
105
+ >
106
+ <View
107
+ ref={ref}
108
+ className={cn("w-full divide-y divide-border", className)}
109
+ {...props}
110
+ >
111
+ {children}
112
+ </View>
113
+ </AccordionContext.Provider>
114
+ );
115
+ }
116
+ );
117
+ Accordion.displayName = "Accordion";
118
+
119
+ // --- Accordion Item ---
120
+
121
+ interface AccordionItemContextValue {
122
+ value: string;
123
+ isOpen: boolean;
124
+ }
125
+
126
+ const AccordionItemContext = React.createContext<AccordionItemContextValue | null>(null);
127
+
128
+ function useAccordionItemContext() {
129
+ const context = React.useContext(AccordionItemContext);
130
+ if (!context) {
131
+ throw new Error("AccordionItem subcomponents must be used within an AccordionItem");
132
+ }
133
+ return context;
134
+ }
135
+
136
+ export interface AccordionItemProps extends ViewProps {
137
+ value: string;
138
+ className?: string;
139
+ children: React.ReactNode;
140
+ }
141
+
142
+ export const AccordionItem = React.forwardRef<View, AccordionItemProps>(
143
+ ({ value, className, children, ...props }, ref) => {
144
+ const { value: selectedValues } = useAccordionContext();
145
+ const isOpen = selectedValues.includes(value);
146
+
147
+ return (
148
+ <AccordionItemContext.Provider value={{ value, isOpen }}>
149
+ <View
150
+ ref={ref}
151
+ className={cn("border-b border-border py-1", className)}
152
+ {...props}
153
+ >
154
+ {children}
155
+ </View>
156
+ </AccordionItemContext.Provider>
157
+ );
158
+ }
159
+ );
160
+ AccordionItem.displayName = "AccordionItem";
161
+
162
+ // --- Accordion Trigger ---
163
+
164
+ export interface AccordionTriggerProps extends PressableProps {
165
+ className?: string;
166
+ children: React.ReactNode;
167
+ }
168
+
169
+ export const AccordionTrigger = React.forwardRef<View, AccordionTriggerProps>(
170
+ ({ className, children, ...props }, ref) => {
171
+ const { toggleItem } = useAccordionContext();
172
+ const { value, isOpen } = useAccordionItemContext();
173
+
174
+ const rotationAnim = React.useRef(new Animated.Value(isOpen ? 1 : 0)).current;
175
+
176
+ React.useEffect(() => {
177
+ Animated.timing(rotationAnim, {
178
+ toValue: isOpen ? 1 : 0,
179
+ duration: 200,
180
+ useNativeDriver: true,
181
+ }).start();
182
+ }, [isOpen, rotationAnim]);
183
+
184
+ const rotate = rotationAnim.interpolate({
185
+ inputRange: [0, 1],
186
+ outputRange: ["0deg", "180deg"],
187
+ });
188
+
189
+ return (
190
+ <Pressable
191
+ ref={ref}
192
+ onPress={() => toggleItem(value)}
193
+ accessibilityRole="button"
194
+ accessibilityState={{ expanded: isOpen }}
195
+ className={cn(
196
+ "flex-row items-center justify-between py-3 px-1 active:opacity-70",
197
+ className
198
+ )}
199
+ {...props}
200
+ >
201
+ <View className="flex-1 pr-2">
202
+ {typeof children === "string" ? (
203
+ <Text className="text-base font-medium text-foreground">{children}</Text>
204
+ ) : (
205
+ children
206
+ )}
207
+ </View>
208
+ <Animated.View style={{ transform: [{ rotate }] }}>
209
+ <Text className="text-muted text-base font-semibold">▼</Text>
210
+ </Animated.View>
211
+ </Pressable>
212
+ );
213
+ }
214
+ );
215
+ AccordionTrigger.displayName = "AccordionTrigger";
216
+
217
+ // --- Accordion Content ---
218
+
219
+ export interface AccordionContentProps extends ViewProps {
220
+ className?: string;
221
+ children: React.ReactNode;
222
+ }
223
+
224
+ export const AccordionContent = React.forwardRef<View, AccordionContentProps>(
225
+ ({ className, children, ...props }, ref) => {
226
+ const { isOpen } = useAccordionItemContext();
227
+
228
+ if (!isOpen) return null;
229
+
230
+ return (
231
+ <View
232
+ ref={ref}
233
+ className={cn("pt-1 pb-3 px-1 text-sm text-muted", className)}
234
+ {...props}
235
+ >
236
+ {typeof children === "string" ? (
237
+ <Text className="text-sm text-muted leading-relaxed">{children}</Text>
238
+ ) : (
239
+ children
240
+ )}
241
+ </View>
242
+ );
243
+ }
244
+ );
245
+ AccordionContent.displayName = "AccordionContent";
@@ -0,0 +1 @@
1
+ export * from "./accordion";
@@ -0,0 +1,118 @@
1
+ import * as React from "react";
2
+ import { View, Text, type ViewProps, type TextProps } from "react-native";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { cn } from "../../utilities/cn";
5
+
6
+ export const alertVariants = cva(
7
+ "w-full rounded-2xl border p-4 flex-col gap-1",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ info: "bg-primary-50 border-primary-200 dark:bg-primary-950/40 dark:border-primary-800",
12
+ success: "bg-success-50 border-success-200 dark:bg-success-950/40 dark:border-success-800",
13
+ warning: "bg-warning-50 border-warning-200 dark:bg-warning-950/40 dark:border-warning-800",
14
+ danger: "bg-danger-50 border-danger-200 dark:bg-danger-950/40 dark:border-danger-800",
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ variant: "info",
19
+ },
20
+ }
21
+ );
22
+
23
+ export const alertTitleVariants = cva("text-base font-bold tracking-tight", {
24
+ variants: {
25
+ variant: {
26
+ info: "text-primary-800 dark:text-primary-300",
27
+ success: "text-success-800 dark:text-success-300",
28
+ warning: "text-warning-800 dark:text-warning-300",
29
+ danger: "text-danger-800 dark:text-danger-300",
30
+ },
31
+ },
32
+ defaultVariants: {
33
+ variant: "info",
34
+ },
35
+ });
36
+
37
+ export const alertDescriptionVariants = cva("text-sm", {
38
+ variants: {
39
+ variant: {
40
+ info: "text-primary-700 dark:text-primary-400",
41
+ success: "text-success-700 dark:text-success-400",
42
+ warning: "text-warning-700 dark:text-warning-400",
43
+ danger: "text-danger-700 dark:text-danger-400",
44
+ },
45
+ },
46
+ defaultVariants: {
47
+ variant: "info",
48
+ },
49
+ });
50
+
51
+ type AlertVariantProps = VariantProps<typeof alertVariants>;
52
+
53
+ interface AlertContextValue {
54
+ variant: AlertVariantProps["variant"];
55
+ }
56
+
57
+ const AlertContext = React.createContext<AlertContextValue>({ variant: "info" });
58
+
59
+ export interface AlertProps extends ViewProps, AlertVariantProps {
60
+ className?: string;
61
+ }
62
+
63
+ export function Alert({
64
+ variant = "info",
65
+ className,
66
+ children,
67
+ ...props
68
+ }: AlertProps) {
69
+ return (
70
+ <AlertContext.Provider value={{ variant }}>
71
+ <View
72
+ accessibilityRole="alert"
73
+ className={cn(alertVariants({ variant }), className)}
74
+ {...props}
75
+ >
76
+ {children}
77
+ </View>
78
+ </AlertContext.Provider>
79
+ );
80
+ }
81
+
82
+ Alert.displayName = "Alert";
83
+
84
+ export function AlertTitle({
85
+ className,
86
+ children,
87
+ ...props
88
+ }: TextProps & { className?: string }) {
89
+ const { variant } = React.useContext(AlertContext);
90
+ return (
91
+ <Text
92
+ className={cn(alertTitleVariants({ variant }), className)}
93
+ {...props}
94
+ >
95
+ {children}
96
+ </Text>
97
+ );
98
+ }
99
+
100
+ AlertTitle.displayName = "Alert.Title";
101
+
102
+ export function AlertDescription({
103
+ className,
104
+ children,
105
+ ...props
106
+ }: TextProps & { className?: string }) {
107
+ const { variant } = React.useContext(AlertContext);
108
+ return (
109
+ <Text
110
+ className={cn(alertDescriptionVariants({ variant }), className)}
111
+ {...props}
112
+ >
113
+ {children}
114
+ </Text>
115
+ );
116
+ }
117
+
118
+ AlertDescription.displayName = "Alert.Description";
@@ -0,0 +1 @@
1
+ export * from "./alert";
@@ -0,0 +1,125 @@
1
+ import * as React from "react";
2
+ import { View, Text, Image as NativeImage, type ImageProps, type ViewProps } from "react-native";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { cn } from "../../utilities/cn";
5
+
6
+ export const avatarVariants = cva(
7
+ "relative flex shrink-0 overflow-hidden rounded-full items-center justify-center bg-neutral-200 dark:bg-neutral-800",
8
+ {
9
+ variants: {
10
+ size: {
11
+ sm: "h-8 w-8",
12
+ md: "h-10 w-10",
13
+ lg: "h-14 w-14",
14
+ xl: "h-20 w-20",
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ size: "md",
19
+ },
20
+ }
21
+ );
22
+
23
+ export const avatarTextVariants = cva("font-semibold text-neutral-700 dark:text-neutral-300", {
24
+ variants: {
25
+ size: {
26
+ sm: "text-xs",
27
+ md: "text-sm",
28
+ lg: "text-lg",
29
+ xl: "text-2xl",
30
+ },
31
+ },
32
+ defaultVariants: {
33
+ size: "md",
34
+ },
35
+ });
36
+
37
+ interface AvatarContextValue {
38
+ size: "sm" | "md" | "lg" | "xl";
39
+ }
40
+
41
+ const AvatarContext = React.createContext<AvatarContextValue>({ size: "md" });
42
+
43
+ export interface AvatarProps
44
+ extends ViewProps,
45
+ VariantProps<typeof avatarVariants> {
46
+ className?: string;
47
+ }
48
+
49
+ export function Avatar({
50
+ className,
51
+ size = "md",
52
+ children,
53
+ ...props
54
+ }: AvatarProps) {
55
+ const currentSize = size ?? "md";
56
+ return (
57
+ <AvatarContext.Provider value={{ size: currentSize }}>
58
+ <View
59
+ className={cn(avatarVariants({ size: currentSize }), className)}
60
+ {...props}
61
+ >
62
+ {children}
63
+ </View>
64
+ </AvatarContext.Provider>
65
+ );
66
+ }
67
+ Avatar.displayName = "Avatar";
68
+
69
+ export interface AvatarImageProps extends ImageProps {
70
+ className?: string;
71
+ }
72
+
73
+ export function AvatarImage({ className, ...props }: AvatarImageProps) {
74
+ const [hasError, setHasError] = React.useState(false);
75
+
76
+ if (hasError) return null;
77
+
78
+ return (
79
+ <NativeImage
80
+ onError={() => setHasError(true)}
81
+ className={cn("h-full w-full rounded-full object-cover", className)}
82
+ {...props}
83
+ />
84
+ );
85
+ }
86
+ AvatarImage.displayName = "Avatar.Image";
87
+
88
+ export interface AvatarFallbackProps extends ViewProps {
89
+ children?: React.ReactNode;
90
+ text?: string;
91
+ className?: string;
92
+ textClassName?: string;
93
+ }
94
+
95
+ export function AvatarFallback({
96
+ children,
97
+ text,
98
+ className,
99
+ textClassName,
100
+ ...props
101
+ }: AvatarFallbackProps) {
102
+ const { size } = React.useContext(AvatarContext);
103
+ const content = text ?? children;
104
+
105
+ return (
106
+ <View
107
+ className={cn(
108
+ "h-full w-full items-center justify-center rounded-full bg-neutral-200 dark:bg-neutral-800",
109
+ className
110
+ )}
111
+ {...props}
112
+ >
113
+ {typeof content === "string" ? (
114
+ <Text
115
+ className={cn(avatarTextVariants({ size }), textClassName)}
116
+ >
117
+ {content}
118
+ </Text>
119
+ ) : (
120
+ content
121
+ )}
122
+ </View>
123
+ );
124
+ }
125
+ AvatarFallback.displayName = "Avatar.Fallback";
@@ -0,0 +1 @@
1
+ export * from "./avatar";
@@ -0,0 +1,89 @@
1
+ import * as React from "react";
2
+ import { View, Text, type ViewProps } from "react-native";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { cn } from "../../utilities/cn";
5
+
6
+ export const badgeVariants = cva(
7
+ "inline-flex flex-row items-center justify-center rounded-full self-start",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ primary: "bg-primary-600 dark:bg-primary-500",
12
+ secondary: "bg-secondary-600 dark:bg-secondary-500",
13
+ danger: "bg-danger-600 dark:bg-danger-500",
14
+ destructive: "bg-danger-600 dark:bg-danger-500",
15
+ success: "bg-success-600 dark:bg-success-500",
16
+ warning: "bg-warning-600 dark:bg-warning-500",
17
+ outline: "border border-neutral-300 dark:border-neutral-700 bg-transparent",
18
+ },
19
+ size: {
20
+ sm: "px-2 py-0.5",
21
+ md: "px-2.5 py-1",
22
+ lg: "px-3.5 py-1.5",
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ variant: "primary",
27
+ size: "md",
28
+ },
29
+ }
30
+ );
31
+
32
+ export const badgeTextVariants = cva("font-semibold text-center", {
33
+ variants: {
34
+ variant: {
35
+ primary: "text-white",
36
+ secondary: "text-white",
37
+ danger: "text-white",
38
+ destructive: "text-white",
39
+ success: "text-white",
40
+ warning: "text-white",
41
+ outline: "text-neutral-700 dark:text-neutral-300",
42
+ },
43
+ size: {
44
+ sm: "text-[10px]",
45
+ md: "text-xs",
46
+ lg: "text-sm",
47
+ },
48
+ },
49
+ defaultVariants: {
50
+ variant: "primary",
51
+ size: "md",
52
+ },
53
+ });
54
+
55
+ export interface BadgeProps
56
+ extends ViewProps,
57
+ VariantProps<typeof badgeVariants> {
58
+ children?: React.ReactNode;
59
+ className?: string;
60
+ textClassName?: string;
61
+ }
62
+
63
+ export function Badge({
64
+ children,
65
+ variant = "primary",
66
+ size = "md",
67
+ className,
68
+ textClassName,
69
+ ...props
70
+ }: BadgeProps) {
71
+ return (
72
+ <View
73
+ className={cn(badgeVariants({ variant, size }), className)}
74
+ {...props}
75
+ >
76
+ {typeof children === "string" || typeof children === "number" ? (
77
+ <Text
78
+ className={cn(badgeTextVariants({ variant, size }), textClassName)}
79
+ >
80
+ {children}
81
+ </Text>
82
+ ) : (
83
+ children
84
+ )}
85
+ </View>
86
+ );
87
+ }
88
+
89
+ Badge.displayName = "Badge";
@@ -0,0 +1 @@
1
+ export * from "./badge";