@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "4.28.7",
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 { useDesignSystemTheme } from '../../theme';
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 { themeMode } = useDesignSystemTheme();
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);
@@ -71,7 +71,7 @@ interface IconStore {
71
71
  reset: () => void;
72
72
  }
73
73
 
74
- export const useIconStore = create<IconStore>((set) => ({
74
+ export const useIconStore = create<IconStore>()((set) => ({
75
75
  iconNames: null,
76
76
  iconRenderer: null,
77
77
  isConfigured: false,
@@ -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, SPACING_CONFIGS, DIVIDER_CONSTANTS } from './types';
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] = Math.floor(value * spacingMultiplier);
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 Math.floor(BASE_SPACING_CONFIGS[spacing] * spacingMultiplier);
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 '../../utils/responsiveUtils';
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 "../../utils/responsiveUtils";
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
- {Array.from({ length: cfg.totalSteps }, (_, i) => (
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 '../../utils/responsiveUtils';
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 '../../utils/responsiveUtils';
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 '../../utils/responsiveUtils';
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 '../../utils/responsiveUtils';
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 '../../utils/responsiveUtils';
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
- // Silent fail
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 "../../utils/responsiveUtils";
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 '../../utils/responsiveUtils';
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 '../../utils/responsiveUtils';
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 '../../utils/responsiveUtils';
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
- () => Array.from({ length: totalSlides }, (_, index) => {
63
+ () => createMappedArray(totalSlides, (index) => {
63
64
  const isActive = index === currentIndex;
64
65
  return {
65
66
  key: index,
@@ -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,
@@ -65,7 +65,8 @@ export type {
65
65
  // =============================================================================
66
66
 
67
67
  export { useAppDesignTokens } from './hooks/useAppDesignTokens';
68
- export { useDesignSystemTheme } from './infrastructure/globalThemeStore';
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 { useDesignSystemTheme, type ThemeMode } from '../globalThemeStore';
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 () => {
@@ -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
-