react-achievements 2.2.2 → 3.0.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.
Files changed (77) hide show
  1. package/README.md +377 -401
  2. package/dist/index.d.ts +222 -8
  3. package/dist/index.js +631 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/stories/Button.d.ts +28 -0
  6. package/dist/stories/Button.stories.d.ts +23 -0
  7. package/dist/stories/Header.d.ts +13 -0
  8. package/dist/stories/Header.stories.d.ts +18 -0
  9. package/dist/stories/Page.d.ts +3 -0
  10. package/dist/stories/Page.stories.d.ts +12 -0
  11. package/dist/types/__mocks__/confetti-wrapper.d.ts +5 -0
  12. package/dist/types/__mocks__/react-confetti.d.ts +3 -0
  13. package/dist/types/__mocks__/react-toastify.d.ts +13 -0
  14. package/dist/types/core/components/BadgesButton.d.ts +10 -0
  15. package/dist/types/core/components/BadgesModal.d.ts +11 -0
  16. package/dist/types/core/components/ConfettiWrapper.d.ts +6 -0
  17. package/dist/types/core/context/AchievementContext.d.ts +5 -0
  18. package/dist/types/core/icons/defaultIcons.d.ts +81 -0
  19. package/dist/types/core/storage/LocalStorage.d.ts +16 -0
  20. package/dist/types/core/storage/MemoryStorage.d.ts +11 -0
  21. package/dist/types/core/styles/defaultStyles.d.ts +2 -0
  22. package/dist/types/core/types.d.ts +74 -0
  23. package/dist/types/hooks/useAchievements.d.ts +1 -0
  24. package/dist/types/index.d.ts +10 -0
  25. package/dist/types/providers/AchievementProvider.d.ts +24 -0
  26. package/dist/types/setupTests.d.ts +1 -0
  27. package/dist/types/stories/Button.d.ts +16 -0
  28. package/dist/types/stories/Button.stories.d.ts +23 -0
  29. package/dist/types/stories/Header.d.ts +13 -0
  30. package/dist/types/stories/Header.stories.d.ts +18 -0
  31. package/dist/types/stories/Page.d.ts +3 -0
  32. package/dist/types/stories/Page.stories.d.ts +12 -0
  33. package/package.json +73 -54
  34. package/.idea/jsLibraryMappings.xml +0 -6
  35. package/.idea/modules.xml +0 -8
  36. package/.idea/react-achievements.iml +0 -12
  37. package/.idea/vcs.xml +0 -6
  38. package/coverage/clover.xml +0 -131
  39. package/coverage/coverage-final.json +0 -9
  40. package/coverage/lcov-report/base.css +0 -224
  41. package/coverage/lcov-report/block-navigation.js +0 -87
  42. package/coverage/lcov-report/favicon.png +0 -0
  43. package/coverage/lcov-report/index.html +0 -146
  44. package/coverage/lcov-report/prettify.css +0 -1
  45. package/coverage/lcov-report/prettify.js +0 -2
  46. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  47. package/coverage/lcov-report/sorter.js +0 -196
  48. package/coverage/lcov-report/src/components/AchievementModal.tsx.html +0 -229
  49. package/coverage/lcov-report/src/components/BadgesButton.tsx.html +0 -169
  50. package/coverage/lcov-report/src/components/BadgesModal.tsx.html +0 -253
  51. package/coverage/lcov-report/src/components/ConfettiWrapper.tsx.html +0 -157
  52. package/coverage/lcov-report/src/components/index.html +0 -161
  53. package/coverage/lcov-report/src/context/AchievementContext.tsx.html +0 -505
  54. package/coverage/lcov-report/src/context/index.html +0 -116
  55. package/coverage/lcov-report/src/index.html +0 -146
  56. package/coverage/lcov-report/src/index.ts.html +0 -121
  57. package/coverage/lcov-report/src/react-confetti.d.ts.html +0 -139
  58. package/coverage/lcov-report/src/react-use.d.ts.html +0 -94
  59. package/coverage/lcov.info +0 -240
  60. package/demo/src/AchievementConfig.ts +0 -0
  61. package/public/badges/icon1.svg +0 -1
  62. package/rollup.config.mjs +0 -34
  63. package/src/assets/defaultIcons.ts +0 -100
  64. package/src/components/BadgesButton.tsx +0 -49
  65. package/src/components/BadgesModal.tsx +0 -47
  66. package/src/components/ConfettiWrapper.tsx +0 -17
  67. package/src/defaultStyles.ts +0 -86
  68. package/src/hooks/useAchievement.ts +0 -17
  69. package/src/index.ts +0 -25
  70. package/src/providers/AchievementProvider.tsx +0 -202
  71. package/src/react-confetti.d.ts +0 -19
  72. package/src/react-use.d.ts +0 -4
  73. package/src/redux/achievementSlice.ts +0 -116
  74. package/src/redux/notificationSlice.ts +0 -26
  75. package/src/redux/store.ts +0 -16
  76. package/src/types.ts +0 -39
  77. package/tsconfig.json +0 -113
package/README.md CHANGED
@@ -1,463 +1,439 @@
1
- <h1 align="center">🏆 React-Achievements 🏆</h1>
1
+ # React Achievements
2
2
 
3
- <p align="center">A flexible and customizable achievement system for React applications, perfect for adding gamification elements to your projects.</p>
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
- ![React Achievements Demo](https://media.giphy.com/media/5sXoITml136LmyBPEc/giphy.gif)
5
+ <p align="center">
6
+ <img src="https://media4.giphy.com/media/v1.Y2lkPTc5MGI3NjExbnMxdHVqanZvbGR6czJqOTdpejZqc3F3NXh6a2FiM3lmdnB0d3VoOSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/LYXAZelQMeeYpzbgtT/giphy.gif" alt="React Achievements Demo" width="600" />
7
+ </p>
6
8
 
7
- <p align="center">If you want to test the package, you can try it out here:</p>
9
+ ## Quick Start
8
10
 
9
- <p align="center">https://stackblitz.com/edit/vitejs-vite-sccdux</p>
11
+ Here's a complete working example that shows automatic notifications and achievement tracking:
10
12
 
11
- <h2 align="center">🚀 Installation</h2>
13
+ ```tsx
14
+ import React, { useState } from 'react';
15
+ import {
16
+ AchievementProvider,
17
+ useAchievements,
18
+ BadgesButton,
19
+ BadgesModal
20
+ } from 'react-achievements';
21
+
22
+ // Define a simple achievement
23
+ const achievementConfig = {
24
+ score: [{
25
+ isConditionMet: (value: number) => value >= 100,
26
+ achievementDetails: {
27
+ achievementId: 'score_100',
28
+ achievementTitle: 'Century!',
29
+ achievementDescription: 'Score 100 points',
30
+ achievementIconKey: 'trophy'
31
+ }
32
+ }]
33
+ };
12
34
 
13
- Install `react-achievements` and its peer dependencies using npm or yarn:
35
+ // Demo component with all essential features
36
+ const DemoComponent = () => {
37
+ const [isModalOpen, setIsModalOpen] = useState(false);
38
+ const { update, achievements, reset } = useAchievements();
39
+
40
+ return (
41
+ <div>
42
+ <h1>Achievement Demo</h1>
43
+
44
+ {/* Button to trigger achievement */}
45
+ <button onClick={() => update({ score: 100 })}>
46
+ Score 100 points
47
+ </button>
48
+
49
+ {/* Reset button */}
50
+ <button onClick={reset}>
51
+ Reset Achievements
52
+ </button>
53
+
54
+ {/* Shows unlocked achievements count */}
55
+ <p>Unlocked: {achievements.unlocked.length}</p>
56
+
57
+ {/* Floating badges button */}
58
+ <BadgesButton
59
+ position="bottom-right"
60
+ onClick={() => setIsModalOpen(true)}
61
+ unlockedAchievements={achievements.unlocked}
62
+ />
63
+
64
+ {/* Achievement history modal */}
65
+ <BadgesModal
66
+ isOpen={isModalOpen}
67
+ onClose={() => setIsModalOpen(false)}
68
+ achievements={achievements.unlocked}
69
+ />
70
+ </div>
71
+ );
72
+ };
14
73
 
15
- ```bash
16
- npm install react-achievements @reduxjs/toolkit react-redux react-toastify react-confetti react-use
74
+ // Root component with provider
75
+ const App = () => {
76
+ return (
77
+ <AchievementProvider
78
+ achievements={achievementConfig}
79
+ storage="local"
80
+ >
81
+ <DemoComponent />
82
+ </AchievementProvider>
83
+ );
84
+ };
85
+
86
+ export default App;
17
87
  ```
18
88
 
19
- or
89
+ When you click "Score 100 points":
90
+ 1. A toast notification appears
91
+ 2. Confetti animation plays
92
+ 3. The achievement is stored and visible in the badges modal
93
+ 4. The badges button updates to show the new count
20
94
 
21
- ```bash
22
- yarn add react-achievements @reduxjs/toolkit react-redux react-toastify react-confetti react-use
23
- ```
95
+ ## State Management Options
24
96
 
25
- <h2 align="center">🎮 Usage</h2>
97
+ This package includes example implementations for different state management solutions in the `stories/examples` directory:
26
98
 
27
- Let's walk through setting up a simple RPG-style game with achievements.
99
+ - **Redux**: For large applications with complex state management needs
100
+ - **Zustand**: For applications needing a lightweight, modern state solution
101
+ - **Context API**: For applications preferring React's built-in solutions
28
102
 
29
- <h3 align="center">🛠 Set up the AchievementProvider</h3>
103
+ See the [examples directory](./stories/examples) for detailed implementations and instructions for each state management solution.
30
104
 
31
- First, wrap your app or a part of your app with the AchievementProvider:
105
+ ## Features
32
106
 
33
- ```jsx
34
- import React from 'react';
35
- import { Provider } from 'react-redux';
36
- import store from './store';
37
- import { AchievementProvider } from 'react-achievements';
38
- import Game from './Game';
39
- import achievementConfig from './achievementConfig';
107
+ - Framework-agnostic achievement system
108
+ - Customizable storage implementations
109
+ - Built-in local storage support
110
+ - Customizable UI components
111
+ - Toast notifications
112
+ - Confetti animations
113
+ - TypeScript support
40
114
 
41
- const initialState = {
42
- level: 1,
43
- experience: 0,
44
- monstersDefeated: 0,
45
- questsCompleted: 0,
46
- previouslyAwardedAchievements: ['first_step'], // Optional: Load previously awarded achievements
47
- };
115
+ ## Achievement Notifications & History
48
116
 
49
- function App() {
50
- return (
51
- <Provider store={store}>
52
- <AchievementProvider
53
- config={achievementConfig} // Required: your achievement configuration
54
- initialState={initialState} // Required: initial game metrics and optionally previously awarded achievements. This can be loaded from your server
55
- storageKey="my-game-achievements" // Optional: customize local storage key
56
- badgesButtonPosition="top-right" // Optional: customize badges button position
57
- // Optional: add custom styles and icons here
58
- >
59
- <Game />
60
- </AchievementProvider>
61
- </Provider>
62
- );
63
- }
117
+ The package provides two ways to display achievements to users:
64
118
 
65
- export default App;
119
+ ### Automatic Notifications
120
+ When an achievement is unlocked, the system automatically:
121
+ - Shows a toast notification in the top-right corner with the achievement details
122
+ - Plays a confetti animation to celebrate the achievement
123
+
124
+ These notifications appear immediately when achievements are unlocked and require no additional setup.
125
+
126
+ ### Achievement History
127
+ To allow users to view their achievement history, the package provides two essential components:
128
+
129
+ 1. `BadgesButton`: A floating button that shows the number of unlocked achievements
130
+ ```tsx
131
+ <BadgesButton
132
+ position="bottom-right" // or "top-right", "top-left", "bottom-left"
133
+ onClick={() => setIsModalOpen(true)}
134
+ unlockedAchievements={achievements.unlocked}
135
+ />
66
136
  ```
67
137
 
68
- <h3 align="center">🛠 Set up the Store</h3>
138
+ 2. `BadgesModal`: A modal dialog that displays all unlocked achievements with their details
139
+ ```tsx
140
+ <BadgesModal
141
+ isOpen={isModalOpen}
142
+ onClose={() => setIsModalOpen(false)}
143
+ achievements={achievements.unlocked}
144
+ icons={customIcons} // Optional custom icons
145
+ />
146
+ ```
147
+
148
+ 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.
69
149
 
70
- You need to create a store for you state
150
+ ## Installation
151
+
152
+ ```bash
153
+ npm install react-achievements
154
+ ```
155
+
156
+ ## Basic Usage
71
157
 
72
158
  ```tsx
73
- // src/store.ts
74
- // src/store.js
159
+ import { AchievementProvider, useAchievements } from 'react-achievements';
160
+ // For specific state management implementations:
161
+ // import { AchievementProvider, useAchievements } from 'react-achievements/redux';
162
+ // import { AchievementProvider, useAchievements } from 'react-achievements/zustand';
163
+ // import { AchievementProvider, useAchievements } from 'react-achievements/context';
164
+
165
+ // Define your achievements with various data types and conditions
166
+ const achievements = {
167
+ // Numeric achievements with thresholds
168
+ score: {
169
+ 100: {
170
+ title: 'Century!',
171
+ description: 'Score 100 points',
172
+ icon: 'trophy'
173
+ },
174
+ 500: {
175
+ title: 'Half a Thousand!',
176
+ description: 'Score 500 points',
177
+ icon: 'gold',
178
+ condition: (value) => value >= 500
179
+ }
180
+ },
75
181
 
76
- import { configureStore } from '@reduxjs/toolkit';
77
- import achievementReducer from 'react-achievements/redux/achievementSlice';
78
- import notificationReducer from 'react-achievements/redux/notificationSlice';
182
+ // Boolean achievements
183
+ completedTutorial: {
184
+ true: {
185
+ title: 'Tutorial Master',
186
+ description: 'Complete the tutorial',
187
+ icon: 'book'
188
+ }
189
+ },
79
190
 
80
- const store = configureStore({
81
- reducer: {
82
- achievements: achievementReducer,
83
- notifications: notificationReducer,
191
+ // String-based achievements
192
+ characterClass: {
193
+ 'wizard': {
194
+ title: 'Arcane Scholar',
195
+ description: 'Choose the wizard class',
196
+ icon: 'wand'
197
+ },
198
+ 'warrior': {
199
+ title: 'Battle Hardened',
200
+ description: 'Choose the warrior class',
201
+ icon: 'sword'
202
+ }
84
203
  },
85
- });
86
204
 
87
- // If you are using JavaScript, you don't need to explicitly export RootState and AppDispatch types.
88
- export type RootState = ReturnType<typeof store.getState>;
89
- export type AppDispatch = typeof store.dispatch;
205
+ // Array-based achievements
206
+ collectedItems: {
207
+ ['sword', 'shield', 'potion']: {
208
+ title: 'Fully Equipped',
209
+ description: 'Collect all essential items',
210
+ icon: 'backpack',
211
+ condition: (items) => ['sword', 'shield', 'potion'].every(item => items.includes(item))
212
+ }
213
+ },
214
+
215
+ // Object-based achievements
216
+ playerStats: {
217
+ { strength: 10, intelligence: 10 }: {
218
+ title: 'Balanced Warrior',
219
+ description: 'Achieve balanced stats',
220
+ icon: 'scale',
221
+ condition: (stats) => stats.strength === 10 && stats.intelligence === 10
222
+ }
223
+ },
224
+
225
+ // Time-based achievements
226
+ playTime: {
227
+ 3600: {
228
+ title: 'Dedicated Player',
229
+ description: 'Play for 1 hour',
230
+ icon: 'clock',
231
+ condition: (seconds) => seconds >= 3600
232
+ }
233
+ },
234
+
235
+ // Combination achievements
236
+ combo: {
237
+ { score: 1000, level: 5 }: {
238
+ title: 'Rising Star',
239
+ description: 'Reach level 5 with 1000 points',
240
+ icon: 'star',
241
+ condition: (metrics) => metrics.score >= 1000 && metrics.level >= 5
242
+ }
243
+ }
244
+ };
245
+
246
+ // Create your app component
247
+ const App = () => {
248
+ return (
249
+ <AchievementProvider
250
+ achievements={achievements}
251
+ storage="local" // or "memory" or custom storage
252
+ >
253
+ <Game />
254
+ </AchievementProvider>
255
+ );
256
+ };
90
257
 
91
- export default store;
258
+ // Use achievements in your components
259
+ const Game = () => {
260
+ const { update, achievements } = useAchievements();
261
+
262
+ const handleScoreUpdate = (newScore: number) => {
263
+ update({ score: newScore });
264
+ };
265
+
266
+ return (
267
+ <div>
268
+ <h1>Game</h1>
269
+ <p>Unlocked Achievements: {achievements.unlocked.length}</p>
270
+ <button onClick={() => handleScoreUpdate(100)}>
271
+ Score 100 points
272
+ </button>
273
+ </div>
274
+ );
275
+ };
92
276
  ```
93
277
 
94
- <h3 align="center">📝 Create an achievement configuration</h3>
278
+ ## Default Icons
95
279
 
96
- Create a file (e.g., achievementConfig.js) to define your achievements:
280
+ The package comes with a comprehensive set of default icons that you can use in your achievements. These are available through the `defaultAchievementIcons` export:
97
281
 
98
- ```javascript
99
- // achievementConfig.js
100
- import levelUpIcon from './icons/level-up.png';
101
- import monsterSlayerIcon from './icons/monster-slayer.png';
102
- import questMasterIcon from './icons/quest-master.png';
282
+ ```tsx
283
+ import { AchievementProvider, defaultAchievementIcons } from 'react-achievements-core';
103
284
 
285
+ // Example achievement configuration using default icons
104
286
  const achievementConfig = {
105
- level: [
106
- {
107
- isConditionMet: (value) => value >= 1,
108
- achievementDetails: {
109
- achievementId: 'level_1',
110
- achievementTitle: 'Novice Adventurer',
111
- achievementDescription: 'Reached level 1',
112
- achievementIconKey: 'levelUpIcon',
113
- },
114
- },
115
- {
116
- isConditionMet: (value) => value >= 5,
117
- achievementDetails: {
118
- achievementId: 'level_5',
119
- achievementTitle: 'Seasoned Warrior',
120
- achievementDescription: 'Reached level 5',
121
- achievementIconKey: 'levelUpIcon',
122
- },
123
- },
124
- ],
125
- monstersDefeated: [
126
- {
127
- isConditionMet: (value) => value >= 10,
128
- achievementDetails: {
129
- achievementId: 'monster_slayer',
130
- achievementTitle: 'Monster Slayer',
131
- achievementDescription: 'Defeated 10 monsters',
132
- achievementIconKey: 'monsterSlayerIcon',
133
- },
134
- },
135
- ],
136
- questsCompleted: [
137
- {
138
- isConditionMet: (value) => value >= 1,
139
- achievementDetails: {
140
- achievementId: 'quest_master',
141
- achievementTitle: 'Quest Master',
142
- achievementDescription: 'Completed 1 quest',
143
- achievementIconKey: 'questMasterIcon',
144
- },
145
- },
146
- ],
287
+ pageViews: [
288
+ {
289
+ isConditionMet: (value) => value >= 5,
290
+ achievementDetails: {
291
+ achievementId: 'views-5',
292
+ achievementTitle: 'Getting Started',
293
+ achievementDescription: 'Viewed 5 pages',
294
+ achievementIconKey: 'firstStep' // This will use the 👣 emoji from defaultAchievementIcons
295
+ }
296
+ }
297
+ ]
147
298
  };
148
299
 
149
- export default achievementConfig;
300
+ // Create your app component
301
+ const App = () => {
302
+ return (
303
+ <AchievementProvider
304
+ achievements={achievementConfig}
305
+ // The provider automatically uses defaultAchievementIcons
306
+ >
307
+ <Game />
308
+ </AchievementProvider>
309
+ );
310
+ };
150
311
  ```
151
312
 
152
- <h3 align="center">🎣 Use the useAchievement hook</h3>
313
+ ### Custom Icons
153
314
 
154
- In your game components, use the useAchievement hook to update metrics and trigger achievement checks:
155
- ```jsx
156
- import React, { useState } from 'react';
157
- import { useAchievement } from 'react-achievements';
158
-
159
- function Game() {
160
- const { updateMetrics, metrics } = useAchievement();
161
- const [currentQuest, setCurrentQuest] = useState(null);
162
-
163
- const defeatMonster = () => {
164
- updateMetrics({
165
- monstersDefeated: [(metrics.monstersDefeated?.[0] || 0) + 1],
166
- experience: [(metrics.experience?.[0] || 0) + 10],
167
- level: [Math.floor(((metrics.experience?.[0] || 0) + 10) / 100) + 1], // Calculate new level
168
- });
169
- };
170
-
171
- const completeQuest = () => {
172
- updateMetrics({
173
- questsCompleted: [(metrics.questsCompleted?.[0] || 0) + 1],
174
- experience: [(metrics.experience?.[0] || 0) + 50],
175
- level: [Math.floor(((metrics.experience?.[0] || 0) + 50) / 100) + 1], // Calculate new level
176
- });
177
- setCurrentQuest(null);
178
- };
179
-
180
- const startQuest = () => {
181
- setCurrentQuest("Defeat the Dragon");
182
- };
183
-
184
- return (
185
- <div>
186
- <h1>My RPG Game</h1>
187
- <p>Level: {metrics.level?.[0] || 1}</p>
188
- <p>Experience: {metrics.experience?.[0] || 0}</p>
189
- <p>Monsters Defeated: {metrics.monstersDefeated?.[0] || 0}</p>
190
- <p>Quests Completed: {metrics.questsCompleted?.[0] || 0}</p>
191
-
192
- <div>
193
- <h2>Battle Arena</h2>
194
- <button onClick={defeatMonster}>Fight a Monster</button>
195
- </div>
196
-
197
- <div>
198
- <h2>Quest Board</h2>
199
- {currentQuest ? (
200
- <>
201
- <p>Current Quest: {currentQuest}</p>
202
- <button onClick={completeQuest}>Complete Quest</button>
203
- </>
204
- ) : (
205
- <button onClick={startQuest}>Start a New Quest</button>
206
- )}
207
- </div>
208
- </div>
209
- );
210
- }
315
+ You can also provide your own custom icons that will override or extend the default ones:
211
316
 
212
- export default Game;
213
- ```
317
+ ```tsx
318
+ import { AchievementProvider, defaultAchievementIcons } from 'react-achievements-core';
214
319
 
215
- <h2 align="center">✨ Features</h2>
216
-
217
- - Flexible Achievement System: Define custom metrics and achievement conditions for your game or app.
218
- - Built with TypeScript: Provides strong typing and improved developer experience.
219
- - Redux-Powered State Management: Leverages Redux for predictable and scalable state management of achievements and metrics.
220
- - Automatic Achievement Tracking: Achievements are automatically checked and unlocked when metrics change.
221
- - Achievement Notifications: Uses react-toastify to display notifications when an achievement is unlocked
222
- - Persistent Achievements: Unlocked achievements and metrics are stored in local storage, allowing players to keep their progress
223
- - Achievement Gallery: Players can view all their unlocked achievements, encouraging completionism
224
- - Confetti Effect: A celebratory confetti effect is displayed when an achievement is unlocked, adding to the excitement
225
- - Local Storage: Achievements are stored locally on the device
226
- - **Loading Previous Awards:** The AchievementProvider accepts an optional previouslyAwardedAchievements array in its initialState prop, allowing you to load achievements that the user has already earned
227
- - **Programmatic Reset:** Includes a `resetStorage` function accessible via the `useAchievementContext` hook to easily reset all achievement data
228
-
229
- <h2 align="center">🔧 API</h2>
230
-
231
- <h3 align="center">🏗 AchievementProvider</h3>
232
-
233
- #### Props:
234
-
235
- - `config` (required): An object defining your metrics and achievements
236
- - `initialState` (optional): The initial state of your metrics. Can also include an optional previouslyAwardedAchievements array of achievement IDs
237
- - `storageKey` (optional): A string to use as the key for localStorage. Default: 'react-achievements'
238
- - `badgesButtonPosition` (optional): Position of the badges button. One of: 'top-left', 'top-right', 'bottom-left', 'bottom-right'. Default: 'top-right'
239
- - `styles` (optional): Custom styles for the badges components (see Customization section below)
240
- - `icons` (optional): Custom icons to use for achievements. You can use the default icons provided by the library (see Available Icons section) or provide your own. Icons should be a Record<string, string> where the key is the iconKey referenced in your achievement config and the value is the icon string/element.
241
-
242
- ### Available Default Icons
243
-
244
- ```javascript
245
- {
246
- // Time & Activity
247
- activeDay: '☀️',
248
- activeWeek: '📅',
249
- activeMonth: '🗓️',
250
- earlyBird: '⏰',
251
- nightOwl: '🌙',
252
- streak: '🔥',
253
- dedicated: '⏳',
254
- punctual: '⏱️',
255
- consistent: '🔄',
256
- marathon: '🏃',
257
-
258
- // Creativity & Skill
259
- artist: '🎨',
260
- writer: '✍️',
261
- innovator: '🔬',
262
- creator: '🛠️',
263
- expert: '🎓',
264
- master: '👑',
265
- pioneer: '🚀',
266
- performer: '🎭',
267
- thinker: '🧠',
268
- explorer: '🗺️',
269
-
270
- // Achievement Types
271
- bronze: '🥉',
272
- silver: '🥈',
273
- gold: '🥇',
274
- diamond: '💎',
275
- legendary: '✨',
276
- epic: '💥',
277
- rare: '🔮',
278
- common: '🔘',
279
- special: '🎁',
280
- hidden: '❓',
281
-
282
- // Numbers & Counters
283
- one: '1️⃣',
284
- ten: '🔟',
285
- hundred: '💯',
286
- thousand: '🔢',
287
-
288
- // Actions & Interactions
289
- clicked: '🖱️',
290
- used: '🔑',
291
- found: '🔍',
292
- built: '🧱',
293
- solved: '🧩',
294
- discovered: '🔭',
295
- unlocked: '🔓',
296
- upgraded: '⬆️',
297
- repaired: '🔧',
298
- defended: '🛡️',
299
-
300
- // Placeholders
301
- default: '⭐', // A fallback icon
302
- loading: '⏳',
303
- error: '⚠️',
304
- success: '✅',
305
- failure: '❌',
306
-
307
- // Miscellaneous
308
- trophy: '🏆',
309
- star: '⭐',
310
- flag: '🚩',
311
- puzzle: '🧩',
312
- gem: '💎',
313
- crown: '👑',
314
- medal: '🏅',
315
- ribbon: '🎗️',
316
- badge: '🎖️',
317
- shield: '🛡️',
318
- }
320
+ // Create custom icons by extending the defaults
321
+ const customIcons = {
322
+ ...defaultAchievementIcons, // Include all default icons
323
+ levelUp: '🚀', // Override the default for 'levelUp'
324
+ myCustomIcon: '💻' // Add a new icon not in the defaults
325
+ };
326
+
327
+ const App = () => {
328
+ return (
329
+ <AchievementProvider
330
+ achievements={achievementConfig}
331
+ icons={customIcons} // Pass your custom icons to the provider
332
+ >
333
+ <Game />
334
+ </AchievementProvider>
335
+ );
336
+ };
319
337
  ```
320
338
 
321
- <h2 align="center">🎨 Customization</h2>
339
+ ### Available Icons
322
340
 
323
- You can customize the look of the achievement badges by overriding the default styles. Pass a `styles` prop to the `AchievementProvider`:
341
+ The `defaultAchievementIcons` includes icons in these categories:
324
342
 
325
- ```javascript
326
- const customStyles = {
327
- badge: {
328
- // Your custom styles here
329
- },
330
- // ...other styles
331
- };
343
+ - General Progress & Milestones (levelUp, questComplete, etc.)
344
+ - Social & Engagement (shared, liked, etc.)
345
+ - Time & Activity (activeDay, streak, etc.)
346
+ - Creativity & Skill (artist, expert, etc.)
347
+ - Achievement Types (bronze, silver, gold, etc.)
348
+ - Numbers & Counters (one, ten, hundred, etc.)
349
+ - Actions & Interactions (clicked, discovered, etc.)
350
+ - Placeholders (default, loading, error, etc.)
351
+ - Miscellaneous (trophy, star, gem, etc.)
332
352
 
333
- <AchievementProvider
334
- config={achievementConfig}
335
- initialState={initialState}
336
- styles={customStyles}
337
- >
338
- <Game />
339
- </AchievementProvider>
340
- ```
353
+ ## Custom Storage
341
354
 
342
- <h2 align="center">🔄 Complex Achievement Conditions</h2>
355
+ You can implement your own storage solution by implementing the `AchievementStorage` interface:
343
356
 
344
- <h3 align="center">Achievement Dependencies</h3>
357
+ ```typescript
358
+ import { AchievementStorage } from 'react-achievements-core';
345
359
 
346
- You can create achievements that depend on other achievements being unlocked first:
360
+ class CustomStorage implements AchievementStorage {
361
+ getMetrics() {
362
+ // Your implementation
363
+ }
347
364
 
348
- ```javascript
349
- const achievementConfig = {
350
- prerequisite: [
351
- {
352
- isConditionMet: (value) => value === true,
353
- achievementDetails: {
354
- achievementId: 'prerequisite',
355
- achievementTitle: 'Prerequisites Met',
356
- achievementDescription: 'Unlocked advanced achievements',
357
- achievementIconKey: 'unlock'
358
- }
359
- }
360
- ],
361
- dependent: [
362
- {
363
- isConditionMet: (value, state) => {
364
- const prereqMet = state.unlockedAchievements.includes('prerequisite');
365
- return prereqMet && typeof value === 'number' && value >= 100;
366
- },
367
- achievementDetails: {
368
- achievementId: 'dependent',
369
- achievementTitle: 'Advanced Achievement',
370
- achievementDescription: 'Completed an advanced challenge',
371
- achievementIconKey: 'star'
372
- }
373
- }
374
- ]
375
- };
376
- ```
365
+ setMetrics(metrics) {
366
+ // Your implementation
367
+ }
377
368
 
378
- <h3 align="center">Time-Based Achievements</h3>
369
+ getUnlockedAchievements() {
370
+ // Your implementation
371
+ }
379
372
 
380
- You can create achievements based on specific times or dates:
373
+ setUnlockedAchievements(achievements) {
374
+ // Your implementation
375
+ }
381
376
 
382
- ```javascript
383
- const achievementConfig = {
384
- loginTime: [
385
- {
386
- isConditionMet: (value) => {
387
- if (!(value instanceof Date)) return false;
388
- const hour = value.getHours();
389
- return hour >= 22 || hour < 6;
390
- },
391
- achievementDetails: {
392
- achievementId: 'night_owl',
393
- achievementTitle: 'Night Owl',
394
- achievementDescription: 'Logged in during night hours',
395
- achievementIconKey: 'moon'
396
- }
397
- }
398
- ]
377
+ clear() {
378
+ // Your implementation
379
+ }
380
+ }
381
+
382
+ // Use your custom storage
383
+ const App = () => {
384
+ return (
385
+ <AchievementProvider
386
+ achievements={achievements}
387
+ storage={new CustomStorage()}
388
+ >
389
+ <Game />
390
+ </AchievementProvider>
391
+ );
399
392
  };
400
393
  ```
401
394
 
402
- <h3 align="center">Progressive Achievements</h3>
395
+ ## Styling
403
396
 
404
- You can create achievement chains that unlock in sequence:
397
+ You can customize the appearance of the achievement components:
405
398
 
406
- ```javascript
407
- const achievementConfig = {
408
- skillLevel: [
409
- {
410
- isConditionMet: (value) => typeof value === 'number' && value >= 1,
411
- achievementDetails: {
412
- achievementId: 'skill_novice',
413
- achievementTitle: 'Novice',
414
- achievementDescription: 'Reached skill level 1',
415
- achievementIconKey: 'bronze'
416
- }
399
+ ```tsx
400
+ const App = () => {
401
+ return (
402
+ <AchievementProvider
403
+ achievements={achievements}
404
+ theme={{
405
+ colors: {
406
+ primary: '#ff0000',
407
+ background: '#f0f0f0'
417
408
  },
418
- {
419
- isConditionMet: (value) => typeof value === 'number' && value >= 5,
420
- achievementDetails: {
421
- achievementId: 'skill_master',
422
- achievementTitle: 'Master',
423
- achievementDescription: 'Reached skill level 5',
424
- achievementIconKey: 'gold'
425
- }
426
- }
427
- ]
409
+ position: 'top-right'
410
+ }}
411
+ >
412
+ <Game />
413
+ </AchievementProvider>
414
+ );
428
415
  };
429
416
  ```
430
417
 
431
- <h2 align="center">💾 Saving and Loading Progress</h2>
432
-
433
- To persist user achievement progress across sessions or devices, you can save the metrics and previouslyAwardedAchievements from your Redux store:
434
-
435
- ```jsx
436
- import React from 'react';
437
- import { useAchievementState } from 'react-achievements/hooks/useAchievementState';
438
-
439
- const LogoutButtonWithSave = ({ onLogout }) => {
440
- const { metrics, previouslyAwardedAchievements } = useAchievementState();
441
-
442
- const handleLogoutAndSave = async () => {
443
- const achievementData = {
444
- metrics,
445
- previouslyAwardedAchievements,
446
- };
447
- try {
448
- await fetch('/api/save-achievements', {
449
- method: 'POST',
450
- headers: {
451
- 'Content-Type': 'application/json',
452
- },
453
- body: JSON.stringify(achievementData),
454
- });
455
- onLogout();
456
- } catch (error) {
457
- console.error('Failed to save achievements:', error);
458
- }
459
- };
460
-
461
- return <button onClick={handleLogoutAndSave}>Logout</button>;
462
- };
463
- ```
418
+ ## API Reference
419
+
420
+ ### AchievementProvider Props
421
+
422
+ | Prop | Type | Description |
423
+ |------|------|-------------|
424
+ | achievements | AchievementConfig | Achievement configuration object |
425
+ | storage | 'local' \| 'memory' \| AchievementStorage | Storage implementation |
426
+ | theme | ThemeConfig | Custom theme configuration |
427
+ | onUnlock | (achievement: Achievement) => void | Callback when achievement is unlocked |
428
+
429
+ ### useAchievements Hook
430
+
431
+ Returns an object with:
432
+
433
+ - `update`: Function to update achievement metrics
434
+ - `achievements`: Object containing unlocked and locked achievements
435
+ - `reset`: Function to reset achievement storage
436
+
437
+ ## License
438
+
439
+ MIT