@unikit-bridge/ui 0.0.0-stage → 0.2.0

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -2
  3. package/docs/tokens.md +63 -0
  4. package/package.json +75 -4
  5. package/src/bridge/native.tsx +301 -0
  6. package/src/bridge/types.ts +174 -0
  7. package/src/bridge/uniwind-env.d.ts +1 -0
  8. package/src/bridge/web.tsx +247 -0
  9. package/src/components/accordion.tsx +231 -0
  10. package/src/components/alert.tsx +157 -0
  11. package/src/components/aspect-ratio.tsx +40 -0
  12. package/src/components/avatar.tsx +275 -0
  13. package/src/components/badge.tsx +65 -0
  14. package/src/components/breadcrumb.tsx +173 -0
  15. package/src/components/button-group.tsx +166 -0
  16. package/src/components/button.tsx +106 -0
  17. package/src/components/card.tsx +112 -0
  18. package/src/components/checkbox.tsx +94 -0
  19. package/src/components/collapsible.tsx +120 -0
  20. package/src/components/empty.tsx +160 -0
  21. package/src/components/field.tsx +349 -0
  22. package/src/components/input.tsx +39 -0
  23. package/src/components/item.tsx +243 -0
  24. package/src/components/kbd.tsx +62 -0
  25. package/src/components/label.tsx +33 -0
  26. package/src/components/pagination.tsx +204 -0
  27. package/src/components/progress.tsx +60 -0
  28. package/src/components/radio-group.tsx +148 -0
  29. package/src/components/separator.tsx +50 -0
  30. package/src/components/skeleton.tsx +36 -0
  31. package/src/components/switch.tsx +128 -0
  32. package/src/components/table.tsx +193 -0
  33. package/src/components/tabs.tsx +213 -0
  34. package/src/components/textarea.tsx +64 -0
  35. package/src/components/toggle-group.tsx +187 -0
  36. package/src/components/toggle.tsx +132 -0
  37. package/src/index.ts +321 -0
  38. package/src/lib/utils.ts +7 -0
  39. package/src/theme/index.ts +4 -0
  40. package/src/theme/presets.ts +187 -0
  41. package/src/theme/provider.tsx +82 -0
  42. package/src/theme/tokens.ts +75 -0
@@ -0,0 +1,157 @@
1
+ // Alert — shadcn/ui new-york-v4 alert 포팅(/tmp/shadcn-ref/src/alert). 3종 export.
2
+ // 포팅 판단:
3
+ // - 원본은 grid + has-자손 셀렉터로 선택적 아이콘 열을 배치한다.
4
+ // data·자손 셀렉터는 네이티브에서 동작하지 않으므로 flex-row(글리프 + 콘텐츠 열)
5
+ // 구조로 재작성하고, 좌측 아이콘은 글리프 Text(default ⓘ / destructive ⚠)로
6
+ // 표시한다(아이콘 없는 배치는 미지원). 색·여백·rounding 토큰은 유지.
7
+ // - 원본의 텍스트 색 상속(text-card-foreground / text-destructive)은 이중 지정
8
+ // 원칙에 따라 내부 Text로 이동. 특히 AlertDescription의 destructive 톤(원본은
9
+ // data-slot=alert-description 자손 셀렉터의 text-destructive/90)은 셀렉터를
10
+ // 쓸 수 없어 React context로 variant를 전달한다 — variant prop은 원본대로
11
+ // Alert에만 존재한다(Alert 단독 사용 시 하위 컴포넌트는 default 톤).
12
+ // - role="alert"은 원본 그대로 유지(신규 접근성 계약 BridgeRole "alert").
13
+ // - 웹 전용 토큰 처리: line-clamp-1·tracking-tight은 web: 접두, 그리드 전용
14
+ // col-start-*·min-h-4와 [&_p]:leading-relaxed(단락 자손 스타일)은 소실.
15
+ import { createContext, forwardRef, useContext } from "react";
16
+ import {
17
+ Text,
18
+ View,
19
+ type BridgeTextRef,
20
+ type BridgeViewRef,
21
+ type TextProps,
22
+ type ViewProps,
23
+ } from "@unikit-bridge/ui/primitives";
24
+ import { cva, type VariantProps } from "class-variance-authority";
25
+
26
+ import { cn } from "../lib/utils";
27
+
28
+ // 컨테이너(View) — 배경·테두리. 원본의 grid 배치를 flex-row(글리프+콘텐츠 열)로 치환.
29
+ const alertVariants = cva(
30
+ "relative w-full flex-row items-start gap-3 rounded-lg border px-4 py-3",
31
+ {
32
+ variants: {
33
+ variant: {
34
+ default: "bg-card",
35
+ // 원본 destructive의 border-destructive/50 토큰 유지(사소 5 — 포팅
36
+ // 초판에서 누락돼 destructive가 글자색으로만 구분됐다).
37
+ destructive: "bg-card border-destructive/50",
38
+ },
39
+ },
40
+ defaultVariants: {
41
+ variant: "default",
42
+ },
43
+ },
44
+ );
45
+
46
+ // 좌측 글리프 Text — 원본 [&>svg]:size-4·text-current 아이콘의 글리프 대체.
47
+ const alertGlyphVariants = cva("text-base leading-none", {
48
+ variants: {
49
+ variant: {
50
+ default: "text-card-foreground",
51
+ destructive: "text-destructive",
52
+ },
53
+ },
54
+ defaultVariants: {
55
+ variant: "default",
56
+ },
57
+ });
58
+
59
+ // 제목 Text — 원본 col-start-2 line-clamp-1 font-medium tracking-tight.
60
+ const alertTitleVariants = cva(
61
+ "font-sans text-sm font-medium leading-none web:line-clamp-1 web:tracking-tight",
62
+ {
63
+ variants: {
64
+ variant: {
65
+ default: "text-card-foreground",
66
+ destructive: "text-destructive",
67
+ },
68
+ },
69
+ defaultVariants: {
70
+ variant: "default",
71
+ },
72
+ },
73
+ );
74
+
75
+ // 설명 Text — 원본 text-muted-foreground, destructive는 /90 톤(자손 셀렉터 대체).
76
+ const alertDescriptionVariants = cva(
77
+ "font-sans text-sm text-muted-foreground",
78
+ {
79
+ variants: {
80
+ variant: {
81
+ default: "",
82
+ destructive: "text-destructive/90",
83
+ },
84
+ },
85
+ defaultVariants: {
86
+ variant: "default",
87
+ },
88
+ },
89
+ );
90
+
91
+ // variant 전파용 React context — 원본의 `*:` 자손 셀렉터를 대체한다.
92
+ const AlertContext = createContext<
93
+ NonNullable<VariantProps<typeof alertVariants>["variant"]>
94
+ >("default");
95
+
96
+ export type AlertProps = ViewProps & VariantProps<typeof alertVariants>;
97
+ export type AlertTitleProps = TextProps;
98
+ export type AlertDescriptionProps = TextProps;
99
+
100
+ const Alert = forwardRef<BridgeViewRef, AlertProps>(
101
+ ({ className, variant, children, ...props }, ref) => (
102
+ <AlertContext.Provider value={variant ?? "default"}>
103
+ <View
104
+ ref={ref}
105
+ accessibilityRole="alert"
106
+ className={cn(alertVariants({ variant }), className)}
107
+ {...props}
108
+ >
109
+ <View aria-hidden className="mt-0.5 shrink-0">
110
+ <Text className={cn(alertGlyphVariants({ variant }))}>
111
+ {variant === "destructive" ? "⚠" : "ⓘ"}
112
+ </Text>
113
+ </View>
114
+ <View className="flex flex-col gap-0.5">{children}</View>
115
+ </View>
116
+ </AlertContext.Provider>
117
+ ),
118
+ );
119
+ Alert.displayName = "Alert";
120
+
121
+ const AlertTitle = forwardRef<BridgeTextRef, AlertTitleProps>(
122
+ ({ className, ...props }, ref) => {
123
+ const variant = useContext(AlertContext);
124
+ return (
125
+ <Text
126
+ ref={ref}
127
+ className={cn(alertTitleVariants({ variant }), className)}
128
+ {...props}
129
+ />
130
+ );
131
+ },
132
+ );
133
+ AlertTitle.displayName = "AlertTitle";
134
+
135
+ const AlertDescription = forwardRef<BridgeTextRef, AlertDescriptionProps>(
136
+ ({ className, ...props }, ref) => {
137
+ const variant = useContext(AlertContext);
138
+ return (
139
+ <Text
140
+ ref={ref}
141
+ className={cn(alertDescriptionVariants({ variant }), className)}
142
+ {...props}
143
+ />
144
+ );
145
+ },
146
+ );
147
+ AlertDescription.displayName = "AlertDescription";
148
+
149
+ export {
150
+ Alert,
151
+ AlertTitle,
152
+ AlertDescription,
153
+ alertVariants,
154
+ alertGlyphVariants,
155
+ alertTitleVariants,
156
+ alertDescriptionVariants,
157
+ };
@@ -0,0 +1,40 @@
1
+ // AspectRatio — shadcn/ui(new-york-v4) aspect-ratio.tsx 유니버설 포팅 (사이클 30, T-3b).
2
+ // 원본: shadcn-ref/src/aspect-ratio/aspect-ratio.tsx — radix AspectRatio 엔진을
3
+ // 제거하고 비율은 BridgeStyle 동적 인라인 스타일 계약으로 표현한다.
4
+ //
5
+ // 포팅 판단(원본 대비 차이):
6
+ // - 원본(radix)은 웹 구형 호환을 위해 패딩 핵으로 비율을 만들었다. 브리지 계약의
7
+ // aspectRatio는 숫자 값이 웹 DOM(단위 없는 비율)·RN(원시 프로퍼티) 양쪽에
8
+ // 유효해 style 지정으로 대체한다(정적 스타일이 아닌 사용자 지정 비율 값이므로
9
+ // className 원칙의 동적 값 예외에 해당).
10
+ // - 원본의 overflow hidden은 className(overflow-hidden)으로 유지.
11
+ import { forwardRef } from "react";
12
+ import {
13
+ View,
14
+ type BridgeViewRef,
15
+ type ViewProps,
16
+ } from "@unikit-bridge/ui/primitives";
17
+
18
+ import { cn } from "../lib/utils";
19
+
20
+ export type AspectRatioProps = ViewProps & {
21
+ /** 가로세로 비율(가로 ÷ 세로, 기본 1 — 정사각형) */
22
+ ratio?: number;
23
+ };
24
+
25
+ const AspectRatio = forwardRef<BridgeViewRef, AspectRatioProps>(
26
+ ({ ratio = 1, className, style, children, ...props }, ref) => (
27
+ <View
28
+ ref={ref}
29
+ className={cn("overflow-hidden", className)}
30
+ // 사용자 style이 있으면 병합 — 비율은 컴포넌트 계약값이 우선한다.
31
+ style={{ ...style, aspectRatio: ratio }}
32
+ {...props}
33
+ >
34
+ {children}
35
+ </View>
36
+ ),
37
+ );
38
+ AspectRatio.displayName = "AspectRatio";
39
+
40
+ export { AspectRatio };
@@ -0,0 +1,275 @@
1
+ // Avatar — shadcn/ui new-york-v4 avatar(Radix Avatar 기반)를 UniKit 유니버설로 이식.
2
+ //
3
+ // 포팅 판단(radix 엔진 의존 제거):
4
+ // - 로드 실패 상태 공유는 React context로 재현 — Avatar(루트)가
5
+ // { size, hasImage, imageFailed }를 제공하고, AvatarImage가 마운트 등록/
6
+ // 실패 보고(브리지 Image onError), AvatarFallback이
7
+ // "소스 없음 또는 로드 실패"일 때만 렌더된다.
8
+ // - 브리지 Image에 onLoad 계약이 없어 '로드 성공'을 알 수 없으므로 폴백
9
+ // 표시 조건을 Radix의 "로드 전+실패"에서 "소스 없음+실패"로 단순화했다.
10
+ // 실패한 AvatarImage는 렌더를 중단한다(Radix는 깨진 img를 남긴다).
11
+ // - 원본의 data-size + group-data size/avatar 셀렉터(폴백 글자 크기·배지
12
+ // 크기 연동)는 아래 조회 테이블로 치환 — 각 컴포넌트가 context size를 읽어
13
+ // 자기 크기 클래스를 적용한다.
14
+ // - AvatarGroup의 겹침(-space-x-2)과 data-slot=avatar 자식 링은 자식 셀렉터가
15
+ // 필요하다. 겹침은 웹 전용 클래스로 유지(네이티브은 나란히 배치)하고, 링은
16
+ // Avatar가 AvatarGroupContext를 읽어 자신에게 ring-2를 건다.
17
+ // - AvatarGroupCount의 그룹 크기 반응(group-has-data-...)은 고정 토큰(size-8)으로
18
+ // 단순화. AvatarBadge 내용은 글리프 Text 또는 비움(배경 자체가 상태 점).
19
+ import {
20
+ createContext,
21
+ forwardRef,
22
+ useContext,
23
+ useEffect,
24
+ useMemo,
25
+ useState,
26
+ type ReactNode,
27
+ } from "react";
28
+ import {
29
+ Image,
30
+ Text,
31
+ View,
32
+ type BridgeImageRef,
33
+ type BridgeViewRef,
34
+ type ImageProps,
35
+ type ViewProps,
36
+ } from "@unikit-bridge/ui/primitives";
37
+ import { cva, type VariantProps } from "class-variance-authority";
38
+
39
+ import { cn } from "../lib/utils";
40
+
41
+ export type AvatarSize = "default" | "sm" | "lg";
42
+
43
+ // 루트 크기 — 원본 data-size 셀렉터(size-8 · sm:size-6 · lg:size-10) 치환.
44
+ const avatarVariants = cva(
45
+ "relative flex shrink-0 overflow-hidden rounded-full web:select-none",
46
+ {
47
+ variants: {
48
+ size: {
49
+ default: "size-8",
50
+ sm: "size-6",
51
+ lg: "size-10",
52
+ },
53
+ },
54
+ defaultVariants: {
55
+ size: "default",
56
+ },
57
+ },
58
+ );
59
+
60
+ // 폴백 글자 크기(원본의 group-data size=sm → text-xs)·배지 크기
61
+ // (sm 2 · default 2.5 · lg 3) — context size 값으로 조회하는 치환 테이블.
62
+ const AVATAR_FALLBACK_TEXT: Record<AvatarSize, string> = {
63
+ default: "text-sm",
64
+ sm: "text-xs",
65
+ lg: "text-sm",
66
+ };
67
+ const AVATAR_BADGE_SIZE: Record<AvatarSize, string> = {
68
+ default: "size-2.5",
69
+ sm: "size-2",
70
+ lg: "size-3",
71
+ };
72
+
73
+ // 루트가 제공하는 상태 — 폴백 전환 판단의 단일 출처.
74
+ type AvatarContextValue = {
75
+ size: AvatarSize;
76
+ /** AvatarImage 마운트 여부 — false면 "소스 없음"으로 폴백 렌더 */
77
+ hasImage: boolean;
78
+ /** AvatarImage 로드 실패 — 폴백으로 전환 */
79
+ imageFailed: boolean;
80
+ reportImageMounted: (mounted: boolean) => void;
81
+ reportImageFailed: () => void;
82
+ };
83
+
84
+ const AvatarContext = createContext<AvatarContextValue | null>(null);
85
+
86
+ // AvatarGroup 내부 여부 — Avatar가 겹침 경계 링을 자신에게 적용한다.
87
+ const AvatarGroupContext = createContext(false);
88
+
89
+ // ---------------------------------------------------------------------------
90
+ // Avatar — 루트 컨테이너. 상태 제공자역도 겸한다(원본 AvatarPrimitive.Root).
91
+ // ---------------------------------------------------------------------------
92
+ export type AvatarProps = ViewProps &
93
+ VariantProps<typeof avatarVariants> & {
94
+ children?: ReactNode;
95
+ };
96
+
97
+ const Avatar = forwardRef<BridgeViewRef, AvatarProps>(
98
+ ({ className, size, ...props }, ref) => {
99
+ const [hasImage, setHasImage] = useState(false);
100
+ const [imageFailed, setImageFailed] = useState(false);
101
+ const grouped = useContext(AvatarGroupContext);
102
+ const resolvedSize = size ?? "default";
103
+ const contextValue = useMemo<AvatarContextValue>(
104
+ () => ({
105
+ size: resolvedSize,
106
+ hasImage,
107
+ imageFailed,
108
+ // setState 설정자는 참조가 안정적이라 그대로 노출해도 안전하다.
109
+ reportImageMounted: setHasImage,
110
+ reportImageFailed: () => setImageFailed(true),
111
+ }),
112
+ [resolvedSize, hasImage, imageFailed],
113
+ );
114
+ return (
115
+ <AvatarContext.Provider value={contextValue}>
116
+ <View
117
+ ref={ref}
118
+ className={cn(
119
+ avatarVariants({ size }),
120
+ grouped && "ring-2 ring-background",
121
+ className,
122
+ )}
123
+ {...props}
124
+ />
125
+ </AvatarContext.Provider>
126
+ );
127
+ },
128
+ );
129
+ Avatar.displayName = "Avatar";
130
+
131
+ // ---------------------------------------------------------------------------
132
+ // AvatarImage — 브리지 Image(원격 {uri}만). 실패 보고는 context로.
133
+ // ---------------------------------------------------------------------------
134
+ // onError는 폴백 전환의 통로이므로 props에서 제외하고 컴포넌트가 소유한다.
135
+ export type AvatarImageProps = Omit<ImageProps, "onError">;
136
+
137
+ const AvatarImage = forwardRef<BridgeImageRef, AvatarImageProps>(
138
+ ({ className, source, ...props }, ref) => {
139
+ const avatar = useContext(AvatarContext);
140
+ // 마운트 등록/해제 — 폴백의 "소스 없음" 판단 근거(provider는 안정적).
141
+ useEffect(() => {
142
+ avatar?.reportImageMounted(true);
143
+ return () => avatar?.reportImageMounted(false);
144
+ }, [avatar]);
145
+ // 실패한 이미지는 렌더하지 않는다(포팅 노트 — Radix는 깨진 img를 남김).
146
+ if (avatar?.imageFailed) {
147
+ return null;
148
+ }
149
+ return (
150
+ <Image
151
+ ref={ref}
152
+ source={source}
153
+ onError={avatar ? avatar.reportImageFailed : undefined}
154
+ className={cn("aspect-square size-full", className)}
155
+ {...props}
156
+ />
157
+ );
158
+ },
159
+ );
160
+ AvatarImage.displayName = "AvatarImage";
161
+
162
+ // ---------------------------------------------------------------------------
163
+ // AvatarFallback — 이니셜/글리프. "소스 없음 또는 실패"일 때만 렌더.
164
+ // ---------------------------------------------------------------------------
165
+ export type AvatarFallbackProps = ViewProps & {
166
+ children?: ReactNode;
167
+ };
168
+
169
+ const AvatarFallback = forwardRef<BridgeViewRef, AvatarFallbackProps>(
170
+ ({ className, children, ...props }, ref) => {
171
+ const avatar = useContext(AvatarContext);
172
+ // Avatar 밖 단독 사용(문맥 없음)이면 항상 렌드한다.
173
+ const visible = avatar ? !avatar.hasImage || avatar.imageFailed : true;
174
+ if (!visible) {
175
+ return null;
176
+ }
177
+ return (
178
+ <View
179
+ ref={ref}
180
+ className={cn(
181
+ "flex size-full items-center justify-center rounded-full bg-muted",
182
+ className,
183
+ )}
184
+ {...props}
185
+ >
186
+ {/* 이중 지정 원칙: 전경색은 내부 Text에 — children은 이니셜/글리프 문자열 권장 */}
187
+ <Text
188
+ className={cn(
189
+ "font-sans text-muted-foreground",
190
+ avatar && AVATAR_FALLBACK_TEXT[avatar.size],
191
+ )}
192
+ >
193
+ {children}
194
+ </Text>
195
+ </View>
196
+ );
197
+ },
198
+ );
199
+ AvatarFallback.displayName = "AvatarFallback";
200
+
201
+ // ---------------------------------------------------------------------------
202
+ // AvatarBadge — 우하단 상태 점. 크기는 context size 연동.
203
+ // ---------------------------------------------------------------------------
204
+ export type AvatarBadgeProps = ViewProps;
205
+
206
+ const AvatarBadge = forwardRef<BridgeViewRef, AvatarBadgeProps>(
207
+ ({ className, ...props }, ref) => {
208
+ const avatar = useContext(AvatarContext);
209
+ return (
210
+ <View
211
+ ref={ref}
212
+ className={cn(
213
+ "absolute right-0 bottom-0 z-10 flex items-center justify-center rounded-full bg-primary ring-2 ring-background web:select-none",
214
+ avatar && AVATAR_BADGE_SIZE[avatar.size],
215
+ className,
216
+ )}
217
+ {...props}
218
+ />
219
+ );
220
+ },
221
+ );
222
+ AvatarBadge.displayName = "AvatarBadge";
223
+
224
+ // ---------------------------------------------------------------------------
225
+ // AvatarGroup — 아바타 무리. 겹침은 웹 전용(네이티브은 나란히).
226
+ // ---------------------------------------------------------------------------
227
+ export type AvatarGroupProps = ViewProps;
228
+
229
+ const AvatarGroup = forwardRef<BridgeViewRef, AvatarGroupProps>(
230
+ ({ className, ...props }, ref) => (
231
+ <AvatarGroupContext.Provider value={true}>
232
+ <View
233
+ ref={ref}
234
+ className={cn("flex flex-row items-center web:-space-x-2", className)}
235
+ {...props}
236
+ />
237
+ </AvatarGroupContext.Provider>
238
+ ),
239
+ );
240
+ AvatarGroup.displayName = "AvatarGroup";
241
+
242
+ // ---------------------------------------------------------------------------
243
+ // AvatarGroupCount — 넘친 인원 표시("+9" 등 문자열 children).
244
+ // ---------------------------------------------------------------------------
245
+ export type AvatarGroupCountProps = ViewProps & {
246
+ children?: ReactNode;
247
+ };
248
+
249
+ const AvatarGroupCount = forwardRef<BridgeViewRef, AvatarGroupCountProps>(
250
+ ({ className, children, ...props }, ref) => (
251
+ <View
252
+ ref={ref}
253
+ className={cn(
254
+ "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted ring-2 ring-background",
255
+ className,
256
+ )}
257
+ {...props}
258
+ >
259
+ {/* 이중 지정 원칙: 전경색은 내부 Text에 */}
260
+ <Text className="font-sans text-sm text-muted-foreground">
261
+ {children}
262
+ </Text>
263
+ </View>
264
+ ),
265
+ );
266
+ AvatarGroupCount.displayName = "AvatarGroupCount";
267
+
268
+ export {
269
+ Avatar,
270
+ AvatarImage,
271
+ AvatarFallback,
272
+ AvatarBadge,
273
+ AvatarGroup,
274
+ AvatarGroupCount,
275
+ };
@@ -0,0 +1,65 @@
1
+ // Badge — docs/sdd/02-design.md v2 §3.5 그대로.
2
+ // 이중 지정 원칙(§0.5): 배경·테두리는 View, 전경색은 내부 Text에 명시(Button과 동일 패턴).
3
+ import { forwardRef } from "react";
4
+ import {
5
+ Text,
6
+ View,
7
+ type BridgeTextRef,
8
+ type BridgeViewRef,
9
+ type TextProps,
10
+ type ViewProps,
11
+ } from "@unikit-bridge/ui/primitives";
12
+ import { cva, type VariantProps } from "class-variance-authority";
13
+
14
+ import { cn } from "../lib/utils";
15
+
16
+ // View(컨테이너)
17
+ const badgeVariants = cva(
18
+ "flex flex-row items-center rounded-md border px-2.5 py-0.5",
19
+ {
20
+ variants: {
21
+ variant: {
22
+ default: "border-transparent bg-primary",
23
+ secondary: "border-transparent bg-secondary",
24
+ destructive: "border-transparent bg-destructive",
25
+ outline: "border-border",
26
+ },
27
+ },
28
+ defaultVariants: {
29
+ variant: "default",
30
+ },
31
+ },
32
+ );
33
+
34
+ // 내부 Text
35
+ const badgeTextVariants = cva("font-sans text-xs font-semibold", {
36
+ variants: {
37
+ variant: {
38
+ default: "text-primary-foreground",
39
+ secondary: "text-secondary-foreground",
40
+ destructive: "text-destructive-foreground",
41
+ outline: "text-foreground",
42
+ },
43
+ },
44
+ defaultVariants: {
45
+ variant: "default",
46
+ },
47
+ });
48
+
49
+ export type BadgeProps = ViewProps &
50
+ VariantProps<typeof badgeVariants>;
51
+
52
+ const Badge = forwardRef<BridgeViewRef, BadgeProps>(
53
+ ({ className, variant, children, ...props }, ref) => (
54
+ <View
55
+ ref={ref}
56
+ className={cn(badgeVariants({ variant }), className)}
57
+ {...props}
58
+ >
59
+ <Text className={cn(badgeTextVariants({ variant }))}>{children}</Text>
60
+ </View>
61
+ ),
62
+ );
63
+ Badge.displayName = "Badge";
64
+
65
+ export { Badge, badgeVariants, badgeTextVariants };