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.
- package/README.md +377 -401
- package/dist/index.d.ts +222 -8
- package/dist/index.js +631 -0
- package/dist/index.js.map +1 -0
- package/dist/stories/Button.d.ts +28 -0
- package/dist/stories/Button.stories.d.ts +23 -0
- package/dist/stories/Header.d.ts +13 -0
- package/dist/stories/Header.stories.d.ts +18 -0
- package/dist/stories/Page.d.ts +3 -0
- package/dist/stories/Page.stories.d.ts +12 -0
- package/dist/types/__mocks__/confetti-wrapper.d.ts +5 -0
- package/dist/types/__mocks__/react-confetti.d.ts +3 -0
- package/dist/types/__mocks__/react-toastify.d.ts +13 -0
- package/dist/types/core/components/BadgesButton.d.ts +10 -0
- package/dist/types/core/components/BadgesModal.d.ts +11 -0
- package/dist/types/core/components/ConfettiWrapper.d.ts +6 -0
- package/dist/types/core/context/AchievementContext.d.ts +5 -0
- package/dist/types/core/icons/defaultIcons.d.ts +81 -0
- package/dist/types/core/storage/LocalStorage.d.ts +16 -0
- package/dist/types/core/storage/MemoryStorage.d.ts +11 -0
- package/dist/types/core/styles/defaultStyles.d.ts +2 -0
- package/dist/types/core/types.d.ts +74 -0
- package/dist/types/hooks/useAchievements.d.ts +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/providers/AchievementProvider.d.ts +24 -0
- package/dist/types/setupTests.d.ts +1 -0
- package/dist/types/stories/Button.d.ts +16 -0
- package/dist/types/stories/Button.stories.d.ts +23 -0
- package/dist/types/stories/Header.d.ts +13 -0
- package/dist/types/stories/Header.stories.d.ts +18 -0
- package/dist/types/stories/Page.d.ts +3 -0
- package/dist/types/stories/Page.stories.d.ts +12 -0
- package/package.json +73 -54
- package/.idea/jsLibraryMappings.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/react-achievements.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/coverage/clover.xml +0 -131
- package/coverage/coverage-final.json +0 -9
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -146
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/src/components/AchievementModal.tsx.html +0 -229
- package/coverage/lcov-report/src/components/BadgesButton.tsx.html +0 -169
- package/coverage/lcov-report/src/components/BadgesModal.tsx.html +0 -253
- package/coverage/lcov-report/src/components/ConfettiWrapper.tsx.html +0 -157
- package/coverage/lcov-report/src/components/index.html +0 -161
- package/coverage/lcov-report/src/context/AchievementContext.tsx.html +0 -505
- package/coverage/lcov-report/src/context/index.html +0 -116
- package/coverage/lcov-report/src/index.html +0 -146
- package/coverage/lcov-report/src/index.ts.html +0 -121
- package/coverage/lcov-report/src/react-confetti.d.ts.html +0 -139
- package/coverage/lcov-report/src/react-use.d.ts.html +0 -94
- package/coverage/lcov.info +0 -240
- package/demo/src/AchievementConfig.ts +0 -0
- package/public/badges/icon1.svg +0 -1
- package/rollup.config.mjs +0 -34
- package/src/assets/defaultIcons.ts +0 -100
- package/src/components/BadgesButton.tsx +0 -49
- package/src/components/BadgesModal.tsx +0 -47
- package/src/components/ConfettiWrapper.tsx +0 -17
- package/src/defaultStyles.ts +0 -86
- package/src/hooks/useAchievement.ts +0 -17
- package/src/index.ts +0 -25
- package/src/providers/AchievementProvider.tsx +0 -202
- package/src/react-confetti.d.ts +0 -19
- package/src/react-use.d.ts +0 -4
- package/src/redux/achievementSlice.ts +0 -116
- package/src/redux/notificationSlice.ts +0 -26
- package/src/redux/store.ts +0 -16
- package/src/types.ts +0 -39
- package/tsconfig.json +0 -113
package/README.md
CHANGED
|
@@ -1,463 +1,439 @@
|
|
|
1
|
-
|
|
1
|
+
# React Achievements
|
|
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
|
-
|
|
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
|
-
|
|
9
|
+
## Quick Start
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Here's a complete working example that shows automatic notifications and achievement tracking:
|
|
10
12
|
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
yarn add react-achievements @reduxjs/toolkit react-redux react-toastify react-confetti react-use
|
|
23
|
-
```
|
|
95
|
+
## State Management Options
|
|
24
96
|
|
|
25
|
-
|
|
97
|
+
This package includes example implementations for different state management solutions in the `stories/examples` directory:
|
|
26
98
|
|
|
27
|
-
|
|
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
|
-
|
|
103
|
+
See the [examples directory](./stories/examples) for detailed implementations and instructions for each state management solution.
|
|
30
104
|
|
|
31
|
-
|
|
105
|
+
## Features
|
|
32
106
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
+
## Installation
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npm install react-achievements
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Basic Usage
|
|
71
157
|
|
|
72
158
|
```tsx
|
|
73
|
-
|
|
74
|
-
//
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
182
|
+
// Boolean achievements
|
|
183
|
+
completedTutorial: {
|
|
184
|
+
true: {
|
|
185
|
+
title: 'Tutorial Master',
|
|
186
|
+
description: 'Complete the tutorial',
|
|
187
|
+
icon: 'book'
|
|
188
|
+
}
|
|
189
|
+
},
|
|
79
190
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
278
|
+
## Default Icons
|
|
95
279
|
|
|
96
|
-
|
|
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
|
-
```
|
|
99
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
313
|
+
### Custom Icons
|
|
153
314
|
|
|
154
|
-
|
|
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
|
-
|
|
213
|
-
|
|
317
|
+
```tsx
|
|
318
|
+
import { AchievementProvider, defaultAchievementIcons } from 'react-achievements-core';
|
|
214
319
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
339
|
+
### Available Icons
|
|
322
340
|
|
|
323
|
-
|
|
341
|
+
The `defaultAchievementIcons` includes icons in these categories:
|
|
324
342
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
334
|
-
config={achievementConfig}
|
|
335
|
-
initialState={initialState}
|
|
336
|
-
styles={customStyles}
|
|
337
|
-
>
|
|
338
|
-
<Game />
|
|
339
|
-
</AchievementProvider>
|
|
340
|
-
```
|
|
353
|
+
## Custom Storage
|
|
341
354
|
|
|
342
|
-
|
|
355
|
+
You can implement your own storage solution by implementing the `AchievementStorage` interface:
|
|
343
356
|
|
|
344
|
-
|
|
357
|
+
```typescript
|
|
358
|
+
import { AchievementStorage } from 'react-achievements-core';
|
|
345
359
|
|
|
346
|
-
|
|
360
|
+
class CustomStorage implements AchievementStorage {
|
|
361
|
+
getMetrics() {
|
|
362
|
+
// Your implementation
|
|
363
|
+
}
|
|
347
364
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
369
|
+
getUnlockedAchievements() {
|
|
370
|
+
// Your implementation
|
|
371
|
+
}
|
|
379
372
|
|
|
380
|
-
|
|
373
|
+
setUnlockedAchievements(achievements) {
|
|
374
|
+
// Your implementation
|
|
375
|
+
}
|
|
381
376
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
395
|
+
## Styling
|
|
403
396
|
|
|
404
|
-
You can
|
|
397
|
+
You can customize the appearance of the achievement components:
|
|
405
398
|
|
|
406
|
-
```
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
achievementIconKey: 'gold'
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
]
|
|
409
|
+
position: 'top-right'
|
|
410
|
+
}}
|
|
411
|
+
>
|
|
412
|
+
<Game />
|
|
413
|
+
</AchievementProvider>
|
|
414
|
+
);
|
|
428
415
|
};
|
|
429
416
|
```
|
|
430
417
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|