react-achievements 3.5.0 → 3.6.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 +1127 -27
- package/dist/index.d.ts +197 -8
- package/dist/index.js +764 -32
- package/dist/index.js.map +1 -1
- package/dist/types/core/components/BadgesButton.d.ts +18 -3
- package/dist/types/core/hooks/useWindowSize.d.ts +16 -0
- package/dist/types/core/types.d.ts +2 -6
- package/dist/types/core/ui/BuiltInConfetti.d.ts +7 -0
- package/dist/types/core/ui/BuiltInModal.d.ts +7 -0
- package/dist/types/core/ui/BuiltInNotification.d.ts +7 -0
- package/dist/types/core/ui/LegacyWrappers.d.ts +21 -0
- package/dist/types/core/ui/interfaces.d.ts +131 -0
- package/dist/types/core/ui/legacyDetector.d.ts +40 -0
- package/dist/types/core/ui/themes.d.ts +14 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/providers/AchievementProvider.d.ts +14 -2
- package/package.json +15 -1
- package/dist/assets/defaultIcons.d.ts +0 -81
- package/dist/badges.d.ts +0 -8
- package/dist/components/Achievement.d.ts +0 -10
- package/dist/components/AchievementModal.d.ts +0 -12
- package/dist/components/Badge.d.ts +0 -9
- package/dist/components/BadgesButton.d.ts +0 -14
- package/dist/components/BadgesModal.d.ts +0 -12
- package/dist/components/ConfettiWrapper.d.ts +0 -6
- package/dist/components/Progress.d.ts +0 -6
- package/dist/context/AchievementContext.d.ts +0 -21
- package/dist/defaultStyles.d.ts +0 -19
- package/dist/hooks/useAchievement.d.ts +0 -8
- package/dist/hooks/useAchievementState.d.ts +0 -4
- package/dist/index.cjs.js +0 -2428
- package/dist/index.esm.js +0 -2403
- package/dist/levels.d.ts +0 -7
- package/dist/providers/AchievementProvider.d.ts +0 -12
- package/dist/redux/achievementSlice.d.ts +0 -30
- package/dist/redux/notificationSlice.d.ts +0 -7
- package/dist/redux/store.d.ts +0 -15
- package/dist/stories/Button.d.ts +0 -28
- package/dist/stories/Button.stories.d.ts +0 -23
- package/dist/stories/Header.d.ts +0 -13
- package/dist/stories/Header.stories.d.ts +0 -18
- package/dist/stories/Page.d.ts +0 -3
- package/dist/stories/Page.stories.d.ts +0 -12
- package/dist/types/core/context/AchievementContext.d.ts +0 -5
- package/dist/types/stories/Button.d.ts +0 -16
- package/dist/types/stories/Button.stories.d.ts +0 -23
- package/dist/types/stories/Header.d.ts +0 -13
- package/dist/types/stories/Header.stories.d.ts +0 -18
- package/dist/types/stories/Page.d.ts +0 -3
- package/dist/types/stories/Page.stories.d.ts +0 -12
- package/dist/types.d.ts +0 -37
- package/dist/utils/EventEmitter.d.ts +0 -6
package/README.md
CHANGED
|
@@ -2,17 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
A flexible and extensible achievement system for React applications. This package provides the foundation for implementing achievements in React applications with support for multiple state management solutions including Redux, Zustand, and Context API. Check the `stories/examples` directory for implementation examples with different state management solutions.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
<img src="https://media4.giphy.com/media/v1.Y2lkPTc5MGI3NjExbnMxdHVqanZvbGR6czJqOTdpejZqc3F3NXh6a2FiM3lmdnB0d3VoOSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/LYXAZelQMeeYpzbgtT/giphy.gif" alt="React Achievements Demo" width="600" />
|
|
7
|
-
</p>
|
|
5
|
+
https://github.com/user-attachments/assets/a33fdae5-439b-4fc9-a388-ccb2f432a3a8
|
|
8
6
|
|
|
9
7
|
## Installation
|
|
10
8
|
|
|
9
|
+
**NEW in v3.6.0**: Optional built-in UI system available! Choose between the traditional external dependencies or the new lightweight built-in UI.
|
|
10
|
+
|
|
11
|
+
### Option 1: Traditional External UI (Default)
|
|
12
|
+
```bash
|
|
13
|
+
npm install react-achievements react-confetti react-modal react-toastify react-use
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Option 2: Built-in UI (NEW - Opt-in)
|
|
11
17
|
```bash
|
|
12
|
-
npm install react-achievements
|
|
18
|
+
npm install react-achievements
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then explicitly enable built-in UI in your code:
|
|
22
|
+
```tsx
|
|
23
|
+
<AchievementProvider
|
|
24
|
+
achievements={config}
|
|
25
|
+
useBuiltInUI={true} // Required to use built-in UI
|
|
26
|
+
>
|
|
27
|
+
<YourApp />
|
|
28
|
+
</AchievementProvider>
|
|
13
29
|
```
|
|
14
30
|
|
|
15
|
-
|
|
31
|
+
**Requirements**: React 16.8+ and react-dom are required (defined as peerDependencies).
|
|
32
|
+
|
|
33
|
+
**Note**: To maintain backwards compatibility, v3.6.0 defaults to external UI dependencies. The built-in UI system is opt-in via the `useBuiltInUI` prop. In v4.0.0, built-in UI will become the default. See the [Built-in UI System](#built-in-ui-system-new-in-v360) section below.
|
|
16
34
|
|
|
17
35
|
## Quick Start
|
|
18
36
|
|
|
@@ -27,26 +45,40 @@ import {
|
|
|
27
45
|
BadgesModal
|
|
28
46
|
} from 'react-achievements';
|
|
29
47
|
|
|
30
|
-
// Define achievements with the
|
|
48
|
+
// Define achievements with the Builder API for easy configuration
|
|
31
49
|
import { AchievementBuilder } from 'react-achievements';
|
|
32
50
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
const gameAchievements = AchievementBuilder.combine([
|
|
52
|
+
// Score achievements with custom awards
|
|
53
|
+
AchievementBuilder.createScoreAchievement(100)
|
|
54
|
+
.withAward({ title: 'Century!', description: 'Score 100 points', icon: '🏆' }),
|
|
55
|
+
AchievementBuilder.createScoreAchievement(500)
|
|
56
|
+
.withAward({ title: 'High Scorer!', description: 'Score 500 points', icon: '⭐' }),
|
|
57
|
+
|
|
58
|
+
// Level achievement
|
|
59
|
+
AchievementBuilder.createLevelAchievement(5)
|
|
60
|
+
.withAward({ title: 'Leveling Up', description: 'Reach level 5', icon: '📈' }),
|
|
61
|
+
|
|
62
|
+
// Boolean achievement
|
|
63
|
+
AchievementBuilder.createBooleanAchievement('completedTutorial')
|
|
64
|
+
.withAward({ title: 'Tutorial Master', description: 'Complete the tutorial', icon: '📚' }),
|
|
65
|
+
|
|
66
|
+
// For custom numeric metrics, use Simple API syntax (easiest)
|
|
67
|
+
{
|
|
68
|
+
buttonClicks: {
|
|
69
|
+
10: { title: 'Clicker', description: 'Click 10 times', icon: '👆' },
|
|
70
|
+
100: { title: 'Super Clicker', description: 'Click 100 times', icon: '🖱️' }
|
|
71
|
+
}
|
|
44
72
|
},
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
73
|
+
|
|
74
|
+
// Or use the full builder for complex conditions (Tier 3)
|
|
75
|
+
AchievementBuilder.create()
|
|
76
|
+
.withId('speed_demon')
|
|
77
|
+
.withMetric('buttonClicks')
|
|
78
|
+
.withCondition((clicks) => typeof clicks === 'number' && clicks >= 50)
|
|
79
|
+
.withAward({ title: 'Speed Demon', description: 'Click 50 times quickly', icon: '⚡' })
|
|
80
|
+
.build()
|
|
81
|
+
]);
|
|
50
82
|
|
|
51
83
|
// Demo component with all essential features
|
|
52
84
|
const DemoComponent = () => {
|
|
@@ -121,10 +153,381 @@ export default App;
|
|
|
121
153
|
|
|
122
154
|
When you click "Score 100 points":
|
|
123
155
|
1. A toast notification appears automatically
|
|
124
|
-
2. Confetti animation plays
|
|
156
|
+
2. Confetti animation plays
|
|
125
157
|
3. The achievement is stored and visible in the badges modal
|
|
126
158
|
4. The badges button updates to show the new count
|
|
127
159
|
|
|
160
|
+
## Built-in UI System (NEW in v3.6.0)
|
|
161
|
+
|
|
162
|
+
React Achievements v3.6.0 introduces a modern, lightweight UI system with **zero external dependencies**. The built-in components provide beautiful notifications, modals, and confetti animations with full theme customization.
|
|
163
|
+
|
|
164
|
+
### Key Benefits
|
|
165
|
+
|
|
166
|
+
- **Modern Design**: Sleek gradients, smooth animations, and polished components
|
|
167
|
+
- **Theme System**: 3 built-in themes (modern, minimal, gamified)
|
|
168
|
+
- **Component Injection**: Replace any UI component with your own implementation
|
|
169
|
+
- **Backwards Compatible**: Existing apps work without changes
|
|
170
|
+
- **SSR Safe**: Proper window checks for server-side rendering
|
|
171
|
+
- **Lightweight**: Built-in UI with zero external dependencies
|
|
172
|
+
|
|
173
|
+
### Quick Migration
|
|
174
|
+
|
|
175
|
+
**To use built-in UI** - opt-in with the `useBuiltInUI` prop:
|
|
176
|
+
```tsx
|
|
177
|
+
<AchievementProvider
|
|
178
|
+
achievements={config}
|
|
179
|
+
useBuiltInUI={true} // Force built-in UI, ignore external dependencies
|
|
180
|
+
>
|
|
181
|
+
<YourApp />
|
|
182
|
+
</AchievementProvider>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Built-in Theme Presets
|
|
186
|
+
|
|
187
|
+
Choose from 3 professionally designed themes:
|
|
188
|
+
|
|
189
|
+
#### Modern Theme (Default)
|
|
190
|
+
```tsx
|
|
191
|
+
<AchievementProvider
|
|
192
|
+
achievements={config}
|
|
193
|
+
useBuiltInUI={true}
|
|
194
|
+
ui={{ theme: 'modern' }}
|
|
195
|
+
>
|
|
196
|
+
```
|
|
197
|
+
- Dark gradients with smooth animations
|
|
198
|
+
- Green accent colors
|
|
199
|
+
- Professional and polished look
|
|
200
|
+
- Perfect for productivity apps and games
|
|
201
|
+
|
|
202
|
+
#### Minimal Theme
|
|
203
|
+
```tsx
|
|
204
|
+
<AchievementProvider
|
|
205
|
+
achievements={config}
|
|
206
|
+
useBuiltInUI={true}
|
|
207
|
+
ui={{ theme: 'minimal' }}
|
|
208
|
+
>
|
|
209
|
+
```
|
|
210
|
+
- Light, clean design
|
|
211
|
+
- Subtle shadows and simple borders
|
|
212
|
+
- Reduced motion for accessibility
|
|
213
|
+
- Perfect for professional and corporate apps
|
|
214
|
+
|
|
215
|
+
#### Gamified Theme
|
|
216
|
+
```tsx
|
|
217
|
+
<AchievementProvider
|
|
218
|
+
achievements={config}
|
|
219
|
+
useBuiltInUI={true}
|
|
220
|
+
ui={{ theme: 'gamified' }}
|
|
221
|
+
>
|
|
222
|
+
```
|
|
223
|
+
- Perfect for games and engaging experiences
|
|
224
|
+
- Badges instead of rectangular displays
|
|
225
|
+
|
|
226
|
+
### Notification Positions
|
|
227
|
+
|
|
228
|
+
Place notifications anywhere on screen:
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
<AchievementProvider
|
|
232
|
+
achievements={config}
|
|
233
|
+
useBuiltInUI={true}
|
|
234
|
+
ui={{
|
|
235
|
+
theme: 'modern',
|
|
236
|
+
notificationPosition: 'top-center', // Default
|
|
237
|
+
// Options: 'top-left', 'top-center', 'top-right',
|
|
238
|
+
// 'bottom-left', 'bottom-center', 'bottom-right'
|
|
239
|
+
}}
|
|
240
|
+
>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Custom Component Injection
|
|
244
|
+
|
|
245
|
+
For advanced users who need full customization beyond the 3 built-in themes, you can replace any UI component with your own implementation:
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
import { AchievementProvider, NotificationProps } from 'react-achievements';
|
|
249
|
+
|
|
250
|
+
// Create your custom notification component
|
|
251
|
+
const MyCustomNotification: React.FC<NotificationProps> = ({
|
|
252
|
+
achievement,
|
|
253
|
+
onClose,
|
|
254
|
+
duration,
|
|
255
|
+
}) => {
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
const timer = setTimeout(onClose, duration);
|
|
258
|
+
return () => clearTimeout(timer);
|
|
259
|
+
}, [duration, onClose]);
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<div className="my-custom-notification">
|
|
263
|
+
<h3>{achievement.title}</h3>
|
|
264
|
+
<p>{achievement.description}</p>
|
|
265
|
+
<span>{achievement.icon}</span>
|
|
266
|
+
</div>
|
|
267
|
+
);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// Inject your component
|
|
271
|
+
<AchievementProvider
|
|
272
|
+
achievements={config}
|
|
273
|
+
ui={{
|
|
274
|
+
NotificationComponent: MyCustomNotification,
|
|
275
|
+
// ModalComponent: MyCustomModal, // Optional
|
|
276
|
+
// ConfettiComponent: MyCustomConfetti, // Optional
|
|
277
|
+
}}
|
|
278
|
+
>
|
|
279
|
+
<YourApp />
|
|
280
|
+
</AchievementProvider>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### BadgesButton Placement Modes
|
|
284
|
+
|
|
285
|
+
**NEW**: BadgesButton now supports both fixed positioning and inline mode:
|
|
286
|
+
|
|
287
|
+
#### Fixed Positioning (Default)
|
|
288
|
+
Traditional floating button:
|
|
289
|
+
```tsx
|
|
290
|
+
import { BadgesButton } from 'react-achievements';
|
|
291
|
+
|
|
292
|
+
<BadgesButton
|
|
293
|
+
placement="fixed" // Default
|
|
294
|
+
position="bottom-right" // Corner position
|
|
295
|
+
onClick={() => setModalOpen(true)}
|
|
296
|
+
unlockedAchievements={achievements}
|
|
297
|
+
/>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### Inline Mode (NEW)
|
|
301
|
+
Embed the badge button in drawers, navbars, sidebars:
|
|
302
|
+
```tsx
|
|
303
|
+
function MyDrawer() {
|
|
304
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
305
|
+
|
|
306
|
+
return (
|
|
307
|
+
<Drawer>
|
|
308
|
+
<nav>
|
|
309
|
+
<NavItem>Home</NavItem>
|
|
310
|
+
<NavItem>Settings</NavItem>
|
|
311
|
+
|
|
312
|
+
{/* Badge button inside drawer - no fixed positioning */}
|
|
313
|
+
<BadgesButton
|
|
314
|
+
placement="inline"
|
|
315
|
+
onClick={() => setModalOpen(true)}
|
|
316
|
+
unlockedAchievements={achievements}
|
|
317
|
+
theme="modern" // Matches your app theme
|
|
318
|
+
/>
|
|
319
|
+
</nav>
|
|
320
|
+
</Drawer>
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Inline mode benefits:**
|
|
326
|
+
- Works in drawers, sidebars, navigation bars
|
|
327
|
+
- Flows with your layout (no fixed positioning)
|
|
328
|
+
- Themeable to match surrounding UI
|
|
329
|
+
- Fully customizable with `styles` prop
|
|
330
|
+
|
|
331
|
+
### UI Configuration Options
|
|
332
|
+
|
|
333
|
+
Complete UI configuration reference:
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
<AchievementProvider
|
|
337
|
+
achievements={config}
|
|
338
|
+
useBuiltInUI={true}
|
|
339
|
+
ui={{
|
|
340
|
+
// Theme configuration
|
|
341
|
+
theme: 'modern', // 'modern' | 'minimal' | 'gamified' | custom theme name
|
|
342
|
+
|
|
343
|
+
// Component overrides
|
|
344
|
+
NotificationComponent: MyCustomNotification, // Optional
|
|
345
|
+
ModalComponent: MyCustomModal, // Optional
|
|
346
|
+
ConfettiComponent: MyCustomConfetti, // Optional
|
|
347
|
+
|
|
348
|
+
// Notification settings
|
|
349
|
+
notificationPosition: 'top-center', // Position on screen
|
|
350
|
+
enableNotifications: true, // Default: true
|
|
351
|
+
|
|
352
|
+
// Confetti settings
|
|
353
|
+
enableConfetti: true, // Default: true
|
|
354
|
+
|
|
355
|
+
// Direct theme object (bypasses registry)
|
|
356
|
+
customTheme: {
|
|
357
|
+
name: 'inline-theme',
|
|
358
|
+
notification: { /* ... */ },
|
|
359
|
+
modal: { /* ... */ },
|
|
360
|
+
confetti: { /* ... */ },
|
|
361
|
+
},
|
|
362
|
+
}}
|
|
363
|
+
>
|
|
364
|
+
<YourApp />
|
|
365
|
+
</AchievementProvider>
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Migration Guide
|
|
369
|
+
|
|
370
|
+
#### Existing Users (v3.5.0 and earlier)
|
|
371
|
+
|
|
372
|
+
**Option 1: No changes (keep using external dependencies)**
|
|
373
|
+
- Your code works exactly as before
|
|
374
|
+
- You'll see a deprecation warning in console (once per session)
|
|
375
|
+
- Plan to migrate before v4.0.0
|
|
376
|
+
|
|
377
|
+
**Option 2: Migrate to built-in UI**
|
|
378
|
+
1. Add `useBuiltInUI={true}` to your AchievementProvider
|
|
379
|
+
2. Test your app (UI will change to modern theme)
|
|
380
|
+
3. Optionally customize with `ui={{ theme: 'minimal' }}` if you prefer lighter styling
|
|
381
|
+
4. Remove external dependencies:
|
|
382
|
+
```bash
|
|
383
|
+
npm uninstall react-toastify react-modal react-confetti react-use
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
#### New Projects
|
|
387
|
+
|
|
388
|
+
For new projects using built-in UI, install react-achievements and explicitly opt-in:
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
npm install react-achievements
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
```tsx
|
|
395
|
+
<AchievementProvider
|
|
396
|
+
achievements={config}
|
|
397
|
+
useBuiltInUI={true} // Explicitly enable built-in UI
|
|
398
|
+
ui={{ theme: 'modern' }} // Optional theme customization
|
|
399
|
+
>
|
|
400
|
+
{/* Beautiful built-in UI */}
|
|
401
|
+
</AchievementProvider>
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Without `useBuiltInUI={true}`, you'll need to install the external UI dependencies (default behavior for v3.6.0).
|
|
405
|
+
|
|
406
|
+
### Built-in UI Component API Reference
|
|
407
|
+
|
|
408
|
+
The built-in UI system includes three core components that can be used standalone or customized via component injection.
|
|
409
|
+
|
|
410
|
+
#### BuiltInNotification
|
|
411
|
+
|
|
412
|
+
Displays achievement unlock notifications.
|
|
413
|
+
|
|
414
|
+
**Props:**
|
|
415
|
+
- `achievement` (object, required): Achievement object with `id`, `title`, `description`, and `icon`
|
|
416
|
+
- `onClose` (function, optional): Callback to dismiss the notification
|
|
417
|
+
- `duration` (number, optional): Auto-dismiss duration in ms (default: 5000)
|
|
418
|
+
- `position` (string, optional): Notification position - 'top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', or 'bottom-right' (default: 'top-center')
|
|
419
|
+
- `theme` (string | ThemeConfig, optional): Theme name or custom theme config
|
|
420
|
+
|
|
421
|
+
**Usage:**
|
|
422
|
+
```tsx
|
|
423
|
+
import { BuiltInNotification } from 'react-achievements';
|
|
424
|
+
|
|
425
|
+
<BuiltInNotification
|
|
426
|
+
achievement={{
|
|
427
|
+
id: 'score_100',
|
|
428
|
+
title: 'Century!',
|
|
429
|
+
description: 'Score 100 points',
|
|
430
|
+
icon: '🏆'
|
|
431
|
+
}}
|
|
432
|
+
onClose={() => console.log('Dismissed')}
|
|
433
|
+
duration={5000}
|
|
434
|
+
position="top-center"
|
|
435
|
+
theme="modern"
|
|
436
|
+
/>
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### BuiltInModal
|
|
440
|
+
|
|
441
|
+
Modal dialog for displaying achievement history.
|
|
442
|
+
|
|
443
|
+
**Props:**
|
|
444
|
+
- `isOpen` (boolean, required): Modal open state
|
|
445
|
+
- `onClose` (function, required): Callback to close modal
|
|
446
|
+
- `achievements` (array, required): Array of achievement objects with `isUnlocked` status
|
|
447
|
+
- `icons` (object, optional): Custom icon mapping
|
|
448
|
+
- `theme` (string | ThemeConfig, optional): Theme name or custom theme config
|
|
449
|
+
|
|
450
|
+
**Usage:**
|
|
451
|
+
```tsx
|
|
452
|
+
import { BuiltInModal } from 'react-achievements';
|
|
453
|
+
|
|
454
|
+
<BuiltInModal
|
|
455
|
+
isOpen={isOpen}
|
|
456
|
+
onClose={() => setIsOpen(false)}
|
|
457
|
+
achievements={achievementsWithStatus}
|
|
458
|
+
icons={customIcons}
|
|
459
|
+
theme="minimal"
|
|
460
|
+
/>
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
**Note:** This is the internal UI component. For the public API component with `showAllAchievements` support, use `BadgesModal` instead.
|
|
464
|
+
|
|
465
|
+
#### BuiltInConfetti
|
|
466
|
+
|
|
467
|
+
Confetti animation component.
|
|
468
|
+
|
|
469
|
+
**Props:**
|
|
470
|
+
- `show` (boolean, required): Whether confetti is active
|
|
471
|
+
- `duration` (number, optional): Animation duration in ms (default: 5000)
|
|
472
|
+
- `particleCount` (number, optional): Number of confetti particles (default: 50)
|
|
473
|
+
- `colors` (string[], optional): Array of color hex codes (default: ['#FFD700', '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'])
|
|
474
|
+
|
|
475
|
+
**Usage:**
|
|
476
|
+
```tsx
|
|
477
|
+
import { BuiltInConfetti } from 'react-achievements';
|
|
478
|
+
|
|
479
|
+
<BuiltInConfetti
|
|
480
|
+
show={showConfetti}
|
|
481
|
+
duration={5000}
|
|
482
|
+
particleCount={150}
|
|
483
|
+
colors={['#ff0000', '#00ff00', '#0000ff']}
|
|
484
|
+
/>
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
**Customization via Component Injection:**
|
|
488
|
+
|
|
489
|
+
You can replace any built-in component with your own implementation:
|
|
490
|
+
|
|
491
|
+
```tsx
|
|
492
|
+
import { AchievementProvider, NotificationProps } from 'react-achievements';
|
|
493
|
+
|
|
494
|
+
const MyCustomNotification: React.FC<NotificationProps> = ({
|
|
495
|
+
achievement,
|
|
496
|
+
onClose,
|
|
497
|
+
duration
|
|
498
|
+
}) => {
|
|
499
|
+
useEffect(() => {
|
|
500
|
+
const timer = setTimeout(onClose, duration);
|
|
501
|
+
return () => clearTimeout(timer);
|
|
502
|
+
}, [duration, onClose]);
|
|
503
|
+
|
|
504
|
+
return (
|
|
505
|
+
<div className="my-notification">
|
|
506
|
+
<h3>{achievement.title}</h3>
|
|
507
|
+
<p>{achievement.description}</p>
|
|
508
|
+
<span>{achievement.icon}</span>
|
|
509
|
+
</div>
|
|
510
|
+
);
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
<AchievementProvider
|
|
514
|
+
achievements={config}
|
|
515
|
+
ui={{
|
|
516
|
+
NotificationComponent: MyCustomNotification,
|
|
517
|
+
// ModalComponent: MyCustomModal,
|
|
518
|
+
// ConfettiComponent: MyCustomConfetti
|
|
519
|
+
}}
|
|
520
|
+
>
|
|
521
|
+
<App />
|
|
522
|
+
</AchievementProvider>
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Deprecation Timeline
|
|
526
|
+
|
|
527
|
+
- **v3.6.0 (current)**: Built-in UI available, external deps optional with deprecation warning
|
|
528
|
+
- **v3.7.0-v3.9.0**: Continued support for both systems, refinements based on feedback
|
|
529
|
+
- **v4.0.0**: External dependencies fully optional, built-in UI becomes default
|
|
530
|
+
|
|
128
531
|
## Simple API (Recommended)
|
|
129
532
|
|
|
130
533
|
Perfect for 90% of use cases - threshold-based achievements with minimal configuration:
|
|
@@ -322,6 +725,10 @@ See the [examples directory](./stories/examples) for detailed implementations an
|
|
|
322
725
|
- Toast notifications
|
|
323
726
|
- Confetti animations
|
|
324
727
|
- TypeScript support
|
|
728
|
+
- **NEW in v3.6.0**: Built-in UI components with zero external dependencies
|
|
729
|
+
- **NEW in v3.6.0**: Extensible theme system with 3 built-in themes (modern, minimal, gamified)
|
|
730
|
+
- **NEW in v3.6.0**: Component injection for full UI customization
|
|
731
|
+
- **NEW in v3.6.0**: BadgesButton inline mode for drawers and sidebars
|
|
325
732
|
- **NEW in v3.4.0**: Async storage support (IndexedDB, REST API, Offline Queue)
|
|
326
733
|
- **NEW in v3.4.0**: 50MB+ storage capacity with IndexedDB
|
|
327
734
|
- **NEW in v3.4.0**: Server-side sync with REST API storage
|
|
@@ -367,6 +774,15 @@ To allow users to view their achievement history, the package provides two essen
|
|
|
367
774
|
|
|
368
775
|
**Show All Achievements** (NEW in v3.5.0): Display both locked and unlocked achievements to motivate users and show them what's available:
|
|
369
776
|
|
|
777
|
+
**⚠️ IMPORTANT: Using getAllAchievements with BadgesModal**
|
|
778
|
+
|
|
779
|
+
When displaying all achievements (locked + unlocked) in the modal, you MUST use the `getAllAchievements()` method from the `useAchievements` hook:
|
|
780
|
+
|
|
781
|
+
- ✅ **Correct**: `allAchievements={getAllAchievements()}`
|
|
782
|
+
- ❌ **Incorrect**: `allAchievements={achievements.all}`
|
|
783
|
+
|
|
784
|
+
**Why?** `getAllAchievements()` returns an array of achievement objects with an `isUnlocked: boolean` property that the modal uses to display locked vs unlocked states. The `achievements.all` property is the raw configuration object and doesn't include unlock status information.
|
|
785
|
+
|
|
370
786
|
```tsx
|
|
371
787
|
import { useAchievements, BadgesModal } from 'react-achievements';
|
|
372
788
|
|
|
@@ -467,6 +883,25 @@ The library provides a small set of essential fallback icons for system use (err
|
|
|
467
883
|
|
|
468
884
|
React Achievements now supports async storage backends for modern applications that need large data capacity, server sync, or offline-first capabilities.
|
|
469
885
|
|
|
886
|
+
### Choosing the Right Storage
|
|
887
|
+
|
|
888
|
+
Select the storage option that best fits your application's needs:
|
|
889
|
+
|
|
890
|
+
| Storage Type | Capacity | Persistence | Network | Offline | Use Case |
|
|
891
|
+
|--------------|----------|-------------|---------|---------|----------|
|
|
892
|
+
| **MemoryStorage** | Unlimited | Session only | No | N/A | Testing, prototypes, temporary state |
|
|
893
|
+
| **LocalStorage** | ~5-10MB | Permanent | No | N/A | Simple apps, browser-only, small datasets |
|
|
894
|
+
| **IndexedDB** | ~50MB+ | Permanent | No | N/A | Large datasets, offline apps, PWAs |
|
|
895
|
+
| **RestAPI** | Unlimited | Server-side | Yes | No | Multi-device sync, cloud backup, user accounts |
|
|
896
|
+
| **OfflineQueue** | Unlimited | Hybrid | Yes | Yes | PWAs, unreliable connections, offline-first apps |
|
|
897
|
+
|
|
898
|
+
**Decision Tree:**
|
|
899
|
+
- **Need cloud sync or multi-device support?** → Use **RestAPI** or **OfflineQueue**
|
|
900
|
+
- **Large data storage (>10MB)?** → Use **IndexedDB**
|
|
901
|
+
- **Simple browser-only app?** → Use **LocalStorage** (default)
|
|
902
|
+
- **Testing or prototypes only?** → Use **MemoryStorage**
|
|
903
|
+
- **Offline-first with sync?** → Use **OfflineQueue** (wraps RestAPI)
|
|
904
|
+
|
|
470
905
|
### IndexedDB Storage
|
|
471
906
|
|
|
472
907
|
Browser-native storage with 50MB+ capacity (vs localStorage's 5-10MB limit):
|
|
@@ -1317,6 +1752,439 @@ The achievement components use default styling that works well out of the box. F
|
|
|
1317
1752
|
/>
|
|
1318
1753
|
```
|
|
1319
1754
|
|
|
1755
|
+
### Default Styles Reference
|
|
1756
|
+
|
|
1757
|
+
The `defaultStyles` export provides access to the default styling configuration for all components. Use this to extend or override specific style properties while keeping other defaults.
|
|
1758
|
+
|
|
1759
|
+
```tsx
|
|
1760
|
+
import { defaultStyles } from 'react-achievements';
|
|
1761
|
+
|
|
1762
|
+
// Access default styles
|
|
1763
|
+
console.log(defaultStyles.badgesButton);
|
|
1764
|
+
console.log(defaultStyles.badgesModal);
|
|
1765
|
+
|
|
1766
|
+
// Extend default styles
|
|
1767
|
+
<BadgesButton
|
|
1768
|
+
style={{
|
|
1769
|
+
...defaultStyles.badgesButton,
|
|
1770
|
+
backgroundColor: '#custom-color',
|
|
1771
|
+
borderRadius: '12px'
|
|
1772
|
+
}}
|
|
1773
|
+
unlockedAchievements={achievements}
|
|
1774
|
+
/>
|
|
1775
|
+
|
|
1776
|
+
// Override specific properties
|
|
1777
|
+
<BadgesModal
|
|
1778
|
+
style={{
|
|
1779
|
+
...defaultStyles.badgesModal,
|
|
1780
|
+
maxWidth: '800px',
|
|
1781
|
+
padding: '2rem'
|
|
1782
|
+
}}
|
|
1783
|
+
isOpen={isOpen}
|
|
1784
|
+
onClose={onClose}
|
|
1785
|
+
achievements={achievements}
|
|
1786
|
+
/>
|
|
1787
|
+
```
|
|
1788
|
+
|
|
1789
|
+
**Available style objects:**
|
|
1790
|
+
- `defaultStyles.badgesButton` - Default button styles
|
|
1791
|
+
- `defaultStyles.badgesModal` - Default modal styles
|
|
1792
|
+
- `defaultStyles.notification` - Default notification styles (built-in UI)
|
|
1793
|
+
- `defaultStyles.confetti` - Default confetti configuration
|
|
1794
|
+
|
|
1795
|
+
## Utility Functions & Hooks
|
|
1796
|
+
|
|
1797
|
+
### useWindowSize Hook
|
|
1798
|
+
|
|
1799
|
+
Returns current window dimensions for responsive UI components.
|
|
1800
|
+
|
|
1801
|
+
```tsx
|
|
1802
|
+
import { useWindowSize } from 'react-achievements';
|
|
1803
|
+
|
|
1804
|
+
const MyComponent = () => {
|
|
1805
|
+
const { width, height } = useWindowSize();
|
|
1806
|
+
|
|
1807
|
+
return (
|
|
1808
|
+
<div>
|
|
1809
|
+
Window size: {width} x {height}
|
|
1810
|
+
</div>
|
|
1811
|
+
);
|
|
1812
|
+
};
|
|
1813
|
+
```
|
|
1814
|
+
|
|
1815
|
+
**Returns:** `{ width: number; height: number }`
|
|
1816
|
+
|
|
1817
|
+
**Use cases:**
|
|
1818
|
+
- Responsive achievement UI layouts
|
|
1819
|
+
- Adaptive modal positioning
|
|
1820
|
+
- Mobile vs desktop rendering
|
|
1821
|
+
|
|
1822
|
+
### normalizeAchievements Function
|
|
1823
|
+
|
|
1824
|
+
Converts Simple API configuration to Complex API format internally. This function is used automatically by the `AchievementProvider`, so you typically don't need to call it directly.
|
|
1825
|
+
|
|
1826
|
+
```tsx
|
|
1827
|
+
import { normalizeAchievements } from 'react-achievements';
|
|
1828
|
+
|
|
1829
|
+
const simpleConfig = {
|
|
1830
|
+
score: {
|
|
1831
|
+
100: { title: 'Century!', icon: '🏆' }
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
|
|
1835
|
+
const normalized = normalizeAchievements(simpleConfig);
|
|
1836
|
+
// Returns complex format used internally
|
|
1837
|
+
```
|
|
1838
|
+
|
|
1839
|
+
**Note:** Usually not needed - the provider handles this automatically.
|
|
1840
|
+
|
|
1841
|
+
### isSimpleConfig Type Guard
|
|
1842
|
+
|
|
1843
|
+
Check if a configuration object uses the Simple API format.
|
|
1844
|
+
|
|
1845
|
+
```tsx
|
|
1846
|
+
import { isSimpleConfig } from 'react-achievements';
|
|
1847
|
+
|
|
1848
|
+
if (isSimpleConfig(myConfig)) {
|
|
1849
|
+
console.log('Using Simple API format');
|
|
1850
|
+
} else {
|
|
1851
|
+
console.log('Using Complex API format');
|
|
1852
|
+
}
|
|
1853
|
+
```
|
|
1854
|
+
|
|
1855
|
+
**Returns:** `boolean`
|
|
1856
|
+
|
|
1857
|
+
## TypeScript Type Reference
|
|
1858
|
+
|
|
1859
|
+
### Core Types
|
|
1860
|
+
|
|
1861
|
+
#### AchievementWithStatus
|
|
1862
|
+
|
|
1863
|
+
Achievement object with unlock status (returned by `getAllAchievements()`).
|
|
1864
|
+
|
|
1865
|
+
```tsx
|
|
1866
|
+
interface AchievementWithStatus {
|
|
1867
|
+
achievementId: string;
|
|
1868
|
+
achievementTitle: string;
|
|
1869
|
+
achievementDescription?: string;
|
|
1870
|
+
achievementIconKey?: string;
|
|
1871
|
+
isUnlocked: boolean;
|
|
1872
|
+
}
|
|
1873
|
+
```
|
|
1874
|
+
|
|
1875
|
+
#### AchievementMetrics
|
|
1876
|
+
|
|
1877
|
+
Metrics tracked for achievements.
|
|
1878
|
+
|
|
1879
|
+
```tsx
|
|
1880
|
+
type AchievementMetrics = Record<string, AchievementMetricValue>;
|
|
1881
|
+
type AchievementMetricValue = number | string | boolean | Date | null | undefined;
|
|
1882
|
+
```
|
|
1883
|
+
|
|
1884
|
+
**Example:**
|
|
1885
|
+
```tsx
|
|
1886
|
+
const metrics: AchievementMetrics = {
|
|
1887
|
+
score: 100,
|
|
1888
|
+
level: 5,
|
|
1889
|
+
completedTutorial: true,
|
|
1890
|
+
lastLoginDate: new Date()
|
|
1891
|
+
};
|
|
1892
|
+
```
|
|
1893
|
+
|
|
1894
|
+
#### UIConfig
|
|
1895
|
+
|
|
1896
|
+
UI configuration for built-in components.
|
|
1897
|
+
|
|
1898
|
+
```tsx
|
|
1899
|
+
interface UIConfig {
|
|
1900
|
+
theme?: 'modern' | 'minimal' | 'gamified' | string;
|
|
1901
|
+
customTheme?: ThemeConfig;
|
|
1902
|
+
NotificationComponent?: React.ComponentType<NotificationProps>;
|
|
1903
|
+
ModalComponent?: React.ComponentType<ModalProps>;
|
|
1904
|
+
ConfettiComponent?: React.ComponentType<ConfettiProps>;
|
|
1905
|
+
notificationPosition?: NotificationPosition;
|
|
1906
|
+
enableNotifications?: boolean;
|
|
1907
|
+
enableConfetti?: boolean;
|
|
1908
|
+
}
|
|
1909
|
+
```
|
|
1910
|
+
|
|
1911
|
+
#### StorageType Enum
|
|
1912
|
+
|
|
1913
|
+
```tsx
|
|
1914
|
+
enum StorageType {
|
|
1915
|
+
Local = 'local',
|
|
1916
|
+
Memory = 'memory',
|
|
1917
|
+
IndexedDB = 'indexeddb',
|
|
1918
|
+
RestAPI = 'restapi'
|
|
1919
|
+
}
|
|
1920
|
+
```
|
|
1921
|
+
|
|
1922
|
+
**Usage:**
|
|
1923
|
+
```tsx
|
|
1924
|
+
<AchievementProvider storage={StorageType.IndexedDB}>
|
|
1925
|
+
```
|
|
1926
|
+
|
|
1927
|
+
### Storage Interfaces
|
|
1928
|
+
|
|
1929
|
+
#### AchievementStorage (Synchronous)
|
|
1930
|
+
|
|
1931
|
+
```tsx
|
|
1932
|
+
interface AchievementStorage {
|
|
1933
|
+
getMetrics(): AchievementMetrics;
|
|
1934
|
+
setMetrics(metrics: AchievementMetrics): void;
|
|
1935
|
+
getUnlockedAchievements(): string[];
|
|
1936
|
+
setUnlockedAchievements(achievements: string[]): void;
|
|
1937
|
+
clear(): void;
|
|
1938
|
+
}
|
|
1939
|
+
```
|
|
1940
|
+
|
|
1941
|
+
#### AsyncAchievementStorage
|
|
1942
|
+
|
|
1943
|
+
```tsx
|
|
1944
|
+
interface AsyncAchievementStorage {
|
|
1945
|
+
getMetrics(): Promise<AchievementMetrics>;
|
|
1946
|
+
setMetrics(metrics: AchievementMetrics): Promise<void>;
|
|
1947
|
+
getUnlockedAchievements(): Promise<string[]>;
|
|
1948
|
+
setUnlockedAchievements(achievements: string[]): Promise<void>;
|
|
1949
|
+
clear(): Promise<void>;
|
|
1950
|
+
}
|
|
1951
|
+
```
|
|
1952
|
+
|
|
1953
|
+
#### RestApiStorageConfig
|
|
1954
|
+
|
|
1955
|
+
```tsx
|
|
1956
|
+
interface RestApiStorageConfig {
|
|
1957
|
+
baseUrl: string;
|
|
1958
|
+
userId: string;
|
|
1959
|
+
headers?: Record<string, string>;
|
|
1960
|
+
timeout?: number; // milliseconds, default: 10000
|
|
1961
|
+
}
|
|
1962
|
+
```
|
|
1963
|
+
|
|
1964
|
+
**Example:**
|
|
1965
|
+
```tsx
|
|
1966
|
+
const config: RestApiStorageConfig = {
|
|
1967
|
+
baseUrl: 'https://api.example.com',
|
|
1968
|
+
userId: 'user123',
|
|
1969
|
+
headers: {
|
|
1970
|
+
'Authorization': 'Bearer token'
|
|
1971
|
+
},
|
|
1972
|
+
timeout: 15000
|
|
1973
|
+
};
|
|
1974
|
+
```
|
|
1975
|
+
|
|
1976
|
+
### Import/Export Types
|
|
1977
|
+
|
|
1978
|
+
#### ImportOptions
|
|
1979
|
+
|
|
1980
|
+
```tsx
|
|
1981
|
+
interface ImportOptions {
|
|
1982
|
+
strategy?: 'replace' | 'merge' | 'preserve';
|
|
1983
|
+
validate?: boolean;
|
|
1984
|
+
expectedConfigHash?: string;
|
|
1985
|
+
}
|
|
1986
|
+
```
|
|
1987
|
+
|
|
1988
|
+
**Strategies:**
|
|
1989
|
+
- `replace`: Completely replace all existing data
|
|
1990
|
+
- `merge`: Combine imported and existing data (takes maximum values)
|
|
1991
|
+
- `preserve`: Only import new achievements, keep existing data
|
|
1992
|
+
|
|
1993
|
+
#### ImportResult
|
|
1994
|
+
|
|
1995
|
+
```tsx
|
|
1996
|
+
interface ImportResult {
|
|
1997
|
+
success: boolean;
|
|
1998
|
+
errors?: string[];
|
|
1999
|
+
warnings?: string[];
|
|
2000
|
+
imported?: {
|
|
2001
|
+
metrics: number;
|
|
2002
|
+
achievements: number;
|
|
2003
|
+
};
|
|
2004
|
+
mergedMetrics?: AchievementMetrics;
|
|
2005
|
+
mergedUnlocked?: string[];
|
|
2006
|
+
configMismatch?: boolean;
|
|
2007
|
+
}
|
|
2008
|
+
```
|
|
2009
|
+
|
|
2010
|
+
## Common Patterns & Recipes
|
|
2011
|
+
|
|
2012
|
+
Quick reference for common use cases and patterns.
|
|
2013
|
+
|
|
2014
|
+
### Pattern 1: Display Only Unlocked Achievements
|
|
2015
|
+
|
|
2016
|
+
Show users only the achievements they've unlocked:
|
|
2017
|
+
|
|
2018
|
+
```tsx
|
|
2019
|
+
import { useAchievements, BadgesModal } from 'react-achievements';
|
|
2020
|
+
|
|
2021
|
+
function MyComponent() {
|
|
2022
|
+
const { achievements } = useAchievements();
|
|
2023
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
2024
|
+
|
|
2025
|
+
return (
|
|
2026
|
+
<BadgesModal
|
|
2027
|
+
isOpen={isOpen}
|
|
2028
|
+
onClose={() => setIsOpen(false)}
|
|
2029
|
+
achievements={achievements.unlocked} // Only unlocked IDs
|
|
2030
|
+
/>
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
2033
|
+
```
|
|
2034
|
+
|
|
2035
|
+
### Pattern 2: Display All Achievements (Locked + Unlocked)
|
|
2036
|
+
|
|
2037
|
+
Show both locked and unlocked achievements to motivate users:
|
|
2038
|
+
|
|
2039
|
+
```tsx
|
|
2040
|
+
import { useAchievements, BadgesModal } from 'react-achievements';
|
|
2041
|
+
|
|
2042
|
+
function MyComponent() {
|
|
2043
|
+
const { getAllAchievements } = useAchievements();
|
|
2044
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
2045
|
+
|
|
2046
|
+
return (
|
|
2047
|
+
<BadgesModal
|
|
2048
|
+
isOpen={isOpen}
|
|
2049
|
+
onClose={() => setIsOpen(false)}
|
|
2050
|
+
showAllAchievements={true}
|
|
2051
|
+
allAchievements={getAllAchievements()} // ⭐ Required!
|
|
2052
|
+
showUnlockConditions={true} // Show hints
|
|
2053
|
+
/>
|
|
2054
|
+
);
|
|
2055
|
+
}
|
|
2056
|
+
```
|
|
2057
|
+
|
|
2058
|
+
### Pattern 3: Export Achievement Data
|
|
2059
|
+
|
|
2060
|
+
Allow users to download their achievement progress:
|
|
2061
|
+
|
|
2062
|
+
```tsx
|
|
2063
|
+
import { useAchievements } from 'react-achievements';
|
|
2064
|
+
|
|
2065
|
+
function MyComponent() {
|
|
2066
|
+
const { exportData } = useAchievements();
|
|
2067
|
+
|
|
2068
|
+
const handleExport = () => {
|
|
2069
|
+
const jsonString = exportData();
|
|
2070
|
+
|
|
2071
|
+
// Create downloadable file
|
|
2072
|
+
const blob = new Blob([jsonString], { type: 'application/json' });
|
|
2073
|
+
const url = URL.createObjectURL(blob);
|
|
2074
|
+
const link = document.createElement('a');
|
|
2075
|
+
link.href = url;
|
|
2076
|
+
link.download = `achievements-${Date.now()}.json`;
|
|
2077
|
+
link.click();
|
|
2078
|
+
URL.revokeObjectURL(url);
|
|
2079
|
+
};
|
|
2080
|
+
|
|
2081
|
+
return <button onClick={handleExport}>Export Progress</button>;
|
|
2082
|
+
}
|
|
2083
|
+
```
|
|
2084
|
+
|
|
2085
|
+
### Pattern 4: Import Achievement Data
|
|
2086
|
+
|
|
2087
|
+
Restore achievements from a backup:
|
|
2088
|
+
|
|
2089
|
+
```tsx
|
|
2090
|
+
import { useAchievements } from 'react-achievements';
|
|
2091
|
+
|
|
2092
|
+
function MyComponent() {
|
|
2093
|
+
const { importData, update } = useAchievements();
|
|
2094
|
+
|
|
2095
|
+
const handleImport = async (file: File) => {
|
|
2096
|
+
const text = await file.text();
|
|
2097
|
+
const result = importData(text, {
|
|
2098
|
+
strategy: 'merge', // Combine with existing data
|
|
2099
|
+
validate: true
|
|
2100
|
+
});
|
|
2101
|
+
|
|
2102
|
+
if (result.success && result.mergedMetrics) {
|
|
2103
|
+
update(result.mergedMetrics);
|
|
2104
|
+
alert(`Imported ${result.imported?.achievements} achievements!`);
|
|
2105
|
+
} else {
|
|
2106
|
+
alert('Import failed: ' + result.errors?.join(', '));
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
2109
|
+
|
|
2110
|
+
return (
|
|
2111
|
+
<input
|
|
2112
|
+
type="file"
|
|
2113
|
+
accept=".json"
|
|
2114
|
+
onChange={(e) => e.target.files?.[0] && handleImport(e.target.files[0])}
|
|
2115
|
+
/>
|
|
2116
|
+
);
|
|
2117
|
+
}
|
|
2118
|
+
```
|
|
2119
|
+
|
|
2120
|
+
### Pattern 5: Get Current Metrics
|
|
2121
|
+
|
|
2122
|
+
Check achievement progress programmatically:
|
|
2123
|
+
|
|
2124
|
+
```tsx
|
|
2125
|
+
import { useAchievements } from 'react-achievements';
|
|
2126
|
+
|
|
2127
|
+
function MyComponent() {
|
|
2128
|
+
const { getState } = useAchievements();
|
|
2129
|
+
|
|
2130
|
+
const handleCheckProgress = () => {
|
|
2131
|
+
const state = getState();
|
|
2132
|
+
console.log('Current metrics:', state.metrics);
|
|
2133
|
+
console.log('Unlocked achievements:', state.unlocked);
|
|
2134
|
+
console.log('Total unlocked:', state.unlocked.length);
|
|
2135
|
+
};
|
|
2136
|
+
|
|
2137
|
+
return <button onClick={handleCheckProgress}>Check Progress</button>;
|
|
2138
|
+
}
|
|
2139
|
+
```
|
|
2140
|
+
|
|
2141
|
+
### Pattern 6: Track Complex Events
|
|
2142
|
+
|
|
2143
|
+
Handle achievements based on multiple conditions:
|
|
2144
|
+
|
|
2145
|
+
```tsx
|
|
2146
|
+
import { useSimpleAchievements } from 'react-achievements';
|
|
2147
|
+
|
|
2148
|
+
function GameComponent() {
|
|
2149
|
+
const { track, trackMultiple } = useSimpleAchievements();
|
|
2150
|
+
|
|
2151
|
+
const handleLevelComplete = (score: number, time: number, accuracy: number) => {
|
|
2152
|
+
// Track multiple related metrics at once
|
|
2153
|
+
trackMultiple({
|
|
2154
|
+
score: score,
|
|
2155
|
+
completionTime: time,
|
|
2156
|
+
accuracy: accuracy,
|
|
2157
|
+
levelsCompleted: true
|
|
2158
|
+
});
|
|
2159
|
+
|
|
2160
|
+
// Achievements with custom conditions will evaluate all metrics
|
|
2161
|
+
// Example: "Perfect Level" achievement for score > 1000 AND accuracy === 100
|
|
2162
|
+
};
|
|
2163
|
+
|
|
2164
|
+
return <button onClick={() => handleLevelComplete(1200, 45, 100)}>Complete Level</button>;
|
|
2165
|
+
}
|
|
2166
|
+
```
|
|
2167
|
+
|
|
2168
|
+
### Pattern 7: Reset Progress
|
|
2169
|
+
|
|
2170
|
+
Clear all achievement data:
|
|
2171
|
+
|
|
2172
|
+
```tsx
|
|
2173
|
+
import { useAchievements } from 'react-achievements';
|
|
2174
|
+
|
|
2175
|
+
function SettingsComponent() {
|
|
2176
|
+
const { reset } = useAchievements();
|
|
2177
|
+
|
|
2178
|
+
const handleReset = () => {
|
|
2179
|
+
if (confirm('Are you sure? This will delete all achievement progress.')) {
|
|
2180
|
+
reset();
|
|
2181
|
+
alert('All achievements have been reset!');
|
|
2182
|
+
}
|
|
2183
|
+
};
|
|
2184
|
+
|
|
2185
|
+
return <button onClick={handleReset}>Reset All Achievements</button>;
|
|
2186
|
+
}
|
|
2187
|
+
```
|
|
1320
2188
|
|
|
1321
2189
|
## API Reference
|
|
1322
2190
|
|
|
@@ -1332,11 +2200,243 @@ The achievement components use default styling that works well out of the box. F
|
|
|
1332
2200
|
|
|
1333
2201
|
### useAchievements Hook
|
|
1334
2202
|
|
|
1335
|
-
|
|
2203
|
+
The `useAchievements` hook provides access to all achievement functionality. It must be used within an `AchievementProvider`.
|
|
2204
|
+
|
|
2205
|
+
```tsx
|
|
2206
|
+
const {
|
|
2207
|
+
update,
|
|
2208
|
+
achievements,
|
|
2209
|
+
reset,
|
|
2210
|
+
getState,
|
|
2211
|
+
exportData,
|
|
2212
|
+
importData,
|
|
2213
|
+
getAllAchievements
|
|
2214
|
+
} = useAchievements();
|
|
2215
|
+
```
|
|
2216
|
+
|
|
2217
|
+
#### Methods
|
|
2218
|
+
|
|
2219
|
+
| Method | Signature | Description |
|
|
2220
|
+
|--------|-----------|-------------|
|
|
2221
|
+
| `update` | `(metrics: Record<string, any>) => void` | Update one or more achievement metrics. Triggers achievement evaluation. |
|
|
2222
|
+
| `achievements` | `{ unlocked: string[]; all: Record<string, any> }` | Object containing arrays of unlocked achievement IDs and the full configuration. |
|
|
2223
|
+
| `reset` | `() => void` | Clear all achievement data including metrics and unlock history. |
|
|
2224
|
+
| `getState` | `() => { metrics: AchievementMetrics; unlocked: string[] }` | Get current state with metrics (in array format) and unlocked IDs. |
|
|
2225
|
+
| `exportData` | `() => string` | Export all achievement data as JSON string for backup/transfer. |
|
|
2226
|
+
| `importData` | `(jsonString: string, options?: ImportOptions) => ImportResult` | Import previously exported data with merge strategies. |
|
|
2227
|
+
| `getAllAchievements` | `() => AchievementWithStatus[]` | **Get all achievements with unlock status. Required for `BadgesModal` when showing locked achievements.** |
|
|
2228
|
+
|
|
2229
|
+
#### Method Details
|
|
2230
|
+
|
|
2231
|
+
**`update(metrics: Record<string, any>): void`**
|
|
2232
|
+
|
|
2233
|
+
Update achievement metrics and trigger evaluation of achievement conditions.
|
|
2234
|
+
|
|
2235
|
+
```tsx
|
|
2236
|
+
// Single metric
|
|
2237
|
+
update({ score: 100 });
|
|
2238
|
+
|
|
2239
|
+
// Multiple metrics
|
|
2240
|
+
update({ score: 500, level: 10 });
|
|
2241
|
+
```
|
|
2242
|
+
|
|
2243
|
+
**`achievements: { unlocked: string[]; all: Record<string, any> }`**
|
|
2244
|
+
|
|
2245
|
+
Object containing current achievement state.
|
|
2246
|
+
|
|
2247
|
+
```tsx
|
|
2248
|
+
// Get unlocked achievement IDs
|
|
2249
|
+
console.log(achievements.unlocked); // ['score_100', 'level_5']
|
|
2250
|
+
|
|
2251
|
+
// Get count of unlocked achievements
|
|
2252
|
+
const count = achievements.unlocked.length;
|
|
2253
|
+
```
|
|
2254
|
+
|
|
2255
|
+
**`reset(): void`**
|
|
2256
|
+
|
|
2257
|
+
Clear all achievement data including metrics, unlocked achievements, and notification history.
|
|
2258
|
+
|
|
2259
|
+
```tsx
|
|
2260
|
+
// Reset all achievements
|
|
2261
|
+
reset();
|
|
2262
|
+
```
|
|
2263
|
+
|
|
2264
|
+
**`getState(): { metrics: AchievementMetrics; unlocked: string[] }`**
|
|
2265
|
+
|
|
2266
|
+
Get the current achievement state including metrics and unlocked achievement IDs.
|
|
2267
|
+
|
|
2268
|
+
```tsx
|
|
2269
|
+
const state = getState();
|
|
2270
|
+
console.log('Current metrics:', state.metrics);
|
|
2271
|
+
console.log('Unlocked achievements:', state.unlocked);
|
|
2272
|
+
```
|
|
2273
|
+
|
|
2274
|
+
**Note:** Metrics are returned in array format (e.g., `{ score: [100] }`) even if you passed scalar values.
|
|
2275
|
+
|
|
2276
|
+
**`exportData(): string`**
|
|
2277
|
+
|
|
2278
|
+
Export all achievement data as a JSON string for backup or transfer.
|
|
1336
2279
|
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
2280
|
+
```tsx
|
|
2281
|
+
const jsonString = exportData();
|
|
2282
|
+
|
|
2283
|
+
// Save to file
|
|
2284
|
+
const blob = new Blob([jsonString], { type: 'application/json' });
|
|
2285
|
+
const url = URL.createObjectURL(blob);
|
|
2286
|
+
const link = document.createElement('a');
|
|
2287
|
+
link.href = url;
|
|
2288
|
+
link.download = `achievements-${Date.now()}.json`;
|
|
2289
|
+
link.click();
|
|
2290
|
+
```
|
|
2291
|
+
|
|
2292
|
+
**`importData(jsonString: string, options?: ImportOptions): ImportResult`**
|
|
2293
|
+
|
|
2294
|
+
Import previously exported achievement data.
|
|
2295
|
+
|
|
2296
|
+
```tsx
|
|
2297
|
+
const result = importData(jsonString, {
|
|
2298
|
+
strategy: 'merge', // 'replace', 'merge', or 'preserve'
|
|
2299
|
+
validate: true
|
|
2300
|
+
});
|
|
2301
|
+
|
|
2302
|
+
if (result.success) {
|
|
2303
|
+
console.log(`Imported ${result.imported.achievements} achievements`);
|
|
2304
|
+
}
|
|
2305
|
+
```
|
|
2306
|
+
|
|
2307
|
+
**`getAllAchievements(): AchievementWithStatus[]`**
|
|
2308
|
+
|
|
2309
|
+
Returns all achievements (locked and unlocked) with their status. **This is required when using `BadgesModal` with `showAllAchievements={true}`**.
|
|
2310
|
+
|
|
2311
|
+
```tsx
|
|
2312
|
+
const allAchievements = getAllAchievements();
|
|
2313
|
+
// Returns: [
|
|
2314
|
+
// { achievementId: 'score_100', achievementTitle: 'Century!', isUnlocked: true, ... },
|
|
2315
|
+
// { achievementId: 'score_500', achievementTitle: 'High Scorer!', isUnlocked: false, ... }
|
|
2316
|
+
// ]
|
|
2317
|
+
|
|
2318
|
+
// Use with BadgesModal to show all achievements
|
|
2319
|
+
<BadgesModal
|
|
2320
|
+
showAllAchievements={true}
|
|
2321
|
+
allAchievements={allAchievements}
|
|
2322
|
+
// ... other props
|
|
2323
|
+
/>
|
|
2324
|
+
```
|
|
2325
|
+
|
|
2326
|
+
**Note:** Use `achievements.unlocked` for simple cases where you only need IDs. Use `getAllAchievements()` when you need full achievement objects with unlock status.
|
|
2327
|
+
|
|
2328
|
+
### useSimpleAchievements Hook
|
|
2329
|
+
|
|
2330
|
+
Simplified wrapper around `useAchievements` with cleaner API for common use cases.
|
|
2331
|
+
|
|
2332
|
+
```tsx
|
|
2333
|
+
const {
|
|
2334
|
+
track,
|
|
2335
|
+
increment,
|
|
2336
|
+
trackMultiple,
|
|
2337
|
+
unlocked,
|
|
2338
|
+
all,
|
|
2339
|
+
unlockedCount,
|
|
2340
|
+
reset,
|
|
2341
|
+
getState,
|
|
2342
|
+
exportData,
|
|
2343
|
+
importData,
|
|
2344
|
+
getAllAchievements
|
|
2345
|
+
} = useSimpleAchievements();
|
|
2346
|
+
```
|
|
2347
|
+
|
|
2348
|
+
#### Methods
|
|
2349
|
+
|
|
2350
|
+
| Method | Signature | Description |
|
|
2351
|
+
|--------|-----------|-------------|
|
|
2352
|
+
| `track` | `(metric: string, value: any) => void` | Update a single metric value. |
|
|
2353
|
+
| `increment` | `(metric: string, amount?: number) => void` | Increment a metric by amount (default: 1). |
|
|
2354
|
+
| `trackMultiple` | `(metrics: Record<string, any>) => void` | Update multiple metrics at once. |
|
|
2355
|
+
| `unlocked` | `string[]` | Array of unlocked achievement IDs. |
|
|
2356
|
+
| `all` | `Record<string, any>` | All achievements configuration. |
|
|
2357
|
+
| `unlockedCount` | `number` | Number of unlocked achievements. |
|
|
2358
|
+
| `reset` | `() => void` | Clear all achievement data. |
|
|
2359
|
+
| `getState` | `() => { metrics; unlocked }` | Get current state. |
|
|
2360
|
+
| `exportData` | `() => string` | Export data as JSON. |
|
|
2361
|
+
| `importData` | `(json, options) => ImportResult` | Import data. |
|
|
2362
|
+
| `getAllAchievements` | `() => AchievementWithStatus[]` | Get all achievements with status. |
|
|
2363
|
+
|
|
2364
|
+
**Example:**
|
|
2365
|
+
|
|
2366
|
+
```tsx
|
|
2367
|
+
const { track, increment, unlocked, unlockedCount } = useSimpleAchievements();
|
|
2368
|
+
|
|
2369
|
+
// Track single metrics
|
|
2370
|
+
track('score', 100);
|
|
2371
|
+
track('completedTutorial', true);
|
|
2372
|
+
|
|
2373
|
+
// Increment values (great for clicks, actions, etc.)
|
|
2374
|
+
increment('buttonClicks'); // Adds 1
|
|
2375
|
+
increment('score', 50); // Adds 50
|
|
2376
|
+
|
|
2377
|
+
// Check progress
|
|
2378
|
+
console.log(`Unlocked ${unlockedCount} achievements`);
|
|
2379
|
+
console.log('Achievement IDs:', unlocked);
|
|
2380
|
+
```
|
|
2381
|
+
|
|
2382
|
+
### Component Props Reference
|
|
2383
|
+
|
|
2384
|
+
#### BadgesButton Props
|
|
2385
|
+
|
|
2386
|
+
| Prop | Type | Required | Description |
|
|
2387
|
+
|------|------|----------|-------------|
|
|
2388
|
+
| `onClick` | `() => void` | Yes | Handler for button click |
|
|
2389
|
+
| `unlockedAchievements` | `AchievementDetails[]` | Yes | Array of unlocked achievements |
|
|
2390
|
+
| `position` | `'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right'` | No | Fixed position (default: 'bottom-right') |
|
|
2391
|
+
| `placement` | `'fixed' \| 'inline'` | No | Positioning mode (default: 'fixed') |
|
|
2392
|
+
| `theme` | `string \| ThemeConfig` | No | Theme name or custom theme |
|
|
2393
|
+
| `style` | `React.CSSProperties` | No | Custom styles |
|
|
2394
|
+
| `icons` | `Record<string, string>` | No | Custom icon mapping |
|
|
2395
|
+
|
|
2396
|
+
**Example:**
|
|
2397
|
+
```tsx
|
|
2398
|
+
<BadgesButton
|
|
2399
|
+
onClick={() => setModalOpen(true)}
|
|
2400
|
+
unlockedAchievements={achievements.unlocked}
|
|
2401
|
+
position="bottom-right"
|
|
2402
|
+
placement="fixed"
|
|
2403
|
+
theme="modern"
|
|
2404
|
+
/>
|
|
2405
|
+
```
|
|
2406
|
+
|
|
2407
|
+
#### BadgesModal Props
|
|
2408
|
+
|
|
2409
|
+
| Prop | Type | Required | Description |
|
|
2410
|
+
|------|------|----------|-------------|
|
|
2411
|
+
| `isOpen` | `boolean` | Yes | Modal open state |
|
|
2412
|
+
| `onClose` | `() => void` | Yes | Close handler |
|
|
2413
|
+
| `achievements` | `AchievementDetails[]` | Yes* | Unlocked achievements (*not used if `showAllAchievements`) |
|
|
2414
|
+
| `showAllAchievements` | `boolean` | No | Show locked + unlocked (default: false) |
|
|
2415
|
+
| `showUnlockConditions` | `boolean` | No | Show unlock hints (default: false) |
|
|
2416
|
+
| `allAchievements` | `AchievementWithStatus[]` | No* | All achievements with status (*required if `showAllAchievements`) |
|
|
2417
|
+
| `icons` | `Record<string, string>` | No | Custom icon mapping |
|
|
2418
|
+
| `style` | `React.CSSProperties` | No | Custom styles |
|
|
2419
|
+
|
|
2420
|
+
**Example (unlocked only):**
|
|
2421
|
+
```tsx
|
|
2422
|
+
<BadgesModal
|
|
2423
|
+
isOpen={isOpen}
|
|
2424
|
+
onClose={() => setIsOpen(false)}
|
|
2425
|
+
achievements={achievements.unlocked}
|
|
2426
|
+
/>
|
|
2427
|
+
```
|
|
2428
|
+
|
|
2429
|
+
**Example (all achievements):**
|
|
2430
|
+
```tsx
|
|
2431
|
+
const { getAllAchievements } = useAchievements();
|
|
2432
|
+
|
|
2433
|
+
<BadgesModal
|
|
2434
|
+
isOpen={isOpen}
|
|
2435
|
+
onClose={() => setIsOpen(false)}
|
|
2436
|
+
showAllAchievements={true}
|
|
2437
|
+
allAchievements={getAllAchievements()} // Required!
|
|
2438
|
+
/>
|
|
2439
|
+
```
|
|
1340
2440
|
|
|
1341
2441
|
## Advanced: Complex API
|
|
1342
2442
|
|