react-achievements 3.0.2 → 3.2.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/README.md CHANGED
@@ -16,43 +16,48 @@ Note: React and React DOM should be version 16.8.0 or higher. If you already hav
16
16
 
17
17
  ## Quick Start
18
18
 
19
- Here's a complete working example that shows automatic notifications and achievement tracking:
19
+ Here's a complete working example using the **new Simple API** that shows automatic notifications and achievement tracking:
20
20
 
21
21
  ```tsx
22
22
  import React, { useState } from 'react';
23
23
  import {
24
24
  AchievementProvider,
25
- useAchievements,
25
+ useSimpleAchievements,
26
26
  BadgesButton,
27
27
  BadgesModal
28
28
  } from 'react-achievements';
29
29
 
30
- // Define a simple achievement
31
- const achievementConfig = {
32
- score: [{
33
- isConditionMet: (value: number) => value >= 100,
34
- achievementDetails: {
35
- achievementId: 'score_100',
36
- achievementTitle: 'Century!',
37
- achievementDescription: 'Score 100 points',
38
- achievementIconKey: 'trophy'
39
- }
40
- }]
41
- };
30
+ // Define achievements with the new three-tier Builder API - 95% less code!
31
+ import { AchievementBuilder } from 'react-achievements';
32
+
33
+ const gameAchievements = AchievementBuilder.combine([
34
+ AchievementBuilder.createScoreAchievements([100, 500]), // Smart defaults
35
+ AchievementBuilder.createLevelAchievement(5).withAward({ title: 'Leveling Up', icon: '📈' }), // Chainable customization
36
+ AchievementBuilder.createBooleanAchievement('completedTutorial').withAward({ title: 'Tutorial Master', icon: '📚' })
37
+ ]);
42
38
 
43
- // Demo component with all essential features
39
+ // Demo component with all essential features
44
40
  const DemoComponent = () => {
45
41
  const [isModalOpen, setIsModalOpen] = useState(false);
46
- const { update, achievements, reset } = useAchievements();
42
+ const { track, unlocked, unlockedCount, reset } = useSimpleAchievements();
47
43
 
48
44
  return (
49
45
  <div>
50
46
  <h1>Achievement Demo</h1>
51
47
 
52
- {/* Button to trigger achievement */}
53
- <button onClick={() => update({ score: 100 })}>
48
+ {/* Simple tracking - much easier! */}
49
+ <button onClick={() => track('score', 100)}>
54
50
  Score 100 points
55
51
  </button>
52
+ <button onClick={() => track('score', 500)}>
53
+ Score 500 points
54
+ </button>
55
+ <button onClick={() => track('level', 5)}>
56
+ Reach level 5
57
+ </button>
58
+ <button onClick={() => track('completedTutorial', true)}>
59
+ Complete tutorial
60
+ </button>
56
61
 
57
62
  {/* Reset button */}
58
63
  <button onClick={reset}>
@@ -60,20 +65,20 @@ const DemoComponent = () => {
60
65
  </button>
61
66
 
62
67
  {/* Shows unlocked achievements count */}
63
- <p>Unlocked: {achievements.unlocked.length}</p>
68
+ <p>Unlocked: {unlockedCount}</p>
64
69
 
65
70
  {/* Floating badges button */}
66
71
  <BadgesButton
67
72
  position="bottom-right"
68
73
  onClick={() => setIsModalOpen(true)}
69
- unlockedAchievements={achievements.unlocked}
74
+ unlockedAchievements={[]} // Simplified for demo
70
75
  />
71
76
 
72
77
  {/* Achievement history modal */}
73
78
  <BadgesModal
74
79
  isOpen={isModalOpen}
75
80
  onClose={() => setIsModalOpen(false)}
76
- achievements={achievements.unlocked}
81
+ achievements={[]} // Simplified for demo
77
82
  />
78
83
  </div>
79
84
  );
@@ -83,7 +88,7 @@ const DemoComponent = () => {
83
88
  const App = () => {
84
89
  return (
85
90
  <AchievementProvider
86
- achievements={achievementConfig}
91
+ achievements={gameAchievements}
87
92
  storage="local"
88
93
  >
89
94
  <DemoComponent />
@@ -95,11 +100,184 @@ export default App;
95
100
  ```
96
101
 
97
102
  When you click "Score 100 points":
98
- 1. A toast notification appears
99
- 2. Confetti animation plays
103
+ 1. A toast notification appears automatically
104
+ 2. Confetti animation plays
100
105
  3. The achievement is stored and visible in the badges modal
101
106
  4. The badges button updates to show the new count
102
107
 
108
+ ## Simple API (Recommended)
109
+
110
+ Perfect for 90% of use cases - threshold-based achievements with minimal configuration:
111
+
112
+ ```tsx
113
+ import { AchievementProvider, useSimpleAchievements } from 'react-achievements';
114
+
115
+ const achievements = {
116
+ // Numeric thresholds
117
+ score: {
118
+ 100: { title: 'Century!', description: 'Score 100 points', icon: '🏆' },
119
+ 500: { title: 'High Scorer!', icon: '⭐' }
120
+ },
121
+
122
+ // Boolean achievements
123
+ completedTutorial: {
124
+ true: { title: 'Tutorial Master', description: 'Complete the tutorial', icon: '📚' }
125
+ },
126
+
127
+ // String-based achievements
128
+ characterClass: {
129
+ wizard: { title: 'Arcane Scholar', description: 'Choose the wizard class', icon: '🧙‍♂️' },
130
+ warrior: { title: 'Battle Hardened', description: 'Choose the warrior class', icon: '⚔️' }
131
+ },
132
+
133
+ // Custom condition functions for complex logic
134
+ combo: {
135
+ custom: {
136
+ title: 'Perfect Combo',
137
+ description: 'Score 1000+ with 100% accuracy',
138
+ icon: '💎',
139
+ condition: (metrics) => metrics.score >= 1000 && metrics.accuracy === 100
140
+ }
141
+ }
142
+ };
143
+
144
+ const { track, unlocked, unlockedCount, reset } = useSimpleAchievements();
145
+
146
+ // Track achievements easily
147
+ track('score', 100); // Unlocks "Century!" achievement
148
+ track('completedTutorial', true); // Unlocks "Tutorial Master"
149
+ track('characterClass', 'wizard'); // Unlocks "Arcane Scholar"
150
+
151
+ // Track multiple metrics for custom conditions
152
+ track('score', 1000);
153
+ track('accuracy', 100); // Unlocks "Perfect Combo" if both conditions met
154
+ ```
155
+
156
+ ### Simple API Comparison Logic
157
+
158
+ When using the Simple API, achievement conditions use different comparison operators depending on the value type:
159
+
160
+ | Value Type | Comparison | Example | When Achievement Unlocks |
161
+ |------------|------------|---------|-------------------------|
162
+ | **Numeric** | `>=` (greater than or equal) | `score: { 100: {...} }` | When `track('score', 100)` or higher |
163
+ | **Boolean** | `===` (strict equality) | `completedTutorial: { true: {...} }` | When `track('completedTutorial', true)` |
164
+ | **String** | `===` (strict equality) | `characterClass: { wizard: {...} }` | When `track('characterClass', 'wizard')` |
165
+
166
+ **Important Notes:**
167
+ - **Numeric achievements** use `>=` comparison, so they unlock when you reach **or exceed** the threshold
168
+ - **Boolean and string achievements** use exact equality matching
169
+ - Custom condition functions have full control over comparison logic
170
+
171
+ **Examples:**
172
+ ```tsx
173
+ // Numeric: Achievement unlocks at 100 or higher
174
+ track('score', 150); // ✅ Unlocks "Century!" (threshold: 100)
175
+ track('score', 99); // ❌ Does not unlock
176
+
177
+ // Boolean: Must match exactly
178
+ track('completedTutorial', true); // ✅ Unlocks achievement
179
+ track('completedTutorial', false); // ❌ Does not unlock
180
+
181
+ // String: Must match exactly
182
+ track('characterClass', 'wizard'); // ✅ Unlocks "Arcane Scholar"
183
+ track('characterClass', 'Wizard'); // ❌ Does not unlock (case sensitive)
184
+ ```
185
+
186
+ ## Three-Tier Builder API
187
+
188
+ The AchievementBuilder provides three levels of complexity to match your needs - from zero-config defaults to full custom logic:
189
+
190
+ ### Tier 1: Smart Defaults (90% of use cases)
191
+
192
+ Zero configuration needed - just specify what you want to track:
193
+
194
+ ```tsx
195
+ import { AchievementBuilder } from 'react-achievements';
196
+
197
+ // Individual achievements with smart defaults
198
+ AchievementBuilder.createScoreAchievement(100); // "Score 100!" + 🏆
199
+ AchievementBuilder.createLevelAchievement(5); // "Level 5!" + 📈
200
+ AchievementBuilder.createBooleanAchievement('completedTutorial'); // "Completed Tutorial!" + ✅
201
+
202
+ // Bulk creation with smart defaults
203
+ AchievementBuilder.createScoreAchievements([100, 500, 1000]);
204
+ AchievementBuilder.createLevelAchievements([5, 10, 25]);
205
+
206
+ // Mixed: some defaults, some custom awards
207
+ const achievements = AchievementBuilder.createScoreAchievements([
208
+ 100, // Uses default "Score 100!" + 🏆
209
+ [500, { title: 'High Scorer!', icon: '⭐' }], // Custom award
210
+ 1000 // Uses default "Score 1000!" + 🏆
211
+ ]);
212
+ ```
213
+
214
+ ### Tier 2: Chainable Customization
215
+
216
+ Start with defaults, then customize awards as needed:
217
+
218
+ ```tsx
219
+ // Individual achievements with custom awards
220
+ const achievements = AchievementBuilder.combine([
221
+ AchievementBuilder.createScoreAchievement(100)
222
+ .withAward({ title: 'Century!', description: 'Amazing score!', icon: '🏆' }),
223
+
224
+ AchievementBuilder.createLevelAchievement(5)
225
+ .withAward({ title: 'Getting Started', icon: '🌱' }),
226
+
227
+ AchievementBuilder.createBooleanAchievement('completedTutorial')
228
+ .withAward({ title: 'Tutorial Master', description: 'You did it!', icon: '📚' }),
229
+
230
+ AchievementBuilder.createValueAchievement('characterClass', 'wizard')
231
+ .withAward({ title: 'Arcane Scholar', icon: '🧙‍♂️' })
232
+ ]);
233
+ ```
234
+
235
+ ### Tier 3: Full Control for Complex Logic
236
+
237
+ Complete control over achievement conditions for power users:
238
+
239
+ ```tsx
240
+ // Handle complex scenarios like Date, null, undefined values
241
+ const complexAchievement = AchievementBuilder.create()
242
+ .withId('weekly_login')
243
+ .withMetric('lastLoginDate')
244
+ .withCondition((value, state) => {
245
+ // Handle all possible value types
246
+ if (value === null || value === undefined) return false;
247
+ if (value instanceof Date) {
248
+ return value.getTime() > Date.now() - (7 * 24 * 60 * 60 * 1000);
249
+ }
250
+ return false;
251
+ })
252
+ .withAward({
253
+ title: 'Weekly Warrior',
254
+ description: 'Logged in within the last week',
255
+ icon: '📅'
256
+ })
257
+ .build();
258
+
259
+ // Multiple complex achievements
260
+ const advancedAchievements = AchievementBuilder.combine([
261
+ complexAchievement,
262
+ AchievementBuilder.create()
263
+ .withId('perfect_combo')
264
+ .withMetric('gameState')
265
+ .withCondition((value, state) => {
266
+ return state.score >= 1000 && state.accuracy === 100;
267
+ })
268
+ .withAward({ title: 'Perfect!', icon: '💎' })
269
+ .build()
270
+ ]);
271
+ ```
272
+
273
+ ### Key Benefits
274
+ - **Progressive complexity**: Start simple, add complexity only when needed
275
+ - **Zero configuration**: Works out of the box with smart defaults
276
+ - **Chainable customization**: Fine-tune awards without changing logic
277
+ - **Type-safe**: Full TypeScript support for complex conditions
278
+ - **Handles edge cases**: Date, null, undefined values in Tier 3
279
+ - **Combinable**: Mix and match different tiers in one configuration
280
+
103
281
  ## State Management Options
104
282
 
105
283
  This package includes example implementations for different state management solutions in the `stories/examples` directory:
@@ -155,156 +333,31 @@ To allow users to view their achievement history, the package provides two essen
155
333
 
156
334
  These components are the recommended way to give users access to their achievement history. While you could build custom UI using the `useAchievements` hook data, these components provide a polished, ready-to-use interface for achievement history.
157
335
 
158
- ## Basic Usage
159
-
160
- ```tsx
161
- import { AchievementProvider, useAchievements } from 'react-achievements';
162
- // For specific state management implementations:
163
- // import { AchievementProvider, useAchievements } from 'react-achievements/redux';
164
- // import { AchievementProvider, useAchievements } from 'react-achievements/zustand';
165
- // import { AchievementProvider, useAchievements } from 'react-achievements/context';
166
-
167
- // Define your achievements with various data types and conditions
168
- const achievements = {
169
- // Numeric achievements with thresholds
170
- score: {
171
- 100: {
172
- title: 'Century!',
173
- description: 'Score 100 points',
174
- icon: 'trophy'
175
- },
176
- 500: {
177
- title: 'Half a Thousand!',
178
- description: 'Score 500 points',
179
- icon: 'gold',
180
- condition: (value) => value >= 500
181
- }
182
- },
183
-
184
- // Boolean achievements
185
- completedTutorial: {
186
- true: {
187
- title: 'Tutorial Master',
188
- description: 'Complete the tutorial',
189
- icon: 'book'
190
- }
191
- },
192
-
193
- // String-based achievements
194
- characterClass: {
195
- 'wizard': {
196
- title: 'Arcane Scholar',
197
- description: 'Choose the wizard class',
198
- icon: 'wand'
199
- },
200
- 'warrior': {
201
- title: 'Battle Hardened',
202
- description: 'Choose the warrior class',
203
- icon: 'sword'
204
- }
205
- },
206
-
207
- // Array-based achievements
208
- collectedItems: {
209
- ['sword', 'shield', 'potion']: {
210
- title: 'Fully Equipped',
211
- description: 'Collect all essential items',
212
- icon: 'backpack',
213
- condition: (items) => ['sword', 'shield', 'potion'].every(item => items.includes(item))
214
- }
215
- },
216
-
217
- // Object-based achievements
218
- playerStats: {
219
- { strength: 10, intelligence: 10 }: {
220
- title: 'Balanced Warrior',
221
- description: 'Achieve balanced stats',
222
- icon: 'scale',
223
- condition: (stats) => stats.strength === 10 && stats.intelligence === 10
224
- }
225
- },
226
-
227
- // Time-based achievements
228
- playTime: {
229
- 3600: {
230
- title: 'Dedicated Player',
231
- description: 'Play for 1 hour',
232
- icon: 'clock',
233
- condition: (seconds) => seconds >= 3600
234
- }
235
- },
236
-
237
- // Combination achievements
238
- combo: {
239
- { score: 1000, level: 5 }: {
240
- title: 'Rising Star',
241
- description: 'Reach level 5 with 1000 points',
242
- icon: 'star',
243
- condition: (metrics) => metrics.score >= 1000 && metrics.level >= 5
244
- }
245
- }
246
- };
247
-
248
- // Create your app component
249
- const App = () => {
250
- return (
251
- <AchievementProvider
252
- achievements={achievements}
253
- storage="local" // or "memory" or custom storage
254
- >
255
- <Game />
256
- </AchievementProvider>
257
- );
258
- };
259
-
260
- // Use achievements in your components
261
- const Game = () => {
262
- const { update, achievements } = useAchievements();
263
-
264
- const handleScoreUpdate = (newScore: number) => {
265
- update({ score: newScore });
266
- };
267
-
268
- return (
269
- <div>
270
- <h1>Game</h1>
271
- <p>Unlocked Achievements: {achievements.unlocked.length}</p>
272
- <button onClick={() => handleScoreUpdate(100)}>
273
- Score 100 points
274
- </button>
275
- </div>
276
- );
277
- };
278
- ```
279
336
 
280
337
  ## Default Icons
281
338
 
282
339
  The package comes with a comprehensive set of default icons that you can use in your achievements. These are available through the `defaultAchievementIcons` export:
283
340
 
284
341
  ```tsx
285
- import { AchievementProvider, defaultAchievementIcons } from 'react-achievements-core';
286
-
287
- // Example achievement configuration using default icons
288
- const achievementConfig = {
289
- pageViews: [
290
- {
291
- isConditionMet: (value) => value >= 5,
292
- achievementDetails: {
293
- achievementId: 'views-5',
294
- achievementTitle: 'Getting Started',
295
- achievementDescription: 'Viewed 5 pages',
296
- achievementIconKey: 'firstStep' // This will use the 👣 emoji from defaultAchievementIcons
297
- }
342
+ import { AchievementProvider } from 'react-achievements';
343
+
344
+ // Example achievement configuration using direct emoji icons
345
+ const achievements = {
346
+ pageViews: {
347
+ 5: {
348
+ title: 'Getting Started',
349
+ description: 'Viewed 5 pages',
350
+ icon: '👣'
298
351
  }
299
- ]
352
+ }
300
353
  };
301
354
 
302
355
  // Create your app component
303
356
  const App = () => {
304
357
  return (
305
358
  <AchievementProvider
306
- achievements={achievementConfig}
307
- // The provider automatically uses defaultAchievementIcons
359
+ achievements={achievements}
360
+ storage="local"
308
361
  >
309
362
  <Game />
310
363
  </AchievementProvider>
@@ -312,111 +365,101 @@ const App = () => {
312
365
  };
313
366
  ```
314
367
 
315
- ### Custom Icons
368
+ ### Using Icons
316
369
 
317
- You can also provide your own custom icons that will override or extend the default ones:
370
+ The Simple API makes icon usage straightforward - just include emojis directly in your achievement definitions:
318
371
 
319
372
  ```tsx
320
- import { AchievementProvider, defaultAchievementIcons } from 'react-achievements-core';
321
-
322
- // Create custom icons by extending the defaults
323
- const customIcons = {
324
- ...defaultAchievementIcons, // Include all default icons
325
- levelUp: '🚀', // Override the default for 'levelUp'
326
- myCustomIcon: '💻' // Add a new icon not in the defaults
327
- };
328
-
329
- const App = () => {
330
- return (
331
- <AchievementProvider
332
- achievements={achievementConfig}
333
- icons={customIcons} // Pass your custom icons to the provider
334
- >
335
- <Game />
336
- </AchievementProvider>
337
- );
373
+ const achievements = {
374
+ score: {
375
+ 100: { title: 'Century!', icon: '🏆' },
376
+ 500: { title: 'High Scorer!', icon: '⭐' },
377
+ 1000: { title: 'Elite Player!', icon: '💎' }
378
+ },
379
+ level: {
380
+ 5: { title: 'Getting Started', icon: '🌱' },
381
+ 10: { title: 'Rising Star', icon: '🚀' },
382
+ 25: { title: 'Expert', icon: '👑' }
383
+ }
338
384
  };
339
385
  ```
340
386
 
341
- ### Available Icons
387
+ ### Fallback Icons
342
388
 
343
- The `defaultAchievementIcons` includes icons in these categories:
344
-
345
- - General Progress & Milestones (levelUp, questComplete, etc.)
346
- - Social & Engagement (shared, liked, etc.)
347
- - Time & Activity (activeDay, streak, etc.)
348
- - Creativity & Skill (artist, expert, etc.)
349
- - Achievement Types (bronze, silver, gold, etc.)
350
- - Numbers & Counters (one, ten, hundred, etc.)
351
- - Actions & Interactions (clicked, discovered, etc.)
352
- - Placeholders (default, loading, error, etc.)
353
- - Miscellaneous (trophy, star, gem, etc.)
389
+ The library provides a small set of essential fallback icons for system use (error states, loading, etc.). These are automatically used when needed and don't require any configuration.
354
390
 
355
391
  ## Custom Storage
356
392
 
357
393
  You can implement your own storage solution by implementing the `AchievementStorage` interface:
358
394
 
359
- ```typescript
360
- import { AchievementStorage } from 'react-achievements-core';
395
+ ```tsx
396
+ import { AchievementStorage, AchievementMetrics, AchievementProvider } from 'react-achievements';
361
397
 
362
398
  class CustomStorage implements AchievementStorage {
363
- getMetrics() {
399
+ getMetrics(): AchievementMetrics {
364
400
  // Your implementation
401
+ return {};
365
402
  }
366
403
 
367
- setMetrics(metrics) {
404
+ setMetrics(metrics: AchievementMetrics): void {
368
405
  // Your implementation
369
406
  }
370
407
 
371
- getUnlockedAchievements() {
408
+ getUnlockedAchievements(): string[] {
372
409
  // Your implementation
410
+ return [];
373
411
  }
374
412
 
375
- setUnlockedAchievements(achievements) {
413
+ setUnlockedAchievements(achievements: string[]): void {
376
414
  // Your implementation
377
415
  }
378
416
 
379
- clear() {
417
+ clear(): void {
380
418
  // Your implementation
381
419
  }
382
420
  }
383
421
 
384
422
  // Use your custom storage
423
+ const gameAchievements = {
424
+ score: {
425
+ 100: { title: 'Century!', icon: '🏆' }
426
+ }
427
+ };
428
+
385
429
  const App = () => {
386
- return (
387
- <AchievementProvider
388
- achievements={achievements}
389
- storage={new CustomStorage()}
390
- >
391
- <Game />
392
- </AchievementProvider>
393
- );
430
+ return (
431
+ <AchievementProvider
432
+ achievements={gameAchievements}
433
+ storage={new CustomStorage()} // Use your custom storage implementation
434
+ >
435
+ </AchievementProvider>
436
+ );
394
437
  };
438
+
439
+ export default App;
395
440
  ```
396
441
 
397
442
  ## Styling
398
443
 
399
- You can customize the appearance of the achievement components:
444
+ The achievement components use default styling that works well out of the box. For custom styling, you can override the CSS classes or customize individual component props:
400
445
 
401
446
  ```tsx
402
- const App = () => {
403
- return (
404
- <AchievementProvider
405
- achievements={achievements}
406
- theme={{
407
- colors: {
408
- primary: '#ff0000',
409
- background: '#f0f0f0'
410
- },
411
- position: 'top-right'
412
- }}
413
- >
414
- <Game />
415
- </AchievementProvider>
416
- );
417
- };
447
+ // Individual component styling
448
+ <BadgesButton
449
+ position="bottom-right"
450
+ style={{ backgroundColor: '#ff0000' }}
451
+ unlockedAchievements={achievements.unlocked}
452
+ />
453
+
454
+ <BadgesModal
455
+ isOpen={isModalOpen}
456
+ onClose={() => setIsModalOpen(false)}
457
+ achievements={achievements.unlocked}
458
+ style={{ backgroundColor: '#f0f0f0' }}
459
+ />
418
460
  ```
419
461
 
462
+
420
463
  ## API Reference
421
464
 
422
465
  ### AchievementProvider Props
@@ -436,6 +479,76 @@ Returns an object with:
436
479
  - `achievements`: Object containing unlocked and locked achievements
437
480
  - `reset`: Function to reset achievement storage
438
481
 
482
+ ## Advanced: Complex API
483
+
484
+ For complex scenarios requiring full control over achievement logic, you can use the traditional Complex API with POJO (Plain Old JavaScript Object) configurations:
485
+
486
+ ```tsx
487
+ import { AchievementProvider, useAchievements } from 'react-achievements';
488
+
489
+ // Define your achievements using the traditional complex format
490
+ const achievements = {
491
+ score: [{
492
+ isConditionMet: (value: AchievementMetricArrayValue, state: AchievementState) => {
493
+ const numValue = Array.isArray(value) ? value[0] : value;
494
+ return typeof numValue === 'number' && numValue >= 100;
495
+ },
496
+ achievementDetails: {
497
+ achievementId: 'score_100',
498
+ achievementTitle: 'Century!',
499
+ achievementDescription: 'Score 100 points',
500
+ achievementIconKey: 'trophy'
501
+ }
502
+ }],
503
+
504
+ completedTutorial: [{
505
+ isConditionMet: (value: AchievementMetricArrayValue, state: AchievementState) => {
506
+ const boolValue = Array.isArray(value) ? value[0] : value;
507
+ return typeof boolValue === 'boolean' && boolValue === true;
508
+ },
509
+ achievementDetails: {
510
+ achievementId: 'tutorial_complete',
511
+ achievementTitle: 'Tutorial Master',
512
+ achievementDescription: 'Complete the tutorial',
513
+ achievementIconKey: 'book'
514
+ }
515
+ }]
516
+ };
517
+
518
+ // Create your app component
519
+ const App = () => {
520
+ return (
521
+ <AchievementProvider
522
+ achievements={achievements}
523
+ storage="local" // or "memory" or custom storage
524
+ >
525
+ <Game />
526
+ </AchievementProvider>
527
+ );
528
+ };
529
+
530
+ // Use achievements in your components
531
+ const Game = () => {
532
+ const { update, achievements } = useAchievements();
533
+
534
+ const handleScoreUpdate = (newScore: number) => {
535
+ update({ score: newScore });
536
+ };
537
+
538
+ return (
539
+ <div>
540
+ <h1>Game</h1>
541
+ <p>Unlocked Achievements: {achievements.unlocked.length}</p>
542
+ <button onClick={() => handleScoreUpdate(100)}>
543
+ Score 100 points
544
+ </button>
545
+ </div>
546
+ );
547
+ };
548
+ ```
549
+
550
+ This API provides maximum flexibility for complex achievement logic but requires more verbose configuration. Most users should use the Simple API or Builder API instead.
551
+
439
552
  ## License
440
553
 
441
554
  MIT