@umituz/react-native-loading 1.0.0
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/LICENSE +22 -0
- package/README.md +111 -0
- package/lib/domain/entities/Loading.d.ts +161 -0
- package/lib/domain/entities/Loading.d.ts.map +1 -0
- package/lib/domain/entities/Loading.js +224 -0
- package/lib/domain/entities/Loading.js.map +1 -0
- package/lib/index.d.ts +85 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +86 -0
- package/lib/index.js.map +1 -0
- package/lib/presentation/components/LoadingState.d.ts +57 -0
- package/lib/presentation/components/LoadingState.d.ts.map +1 -0
- package/lib/presentation/components/LoadingState.js +110 -0
- package/lib/presentation/components/LoadingState.js.map +1 -0
- package/lib/presentation/components/SkeletonLoader.d.ts +59 -0
- package/lib/presentation/components/SkeletonLoader.d.ts.map +1 -0
- package/lib/presentation/components/SkeletonLoader.js +112 -0
- package/lib/presentation/components/SkeletonLoader.js.map +1 -0
- package/lib/presentation/hooks/useLoading.d.ts +67 -0
- package/lib/presentation/hooks/useLoading.d.ts.map +1 -0
- package/lib/presentation/hooks/useLoading.js +124 -0
- package/lib/presentation/hooks/useLoading.js.map +1 -0
- package/package.json +55 -0
- package/src/USAGE_EXAMPLES.md +429 -0
- package/src/domain/entities/Loading.ts +295 -0
- package/src/index.ts +117 -0
- package/src/presentation/components/LoadingState.tsx +161 -0
- package/src/presentation/components/SkeletonLoader.tsx +159 -0
- package/src/presentation/hooks/useLoading.ts +170 -0
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@umituz/react-native-loading",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Loading states and animations for React Native apps with breathing animations, skeleton loaders, and state management hooks",
|
|
5
|
+
"main": "./lib/index.js",
|
|
6
|
+
"types": "./lib/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"typecheck": "tsc --noEmit",
|
|
10
|
+
"lint": "tsc --noEmit",
|
|
11
|
+
"clean": "rm -rf lib",
|
|
12
|
+
"prebuild": "npm run clean",
|
|
13
|
+
"prepublishOnly": "npm run build",
|
|
14
|
+
"version:patch": "npm version patch -m 'chore: release v%s'",
|
|
15
|
+
"version:minor": "npm version minor -m 'chore: release v%s'",
|
|
16
|
+
"version:major": "npm version major -m 'chore: release v%s'"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"react-native",
|
|
20
|
+
"loading",
|
|
21
|
+
"skeleton",
|
|
22
|
+
"loader",
|
|
23
|
+
"animation",
|
|
24
|
+
"spinner"
|
|
25
|
+
],
|
|
26
|
+
"author": "รmit UZ <umit@umituz.com>",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/umituz/react-native-loading"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@umituz/react-native-design-system": ">=1.5.0",
|
|
34
|
+
"react": ">=18.2.0",
|
|
35
|
+
"react-native": ">=0.74.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/react": "^18.2.45",
|
|
39
|
+
"@types/react-native": "^0.73.0",
|
|
40
|
+
"@umituz/react-native-design-system": "^1.5.0",
|
|
41
|
+
"react": "^18.2.0",
|
|
42
|
+
"react-native": "^0.74.0",
|
|
43
|
+
"typescript": "^5.3.3"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"lib",
|
|
50
|
+
"src",
|
|
51
|
+
"README.md",
|
|
52
|
+
"LICENSE"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
|
|
@@ -0,0 +1,429 @@
|
|
|
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! ๐ง
|