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