@umituz/react-native-design-system 4.27.16 → 4.27.18

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 (42) hide show
  1. package/dist/core/cache/domain/CleanupStrategy.d.ts +62 -0
  2. package/dist/core/cache/domain/UnifiedCache.d.ts +82 -0
  3. package/dist/core/cache/domain/types.d.ts +15 -0
  4. package/dist/core/cache/index.d.ts +13 -0
  5. package/dist/core/cache/infrastructure/CacheFactory.d.ts +63 -0
  6. package/dist/core/index.d.ts +17 -0
  7. package/dist/core/permissions/domain/PermissionHandler.d.ts +82 -0
  8. package/dist/core/permissions/domain/types.d.ts +42 -0
  9. package/dist/core/permissions/index.d.ts +7 -0
  10. package/dist/core/repositories/domain/RepositoryKeyFactory.d.ts +25 -0
  11. package/dist/core/repositories/domain/RepositoryUtils.d.ts +39 -0
  12. package/dist/core/repositories/domain/types.d.ts +53 -0
  13. package/dist/core/repositories/index.d.ts +10 -0
  14. package/dist/hooks/index.d.ts +27 -0
  15. package/dist/index.d.ts +1 -0
  16. package/dist/media/domain/strategies/CameraPickerStrategy.d.ts +25 -0
  17. package/dist/media/domain/strategies/LibraryPickerStrategy.d.ts +18 -0
  18. package/dist/media/domain/strategies/PickerStrategy.d.ts +55 -0
  19. package/dist/media/domain/strategies/index.d.ts +10 -0
  20. package/dist/media/infrastructure/services/MediaPickerService.d.ts +49 -6
  21. package/dist/media/infrastructure/utils/PermissionManager.d.ts +6 -0
  22. package/dist/media/infrastructure/utils/mediaPickerMappers.d.ts +15 -1
  23. package/dist/molecules/calendar/infrastructure/services/CalendarService.d.ts +1 -0
  24. package/dist/molecules/calendar/infrastructure/storage/CalendarStore.d.ts +2 -2
  25. package/dist/molecules/filter-group/FilterGroup.d.ts +1 -1
  26. package/dist/molecules/navigation/index.d.ts +1 -1
  27. package/dist/offline/index.d.ts +1 -1
  28. package/dist/offline/presentation/hooks/useOffline.d.ts +0 -5
  29. package/dist/tanstack/domain/repositories/BaseRepository.d.ts +5 -1
  30. package/dist/tanstack/infrastructure/monitoring/DevMonitor.d.ts +1 -0
  31. package/dist/utils/constants/TimeConstants.d.ts +27 -0
  32. package/package.json +1 -1
  33. package/src/atoms/image/AtomicImage.tsx +9 -3
  34. package/src/atoms/picker/components/PickerChips.tsx +27 -21
  35. package/src/image/presentation/components/ImageGallery.tsx +25 -21
  36. package/src/infinite-scroll/presentation/components/infinite-scroll-list.tsx +7 -1
  37. package/src/molecules/avatar/AvatarGroup.tsx +137 -62
  38. package/src/molecules/filter-group/FilterGroup.tsx +33 -22
  39. package/src/molecules/icon-grid/IconGrid.tsx +52 -20
  40. package/src/molecules/navigation/types.ts +2 -2
  41. package/src/onboarding/presentation/components/BackgroundImageCollage.tsx +32 -23
  42. package/src/onboarding/presentation/components/OnboardingBackground.tsx +7 -2
@@ -1,11 +1,11 @@
1
1
 
2
- import React, { useMemo } from 'react';
2
+ import React, { useMemo, useCallback } from 'react';
3
3
  import { ScrollView, StyleSheet } from 'react-native';
4
4
  import { AtomicChip } from '../../atoms/chip/AtomicChip';
5
5
  import { useAppDesignTokens } from '../../theme';
6
6
  import type { FilterGroupProps } from './types';
7
7
 
8
- export function FilterGroup<T = string>({
8
+ export const FilterGroup = React.memo(function FilterGroup<T = string>({
9
9
  items,
10
10
  selectedValue,
11
11
  onSelect,
@@ -29,6 +29,35 @@ export function FilterGroup<T = string>({
29
29
  },
30
30
  }), [tokens.spacing.md]);
31
31
 
32
+ // Memoize selected items to prevent unnecessary re-renders
33
+ const selectedSet = useMemo(() => {
34
+ if (multiSelect && Array.isArray(selectedValue)) {
35
+ return new Set<T>(selectedValue);
36
+ }
37
+ // Single selection: wrap in array if present
38
+ const singleValue = selectedValue !== undefined ? [selectedValue] : [];
39
+ return new Set<T>(singleValue as T[]);
40
+ }, [selectedValue, multiSelect]);
41
+
42
+ // Memoize isSelected calculation for each item
43
+ const isSelected = useCallback((value: T) => selectedSet.has(value), [selectedSet]);
44
+
45
+ // Memoized chip renderer
46
+ const renderChip = useCallback((item: { value: T; label: string; testID?: string }) => (
47
+ <AtomicChip
48
+ key={String(item.value)}
49
+ variant={isSelected(item.value) ? 'filled' : 'outlined'}
50
+ color={isSelected(item.value) ? 'primary' : 'secondary'}
51
+ selected={isSelected(item.value)}
52
+ onPress={() => onSelect(item.value)}
53
+ clickable
54
+ style={[styles.item, itemStyle]}
55
+ testID={item.testID}
56
+ >
57
+ {item.label}
58
+ </AtomicChip>
59
+ ), [isSelected, onSelect, styles.item, itemStyle]);
60
+
32
61
  return (
33
62
  <ScrollView
34
63
  horizontal
@@ -37,25 +66,7 @@ export function FilterGroup<T = string>({
37
66
  style={[styles.container, style]}
38
67
  contentContainerStyle={[styles.content, contentContainerStyle]}
39
68
  >
40
- {items.map((item) => {
41
- const isSelected = multiSelect
42
- ? Array.isArray(selectedValue) && selectedValue.includes(item.value)
43
- : item.value === selectedValue;
44
- return (
45
- <AtomicChip
46
- key={`${item.value}`}
47
- variant={isSelected ? 'filled' : 'outlined'}
48
- color={isSelected ? 'primary' : 'secondary'}
49
- selected={isSelected}
50
- onPress={() => onSelect(item.value)}
51
- clickable
52
- style={[styles.item, itemStyle]}
53
- testID={item.testID}
54
- >
55
- {item.label}
56
- </AtomicChip>
57
- );
58
- })}
69
+ {items.map(renderChip)}
59
70
  </ScrollView>
60
71
  );
61
- }
72
+ });
@@ -14,6 +14,7 @@ import {
14
14
  type LayoutChangeEvent,
15
15
  type StyleProp,
16
16
  type ViewStyle,
17
+ FlatList,
17
18
  } from 'react-native';
18
19
  import { useAppDesignTokens } from '../../theme';
19
20
  import { AtomicIcon } from '../../atoms';
@@ -53,6 +54,8 @@ const GridItem = React.memo<{
53
54
  borderLight: string;
54
55
  textPrimary: string;
55
56
  }>(({ item, itemWidth, cardBackground, borderLight, textPrimary }) => {
57
+ const { onPress: handlePress } = item;
58
+
56
59
  const cardStyle = useMemo(
57
60
  () => [styles.card, { width: itemWidth }],
58
61
  [itemWidth]
@@ -74,7 +77,7 @@ const GridItem = React.memo<{
74
77
  return (
75
78
  <TouchableOpacity
76
79
  activeOpacity={0.7}
77
- onPress={item.onPress}
80
+ onPress={handlePress}
78
81
  style={cardStyle}
79
82
  >
80
83
  <View style={iconBoxStyle}>
@@ -133,28 +136,57 @@ export const IconGrid = React.memo<IconGridProps>(({
133
136
  [gap, rowGap, style]
134
137
  );
135
138
 
136
- const placeholderStyle = useMemo(
137
- () => ({ width: 0, height: 0 }),
138
- []
139
- );
139
+ // Memoize color props to prevent unnecessary GridItem re-renders
140
+ const colorProps = useMemo(() => ({
141
+ cardBackground,
142
+ borderLight,
143
+ textPrimary,
144
+ }), [cardBackground, borderLight, textPrimary]);
145
+
146
+ // Stable renderItem with memoization
147
+ const renderItem = useCallback(({ item }: { item: IconGridItem }) => {
148
+ if (itemWidth === 0) {
149
+ return <View key={item.id} style={{ width: 0, height: 0 }} />;
150
+ }
151
+
152
+ return (
153
+ <GridItem
154
+ item={item}
155
+ itemWidth={itemWidth}
156
+ {...colorProps}
157
+ />
158
+ );
159
+ }, [itemWidth, colorProps]);
160
+
161
+ const keyExtractor = useCallback((item: IconGridItem) => item.id, []);
162
+
163
+ const getItemLayout = useCallback((_?: unknown, index?: number) => ({
164
+ length: itemWidth,
165
+ offset: itemWidth * (index || 0),
166
+ index: index || 0,
167
+ }), [itemWidth]);
168
+
169
+ if (itemWidth === 0) {
170
+ return (
171
+ <View style={gridStyle} onLayout={handleLayout}>
172
+ {items.map((item) => (
173
+ <View key={item.id} style={{ width: 0, height: 0 }} />
174
+ ))}
175
+ </View>
176
+ );
177
+ }
140
178
 
141
179
  return (
142
180
  <View style={gridStyle} onLayout={handleLayout}>
143
- {items.map((item) =>
144
- itemWidth > 0 ? (
145
- <GridItem
146
- key={item.id}
147
- item={item}
148
- itemWidth={itemWidth}
149
- cardBackground={cardBackground}
150
- borderLight={borderLight}
151
- textPrimary={textPrimary}
152
- />
153
- ) : (
154
- // Placeholder — keeps grid stable before first layout measurement
155
- <View key={item.id} style={placeholderStyle} />
156
- ),
157
- )}
181
+ <FlatList
182
+ data={items}
183
+ renderItem={renderItem}
184
+ keyExtractor={keyExtractor}
185
+ getItemLayout={getItemLayout}
186
+ numColumns={columns}
187
+ scrollEnabled={false}
188
+ contentContainerStyle={gridStyle}
189
+ />
158
190
  </View>
159
191
  );
160
192
  });
@@ -30,9 +30,9 @@ export interface BaseScreen<T extends ParamListBase = ParamListBase> {
30
30
  /** Unique name identifier for the screen */
31
31
  name: Extract<keyof T, string>;
32
32
  /** React component to render for this screen */
33
- component?: React.ComponentType<any>;
33
+ component?: React.ComponentType<{ navigation: unknown; route: unknown }>;
34
34
  /** Render function for children (alternative to component) */
35
- children?: (props: any) => React.ReactNode;
35
+ children?: (props: { navigation: unknown; route: unknown }) => React.ReactNode;
36
36
  }
37
37
 
38
38
  /**
@@ -4,7 +4,7 @@
4
4
  * Uses expo-image when available, falls back to React Native Image.
5
5
  */
6
6
 
7
- import React, { useMemo } from "react";
7
+ import React, { useCallback, useMemo } from "react";
8
8
  import { View, Image as RNImage, StyleSheet, type ImageURISource, type ImageStyle } from "react-native";
9
9
  import { useSafeAreaInsets } from "../../../safe-area/hooks/useSafeAreaInsets";
10
10
  import {
@@ -59,7 +59,7 @@ const LAYOUT_GENERATORS: Record<CollageLayout, LayoutGenerator> = {
59
59
  honeycomb: generateHoneycombLayout,
60
60
  };
61
61
 
62
- export const BackgroundImageCollage: React.FC<BackgroundImageCollageProps> = ({
62
+ export const BackgroundImageCollage: React.FC<BackgroundImageCollageProps> = React.memo(({
63
63
  images,
64
64
  layout = "grid",
65
65
  columns,
@@ -75,30 +75,39 @@ export const BackgroundImageCollage: React.FC<BackgroundImageCollageProps> = ({
75
75
  return generator(images, { columns, gap, borderRadius, safeAreaInsets: insets });
76
76
  }, [images, layout, columns, gap, borderRadius, insets]);
77
77
 
78
+ // Stable key extractor - must be before early return
79
+ const keyExtractor = useCallback((item: ImageLayoutItem) => {
80
+ return typeof item.source === 'string' ? item.source : String(item.source);
81
+ }, []);
82
+
78
83
  if (imageLayouts.length === 0) return null;
79
84
 
85
+ // Memoized image component to prevent unnecessary re-renders
86
+ const CollageImage = React.memo<{ item: ImageLayoutItem }>(({ item }) => {
87
+ if (ExpoImage) {
88
+ return (
89
+ <ExpoImage
90
+ source={item.source}
91
+ style={item.style}
92
+ contentFit="cover"
93
+ />
94
+ );
95
+ }
96
+ return (
97
+ <RNImage
98
+ source={item.source as ImageURISource | number}
99
+ style={item.style as ImageStyle}
100
+ resizeMode="cover"
101
+ />
102
+ );
103
+ });
104
+ CollageImage.displayName = 'CollageImage';
105
+
80
106
  return (
81
107
  <View style={[StyleSheet.absoluteFill, { opacity }]} pointerEvents="none">
82
- {imageLayouts.map((item) => {
83
- if (ExpoImage) {
84
- return (
85
- <ExpoImage
86
- key={String(item.source)}
87
- source={item.source}
88
- style={item.style}
89
- contentFit="cover"
90
- />
91
- );
92
- }
93
- return (
94
- <RNImage
95
- key={String(item.source)}
96
- source={item.source as ImageURISource | number}
97
- style={item.style as ImageStyle}
98
- resizeMode="cover"
99
- />
100
- );
101
- })}
108
+ {imageLayouts.map((item) => (
109
+ <CollageImage key={keyExtractor(item)} item={item} />
110
+ ))}
102
111
  </View>
103
112
  );
104
- };
113
+ });
@@ -55,13 +55,18 @@ const BackgroundContent: React.FC<BackgroundContentProps> = ({
55
55
  }
56
56
 
57
57
  if (slide.backgroundImage) {
58
+ // Normalize ImageSourceType to ImageSourcePropType
59
+ // ImageSourceType supports string URIs, but ImageSourcePropType requires { uri: string } objects
60
+ const normalizedSource = typeof slide.backgroundImage === 'string'
61
+ ? { uri: slide.backgroundImage }
62
+ : slide.backgroundImage;
63
+
58
64
  return (
59
65
  <AtomicImage
60
- source={slide.backgroundImage}
66
+ source={normalizedSource}
61
67
  style={StyleSheet.absoluteFill}
62
68
  contentFit="cover"
63
69
  cachePolicy="memory-disk"
64
- priority="high"
65
70
  />
66
71
  );
67
72
  }