@umituz/react-native-design-system 2.6.107 → 2.6.111

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 (35) hide show
  1. package/package.json +2 -2
  2. package/src/exception/domain/entities/ExceptionEntity.ts +115 -0
  3. package/src/exception/domain/repositories/IExceptionRepository.ts +37 -0
  4. package/src/exception/index.ts +65 -0
  5. package/src/exception/infrastructure/services/ExceptionHandler.ts +93 -0
  6. package/src/exception/infrastructure/services/ExceptionLogger.ts +142 -0
  7. package/src/exception/infrastructure/services/ExceptionReporter.ts +134 -0
  8. package/src/exception/infrastructure/services/ExceptionService.ts +154 -0
  9. package/src/exception/infrastructure/storage/ExceptionStore.ts +44 -0
  10. package/src/exception/presentation/components/ErrorBoundary.tsx +129 -0
  11. package/src/exception/presentation/components/ExceptionEmptyState.tsx +123 -0
  12. package/src/exception/presentation/components/ExceptionErrorState.tsx +118 -0
  13. package/src/exports/exception.ts +7 -0
  14. package/src/exports/infinite-scroll.ts +7 -0
  15. package/src/exports/uuid.ts +7 -0
  16. package/src/index.ts +15 -0
  17. package/src/infinite-scroll/domain/interfaces/infinite-scroll-list-props.ts +67 -0
  18. package/src/infinite-scroll/domain/types/infinite-scroll-config.ts +108 -0
  19. package/src/infinite-scroll/domain/types/infinite-scroll-return.ts +40 -0
  20. package/src/infinite-scroll/domain/types/infinite-scroll-state.ts +58 -0
  21. package/src/infinite-scroll/domain/utils/pagination-utils.ts +63 -0
  22. package/src/infinite-scroll/domain/utils/type-guards.ts +53 -0
  23. package/src/infinite-scroll/index.ts +62 -0
  24. package/src/infinite-scroll/presentation/components/empty.tsx +44 -0
  25. package/src/infinite-scroll/presentation/components/error.tsx +66 -0
  26. package/src/infinite-scroll/presentation/components/infinite-scroll-list.tsx +120 -0
  27. package/src/infinite-scroll/presentation/components/loading-more.tsx +38 -0
  28. package/src/infinite-scroll/presentation/components/loading.tsx +40 -0
  29. package/src/infinite-scroll/presentation/components/types.ts +124 -0
  30. package/src/infinite-scroll/presentation/hooks/pagination.helper.ts +83 -0
  31. package/src/infinite-scroll/presentation/hooks/useInfiniteScroll.ts +327 -0
  32. package/src/uuid/index.ts +15 -0
  33. package/src/uuid/infrastructure/utils/UUIDUtils.ts +75 -0
  34. package/src/uuid/package-lock.json +14255 -0
  35. package/src/uuid/types/UUID.ts +36 -0
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Exception Service - Infrastructure Layer
3
+ *
4
+ * Facade for exception handling using composition.
5
+ * Delegates to specialized services for specific operations.
6
+ *
7
+ * SOLID: Facade pattern - Single entry point, delegates to specialists
8
+ * DRY: Avoids code duplication by composing smaller services
9
+ * KISS: Simple interface, complex operations delegated
10
+ */
11
+
12
+ import type {
13
+ ExceptionEntity,
14
+ ExceptionContext,
15
+ ExceptionSeverity,
16
+ ExceptionCategory,
17
+ } from '../../domain/entities/ExceptionEntity';
18
+ import { ExceptionHandler } from './ExceptionHandler';
19
+ import { ExceptionReporter } from './ExceptionReporter';
20
+ import { ExceptionLogger } from './ExceptionLogger';
21
+ import { useExceptionStore } from '../storage/ExceptionStore';
22
+
23
+ export class ExceptionService {
24
+ private logger: ExceptionLogger;
25
+ private reporter: ExceptionReporter;
26
+
27
+ constructor(reporterConfig?: ExceptionReporter['config']) {
28
+ this.logger = new ExceptionLogger();
29
+ this.reporter = new ExceptionReporter(
30
+ reporterConfig || {
31
+ enabled: false,
32
+ environment: 'development'
33
+ }
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Handle an exception
39
+ */
40
+ async handleException(
41
+ error: Error,
42
+ severity: ExceptionSeverity = 'error',
43
+ category: ExceptionCategory = 'unknown',
44
+ context: ExceptionContext = {},
45
+ ): Promise<void> {
46
+ const exception = ExceptionHandler.createException(error, severity, category, context);
47
+
48
+ // Add to store
49
+ useExceptionStore.getState().addException(exception);
50
+
51
+ // Log locally
52
+ await this.logger.logException(exception);
53
+
54
+ // Report to external service if needed
55
+ if (ExceptionHandler.shouldReportException(exception)) {
56
+ await this.reporter.reportException(exception);
57
+ }
58
+
59
+ // Mark as handled
60
+ useExceptionStore.getState().markAsHandled(exception.id);
61
+ }
62
+
63
+ /**
64
+ * Handle network errors
65
+ */
66
+ async handleNetworkError(error: Error, context: ExceptionContext = {}): Promise<void> {
67
+ await this.handleException(error, 'error', 'network', context);
68
+ }
69
+
70
+ /**
71
+ * Handle validation errors
72
+ */
73
+ async handleValidationError(error: Error, context: ExceptionContext = {}): Promise<void> {
74
+ await this.handleException(error, 'warning', 'validation', context);
75
+ }
76
+
77
+ /**
78
+ * Handle authentication errors
79
+ */
80
+ async handleAuthError(error: Error, context: ExceptionContext = {}): Promise<void> {
81
+ await this.handleException(error, 'error', 'authentication', context);
82
+ }
83
+
84
+ /**
85
+ * Handle system errors
86
+ */
87
+ async handleSystemError(error: Error, context: ExceptionContext = {}): Promise<void> {
88
+ await this.handleException(error, 'fatal', 'system', context);
89
+ }
90
+
91
+ /**
92
+ * Get stored exceptions
93
+ */
94
+ async getStoredExceptions(): Promise<ExceptionEntity[]> {
95
+ return this.logger.getStoredExceptions();
96
+ }
97
+
98
+ /**
99
+ * Get exception statistics
100
+ */
101
+ async getExceptionStats() {
102
+ return this.logger.getExceptionStats();
103
+ }
104
+
105
+ /**
106
+ * Clear stored exceptions
107
+ */
108
+ async clearStoredExceptions(): Promise<void> {
109
+ await this.logger.clearStoredExceptions();
110
+ }
111
+
112
+ /**
113
+ * Update reporter configuration
114
+ */
115
+ updateReporterConfig(config: Partial<ExceptionReporter['config']>): void {
116
+ this.reporter.updateConfig(config);
117
+ }
118
+
119
+ /**
120
+ * Set max stored exceptions
121
+ */
122
+ setMaxStoredExceptions(limit: number): void {
123
+ this.logger.setMaxStoredExceptions(limit);
124
+ }
125
+
126
+ /**
127
+ * Handle storage/permission errors
128
+ */
129
+ async handleStorageError(error: Error, context: ExceptionContext = {}): Promise<void> {
130
+ await this.handleException(error, 'error', 'storage', context);
131
+ }
132
+
133
+ /**
134
+ * Handle fatal errors
135
+ */
136
+ async handleFatalError(error: Error, context: ExceptionContext = {}): Promise<void> {
137
+ await this.handleException(error, 'fatal', 'system', context);
138
+ }
139
+
140
+ /**
141
+ * Clear all exceptions
142
+ */
143
+ clearExceptions(): void {
144
+ useExceptionStore.getState().clearExceptions();
145
+ }
146
+ }
147
+
148
+ // Export default instance
149
+ export const exceptionService = new ExceptionService();
150
+
151
+
152
+
153
+
154
+
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Exception Store
3
+ * Zustand store for exception state management
4
+ */
5
+
6
+ import { createStore } from '@umituz/react-native-storage';
7
+ import type { ExceptionEntity } from '../../domain/entities/ExceptionEntity';
8
+
9
+ interface ExceptionState {
10
+ exceptions: ExceptionEntity[];
11
+ }
12
+
13
+ interface ExceptionActions {
14
+ addException: (exception: ExceptionEntity) => void;
15
+ clearExceptions: () => void;
16
+ markAsHandled: (id: string) => void;
17
+ }
18
+
19
+ export const useExceptionStore = createStore<ExceptionState, ExceptionActions>({
20
+ name: 'exception-store',
21
+ initialState: {
22
+ exceptions: [],
23
+ },
24
+ persist: false,
25
+ actions: (set: (state: Partial<ExceptionState>) => void, get: () => ExceptionState) => ({
26
+ addException: (exception: ExceptionEntity) =>
27
+ set({ exceptions: [...get().exceptions, exception] }),
28
+ clearExceptions: () => set({ exceptions: [] }),
29
+ markAsHandled: (id: string) =>
30
+ set({
31
+ exceptions: get().exceptions.map((e: ExceptionEntity) =>
32
+ e.id === id ? { ...e, handled: true } : e
33
+ ),
34
+ }),
35
+ }),
36
+ });
37
+
38
+ /**
39
+ * Hook to get exceptions from store
40
+ */
41
+ export const useExceptions = () => {
42
+ const exceptions = useExceptionStore((state: ExceptionState) => state.exceptions);
43
+ return exceptions;
44
+ };
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Error Boundary Component
3
+ * Catches React errors and provides fallback UI
4
+ */
5
+
6
+ import React, { Component, ReactNode } from 'react';
7
+ import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
8
+ import { exceptionService } from '../../infrastructure/services/ExceptionService';
9
+ import { useAppDesignTokens } from '../../../theme';
10
+
11
+ interface Props {
12
+ children: ReactNode;
13
+ fallback?: ReactNode;
14
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
15
+ }
16
+
17
+ interface State {
18
+ hasError: boolean;
19
+ error: Error | null;
20
+ }
21
+
22
+ export class ErrorBoundary extends Component<Props, State> {
23
+ constructor(props: Props) {
24
+ super(props);
25
+ this.state = {
26
+ hasError: false,
27
+ error: null,
28
+ };
29
+ }
30
+
31
+ static getDerivedStateFromError(error: Error): State {
32
+ return {
33
+ hasError: true,
34
+ error,
35
+ };
36
+ }
37
+
38
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
39
+ // Log error to exception service
40
+ exceptionService.handleFatalError(error, {
41
+ componentStack: errorInfo.componentStack ?? undefined,
42
+ screen: 'ErrorBoundary',
43
+ });
44
+
45
+ // Call external onError if provided
46
+ if (this.props.onError) {
47
+ this.props.onError(error, errorInfo);
48
+ }
49
+ }
50
+
51
+ handleReset = (): void => {
52
+ this.setState({
53
+ hasError: false,
54
+ error: null,
55
+ });
56
+ };
57
+
58
+ render(): ReactNode {
59
+ if (this.state.hasError) {
60
+ if (this.props.fallback) {
61
+ return this.props.fallback;
62
+ }
63
+
64
+ return (
65
+ <ErrorDisplay error={this.state.error} onReset={this.handleReset} />
66
+ );
67
+ }
68
+
69
+ return this.props.children;
70
+ }
71
+ }
72
+
73
+ interface ErrorDisplayProps {
74
+ error: Error | null;
75
+ onReset: () => void;
76
+ }
77
+
78
+ const ErrorDisplay: React.FC<ErrorDisplayProps> = ({ error, onReset }) => {
79
+ const tokens = useAppDesignTokens();
80
+ const styles = getStyles(tokens);
81
+
82
+ return (
83
+ <View style={styles.container}>
84
+ <Text style={styles.title}>Something went wrong</Text>
85
+ <Text style={styles.message}>
86
+ {error?.message || 'An unexpected error occurred'}
87
+ </Text>
88
+ <TouchableOpacity style={styles.button} onPress={onReset}>
89
+ <Text style={styles.buttonText}>Try Again</Text>
90
+ </TouchableOpacity>
91
+ </View>
92
+ );
93
+ };
94
+
95
+ const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
96
+ StyleSheet.create({
97
+ button: {
98
+ backgroundColor: tokens.colors.primary,
99
+ borderRadius: tokens.borders.radius.sm,
100
+ paddingHorizontal: tokens.spacing.lg,
101
+ paddingVertical: tokens.spacing.sm,
102
+ },
103
+ buttonText: {
104
+ color: tokens.colors.textInverse,
105
+ fontSize: tokens.typography.bodyLarge.fontSize,
106
+ fontWeight: tokens.typography.labelLarge.fontWeight,
107
+ },
108
+ container: {
109
+ alignItems: 'center',
110
+ backgroundColor: tokens.colors.backgroundPrimary,
111
+ flex: 1,
112
+ justifyContent: 'center',
113
+ padding: tokens.spacing.lg,
114
+ },
115
+ message: {
116
+ color: tokens.colors.textSecondary,
117
+ fontSize: tokens.typography.bodyLarge.fontSize,
118
+ marginBottom: tokens.spacing.lg,
119
+ textAlign: 'center',
120
+ },
121
+ title: {
122
+ color: tokens.colors.textPrimary,
123
+ fontSize: tokens.typography.headlineSmall.fontSize,
124
+ fontWeight: tokens.typography.headlineSmall.fontWeight,
125
+ marginBottom: tokens.spacing.sm,
126
+ },
127
+ });
128
+
129
+
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Empty State Component
3
+ * Displays when no data is available
4
+ *
5
+ * Presentation Layer - UI Component
6
+ */
7
+
8
+ import React from "react";
9
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
10
+ import { AtomicIcon, AtomicText } from "../../../atoms";
11
+ import { useAppDesignTokens } from "../../../theme";
12
+
13
+ export interface ExceptionEmptyStateProps {
14
+ icon?: string;
15
+ title: string;
16
+ description?: string;
17
+ actionLabel?: string;
18
+ onAction?: () => void;
19
+ illustration?: React.ReactNode;
20
+ }
21
+
22
+ export const ExceptionEmptyState: React.FC<ExceptionEmptyStateProps> = ({
23
+ icon = "inbox",
24
+ title,
25
+ description,
26
+ actionLabel,
27
+ onAction,
28
+ illustration,
29
+ }) => {
30
+ const tokens = useAppDesignTokens();
31
+
32
+ const styles = React.useMemo(
33
+ () =>
34
+ StyleSheet.create({
35
+ actionButton: {
36
+ borderRadius: tokens.borders.radius.md,
37
+ marginTop: tokens.spacing.sm,
38
+ paddingHorizontal: tokens.spacing.lg,
39
+ paddingVertical: tokens.spacing.md,
40
+ },
41
+ actionButtonText: {
42
+ // AtomicText handles typography
43
+ },
44
+ container: {
45
+ alignItems: "center",
46
+ flex: 1,
47
+ justifyContent: "center",
48
+ padding: tokens.spacing.xl,
49
+ },
50
+ description: {
51
+ marginBottom: tokens.spacing.lg,
52
+ maxWidth: 280,
53
+ },
54
+ iconContainer: {
55
+ alignItems: "center",
56
+ borderRadius: 60,
57
+ height: 120,
58
+ justifyContent: "center",
59
+ marginBottom: tokens.spacing.lg,
60
+ width: 120,
61
+ },
62
+ title: {
63
+ marginBottom: tokens.spacing.sm,
64
+ },
65
+ }),
66
+ [tokens],
67
+ );
68
+
69
+ return (
70
+ <View style={styles.container}>
71
+ {illustration ? (
72
+ illustration
73
+ ) : (
74
+ <View
75
+ style={[
76
+ styles.iconContainer,
77
+ { backgroundColor: tokens.colors.surface },
78
+ ]}
79
+ >
80
+ <AtomicIcon name={icon} size="xxl" color="secondary" />
81
+ </View>
82
+ )}
83
+
84
+ <AtomicText
85
+ type="headlineSmall"
86
+ color="primary"
87
+ style={[styles.title, { textAlign: "center" }]}
88
+ >
89
+ {title}
90
+ </AtomicText>
91
+
92
+ {description && (
93
+ <AtomicText
94
+ type="bodyMedium"
95
+ color="secondary"
96
+ style={[styles.description, { textAlign: "center" }]}
97
+ >
98
+ {description}
99
+ </AtomicText>
100
+ )}
101
+
102
+ {actionLabel && onAction && (
103
+ <TouchableOpacity
104
+ style={[
105
+ styles.actionButton,
106
+ { backgroundColor: tokens.colors.primary },
107
+ ]}
108
+ onPress={onAction}
109
+ activeOpacity={0.8}
110
+ >
111
+ <AtomicText
112
+ type="labelLarge"
113
+ color="onPrimary"
114
+ style={styles.actionButtonText}
115
+ >
116
+ {actionLabel}
117
+ </AtomicText>
118
+ </TouchableOpacity>
119
+ )}
120
+ </View>
121
+ );
122
+ };
123
+
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Error State Component
3
+ * Generic error display with retry action
4
+ *
5
+ * Presentation Layer - UI Component
6
+ */
7
+
8
+ import React from "react";
9
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
10
+ import {
11
+ AtomicIcon,
12
+ AtomicText,
13
+ } from "../../../atoms";
14
+ import {
15
+ useAppDesignTokens,
16
+ } from "../../../theme";
17
+
18
+ export interface ExceptionErrorStateProps {
19
+ /** Icon name from Ionicons */
20
+ icon?: string;
21
+ /** Error title */
22
+ title: string;
23
+ /** Error description */
24
+ description?: string;
25
+ /** Retry button label */
26
+ actionLabel?: string;
27
+ /** Retry action callback */
28
+ onAction?: () => void;
29
+ /** Custom illustration instead of icon */
30
+ illustration?: React.ReactNode;
31
+ }
32
+
33
+ export const ExceptionErrorState: React.FC<ExceptionErrorStateProps> = ({
34
+ icon = "alert-circle-outline",
35
+ title,
36
+ description,
37
+ actionLabel,
38
+ onAction,
39
+ illustration,
40
+ }) => {
41
+ const tokens = useAppDesignTokens();
42
+
43
+ const styles = React.useMemo(
44
+ () =>
45
+ StyleSheet.create({
46
+ actionButton: {
47
+ alignItems: "center",
48
+ borderRadius: tokens.borders.radius.full,
49
+ flexDirection: "row",
50
+ gap: tokens.spacing.sm,
51
+ marginTop: tokens.spacing.sm,
52
+ paddingHorizontal: tokens.spacing.lg,
53
+ paddingVertical: tokens.spacing.md,
54
+ },
55
+ container: {
56
+ alignItems: "center",
57
+ flex: 1,
58
+ justifyContent: "center",
59
+ padding: tokens.spacing.xl,
60
+ },
61
+ description: {
62
+ marginBottom: tokens.spacing.lg,
63
+ maxWidth: 280,
64
+ textAlign: "center",
65
+ },
66
+ iconContainer: {
67
+ alignItems: "center",
68
+ borderRadius: 60,
69
+ height: 120,
70
+ justifyContent: "center",
71
+ marginBottom: tokens.spacing.lg,
72
+ width: 120,
73
+ },
74
+ title: {
75
+ marginBottom: tokens.spacing.sm,
76
+ textAlign: "center",
77
+ },
78
+ }),
79
+ [tokens],
80
+ );
81
+
82
+ return (
83
+ <View style={styles.container}>
84
+ {illustration ? (
85
+ illustration
86
+ ) : (
87
+ <View
88
+ style={[styles.iconContainer, { backgroundColor: tokens.colors.surface }]}
89
+ >
90
+ <AtomicIcon name={icon as never} size="xxl" color="secondary" />
91
+ </View>
92
+ )}
93
+
94
+ <AtomicText type="headlineSmall" color="primary" style={styles.title}>
95
+ {title}
96
+ </AtomicText>
97
+
98
+ {description && (
99
+ <AtomicText type="bodyMedium" color="secondary" style={styles.description}>
100
+ {description}
101
+ </AtomicText>
102
+ )}
103
+
104
+ {actionLabel && onAction && (
105
+ <TouchableOpacity
106
+ style={[styles.actionButton, { backgroundColor: tokens.colors.primary }]}
107
+ onPress={onAction}
108
+ activeOpacity={0.8}
109
+ >
110
+ <AtomicIcon name="refresh-outline" size="sm" color="onPrimary" />
111
+ <AtomicText type="labelLarge" color="onPrimary">
112
+ {actionLabel}
113
+ </AtomicText>
114
+ </TouchableOpacity>
115
+ )}
116
+ </View>
117
+ );
118
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Exception Exports
3
+ *
4
+ * Exception handling and error tracking for React Native apps
5
+ */
6
+
7
+ export * from '../exception';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Infinite Scroll Exports
3
+ *
4
+ * Modern infinite scroll system for React Native
5
+ */
6
+
7
+ export * from '../infinite-scroll';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * UUID Exports
3
+ *
4
+ * UUID generation utilities for React Native
5
+ */
6
+
7
+ export * from '../uuid';
package/src/index.ts CHANGED
@@ -57,6 +57,21 @@ export * from './exports/organisms';
57
57
  // =============================================================================
58
58
  export * from './exports/safe-area';
59
59
 
60
+ // =============================================================================
61
+ // EXCEPTION EXPORTS
62
+ // =============================================================================
63
+ export * from './exports/exception';
64
+
65
+ // =============================================================================
66
+ // INFINITE SCROLL EXPORTS
67
+ // =============================================================================
68
+ export * from './exports/infinite-scroll';
69
+
70
+ // =============================================================================
71
+ // UUID EXPORTS
72
+ // =============================================================================
73
+ export * from './exports/uuid';
74
+
60
75
  // =============================================================================
61
76
  // VARIANT UTILITIES
62
77
  // =============================================================================
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Infinite Scroll List Props Interface
3
+ *
4
+ * Domain interface for component props
5
+ * Follows SOLID, DRY, KISS principles
6
+ */
7
+
8
+ import type React from "react";
9
+ import type { InfiniteScrollConfig } from "../types/infinite-scroll-config";
10
+
11
+ export interface InfiniteScrollListProps<T> {
12
+ /**
13
+ * Configuration for infinite scroll
14
+ */
15
+ config: InfiniteScrollConfig<T>;
16
+
17
+ /**
18
+ * Render function for each item
19
+ */
20
+ renderItem: (item: T, index: number) => React.ReactElement;
21
+
22
+ /**
23
+ * Optional: Custom loading component
24
+ */
25
+ loadingComponent?: React.ReactElement;
26
+
27
+ /**
28
+ * Optional: Custom loading more component
29
+ */
30
+ loadingMoreComponent?: React.ReactElement;
31
+
32
+ /**
33
+ * Optional: Custom empty component
34
+ */
35
+ emptyComponent?: React.ReactElement;
36
+
37
+ /**
38
+ * Optional: Custom error component
39
+ */
40
+ errorComponent?: (error: string, retry: () => void) => React.ReactElement;
41
+
42
+ /**
43
+ * Optional: List header component
44
+ */
45
+ ListHeaderComponent?: React.ReactElement;
46
+
47
+ /**
48
+ * Optional: List footer component
49
+ */
50
+ ListFooterComponent?: React.ReactElement;
51
+
52
+ /**
53
+ * Optional: Additional FlatList props
54
+ */
55
+ flatListProps?: Omit<
56
+ React.ComponentProps<typeof import("react-native").FlatList<T>>,
57
+ | "data"
58
+ | "renderItem"
59
+ | "keyExtractor"
60
+ | "onEndReached"
61
+ | "onEndReachedThreshold"
62
+ | "onRefresh"
63
+ | "refreshing"
64
+ | "ListHeaderComponent"
65
+ | "ListFooterComponent"
66
+ >;
67
+ }