@gv-tech/ui-native 2.25.1 → 2.25.3

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.1",
3
+ "version": "2.25.3",
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",
@@ -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}
@@ -1,8 +1,15 @@
1
1
  import { theme as designTokens } from '@gv-tech/design-tokens';
2
- import { useColorScheme } from 'nativewind';
2
+ import * as React from 'react';
3
+ import { useColorScheme } from 'react-native';
4
+ import { ThemeContext } from '../theme-provider';
3
5
 
4
6
  export function useTheme() {
5
- const { colorScheme, setColorScheme } = useColorScheme();
7
+ const context = React.useContext(ThemeContext);
8
+ if (context) {
9
+ return context;
10
+ }
11
+
12
+ const colorScheme = useColorScheme();
6
13
 
7
14
  const resolvedTheme = colorScheme as 'light' | 'dark';
8
15
 
@@ -11,7 +18,6 @@ export function useTheme() {
11
18
 
12
19
  return {
13
20
  theme: colorScheme,
14
- setTheme: setColorScheme,
15
21
  resolvedTheme,
16
22
  tokens: activeTokens,
17
23
  };
@@ -1,9 +1,48 @@
1
- import { useColorScheme } from 'nativewind';
1
+ import { theme as designTokens } from '@gv-tech/design-tokens';
2
2
  import * as React from 'react';
3
- import { View } from 'react-native';
3
+ import { useColorScheme, View, ViewProps } from 'react-native';
4
4
  import { cn } from './lib/utils';
5
5
 
6
- export function ThemeProvider({ children, className }: { children: React.ReactNode; className?: string }) {
7
- const { colorScheme } = useColorScheme();
8
- return <View className={cn('flex-1', colorScheme === 'dark' ? 'dark' : '', className)}>{children}</View>;
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
+
14
+ export interface ThemeProviderProps extends ViewProps {
15
+ children: React.ReactNode;
16
+ value?: 'light' | 'dark' | 'system';
17
+ [key: string]: unknown;
18
+ }
19
+
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
+ );
36
+
37
+ return (
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>
47
+ );
9
48
  }
@@ -1,6 +1,6 @@
1
1
  import { ThemeToggleBaseProps } from '@gv-tech/ui-core';
2
2
  import { Moon, Sun, SunMoon } from 'lucide-react-native';
3
- import { View } from 'react-native';
3
+ import { Appearance, View } from 'react-native';
4
4
  import { Button } from './button';
5
5
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from './dropdown-menu';
6
6
  import { useTheme } from './hooks/use-theme';
@@ -15,12 +15,12 @@ iconWithClassName(SunMoon);
15
15
  export type ThemeToggleProps = ThemeToggleBaseProps;
16
16
 
17
17
  export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, className }: ThemeToggleProps) {
18
- const { theme, setTheme, resolvedTheme } = useTheme();
18
+ const { theme, resolvedTheme } = useTheme();
19
19
 
20
20
  const currentTheme = (customTheme ?? theme) as 'light' | 'dark' | 'system';
21
21
 
22
22
  // Determine the effective theme for icon rendering
23
- const effectiveTheme = customTheme ? customTheme : resolvedTheme;
23
+ const effectiveTheme = customTheme === 'system' || !customTheme ? resolvedTheme : customTheme;
24
24
  const isDark = effectiveTheme === 'dark';
25
25
  const isSystem = currentTheme === 'system';
26
26
 
@@ -28,7 +28,12 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
28
28
  if (onThemeChange) {
29
29
  onThemeChange(newTheme);
30
30
  } else {
31
- setTheme(newTheme as 'light' | 'dark');
31
+ if (newTheme === 'system') {
32
+ // @ts-expect-error React Native Appearance.setColorScheme accepts null to reset to system theme
33
+ Appearance.setColorScheme(null);
34
+ } else {
35
+ Appearance.setColorScheme(newTheme);
36
+ }
32
37
  }
33
38
  };
34
39
 
@@ -64,10 +69,13 @@ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, cl
64
69
  if (variant === 'ternary') {
65
70
  return (
66
71
  <DropdownMenu>
67
- <DropdownMenuTrigger asChild>
68
- <Button variant="ghost" size="icon" className={cn('relative h-9 w-9', className)}>
69
- <IconToggle />
70
- </Button>
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 />
71
79
  </DropdownMenuTrigger>
72
80
  <DropdownMenuContent align="end">
73
81
  <DropdownMenuItem onSelect={() => handleThemeChange('light')}>