@gv-tech/ui-native 2.25.2 → 2.25.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.
- package/dist/alert-dialog.d.ts.map +1 -1
- package/dist/dialog.d.ts.map +1 -1
- package/dist/dropdown-menu.d.ts.map +1 -1
- package/dist/hooks/use-theme.d.ts +30 -30
- package/dist/hooks/use-theme.d.ts.map +1 -1
- package/dist/theme-provider.d.ts +9 -1
- package/dist/theme-provider.d.ts.map +1 -1
- package/dist/theme-toggle.d.ts.map +1 -1
- package/dist/ui-native.cjs +2 -2
- package/dist/ui-native.esm.js +1052 -1045
- package/package.json +2 -1
- package/src/alert-dialog.tsx +18 -11
- package/src/dialog.tsx +28 -51
- package/src/dropdown-menu.tsx +18 -0
- package/src/hooks/use-theme.ts +7 -0
- package/src/theme-provider.tsx +36 -12
- package/src/theme-toggle.tsx +14 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gv-tech/ui-native",
|
|
3
|
-
"version": "2.25.
|
|
3
|
+
"version": "2.25.4",
|
|
4
4
|
"description": "React Native implementations of the GV Tech design system components",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"@gv-tech/design-tokens": "^2.12.0",
|
|
38
38
|
"@gv-tech/ui-core": "^2.12.0",
|
|
39
39
|
"@react-native-community/datetimepicker": "^9.1.0",
|
|
40
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
40
41
|
"@rn-primitives/accordion": "^1.2.0",
|
|
41
42
|
"@rn-primitives/alert-dialog": "^1.2.0",
|
|
42
43
|
"@rn-primitives/aspect-ratio": "^1.2.0",
|
package/src/alert-dialog.tsx
CHANGED
|
@@ -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';
|
|
@@ -43,16 +43,23 @@ const AlertDialogContent: React.ForwardRefExoticComponent<
|
|
|
43
43
|
return (
|
|
44
44
|
<AlertDialogPortal hostName={portalHost}>
|
|
45
45
|
<AlertDialogOverlay className={overlayClassName} style={overlayStyle} />
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
@@ -60,63 +60,40 @@ export type DialogContentRef = React.ComponentRef<typeof DialogPrimitive.Content
|
|
|
60
60
|
const DialogContent: React.ForwardRefExoticComponent<DialogContentProps & React.RefAttributes<DialogContentRef>> =
|
|
61
61
|
React.forwardRef<DialogContentRef, DialogContentProps>(
|
|
62
62
|
({ 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
63
|
return (
|
|
88
64
|
<DialogPortal hostName={portalHost}>
|
|
89
65
|
<DialogOverlay className={overlayClassName} style={overlayStyle} />
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
66
|
+
{/* Centering wrapper that is full screen */}
|
|
67
|
+
<View
|
|
68
|
+
pointerEvents="box-none"
|
|
69
|
+
className={cn(
|
|
70
|
+
'absolute inset-0 z-50 flex items-center justify-center p-4',
|
|
71
|
+
Platform.OS === 'web' && 'fixed',
|
|
72
|
+
)}
|
|
73
|
+
>
|
|
74
|
+
<DialogPrimitive.Content ref={ref} asChild {...props}>
|
|
75
|
+
<Animated.View
|
|
76
|
+
entering={FadeIn.duration(150)}
|
|
77
|
+
exiting={FadeOut.duration(150)}
|
|
78
|
+
className={cn(
|
|
79
|
+
'border-border bg-background w-full max-w-lg gap-4 rounded-xl border p-6 shadow-lg sm:rounded-lg',
|
|
80
|
+
className,
|
|
81
|
+
)}
|
|
96
82
|
>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
className,
|
|
103
|
-
)}
|
|
83
|
+
{children}
|
|
84
|
+
<DialogPrimitive.Close
|
|
85
|
+
className={
|
|
86
|
+
'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'
|
|
87
|
+
}
|
|
104
88
|
>
|
|
105
|
-
{
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
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>
|
|
89
|
+
<X size={18} className="text-muted-foreground" />
|
|
90
|
+
<View className="sr-only">
|
|
91
|
+
<DialogPrimitive.Title>Close</DialogPrimitive.Title>
|
|
92
|
+
</View>
|
|
93
|
+
</DialogPrimitive.Close>
|
|
94
|
+
</Animated.View>
|
|
118
95
|
</DialogPrimitive.Content>
|
|
119
|
-
</
|
|
96
|
+
</View>
|
|
120
97
|
</DialogPortal>
|
|
121
98
|
);
|
|
122
99
|
},
|
package/src/dropdown-menu.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
DropdownMenuSubContentBaseProps,
|
|
10
10
|
DropdownMenuSubTriggerBaseProps,
|
|
11
11
|
} from '@gv-tech/ui-core';
|
|
12
|
+
import * as RadixDropdownMenu from '@radix-ui/react-dropdown-menu';
|
|
12
13
|
import * as DropdownMenuPrimitive from '@rn-primitives/dropdown-menu';
|
|
13
14
|
import { Check, ChevronRight, Circle } from 'lucide-react-native';
|
|
14
15
|
import * as React from 'react';
|
|
@@ -78,6 +79,23 @@ export const DropdownMenuItem = React.forwardRef<
|
|
|
78
79
|
React.ComponentRef<typeof DropdownMenuPrimitive.Item>,
|
|
79
80
|
DropdownMenuItemBaseProps
|
|
80
81
|
>(({ className, children, inset, onSelect, ...props }, ref) => {
|
|
82
|
+
if (Platform.OS === 'web') {
|
|
83
|
+
return (
|
|
84
|
+
<RadixDropdownMenu.Item
|
|
85
|
+
ref={ref as React.Ref<HTMLDivElement>}
|
|
86
|
+
onSelect={onSelect}
|
|
87
|
+
className={cn(
|
|
88
|
+
'focus:bg-accent focus:text-accent-foreground active:bg-accent active:text-accent-foreground relative flex cursor-default flex-row items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none',
|
|
89
|
+
inset && 'pl-8',
|
|
90
|
+
className,
|
|
91
|
+
)}
|
|
92
|
+
{...props}
|
|
93
|
+
>
|
|
94
|
+
{children}
|
|
95
|
+
</RadixDropdownMenu.Item>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
81
99
|
return (
|
|
82
100
|
<DropdownMenuPrimitive.Item
|
|
83
101
|
ref={ref}
|
package/src/hooks/use-theme.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { theme as designTokens } from '@gv-tech/design-tokens';
|
|
2
|
+
import * as React from 'react';
|
|
2
3
|
import { useColorScheme } from 'react-native';
|
|
4
|
+
import { ThemeContext } from '../theme-provider';
|
|
3
5
|
|
|
4
6
|
export function useTheme() {
|
|
7
|
+
const context = React.useContext(ThemeContext);
|
|
8
|
+
if (context) {
|
|
9
|
+
return context;
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
const colorScheme = useColorScheme();
|
|
6
13
|
|
|
7
14
|
const resolvedTheme = colorScheme as 'light' | 'dark';
|
package/src/theme-provider.tsx
CHANGED
|
@@ -1,24 +1,48 @@
|
|
|
1
|
+
import { theme as designTokens } from '@gv-tech/design-tokens';
|
|
1
2
|
import * as React from 'react';
|
|
2
|
-
import { View, ViewProps } from 'react-native';
|
|
3
|
-
import { useTheme } from './hooks/use-theme';
|
|
3
|
+
import { useColorScheme, View, ViewProps } from 'react-native';
|
|
4
4
|
import { cn } from './lib/utils';
|
|
5
5
|
|
|
6
|
+
export interface ThemeContextValue {
|
|
7
|
+
theme: 'light' | 'dark' | 'system';
|
|
8
|
+
resolvedTheme: 'light' | 'dark';
|
|
9
|
+
tokens: typeof designTokens.light | typeof designTokens.dark;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const ThemeContext = React.createContext<ThemeContextValue | null>(null);
|
|
13
|
+
|
|
6
14
|
export interface ThemeProviderProps extends ViewProps {
|
|
7
15
|
children: React.ReactNode;
|
|
16
|
+
value?: 'light' | 'dark' | 'system';
|
|
8
17
|
[key: string]: unknown;
|
|
9
18
|
}
|
|
10
19
|
|
|
11
|
-
export function ThemeProvider({ children, className, style, ...props }: ThemeProviderProps) {
|
|
12
|
-
const
|
|
13
|
-
const
|
|
20
|
+
export function ThemeProvider({ children, className, style, value, ...props }: ThemeProviderProps) {
|
|
21
|
+
const systemScheme = useColorScheme();
|
|
22
|
+
const theme = value || 'system';
|
|
23
|
+
const resolvedTheme = theme === 'system' ? (systemScheme === 'dark' ? 'dark' : 'light') : theme;
|
|
24
|
+
const tokens = resolvedTheme === 'dark' ? designTokens.dark : designTokens.light;
|
|
25
|
+
|
|
26
|
+
const isDark = resolvedTheme === 'dark';
|
|
27
|
+
|
|
28
|
+
const contextValue = React.useMemo<ThemeContextValue>(
|
|
29
|
+
() => ({
|
|
30
|
+
theme,
|
|
31
|
+
resolvedTheme,
|
|
32
|
+
tokens,
|
|
33
|
+
}),
|
|
34
|
+
[theme, resolvedTheme, tokens],
|
|
35
|
+
);
|
|
14
36
|
|
|
15
37
|
return (
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
38
|
+
<ThemeContext.Provider value={contextValue}>
|
|
39
|
+
<View
|
|
40
|
+
className={cn('flex-1', isDark ? 'dark' : '', className)}
|
|
41
|
+
style={[{ backgroundColor: tokens.background }, style]}
|
|
42
|
+
{...props}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
</View>
|
|
46
|
+
</ThemeContext.Provider>
|
|
23
47
|
);
|
|
24
48
|
}
|
package/src/theme-toggle.tsx
CHANGED
|
@@ -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
|
|
|
@@ -45,7 +45,7 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
|
|
|
45
45
|
!isSystem && !isDark ? 'rotate-0 opacity-100' : 'absolute -rotate-90 opacity-0',
|
|
46
46
|
)}
|
|
47
47
|
>
|
|
48
|
-
<Sun size={18}
|
|
48
|
+
<Sun size={18} color={tokens.foreground} />
|
|
49
49
|
</View>
|
|
50
50
|
<View
|
|
51
51
|
className={cn(
|
|
@@ -53,7 +53,7 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
|
|
|
53
53
|
!isSystem && isDark ? 'rotate-0 opacity-100' : 'absolute rotate-90 opacity-0',
|
|
54
54
|
)}
|
|
55
55
|
>
|
|
56
|
-
<Moon size={18}
|
|
56
|
+
<Moon size={18} color={tokens.foreground} />
|
|
57
57
|
</View>
|
|
58
58
|
<View
|
|
59
59
|
className={cn(
|
|
@@ -61,7 +61,7 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
|
|
|
61
61
|
isSystem ? 'rotate-0 opacity-100' : 'absolute rotate-90 opacity-0',
|
|
62
62
|
)}
|
|
63
63
|
>
|
|
64
|
-
<SunMoon size={18}
|
|
64
|
+
<SunMoon size={18} color={tokens.foreground} />
|
|
65
65
|
</View>
|
|
66
66
|
</View>
|
|
67
67
|
);
|
|
@@ -69,22 +69,25 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
|
|
|
69
69
|
if (variant === 'ternary') {
|
|
70
70
|
return (
|
|
71
71
|
<DropdownMenu>
|
|
72
|
-
<DropdownMenuTrigger
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
<DropdownMenuTrigger
|
|
73
|
+
className={cn(
|
|
74
|
+
'relative h-9 w-9 flex-row items-center justify-center rounded-md bg-transparent transition-colors active:opacity-80',
|
|
75
|
+
className,
|
|
76
|
+
)}
|
|
77
|
+
>
|
|
78
|
+
<IconToggle />
|
|
76
79
|
</DropdownMenuTrigger>
|
|
77
80
|
<DropdownMenuContent align="end">
|
|
78
81
|
<DropdownMenuItem onSelect={() => handleThemeChange('light')}>
|
|
79
|
-
<Sun size={14} className="
|
|
82
|
+
<Sun size={14} color={tokens.foreground} className="mr-2" />
|
|
80
83
|
<Text>Light</Text>
|
|
81
84
|
</DropdownMenuItem>
|
|
82
85
|
<DropdownMenuItem onSelect={() => handleThemeChange('dark')}>
|
|
83
|
-
<Moon size={14} className="
|
|
86
|
+
<Moon size={14} color={tokens.foreground} className="mr-2" />
|
|
84
87
|
<Text>Dark</Text>
|
|
85
88
|
</DropdownMenuItem>
|
|
86
89
|
<DropdownMenuItem onSelect={() => handleThemeChange('system')}>
|
|
87
|
-
<SunMoon size={14} className="
|
|
90
|
+
<SunMoon size={14} color={tokens.foreground} className="mr-2" />
|
|
88
91
|
<Text>System</Text>
|
|
89
92
|
</DropdownMenuItem>
|
|
90
93
|
</DropdownMenuContent>
|