@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.
- package/dist/button-group.d.ts +9 -0
- package/dist/button-group.d.ts.map +1 -0
- package/dist/carousel.d.ts +17 -5
- package/dist/carousel.d.ts.map +1 -1
- package/dist/carousel.test.d.ts +2 -0
- package/dist/carousel.test.d.ts.map +1 -0
- package/dist/combobox.d.ts +22 -0
- package/dist/combobox.d.ts.map +1 -0
- package/dist/direction.d.ts +5 -0
- package/dist/direction.d.ts.map +1 -0
- package/dist/empty.d.ts +11 -0
- package/dist/empty.d.ts.map +1 -0
- package/dist/field.d.ts +15 -0
- package/dist/field.d.ts.map +1 -0
- package/dist/form.d.ts +30 -1
- package/dist/form.d.ts.map +1 -1
- package/dist/form.test.d.ts +2 -0
- package/dist/form.test.d.ts.map +1 -0
- package/dist/hooks/use-theme.d.ts +16 -0
- package/dist/hooks/use-theme.d.ts.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/input-group.d.ts +12 -0
- package/dist/input-group.d.ts.map +1 -0
- package/dist/input-otp.d.ts +9 -0
- package/dist/input-otp.d.ts.map +1 -0
- package/dist/item.d.ts +15 -0
- package/dist/item.d.ts.map +1 -0
- package/dist/kbd.d.ts +7 -0
- package/dist/kbd.d.ts.map +1 -0
- package/dist/native-select.d.ts +8 -0
- package/dist/native-select.d.ts.map +1 -0
- package/dist/scroll-area.d.ts.map +1 -1
- package/dist/search.d.ts +9 -2
- package/dist/search.d.ts.map +1 -1
- package/dist/sidebar.d.ts +37 -0
- package/dist/sidebar.d.ts.map +1 -0
- package/dist/sonner.d.ts.map +1 -1
- package/dist/spinner.d.ts +6 -0
- package/dist/spinner.d.ts.map +1 -0
- package/dist/ui-native.cjs +2 -2
- package/dist/ui-native.mjs +1702 -907
- package/package.json +10 -5
- package/src/button-group.tsx +30 -0
- package/src/carousel.tsx +191 -14
- package/src/combobox.tsx +125 -0
- package/src/direction.tsx +12 -0
- package/src/empty.tsx +38 -0
- package/src/field.tsx +89 -0
- package/src/form.tsx +166 -4
- package/src/index.ts +108 -0
- package/src/input-group.tsx +54 -0
- package/src/input-otp.tsx +34 -0
- package/src/item.tsx +74 -0
- package/src/kbd.tsx +16 -0
- package/src/native-select.tsx +30 -0
- package/src/scroll-area.tsx +4 -2
- package/src/search.tsx +74 -12
- package/src/sidebar.tsx +209 -0
- package/src/sonner.tsx +7 -3
- 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.
|
|
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-
|
|
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 {
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
16
|
-
return <View className={className}>{children}</View>;
|
|
17
|
-
};
|
|
33
|
+
const CarouselContext = React.createContext<CarouselContextType | null>(null);
|
|
18
34
|
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
24
|
-
|
|
43
|
+
export type CarouselProps = CarouselBaseProps & {
|
|
44
|
+
opts?: unknown;
|
|
45
|
+
plugins?: unknown;
|
|
46
|
+
setApi?: (api: CarouselApi) => void;
|
|
25
47
|
};
|
|
26
48
|
|
|
27
|
-
export const
|
|
28
|
-
|
|
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';
|
package/src/combobox.tsx
ADDED
|
@@ -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
|
+
};
|