@umituz/react-native-loading 1.1.1 → 1.2.1

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/README.md CHANGED
File without changes
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@umituz/react-native-loading",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "Loading states and animations for React Native apps with breathing animations, skeleton loaders, and state management hooks",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
7
7
  "scripts": {
8
- "build": "tsc",
9
8
  "typecheck": "tsc --noEmit",
10
9
  "lint": "tsc --noEmit",
11
10
  "clean": "rm -rf lib",
@@ -1,18 +1,12 @@
1
1
  /**
2
2
  * Loading Domain - Entity Definitions
3
3
  *
4
- * Core types and interfaces for loading states and animations.
5
- * Provides consistent loading UX across all apps.
4
+ * Core types and configurations for loading states.
6
5
  *
7
6
  * @domain loading
8
7
  * @layer domain/entities
9
8
  */
10
9
 
11
- /**
12
- * Loading animation type
13
- */
14
- export type LoadingType = 'pulse' | 'spinner' | 'dots' | 'skeleton';
15
-
16
10
  /**
17
11
  * Loading size preset
18
12
  */
@@ -23,22 +17,6 @@ export type LoadingSize = 'small' | 'medium' | 'large';
23
17
  */
24
18
  export type SkeletonPattern = 'list' | 'card' | 'profile' | 'text' | 'custom';
25
19
 
26
- /**
27
- * Loading configuration
28
- */
29
- export interface LoadingConfig {
30
- /** Animation type */
31
- type: LoadingType;
32
- /** Size preset */
33
- size: LoadingSize;
34
- /** Loading emoji (customizable per app) */
35
- emoji?: string;
36
- /** Loading message */
37
- message?: string;
38
- /** Full screen mode */
39
- fullScreen?: boolean;
40
- }
41
-
42
20
  /**
43
21
  * Size configuration for each preset
44
22
  */
@@ -58,15 +36,6 @@ export interface SkeletonConfig {
58
36
  marginBottom?: number;
59
37
  }
60
38
 
61
- /**
62
- * Animation timing configuration
63
- */
64
- export interface AnimationConfig {
65
- duration: number;
66
- toValue: number;
67
- easing?: 'linear' | 'ease' | 'easeIn' | 'easeOut' | 'easeInOut';
68
- }
69
-
70
39
  /**
71
40
  * Size configurations for loading states
72
41
  */
@@ -88,61 +57,6 @@ export const SIZE_CONFIGS: Record<LoadingSize, SizeConfig> = {
88
57
  },
89
58
  };
90
59
 
91
- /**
92
- * App-specific emoji presets
93
- * Apps can override the default emoji based on their theme
94
- */
95
- export const LOADING_EMOJIS = {
96
- meditation: '🧘',
97
- fitness: '💪',
98
- workout: '🏋️',
99
- running: '🏃',
100
- cycling: '🚴',
101
- yoga: '🧘‍♀️',
102
- health: '🏥',
103
- nutrition: '🥗',
104
- productivity: '⏳',
105
- education: '📚',
106
- reading: '📖',
107
- music: '🎵',
108
- art: '🎨',
109
- travel: '✈️',
110
- finance: '💰',
111
- shopping: '🛍️',
112
- cooking: '👨‍🍳',
113
- gaming: '🎮',
114
- video: '🎬',
115
- social: '📱',
116
- chat: '💬',
117
- default: '⌛',
118
- } as const;
119
-
120
- /**
121
- * Animation configurations
122
- */
123
- export const ANIMATION_CONFIGS = {
124
- pulse: {
125
- duration: 1000,
126
- toValue: 1.15,
127
- easing: 'easeInOut' as const,
128
- },
129
- spinner: {
130
- duration: 1000,
131
- toValue: 360,
132
- easing: 'linear' as const,
133
- },
134
- dots: {
135
- duration: 500,
136
- toValue: 1,
137
- easing: 'easeInOut' as const,
138
- },
139
- skeleton: {
140
- duration: 1200,
141
- toValue: 1,
142
- easing: 'linear' as const,
143
- },
144
- } as const;
145
-
146
60
  /**
147
61
  * Skeleton pattern configurations
148
62
  */
@@ -169,139 +83,6 @@ export const SKELETON_PATTERNS: Record<SkeletonPattern, SkeletonConfig[]> = {
169
83
  };
170
84
 
171
85
  /**
172
- * Loading utility class
173
- */
174
- export class LoadingUtils {
175
- /**
176
- * Get emoji for app category
177
- */
178
- static getEmojiForCategory(category: string): string {
179
- const normalizedCategory = category.toLowerCase();
180
-
181
- if (normalizedCategory.includes('meditation') || normalizedCategory.includes('mindfulness')) {
182
- return LOADING_EMOJIS.meditation;
183
- }
184
- if (normalizedCategory.includes('fitness') || normalizedCategory.includes('gym')) {
185
- return LOADING_EMOJIS.fitness;
186
- }
187
- if (normalizedCategory.includes('workout')) {
188
- return LOADING_EMOJIS.workout;
189
- }
190
- if (normalizedCategory.includes('running') || normalizedCategory.includes('run')) {
191
- return LOADING_EMOJIS.running;
192
- }
193
- if (normalizedCategory.includes('cycling') || normalizedCategory.includes('bike')) {
194
- return LOADING_EMOJIS.cycling;
195
- }
196
- if (normalizedCategory.includes('yoga')) {
197
- return LOADING_EMOJIS.yoga;
198
- }
199
- if (normalizedCategory.includes('health') || normalizedCategory.includes('medical')) {
200
- return LOADING_EMOJIS.health;
201
- }
202
- if (normalizedCategory.includes('nutrition') || normalizedCategory.includes('diet')) {
203
- return LOADING_EMOJIS.nutrition;
204
- }
205
- if (normalizedCategory.includes('productivity') || normalizedCategory.includes('task')) {
206
- return LOADING_EMOJIS.productivity;
207
- }
208
- if (normalizedCategory.includes('education') || normalizedCategory.includes('learn')) {
209
- return LOADING_EMOJIS.education;
210
- }
211
- if (normalizedCategory.includes('reading') || normalizedCategory.includes('book')) {
212
- return LOADING_EMOJIS.reading;
213
- }
214
- if (normalizedCategory.includes('music') || normalizedCategory.includes('audio')) {
215
- return LOADING_EMOJIS.music;
216
- }
217
- if (normalizedCategory.includes('art') || normalizedCategory.includes('creative')) {
218
- return LOADING_EMOJIS.art;
219
- }
220
- if (normalizedCategory.includes('travel') || normalizedCategory.includes('trip')) {
221
- return LOADING_EMOJIS.travel;
222
- }
223
- if (normalizedCategory.includes('finance') || normalizedCategory.includes('money')) {
224
- return LOADING_EMOJIS.finance;
225
- }
226
- if (normalizedCategory.includes('shopping') || normalizedCategory.includes('shop')) {
227
- return LOADING_EMOJIS.shopping;
228
- }
229
- if (normalizedCategory.includes('cooking') || normalizedCategory.includes('recipe')) {
230
- return LOADING_EMOJIS.cooking;
231
- }
232
- if (normalizedCategory.includes('gaming') || normalizedCategory.includes('game')) {
233
- return LOADING_EMOJIS.gaming;
234
- }
235
- if (normalizedCategory.includes('video') || normalizedCategory.includes('film') || normalizedCategory.includes('movie')) {
236
- return LOADING_EMOJIS.video;
237
- }
238
- if (normalizedCategory.includes('social') || normalizedCategory.includes('feed')) {
239
- return LOADING_EMOJIS.social;
240
- }
241
- if (normalizedCategory.includes('chat') || normalizedCategory.includes('message')) {
242
- return LOADING_EMOJIS.chat;
243
- }
244
-
245
- return LOADING_EMOJIS.default;
246
- }
247
-
248
- /**
249
- * Get default loading config
250
- */
251
- static getDefaultConfig(overrides?: Partial<LoadingConfig>): LoadingConfig {
252
- return {
253
- type: 'pulse',
254
- size: 'large',
255
- emoji: LOADING_EMOJIS.default,
256
- fullScreen: false,
257
- ...overrides,
258
- };
259
- }
260
-
261
- /**
262
- * Get size config
263
- */
264
- static getSizeConfig(size: LoadingSize): SizeConfig {
265
- return SIZE_CONFIGS[size];
266
- }
267
-
268
- /**
269
- * Get animation config
270
- */
271
- static getAnimationConfig(type: LoadingType): AnimationConfig {
272
- return ANIMATION_CONFIGS[type];
273
- }
274
-
275
- /**
276
- * Get skeleton pattern
277
- */
278
- static getSkeletonPattern(pattern: SkeletonPattern): SkeletonConfig[] {
279
- return SKELETON_PATTERNS[pattern];
280
- }
281
-
282
- /**
283
- * Validate loading config
284
- */
285
- static validateConfig(config: Partial<LoadingConfig>): LoadingConfig {
286
- return {
287
- type: config.type || 'pulse',
288
- size: config.size || 'large',
289
- emoji: config.emoji || LOADING_EMOJIS.default,
290
- message: config.message,
291
- fullScreen: config.fullScreen ?? false,
292
- };
293
- }
294
- }
295
-
296
- /**
297
- * Loading constants
86
+ * Default emoji for loading state
298
87
  */
299
- export const LOADING_CONSTANTS = {
300
- DEFAULT_TYPE: 'pulse' as LoadingType,
301
- DEFAULT_SIZE: 'large' as LoadingSize,
302
- DEFAULT_EMOJI: LOADING_EMOJIS.default,
303
- BREATHING_CYCLE_DURATION: 2000, // 2 seconds (inhale + exhale)
304
- SPINNER_ROTATION_DURATION: 1000, // 1 second
305
- DOTS_WAVE_DURATION: 1500, // 1.5 seconds
306
- SKELETON_SHIMMER_DURATION: 1200, // 1.2 seconds
307
- } as const;
88
+ export const DEFAULT_LOADING_EMOJI = '⏳';
package/src/index.ts CHANGED
@@ -1,101 +1,35 @@
1
1
  /**
2
- * Loading Domain - Barrel Export
2
+ * @umituz/react-native-loading
3
3
  *
4
- * Public API for the loading domain.
5
- * Provides consistent loading states and animations across all apps.
6
- *
7
- * Features:
8
- * - Breathing animation loading state (meditation-inspired)
9
- * - Skeleton loaders with shimmer effect
10
- * - Loading state management hooks
11
- * - App-specific emoji presets
12
- * - Configurable sizes and patterns
4
+ * Loading states and skeleton loaders for React Native apps.
13
5
  *
14
6
  * Usage:
15
7
  * ```tsx
16
- * import {
17
- * LoadingState,
18
- * SkeletonLoader,
19
- * useLoading,
20
- * LOADING_EMOJIS,
21
- * } from '@umituz/react-native-loading';
22
- *
23
- * // Basic loading state
24
- * const MyScreen = () => {
25
- * const { isLoading, startLoading, stopLoading } = useLoading();
26
- *
27
- * return (
28
- * <View>
29
- * {isLoading ? (
30
- * <LoadingState message="Loading..." />
31
- * ) : (
32
- * <Content />
33
- * )}
34
- * </View>
35
- * );
36
- * };
37
- *
38
- * // Skeleton loader for lists
39
- * const ListScreen = () => {
40
- * const [data, setData] = useState([]);
41
- * const { isLoading } = useLoading();
42
- *
43
- * return (
44
- * <View>
45
- * {isLoading ? (
46
- * <SkeletonLoader pattern="list" count={5} />
47
- * ) : (
48
- * <FlatList data={data} ... />
49
- * )}
50
- * </View>
51
- * );
52
- * };
53
- *
54
- * // With async wrapper
55
- * const DataScreen = () => {
56
- * const { isLoading, loadingMessage, withLoading } = useLoading();
8
+ * import { LoadingState, SkeletonLoader, useLoading } from '@umituz/react-native-loading';
57
9
  *
58
- * const loadData = () => withLoading(
59
- * fetchData(),
60
- * 'Loading data...'
61
- * );
10
+ * // Full screen loading with custom emoji
11
+ * <LoadingState emoji="🎬" fullScreen />
62
12
  *
63
- * return (
64
- * <View>
65
- * {isLoading && <LoadingState message={loadingMessage} />}
66
- * <Button onPress={loadData}>Load</Button>
67
- * </View>
68
- * );
69
- * };
13
+ * // With message
14
+ * <LoadingState emoji="⏳" message="Loading..." />
70
15
  *
71
- * // Custom emoji per app
72
- * const FitnessLoadingScreen = () => (
73
- * <LoadingState
74
- * emoji={LOADING_EMOJIS.fitness}
75
- * message="Loading workouts..."
76
- * />
77
- * );
16
+ * // Skeleton loader
17
+ * <SkeletonLoader pattern="list" count={5} />
78
18
  * ```
79
19
  */
80
20
 
81
21
  // Domain Entities
82
22
  export type {
83
- LoadingType,
84
23
  LoadingSize,
85
24
  SkeletonPattern,
86
- LoadingConfig,
87
25
  SizeConfig,
88
26
  SkeletonConfig,
89
- AnimationConfig,
90
27
  } from './domain/entities/Loading';
91
28
 
92
29
  export {
93
30
  SIZE_CONFIGS,
94
- LOADING_EMOJIS,
95
- ANIMATION_CONFIGS,
96
31
  SKELETON_PATTERNS,
97
- LoadingUtils,
98
- LOADING_CONSTANTS,
32
+ DEFAULT_LOADING_EMOJI,
99
33
  } from './domain/entities/Loading';
100
34
 
101
35
  // Presentation Components
@@ -1,30 +1,17 @@
1
1
  /**
2
- * Loading Domain - LoadingState Component
2
+ * Loading State Component
3
3
  *
4
4
  * Universal loading component with breathing animation.
5
- * Provides consistent, calm loading UX across all apps.
6
- *
7
- * Adapted from meditation_timer app's LoadingState component.
8
- *
9
- * @domain loading
10
- * @layer presentation/components
11
5
  */
12
6
 
13
7
  import React, { useEffect, useRef } from 'react';
14
8
  import { View, StyleSheet, Animated, Easing, type StyleProp, type ViewStyle, type TextStyle } from 'react-native';
15
- import { AtomicText, useAppDesignTokens } from '@umituz/react-native-design-system';
9
+ import { AtomicText } from '@umituz/react-native-design-system';
16
10
  import type { LoadingSize } from '../../domain/entities/Loading';
17
- import {
18
- SIZE_CONFIGS,
19
- LOADING_EMOJIS,
20
- LOADING_CONSTANTS,
21
- } from '../../domain/entities/Loading';
11
+ import { SIZE_CONFIGS, DEFAULT_LOADING_EMOJI } from '../../domain/entities/Loading';
22
12
 
23
- /**
24
- * LoadingState component props
25
- */
26
13
  export interface LoadingStateProps {
27
- /** Loading emoji - default: (customizable per app) */
14
+ /** Loading emoji - pass any emoji dynamically */
28
15
  emoji?: string;
29
16
  /** Loading message (optional) */
30
17
  message?: string;
@@ -38,56 +25,26 @@ export interface LoadingStateProps {
38
25
  messageStyle?: StyleProp<TextStyle>;
39
26
  }
40
27
 
41
- /**
42
- * LoadingState Component
43
- *
44
- * Universal loading indicator with breathing animation.
45
- * Creates a calm, mindful loading experience.
46
- *
47
- * USAGE:
48
- * ```typescript
49
- * // Basic usage
50
- * <LoadingState />
51
- *
52
- * // With message
53
- * <LoadingState message="Loading data..." size="medium" />
54
- *
55
- * // Full screen
56
- * <LoadingState fullScreen message="Please wait..." />
57
- *
58
- * // Custom emoji (per app theme)
59
- * <LoadingState emoji="🧘" message="Loading meditations..." />
60
- *
61
- * // Inline loading
62
- * <LoadingState size="small" />
63
- * ```
64
- */
65
28
  export const LoadingState: React.FC<LoadingStateProps> = ({
66
- emoji = LOADING_EMOJIS.default,
29
+ emoji = DEFAULT_LOADING_EMOJI,
67
30
  message,
68
31
  size = 'large',
69
32
  fullScreen = false,
70
33
  style,
71
34
  messageStyle,
72
35
  }) => {
73
- const tokens = useAppDesignTokens();
74
36
  const config = SIZE_CONFIGS[size];
75
-
76
- // Animated value for emoji pulse (breathing effect)
77
37
  const scaleAnim = useRef(new Animated.Value(1)).current;
78
38
 
79
39
  useEffect(() => {
80
- // Meditation breathing animation: 2 second cycle (inhale/exhale)
81
40
  const breathingAnimation = Animated.loop(
82
41
  Animated.sequence([
83
- // Inhale - expand
84
42
  Animated.timing(scaleAnim, {
85
43
  toValue: 1.15,
86
44
  duration: 1000,
87
45
  easing: Easing.inOut(Easing.ease),
88
46
  useNativeDriver: true,
89
47
  }),
90
- // Exhale - contract
91
48
  Animated.timing(scaleAnim, {
92
49
  toValue: 1,
93
50
  duration: 1000,
@@ -113,7 +70,6 @@ export const LoadingState: React.FC<LoadingStateProps> = ({
113
70
  style,
114
71
  ]}
115
72
  >
116
- {/* Animated Emoji with breathing pulse */}
117
73
  <Animated.Text
118
74
  style={[
119
75
  styles.emoji,
@@ -126,7 +82,6 @@ export const LoadingState: React.FC<LoadingStateProps> = ({
126
82
  {emoji}
127
83
  </Animated.Text>
128
84
 
129
- {/* Optional Loading Message */}
130
85
  {config.showMessage && message && (
131
86
  <AtomicText
132
87
  type="bodySmall"
@@ -151,7 +106,7 @@ const styles = StyleSheet.create({
151
106
  paddingVertical: 60,
152
107
  },
153
108
  emoji: {
154
- fontFamily: 'System', // Emoji font
109
+ fontFamily: 'System',
155
110
  },
156
111
  message: {
157
112
  textAlign: 'center',
@@ -1,25 +1,17 @@
1
1
  /**
2
- * Loading Domain - SkeletonLoader Component
2
+ * Skeleton Loader Component
3
3
  *
4
4
  * Skeleton placeholder loader with shimmer animation.
5
- * Provides visual feedback during content loading.
6
- *
7
- * @domain loading
8
- * @layer presentation/components
9
5
  */
10
6
 
11
7
  import React, { useEffect, useRef } from 'react';
12
8
  import { View, StyleSheet, Animated, type StyleProp, type ViewStyle } from 'react-native';
13
9
  import { useAppDesignTokens } from '@umituz/react-native-design-system';
14
10
  import type { SkeletonPattern, SkeletonConfig } from '../../domain/entities/Loading';
15
- import {
16
- SKELETON_PATTERNS,
17
- LOADING_CONSTANTS,
18
- } from '../../domain/entities/Loading';
11
+ import { SKELETON_PATTERNS } from '../../domain/entities/Loading';
12
+
13
+ const SHIMMER_DURATION = 1200;
19
14
 
20
- /**
21
- * SkeletonLoader component props
22
- */
23
15
  export interface SkeletonLoaderProps {
24
16
  /** Skeleton pattern preset */
25
17
  pattern?: SkeletonPattern;
@@ -33,36 +25,6 @@ export interface SkeletonLoaderProps {
33
25
  disableAnimation?: boolean;
34
26
  }
35
27
 
36
- /**
37
- * SkeletonLoader Component
38
- *
39
- * Renders skeleton placeholders with shimmer animation.
40
- * Provides visual feedback while content is loading.
41
- *
42
- * USAGE:
43
- * ```typescript
44
- * // List skeleton (default)
45
- * <SkeletonLoader pattern="list" count={5} />
46
- *
47
- * // Card skeleton
48
- * <SkeletonLoader pattern="card" count={3} />
49
- *
50
- * // Profile skeleton
51
- * <SkeletonLoader pattern="profile" />
52
- *
53
- * // Text skeleton
54
- * <SkeletonLoader pattern="text" count={3} />
55
- *
56
- * // Custom skeleton
57
- * <SkeletonLoader
58
- * pattern="custom"
59
- * custom={[
60
- * { width: 100, height: 100, borderRadius: 50 },
61
- * { width: '80%', height: 20, borderRadius: 4 },
62
- * ]}
63
- * />
64
- * ```
65
- */
66
28
  export const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
67
29
  pattern = 'list',
68
30
  custom,
@@ -71,25 +33,21 @@ export const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
71
33
  disableAnimation = false,
72
34
  }) => {
73
35
  const tokens = useAppDesignTokens();
74
-
75
- // Get skeleton config
76
36
  const skeletonConfigs = pattern === 'custom' && custom
77
37
  ? custom
78
38
  : SKELETON_PATTERNS[pattern];
79
39
 
80
- // Animated value for shimmer effect
81
40
  const shimmerAnim = useRef(new Animated.Value(0)).current;
82
41
 
83
42
  useEffect(() => {
84
43
  if (disableAnimation) return;
85
44
 
86
- // Shimmer animation: 1.2 second cycle
87
45
  const shimmerAnimation = Animated.loop(
88
46
  Animated.sequence([
89
47
  Animated.timing(shimmerAnim, {
90
48
  toValue: 1,
91
- duration: LOADING_CONSTANTS.SKELETON_SHIMMER_DURATION,
92
- useNativeDriver: false, // backgroundColor animation requires false
49
+ duration: SHIMMER_DURATION,
50
+ useNativeDriver: false,
93
51
  }),
94
52
  Animated.timing(shimmerAnim, {
95
53
  toValue: 0,
@@ -106,7 +64,6 @@ export const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
106
64
  };
107
65
  }, [shimmerAnim, disableAnimation]);
108
66
 
109
- // Interpolate shimmer animation to background color
110
67
  const backgroundColor = shimmerAnim.interpolate({
111
68
  inputRange: [0, 0.5, 1],
112
69
  outputRange: [
@@ -116,7 +73,6 @@ export const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
116
73
  ],
117
74
  });
118
75
 
119
- // Render skeleton items
120
76
  const renderSkeletonItem = (index: number) => (
121
77
  <View key={`skeleton-group-${index}`} style={styles.skeletonGroup}>
122
78
  {skeletonConfigs.map((config, configIndex) => (
@@ -1,122 +1,43 @@
1
1
  /**
2
- * Loading Domain - useLoading Hook
2
+ * Loading Hook
3
3
  *
4
4
  * React hook for managing loading states.
5
- * Provides consistent loading state management across components.
6
- *
7
- * @domain loading
8
- * @layer presentation/hooks
9
5
  */
10
6
 
11
7
  import { useState, useCallback } from 'react';
12
- import type { LoadingType } from '../../domain/entities/Loading';
13
8
 
14
- /**
15
- * useLoading hook return type
16
- */
17
9
  export interface UseLoadingReturn {
18
- // Loading state
19
10
  isLoading: boolean;
20
11
  loadingMessage: string | null;
21
- loadingType: LoadingType;
22
-
23
- // Actions
24
- startLoading: (message?: string, type?: LoadingType) => void;
12
+ startLoading: (message?: string) => void;
25
13
  stopLoading: () => void;
26
14
  setLoadingMessage: (message: string | null) => void;
27
- setLoadingType: (type: LoadingType) => void;
28
-
29
- // Async wrapper
30
- withLoading: <T>(
31
- asyncFn: Promise<T>,
32
- message?: string,
33
- type?: LoadingType
34
- ) => Promise<T>;
15
+ withLoading: <T>(asyncFn: Promise<T>, message?: string) => Promise<T>;
35
16
  }
36
17
 
37
- /**
38
- * useLoading hook for loading state management
39
- *
40
- * USAGE:
41
- * ```typescript
42
- * const {
43
- * isLoading,
44
- * loadingMessage,
45
- * startLoading,
46
- * stopLoading,
47
- * withLoading,
48
- * } = useLoading();
49
- *
50
- * // Manual control
51
- * const handleSave = async () => {
52
- * startLoading('Saving data...');
53
- * try {
54
- * await saveData();
55
- * } finally {
56
- * stopLoading();
57
- * }
58
- * };
59
- *
60
- * // Automatic wrapper
61
- * const handleLoad = () => withLoading(
62
- * loadData(),
63
- * 'Loading data...'
64
- * );
65
- *
66
- * // In render
67
- * {isLoading && <LoadingState message={loadingMessage} />}
68
- * ```
69
- */
70
18
  export const useLoading = (): UseLoadingReturn => {
71
19
  const [isLoading, setIsLoading] = useState(false);
72
20
  const [loadingMessage, setLoadingMessage] = useState<string | null>(null);
73
- const [loadingType, setLoadingType] = useState<LoadingType>('pulse');
74
21
 
75
- /**
76
- * Start loading state
77
- */
78
- const startLoading = useCallback((message?: string, type: LoadingType = 'pulse') => {
22
+ const startLoading = useCallback((message?: string) => {
79
23
  setIsLoading(true);
80
24
  setLoadingMessage(message || null);
81
- setLoadingType(type);
82
25
  }, []);
83
26
 
84
- /**
85
- * Stop loading state
86
- */
87
27
  const stopLoading = useCallback(() => {
88
28
  setIsLoading(false);
89
29
  setLoadingMessage(null);
90
30
  }, []);
91
31
 
92
- /**
93
- * Update loading message
94
- */
95
32
  const updateLoadingMessage = useCallback((message: string | null) => {
96
33
  setLoadingMessage(message);
97
34
  }, []);
98
35
 
99
- /**
100
- * Update loading type
101
- */
102
- const updateLoadingType = useCallback((type: LoadingType) => {
103
- setLoadingType(type);
104
- }, []);
105
-
106
- /**
107
- * Async wrapper that automatically manages loading state
108
- */
109
36
  const withLoading = useCallback(
110
- async <T,>(
111
- asyncFn: Promise<T>,
112
- message?: string,
113
- type: LoadingType = 'pulse'
114
- ): Promise<T> => {
115
- startLoading(message, type);
116
-
37
+ async <T,>(asyncFn: Promise<T>, message?: string): Promise<T> => {
38
+ startLoading(message);
117
39
  try {
118
- const result = await asyncFn;
119
- return result;
40
+ return await asyncFn;
120
41
  } finally {
121
42
  stopLoading();
122
43
  }
@@ -125,24 +46,17 @@ export const useLoading = (): UseLoadingReturn => {
125
46
  );
126
47
 
127
48
  return {
128
- // Loading state
129
49
  isLoading,
130
50
  loadingMessage,
131
- loadingType,
132
-
133
- // Actions
134
51
  startLoading,
135
52
  stopLoading,
136
53
  setLoadingMessage: updateLoadingMessage,
137
- setLoadingType: updateLoadingType,
138
-
139
- // Async wrapper
140
54
  withLoading,
141
55
  };
142
56
  };
143
57
 
144
58
  /**
145
- * Convenience hook for simple loading state (no message)
59
+ * Simple loading hook (no message)
146
60
  */
147
61
  export const useSimpleLoading = () => {
148
62
  const [isLoading, setIsLoading] = useState(false);
@@ -150,10 +64,8 @@ export const useSimpleLoading = () => {
150
64
  const withLoading = useCallback(
151
65
  async <T,>(asyncFn: Promise<T>): Promise<T> => {
152
66
  setIsLoading(true);
153
-
154
67
  try {
155
- const result = await asyncFn;
156
- return result;
68
+ return await asyncFn;
157
69
  } finally {
158
70
  setIsLoading(false);
159
71
  }
package/LICENSE DELETED
@@ -1,22 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Ümit UZ
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
22
-
@@ -1,429 +0,0 @@
1
- # Loading Domain - Usage Examples
2
-
3
- This file provides comprehensive examples for using the loading domain across all generated apps.
4
-
5
- ## 📋 Table of Contents
6
-
7
- 1. [Basic Loading State](#basic-loading-state)
8
- 2. [Skeleton Loaders](#skeleton-loaders)
9
- 3. [Loading State Hook](#loading-state-hook)
10
- 4. [Full Screen Loading](#full-screen-loading)
11
- 5. [List Screens with Skeleton](#list-screens-with-skeleton)
12
- 6. [Custom Emoji per App](#custom-emoji-per-app)
13
-
14
- ---
15
-
16
- ## Basic Loading State
17
-
18
- ### Simple Loading Indicator
19
-
20
- ```typescript
21
- import { LoadingState } from '@domains/loading';
22
-
23
- const MyScreen = () => {
24
- const [isLoading, setIsLoading] = useState(true);
25
-
26
- return (
27
- <View>
28
- {isLoading ? (
29
- <LoadingState message="Loading data..." />
30
- ) : (
31
- <Content />
32
- )}
33
- </View>
34
- );
35
- };
36
- ```
37
-
38
- ### Inline Loading (Small Size)
39
-
40
- ```typescript
41
- <LoadingState size="small" />
42
- ```
43
-
44
- ### Section Loading (Medium Size)
45
-
46
- ```typescript
47
- <LoadingState size="medium" message="Fetching settings..." />
48
- ```
49
-
50
- ---
51
-
52
- ## Skeleton Loaders
53
-
54
- ### List Skeleton
55
-
56
- ```typescript
57
- import { SkeletonLoader } from '@domains/loading';
58
-
59
- const ListScreen = () => {
60
- const [data, setData] = useState([]);
61
- const [isLoading, setIsLoading] = useState(true);
62
-
63
- return (
64
- <View>
65
- {isLoading ? (
66
- <SkeletonLoader pattern="list" count={5} />
67
- ) : (
68
- <FlatList data={data} ... />
69
- )}
70
- </View>
71
- );
72
- };
73
- ```
74
-
75
- ### Card Skeleton
76
-
77
- ```typescript
78
- <SkeletonLoader pattern="card" count={3} />
79
- ```
80
-
81
- ### Profile Skeleton
82
-
83
- ```typescript
84
- <SkeletonLoader pattern="profile" />
85
- ```
86
-
87
- ### Text Skeleton
88
-
89
- ```typescript
90
- <SkeletonLoader pattern="text" count={3} />
91
- ```
92
-
93
- ### Custom Skeleton
94
-
95
- ```typescript
96
- <SkeletonLoader
97
- pattern="custom"
98
- custom={[
99
- { width: 100, height: 100, borderRadius: 50 },
100
- { width: '80%', height: 20, borderRadius: 4, marginBottom: 8 },
101
- { width: '60%', height: 16, borderRadius: 4 },
102
- ]}
103
- />
104
- ```
105
-
106
- ---
107
-
108
- ## Loading State Hook
109
-
110
- ### Manual Control
111
-
112
- ```typescript
113
- import { useLoading } from '@domains/loading';
114
-
115
- const DataScreen = () => {
116
- const { isLoading, loadingMessage, startLoading, stopLoading } = useLoading();
117
-
118
- const handleSave = async () => {
119
- startLoading('Saving changes...');
120
- try {
121
- await saveData();
122
- } finally {
123
- stopLoading();
124
- }
125
- };
126
-
127
- return (
128
- <View>
129
- {isLoading && <LoadingState message={loadingMessage} />}
130
- <Button onPress={handleSave}>Save</Button>
131
- </View>
132
- );
133
- };
134
- ```
135
-
136
- ### Async Wrapper (Automatic)
137
-
138
- ```typescript
139
- const DataScreen = () => {
140
- const { isLoading, loadingMessage, withLoading } = useLoading();
141
-
142
- const loadData = () => withLoading(
143
- fetchData(),
144
- 'Loading data...'
145
- );
146
-
147
- useEffect(() => {
148
- loadData();
149
- }, []);
150
-
151
- return (
152
- <View>
153
- {isLoading ? (
154
- <LoadingState message={loadingMessage} />
155
- ) : (
156
- <Content />
157
- )}
158
- </View>
159
- );
160
- };
161
- ```
162
-
163
- ### Simple Loading Hook
164
-
165
- ```typescript
166
- import { useSimpleLoading } from '@domains/loading';
167
-
168
- const SimpleScreen = () => {
169
- const { isLoading, withLoading } = useSimpleLoading();
170
-
171
- const handleAction = () => withLoading(performAction());
172
-
173
- return (
174
- <View>
175
- {isLoading && <LoadingState />}
176
- <Button onPress={handleAction}>Do Action</Button>
177
- </View>
178
- );
179
- };
180
- ```
181
-
182
- ---
183
-
184
- ## Full Screen Loading
185
-
186
- ### With Message
187
-
188
- ```typescript
189
- <LoadingState
190
- fullScreen
191
- message="Please wait..."
192
- size="large"
193
- />
194
- ```
195
-
196
- ### Initial App Load
197
-
198
- ```typescript
199
- const App = () => {
200
- const [isReady, setIsReady] = useState(false);
201
-
202
- useEffect(() => {
203
- const prepare = async () => {
204
- await loadAssets();
205
- await loadData();
206
- setIsReady(true);
207
- };
208
- prepare();
209
- }, []);
210
-
211
- if (!isReady) {
212
- return <LoadingState fullScreen message="Initializing app..." />;
213
- }
214
-
215
- return <MainApp />;
216
- };
217
- ```
218
-
219
- ---
220
-
221
- ## List Screens with Skeleton
222
-
223
- ### Complete Example
224
-
225
- ```typescript
226
- import { SkeletonLoader, useLoading } from '@domains/loading';
227
-
228
- const WorkoutsScreen = () => {
229
- const [workouts, setWorkouts] = useState([]);
230
- const { isLoading, withLoading } = useLoading();
231
-
232
- const loadWorkouts = () => withLoading(
233
- fetchWorkouts(),
234
- 'Loading workouts...'
235
- );
236
-
237
- useEffect(() => {
238
- loadWorkouts();
239
- }, []);
240
-
241
- return (
242
- <ScreenLayout>
243
- <ScreenHeader title="Workouts" />
244
-
245
- {isLoading ? (
246
- <SkeletonLoader pattern="list" count={8} />
247
- ) : workouts.length === 0 ? (
248
- <EmptyState message="No workouts found" />
249
- ) : (
250
- <FlatList
251
- data={workouts}
252
- renderItem={({ item }) => <WorkoutCard workout={item} />}
253
- />
254
- )}
255
- </ScreenLayout>
256
- );
257
- };
258
- ```
259
-
260
- ---
261
-
262
- ## Custom Emoji per App
263
-
264
- ### Meditation App
265
-
266
- ```typescript
267
- import { LoadingState, LOADING_EMOJIS } from '@domains/loading';
268
-
269
- <LoadingState
270
- emoji={LOADING_EMOJIS.meditation}
271
- message="Loading meditations..."
272
- />
273
- ```
274
-
275
- ### Fitness App
276
-
277
- ```typescript
278
- <LoadingState
279
- emoji={LOADING_EMOJIS.fitness}
280
- message="Loading workouts..."
281
- />
282
- ```
283
-
284
- ### Productivity App
285
-
286
- ```typescript
287
- <LoadingState
288
- emoji={LOADING_EMOJIS.productivity}
289
- message="Loading tasks..."
290
- />
291
- ```
292
-
293
- ### Available Emojis
294
-
295
- ```typescript
296
- LOADING_EMOJIS = {
297
- meditation: '🧘',
298
- fitness: '💪',
299
- workout: '🏋️',
300
- running: '🏃',
301
- cycling: '🚴',
302
- yoga: '🧘‍♀️',
303
- health: '🏥',
304
- nutrition: '🥗',
305
- productivity: '⏳',
306
- education: '📚',
307
- reading: '📖',
308
- music: '🎵',
309
- art: '🎨',
310
- travel: '✈️',
311
- finance: '💰',
312
- shopping: '🛍️',
313
- cooking: '👨‍🍳',
314
- gaming: '🎮',
315
- default: '⌛',
316
- }
317
- ```
318
-
319
- ---
320
-
321
- ## Settings Screen Example
322
-
323
- ```typescript
324
- import { LoadingState, useLoading } from '@domains/loading';
325
-
326
- const SettingsScreen = () => {
327
- const { theme } = useTheme();
328
- const { language, setLanguage } = useLocalization();
329
- const { isLoading, withLoading } = useLoading();
330
-
331
- const handleLanguageChange = (newLanguage: string) => withLoading(
332
- setLanguage(newLanguage),
333
- 'Changing language...'
334
- );
335
-
336
- if (isLoading) {
337
- return <LoadingState fullScreen message="Applying changes..." />;
338
- }
339
-
340
- return (
341
- <ScreenLayout>
342
- <ScreenHeader title="Settings" />
343
- <ScrollView>
344
- {/* Settings content */}
345
- </ScrollView>
346
- </ScreenLayout>
347
- );
348
- };
349
- ```
350
-
351
- ---
352
-
353
- ## Onboarding Screen Example
354
-
355
- ```typescript
356
- import { LoadingState, useLoading } from '@domains/loading';
357
-
358
- const OnboardingScreen = () => {
359
- const { completeOnboarding } = useOnboarding();
360
- const { isLoading, withLoading } = useLoading();
361
-
362
- const handleComplete = () => withLoading(
363
- completeOnboarding(),
364
- 'Setting up your account...'
365
- );
366
-
367
- return (
368
- <ScreenLayout>
369
- {isLoading ? (
370
- <LoadingState fullScreen message="Almost there..." />
371
- ) : (
372
- <OnboardingContent onComplete={handleComplete} />
373
- )}
374
- </ScreenLayout>
375
- );
376
- };
377
- ```
378
-
379
- ---
380
-
381
- ## Best Practices
382
-
383
- 1. **Use skeleton loaders for lists** - Better UX than full-screen loading
384
- 2. **Match emoji to app theme** - Use LOADING_EMOJIS for consistency
385
- 3. **Show meaningful messages** - "Loading workouts..." not "Please wait..."
386
- 4. **Use full screen sparingly** - Only for critical app-wide operations
387
- 5. **Prefer useLoading hook** - Centralized state management
388
- 6. **Use withLoading wrapper** - Cleaner async code
389
- 7. **Test loading states** - Ensure they work on slow connections
390
-
391
- ---
392
-
393
- ## Migration from Old Loading Patterns
394
-
395
- ### Before (ActivityIndicator)
396
-
397
- ```typescript
398
- // ❌ OLD - Inconsistent
399
- {isLoading && <ActivityIndicator />}
400
- {isLoading && <Text>Loading...</Text>}
401
- ```
402
-
403
- ### After (LoadingState)
404
-
405
- ```typescript
406
- // ✅ NEW - Consistent, themed
407
- {isLoading && <LoadingState message="Loading..." />}
408
- ```
409
-
410
- ### Before (Custom Spinner)
411
-
412
- ```typescript
413
- // ❌ OLD - Duplicated code
414
- <View style={styles.loadingContainer}>
415
- <ActivityIndicator size="large" />
416
- <Text style={styles.loadingText}>Please wait...</Text>
417
- </View>
418
- ```
419
-
420
- ### After (LoadingState)
421
-
422
- ```typescript
423
- // ✅ NEW - One line, consistent
424
- <LoadingState size="large" message="Please wait..." />
425
- ```
426
-
427
- ---
428
-
429
- This loading domain ensures **consistent, beautiful, calming loading experiences** across all 100+ generated apps! 🧘