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

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 (38) 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/picker/components/PickerChips.tsx +27 -21
  34. package/src/image/presentation/components/ImageGallery.tsx +25 -21
  35. package/src/molecules/avatar/AvatarGroup.tsx +137 -62
  36. package/src/molecules/filter-group/FilterGroup.tsx +31 -22
  37. package/src/molecules/icon-grid/IconGrid.tsx +52 -20
  38. package/src/onboarding/presentation/components/BackgroundImageCollage.tsx +32 -23
@@ -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,33 @@ 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(selectedValue);
36
+ }
37
+ return new Set(selectedValue !== undefined ? [selectedValue] : []);
38
+ }, [selectedValue, multiSelect]);
39
+
40
+ // Memoize isSelected calculation for each item
41
+ const isSelected = useCallback((value: any) => selectedSet.has(value), [selectedSet]);
42
+
43
+ // Memoized chip renderer
44
+ const renderChip = useCallback((item: any) => (
45
+ <AtomicChip
46
+ key={String(item.value)}
47
+ variant={isSelected(item.value) ? 'filled' : 'outlined'}
48
+ color={isSelected(item.value) ? 'primary' : 'secondary'}
49
+ selected={isSelected(item.value)}
50
+ onPress={() => onSelect(item.value)}
51
+ clickable
52
+ style={[styles.item, itemStyle]}
53
+ testID={item.testID}
54
+ >
55
+ {item.label}
56
+ </AtomicChip>
57
+ ), [isSelected, onSelect, styles.item, itemStyle]);
58
+
32
59
  return (
33
60
  <ScrollView
34
61
  horizontal
@@ -37,25 +64,7 @@ export function FilterGroup<T = string>({
37
64
  style={[styles.container, style]}
38
65
  contentContainerStyle={[styles.content, contentContainerStyle]}
39
66
  >
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
- })}
67
+ {items.map(renderChip)}
59
68
  </ScrollView>
60
69
  );
61
- }
70
+ });
@@ -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
  });
@@ -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
+ });