@umituz/react-native-design-system 4.23.45 → 4.23.47
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": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.47",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -155,8 +155,5 @@
|
|
|
155
155
|
"src",
|
|
156
156
|
"README.md",
|
|
157
157
|
"LICENSE"
|
|
158
|
-
]
|
|
159
|
-
"dependencies": {
|
|
160
|
-
"@umituz/react-native-design-system": "^2.10.1"
|
|
161
|
-
}
|
|
158
|
+
]
|
|
162
159
|
}
|
|
@@ -1,70 +1,77 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Theme Storage
|
|
3
|
-
* Persists theme
|
|
4
|
-
*
|
|
5
|
-
* CRITICAL: This is a standalone storage utility for theme package.
|
|
6
|
-
* Apps should use this for theme persistence.
|
|
3
|
+
* Persists theme mode and custom colors using AsyncStorage
|
|
7
4
|
*/
|
|
8
5
|
|
|
9
6
|
import { storageRepository, unwrap } from '../../../storage';
|
|
10
7
|
import type { ThemeMode } from '../../core/ColorPalette';
|
|
8
|
+
import type { CustomThemeColors } from '../../core/CustomColors';
|
|
11
9
|
import { DESIGN_CONSTANTS } from '../../core/constants/DesignConstants';
|
|
12
10
|
|
|
13
|
-
const
|
|
11
|
+
const MODE_KEY = `${DESIGN_CONSTANTS.STORAGE_NAMESPACE}/mode`;
|
|
12
|
+
const COLORS_KEY = `${DESIGN_CONSTANTS.STORAGE_NAMESPACE}/colors`;
|
|
14
13
|
|
|
15
14
|
export class ThemeStorage {
|
|
16
|
-
/**
|
|
17
|
-
* Get stored theme mode
|
|
18
|
-
*/
|
|
19
15
|
static async getThemeMode(): Promise<ThemeMode | null> {
|
|
20
16
|
try {
|
|
21
|
-
const result = await storageRepository.getString(
|
|
17
|
+
const result = await storageRepository.getString(MODE_KEY, '');
|
|
22
18
|
const value = unwrap(result, '');
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Validate theme mode value
|
|
29
|
-
if (value === 'light' || value === 'dark') {
|
|
30
|
-
return value as ThemeMode;
|
|
31
|
-
}
|
|
32
|
-
|
|
19
|
+
if (!value) return null;
|
|
20
|
+
if (value === 'light' || value === 'dark') return value as ThemeMode;
|
|
33
21
|
return null;
|
|
34
22
|
} catch {
|
|
35
|
-
// Return null instead of throwing to prevent app crashes
|
|
36
23
|
return null;
|
|
37
24
|
}
|
|
38
25
|
}
|
|
39
26
|
|
|
40
|
-
/**
|
|
41
|
-
* Save theme mode
|
|
42
|
-
*/
|
|
43
27
|
static async setThemeMode(mode: ThemeMode): Promise<void> {
|
|
44
28
|
try {
|
|
45
|
-
// Validate input
|
|
46
29
|
if (!mode || (mode !== 'light' && mode !== 'dark')) {
|
|
47
30
|
throw new Error(`Invalid theme mode: ${mode}`);
|
|
48
31
|
}
|
|
49
|
-
|
|
50
|
-
await storageRepository.setString(STORAGE_KEY, mode);
|
|
32
|
+
await storageRepository.setString(MODE_KEY, mode);
|
|
51
33
|
} catch (error) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
if (errorMessage.includes('Invalid theme mode')) {
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
34
|
+
const msg = error instanceof Error ? error.message : '';
|
|
35
|
+
if (msg.includes('Invalid theme mode')) throw error;
|
|
57
36
|
}
|
|
58
37
|
}
|
|
59
38
|
|
|
60
|
-
/**
|
|
61
|
-
* Clear stored theme mode
|
|
62
|
-
*/
|
|
63
39
|
static async clearThemeMode(): Promise<void> {
|
|
64
40
|
try {
|
|
65
|
-
await storageRepository.removeItem(
|
|
41
|
+
await storageRepository.removeItem(MODE_KEY);
|
|
42
|
+
} catch {
|
|
43
|
+
// Silent fail
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static async getCustomColors(): Promise<CustomThemeColors | undefined> {
|
|
48
|
+
try {
|
|
49
|
+
const result = await storageRepository.getString(COLORS_KEY, '');
|
|
50
|
+
const value = unwrap(result, '');
|
|
51
|
+
if (!value) return undefined;
|
|
52
|
+
return JSON.parse(value) as CustomThemeColors;
|
|
53
|
+
} catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static async setCustomColors(colors?: CustomThemeColors): Promise<void> {
|
|
59
|
+
try {
|
|
60
|
+
if (!colors || Object.keys(colors).length === 0) {
|
|
61
|
+
await storageRepository.removeItem(COLORS_KEY);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
await storageRepository.setString(COLORS_KEY, JSON.stringify(colors));
|
|
65
|
+
} catch {
|
|
66
|
+
// Silent fail
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static async clearCustomColors(): Promise<void> {
|
|
71
|
+
try {
|
|
72
|
+
await storageRepository.removeItem(COLORS_KEY);
|
|
66
73
|
} catch {
|
|
67
|
-
//
|
|
74
|
+
// Silent fail
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
}
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Theme Store - Zustand State Management
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Architecture:
|
|
7
|
-
* - Zustand for state management (NOT Context API)
|
|
8
|
-
* - AsyncStorage for persistence via ThemeStorage
|
|
9
|
-
* - Automatic initialization on app launch
|
|
10
|
-
* - Syncs with design system global theme store
|
|
4
|
+
* Single source of truth for theme mode AND custom colors.
|
|
5
|
+
* Uses AsyncStorage for persistence via ThemeStorage.
|
|
11
6
|
*/
|
|
12
7
|
|
|
13
8
|
import { createStore } from '../../../storage';
|
|
@@ -15,82 +10,66 @@ import { lightTheme, darkTheme, type Theme } from '../../core/themes';
|
|
|
15
10
|
import { ThemeStorage } from '../storage/ThemeStorage';
|
|
16
11
|
import { useDesignSystemTheme } from '../globalThemeStore';
|
|
17
12
|
import type { ThemeMode } from '../../core/ColorPalette';
|
|
13
|
+
import type { CustomThemeColors } from '../../core/CustomColors';
|
|
18
14
|
|
|
19
15
|
interface ThemeState {
|
|
20
16
|
theme: Theme;
|
|
21
17
|
themeMode: ThemeMode;
|
|
18
|
+
customColors?: CustomThemeColors;
|
|
22
19
|
isDark: boolean;
|
|
23
20
|
isInitialized: boolean;
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
interface ThemeActions {
|
|
27
24
|
setThemeMode: (mode: ThemeMode) => Promise<void>;
|
|
25
|
+
setCustomColors: (colors?: CustomThemeColors) => Promise<void>;
|
|
28
26
|
toggleTheme: () => Promise<void>;
|
|
29
27
|
initialize: () => Promise<void>;
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
// Mutex to prevent race condition in setThemeMode
|
|
33
30
|
let themeUpdateInProgress = false;
|
|
34
|
-
// Mutex to prevent race condition in initialize
|
|
35
31
|
let themeInitInProgress = false;
|
|
36
32
|
|
|
37
|
-
/**
|
|
38
|
-
* Theme Store - Global state management for theme
|
|
39
|
-
*
|
|
40
|
-
* Usage:
|
|
41
|
-
* ```typescript
|
|
42
|
-
* import { useTheme } from '@umituz/react-native-design-system';
|
|
43
|
-
*
|
|
44
|
-
* const MyComponent = () => {
|
|
45
|
-
* const { theme, themeMode, setThemeMode, toggleTheme } = useTheme();
|
|
46
|
-
* // ...
|
|
47
|
-
* };
|
|
48
|
-
* ```
|
|
49
|
-
*
|
|
50
|
-
* NOTE: Make sure to wrap your app with DesignSystemProvider for auto-initialization
|
|
51
|
-
*/
|
|
52
33
|
export const useTheme = createStore<ThemeState, ThemeActions>({
|
|
53
34
|
name: 'theme-store',
|
|
54
35
|
initialState: {
|
|
55
36
|
theme: lightTheme,
|
|
56
37
|
themeMode: 'light',
|
|
38
|
+
customColors: undefined,
|
|
57
39
|
isDark: false,
|
|
58
40
|
isInitialized: false,
|
|
59
41
|
},
|
|
60
42
|
persist: false,
|
|
61
43
|
actions: (set, get) => ({
|
|
62
44
|
initialize: async () => {
|
|
63
|
-
// Atomic check with mutex
|
|
64
45
|
const { isInitialized } = get();
|
|
65
|
-
if (isInitialized || themeInitInProgress)
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
46
|
+
if (isInitialized || themeInitInProgress) return;
|
|
68
47
|
|
|
69
48
|
themeInitInProgress = true;
|
|
70
49
|
|
|
71
50
|
try {
|
|
72
|
-
const savedMode = await
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
51
|
+
const [savedMode, savedColors] = await Promise.all([
|
|
52
|
+
ThemeStorage.getThemeMode(),
|
|
53
|
+
ThemeStorage.getCustomColors(),
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
const mode = savedMode || 'light';
|
|
57
|
+
const theme = mode === 'light' ? lightTheme : darkTheme;
|
|
58
|
+
|
|
59
|
+
set({
|
|
60
|
+
themeMode: mode,
|
|
61
|
+
theme,
|
|
62
|
+
customColors: savedColors,
|
|
63
|
+
isDark: mode === 'dark',
|
|
64
|
+
isInitialized: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Sync with design system global theme
|
|
68
|
+
const dsTheme = useDesignSystemTheme.getState();
|
|
69
|
+
dsTheme.setThemeMode(mode);
|
|
70
|
+
dsTheme.setCustomColors(savedColors);
|
|
90
71
|
} catch {
|
|
91
|
-
// Silent failure - still mark as initialized to prevent blocking
|
|
92
72
|
set({ isInitialized: true });
|
|
93
|
-
// Ensure design system store is synced even on error
|
|
94
73
|
useDesignSystemTheme.getState().setThemeMode('light');
|
|
95
74
|
} finally {
|
|
96
75
|
themeInitInProgress = false;
|
|
@@ -98,44 +77,34 @@ export const useTheme = createStore<ThemeState, ThemeActions>({
|
|
|
98
77
|
},
|
|
99
78
|
|
|
100
79
|
setThemeMode: async (mode: ThemeMode) => {
|
|
101
|
-
|
|
102
|
-
if (themeUpdateInProgress) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
80
|
+
if (themeUpdateInProgress) return;
|
|
106
81
|
themeUpdateInProgress = true;
|
|
107
82
|
|
|
108
83
|
try {
|
|
109
84
|
const theme = mode === 'light' ? lightTheme : darkTheme;
|
|
110
|
-
|
|
111
|
-
// Set state first
|
|
112
|
-
set({
|
|
113
|
-
themeMode: mode,
|
|
114
|
-
theme,
|
|
115
|
-
isDark: mode === 'dark',
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// Then persist (even if this fails, UI is already updated)
|
|
85
|
+
set({ themeMode: mode, theme, isDark: mode === 'dark' });
|
|
119
86
|
await ThemeStorage.setThemeMode(mode);
|
|
120
|
-
|
|
121
|
-
// Sync with design system global theme
|
|
122
87
|
useDesignSystemTheme.getState().setThemeMode(mode);
|
|
123
88
|
} catch {
|
|
124
|
-
// Silent failure
|
|
89
|
+
// Silent failure
|
|
125
90
|
} finally {
|
|
126
91
|
themeUpdateInProgress = false;
|
|
127
92
|
}
|
|
128
93
|
},
|
|
129
94
|
|
|
95
|
+
setCustomColors: async (colors?: CustomThemeColors) => {
|
|
96
|
+
set({ customColors: colors });
|
|
97
|
+
await ThemeStorage.setCustomColors(colors);
|
|
98
|
+
useDesignSystemTheme.getState().setCustomColors(colors);
|
|
99
|
+
},
|
|
100
|
+
|
|
130
101
|
toggleTheme: async () => {
|
|
131
102
|
const { themeMode, setThemeMode } = get();
|
|
132
|
-
|
|
133
|
-
await setThemeMode(newMode);
|
|
103
|
+
await setThemeMode(themeMode === 'light' ? 'dark' : 'light');
|
|
134
104
|
},
|
|
135
105
|
}),
|
|
136
106
|
});
|
|
137
107
|
|
|
138
|
-
// Export internal store for DesignSystemProvider
|
|
139
108
|
export const useThemeStore = useTheme;
|
|
140
109
|
|
|
141
110
|
|