@umituz/react-native-design-system 4.23.67 → 4.23.69
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/EmptyState.tsx +2 -2
- package/src/atoms/icon/AtomicIcon.tsx +41 -112
- package/src/atoms/icon/components/iconRenderer.tsx +118 -0
- package/src/atoms/icon/utils/iconUtils.ts +94 -0
- package/src/exception/infrastructure/services/ExceptionService.ts +29 -17
- package/src/exception/presentation/components/ExceptionEmptyState.tsx +1 -1
- package/src/exception/presentation/components/ExceptionErrorState.tsx +1 -1
- package/src/image/infrastructure/services/ImageBatchService.ts +1 -7
- package/src/image/infrastructure/types/BatchTypes.ts +11 -0
- package/src/image/infrastructure/utils/BatchProcessor.ts +1 -1
- package/src/image/infrastructure/utils/ImageErrorHandler.ts +1 -0
- package/src/infinite-scroll/presentation/components/infinite-scroll-list.tsx +2 -2
- package/src/layouts/ScreenHeader/ScreenHeader.tsx +3 -3
- package/src/media/presentation/hooks/useCardMediaGeneration.ts +4 -4
- package/src/media/presentation/hooks/useCardMediaUpload.ts +4 -4
- package/src/media/presentation/hooks/useCardMediaValidation.ts +4 -4
- package/src/media/presentation/hooks/useCardMultimediaFlashcard.ts +5 -5
- package/src/media/presentation/hooks/useMediaGeneration.ts +4 -4
- package/src/media/presentation/hooks/useMediaUpload.ts +4 -4
- package/src/media/presentation/hooks/useMediaValidation.ts +4 -4
- package/src/media/presentation/hooks/useMultimediaFlashcard.ts +5 -5
- package/src/molecules/BaseModal.tsx +1 -1
- package/src/molecules/ConfirmationModalContent.tsx +2 -2
- package/src/molecules/ConfirmationModalMain.tsx +2 -2
- package/src/molecules/alerts/AlertToast.tsx +163 -192
- package/src/molecules/alerts/utils/alertToastHelpers.ts +70 -0
- package/src/molecules/bottom-sheet/components/filter/FilterBottomSheet.tsx +2 -2
- package/src/molecules/calendar/presentation/components/AtomicCalendar.tsx +1 -1
- package/src/molecules/calendar/presentation/components/CalendarDayCell.tsx +2 -1
- package/src/molecules/calendar/presentation/components/CalendarWeekdayHeader.tsx +1 -1
- package/src/molecules/confirmation-modal/useConfirmationModal.ts +6 -6
- package/src/molecules/countdown/components/Countdown.tsx +2 -2
- package/src/molecules/splash/components/SplashScreen.tsx +9 -23
- package/src/molecules/swipe-actions/domain/entities/SwipeAction.ts +1 -1
- package/src/molecules/swipe-actions/presentation/components/SwipeActionButton.tsx +2 -2
- package/src/organisms/FormContainer.tsx +2 -2
- package/src/responsive/validation.ts +1 -0
- package/src/services/api/ApiClient.ts +242 -0
- package/src/services/api/index.ts +9 -0
- package/src/services/api/types/ApiTypes.ts +50 -0
- package/src/services/api/utils/requestBuilder.ts +92 -0
- package/src/services/api/utils/responseHandler.ts +130 -0
- package/src/storage/cache/domain/ErrorHandler.ts +1 -0
- package/src/storage/domain/errors/StorageError.ts +6 -0
- package/src/storage/infrastructure/repositories/AsyncStorageRepository.ts +31 -16
- package/src/tanstack/domain/repositories/BaseRepository.ts +16 -72
- package/src/tanstack/domain/repositories/IBaseRepository.ts +34 -0
- package/src/tanstack/domain/repositories/helpers/repositoryHelpers.ts +58 -0
- package/src/tanstack/domain/repositories/mixins/repositoryInvalidationMethods.ts +101 -0
- package/src/tanstack/domain/repositories/mixins/repositoryQueryMethods.ts +102 -0
- package/src/tanstack/infrastructure/providers/TanstackProvider.tsx +3 -3
- package/src/tanstack/presentation/hooks/types/prefetchTypes.ts +33 -0
- package/src/tanstack/presentation/hooks/usePrefetch.ts +8 -28
- package/src/tanstack/presentation/hooks/utils/prefetchLogger.ts +27 -0
- package/src/theme/index.ts +0 -3
- package/src/theme/infrastructure/providers/DesignSystemProvider.tsx +15 -4
- package/src/utils/colorMapper.ts +193 -0
- package/src/utils/formatHelper.ts +16 -0
- package/src/utils/formatters/dateFormatter.ts +64 -0
- package/src/utils/formatters/numberFormatter.ts +130 -0
- package/src/utils/formatters/stringFormatter.ts +190 -0
- package/src/utils/index.ts +15 -0
- package/src/utils/styleComposer.ts +94 -0
- package/src/utils/validationHelper.ts +16 -0
- package/src/utils/validators/dataValidators.ts +111 -0
- package/src/utils/validators/numericValidators.ts +106 -0
- package/src/utils/validators/stringValidators.ts +85 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.69",
|
|
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",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
package/src/atoms/EmptyState.tsx
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Purpose: Empty state indication across all apps
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import React from 'react';
|
|
10
|
+
import React, { useState, useEffect, useCallback, useMemo, useRef, useContext } from 'react';
|
|
11
11
|
import { View, StyleSheet, TouchableOpacity, ViewStyle } from 'react-native';
|
|
12
12
|
import { AtomicIcon } from './icon';
|
|
13
13
|
import { AtomicText } from './AtomicText';
|
|
@@ -39,7 +39,7 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
|
|
|
39
39
|
const tokens = useAppDesignTokens();
|
|
40
40
|
const displayDescription = description || subtitle;
|
|
41
41
|
|
|
42
|
-
const themedStyles =
|
|
42
|
+
const themedStyles = useMemo(
|
|
43
43
|
() =>
|
|
44
44
|
StyleSheet.create({
|
|
45
45
|
container: {
|
|
@@ -20,19 +20,27 @@
|
|
|
20
20
|
* <AtomicIcon name="favorite" size="md" color="primary" />
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import React from
|
|
24
|
-
import {
|
|
25
|
-
import
|
|
26
|
-
import {
|
|
27
|
-
import { useIconRenderer, type IconRenderProps } from "./iconStore";
|
|
23
|
+
import React from 'react';
|
|
24
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
25
|
+
import { useAppDesignTokens } from '../../theme/hooks/useAppDesignTokens';
|
|
26
|
+
import { useIconRenderer } from './iconStore';
|
|
28
27
|
import {
|
|
29
28
|
type IconSize as BaseIconSize,
|
|
30
29
|
type IconColor,
|
|
31
|
-
} from
|
|
30
|
+
} from './AtomicIcon.types';
|
|
31
|
+
import {
|
|
32
|
+
calculateIconSize,
|
|
33
|
+
calculateIconColor,
|
|
34
|
+
} from './utils/iconUtils';
|
|
35
|
+
import {
|
|
36
|
+
renderSvgIcon,
|
|
37
|
+
renderWithBackground,
|
|
38
|
+
buildIconRenderProps,
|
|
39
|
+
} from './components/iconRenderer';
|
|
32
40
|
|
|
33
41
|
export type IconSize = BaseIconSize;
|
|
34
42
|
export type IconName = string;
|
|
35
|
-
export type { IconColor
|
|
43
|
+
export type { IconColor };
|
|
36
44
|
|
|
37
45
|
export interface AtomicIconProps {
|
|
38
46
|
/** Icon name - interpreted by the app's icon renderer */
|
|
@@ -61,44 +69,20 @@ export interface AtomicIconProps {
|
|
|
61
69
|
style?: StyleProp<ViewStyle>;
|
|
62
70
|
}
|
|
63
71
|
|
|
64
|
-
const getSemanticColor = (
|
|
65
|
-
color: IconColor,
|
|
66
|
-
tokens: ReturnType<typeof useAppDesignTokens>
|
|
67
|
-
): string => {
|
|
68
|
-
const colorMap: Record<IconColor, string> = {
|
|
69
|
-
primary: tokens.colors.primary,
|
|
70
|
-
secondary: tokens.colors.secondary,
|
|
71
|
-
success: tokens.colors.success,
|
|
72
|
-
warning: tokens.colors.warning,
|
|
73
|
-
error: tokens.colors.error,
|
|
74
|
-
info: tokens.colors.info,
|
|
75
|
-
onSurface: tokens.colors.onSurface,
|
|
76
|
-
surfaceVariant: tokens.colors.surfaceVariant,
|
|
77
|
-
onPrimary: tokens.colors.onPrimary,
|
|
78
|
-
onSecondary: tokens.colors.onSecondary,
|
|
79
|
-
textInverse: tokens.colors.textInverse,
|
|
80
|
-
textPrimary: tokens.colors.textPrimary,
|
|
81
|
-
textSecondary: tokens.colors.textSecondary,
|
|
82
|
-
textTertiary: tokens.colors.textTertiary,
|
|
83
|
-
onSurfaceVariant: tokens.colors.onSurfaceVariant,
|
|
84
|
-
};
|
|
85
|
-
return colorMap[color];
|
|
86
|
-
};
|
|
87
|
-
|
|
88
72
|
/**
|
|
89
73
|
* Agnostic icon component - requires iconRenderer in DesignSystemProvider
|
|
90
74
|
*/
|
|
91
75
|
export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(
|
|
92
76
|
({
|
|
93
77
|
name,
|
|
94
|
-
size =
|
|
78
|
+
size = 'md',
|
|
95
79
|
customSize,
|
|
96
80
|
color,
|
|
97
81
|
customColor,
|
|
98
82
|
withBackground = false,
|
|
99
83
|
backgroundColor,
|
|
100
84
|
svgPath,
|
|
101
|
-
svgViewBox =
|
|
85
|
+
svgViewBox = '0 0 24 24',
|
|
102
86
|
accessibilityLabel,
|
|
103
87
|
testID,
|
|
104
88
|
style,
|
|
@@ -106,34 +90,19 @@ export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(
|
|
|
106
90
|
const tokens = useAppDesignTokens();
|
|
107
91
|
const iconRenderer = useIconRenderer();
|
|
108
92
|
|
|
109
|
-
// Calculate size
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
const sizeInPixels: number =
|
|
113
|
-
typeof baseSize === "number"
|
|
114
|
-
? baseSize * tokens.spacingMultiplier
|
|
115
|
-
: iconSizesMap[baseSize] ?? iconSizesMap["md"] ?? 24;
|
|
116
|
-
|
|
117
|
-
// Calculate color
|
|
118
|
-
const iconColor = customColor
|
|
119
|
-
? customColor
|
|
120
|
-
: color
|
|
121
|
-
? getSemanticColor(color, tokens)
|
|
122
|
-
: tokens.colors.textPrimary;
|
|
93
|
+
// Calculate size and color using utility functions
|
|
94
|
+
const sizeInPixels = calculateIconSize(size, customSize, tokens);
|
|
95
|
+
const iconColor = calculateIconColor(customColor, color, tokens);
|
|
123
96
|
|
|
124
97
|
// SVG path rendering (built-in, no external dependency)
|
|
125
98
|
if (svgPath) {
|
|
126
|
-
const svgElement = (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
accessibilityLabel={accessibilityLabel}
|
|
134
|
-
>
|
|
135
|
-
<Path d={svgPath} fill={iconColor} />
|
|
136
|
-
</Svg>
|
|
99
|
+
const svgElement = renderSvgIcon(
|
|
100
|
+
svgPath,
|
|
101
|
+
svgViewBox,
|
|
102
|
+
sizeInPixels,
|
|
103
|
+
iconColor,
|
|
104
|
+
testID,
|
|
105
|
+
accessibilityLabel
|
|
137
106
|
);
|
|
138
107
|
|
|
139
108
|
if (withBackground) {
|
|
@@ -154,27 +123,27 @@ export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(
|
|
|
154
123
|
if (!iconRenderer) {
|
|
155
124
|
if (__DEV__) {
|
|
156
125
|
console.warn(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
126
|
+
'[DesignSystem] AtomicIcon requires an iconRenderer in DesignSystemProvider.\n' +
|
|
127
|
+
'Example:\n' +
|
|
128
|
+
'<DesignSystemProvider\n' +
|
|
160
129
|
' iconRenderer={({ name, size, color }) => (\n' +
|
|
161
130
|
' <YourIconLibrary name={name} size={size} color={color} />\n' +
|
|
162
|
-
|
|
163
|
-
|
|
131
|
+
' )}\n' +
|
|
132
|
+
'>'
|
|
164
133
|
);
|
|
165
134
|
}
|
|
166
135
|
return null;
|
|
167
136
|
}
|
|
168
137
|
|
|
169
|
-
// Build render props
|
|
170
|
-
const renderProps
|
|
171
|
-
name
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
style
|
|
138
|
+
// Build render props and render icon
|
|
139
|
+
const renderProps = buildIconRenderProps(
|
|
140
|
+
name || '',
|
|
141
|
+
sizeInPixels,
|
|
142
|
+
iconColor,
|
|
143
|
+
style,
|
|
175
144
|
testID,
|
|
176
|
-
accessibilityLabel
|
|
177
|
-
|
|
145
|
+
accessibilityLabel
|
|
146
|
+
);
|
|
178
147
|
|
|
179
148
|
const iconElement = iconRenderer(renderProps);
|
|
180
149
|
|
|
@@ -193,44 +162,4 @@ export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(
|
|
|
193
162
|
}
|
|
194
163
|
);
|
|
195
164
|
|
|
196
|
-
AtomicIcon.displayName =
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Helper to render icon with circular background
|
|
200
|
-
*/
|
|
201
|
-
function renderWithBackground(
|
|
202
|
-
iconElement: React.ReactNode,
|
|
203
|
-
sizeInPixels: number,
|
|
204
|
-
bgColor: string,
|
|
205
|
-
style: StyleProp<ViewStyle> | undefined,
|
|
206
|
-
testID: string | undefined,
|
|
207
|
-
accessibilityLabel: string | undefined
|
|
208
|
-
): React.ReactElement {
|
|
209
|
-
const containerSize = sizeInPixels + 16;
|
|
210
|
-
|
|
211
|
-
return (
|
|
212
|
-
<View
|
|
213
|
-
style={[
|
|
214
|
-
styles.backgroundContainer,
|
|
215
|
-
{
|
|
216
|
-
width: containerSize,
|
|
217
|
-
height: containerSize,
|
|
218
|
-
borderRadius: containerSize / 2,
|
|
219
|
-
backgroundColor: bgColor,
|
|
220
|
-
},
|
|
221
|
-
style,
|
|
222
|
-
]}
|
|
223
|
-
testID={testID}
|
|
224
|
-
accessibilityLabel={accessibilityLabel}
|
|
225
|
-
>
|
|
226
|
-
{iconElement}
|
|
227
|
-
</View>
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const styles = StyleSheet.create({
|
|
232
|
-
backgroundContainer: {
|
|
233
|
-
justifyContent: "center",
|
|
234
|
-
alignItems: "center",
|
|
235
|
-
},
|
|
236
|
-
});
|
|
165
|
+
AtomicIcon.displayName = 'AtomicIcon';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon Renderer Components
|
|
3
|
+
* SVG rendering and background rendering logic
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
|
8
|
+
import Svg, { Path } from 'react-native-svg';
|
|
9
|
+
import type { IconRenderProps } from '../iconStore';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Renders an SVG icon from path data
|
|
13
|
+
*
|
|
14
|
+
* @param svgPath - SVG path string
|
|
15
|
+
* @param svgViewBox - SVG viewBox attribute
|
|
16
|
+
* @param size - Icon size in pixels
|
|
17
|
+
* @param color - Icon color
|
|
18
|
+
* @param testID - Test ID for testing
|
|
19
|
+
* @param accessibilityLabel - Accessibility label
|
|
20
|
+
* @returns SVG element
|
|
21
|
+
*/
|
|
22
|
+
export function renderSvgIcon(
|
|
23
|
+
svgPath: string,
|
|
24
|
+
svgViewBox: string,
|
|
25
|
+
size: number,
|
|
26
|
+
color: string,
|
|
27
|
+
testID?: string,
|
|
28
|
+
accessibilityLabel?: string
|
|
29
|
+
): React.ReactElement {
|
|
30
|
+
return (
|
|
31
|
+
<Svg
|
|
32
|
+
viewBox={svgViewBox}
|
|
33
|
+
width={size}
|
|
34
|
+
height={size}
|
|
35
|
+
testID={testID}
|
|
36
|
+
accessibilityLabel={accessibilityLabel}
|
|
37
|
+
>
|
|
38
|
+
<Path d={svgPath} fill={color} />
|
|
39
|
+
</Svg>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Renders an icon with a circular background
|
|
45
|
+
*
|
|
46
|
+
* @param iconElement - Icon element to wrap
|
|
47
|
+
* @param size - Icon size in pixels
|
|
48
|
+
* @param backgroundColor - Background color
|
|
49
|
+
* @param style - Additional styles
|
|
50
|
+
* @param testID - Test ID for testing
|
|
51
|
+
* @param accessibilityLabel - Accessibility label
|
|
52
|
+
* @returns Wrapped icon with background
|
|
53
|
+
*/
|
|
54
|
+
export function renderWithBackground(
|
|
55
|
+
iconElement: React.ReactNode,
|
|
56
|
+
size: number,
|
|
57
|
+
backgroundColor: string,
|
|
58
|
+
style?: StyleProp<ViewStyle>,
|
|
59
|
+
testID?: string,
|
|
60
|
+
accessibilityLabel?: string
|
|
61
|
+
): React.ReactElement {
|
|
62
|
+
const containerSize = size + 16;
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<View
|
|
66
|
+
style={[
|
|
67
|
+
styles.backgroundContainer,
|
|
68
|
+
{
|
|
69
|
+
width: containerSize,
|
|
70
|
+
height: containerSize,
|
|
71
|
+
borderRadius: containerSize / 2,
|
|
72
|
+
backgroundColor,
|
|
73
|
+
},
|
|
74
|
+
style,
|
|
75
|
+
]}
|
|
76
|
+
testID={testID}
|
|
77
|
+
accessibilityLabel={accessibilityLabel}
|
|
78
|
+
>
|
|
79
|
+
{iconElement}
|
|
80
|
+
</View>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Builds render props for custom icon renderer
|
|
86
|
+
*
|
|
87
|
+
* @param name - Icon name
|
|
88
|
+
* @param size - Icon size in pixels
|
|
89
|
+
* @param color - Icon color
|
|
90
|
+
* @param style - Additional styles
|
|
91
|
+
* @param testID - Test ID
|
|
92
|
+
* @param accessibilityLabel - Accessibility label
|
|
93
|
+
* @returns Icon render props
|
|
94
|
+
*/
|
|
95
|
+
export function buildIconRenderProps(
|
|
96
|
+
name: string,
|
|
97
|
+
size: number,
|
|
98
|
+
color: string,
|
|
99
|
+
style?: StyleProp<ViewStyle>,
|
|
100
|
+
testID?: string,
|
|
101
|
+
accessibilityLabel?: string
|
|
102
|
+
): IconRenderProps {
|
|
103
|
+
return {
|
|
104
|
+
name,
|
|
105
|
+
size,
|
|
106
|
+
color,
|
|
107
|
+
style: style as StyleProp<ViewStyle>,
|
|
108
|
+
testID,
|
|
109
|
+
accessibilityLabel,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const styles = StyleSheet.create({
|
|
114
|
+
backgroundContainer: {
|
|
115
|
+
justifyContent: 'center',
|
|
116
|
+
alignItems: 'center',
|
|
117
|
+
},
|
|
118
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon Utility Functions
|
|
3
|
+
* Helper functions for icon color and size calculations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IconColor } from '../AtomicIcon.types';
|
|
7
|
+
import type { DesignTokens } from '../../../theme/types/ThemeTypes';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Maps semantic color names to actual color values from tokens
|
|
11
|
+
*
|
|
12
|
+
* @param color - Semantic color name
|
|
13
|
+
* @param tokens - Design tokens
|
|
14
|
+
* @returns Hex color string
|
|
15
|
+
*/
|
|
16
|
+
export function getSemanticColor(
|
|
17
|
+
color: IconColor,
|
|
18
|
+
tokens: DesignTokens
|
|
19
|
+
): string {
|
|
20
|
+
const colorMap: Record<IconColor, string> = {
|
|
21
|
+
primary: tokens.colors.primary,
|
|
22
|
+
secondary: tokens.colors.secondary,
|
|
23
|
+
success: tokens.colors.success,
|
|
24
|
+
warning: tokens.colors.warning,
|
|
25
|
+
error: tokens.colors.error,
|
|
26
|
+
info: tokens.colors.info,
|
|
27
|
+
onSurface: tokens.colors.onSurface,
|
|
28
|
+
surfaceVariant: tokens.colors.surfaceVariant,
|
|
29
|
+
onPrimary: tokens.colors.onPrimary,
|
|
30
|
+
onSecondary: tokens.colors.onSecondary,
|
|
31
|
+
textInverse: tokens.colors.textInverse,
|
|
32
|
+
textPrimary: tokens.colors.textPrimary,
|
|
33
|
+
textSecondary: tokens.colors.textSecondary,
|
|
34
|
+
textTertiary: tokens.colors.textTertiary,
|
|
35
|
+
onSurfaceVariant: tokens.colors.onSurfaceVariant,
|
|
36
|
+
};
|
|
37
|
+
return colorMap[color];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Calculates icon size in pixels
|
|
42
|
+
*
|
|
43
|
+
* @param size - Size preset or custom size value
|
|
44
|
+
* @param customSize - Custom size in pixels (overrides size)
|
|
45
|
+
* @param tokens - Design tokens
|
|
46
|
+
* @returns Size in pixels
|
|
47
|
+
*/
|
|
48
|
+
export function calculateIconSize(
|
|
49
|
+
size: string | number,
|
|
50
|
+
customSize: number | undefined,
|
|
51
|
+
tokens: DesignTokens
|
|
52
|
+
): number {
|
|
53
|
+
const baseSize = customSize ?? size;
|
|
54
|
+
const iconSizesMap = tokens.iconSizes as Record<string, number>;
|
|
55
|
+
|
|
56
|
+
return typeof baseSize === 'number'
|
|
57
|
+
? baseSize * tokens.spacingMultiplier
|
|
58
|
+
: iconSizesMap[baseSize] ?? iconSizesMap['md'] ?? 24;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Calculates icon color
|
|
63
|
+
*
|
|
64
|
+
* @param customColor - Custom color override
|
|
65
|
+
* @param semanticColor - Semantic color from theme
|
|
66
|
+
* @param tokens - Design tokens
|
|
67
|
+
* @returns Hex color string
|
|
68
|
+
*/
|
|
69
|
+
export function calculateIconColor(
|
|
70
|
+
customColor: string | undefined,
|
|
71
|
+
semanticColor: IconColor | undefined,
|
|
72
|
+
tokens: DesignTokens
|
|
73
|
+
): string {
|
|
74
|
+
if (customColor) {
|
|
75
|
+
return customColor;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (semanticColor) {
|
|
79
|
+
return getSemanticColor(semanticColor, tokens);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return tokens.colors.textPrimary;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Calculates container size for icon with background
|
|
87
|
+
*
|
|
88
|
+
* @param iconSize - Icon size in pixels
|
|
89
|
+
* @param padding - Padding around icon (default: 16)
|
|
90
|
+
* @returns Container size
|
|
91
|
+
*/
|
|
92
|
+
export function calculateContainerSize(iconSize: number, padding: number = 16): number {
|
|
93
|
+
return iconSize + padding;
|
|
94
|
+
}
|
|
@@ -21,17 +21,22 @@ import { ExceptionLogger } from './ExceptionLogger';
|
|
|
21
21
|
import { useExceptionStore } from '../storage/ExceptionStore';
|
|
22
22
|
|
|
23
23
|
export class ExceptionService {
|
|
24
|
-
private logger: ExceptionLogger;
|
|
25
|
-
private reporter: ExceptionReporter;
|
|
24
|
+
private logger: ExceptionLogger | null = null;
|
|
25
|
+
private reporter: ExceptionReporter | null = null;
|
|
26
|
+
private reporterConfig: ExceptionReporter['config'];
|
|
26
27
|
|
|
27
28
|
constructor(reporterConfig?: ExceptionReporter['config']) {
|
|
28
|
-
this.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
this.reporterConfig = reporterConfig || {
|
|
30
|
+
enabled: false,
|
|
31
|
+
environment: 'development'
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private ensureInitialized() {
|
|
36
|
+
if (!this.logger) {
|
|
37
|
+
this.logger = new ExceptionLogger();
|
|
38
|
+
this.reporter = new ExceptionReporter(this.reporterConfig);
|
|
39
|
+
}
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
/**
|
|
@@ -43,17 +48,19 @@ export class ExceptionService {
|
|
|
43
48
|
category: ExceptionCategory = 'unknown',
|
|
44
49
|
context: ExceptionContext = {},
|
|
45
50
|
): Promise<void> {
|
|
51
|
+
this.ensureInitialized();
|
|
52
|
+
|
|
46
53
|
const exception = ExceptionHandler.createException(error, severity, category, context);
|
|
47
54
|
|
|
48
55
|
// Add to store
|
|
49
56
|
useExceptionStore.getState().addException(exception);
|
|
50
57
|
|
|
51
58
|
// Log locally
|
|
52
|
-
await this.logger
|
|
59
|
+
await this.logger!.logException(exception);
|
|
53
60
|
|
|
54
61
|
// Report to external service if needed
|
|
55
62
|
if (ExceptionHandler.shouldReportException(exception)) {
|
|
56
|
-
await this.reporter
|
|
63
|
+
await this.reporter!.reportException(exception);
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
// Mark as handled
|
|
@@ -92,35 +99,40 @@ export class ExceptionService {
|
|
|
92
99
|
* Get stored exceptions
|
|
93
100
|
*/
|
|
94
101
|
async getStoredExceptions(): Promise<ExceptionEntity[]> {
|
|
95
|
-
|
|
102
|
+
this.ensureInitialized();
|
|
103
|
+
return this.logger!.getStoredExceptions();
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
/**
|
|
99
107
|
* Get exception statistics
|
|
100
108
|
*/
|
|
101
109
|
async getExceptionStats() {
|
|
102
|
-
|
|
110
|
+
this.ensureInitialized();
|
|
111
|
+
return this.logger!.getExceptionStats();
|
|
103
112
|
}
|
|
104
113
|
|
|
105
114
|
/**
|
|
106
115
|
* Clear stored exceptions
|
|
107
116
|
*/
|
|
108
117
|
async clearStoredExceptions(): Promise<void> {
|
|
109
|
-
|
|
118
|
+
this.ensureInitialized();
|
|
119
|
+
await this.logger!.clearStoredExceptions();
|
|
110
120
|
}
|
|
111
121
|
|
|
112
122
|
/**
|
|
113
123
|
* Update reporter configuration
|
|
114
124
|
*/
|
|
115
125
|
updateReporterConfig(config: Partial<ExceptionReporter['config']>): void {
|
|
116
|
-
this.
|
|
126
|
+
this.ensureInitialized();
|
|
127
|
+
this.reporter!.updateConfig(config);
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
/**
|
|
120
131
|
* Set max stored exceptions
|
|
121
132
|
*/
|
|
122
133
|
setMaxStoredExceptions(limit: number): void {
|
|
123
|
-
this.
|
|
134
|
+
this.ensureInitialized();
|
|
135
|
+
this.logger!.setMaxStoredExceptions(limit);
|
|
124
136
|
}
|
|
125
137
|
|
|
126
138
|
/**
|
|
@@ -145,7 +157,7 @@ export class ExceptionService {
|
|
|
145
157
|
}
|
|
146
158
|
}
|
|
147
159
|
|
|
148
|
-
// Export default instance
|
|
160
|
+
// Export default instance - lazy initialization
|
|
149
161
|
export const exceptionService = new ExceptionService();
|
|
150
162
|
|
|
151
163
|
|
|
@@ -44,7 +44,7 @@ export const ExceptionErrorState: React.FC<ExceptionErrorStateProps> = ({
|
|
|
44
44
|
const refreshIcon = useIconName('refresh');
|
|
45
45
|
const displayIcon = icon || alertCircleIcon;
|
|
46
46
|
|
|
47
|
-
const styles =
|
|
47
|
+
const styles = useMemo(
|
|
48
48
|
() =>
|
|
49
49
|
StyleSheet.create({
|
|
50
50
|
actionButton: {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
|
|
8
|
+
import type { BatchOperation } from '../types/BatchTypes';
|
|
8
9
|
import { BatchProcessor } from '../utils/BatchProcessor';
|
|
9
10
|
|
|
10
11
|
export interface BatchProcessingOptions {
|
|
@@ -27,13 +28,6 @@ export interface BatchProcessingResult {
|
|
|
27
28
|
failureCount: number;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
export interface BatchOperation {
|
|
31
|
-
uri: string;
|
|
32
|
-
type: 'resize' | 'crop' | 'filter' | 'compress' | 'convert';
|
|
33
|
-
params: Record<string, unknown>;
|
|
34
|
-
options?: Record<string, unknown>;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
31
|
export class ImageBatchService {
|
|
38
32
|
static async processBatch(
|
|
39
33
|
operations: BatchOperation[],
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch Operation Types
|
|
3
|
+
* Shared types for batch processing operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface BatchOperation {
|
|
7
|
+
uri: string;
|
|
8
|
+
type: 'resize' | 'crop' | 'filter' | 'compress' | 'convert';
|
|
9
|
+
params: Record<string, unknown>;
|
|
10
|
+
options?: Record<string, unknown>;
|
|
11
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ImageManipulationResult, ImageCropArea, SaveFormat } from '../../domain/entities/ImageTypes';
|
|
8
|
-
import type { BatchOperation } from '../
|
|
8
|
+
import type { BatchOperation } from '../types/BatchTypes';
|
|
9
9
|
import { ImageTransformService } from '../services/ImageTransformService';
|
|
10
10
|
import { ImageConversionService } from '../services/ImageConversionService';
|
|
11
11
|
import { ImageValidator } from './ImageValidator';
|
|
@@ -62,13 +62,13 @@ function InfiniteScrollListComponent<T>({
|
|
|
62
62
|
}: InfiniteScrollListProps<T>): React.ReactElement {
|
|
63
63
|
const { items, state, loadMore, refresh, canLoadMore } = useInfiniteScroll(config);
|
|
64
64
|
|
|
65
|
-
const handleEndReached =
|
|
65
|
+
const handleEndReached = useCallback(() => {
|
|
66
66
|
if (canLoadMore && config.autoLoad !== false) {
|
|
67
67
|
loadMore();
|
|
68
68
|
}
|
|
69
69
|
}, [canLoadMore, loadMore, config.autoLoad]);
|
|
70
70
|
|
|
71
|
-
const getItemKey =
|
|
71
|
+
const getItemKey = useCallback(
|
|
72
72
|
(item: T, index: number): string => {
|
|
73
73
|
if (config.getItemKey) {
|
|
74
74
|
return config.getItemKey(item, index);
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - Fully configurable for general purpose use
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import React from 'react';
|
|
15
|
+
import React, { useState, useEffect, useCallback, useMemo, useRef, useContext } from 'react';
|
|
16
16
|
import { View, TouchableOpacity, ViewStyle } from 'react-native';
|
|
17
17
|
import { AtomicIcon, AtomicText } from '../../atoms';
|
|
18
18
|
import { useAppDesignTokens } from '../../theme';
|
|
@@ -71,7 +71,7 @@ const ScreenHeaderBackButton: React.FC<{
|
|
|
71
71
|
backIconColor?: 'primary' | 'secondary' | 'error' | 'warning' | 'success' | 'surfaceVariant';
|
|
72
72
|
testID?: string;
|
|
73
73
|
}> = ({ hideBackButton, onBackPress, backIconName, backIconColor, testID }) => {
|
|
74
|
-
const handleBackPress =
|
|
74
|
+
const handleBackPress = useCallback(() => {
|
|
75
75
|
if (onBackPress) {
|
|
76
76
|
onBackPress();
|
|
77
77
|
}
|
|
@@ -136,7 +136,7 @@ export const ScreenHeader: React.FC<ScreenHeaderProps> = ({
|
|
|
136
136
|
}) => {
|
|
137
137
|
const tokens = useAppDesignTokens();
|
|
138
138
|
|
|
139
|
-
const headerStyle =
|
|
139
|
+
const headerStyle = useMemo(() => [
|
|
140
140
|
{
|
|
141
141
|
flexDirection: 'row' as const,
|
|
142
142
|
alignItems: 'center' as const,
|