@umituz/react-native-design-system 4.28.7 → 4.28.9
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 +1 -1
- package/src/atoms/GlassView/GlassView.tsx +4 -3
- package/src/atoms/icon/iconStore.ts +1 -1
- package/src/device/infrastructure/services/DeviceFeatureService.ts +3 -1
- package/src/molecules/Divider/index.ts +1 -1
- package/src/molecules/Divider/types.ts +4 -8
- package/src/molecules/SearchBar/SearchBar.tsx +1 -1
- package/src/molecules/StepHeader/StepHeader.tsx +3 -2
- package/src/molecules/StepProgress/StepProgress.tsx +1 -1
- package/src/molecules/action-footer/ActionFooter.tsx +1 -1
- package/src/molecules/alerts/AlertModal.tsx +1 -1
- package/src/molecules/avatar/Avatar.tsx +1 -1
- package/src/molecules/avatar/AvatarGroup.tsx +1 -1
- package/src/molecules/calendar/infrastructure/stores/useCalendarEvents.ts +19 -9
- package/src/molecules/circular-menu/CircularMenuItem.tsx +1 -1
- package/src/molecules/hero-section/HeroSection.tsx +1 -1
- package/src/molecules/icon-grid/IconGrid.tsx +1 -1
- package/src/molecules/info-grid/InfoGrid.tsx +1 -1
- package/src/onboarding/presentation/components/OnboardingFooter.tsx +2 -1
- package/src/responsive/index.ts +9 -0
- package/src/theme/index.ts +2 -1
- package/src/theme/infrastructure/providers/DesignSystemProvider.tsx +1 -7
- package/src/theme/infrastructure/stores/themeStore.ts +0 -11
- package/src/utils/index.ts +0 -8
- package/src/theme/infrastructure/globalThemeStore.ts +0 -74
- /package/src/{utils/responsiveUtils.ts → responsive/utils.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "4.28.
|
|
3
|
+
"version": "4.28.9",
|
|
4
4
|
"description": "Universal design system for React Native apps with safe navigation hooks - updated SKILL.md with navigation documentation",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { StyleSheet, ViewStyle, StyleProp, View } from 'react-native';
|
|
3
|
-
import {
|
|
3
|
+
import { useTheme } from '../../theme';
|
|
4
4
|
import { intensityToOpacity } from '../../utils/math';
|
|
5
|
+
import { useMemo } from 'react';
|
|
5
6
|
|
|
6
7
|
// Define a local type for tint to maintain API compatibility
|
|
7
8
|
export type GlassTint = 'light' | 'dark' | 'default' | 'prominent' | 'regular' | 'extraLight' | 'systemThinMaterial' | 'systemMaterial' | 'systemThickMaterial' | 'systemChromeMaterial' | 'systemUltraThinMaterial' | 'systemThinMaterialLight' | 'systemMaterialLight' | 'systemThickMaterialLight' | 'systemChromeMaterialLight' | 'systemUltraThinMaterialLight' | 'systemThinMaterialDark' | 'systemMaterialDark' | 'systemThickMaterialDark' | 'systemChromeMaterialDark' | 'systemUltraThinMaterialDark';
|
|
@@ -26,8 +27,8 @@ export const GlassView: React.FC<GlassViewProps> = ({
|
|
|
26
27
|
intensity = 50,
|
|
27
28
|
tint,
|
|
28
29
|
}) => {
|
|
29
|
-
const
|
|
30
|
-
const isDark = themeMode === 'dark';
|
|
30
|
+
const themeMode = useTheme((state) => state.themeMode);
|
|
31
|
+
const isDark = useMemo(() => themeMode === 'dark', [themeMode]);
|
|
31
32
|
|
|
32
33
|
// Calculate opacity using utility function
|
|
33
34
|
const opacity = intensityToOpacity(intensity);
|
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
ResetPeriod,
|
|
17
17
|
} from '../../domain/entities/DeviceFeatureConfig';
|
|
18
18
|
import { PersistentDeviceIdService } from './PersistentDeviceIdService';
|
|
19
|
+
import { ErrorHandler } from '../../../utils/errors/ErrorHandler';
|
|
19
20
|
|
|
20
21
|
export class DeviceFeatureService {
|
|
21
22
|
private static config: DeviceFeatureConfig = { features: {} };
|
|
@@ -99,7 +100,8 @@ export class DeviceFeatureService {
|
|
|
99
100
|
defaultUsage
|
|
100
101
|
);
|
|
101
102
|
return unwrap(result, defaultUsage);
|
|
102
|
-
} catch {
|
|
103
|
+
} catch (error) {
|
|
104
|
+
ErrorHandler.log(error);
|
|
103
105
|
return defaultUsage;
|
|
104
106
|
}
|
|
105
107
|
}
|
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
|
|
5
5
|
export { Divider } from './Divider';
|
|
6
6
|
export type { DividerConfig, DividerOrientation, DividerStyle, DividerSpacing } from './types';
|
|
7
|
-
export { DividerUtils,
|
|
7
|
+
export { DividerUtils, DIVIDER_CONSTANTS } from './types';
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* @layer domain/entities
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { calculateResponsiveSize } from '../../utils/responsiveUtils';
|
|
12
|
+
|
|
11
13
|
/**
|
|
12
14
|
* Divider orientation
|
|
13
15
|
*/
|
|
@@ -57,17 +59,11 @@ const BASE_SPACING_CONFIGS: Record<DividerSpacing, number> = {
|
|
|
57
59
|
*/
|
|
58
60
|
export const getSpacingConfigs = (spacingMultiplier: number): Record<DividerSpacing, number> => {
|
|
59
61
|
return Object.entries(BASE_SPACING_CONFIGS).reduce((acc, [key, value]) => {
|
|
60
|
-
acc[key as DividerSpacing] =
|
|
62
|
+
acc[key as DividerSpacing] = calculateResponsiveSize(value, spacingMultiplier);
|
|
61
63
|
return acc;
|
|
62
64
|
}, {} as Record<DividerSpacing, number>);
|
|
63
65
|
};
|
|
64
66
|
|
|
65
|
-
/**
|
|
66
|
-
* @deprecated Use getSpacingConfigs(spacingMultiplier) instead
|
|
67
|
-
* Kept for backward compatibility
|
|
68
|
-
*/
|
|
69
|
-
export const SPACING_CONFIGS = BASE_SPACING_CONFIGS;
|
|
70
|
-
|
|
71
67
|
/**
|
|
72
68
|
* Divider utility class
|
|
73
69
|
*/
|
|
@@ -76,7 +72,7 @@ export class DividerUtils {
|
|
|
76
72
|
* Get spacing value (responsive)
|
|
77
73
|
*/
|
|
78
74
|
static getSpacing(spacing: DividerSpacing, spacingMultiplier: number = 1): number {
|
|
79
|
-
return
|
|
75
|
+
return calculateResponsiveSize(BASE_SPACING_CONFIGS[spacing], spacingMultiplier);
|
|
80
76
|
}
|
|
81
77
|
|
|
82
78
|
/**
|
|
@@ -9,7 +9,7 @@ import { useAppDesignTokens } from '../../theme';
|
|
|
9
9
|
import { AtomicIcon, useIconName } from '../../atoms';
|
|
10
10
|
import { AtomicSpinner } from '../../atoms/AtomicSpinner';
|
|
11
11
|
import type { SearchBarProps } from './types';
|
|
12
|
-
import { calculateResponsiveSize } from '
|
|
12
|
+
import { calculateResponsiveSize } from '../responsive'
|
|
13
13
|
import { MISC_SIZES } from '../../constants';
|
|
14
14
|
|
|
15
15
|
export const SearchBar: React.FC<SearchBarProps> = ({
|
|
@@ -12,8 +12,9 @@ import { useAppDesignTokens } from "../../theme/hooks/useAppDesignTokens";
|
|
|
12
12
|
import {
|
|
13
13
|
calculateResponsiveSize,
|
|
14
14
|
calculateLineHeight,
|
|
15
|
-
} from
|
|
15
|
+
} from '../responsive'
|
|
16
16
|
import { SPACING, STEP_INDICATOR } from "../../constants";
|
|
17
|
+
import { createMappedArray } from "../../utils";
|
|
17
18
|
|
|
18
19
|
const DEFAULT_CONFIG: StepHeaderConfig = {
|
|
19
20
|
showStepIndicator: false,
|
|
@@ -114,7 +115,7 @@ export const StepHeader: React.FC<StepHeaderProps> = ({
|
|
|
114
115
|
cfg.currentStep !== undefined &&
|
|
115
116
|
cfg.totalSteps !== undefined && (
|
|
116
117
|
<View style={styles.stepIndicator}>
|
|
117
|
-
{
|
|
118
|
+
{createMappedArray(cfg.totalSteps, (i) => (
|
|
118
119
|
<View
|
|
119
120
|
key={`step-${i}`}
|
|
120
121
|
style={[
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import { View, StyleSheet, ViewStyle } from "react-native";
|
|
3
3
|
import { useAppDesignTokens } from '../../theme/hooks/useAppDesignTokens';
|
|
4
|
-
import { calculateResponsiveSize } from '
|
|
4
|
+
import { calculateResponsiveSize } from '../responsive'
|
|
5
5
|
import { STEP_INDICATOR } from '../../constants';
|
|
6
6
|
import { createMappedArray } from '../../utils/arrayUtils';
|
|
7
7
|
|
|
@@ -5,7 +5,7 @@ import { AtomicText } from '../../atoms/AtomicText';
|
|
|
5
5
|
import { AtomicIcon } from '../../atoms';
|
|
6
6
|
import { useAppDesignTokens } from '../../theme';
|
|
7
7
|
import type { ActionFooterProps } from './types';
|
|
8
|
-
import { calculateResponsiveSize } from '
|
|
8
|
+
import { calculateResponsiveSize } from '../responsive'
|
|
9
9
|
import { NAVIGATION } from '../../constants';
|
|
10
10
|
|
|
11
11
|
const createStyles = (spacingMultiplier: number) => StyleSheet.create({
|
|
@@ -9,7 +9,7 @@ import { useAppDesignTokens } from '../../theme';
|
|
|
9
9
|
import { Alert, AlertType } from './AlertTypes';
|
|
10
10
|
import { getAlertBackgroundColor } from './utils/alertUtils';
|
|
11
11
|
import { useAlertDismissHandler } from './hooks';
|
|
12
|
-
import { calculateResponsiveSize } from '
|
|
12
|
+
import { calculateResponsiveSize } from '../responsive'
|
|
13
13
|
import { MODAL_SIZES, ALERT_MODAL_ICON } from '../../constants';
|
|
14
14
|
|
|
15
15
|
interface AlertModalProps {
|
|
@@ -12,7 +12,7 @@ import { AtomicText, AtomicIcon } from '../../atoms';
|
|
|
12
12
|
import type { AvatarSize, AvatarShape } from './Avatar.types';
|
|
13
13
|
import type { SizeConfig } from './Avatar.types';
|
|
14
14
|
import { AVATAR_SIZES } from '../../constants';
|
|
15
|
-
import { calculateResponsiveSize } from '
|
|
15
|
+
import { calculateResponsiveSize } from '../responsive'
|
|
16
16
|
import { AvatarUtils } from './Avatar.utils';
|
|
17
17
|
|
|
18
18
|
export interface AvatarProps {
|
|
@@ -12,7 +12,7 @@ import { AtomicText } from '../../atoms';
|
|
|
12
12
|
import { Avatar } from './Avatar';
|
|
13
13
|
import type { AvatarSize, AvatarShape } from './Avatar.types';
|
|
14
14
|
import { AVATAR_SIZES } from '../../constants';
|
|
15
|
-
import { calculateResponsiveSize } from '
|
|
15
|
+
import { calculateResponsiveSize } from '../responsive'
|
|
16
16
|
import type { SizeConfig } from './Avatar.types';
|
|
17
17
|
|
|
18
18
|
const AVATAR_CONSTANTS = {
|
|
@@ -8,6 +8,7 @@ import { create } from 'zustand';
|
|
|
8
8
|
import type { CalendarEvent, CreateCalendarEventRequest, UpdateCalendarEventRequest } from '../../domain/entities/CalendarEvent.entity';
|
|
9
9
|
import { zustandStorage } from './storageAdapter';
|
|
10
10
|
import { isValidArray, isValidCalendarEvent } from '../../../../storage/domain/utils/ValidationUtils';
|
|
11
|
+
import { ErrorHandler } from '../../../../utils/errors/ErrorHandler';
|
|
11
12
|
|
|
12
13
|
const STORAGE_KEY = 'calendar_events';
|
|
13
14
|
|
|
@@ -35,8 +36,9 @@ const generateId = (): string => `${Date.now()}-${Math.random().toString(36).sub
|
|
|
35
36
|
const persistEvents = async (events: CalendarEvent[]): Promise<void> => {
|
|
36
37
|
try {
|
|
37
38
|
await zustandStorage.setItem(STORAGE_KEY, JSON.stringify(events));
|
|
38
|
-
} catch {
|
|
39
|
-
|
|
39
|
+
} catch (error) {
|
|
40
|
+
ErrorHandler.log(error);
|
|
41
|
+
throw error; // Re-throw to allow caller to handle
|
|
40
42
|
}
|
|
41
43
|
};
|
|
42
44
|
|
|
@@ -59,6 +61,7 @@ export const useCalendarEvents = create<CalendarEventsStore>()((set, get) => ({
|
|
|
59
61
|
// Runtime validation
|
|
60
62
|
if (!isValidArray(parsed)) {
|
|
61
63
|
if (__DEV__) {
|
|
64
|
+
console.error('[CalendarEvents] Invalid data format: expected array, got', typeof parsed);
|
|
62
65
|
}
|
|
63
66
|
set({ error: 'Invalid data format', isLoading: false });
|
|
64
67
|
return;
|
|
@@ -77,7 +80,8 @@ export const useCalendarEvents = create<CalendarEventsStore>()((set, get) => ({
|
|
|
77
80
|
} else {
|
|
78
81
|
set({ isLoading: false });
|
|
79
82
|
}
|
|
80
|
-
} catch {
|
|
83
|
+
} catch (error) {
|
|
84
|
+
ErrorHandler.log(error);
|
|
81
85
|
set({ error: 'Failed to load events', isLoading: false });
|
|
82
86
|
}
|
|
83
87
|
},
|
|
@@ -97,7 +101,8 @@ export const useCalendarEvents = create<CalendarEventsStore>()((set, get) => ({
|
|
|
97
101
|
|
|
98
102
|
await persistEvents(updatedEvents);
|
|
99
103
|
set({ events: updatedEvents, isLoading: false });
|
|
100
|
-
} catch {
|
|
104
|
+
} catch (error) {
|
|
105
|
+
ErrorHandler.log(error);
|
|
101
106
|
set({ error: 'Failed to add event', isLoading: false });
|
|
102
107
|
}
|
|
103
108
|
},
|
|
@@ -114,7 +119,8 @@ export const useCalendarEvents = create<CalendarEventsStore>()((set, get) => ({
|
|
|
114
119
|
|
|
115
120
|
await persistEvents(updatedEvents);
|
|
116
121
|
set({ events: updatedEvents, isLoading: false });
|
|
117
|
-
} catch {
|
|
122
|
+
} catch (error) {
|
|
123
|
+
ErrorHandler.log(error);
|
|
118
124
|
set({ error: 'Failed to update event', isLoading: false });
|
|
119
125
|
}
|
|
120
126
|
},
|
|
@@ -127,7 +133,8 @@ export const useCalendarEvents = create<CalendarEventsStore>()((set, get) => ({
|
|
|
127
133
|
|
|
128
134
|
await persistEvents(updatedEvents);
|
|
129
135
|
set({ events: updatedEvents, isLoading: false });
|
|
130
|
-
} catch {
|
|
136
|
+
} catch (error) {
|
|
137
|
+
ErrorHandler.log(error);
|
|
131
138
|
set({ error: 'Failed to delete event', isLoading: false });
|
|
132
139
|
}
|
|
133
140
|
},
|
|
@@ -144,7 +151,8 @@ export const useCalendarEvents = create<CalendarEventsStore>()((set, get) => ({
|
|
|
144
151
|
|
|
145
152
|
await persistEvents(updatedEvents);
|
|
146
153
|
set({ events: updatedEvents, isLoading: false });
|
|
147
|
-
} catch {
|
|
154
|
+
} catch (error) {
|
|
155
|
+
ErrorHandler.log(error);
|
|
148
156
|
set({ error: 'Failed to complete event', isLoading: false });
|
|
149
157
|
}
|
|
150
158
|
},
|
|
@@ -161,7 +169,8 @@ export const useCalendarEvents = create<CalendarEventsStore>()((set, get) => ({
|
|
|
161
169
|
|
|
162
170
|
await persistEvents(updatedEvents);
|
|
163
171
|
set({ events: updatedEvents, isLoading: false });
|
|
164
|
-
} catch {
|
|
172
|
+
} catch (error) {
|
|
173
|
+
ErrorHandler.log(error);
|
|
165
174
|
set({ error: 'Failed to uncomplete event', isLoading: false });
|
|
166
175
|
}
|
|
167
176
|
},
|
|
@@ -173,7 +182,8 @@ export const useCalendarEvents = create<CalendarEventsStore>()((set, get) => ({
|
|
|
173
182
|
try {
|
|
174
183
|
await zustandStorage.removeItem(STORAGE_KEY);
|
|
175
184
|
set({ events: [], isLoading: false });
|
|
176
|
-
} catch {
|
|
185
|
+
} catch (error) {
|
|
186
|
+
ErrorHandler.log(error);
|
|
177
187
|
set({ error: 'Failed to clear events', isLoading: false });
|
|
178
188
|
}
|
|
179
189
|
},
|
|
@@ -4,7 +4,7 @@ import { AtomicIcon } from "../../atoms";
|
|
|
4
4
|
import { AtomicText } from "../../atoms";
|
|
5
5
|
import { useAppDesignTokens } from "../../theme";
|
|
6
6
|
import { LAYOUT } from "./constants";
|
|
7
|
-
import { calculateResponsiveSize } from
|
|
7
|
+
import { calculateResponsiveSize } from '../responsive'
|
|
8
8
|
|
|
9
9
|
export interface CircularMenuItemProps {
|
|
10
10
|
icon: string;
|
|
@@ -3,7 +3,7 @@ import React, { useMemo } from 'react';
|
|
|
3
3
|
import { View, StyleSheet, Text, Image } from 'react-native';
|
|
4
4
|
import { useAppDesignTokens } from '../../theme';
|
|
5
5
|
import type { HeroSectionProps } from './types';
|
|
6
|
-
import { calculateResponsiveSize } from '
|
|
6
|
+
import { calculateResponsiveSize } from '../responsive'
|
|
7
7
|
import { HERO_ICON } from '../../constants';
|
|
8
8
|
|
|
9
9
|
export const HeroSection: React.FC<HeroSectionProps> = ({
|
|
@@ -21,7 +21,7 @@ import { AtomicIcon } from '../../atoms';
|
|
|
21
21
|
import { AtomicText } from '../../atoms';
|
|
22
22
|
import type { IconName } from '../../atoms';
|
|
23
23
|
import { calculateGridItemWidth } from '../../utils/math';
|
|
24
|
-
import { calculateResponsiveSize } from '
|
|
24
|
+
import { calculateResponsiveSize } from '../responsive'
|
|
25
25
|
import { ICON_GRID } from '../../constants';
|
|
26
26
|
|
|
27
27
|
export interface IconGridItem {
|
|
@@ -5,7 +5,7 @@ import { AtomicText } from '../../atoms/AtomicText';
|
|
|
5
5
|
import { AtomicIcon } from '../../atoms';
|
|
6
6
|
import { useAppDesignTokens } from '../../theme';
|
|
7
7
|
import type { InfoGridProps } from './types';
|
|
8
|
-
import { calculateResponsiveSize } from '
|
|
8
|
+
import { calculateResponsiveSize } from '../responsive'
|
|
9
9
|
import { INFO_GRID_ICONS } from '../../constants';
|
|
10
10
|
|
|
11
11
|
export const InfoGrid: React.FC<InfoGridProps> = ({
|
|
@@ -4,6 +4,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
|
4
4
|
import { AtomicText } from "../../../atoms/AtomicText";
|
|
5
5
|
import { useOnboardingProvider } from "../providers/OnboardingProvider";
|
|
6
6
|
import { calculateStepProgress } from "../../../utils/math";
|
|
7
|
+
import { createMappedArray } from "../../../utils";
|
|
7
8
|
|
|
8
9
|
export interface OnboardingFooterProps {
|
|
9
10
|
currentIndex: number;
|
|
@@ -59,7 +60,7 @@ export const OnboardingFooter = React.memo<OnboardingFooterProps>(({
|
|
|
59
60
|
);
|
|
60
61
|
|
|
61
62
|
const dots = useMemo(
|
|
62
|
-
() =>
|
|
63
|
+
() => createMappedArray(totalSlides, (index) => {
|
|
63
64
|
const isActive = index === currentIndex;
|
|
64
65
|
return {
|
|
65
66
|
key: index,
|
package/src/responsive/index.ts
CHANGED
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
export { useResponsive } from './useResponsive';
|
|
10
10
|
export type { UseResponsiveReturn } from './useResponsive';
|
|
11
11
|
|
|
12
|
+
// Responsive calculation utilities
|
|
13
|
+
export {
|
|
14
|
+
calculateResponsiveSize,
|
|
15
|
+
calculateResponsiveSizes,
|
|
16
|
+
calculateResponsiveSizeSubtle,
|
|
17
|
+
calculateLineHeight,
|
|
18
|
+
createResponsiveSizes,
|
|
19
|
+
} from './utils';
|
|
20
|
+
|
|
12
21
|
// Responsive sizing utilities
|
|
13
22
|
export {
|
|
14
23
|
getResponsiveLogoSize,
|
package/src/theme/index.ts
CHANGED
|
@@ -65,7 +65,8 @@ export type {
|
|
|
65
65
|
// =============================================================================
|
|
66
66
|
|
|
67
67
|
export { useAppDesignTokens } from './hooks/useAppDesignTokens';
|
|
68
|
-
|
|
68
|
+
// Alias for backward compatibility - useTheme is now the single source of truth
|
|
69
|
+
export { useTheme as useDesignSystemTheme } from './infrastructure/stores/themeStore';
|
|
69
70
|
export { useTheme } from './infrastructure/stores/themeStore';
|
|
70
71
|
export { useThemedStyles, useThemedStyleSheet } from './hooks/useThemedStyles';
|
|
71
72
|
export { useCommonStyles } from './hooks/useCommonStyles';
|
|
@@ -4,7 +4,7 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
|
4
4
|
import { useFonts } from 'expo-font';
|
|
5
5
|
import { SafeAreaProvider, initialWindowMetrics } from '../../../safe-area';
|
|
6
6
|
import { useTheme } from '../stores/themeStore';
|
|
7
|
-
import {
|
|
7
|
+
import type { ThemeMode } from '../../core/ColorPalette';
|
|
8
8
|
import type { CustomThemeColors } from '../../core/CustomColors';
|
|
9
9
|
import type { SplashScreenProps } from '../../../molecules/splash/types';
|
|
10
10
|
import { FIVE_SECONDS_MS } from '../../../utils/constants/TimeConstants';
|
|
@@ -46,20 +46,16 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
|
|
|
46
46
|
const setCustomColors = useTheme((state) => state.setCustomColors);
|
|
47
47
|
const setDefaultColors = useTheme((state) => state.setDefaultColors);
|
|
48
48
|
const setDefaultThemeMode = useTheme((state) => state.setDefaultThemeMode);
|
|
49
|
-
const setGlobalCustomColors = useDesignSystemTheme((state) => state.setCustomColors);
|
|
50
|
-
const setGlobalThemeMode = useDesignSystemTheme((state) => state.setThemeMode);
|
|
51
49
|
|
|
52
50
|
useEffect(() => {
|
|
53
51
|
// Register app's default colors for reset feature
|
|
54
52
|
if (customColors) {
|
|
55
53
|
setDefaultColors(customColors);
|
|
56
54
|
setCustomColors(customColors);
|
|
57
|
-
setGlobalCustomColors(customColors);
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
// Set default theme mode BEFORE initialize
|
|
61
58
|
setDefaultThemeMode(initialThemeMode);
|
|
62
|
-
setGlobalThemeMode(initialThemeMode);
|
|
63
59
|
|
|
64
60
|
// Safety timeout: if initialization takes too long, proceed anyway
|
|
65
61
|
const safetyTimer = setTimeout(() => {
|
|
@@ -89,8 +85,6 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
|
|
|
89
85
|
setCustomColors,
|
|
90
86
|
setDefaultColors,
|
|
91
87
|
setDefaultThemeMode,
|
|
92
|
-
setGlobalCustomColors,
|
|
93
|
-
setGlobalThemeMode,
|
|
94
88
|
]);
|
|
95
89
|
|
|
96
90
|
// Skip font loading gate when no custom fonts are provided
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
import { createStore } from '../../../storage';
|
|
9
9
|
import { lightTheme, darkTheme, type Theme } from '../../core/themes';
|
|
10
10
|
import { ThemeStorage } from '../storage/ThemeStorage';
|
|
11
|
-
import { useDesignSystemTheme } from '../globalThemeStore';
|
|
12
11
|
import type { ThemeMode } from '../../core/ColorPalette';
|
|
13
12
|
import type { CustomThemeColors } from '../../core/CustomColors';
|
|
14
13
|
|
|
@@ -95,16 +94,11 @@ export const useTheme = createStore<ThemeState, ThemeActions>({
|
|
|
95
94
|
isDark: mode === 'dark',
|
|
96
95
|
isInitialized: true,
|
|
97
96
|
});
|
|
98
|
-
|
|
99
|
-
const dsTheme = useDesignSystemTheme.getState();
|
|
100
|
-
dsTheme.setThemeMode(mode);
|
|
101
|
-
dsTheme.setCustomColors(colors);
|
|
102
97
|
} catch (error) {
|
|
103
98
|
if (__DEV__) {
|
|
104
99
|
console.error('[ThemeStore] Failed to initialize theme:', error);
|
|
105
100
|
}
|
|
106
101
|
set({ isInitialized: true, _initInProgress: false });
|
|
107
|
-
useDesignSystemTheme.getState().setThemeMode(defaultThemeMode);
|
|
108
102
|
} finally {
|
|
109
103
|
set({ _initInProgress: false });
|
|
110
104
|
}
|
|
@@ -121,7 +115,6 @@ export const useTheme = createStore<ThemeState, ThemeActions>({
|
|
|
121
115
|
const theme = mode === 'light' ? lightTheme : darkTheme;
|
|
122
116
|
set({ themeMode: mode, theme, isDark: mode === 'dark' });
|
|
123
117
|
await ThemeStorage.setThemeMode(mode);
|
|
124
|
-
useDesignSystemTheme.getState().setThemeMode(mode);
|
|
125
118
|
} catch (error) {
|
|
126
119
|
// Revert state on error
|
|
127
120
|
set({ _lastUpdateId: undefined });
|
|
@@ -146,7 +139,6 @@ export const useTheme = createStore<ThemeState, ThemeActions>({
|
|
|
146
139
|
|
|
147
140
|
try {
|
|
148
141
|
await ThemeStorage.setCustomColors(colors);
|
|
149
|
-
useDesignSystemTheme.getState().setCustomColors(colors);
|
|
150
142
|
} catch (error) {
|
|
151
143
|
// Revert to previous colors on error
|
|
152
144
|
set({ customColors: currentColors, _lastUpdateId: undefined });
|
|
@@ -173,9 +165,6 @@ export const useTheme = createStore<ThemeState, ThemeActions>({
|
|
|
173
165
|
set({ themeMode: defaultThemeMode, theme, isDark: defaultThemeMode === 'dark', customColors: defaultColors });
|
|
174
166
|
await ThemeStorage.clearThemeMode();
|
|
175
167
|
await ThemeStorage.clearCustomColors();
|
|
176
|
-
const dsTheme = useDesignSystemTheme.getState();
|
|
177
|
-
dsTheme.setThemeMode(defaultThemeMode);
|
|
178
|
-
dsTheme.setCustomColors(defaultColors);
|
|
179
168
|
},
|
|
180
169
|
|
|
181
170
|
toggleTheme: async () => {
|
package/src/utils/index.ts
CHANGED
|
@@ -4,14 +4,6 @@
|
|
|
4
4
|
* Centralized utility functions for the design system.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export {
|
|
8
|
-
calculateResponsiveSize,
|
|
9
|
-
calculateResponsiveSizes,
|
|
10
|
-
calculateResponsiveSizeSubtle,
|
|
11
|
-
calculateLineHeight,
|
|
12
|
-
createResponsiveSizes,
|
|
13
|
-
} from './responsiveUtils';
|
|
14
|
-
|
|
15
7
|
export {
|
|
16
8
|
createMappedArray,
|
|
17
9
|
safeSlice,
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Global Theme Store for Design System
|
|
3
|
-
*
|
|
4
|
-
* Minimal Zustand store for theme state management.
|
|
5
|
-
* Apps can sync their theme state with this global store.
|
|
6
|
-
*
|
|
7
|
-
* WHY THIS EXISTS:
|
|
8
|
-
* - ScreenLayout needs to know current theme mode
|
|
9
|
-
* - Without prop drilling or Context API
|
|
10
|
-
* - Single source of truth for design system components
|
|
11
|
-
* - Apps control theme, design system reacts
|
|
12
|
-
*
|
|
13
|
-
* USAGE IN APP:
|
|
14
|
-
* ```typescript
|
|
15
|
-
* import { useDesignSystemTheme } from '@umituz/react-native-design-system-theme';
|
|
16
|
-
* import { useTheme } from '@domains/theme';
|
|
17
|
-
*
|
|
18
|
-
* // Sync app theme with design system
|
|
19
|
-
* const { themeMode } = useTheme();
|
|
20
|
-
* const { setThemeMode } = useDesignSystemTheme();
|
|
21
|
-
*
|
|
22
|
-
* useEffect(() => {
|
|
23
|
-
* setThemeMode(themeMode);
|
|
24
|
-
* }, [themeMode]);
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
import { createStore } from '../../storage';
|
|
29
|
-
import type { ThemeMode } from '../core/ColorPalette';
|
|
30
|
-
import type { CustomThemeColors } from '../core/CustomColors';
|
|
31
|
-
|
|
32
|
-
interface GlobalThemeState {
|
|
33
|
-
/** Current theme mode */
|
|
34
|
-
themeMode: ThemeMode;
|
|
35
|
-
/** Custom theme colors override */
|
|
36
|
-
customColors?: CustomThemeColors;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface GlobalThemeActions {
|
|
40
|
-
/** Update theme mode (called by app when theme changes) */
|
|
41
|
-
setThemeMode: (mode: ThemeMode) => void;
|
|
42
|
-
/** Set custom theme colors (called by app when custom colors change) */
|
|
43
|
-
setCustomColors: (colors?: CustomThemeColors) => void;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Global theme store for design system components
|
|
48
|
-
*
|
|
49
|
-
* This is a MINIMAL store - app has the real theme logic.
|
|
50
|
-
* Design system just mirrors the current theme for its components.
|
|
51
|
-
*/
|
|
52
|
-
export const useDesignSystemTheme = createStore<GlobalThemeState, GlobalThemeActions>({
|
|
53
|
-
name: 'design-system-theme',
|
|
54
|
-
initialState: {
|
|
55
|
-
themeMode: 'light',
|
|
56
|
-
customColors: undefined,
|
|
57
|
-
},
|
|
58
|
-
persist: false,
|
|
59
|
-
actions: (set, get) => ({
|
|
60
|
-
setThemeMode: (mode: ThemeMode) => {
|
|
61
|
-
// Only update if mode actually changed to prevent unnecessary re-renders
|
|
62
|
-
const currentMode = get().themeMode;
|
|
63
|
-
if (currentMode !== mode) {
|
|
64
|
-
set({ themeMode: mode });
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
setCustomColors: (colors?: CustomThemeColors) => {
|
|
68
|
-
set({ customColors: colors });
|
|
69
|
-
},
|
|
70
|
-
}),
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
export type { ThemeMode };
|
|
74
|
-
|
|
File without changes
|