@gv-tech/ui-native 2.22.1 → 2.23.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 (61) hide show
  1. package/dist/button-group.d.ts +9 -0
  2. package/dist/button-group.d.ts.map +1 -0
  3. package/dist/carousel.d.ts +17 -5
  4. package/dist/carousel.d.ts.map +1 -1
  5. package/dist/carousel.test.d.ts +2 -0
  6. package/dist/carousel.test.d.ts.map +1 -0
  7. package/dist/combobox.d.ts +22 -0
  8. package/dist/combobox.d.ts.map +1 -0
  9. package/dist/direction.d.ts +5 -0
  10. package/dist/direction.d.ts.map +1 -0
  11. package/dist/empty.d.ts +11 -0
  12. package/dist/empty.d.ts.map +1 -0
  13. package/dist/field.d.ts +15 -0
  14. package/dist/field.d.ts.map +1 -0
  15. package/dist/form.d.ts +30 -1
  16. package/dist/form.d.ts.map +1 -1
  17. package/dist/form.test.d.ts +2 -0
  18. package/dist/form.test.d.ts.map +1 -0
  19. package/dist/hooks/use-theme.d.ts +16 -0
  20. package/dist/hooks/use-theme.d.ts.map +1 -1
  21. package/dist/index.d.ts +12 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/input-group.d.ts +12 -0
  24. package/dist/input-group.d.ts.map +1 -0
  25. package/dist/input-otp.d.ts +9 -0
  26. package/dist/input-otp.d.ts.map +1 -0
  27. package/dist/item.d.ts +15 -0
  28. package/dist/item.d.ts.map +1 -0
  29. package/dist/kbd.d.ts +7 -0
  30. package/dist/kbd.d.ts.map +1 -0
  31. package/dist/native-select.d.ts +8 -0
  32. package/dist/native-select.d.ts.map +1 -0
  33. package/dist/scroll-area.d.ts.map +1 -1
  34. package/dist/search.d.ts +9 -2
  35. package/dist/search.d.ts.map +1 -1
  36. package/dist/sidebar.d.ts +37 -0
  37. package/dist/sidebar.d.ts.map +1 -0
  38. package/dist/sonner.d.ts.map +1 -1
  39. package/dist/spinner.d.ts +6 -0
  40. package/dist/spinner.d.ts.map +1 -0
  41. package/dist/ui-native.cjs +2 -2
  42. package/dist/ui-native.mjs +1702 -907
  43. package/package.json +10 -5
  44. package/src/button-group.tsx +30 -0
  45. package/src/carousel.tsx +191 -14
  46. package/src/combobox.tsx +125 -0
  47. package/src/direction.tsx +12 -0
  48. package/src/empty.tsx +38 -0
  49. package/src/field.tsx +89 -0
  50. package/src/form.tsx +166 -4
  51. package/src/index.ts +108 -0
  52. package/src/input-group.tsx +54 -0
  53. package/src/input-otp.tsx +34 -0
  54. package/src/item.tsx +74 -0
  55. package/src/kbd.tsx +16 -0
  56. package/src/native-select.tsx +30 -0
  57. package/src/scroll-area.tsx +4 -2
  58. package/src/search.tsx +74 -12
  59. package/src/sidebar.tsx +209 -0
  60. package/src/sonner.tsx +7 -3
  61. package/src/spinner.tsx +9 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gv-tech/ui-native",
3
- "version": "2.22.1",
3
+ "version": "2.23.0",
4
4
  "description": "React Native implementations of the GV Tech design system components",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,8 +36,7 @@
36
36
  "dependencies": {
37
37
  "@gv-tech/design-tokens": "^2.12.0",
38
38
  "@gv-tech/ui-core": "^2.12.0",
39
- "react-native-reanimated": "4.3.0",
40
- "react-native-worklets": "0.8.1",
39
+ "react-hook-form": "^7.71.1",
41
40
  "@rn-primitives/accordion": "^1.2.0",
42
41
  "@rn-primitives/alert-dialog": "^1.2.0",
43
42
  "@rn-primitives/aspect-ratio": "^1.2.0",
@@ -66,22 +65,28 @@
66
65
  "@rn-primitives/toggle-group": "^1.2.0",
67
66
  "@rn-primitives/tooltip": "^1.2.0",
68
67
  "clsx": "^2.1.1",
69
- "lucide-react-native": "^1.8.0",
70
68
  "nativewind": "^4.2.1",
71
- "react-native-svg": "^15.15.3",
72
69
  "tailwind-merge": "^3.4.1",
73
70
  "tailwindcss": "^4.1.18"
74
71
  },
75
72
  "peerDependencies": {
73
+ "lucide-react-native": "^1.8.0",
76
74
  "react": ">=18",
77
75
  "react-native": ">=0.72",
78
76
  "react-native-reanimated": "^4.2.3",
77
+ "react-native-svg": "^15.15.3",
79
78
  "react-native-worklets": "^0.7.2"
80
79
  },
81
80
  "peerDependenciesMeta": {
81
+ "lucide-react-native": {
82
+ "optional": false
83
+ },
82
84
  "react-native-reanimated": {
83
85
  "optional": false
84
86
  },
87
+ "react-native-svg": {
88
+ "optional": false
89
+ },
85
90
  "react-native-worklets": {
86
91
  "optional": false
87
92
  }
@@ -0,0 +1,30 @@
1
+ import type { ButtonGroupBaseProps, ButtonGroupSeparatorBaseProps, ButtonGroupTextBaseProps } from '@gv-tech/ui-core';
2
+ import * as React from 'react';
3
+ import { Text, View } from 'react-native';
4
+ import { cn } from './lib/utils';
5
+
6
+ // TODO: Implement proper React Native ButtonGroup logic
7
+ function ButtonGroup({ className, orientation, ...props }: React.ComponentProps<typeof View> & ButtonGroupBaseProps) {
8
+ return <View className={cn('flex flex-row', className)} {...props} />;
9
+ }
10
+
11
+ function ButtonGroupText({
12
+ className,
13
+ asChild,
14
+ ...props
15
+ }: React.ComponentProps<typeof Text> & ButtonGroupTextBaseProps) {
16
+ return <Text className={cn('text-sm font-medium', className)} {...props} />;
17
+ }
18
+
19
+ function ButtonGroupSeparator({
20
+ className,
21
+ orientation,
22
+ ...props
23
+ }: React.ComponentProps<typeof View> & ButtonGroupSeparatorBaseProps) {
24
+ return <View className={cn('bg-border h-full w-px', className)} {...props} />;
25
+ }
26
+
27
+ // Stub for buttonGroupVariants to satisfy web/native shared imports if needed
28
+ const buttonGroupVariants = () => '';
29
+
30
+ export { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, buttonGroupVariants };
package/src/carousel.tsx CHANGED
@@ -5,25 +5,202 @@ import type {
5
5
  CarouselNextBaseProps,
6
6
  CarouselPreviousBaseProps,
7
7
  } from '@gv-tech/ui-core';
8
+ import { ArrowLeft, ArrowRight } from 'lucide-react-native';
8
9
  import * as React from 'react';
9
- import { View } from 'react-native';
10
+ import {
11
+ Dimensions,
12
+ ScrollView,
13
+ View,
14
+ type LayoutChangeEvent,
15
+ type NativeScrollEvent,
16
+ type NativeSyntheticEvent,
17
+ } from 'react-native';
18
+ import { Button } from './button';
19
+ import { cn } from './lib/utils';
20
+ type CarouselApi = unknown;
10
21
 
11
- export const Carousel: React.FC<CarouselBaseProps> = ({ children, className }) => {
12
- return <View className={className}>{children}</View>;
22
+ type CarouselContextType = {
23
+ orientation: 'horizontal' | 'vertical';
24
+ scrollRef: React.RefObject<ScrollView>;
25
+ scrollNext: () => void;
26
+ scrollPrev: () => void;
27
+ canScrollNext: boolean;
28
+ canScrollPrev: boolean;
29
+ itemWidth: number;
30
+ setItemWidth: (width: number) => void;
13
31
  };
14
32
 
15
- export const CarouselContent: React.FC<CarouselContentBaseProps> = ({ children, className }) => {
16
- return <View className={className}>{children}</View>;
17
- };
33
+ const CarouselContext = React.createContext<CarouselContextType | null>(null);
18
34
 
19
- export const CarouselItem: React.FC<CarouselItemBaseProps> = ({ children, className }) => {
20
- return <View className={className}>{children}</View>;
21
- };
35
+ function useCarousel() {
36
+ const context = React.useContext(CarouselContext);
37
+ if (!context) {
38
+ throw new Error('useCarousel must be used within a <Carousel />');
39
+ }
40
+ return context;
41
+ }
22
42
 
23
- export const CarouselPrevious: React.FC<CarouselPreviousBaseProps> = ({ className }) => {
24
- return <View className={className} />;
43
+ export type CarouselProps = CarouselBaseProps & {
44
+ opts?: unknown;
45
+ plugins?: unknown;
46
+ setApi?: (api: CarouselApi) => void;
25
47
  };
26
48
 
27
- export const CarouselNext: React.FC<CarouselNextBaseProps> = ({ className }) => {
28
- return <View className={className} />;
29
- };
49
+ export const Carousel = React.forwardRef<View, CarouselProps>(
50
+ ({ children, className, opts, orientation = 'horizontal', setApi, plugins, ...props }, ref) => {
51
+ const scrollRef = React.useRef<ScrollView>(null) as React.RefObject<ScrollView>;
52
+ const [canScrollNext, setCanScrollNext] = React.useState(true);
53
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false);
54
+ const [itemWidth, setItemWidth] = React.useState(Dimensions.get('window').width);
55
+ const [currentIndex, setCurrentIndex] = React.useState(0);
56
+
57
+ const scrollNext = React.useCallback(() => {
58
+ scrollRef.current?.scrollTo({ x: (currentIndex + 1) * itemWidth, animated: true });
59
+ }, [currentIndex, itemWidth]);
60
+
61
+ const scrollPrev = React.useCallback(() => {
62
+ scrollRef.current?.scrollTo({ x: Math.max(0, currentIndex - 1) * itemWidth, animated: true });
63
+ }, [currentIndex, itemWidth]);
64
+
65
+ // Very basic API shim
66
+ React.useEffect(() => {
67
+ if (setApi) {
68
+ setApi({
69
+ scrollNext,
70
+ scrollPrev,
71
+ canScrollNext: () => canScrollNext,
72
+ canScrollPrev: () => canScrollPrev,
73
+ on: () => {},
74
+ off: () => {},
75
+ } as unknown as CarouselApi);
76
+ }
77
+ }, [setApi, scrollNext, scrollPrev, canScrollNext, canScrollPrev]);
78
+
79
+ const handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
80
+ const offsetX = event.nativeEvent.contentOffset.x;
81
+ const contentWidth = event.nativeEvent.contentSize.width;
82
+ const layoutWidth = event.nativeEvent.layoutMeasurement.width;
83
+
84
+ const newIndex = Math.round(offsetX / itemWidth);
85
+ setCurrentIndex(newIndex);
86
+ setCanScrollPrev(offsetX > 0);
87
+ setCanScrollNext(offsetX + layoutWidth < contentWidth);
88
+ };
89
+
90
+ return (
91
+ <CarouselContext.Provider
92
+ value={{
93
+ orientation,
94
+ scrollRef,
95
+ scrollNext,
96
+ scrollPrev,
97
+ canScrollNext,
98
+ canScrollPrev,
99
+ itemWidth,
100
+ setItemWidth,
101
+ }}
102
+ >
103
+ <View ref={ref} className={cn('relative', className)} {...props}>
104
+ {children}
105
+ </View>
106
+ </CarouselContext.Provider>
107
+ );
108
+ },
109
+ );
110
+ Carousel.displayName = 'Carousel';
111
+
112
+ export const CarouselContent = React.forwardRef<ScrollView, CarouselContentBaseProps>(
113
+ ({ children, className, ...props }, ref) => {
114
+ const { scrollRef, orientation } = useCarousel();
115
+
116
+ return (
117
+ <View className="overflow-hidden">
118
+ <ScrollView
119
+ ref={scrollRef}
120
+ horizontal={orientation === 'horizontal'}
121
+ showsHorizontalScrollIndicator={false}
122
+ showsVerticalScrollIndicator={false}
123
+ pagingEnabled
124
+ snapToInterval={orientation === 'horizontal' ? Dimensions.get('window').width : undefined}
125
+ decelerationRate="fast"
126
+ className={cn('flex', orientation === 'horizontal' ? 'flex-row' : 'flex-col', className)}
127
+ {...props}
128
+ >
129
+ {children}
130
+ </ScrollView>
131
+ </View>
132
+ );
133
+ },
134
+ );
135
+ CarouselContent.displayName = 'CarouselContent';
136
+
137
+ export const CarouselItem = React.forwardRef<View, CarouselItemBaseProps>(({ children, className, ...props }, ref) => {
138
+ const { orientation, setItemWidth } = useCarousel();
139
+
140
+ const handleLayout = (e: LayoutChangeEvent) => {
141
+ if (orientation === 'horizontal') {
142
+ setItemWidth(e.nativeEvent.layout.width);
143
+ }
144
+ };
145
+
146
+ return (
147
+ <View ref={ref} onLayout={handleLayout} className={cn('min-w-0 shrink-0 grow-0 basis-full', className)} {...props}>
148
+ {children}
149
+ </View>
150
+ );
151
+ });
152
+ CarouselItem.displayName = 'CarouselItem';
153
+
154
+ export const CarouselPrevious = React.forwardRef<React.ElementRef<typeof Button>, CarouselPreviousBaseProps>(
155
+ ({ className, variant = 'outline', size = 'icon', ...props }, ref) => {
156
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel();
157
+
158
+ return (
159
+ <Button
160
+ ref={ref}
161
+ variant={variant as React.ComponentProps<typeof Button>['variant']}
162
+ size={size as React.ComponentProps<typeof Button>['size']}
163
+ className={cn(
164
+ 'absolute h-8 w-8 rounded-full',
165
+ orientation === 'horizontal'
166
+ ? 'top-1/2 -left-12 -translate-y-1/2'
167
+ : '-top-12 left-1/2 -translate-x-1/2 rotate-90',
168
+ className,
169
+ )}
170
+ disabled={!canScrollPrev}
171
+ onPress={scrollPrev}
172
+ {...props}
173
+ >
174
+ <ArrowLeft className="text-foreground h-4 w-4" size={16} />
175
+ </Button>
176
+ );
177
+ },
178
+ );
179
+ CarouselPrevious.displayName = 'CarouselPrevious';
180
+
181
+ export const CarouselNext = React.forwardRef<React.ElementRef<typeof Button>, CarouselNextBaseProps>(
182
+ ({ className, variant = 'outline', size = 'icon', ...props }, ref) => {
183
+ const { orientation, scrollNext, canScrollNext } = useCarousel();
184
+
185
+ return (
186
+ <Button
187
+ ref={ref}
188
+ variant={variant as React.ComponentProps<typeof Button>['variant']}
189
+ size={size as React.ComponentProps<typeof Button>['size']}
190
+ className={cn(
191
+ 'absolute h-8 w-8 rounded-full',
192
+ orientation === 'horizontal'
193
+ ? 'top-1/2 -right-12 -translate-y-1/2'
194
+ : '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
195
+ className,
196
+ )}
197
+ disabled={!canScrollNext}
198
+ onPress={scrollNext}
199
+ {...props}
200
+ >
201
+ <ArrowRight className="text-foreground h-4 w-4" size={16} />
202
+ </Button>
203
+ );
204
+ },
205
+ );
206
+ CarouselNext.displayName = 'CarouselNext';
@@ -0,0 +1,125 @@
1
+ import type {
2
+ ComboboxChipBaseProps,
3
+ ComboboxChipsBaseProps,
4
+ ComboboxChipsInputBaseProps,
5
+ ComboboxClearBaseProps,
6
+ ComboboxCollectionBaseProps,
7
+ ComboboxContentBaseProps,
8
+ ComboboxEmptyBaseProps,
9
+ ComboboxGroupBaseProps,
10
+ ComboboxInputBaseProps,
11
+ ComboboxItemBaseProps,
12
+ ComboboxLabelBaseProps,
13
+ ComboboxListBaseProps,
14
+ ComboboxSeparatorBaseProps,
15
+ ComboboxTriggerBaseProps,
16
+ ComboboxValueBaseProps,
17
+ } from '@gv-tech/ui-core';
18
+ import * as React from 'react';
19
+ import { Text, View } from 'react-native';
20
+ import { cn } from './lib/utils';
21
+
22
+ function Combobox({ className, ...props }: React.ComponentProps<typeof View>) {
23
+ return (
24
+ <View className={cn('border-destructive/50 rounded-md border border-dashed p-4', className)} {...props}>
25
+ <Text className="text-destructive font-mono text-xs">Combobox (Not Implemented)</Text>
26
+ </View>
27
+ );
28
+ }
29
+
30
+ function ComboboxValue({ className, ...props }: React.ComponentProps<typeof View> & ComboboxValueBaseProps) {
31
+ return <View className={className} {...props} />;
32
+ }
33
+
34
+ function ComboboxTrigger({ className, ...props }: React.ComponentProps<typeof View> & ComboboxTriggerBaseProps) {
35
+ return <View className={className} {...props} />;
36
+ }
37
+
38
+ function ComboboxClear({ className, ...props }: React.ComponentProps<typeof View> & ComboboxClearBaseProps) {
39
+ return <View className={className} {...props} />;
40
+ }
41
+
42
+ function ComboboxInput({
43
+ className,
44
+ showClear,
45
+ showTrigger,
46
+ ...props
47
+ }: React.ComponentProps<typeof View> & ComboboxInputBaseProps) {
48
+ return <View className={className} {...props} />;
49
+ }
50
+
51
+ function ComboboxContent({
52
+ className,
53
+ side,
54
+ sideOffset,
55
+ align,
56
+ alignOffset,
57
+ anchor,
58
+ ...props
59
+ }: React.ComponentProps<typeof View> & ComboboxContentBaseProps) {
60
+ return <View className={className} {...props} />;
61
+ }
62
+
63
+ function ComboboxList({ className, ...props }: React.ComponentProps<typeof View> & ComboboxListBaseProps) {
64
+ return <View className={className} {...props} />;
65
+ }
66
+
67
+ function ComboboxItem({ className, ...props }: React.ComponentProps<typeof View> & ComboboxItemBaseProps) {
68
+ return <View className={className} {...props} />;
69
+ }
70
+
71
+ function ComboboxGroup({ className, ...props }: React.ComponentProps<typeof View> & ComboboxGroupBaseProps) {
72
+ return <View className={className} {...props} />;
73
+ }
74
+
75
+ function ComboboxLabel({ className, ...props }: React.ComponentProps<typeof View> & ComboboxLabelBaseProps) {
76
+ return <View className={className} {...props} />;
77
+ }
78
+
79
+ function ComboboxCollection({ className, ...props }: React.ComponentProps<typeof View> & ComboboxCollectionBaseProps) {
80
+ return <View className={className} {...props} />;
81
+ }
82
+
83
+ function ComboboxEmpty({ className, ...props }: React.ComponentProps<typeof View> & ComboboxEmptyBaseProps) {
84
+ return <View className={className} {...props} />;
85
+ }
86
+
87
+ function ComboboxSeparator({ className, ...props }: React.ComponentProps<typeof View> & ComboboxSeparatorBaseProps) {
88
+ return <View className={className} {...props} />;
89
+ }
90
+
91
+ function ComboboxChips({ className, ...props }: React.ComponentProps<typeof View> & ComboboxChipsBaseProps) {
92
+ return <View className={className} {...props} />;
93
+ }
94
+
95
+ function ComboboxChip({ className, showRemove, ...props }: React.ComponentProps<typeof View> & ComboboxChipBaseProps) {
96
+ return <View className={className} {...props} />;
97
+ }
98
+
99
+ function ComboboxChipsInput({ className, ...props }: React.ComponentProps<typeof View> & ComboboxChipsInputBaseProps) {
100
+ return <View className={className} {...props} />;
101
+ }
102
+
103
+ function useComboboxAnchor() {
104
+ return React.useRef(null);
105
+ }
106
+
107
+ export {
108
+ Combobox,
109
+ ComboboxChip,
110
+ ComboboxChips,
111
+ ComboboxChipsInput,
112
+ ComboboxClear,
113
+ ComboboxCollection,
114
+ ComboboxContent,
115
+ ComboboxEmpty,
116
+ ComboboxGroup,
117
+ ComboboxInput,
118
+ ComboboxItem,
119
+ ComboboxLabel,
120
+ ComboboxList,
121
+ ComboboxSeparator,
122
+ ComboboxTrigger,
123
+ ComboboxValue,
124
+ useComboboxAnchor,
125
+ };
@@ -0,0 +1,12 @@
1
+ import type { DirectionProviderBaseProps } from '@gv-tech/ui-core';
2
+
3
+ function DirectionProvider({ children, dir, direction }: DirectionProviderBaseProps) {
4
+ // RN direction is mostly handled via I18nManager
5
+ return <>{children}</>;
6
+ }
7
+
8
+ function useDirection() {
9
+ return 'ltr';
10
+ }
11
+
12
+ export { DirectionProvider, useDirection };
package/src/empty.tsx ADDED
@@ -0,0 +1,38 @@
1
+ import type { EmptyBaseProps, EmptyMediaBaseProps } from '@gv-tech/ui-core';
2
+ import * as React from 'react';
3
+ import { Text, View } from 'react-native';
4
+ import { cn } from './lib/utils';
5
+
6
+ function Empty({ className, ...props }: React.ComponentProps<typeof View> & EmptyBaseProps) {
7
+ return (
8
+ <View
9
+ className={cn(
10
+ 'border-border flex w-full items-center justify-center rounded-xl border-2 border-dashed p-6',
11
+ className,
12
+ )}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function EmptyHeader({ className, ...props }: React.ComponentProps<typeof View>) {
19
+ return <View className={cn('flex flex-col items-center gap-2', className)} {...props} />;
20
+ }
21
+
22
+ function EmptyMedia({ className, variant, ...props }: React.ComponentProps<typeof View> & EmptyMediaBaseProps) {
23
+ return <View className={cn('flex items-center justify-center', className)} {...props} />;
24
+ }
25
+
26
+ function EmptyTitle({ className, ...props }: React.ComponentProps<typeof Text>) {
27
+ return <Text className={cn('text-foreground text-lg font-semibold', className)} {...props} />;
28
+ }
29
+
30
+ function EmptyDescription({ className, ...props }: React.ComponentProps<typeof Text>) {
31
+ return <Text className={cn('text-muted-foreground text-center text-sm', className)} {...props} />;
32
+ }
33
+
34
+ function EmptyContent({ className, ...props }: React.ComponentProps<typeof View>) {
35
+ return <View className={cn('mt-4 flex flex-col items-center gap-2', className)} {...props} />;
36
+ }
37
+
38
+ export { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle };
package/src/field.tsx ADDED
@@ -0,0 +1,89 @@
1
+ import type {
2
+ FieldBaseProps,
3
+ FieldContentBaseProps,
4
+ FieldDescriptionBaseProps,
5
+ FieldErrorBaseProps,
6
+ FieldGroupBaseProps,
7
+ FieldLabelBaseProps,
8
+ FieldLegendBaseProps,
9
+ FieldSeparatorBaseProps,
10
+ FieldSetBaseProps,
11
+ FieldTitleBaseProps,
12
+ } from '@gv-tech/ui-core';
13
+ import * as React from 'react';
14
+ import { Text, View } from 'react-native';
15
+ import { cn } from './lib/utils';
16
+
17
+ function Field({ className, orientation, ...props }: React.ComponentProps<typeof View> & FieldBaseProps) {
18
+ return <View className={cn('flex flex-col gap-2', className)} {...props} />;
19
+ }
20
+
21
+ function FieldContent({ className, ...props }: React.ComponentProps<typeof View> & FieldContentBaseProps) {
22
+ return <View className={cn('flex flex-col', className)} {...props} />;
23
+ }
24
+
25
+ function FieldDescription({ className, ...props }: React.ComponentProps<typeof Text> & FieldDescriptionBaseProps) {
26
+ return <Text className={cn('text-muted-foreground text-sm', className)} {...props} />;
27
+ }
28
+
29
+ function FieldError({
30
+ className,
31
+ errors,
32
+ children,
33
+ ...props
34
+ }: React.ComponentProps<typeof Text> & FieldErrorBaseProps) {
35
+ const message = children || (errors && errors[0]?.message);
36
+ if (!message) {
37
+ return null;
38
+ }
39
+ return (
40
+ <Text className={cn('text-destructive text-sm', className)} {...props}>
41
+ {message}
42
+ </Text>
43
+ );
44
+ }
45
+
46
+ function FieldGroup({ className, ...props }: React.ComponentProps<typeof View> & FieldGroupBaseProps) {
47
+ return <View className={cn('flex flex-col gap-4', className)} {...props} />;
48
+ }
49
+
50
+ function FieldLabel({ className, ...props }: React.ComponentProps<typeof Text> & FieldLabelBaseProps) {
51
+ return <Text className={cn('text-sm font-medium', className)} {...props} />;
52
+ }
53
+
54
+ function FieldLegend({ className, variant, ...props }: React.ComponentProps<typeof Text> & FieldLegendBaseProps) {
55
+ return <Text className={cn('font-medium', variant === 'label' ? 'text-sm' : 'text-base', className)} {...props} />;
56
+ }
57
+
58
+ function FieldSeparator({
59
+ className,
60
+ children,
61
+ ...props
62
+ }: React.ComponentProps<typeof View> & FieldSeparatorBaseProps) {
63
+ return (
64
+ <View className={cn('bg-border my-2 h-px', className)} {...props}>
65
+ {children ? <Text className="bg-background absolute px-2 text-xs">{children}</Text> : null}
66
+ </View>
67
+ );
68
+ }
69
+
70
+ function FieldSet({ className, ...props }: React.ComponentProps<typeof View> & FieldSetBaseProps) {
71
+ return <View className={cn('flex flex-col gap-4', className)} {...props} />;
72
+ }
73
+
74
+ function FieldTitle({ className, ...props }: React.ComponentProps<typeof Text> & FieldTitleBaseProps) {
75
+ return <Text className={cn('text-sm font-medium', className)} {...props} />;
76
+ }
77
+
78
+ export {
79
+ Field,
80
+ FieldContent,
81
+ FieldDescription,
82
+ FieldError,
83
+ FieldGroup,
84
+ FieldLabel,
85
+ FieldLegend,
86
+ FieldSeparator,
87
+ FieldSet,
88
+ FieldTitle,
89
+ };