@umituz/react-native-design-system 4.27.17 → 4.27.19
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/image/AtomicImage.tsx +9 -3
- package/src/core/index.ts +3 -0
- package/src/core/shared/AsyncService.ts +53 -0
- package/src/core/shared/Result.ts +34 -0
- package/src/core/shared/index.ts +6 -0
- package/src/infinite-scroll/presentation/components/infinite-scroll-list.tsx +7 -1
- package/src/molecules/filter-group/FilterGroup.tsx +6 -4
- package/src/molecules/navigation/types.ts +2 -2
- package/src/onboarding/presentation/components/OnboardingBackground.tsx +7 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "4.27.
|
|
3
|
+
"version": "4.27.19",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities - TanStack persistence and expo-image-manipulator now lazy loaded",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Image as RNImage, type StyleProp, type ImageStyle } from 'react-native';
|
|
2
|
+
import { Image as RNImage, type StyleProp, type ImageStyle, ImageSourcePropType } from 'react-native';
|
|
3
3
|
|
|
4
4
|
// Lazy-load expo-image (optional peer dep) — falls back to React Native Image
|
|
5
|
+
// biome-ignore lint/suspicious/noExplicitAny: ExpoImage type is dynamic from optional peer dependency
|
|
5
6
|
let ExpoImage: React.ComponentType<any> | null = null;
|
|
6
7
|
try {
|
|
7
8
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
@@ -10,13 +11,18 @@ try {
|
|
|
10
11
|
// expo-image not installed — using React Native Image fallback
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Image source type compatible with both React Native and expo-image
|
|
16
|
+
* Supports: require() assets, URI strings, and image source objects
|
|
17
|
+
*/
|
|
18
|
+
export type ImageSource = ImageSourcePropType;
|
|
19
|
+
|
|
13
20
|
export type AtomicImageProps = {
|
|
14
|
-
source?:
|
|
21
|
+
source?: ImageSource;
|
|
15
22
|
style?: StyleProp<ImageStyle>;
|
|
16
23
|
rounded?: boolean;
|
|
17
24
|
contentFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
|
|
18
25
|
cachePolicy?: 'none' | 'disk' | 'memory' | 'memory-disk';
|
|
19
|
-
[key: string]: any;
|
|
20
26
|
};
|
|
21
27
|
|
|
22
28
|
const RESIZE_MODE_MAP: Record<string, 'cover' | 'contain' | 'stretch' | 'center'> = {
|
package/src/core/index.ts
CHANGED
|
@@ -21,3 +21,6 @@ export type { PermissionMethod, PermissionStatus, PermissionResult, PermissionHa
|
|
|
21
21
|
export { createRepositoryKeyFactory } from './repositories/domain/RepositoryKeyFactory';
|
|
22
22
|
export { mergeRepositoryOptions, getCacheOptions, normalizeListParams, createRepositoryLogger } from './repositories/domain/RepositoryUtils';
|
|
23
23
|
export type { RepositoryOptions, ListParams, CreateParams, UpdateParams, QueryKeyFactory } from './repositories/domain/types';
|
|
24
|
+
|
|
25
|
+
// Shared utilities (for new code)
|
|
26
|
+
export * from './shared';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async Service Base - Shared Utilities
|
|
3
|
+
*
|
|
4
|
+
* Base class for services with automatic error handling.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Result } from './Result';
|
|
8
|
+
import { ResultHelper } from './Result';
|
|
9
|
+
|
|
10
|
+
export abstract class AsyncService {
|
|
11
|
+
protected readonly serviceName: string;
|
|
12
|
+
|
|
13
|
+
constructor(serviceName: string) {
|
|
14
|
+
this.serviceName = serviceName;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Execute async operation with automatic error handling
|
|
19
|
+
*/
|
|
20
|
+
protected async execute<T>(
|
|
21
|
+
operation: string,
|
|
22
|
+
fn: () => Promise<T>
|
|
23
|
+
): Promise<Result<T>> {
|
|
24
|
+
try {
|
|
25
|
+
const data = await fn();
|
|
26
|
+
return ResultHelper.ok(data);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
this.logError(operation, error);
|
|
29
|
+
return ResultHelper.fail(error instanceof Error ? error : new Error(String(error)));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Log error in development mode
|
|
35
|
+
*/
|
|
36
|
+
protected logError(operation: string, error: unknown): void {
|
|
37
|
+
if (__DEV__) {
|
|
38
|
+
console.error(`[${this.serviceName}] ${operation}:`, error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected logWarning(message: string): void {
|
|
43
|
+
if (__DEV__) {
|
|
44
|
+
console.warn(`[${this.serviceName}] ${message}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected logInfo(message: string): void {
|
|
49
|
+
if (__DEV__) {
|
|
50
|
+
console.log(`[${this.serviceName}] ${message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result Type - Shared Utilities
|
|
3
|
+
*
|
|
4
|
+
* Functional error handling without exceptions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type Result<T, E = Error> =
|
|
8
|
+
| { success: true; data: T; error?: never }
|
|
9
|
+
| { success: false; data?: never; error: E };
|
|
10
|
+
|
|
11
|
+
export const ResultHelper = {
|
|
12
|
+
ok: <T>(data: T): Result<T> => ({ success: true, data }),
|
|
13
|
+
|
|
14
|
+
fail: <E = Error>(error: E): Result<never, E> => ({
|
|
15
|
+
success: false,
|
|
16
|
+
error,
|
|
17
|
+
}),
|
|
18
|
+
|
|
19
|
+
fromAsync: async <T>(
|
|
20
|
+
fn: () => Promise<T>
|
|
21
|
+
): Promise<Result<T, Error>> => {
|
|
22
|
+
try {
|
|
23
|
+
return ResultHelper.ok(await fn());
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return ResultHelper.fail(error instanceof Error ? error : new Error(String(error)));
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
map: <T, U>(
|
|
30
|
+
result: Result<T>,
|
|
31
|
+
fn: (data: T) => U
|
|
32
|
+
): Result<U> =>
|
|
33
|
+
result.success ? ResultHelper.ok(fn(result.data)) : ResultHelper.fail(result.error),
|
|
34
|
+
};
|
|
@@ -93,11 +93,17 @@ function InfiniteScrollListComponent<T>({
|
|
|
93
93
|
return emptyComponent || <Empty />;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// Memoize renderItem wrapper to prevent unnecessary re-renders
|
|
97
|
+
const memoizedRenderItem = useCallback(
|
|
98
|
+
({ item, index }: { item: T; index: number }) => renderItem(item, index),
|
|
99
|
+
[renderItem]
|
|
100
|
+
);
|
|
101
|
+
|
|
96
102
|
// Render list
|
|
97
103
|
return (
|
|
98
104
|
<FlatList
|
|
99
105
|
data={items}
|
|
100
|
-
renderItem={
|
|
106
|
+
renderItem={memoizedRenderItem}
|
|
101
107
|
keyExtractor={(item, index) => getItemKey(item, index)}
|
|
102
108
|
onEndReached={handleEndReached}
|
|
103
109
|
onEndReachedThreshold={calculateEndReachedThreshold(config.threshold)}
|
|
@@ -32,16 +32,18 @@ export const FilterGroup = React.memo(function FilterGroup<T = string>({
|
|
|
32
32
|
// Memoize selected items to prevent unnecessary re-renders
|
|
33
33
|
const selectedSet = useMemo(() => {
|
|
34
34
|
if (multiSelect && Array.isArray(selectedValue)) {
|
|
35
|
-
return new Set(selectedValue);
|
|
35
|
+
return new Set<T>(selectedValue);
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
// Single selection: wrap in array if present
|
|
38
|
+
const singleValue = selectedValue !== undefined ? [selectedValue] : [];
|
|
39
|
+
return new Set<T>(singleValue as T[]);
|
|
38
40
|
}, [selectedValue, multiSelect]);
|
|
39
41
|
|
|
40
42
|
// Memoize isSelected calculation for each item
|
|
41
|
-
const isSelected = useCallback((value:
|
|
43
|
+
const isSelected = useCallback((value: T) => selectedSet.has(value), [selectedSet]);
|
|
42
44
|
|
|
43
45
|
// Memoized chip renderer
|
|
44
|
-
const renderChip = useCallback((item:
|
|
46
|
+
const renderChip = useCallback((item: { value: T; label: string; testID?: string }) => (
|
|
45
47
|
<AtomicChip
|
|
46
48
|
key={String(item.value)}
|
|
47
49
|
variant={isSelected(item.value) ? 'filled' : 'outlined'}
|
|
@@ -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
|
/**
|
|
@@ -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
|
}
|