@umituz/react-native-design-system 2.5.7 → 2.5.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/AtomicIcon.tsx +3 -2
- package/src/layouts/ScreenHeader/ScreenHeader.tsx +2 -2
- package/src/molecules/alerts/AlertStore.ts +15 -12
- package/src/molecules/animation/presentation/hooks/useFireworks.ts +2 -1
- package/src/molecules/avatar/Avatar.utils.ts +5 -3
- package/src/molecules/calendar/infrastructure/storage/CalendarStore.ts +2 -2
- package/src/molecules/calendar/infrastructure/utils/DateUtilities.ts +6 -2
- package/src/molecules/calendar/presentation/hooks/useCalendar.ts +3 -3
- package/src/theme/infrastructure/globalThemeStore.ts +23 -17
- package/src/theme/infrastructure/stores/themeStore.ts +61 -54
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.9",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive and safe area utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
package/src/atoms/AtomicIcon.tsx
CHANGED
|
@@ -110,9 +110,10 @@ export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(({
|
|
|
110
110
|
|
|
111
111
|
// Calculate size
|
|
112
112
|
const baseSize = customSize ?? size;
|
|
113
|
-
const
|
|
113
|
+
const iconSizesMap = tokens.iconSizes as Record<string, number>;
|
|
114
|
+
const sizeInPixels: number = typeof baseSize === 'number'
|
|
114
115
|
? baseSize * tokens.spacingMultiplier
|
|
115
|
-
:
|
|
116
|
+
: iconSizesMap[baseSize] ?? iconSizesMap['md'] ?? 24;
|
|
116
117
|
|
|
117
118
|
// Calculate color
|
|
118
119
|
const iconColor = customColor
|
|
@@ -88,7 +88,7 @@ const ScreenHeaderBackButton: React.FC<{
|
|
|
88
88
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
89
89
|
testID={`${testID}-back-button`}
|
|
90
90
|
>
|
|
91
|
-
<AtomicIcon name={backIconName || '
|
|
91
|
+
<AtomicIcon name={backIconName || 'arrow-back'} color={backIconColor} />
|
|
92
92
|
</TouchableOpacity>
|
|
93
93
|
</View>
|
|
94
94
|
);
|
|
@@ -132,7 +132,7 @@ export const ScreenHeader: React.FC<ScreenHeaderProps> = ({
|
|
|
132
132
|
hideBackButton = false,
|
|
133
133
|
style,
|
|
134
134
|
testID = 'screen-header',
|
|
135
|
-
backIconName = '
|
|
135
|
+
backIconName = 'arrow-back',
|
|
136
136
|
backIconColor = 'primary',
|
|
137
137
|
}) => {
|
|
138
138
|
const tokens = useAppDesignTokens();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Alert Store
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { createStore } from '@umituz/react-native-storage';
|
|
6
6
|
import { Alert } from './AlertTypes';
|
|
7
7
|
|
|
8
8
|
interface AlertState {
|
|
@@ -15,14 +15,17 @@ interface AlertActions {
|
|
|
15
15
|
clearAlerts: () => void;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
})
|
|
18
|
+
export const useAlertStore = createStore<AlertState, AlertActions>({
|
|
19
|
+
name: 'alert-store',
|
|
20
|
+
initialState: {
|
|
21
|
+
alerts: [],
|
|
22
|
+
},
|
|
23
|
+
persist: false,
|
|
24
|
+
actions: (set, get) => ({
|
|
25
|
+
addAlert: (alert: Alert) => set({ alerts: [...get().alerts, alert] }),
|
|
26
|
+
dismissAlert: (id: string) => set({
|
|
27
|
+
alerts: get().alerts.filter((a: Alert) => a.id !== id)
|
|
28
|
+
}),
|
|
29
|
+
clearAlerts: () => set({ alerts: [] }),
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
@@ -104,7 +104,8 @@ export const useFireworks = (config: FireworksConfig) => {
|
|
|
104
104
|
|
|
105
105
|
const newParticles: ParticleConfig[] = [];
|
|
106
106
|
for (let i = 0; i < particleCount; i++) {
|
|
107
|
-
const
|
|
107
|
+
const colorIndex = Math.floor(Math.random() * colors.length);
|
|
108
|
+
const color = colors[colorIndex] ?? colors[0] ?? '#FFFFFF';
|
|
108
109
|
newParticles.push(createParticle(centerX, centerY, color));
|
|
109
110
|
}
|
|
110
111
|
|
|
@@ -165,9 +165,11 @@ export class AvatarUtils {
|
|
|
165
165
|
|
|
166
166
|
if (words.length >= 2) {
|
|
167
167
|
// Full name: First letter of first + first letter of last
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
168
|
+
const firstWord = words[0] ?? '';
|
|
169
|
+
const lastWord = words[words.length - 1] ?? '';
|
|
170
|
+
const first = firstWord[0] ?? '';
|
|
171
|
+
const last = lastWord[0] ?? '';
|
|
172
|
+
return (first + last).toLocaleUpperCase('tr-TR') || '?';
|
|
171
173
|
} else {
|
|
172
174
|
// Single word: First 2 letters
|
|
173
175
|
return trimmed.slice(0, 2).toLocaleUpperCase('tr-TR');
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* - Timezone-aware via CalendarService
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { create } from 'zustand';
|
|
15
|
-
import { persist, createJSONStorage } from 'zustand/middleware';
|
|
14
|
+
import { create, type StateCreator } from 'zustand';
|
|
15
|
+
import { persist, createJSONStorage, type PersistOptions } from 'zustand/middleware';
|
|
16
16
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
17
17
|
import type { CalendarEvent, CreateCalendarEventRequest, UpdateCalendarEventRequest } from '../../domain/entities/CalendarEvent.entity';
|
|
18
18
|
import { CalendarService } from '../services/CalendarService';
|
|
@@ -14,7 +14,8 @@ export class DateUtilities {
|
|
|
14
14
|
* Format date to string (YYYY-MM-DD)
|
|
15
15
|
*/
|
|
16
16
|
static formatDateToString(date: Date): string {
|
|
17
|
-
|
|
17
|
+
const parts = date.toISOString().split('T');
|
|
18
|
+
return parts[0] ?? '';
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -91,7 +92,10 @@ export class DateUtilities {
|
|
|
91
92
|
* Parse date from string (YYYY-MM-DD)
|
|
92
93
|
*/
|
|
93
94
|
static parseDate(dateString: string): Date {
|
|
94
|
-
const
|
|
95
|
+
const parts = dateString.split('-').map(Number);
|
|
96
|
+
const year = parts[0] ?? 0;
|
|
97
|
+
const month = parts[1] ?? 1;
|
|
98
|
+
const day = parts[2] ?? 1;
|
|
95
99
|
return new Date(year, month - 1, day);
|
|
96
100
|
}
|
|
97
101
|
|
|
@@ -84,7 +84,7 @@ export const useCalendar = (): UseCalendarReturn => {
|
|
|
84
84
|
isLoading,
|
|
85
85
|
error,
|
|
86
86
|
actions,
|
|
87
|
-
} = useCalendarStore((state) => state);
|
|
87
|
+
} = useCalendarStore((state: ReturnType<typeof useCalendarStore.getState>) => state);
|
|
88
88
|
|
|
89
89
|
// Load events on mount
|
|
90
90
|
useEffect(() => {
|
|
@@ -133,7 +133,7 @@ export const useCalendarNavigation = () => {
|
|
|
133
133
|
selectedDate,
|
|
134
134
|
currentMonth,
|
|
135
135
|
actions: { setSelectedDate, navigateMonth, goToToday, setCurrentMonth },
|
|
136
|
-
} = useCalendarStore((state) => state);
|
|
136
|
+
} = useCalendarStore((state: ReturnType<typeof useCalendarStore.getState>) => state);
|
|
137
137
|
|
|
138
138
|
return {
|
|
139
139
|
selectedDate,
|
|
@@ -163,7 +163,7 @@ export const useCalendarEvents = () => {
|
|
|
163
163
|
uncompleteEvent,
|
|
164
164
|
clearError,
|
|
165
165
|
},
|
|
166
|
-
} = useCalendarStore((state) => state);
|
|
166
|
+
} = useCalendarStore((state: ReturnType<typeof useCalendarStore.getState>) => state);
|
|
167
167
|
|
|
168
168
|
return {
|
|
169
169
|
events,
|
|
@@ -25,20 +25,20 @@
|
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import {
|
|
28
|
+
import { createStore } from '@umituz/react-native-storage';
|
|
29
29
|
import type { ThemeMode } from '../core/ColorPalette';
|
|
30
30
|
import type { CustomThemeColors } from '../core/CustomColors';
|
|
31
31
|
|
|
32
|
-
interface
|
|
32
|
+
interface GlobalThemeState {
|
|
33
33
|
/** Current theme mode */
|
|
34
34
|
themeMode: ThemeMode;
|
|
35
|
-
|
|
36
35
|
/** Custom theme colors override */
|
|
37
36
|
customColors?: CustomThemeColors;
|
|
37
|
+
}
|
|
38
38
|
|
|
39
|
+
interface GlobalThemeActions {
|
|
39
40
|
/** Update theme mode (called by app when theme changes) */
|
|
40
41
|
setThemeMode: (mode: ThemeMode) => void;
|
|
41
|
-
|
|
42
42
|
/** Set custom theme colors (called by app when custom colors change) */
|
|
43
43
|
setCustomColors: (colors?: CustomThemeColors) => void;
|
|
44
44
|
}
|
|
@@ -49,20 +49,26 @@ interface GlobalThemeStore {
|
|
|
49
49
|
* This is a MINIMAL store - app has the real theme logic.
|
|
50
50
|
* Design system just mirrors the current theme for its components.
|
|
51
51
|
*/
|
|
52
|
-
export const useDesignSystemTheme =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const currentMode = get().themeMode;
|
|
58
|
-
if (currentMode !== mode) {
|
|
59
|
-
set({ themeMode: mode });
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
setCustomColors: (colors?: CustomThemeColors) => {
|
|
63
|
-
set({ customColors: colors });
|
|
52
|
+
export const useDesignSystemTheme = createStore<GlobalThemeState, GlobalThemeActions>({
|
|
53
|
+
name: 'design-system-theme',
|
|
54
|
+
initialState: {
|
|
55
|
+
themeMode: 'dark',
|
|
56
|
+
customColors: undefined,
|
|
64
57
|
},
|
|
65
|
-
|
|
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
|
+
});
|
|
66
72
|
|
|
67
73
|
// Re-export ThemeMode for backward compatibility
|
|
68
74
|
export type { ThemeMode };
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Syncs with design system global theme store
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { createStore } from '@umituz/react-native-storage';
|
|
14
14
|
import { lightTheme, darkTheme, type Theme } from '../../core/themes';
|
|
15
15
|
import { ThemeStorage } from '../storage/ThemeStorage';
|
|
16
16
|
import { useDesignSystemTheme } from '../globalThemeStore';
|
|
@@ -21,6 +21,9 @@ interface ThemeState {
|
|
|
21
21
|
themeMode: ThemeMode;
|
|
22
22
|
isDark: boolean;
|
|
23
23
|
isInitialized: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ThemeActions {
|
|
24
27
|
setThemeMode: (mode: ThemeMode) => Promise<void>;
|
|
25
28
|
toggleTheme: () => Promise<void>;
|
|
26
29
|
initialize: () => Promise<void>;
|
|
@@ -39,66 +42,70 @@ interface ThemeState {
|
|
|
39
42
|
* };
|
|
40
43
|
* ```
|
|
41
44
|
*/
|
|
42
|
-
export const useTheme =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
45
|
+
export const useTheme = createStore<ThemeState, ThemeActions>({
|
|
46
|
+
name: 'theme-store',
|
|
47
|
+
initialState: {
|
|
48
|
+
theme: darkTheme,
|
|
49
|
+
themeMode: 'dark',
|
|
50
|
+
isDark: true,
|
|
51
|
+
isInitialized: false,
|
|
52
|
+
},
|
|
53
|
+
persist: false,
|
|
54
|
+
actions: (set, get) => ({
|
|
55
|
+
initialize: async () => {
|
|
56
|
+
try {
|
|
57
|
+
const savedMode = await ThemeStorage.getThemeMode();
|
|
58
|
+
if (savedMode) {
|
|
59
|
+
const theme = savedMode === 'light' ? lightTheme : darkTheme;
|
|
60
|
+
set({
|
|
61
|
+
themeMode: savedMode,
|
|
62
|
+
theme,
|
|
63
|
+
isDark: savedMode === 'dark',
|
|
64
|
+
isInitialized: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Sync with design system global theme
|
|
68
|
+
useDesignSystemTheme.getState().setThemeMode(savedMode);
|
|
69
|
+
} else {
|
|
70
|
+
// No saved mode - use default 'dark' and sync to design system store
|
|
71
|
+
set({ isInitialized: true });
|
|
72
|
+
// Ensure design system store is synced even if no saved mode exists
|
|
73
|
+
useDesignSystemTheme.getState().setThemeMode('dark');
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// Silent failure - still mark as initialized to prevent blocking
|
|
64
77
|
set({ isInitialized: true });
|
|
65
|
-
// Ensure design system store is synced even
|
|
78
|
+
// Ensure design system store is synced even on error
|
|
66
79
|
useDesignSystemTheme.getState().setThemeMode('dark');
|
|
67
80
|
}
|
|
68
|
-
}
|
|
69
|
-
if (__DEV__) console.error('[ThemeStore] Initialization error:', error);
|
|
70
|
-
// Silent failure - still mark as initialized to prevent blocking
|
|
71
|
-
set({ isInitialized: true });
|
|
72
|
-
// Ensure design system store is synced even on error
|
|
73
|
-
useDesignSystemTheme.getState().setThemeMode('dark');
|
|
74
|
-
}
|
|
75
|
-
},
|
|
81
|
+
},
|
|
76
82
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
setThemeMode: async (mode: ThemeMode) => {
|
|
84
|
+
try {
|
|
85
|
+
const theme = mode === 'light' ? lightTheme : darkTheme;
|
|
80
86
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
await ThemeStorage.setThemeMode(mode);
|
|
87
|
+
set({
|
|
88
|
+
themeMode: mode,
|
|
89
|
+
theme,
|
|
90
|
+
isDark: mode === 'dark',
|
|
91
|
+
});
|
|
88
92
|
|
|
89
|
-
|
|
90
|
-
useDesignSystemTheme.getState().setThemeMode(mode);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
if (__DEV__) console.error('[ThemeStore] Error setting theme mode:', error);
|
|
93
|
-
}
|
|
94
|
-
},
|
|
93
|
+
await ThemeStorage.setThemeMode(mode);
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
95
|
+
// Sync with design system global theme
|
|
96
|
+
useDesignSystemTheme.getState().setThemeMode(mode);
|
|
97
|
+
} catch {
|
|
98
|
+
// Silent failure
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
toggleTheme: async () => {
|
|
103
|
+
const { themeMode, setThemeMode } = get();
|
|
104
|
+
const newMode: ThemeMode = themeMode === 'light' ? 'dark' : 'light';
|
|
105
|
+
await setThemeMode(newMode);
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
});
|
|
102
109
|
|
|
103
110
|
|
|
104
111
|
|