@umituz/react-native-design-system 2.5.9 → 2.5.11

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.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -1
  3. package/src/atoms/AtomicButton.tsx +2 -5
  4. package/src/atoms/AtomicChip.tsx +13 -13
  5. package/src/atoms/AtomicIcon.tsx +1 -8
  6. package/src/atoms/AtomicPicker.tsx +6 -5
  7. package/src/atoms/__tests__/AtomicIcon.test.tsx +0 -6
  8. package/src/atoms/input/hooks/useInputState.ts +0 -6
  9. package/src/atoms/picker/components/PickerModal.tsx +15 -6
  10. package/src/atoms/picker/types/index.ts +17 -4
  11. package/src/device/detection/deviceDetection.ts +7 -20
  12. package/src/layouts/ScreenHeader/ScreenHeader.tsx +1 -2
  13. package/src/molecules/alerts/AlertToast.tsx +1 -1
  14. package/src/molecules/animation/presentation/hooks/useFireworks.ts +1 -3
  15. package/src/molecules/animation/presentation/providers/AnimationThemeProvider.tsx +1 -3
  16. package/src/molecules/avatar/Avatar.tsx +2 -2
  17. package/src/molecules/bottom-sheet/components/BottomSheetModal.tsx +0 -6
  18. package/src/molecules/calendar/infrastructure/storage/CalendarStore.ts +243 -245
  19. package/src/molecules/navigation/StackNavigator.tsx +6 -9
  20. package/src/molecules/navigation/TabsNavigator.tsx +9 -13
  21. package/src/molecules/navigation/utils/AppNavigation.ts +0 -2
  22. package/src/molecules/navigation/utils/LabelProcessor.ts +1 -10
  23. package/src/molecules/navigation/utils/NavigationCleanup.ts +4 -8
  24. package/src/molecules/navigation/utils/NavigationValidator.ts +2 -11
  25. package/src/presentation/utils/variants/compound.ts +0 -4
  26. package/src/presentation/utils/variants/core.ts +0 -4
  27. package/src/presentation/utils/variants/helpers.ts +0 -6
  28. package/src/safe-area/components/SafeAreaProvider.tsx +0 -10
  29. package/src/safe-area/utils/validation.ts +3 -27
  30. package/src/theme/core/CustomColors.ts +2 -8
  31. package/src/theme/core/colors/ColorUtils.ts +0 -6
  32. package/src/theme/infrastructure/storage/ThemeStorage.ts +2 -16
  33. package/src/typography/presentation/utils/textColorUtils.ts +0 -3
  34. package/src/utilities/clipboard/ClipboardUtils.ts +1 -5
@@ -1,19 +1,17 @@
1
1
  /**
2
- * Calendar Store (Zustand)
2
+ * Calendar Store (Zustand via @umituz/react-native-storage)
3
3
  *
4
4
  * Global state management for calendar functionality.
5
5
  * Manages calendar view state, selected date, and events.
6
6
  *
7
7
  * Design Philosophy:
8
- * - Zustand for lightweight state
9
- * - AsyncStorage for persistence
8
+ * - Uses createStore from @umituz/react-native-storage
9
+ * - AsyncStorage persistence built-in
10
10
  * - Generic event handling
11
11
  * - Timezone-aware via CalendarService
12
12
  */
13
13
 
14
- import { create, type StateCreator } from 'zustand';
15
- import { persist, createJSONStorage, type PersistOptions } from 'zustand/middleware';
16
- import AsyncStorage from '@react-native-async-storage/async-storage';
14
+ import { createStore, storageRepository } from '@umituz/react-native-storage';
17
15
  import type { CalendarEvent, CreateCalendarEventRequest, UpdateCalendarEventRequest } from '../../domain/entities/CalendarEvent.entity';
18
16
  import { CalendarService } from '../services/CalendarService';
19
17
 
@@ -23,285 +21,285 @@ import { CalendarService } from '../services/CalendarService';
23
21
  export type CalendarViewMode = 'month' | 'week' | 'day' | 'list';
24
22
 
25
23
  /**
26
- * Calendar state interface
24
+ * Storage key for calendar events
25
+ */
26
+ const STORAGE_KEY = 'calendar_events';
27
+
28
+ /**
29
+ * Generate unique ID for events
30
+ */
31
+ const generateId = (): string => {
32
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
33
+ };
34
+
35
+ /**
36
+ * Calendar state (data only)
27
37
  */
28
38
  interface CalendarState {
29
- // State
30
39
  events: CalendarEvent[];
31
40
  selectedDate: Date;
32
41
  currentMonth: Date;
33
42
  viewMode: CalendarViewMode;
34
43
  isLoading: boolean;
35
44
  error: string | null;
45
+ }
36
46
 
37
- // Actions
38
- actions: {
39
- // Event CRUD
40
- loadEvents: () => Promise<void>;
41
- addEvent: (request: CreateCalendarEventRequest) => Promise<void>;
42
- updateEvent: (request: UpdateCalendarEventRequest) => Promise<void>;
43
- deleteEvent: (id: string) => Promise<void>;
44
- completeEvent: (id: string) => Promise<void>;
45
- uncompleteEvent: (id: string) => Promise<void>;
47
+ /**
48
+ * Calendar actions
49
+ */
50
+ interface CalendarActions {
51
+ // Event CRUD
52
+ loadEvents: () => Promise<void>;
53
+ addEvent: (request: CreateCalendarEventRequest) => Promise<void>;
54
+ updateEvent: (request: UpdateCalendarEventRequest) => Promise<void>;
55
+ deleteEvent: (id: string) => Promise<void>;
56
+ completeEvent: (id: string) => Promise<void>;
57
+ uncompleteEvent: (id: string) => Promise<void>;
46
58
 
47
- // Navigation
48
- setSelectedDate: (date: Date) => void;
49
- goToToday: () => void;
50
- navigateMonth: (direction: 'prev' | 'next') => void;
51
- navigateWeek: (direction: 'prev' | 'next') => void;
52
- setCurrentMonth: (date: Date) => void;
59
+ // Navigation
60
+ setSelectedDate: (date: Date) => void;
61
+ goToToday: () => void;
62
+ navigateMonth: (direction: 'prev' | 'next') => void;
63
+ navigateWeek: (direction: 'prev' | 'next') => void;
64
+ setCurrentMonth: (date: Date) => void;
53
65
 
54
- // View mode
55
- setViewMode: (mode: CalendarViewMode) => void;
66
+ // View mode
67
+ setViewMode: (mode: CalendarViewMode) => void;
56
68
 
57
- // Utilities
58
- getEventsForDate: (date: Date) => CalendarEvent[];
59
- getEventsForMonth: (year: number, month: number) => CalendarEvent[];
60
- clearError: () => void;
61
- clearAllEvents: () => Promise<void>;
62
- };
69
+ // Utilities
70
+ getEventsForDate: (date: Date) => CalendarEvent[];
71
+ getEventsForMonth: (year: number, month: number) => CalendarEvent[];
72
+ clearError: () => void;
73
+ clearAllEvents: () => Promise<void>;
63
74
  }
64
75
 
65
76
  /**
66
- * Storage key for calendar events
67
- */
68
- const STORAGE_KEY = 'calendar_events';
69
-
70
- /**
71
- * Generate unique ID for events
77
+ * Initial state
72
78
  */
73
- const generateId = (): string => {
74
- return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
79
+ const initialState: CalendarState = {
80
+ events: [],
81
+ selectedDate: new Date(),
82
+ currentMonth: new Date(),
83
+ viewMode: 'month',
84
+ isLoading: false,
85
+ error: null,
75
86
  };
76
87
 
77
88
  /**
78
89
  * Calendar Store
79
90
  */
80
- export const useCalendarStore = create<CalendarState>()(
81
- persist(
82
- (set, get) => ({
83
- // Initial State
84
- events: [],
85
- selectedDate: new Date(),
86
- currentMonth: new Date(),
87
- viewMode: 'month',
88
- isLoading: false,
89
- error: null,
91
+ export const useCalendarStore = createStore<CalendarState, CalendarActions>({
92
+ name: 'calendar-storage',
93
+ initialState,
94
+ persist: true,
95
+ partialize: (state) => ({ events: state.events }),
96
+ actions: (set, get) => ({
97
+ /**
98
+ * Load events from storage
99
+ */
100
+ loadEvents: async () => {
101
+ set({ isLoading: true, error: null });
102
+ try {
103
+ const stored = await storageRepository.getItem<CalendarEvent[]>(STORAGE_KEY);
104
+ if (stored) {
105
+ // Restore Date objects
106
+ const events = stored.map((event) => ({
107
+ ...event,
108
+ createdAt: new Date(event.createdAt),
109
+ updatedAt: new Date(event.updatedAt),
110
+ }));
111
+ set({ events, isLoading: false });
112
+ } else {
113
+ set({ isLoading: false });
114
+ }
115
+ } catch (error) {
116
+ set({
117
+ error: error instanceof Error ? error.message : 'Failed to load events',
118
+ isLoading: false,
119
+ });
120
+ }
121
+ },
90
122
 
91
- // Actions
92
- actions: {
93
- /**
94
- * Load events from storage
95
- */
96
- loadEvents: async () => {
97
- set({ isLoading: true, error: null });
98
- try {
99
- const stored = await AsyncStorage.getItem(STORAGE_KEY);
100
- if (stored) {
101
- const events = JSON.parse(stored);
102
- // Restore Date objects
103
- events.forEach((event: CalendarEvent) => {
104
- event.createdAt = new Date(event.createdAt);
105
- event.updatedAt = new Date(event.updatedAt);
106
- });
107
- set({ events, isLoading: false });
108
- } else {
109
- set({ isLoading: false });
110
- }
111
- } catch (error) {
112
- set({
113
- error: error instanceof Error ? error.message : 'Failed to load events',
114
- isLoading: false
115
- });
116
- }
117
- },
123
+ /**
124
+ * Add a new event
125
+ */
126
+ addEvent: async (request: CreateCalendarEventRequest) => {
127
+ set({ isLoading: true, error: null });
128
+ try {
129
+ const newEvent: CalendarEvent = {
130
+ id: generateId(),
131
+ ...request,
132
+ isCompleted: false,
133
+ createdAt: new Date(),
134
+ updatedAt: new Date(),
135
+ };
136
+
137
+ const events = [...get().events, newEvent];
138
+ await storageRepository.setItem(STORAGE_KEY, events);
139
+ set({ events, isLoading: false });
140
+ } catch (error) {
141
+ set({
142
+ error: error instanceof Error ? error.message : 'Failed to add event',
143
+ isLoading: false,
144
+ });
145
+ }
146
+ },
118
147
 
119
- /**
120
- * Add a new event
121
- */
122
- addEvent: async (request: CreateCalendarEventRequest) => {
123
- set({ isLoading: true, error: null });
124
- try {
125
- const newEvent: CalendarEvent = {
126
- id: generateId(),
148
+ /**
149
+ * Update an existing event
150
+ */
151
+ updateEvent: async (request: UpdateCalendarEventRequest) => {
152
+ set({ isLoading: true, error: null });
153
+ try {
154
+ const events = get().events.map((event) => {
155
+ if (event.id === request.id) {
156
+ return {
157
+ ...event,
127
158
  ...request,
128
- isCompleted: false,
129
- createdAt: new Date(),
130
159
  updatedAt: new Date(),
131
160
  };
132
-
133
- const events = [...get().events, newEvent];
134
- await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(events));
135
- set({ events, isLoading: false });
136
- } catch (error) {
137
- set({
138
- error: error instanceof Error ? error.message : 'Failed to add event',
139
- isLoading: false
140
- });
141
161
  }
142
- },
162
+ return event;
163
+ });
143
164
 
144
- /**
145
- * Update an existing event
146
- */
147
- updateEvent: async (request: UpdateCalendarEventRequest) => {
148
- set({ isLoading: true, error: null });
149
- try {
150
- const events = get().events.map(event => {
151
- if (event.id === request.id) {
152
- return {
153
- ...event,
154
- ...request,
155
- updatedAt: new Date(),
156
- };
157
- }
158
- return event;
159
- });
165
+ await storageRepository.setItem(STORAGE_KEY, events);
166
+ set({ events, isLoading: false });
167
+ } catch (error) {
168
+ set({
169
+ error: error instanceof Error ? error.message : 'Failed to update event',
170
+ isLoading: false,
171
+ });
172
+ }
173
+ },
160
174
 
161
- await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(events));
162
- set({ events, isLoading: false });
163
- } catch (error) {
164
- set({
165
- error: error instanceof Error ? error.message : 'Failed to update event',
166
- isLoading: false
167
- });
168
- }
169
- },
175
+ /**
176
+ * Delete an event
177
+ */
178
+ deleteEvent: async (id: string) => {
179
+ set({ isLoading: true, error: null });
180
+ try {
181
+ const events = get().events.filter((event) => event.id !== id);
182
+ await storageRepository.setItem(STORAGE_KEY, events);
183
+ set({ events, isLoading: false });
184
+ } catch (error) {
185
+ set({
186
+ error: error instanceof Error ? error.message : 'Failed to delete event',
187
+ isLoading: false,
188
+ });
189
+ }
190
+ },
170
191
 
171
- /**
172
- * Delete an event
173
- */
174
- deleteEvent: async (id: string) => {
175
- set({ isLoading: true, error: null });
176
- try {
177
- const events = get().events.filter(event => event.id !== id);
178
- await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(events));
179
- set({ events, isLoading: false });
180
- } catch (error) {
181
- set({
182
- error: error instanceof Error ? error.message : 'Failed to delete event',
183
- isLoading: false
184
- });
185
- }
186
- },
187
-
188
- /**
189
- * Mark event as completed
190
- */
191
- completeEvent: async (id: string) => {
192
- await get().actions.updateEvent({ id, isCompleted: true });
193
- },
192
+ /**
193
+ * Mark event as completed
194
+ */
195
+ completeEvent: async (id: string) => {
196
+ await get().updateEvent({ id, isCompleted: true });
197
+ },
194
198
 
195
- /**
196
- * Mark event as incomplete
197
- */
198
- uncompleteEvent: async (id: string) => {
199
- await get().actions.updateEvent({ id, isCompleted: false });
200
- },
199
+ /**
200
+ * Mark event as incomplete
201
+ */
202
+ uncompleteEvent: async (id: string) => {
203
+ await get().updateEvent({ id, isCompleted: false });
204
+ },
201
205
 
202
- /**
203
- * Set selected date
204
- */
205
- setSelectedDate: (date: Date) => {
206
- set({ selectedDate: date });
207
- },
206
+ /**
207
+ * Set selected date
208
+ */
209
+ setSelectedDate: (date: Date) => {
210
+ set({ selectedDate: date });
211
+ },
208
212
 
209
- /**
210
- * Go to today's date
211
- */
212
- goToToday: () => {
213
- const today = new Date();
214
- set({
215
- selectedDate: today,
216
- currentMonth: today,
217
- });
218
- },
213
+ /**
214
+ * Go to today's date
215
+ */
216
+ goToToday: () => {
217
+ const today = new Date();
218
+ set({
219
+ selectedDate: today,
220
+ currentMonth: today,
221
+ });
222
+ },
219
223
 
220
- /**
221
- * Navigate to previous/next month
222
- */
223
- navigateMonth: (direction: 'prev' | 'next') => {
224
- const currentMonth = get().currentMonth;
225
- const newMonth = direction === 'prev'
226
- ? CalendarService.getPreviousMonth(currentMonth)
227
- : CalendarService.getNextMonth(currentMonth);
224
+ /**
225
+ * Navigate to previous/next month
226
+ */
227
+ navigateMonth: (direction: 'prev' | 'next') => {
228
+ const currentMonth = get().currentMonth;
229
+ const newMonth =
230
+ direction === 'prev'
231
+ ? CalendarService.getPreviousMonth(currentMonth)
232
+ : CalendarService.getNextMonth(currentMonth);
228
233
 
229
- set({ currentMonth: newMonth });
230
- },
234
+ set({ currentMonth: newMonth });
235
+ },
231
236
 
232
- /**
233
- * Navigate to previous/next week
234
- */
235
- navigateWeek: (direction: 'prev' | 'next') => {
236
- const selectedDate = get().selectedDate;
237
- const newDate = direction === 'prev'
238
- ? CalendarService.getPreviousWeek(selectedDate)
239
- : CalendarService.getNextWeek(selectedDate);
237
+ /**
238
+ * Navigate to previous/next week
239
+ */
240
+ navigateWeek: (direction: 'prev' | 'next') => {
241
+ const selectedDate = get().selectedDate;
242
+ const newDate =
243
+ direction === 'prev'
244
+ ? CalendarService.getPreviousWeek(selectedDate)
245
+ : CalendarService.getNextWeek(selectedDate);
240
246
 
241
- set({ selectedDate: newDate });
242
- },
247
+ set({ selectedDate: newDate });
248
+ },
243
249
 
244
- /**
245
- * Set current month directly
246
- */
247
- setCurrentMonth: (date: Date) => {
248
- set({ currentMonth: date });
249
- },
250
+ /**
251
+ * Set current month directly
252
+ */
253
+ setCurrentMonth: (date: Date) => {
254
+ set({ currentMonth: date });
255
+ },
250
256
 
251
- /**
252
- * Set view mode
253
- */
254
- setViewMode: (mode: CalendarViewMode) => {
255
- set({ viewMode: mode });
256
- },
257
+ /**
258
+ * Set view mode
259
+ */
260
+ setViewMode: (mode: CalendarViewMode) => {
261
+ set({ viewMode: mode });
262
+ },
257
263
 
258
- /**
259
- * Get events for a specific date
260
- */
261
- getEventsForDate: (date: Date) => {
262
- const events = get().events;
263
- return CalendarService.getEventsForDate(date, events);
264
- },
264
+ /**
265
+ * Get events for a specific date
266
+ */
267
+ getEventsForDate: (date: Date) => {
268
+ const events = get().events;
269
+ return CalendarService.getEventsForDate(date, events);
270
+ },
265
271
 
266
- /**
267
- * Get events for a specific month
268
- */
269
- getEventsForMonth: (year: number, month: number) => {
270
- const events = get().events;
271
- const firstDay = new Date(year, month, 1);
272
- const lastDay = new Date(year, month + 1, 0);
273
- return CalendarService.getEventsInRange(firstDay, lastDay, events);
274
- },
272
+ /**
273
+ * Get events for a specific month
274
+ */
275
+ getEventsForMonth: (year: number, month: number) => {
276
+ const events = get().events;
277
+ const firstDay = new Date(year, month, 1);
278
+ const lastDay = new Date(year, month + 1, 0);
279
+ return CalendarService.getEventsInRange(firstDay, lastDay, events);
280
+ },
275
281
 
276
- /**
277
- * Clear error state
278
- */
279
- clearError: () => {
280
- set({ error: null });
281
- },
282
+ /**
283
+ * Clear error state
284
+ */
285
+ clearError: () => {
286
+ set({ error: null });
287
+ },
282
288
 
283
- /**
284
- * Clear all events (for testing/reset)
285
- */
286
- clearAllEvents: async () => {
287
- set({ isLoading: true, error: null });
288
- try {
289
- await AsyncStorage.removeItem(STORAGE_KEY);
290
- set({ events: [], isLoading: false });
291
- } catch (error) {
292
- set({
293
- error: error instanceof Error ? error.message : 'Failed to clear events',
294
- isLoading: false
295
- });
296
- }
297
- },
298
- },
299
- }),
300
- {
301
- name: 'calendar-storage',
302
- storage: createJSONStorage(() => AsyncStorage),
303
- // Only persist events, not UI state
304
- partialize: (state) => ({ events: state.events }),
305
- }
306
- )
307
- );
289
+ /**
290
+ * Clear all events (for testing/reset)
291
+ */
292
+ clearAllEvents: async () => {
293
+ set({ isLoading: true, error: null });
294
+ try {
295
+ await storageRepository.removeItem(STORAGE_KEY);
296
+ set({ events: [], isLoading: false });
297
+ } catch (error) {
298
+ set({
299
+ error: error instanceof Error ? error.message : 'Failed to clear events',
300
+ isLoading: false,
301
+ });
302
+ }
303
+ },
304
+ }),
305
+ });
@@ -22,14 +22,12 @@ export interface StackNavigatorProps<T extends ParamListBase> {
22
22
  export function StackNavigator<T extends ParamListBase>({ config }: StackNavigatorProps<T>) {
23
23
  const tokens = useAppDesignTokens();
24
24
 
25
- // Validate configuration
26
- if (__DEV__) {
27
- try {
28
- NavigationValidator.validateScreens(config.screens, "stack");
29
- NavigationValidator.validateInitialRoute(config.initialRouteName, config.screens);
30
- } catch (error) {
31
- if (__DEV__) console.error('[StackNavigator] Configuration validation failed:', error);
32
- }
25
+ // Validate configuration silently
26
+ try {
27
+ NavigationValidator.validateScreens(config.screens, "stack");
28
+ NavigationValidator.validateInitialRoute(config.initialRouteName, config.screens);
29
+ } catch {
30
+ // Silent validation failure
33
31
  }
34
32
 
35
33
  const { screens } = config;
@@ -55,7 +53,6 @@ export function StackNavigator<T extends ParamListBase>({ config }: StackNavigat
55
53
  }), [tokens]);
56
54
 
57
55
  if (screens.length === 0) {
58
- if (__DEV__) console.warn('[StackNavigator] No screens found');
59
56
  return null;
60
57
  }
61
58
 
@@ -24,18 +24,15 @@ export function TabsNavigator<T extends ParamListBase>({
24
24
  }: TabsNavigatorProps<T>) {
25
25
  const tokens = useAppDesignTokens();
26
26
 
27
- // Validate configuration
28
- if (__DEV__) {
29
- try {
30
- NavigationValidator.validateScreens(config.screens, "tab");
31
- NavigationValidator.validateInitialRoute(
32
- config.initialRouteName,
33
- config.screens
34
- );
35
- } catch (error) {
36
- if (__DEV__)
37
- console.error("[TabsNavigator] Configuration validation failed:", error);
38
- }
27
+ // Validate configuration silently
28
+ try {
29
+ NavigationValidator.validateScreens(config.screens, "tab");
30
+ NavigationValidator.validateInitialRoute(
31
+ config.initialRouteName,
32
+ config.screens
33
+ );
34
+ } catch {
35
+ // Silent validation failure
39
36
  }
40
37
 
41
38
  // Memoize filtered screens
@@ -74,7 +71,6 @@ export function TabsNavigator<T extends ParamListBase>({
74
71
  );
75
72
 
76
73
  if (visibleScreens.length === 0) {
77
- if (__DEV__) console.warn("[TabsNavigator] No visible screens found");
78
74
  return null;
79
75
  }
80
76
 
@@ -31,8 +31,6 @@ export class AppNavigation {
31
31
  static navigate(name: string, params?: object): void {
32
32
  if (this.navigationRef?.isReady()) {
33
33
  this.navigationRef.navigate(name, params);
34
- } else if (__DEV__) {
35
- console.warn('[AppNavigation] Navigation ref is not ready. Call setRef() first.');
36
34
  }
37
35
  }
38
36
 
@@ -28,10 +28,7 @@ export class LabelProcessor {
28
28
  try {
29
29
  const processedLabel = getLabel(label);
30
30
  result = typeof processedLabel === "string" ? processedLabel : label;
31
- } catch (error) {
32
- if (__DEV__) {
33
- console.error(`[LabelProcessor] Error processing label: ${label}`, error);
34
- }
31
+ } catch {
35
32
  result = label;
36
33
  }
37
34
  }
@@ -45,12 +42,6 @@ export class LabelProcessor {
45
42
  }
46
43
  this.labelCache.set(cacheKey, result);
47
44
 
48
- // Log cache performance in development
49
- if (__DEV__ && (this.cacheHits + this.cacheMisses) % 100 === 0) {
50
- const hitRate = (this.cacheHits / (this.cacheHits + this.cacheMisses)) * 100;
51
- console.log(`[LabelProcessor] Cache hit rate: ${hitRate.toFixed(1)}%`);
52
- }
53
-
54
45
  return result;
55
46
  }
56
47