@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.
- package/dist/core/cache/domain/CleanupStrategy.d.ts +62 -0
- package/dist/core/cache/domain/UnifiedCache.d.ts +82 -0
- package/dist/core/cache/domain/types.d.ts +15 -0
- package/dist/core/cache/index.d.ts +13 -0
- package/dist/core/cache/infrastructure/CacheFactory.d.ts +63 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/permissions/domain/PermissionHandler.d.ts +82 -0
- package/dist/core/permissions/domain/types.d.ts +42 -0
- package/dist/core/permissions/index.d.ts +7 -0
- package/dist/core/repositories/domain/RepositoryKeyFactory.d.ts +25 -0
- package/dist/core/repositories/domain/RepositoryUtils.d.ts +39 -0
- package/dist/core/repositories/domain/types.d.ts +53 -0
- package/dist/core/repositories/index.d.ts +10 -0
- package/dist/hooks/index.d.ts +27 -0
- package/dist/index.d.ts +1 -0
- package/dist/media/domain/strategies/CameraPickerStrategy.d.ts +25 -0
- package/dist/media/domain/strategies/LibraryPickerStrategy.d.ts +18 -0
- package/dist/media/domain/strategies/PickerStrategy.d.ts +55 -0
- package/dist/media/domain/strategies/index.d.ts +10 -0
- package/dist/media/infrastructure/services/MediaPickerService.d.ts +49 -6
- package/dist/media/infrastructure/utils/PermissionManager.d.ts +6 -0
- package/dist/media/infrastructure/utils/mediaPickerMappers.d.ts +15 -1
- package/dist/molecules/calendar/infrastructure/services/CalendarService.d.ts +1 -0
- package/dist/molecules/calendar/infrastructure/storage/CalendarStore.d.ts +2 -2
- package/dist/molecules/filter-group/FilterGroup.d.ts +1 -1
- package/dist/molecules/navigation/index.d.ts +1 -1
- package/dist/offline/index.d.ts +1 -1
- package/dist/offline/presentation/hooks/useOffline.d.ts +0 -5
- package/dist/tanstack/domain/repositories/BaseRepository.d.ts +5 -1
- package/dist/tanstack/infrastructure/monitoring/DevMonitor.d.ts +1 -0
- package/dist/utils/constants/TimeConstants.d.ts +27 -0
- package/package.json +1 -1
- package/src/atoms/image/AtomicImage.tsx +9 -3
- package/src/atoms/picker/components/PickerChips.tsx +27 -21
- package/src/image/presentation/components/ImageGallery.tsx +25 -21
- package/src/infinite-scroll/presentation/components/infinite-scroll-list.tsx +7 -1
- package/src/molecules/avatar/AvatarGroup.tsx +137 -62
- package/src/molecules/filter-group/FilterGroup.tsx +33 -22
- package/src/molecules/icon-grid/IconGrid.tsx +52 -20
- package/src/molecules/navigation/types.ts +2 -2
- package/src/onboarding/presentation/components/BackgroundImageCollage.tsx +32 -23
- 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(
|
|
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={
|
|
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
|
-
|
|
137
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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<
|
|
33
|
+
component?: React.ComponentType<{ navigation: unknown; route: unknown }>;
|
|
34
34
|
/** Render function for children (alternative to component) */
|
|
35
|
-
children?: (props:
|
|
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
|
-
|
|
84
|
-
|
|
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={
|
|
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
|
}
|