@gv-tech/ui-native 2.25.3 → 2.26.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gv-tech/ui-native",
3
- "version": "2.25.3",
3
+ "version": "2.26.0",
4
4
  "description": "React Native implementations of the GV Tech design system components",
5
5
  "repository": {
6
6
  "type": "git",
package/src/accordion.tsx CHANGED
@@ -55,7 +55,7 @@ const AccordionTrigger: React.ForwardRefExoticComponent<
55
55
  isExpanded ? withTiming(1, { duration: 250 }) : withTiming(0, { duration: 200 }),
56
56
  );
57
57
  const chevronStyle = useAnimatedStyle(() => ({
58
- transform: [{ rotate: `${progress.value * 180}deg` }],
58
+ transform: [{ rotate: `${progress.value * -180}deg` }],
59
59
  opacity: interpolate(progress.value, [0, 1], [1, 0.8], Extrapolation.CLAMP),
60
60
  }));
61
61
 
@@ -1,6 +1,6 @@
1
1
  import * as AlertDialogPrimitive from '@rn-primitives/alert-dialog';
2
2
  import * as React from 'react';
3
- import { StyleSheet, View, type ViewStyle } from 'react-native';
3
+ import { Platform, StyleSheet, View, type ViewStyle } from 'react-native';
4
4
  import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
5
5
 
6
6
  import { buttonVariants } from './button';
@@ -23,7 +23,7 @@ const AlertDialogOverlay: React.ForwardRefExoticComponent<
23
23
  <Animated.View
24
24
  entering={FadeIn.duration(150)}
25
25
  exiting={FadeOut.duration(150)}
26
- className={cn('z-50 flex items-center justify-center bg-black/80 p-2', className)}
26
+ className={cn('z-50 flex items-center justify-center bg-black/60 p-2 backdrop-blur-md', className)}
27
27
  />
28
28
  </AlertDialogPrimitive.Overlay>
29
29
  );
@@ -43,16 +43,23 @@ const AlertDialogContent: React.ForwardRefExoticComponent<
43
43
  return (
44
44
  <AlertDialogPortal hostName={portalHost}>
45
45
  <AlertDialogOverlay className={overlayClassName} style={overlayStyle} />
46
- <AlertDialogPrimitive.Content ref={ref} asChild {...props}>
47
- <Animated.View
48
- entering={FadeIn.duration(150)}
49
- exiting={FadeOut.duration(150)}
50
- className={cn(
51
- 'border-border bg-background z-50 w-full max-w-lg gap-4 rounded-xl border p-6 shadow-lg sm:rounded-lg',
52
- className,
53
- )}
54
- />
55
- </AlertDialogPrimitive.Content>
46
+ <View
47
+ pointerEvents="box-none"
48
+ className={cn('absolute inset-0 z-50 flex items-center justify-center p-4', Platform.OS === 'web' && 'fixed')}
49
+ >
50
+ <AlertDialogPrimitive.Content ref={ref} asChild {...props}>
51
+ <Animated.View
52
+ entering={FadeIn.duration(150)}
53
+ exiting={FadeOut.duration(150)}
54
+ className={cn(
55
+ 'border-border bg-background w-full max-w-lg gap-4 rounded-xl border p-6 shadow-lg sm:rounded-lg',
56
+ className,
57
+ )}
58
+ >
59
+ {props.children}
60
+ </Animated.View>
61
+ </AlertDialogPrimitive.Content>
62
+ </View>
56
63
  </AlertDialogPortal>
57
64
  );
58
65
  });
package/src/dialog.tsx CHANGED
@@ -6,6 +6,7 @@ import { Platform, View, type StyleProp, type ViewStyle } from 'react-native';
6
6
  import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
7
7
 
8
8
  import { cn } from './lib/utils';
9
+ import { Text } from './text';
9
10
 
10
11
  const Dialog = DialogPrimitive.Root;
11
12
 
@@ -49,7 +50,7 @@ const DialogOverlay: React.ForwardRefExoticComponent<DialogOverlayProps & React.
49
50
  <Animated.View
50
51
  entering={FadeIn.duration(150)}
51
52
  exiting={FadeOut.duration(150)}
52
- className={cn('flex items-center justify-center bg-black/80 p-2', className)}
53
+ className={cn('flex items-center justify-center bg-black/60 p-2 backdrop-blur-md', className)}
53
54
  />
54
55
  </DialogPrimitive.Overlay>
55
56
  );
@@ -60,63 +61,38 @@ export type DialogContentRef = React.ComponentRef<typeof DialogPrimitive.Content
60
61
  const DialogContent: React.ForwardRefExoticComponent<DialogContentProps & React.RefAttributes<DialogContentRef>> =
61
62
  React.forwardRef<DialogContentRef, DialogContentProps>(
62
63
  ({ className, children, portalHost, overlayClassName, overlayStyle, ...props }, ref) => {
63
- const PlatformWrapper = React.useCallback(({ children }: { children: React.ReactNode }) => {
64
- if (Platform.OS === 'web') {
65
- return <>{children}</>;
66
- }
67
- return (
68
- <View
69
- pointerEvents="box-none"
70
- style={{
71
- position: 'absolute',
72
- top: 0,
73
- right: 0,
74
- bottom: 0,
75
- left: 0,
76
- zIndex: 50,
77
- alignItems: 'center',
78
- justifyContent: 'center',
79
- padding: 16,
80
- }}
81
- >
82
- {children}
83
- </View>
84
- );
85
- }, []);
86
-
87
64
  return (
88
65
  <DialogPortal hostName={portalHost}>
89
66
  <DialogOverlay className={overlayClassName} style={overlayStyle} />
90
- <PlatformWrapper>
91
- <DialogPrimitive.Content ref={ref} {...props}>
92
- <View
93
- pointerEvents="box-none"
94
- className="absolute inset-0 z-50 flex items-center justify-center"
95
- style={Platform.OS === 'web' ? ({ position: 'fixed' } as unknown as ViewStyle) : undefined}
67
+ {/* Centering wrapper that is full screen */}
68
+ <View
69
+ pointerEvents="box-none"
70
+ className={cn(
71
+ 'absolute inset-0 z-50 flex items-center justify-center p-4',
72
+ Platform.OS === 'web' && 'fixed',
73
+ )}
74
+ >
75
+ <DialogPrimitive.Content ref={ref} asChild {...props}>
76
+ <Animated.View
77
+ entering={FadeIn.duration(150)}
78
+ exiting={FadeOut.duration(150)}
79
+ className={cn(
80
+ 'border-border bg-background w-full max-w-lg gap-4 rounded-xl border p-6 shadow-lg sm:rounded-lg',
81
+ className,
82
+ )}
96
83
  >
97
- <Animated.View
98
- entering={FadeIn.duration(150)}
99
- exiting={FadeOut.duration(150)}
100
- className={cn(
101
- 'border-border bg-background w-full max-w-lg gap-4 rounded-xl border p-6 shadow-lg sm:rounded-lg',
102
- className,
103
- )}
84
+ {children}
85
+ <DialogPrimitive.Close
86
+ className={
87
+ 'ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:pointer-events-none'
88
+ }
104
89
  >
105
- {children}
106
- <DialogPrimitive.Close
107
- className={
108
- 'ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:pointer-events-none'
109
- }
110
- >
111
- <X size={18} className="text-muted-foreground" />
112
- <View className="sr-only">
113
- <DialogPrimitive.Title>Close</DialogPrimitive.Title>
114
- </View>
115
- </DialogPrimitive.Close>
116
- </Animated.View>
117
- </View>
90
+ <X size={18} className="text-muted-foreground" />
91
+ <Text className="sr-only">Close</Text>
92
+ </DialogPrimitive.Close>
93
+ </Animated.View>
118
94
  </DialogPrimitive.Content>
119
- </PlatformWrapper>
95
+ </View>
120
96
  </DialogPortal>
121
97
  );
122
98
  },
package/src/drawer.tsx CHANGED
@@ -10,13 +10,15 @@ import type {
10
10
  } from '@gv-tech/ui-core';
11
11
  import * as DialogPrimitive from '@rn-primitives/dialog';
12
12
  import * as React from 'react';
13
- import { StyleSheet, View, type ViewStyle } from 'react-native';
13
+ import { Platform, View, type StyleProp, type ViewStyle } from 'react-native';
14
14
  import Animated, { FadeIn, FadeOut, SlideInDown, SlideOutDown } from 'react-native-reanimated';
15
15
  import { wrapTextChildren } from './lib/render-native';
16
16
  import { cn } from './lib/utils';
17
17
 
18
- export const Drawer: React.FC<DrawerBaseProps> = ({ children }) => {
19
- return <DialogPrimitive.Root>{children}</DialogPrimitive.Root>;
18
+ export interface DrawerProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root>, DrawerBaseProps {}
19
+
20
+ export const Drawer: React.FC<DrawerProps> = ({ children, ...props }) => {
21
+ return <DialogPrimitive.Root {...props}>{children}</DialogPrimitive.Root>;
20
22
  };
21
23
  Drawer.displayName = 'Drawer';
22
24
 
@@ -39,13 +41,28 @@ export type DrawerOverlayRef = React.ComponentRef<typeof DialogPrimitive.Overlay
39
41
 
40
42
  export const DrawerOverlay: React.ForwardRefExoticComponent<
41
43
  DrawerOverlayProps & React.RefAttributes<DrawerOverlayRef>
42
- > = React.forwardRef<DrawerOverlayRef, DrawerOverlayProps>(({ className, ...props }, ref) => {
44
+ > = React.forwardRef<DrawerOverlayRef, DrawerOverlayProps>(({ className, style, ...props }, ref) => {
43
45
  return (
44
- <DialogPrimitive.Overlay style={StyleSheet.absoluteFill} asChild ref={ref} {...props}>
46
+ <DialogPrimitive.Overlay
47
+ style={[
48
+ {
49
+ position: Platform.OS === 'web' ? 'fixed' : 'absolute',
50
+ top: 0,
51
+ right: 0,
52
+ bottom: 0,
53
+ left: 0,
54
+ zIndex: 50,
55
+ } as unknown as ViewStyle,
56
+ style as StyleProp<ViewStyle>,
57
+ ]}
58
+ asChild
59
+ ref={ref}
60
+ {...props}
61
+ >
45
62
  <Animated.View
46
63
  entering={FadeIn.duration(150)}
47
64
  exiting={FadeOut.duration(150)}
48
- className={cn('z-50 bg-black/80', className)}
65
+ className={cn('bg-black/80', className)}
49
66
  />
50
67
  </DialogPrimitive.Overlay>
51
68
  );
@@ -68,7 +85,8 @@ export const DrawerContent = React.forwardRef<
68
85
  entering={SlideInDown.duration(200)}
69
86
  exiting={SlideOutDown.duration(200)}
70
87
  className={cn(
71
- 'border-border bg-background fixed inset-x-0 bottom-0 z-50 flex h-auto flex-col rounded-t-xl border p-6 pb-10 shadow-lg',
88
+ 'border-border bg-background z-50 flex h-auto flex-col rounded-t-xl border p-6 pb-10 shadow-lg',
89
+ Platform.OS === 'web' ? 'fixed inset-x-0 bottom-0' : 'absolute inset-x-0 bottom-0',
72
90
  className,
73
91
  )}
74
92
  >
@@ -44,7 +44,7 @@ const DropdownMenuOverlay = React.forwardRef<
44
44
  <Animated.View
45
45
  entering={FadeIn.duration(100)}
46
46
  exiting={FadeOut.duration(100)}
47
- className={cn('absolute inset-0 z-50 bg-black/30', className)}
47
+ className={cn('absolute inset-0 z-50 bg-black/30 backdrop-blur-md', className)}
48
48
  />
49
49
  </DropdownMenuPrimitive.Overlay>
50
50
  );
package/src/sheet.tsx CHANGED
@@ -2,7 +2,18 @@ import * as DialogPrimitive from '@rn-primitives/dialog';
2
2
  import { X } from 'lucide-react-native';
3
3
  import * as React from 'react';
4
4
  import { Platform, StyleSheet, View, type ViewStyle } from 'react-native';
5
- import Animated, { FadeIn, FadeOut, SlideInRight, SlideOutRight } from 'react-native-reanimated';
5
+ import Animated, {
6
+ FadeIn,
7
+ FadeOut,
8
+ SlideInDown,
9
+ SlideInLeft,
10
+ SlideInRight,
11
+ SlideInUp,
12
+ SlideOutDown,
13
+ SlideOutLeft,
14
+ SlideOutRight,
15
+ SlideOutUp,
16
+ } from 'react-native-reanimated';
6
17
 
7
18
  import { type DialogContentProps } from './dialog';
8
19
  import { cn } from './lib/utils';
@@ -30,7 +41,7 @@ const SheetOverlay: React.ForwardRefExoticComponent<SheetOverlayProps & React.Re
30
41
  <Animated.View
31
42
  entering={FadeIn.duration(150)}
32
43
  exiting={FadeOut.duration(150)}
33
- className={cn('web:cursor-default absolute inset-0 z-50 bg-black/80', className)}
44
+ className={cn('web:cursor-default absolute inset-0 z-50 bg-black/60 backdrop-blur-md', className)}
34
45
  />
35
46
  </DialogPrimitive.Overlay>
36
47
  );
@@ -48,8 +59,26 @@ const SheetContent: React.ForwardRefExoticComponent<SheetContentProps & React.Re
48
59
  ({ className, children, side = 'right', overlayClassName, overlayStyle, ...props }, ref) => {
49
60
  const isWeb = Platform.OS === 'web';
50
61
  // TODO: Add support for other sides
51
- const entering = isWeb ? undefined : SlideInRight;
52
- const exiting = isWeb ? undefined : SlideOutRight;
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ let entering: any = undefined;
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ let exiting: any = undefined;
66
+
67
+ if (!isWeb) {
68
+ if (side === 'bottom') {
69
+ entering = SlideInDown;
70
+ exiting = SlideOutDown;
71
+ } else if (side === 'top') {
72
+ entering = SlideInUp;
73
+ exiting = SlideOutUp;
74
+ } else if (side === 'left') {
75
+ entering = SlideInLeft;
76
+ exiting = SlideOutLeft;
77
+ } else {
78
+ entering = SlideInRight;
79
+ exiting = SlideOutRight;
80
+ }
81
+ }
53
82
 
54
83
  return (
55
84
  <SheetPortal>
@@ -15,7 +15,7 @@ iconWithClassName(SunMoon);
15
15
  export type ThemeToggleProps = ThemeToggleBaseProps;
16
16
 
17
17
  export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, className }: ThemeToggleProps) {
18
- const { theme, resolvedTheme } = useTheme();
18
+ const { theme, resolvedTheme, tokens } = useTheme();
19
19
 
20
20
  const currentTheme = (customTheme ?? theme) as 'light' | 'dark' | 'system';
21
21
 
@@ -29,8 +29,7 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
29
29
  onThemeChange(newTheme);
30
30
  } else {
31
31
  if (newTheme === 'system') {
32
- // @ts-expect-error React Native Appearance.setColorScheme accepts null to reset to system theme
33
- Appearance.setColorScheme(null);
32
+ Appearance.setColorScheme('unspecified');
34
33
  } else {
35
34
  Appearance.setColorScheme(newTheme);
36
35
  }
@@ -45,7 +44,7 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
45
44
  !isSystem && !isDark ? 'rotate-0 opacity-100' : 'absolute -rotate-90 opacity-0',
46
45
  )}
47
46
  >
48
- <Sun size={18} className="text-foreground" />
47
+ <Sun size={18} color={tokens.foreground} />
49
48
  </View>
50
49
  <View
51
50
  className={cn(
@@ -53,7 +52,7 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
53
52
  !isSystem && isDark ? 'rotate-0 opacity-100' : 'absolute rotate-90 opacity-0',
54
53
  )}
55
54
  >
56
- <Moon size={18} className="text-foreground" />
55
+ <Moon size={18} color={tokens.foreground} />
57
56
  </View>
58
57
  <View
59
58
  className={cn(
@@ -61,7 +60,7 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
61
60
  isSystem ? 'rotate-0 opacity-100' : 'absolute rotate-90 opacity-0',
62
61
  )}
63
62
  >
64
- <SunMoon size={18} className="text-foreground" />
63
+ <SunMoon size={18} color={tokens.foreground} />
65
64
  </View>
66
65
  </View>
67
66
  );
@@ -79,15 +78,15 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
79
78
  </DropdownMenuTrigger>
80
79
  <DropdownMenuContent align="end">
81
80
  <DropdownMenuItem onSelect={() => handleThemeChange('light')}>
82
- <Sun size={14} className="text-foreground mr-2" />
81
+ <Sun size={14} color={tokens.foreground} className="mr-2" />
83
82
  <Text>Light</Text>
84
83
  </DropdownMenuItem>
85
84
  <DropdownMenuItem onSelect={() => handleThemeChange('dark')}>
86
- <Moon size={14} className="text-foreground mr-2" />
85
+ <Moon size={14} color={tokens.foreground} className="mr-2" />
87
86
  <Text>Dark</Text>
88
87
  </DropdownMenuItem>
89
88
  <DropdownMenuItem onSelect={() => handleThemeChange('system')}>
90
- <SunMoon size={14} className="text-foreground mr-2" />
89
+ <SunMoon size={14} color={tokens.foreground} className="mr-2" />
91
90
  <Text>System</Text>
92
91
  </DropdownMenuItem>
93
92
  </DropdownMenuContent>