react-achievements 2.2.2 → 3.0.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 +257 -406
- 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,314 @@
|
|
|
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
|
+
## State Management Options
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This package includes example implementations for different state management solutions in the `stories/examples` directory:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- **Redux**: For large applications with complex state management needs
|
|
10
|
+
- **Zustand**: For applications needing a lightweight, modern state solution
|
|
11
|
+
- **Context API**: For applications preferring React's built-in solutions
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
See the [examples directory](./stories/examples) for detailed implementations and instructions for each state management solution.
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
## Features
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
- Framework-agnostic achievement system
|
|
18
|
+
- Customizable storage implementations
|
|
19
|
+
- Built-in local storage support
|
|
20
|
+
- Customizable UI components
|
|
21
|
+
- Toast notifications
|
|
22
|
+
- Confetti animations
|
|
23
|
+
- TypeScript support
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
## Installation
|
|
20
26
|
|
|
21
27
|
```bash
|
|
22
|
-
|
|
28
|
+
npm install react-achievements
|
|
23
29
|
```
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
## Basic Usage
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
```tsx
|
|
34
|
+
import { AchievementProvider, useAchievements } from 'react-achievements';
|
|
35
|
+
// For specific state management implementations:
|
|
36
|
+
// import { AchievementProvider, useAchievements } from 'react-achievements/redux';
|
|
37
|
+
// import { AchievementProvider, useAchievements } from 'react-achievements/zustand';
|
|
38
|
+
// import { AchievementProvider, useAchievements } from 'react-achievements/context';
|
|
39
|
+
|
|
40
|
+
// Define your achievements with various data types and conditions
|
|
41
|
+
const achievements = {
|
|
42
|
+
// Numeric achievements with thresholds
|
|
43
|
+
score: {
|
|
44
|
+
100: {
|
|
45
|
+
title: 'Century!',
|
|
46
|
+
description: 'Score 100 points',
|
|
47
|
+
icon: 'trophy'
|
|
48
|
+
},
|
|
49
|
+
500: {
|
|
50
|
+
title: 'Half a Thousand!',
|
|
51
|
+
description: 'Score 500 points',
|
|
52
|
+
icon: 'gold',
|
|
53
|
+
condition: (value) => value >= 500
|
|
54
|
+
}
|
|
55
|
+
},
|
|
28
56
|
|
|
29
|
-
|
|
57
|
+
// Boolean achievements
|
|
58
|
+
completedTutorial: {
|
|
59
|
+
true: {
|
|
60
|
+
title: 'Tutorial Master',
|
|
61
|
+
description: 'Complete the tutorial',
|
|
62
|
+
icon: 'book'
|
|
63
|
+
}
|
|
64
|
+
},
|
|
30
65
|
|
|
31
|
-
|
|
66
|
+
// String-based achievements
|
|
67
|
+
characterClass: {
|
|
68
|
+
'wizard': {
|
|
69
|
+
title: 'Arcane Scholar',
|
|
70
|
+
description: 'Choose the wizard class',
|
|
71
|
+
icon: 'wand'
|
|
72
|
+
},
|
|
73
|
+
'warrior': {
|
|
74
|
+
title: 'Battle Hardened',
|
|
75
|
+
description: 'Choose the warrior class',
|
|
76
|
+
icon: 'sword'
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// Array-based achievements
|
|
81
|
+
collectedItems: {
|
|
82
|
+
['sword', 'shield', 'potion']: {
|
|
83
|
+
title: 'Fully Equipped',
|
|
84
|
+
description: 'Collect all essential items',
|
|
85
|
+
icon: 'backpack',
|
|
86
|
+
condition: (items) => ['sword', 'shield', 'potion'].every(item => items.includes(item))
|
|
87
|
+
}
|
|
88
|
+
},
|
|
32
89
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
90
|
+
// Object-based achievements
|
|
91
|
+
playerStats: {
|
|
92
|
+
{ strength: 10, intelligence: 10 }: {
|
|
93
|
+
title: 'Balanced Warrior',
|
|
94
|
+
description: 'Achieve balanced stats',
|
|
95
|
+
icon: 'scale',
|
|
96
|
+
condition: (stats) => stats.strength === 10 && stats.intelligence === 10
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// Time-based achievements
|
|
101
|
+
playTime: {
|
|
102
|
+
3600: {
|
|
103
|
+
title: 'Dedicated Player',
|
|
104
|
+
description: 'Play for 1 hour',
|
|
105
|
+
icon: 'clock',
|
|
106
|
+
condition: (seconds) => seconds >= 3600
|
|
107
|
+
}
|
|
108
|
+
},
|
|
40
109
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
110
|
+
// Combination achievements
|
|
111
|
+
combo: {
|
|
112
|
+
{ score: 1000, level: 5 }: {
|
|
113
|
+
title: 'Rising Star',
|
|
114
|
+
description: 'Reach level 5 with 1000 points',
|
|
115
|
+
icon: 'star',
|
|
116
|
+
condition: (metrics) => metrics.score >= 1000 && metrics.level >= 5
|
|
117
|
+
}
|
|
118
|
+
}
|
|
47
119
|
};
|
|
48
120
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
</AchievementProvider>
|
|
61
|
-
</Provider>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
121
|
+
// Create your app component
|
|
122
|
+
const App = () => {
|
|
123
|
+
return (
|
|
124
|
+
<AchievementProvider
|
|
125
|
+
achievements={achievements}
|
|
126
|
+
storage="local" // or "memory" or custom storage
|
|
127
|
+
>
|
|
128
|
+
<Game />
|
|
129
|
+
</AchievementProvider>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
64
132
|
|
|
65
|
-
|
|
133
|
+
// Use achievements in your components
|
|
134
|
+
const Game = () => {
|
|
135
|
+
const { update, achievements } = useAchievements();
|
|
136
|
+
|
|
137
|
+
const handleScoreUpdate = (newScore: number) => {
|
|
138
|
+
update({ score: newScore });
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div>
|
|
143
|
+
<h1>Game</h1>
|
|
144
|
+
<p>Unlocked Achievements: {achievements.unlocked.length}</p>
|
|
145
|
+
<button onClick={() => handleScoreUpdate(100)}>
|
|
146
|
+
Score 100 points
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
66
151
|
```
|
|
67
152
|
|
|
68
|
-
|
|
153
|
+
## Default Icons
|
|
69
154
|
|
|
70
|
-
|
|
155
|
+
The package comes with a comprehensive set of default icons that you can use in your achievements. These are available through the `defaultAchievementIcons` export:
|
|
71
156
|
|
|
72
157
|
```tsx
|
|
73
|
-
|
|
74
|
-
// src/store.js
|
|
75
|
-
|
|
76
|
-
import { configureStore } from '@reduxjs/toolkit';
|
|
77
|
-
import achievementReducer from 'react-achievements/redux/achievementSlice';
|
|
78
|
-
import notificationReducer from 'react-achievements/redux/notificationSlice';
|
|
158
|
+
import { AchievementProvider, defaultAchievementIcons } from 'react-achievements-core';
|
|
79
159
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
160
|
+
// Example achievement configuration using default icons
|
|
161
|
+
const achievementConfig = {
|
|
162
|
+
pageViews: [
|
|
163
|
+
{
|
|
164
|
+
isConditionMet: (value) => value >= 5,
|
|
165
|
+
achievementDetails: {
|
|
166
|
+
achievementId: 'views-5',
|
|
167
|
+
achievementTitle: 'Getting Started',
|
|
168
|
+
achievementDescription: 'Viewed 5 pages',
|
|
169
|
+
achievementIconKey: 'firstStep' // This will use the 👣 emoji from defaultAchievementIcons
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
};
|
|
90
174
|
|
|
91
|
-
|
|
175
|
+
// Create your app component
|
|
176
|
+
const App = () => {
|
|
177
|
+
return (
|
|
178
|
+
<AchievementProvider
|
|
179
|
+
achievements={achievementConfig}
|
|
180
|
+
// The provider automatically uses defaultAchievementIcons
|
|
181
|
+
>
|
|
182
|
+
<Game />
|
|
183
|
+
</AchievementProvider>
|
|
184
|
+
);
|
|
185
|
+
};
|
|
92
186
|
```
|
|
93
187
|
|
|
94
|
-
|
|
188
|
+
### Custom Icons
|
|
95
189
|
|
|
96
|
-
|
|
190
|
+
You can also provide your own custom icons that will override or extend the default ones:
|
|
97
191
|
|
|
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';
|
|
192
|
+
```tsx
|
|
193
|
+
import { AchievementProvider, defaultAchievementIcons } from 'react-achievements-core';
|
|
103
194
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
],
|
|
195
|
+
// Create custom icons by extending the defaults
|
|
196
|
+
const customIcons = {
|
|
197
|
+
...defaultAchievementIcons, // Include all default icons
|
|
198
|
+
levelUp: '🚀', // Override the default for 'levelUp'
|
|
199
|
+
myCustomIcon: '💻' // Add a new icon not in the defaults
|
|
147
200
|
};
|
|
148
201
|
|
|
149
|
-
|
|
202
|
+
const App = () => {
|
|
203
|
+
return (
|
|
204
|
+
<AchievementProvider
|
|
205
|
+
achievements={achievementConfig}
|
|
206
|
+
icons={customIcons} // Pass your custom icons to the provider
|
|
207
|
+
>
|
|
208
|
+
<Game />
|
|
209
|
+
</AchievementProvider>
|
|
210
|
+
);
|
|
211
|
+
};
|
|
150
212
|
```
|
|
151
213
|
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
}
|
|
214
|
+
### Available Icons
|
|
211
215
|
|
|
212
|
-
|
|
213
|
-
```
|
|
216
|
+
The `defaultAchievementIcons` includes icons in these categories:
|
|
214
217
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
-
|
|
218
|
-
-
|
|
219
|
-
-
|
|
220
|
-
-
|
|
221
|
-
-
|
|
222
|
-
-
|
|
223
|
-
-
|
|
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
|
-
}
|
|
319
|
-
```
|
|
218
|
+
- General Progress & Milestones (levelUp, questComplete, etc.)
|
|
219
|
+
- Social & Engagement (shared, liked, etc.)
|
|
220
|
+
- Time & Activity (activeDay, streak, etc.)
|
|
221
|
+
- Creativity & Skill (artist, expert, etc.)
|
|
222
|
+
- Achievement Types (bronze, silver, gold, etc.)
|
|
223
|
+
- Numbers & Counters (one, ten, hundred, etc.)
|
|
224
|
+
- Actions & Interactions (clicked, discovered, etc.)
|
|
225
|
+
- Placeholders (default, loading, error, etc.)
|
|
226
|
+
- Miscellaneous (trophy, star, gem, etc.)
|
|
320
227
|
|
|
321
|
-
|
|
228
|
+
## Custom Storage
|
|
322
229
|
|
|
323
|
-
You can
|
|
230
|
+
You can implement your own storage solution by implementing the `AchievementStorage` interface:
|
|
324
231
|
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
badge: {
|
|
328
|
-
// Your custom styles here
|
|
329
|
-
},
|
|
330
|
-
// ...other styles
|
|
331
|
-
};
|
|
232
|
+
```typescript
|
|
233
|
+
import { AchievementStorage } from 'react-achievements-core';
|
|
332
234
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
>
|
|
338
|
-
<Game />
|
|
339
|
-
</AchievementProvider>
|
|
340
|
-
```
|
|
235
|
+
class CustomStorage implements AchievementStorage {
|
|
236
|
+
getMetrics() {
|
|
237
|
+
// Your implementation
|
|
238
|
+
}
|
|
341
239
|
|
|
342
|
-
|
|
240
|
+
setMetrics(metrics) {
|
|
241
|
+
// Your implementation
|
|
242
|
+
}
|
|
343
243
|
|
|
344
|
-
|
|
244
|
+
getUnlockedAchievements() {
|
|
245
|
+
// Your implementation
|
|
246
|
+
}
|
|
345
247
|
|
|
346
|
-
|
|
248
|
+
setUnlockedAchievements(achievements) {
|
|
249
|
+
// Your implementation
|
|
250
|
+
}
|
|
347
251
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
]
|
|
252
|
+
clear() {
|
|
253
|
+
// Your implementation
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Use your custom storage
|
|
258
|
+
const App = () => {
|
|
259
|
+
return (
|
|
260
|
+
<AchievementProvider
|
|
261
|
+
achievements={achievements}
|
|
262
|
+
storage={new CustomStorage()}
|
|
263
|
+
>
|
|
264
|
+
<Game />
|
|
265
|
+
</AchievementProvider>
|
|
266
|
+
);
|
|
375
267
|
};
|
|
376
268
|
```
|
|
377
269
|
|
|
378
|
-
|
|
270
|
+
## Styling
|
|
379
271
|
|
|
380
|
-
You can
|
|
272
|
+
You can customize the appearance of the achievement components:
|
|
381
273
|
|
|
382
|
-
```
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
]
|
|
274
|
+
```tsx
|
|
275
|
+
const App = () => {
|
|
276
|
+
return (
|
|
277
|
+
<AchievementProvider
|
|
278
|
+
achievements={achievements}
|
|
279
|
+
theme={{
|
|
280
|
+
colors: {
|
|
281
|
+
primary: '#ff0000',
|
|
282
|
+
background: '#f0f0f0'
|
|
283
|
+
},
|
|
284
|
+
position: 'top-right'
|
|
285
|
+
}}
|
|
286
|
+
>
|
|
287
|
+
<Game />
|
|
288
|
+
</AchievementProvider>
|
|
289
|
+
);
|
|
399
290
|
};
|
|
400
291
|
```
|
|
401
292
|
|
|
402
|
-
|
|
293
|
+
## API Reference
|
|
403
294
|
|
|
404
|
-
|
|
295
|
+
### AchievementProvider Props
|
|
405
296
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
achievementId: 'skill_novice',
|
|
413
|
-
achievementTitle: 'Novice',
|
|
414
|
-
achievementDescription: 'Reached skill level 1',
|
|
415
|
-
achievementIconKey: 'bronze'
|
|
416
|
-
}
|
|
417
|
-
},
|
|
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
|
-
]
|
|
428
|
-
};
|
|
429
|
-
```
|
|
297
|
+
| Prop | Type | Description |
|
|
298
|
+
|------|------|-------------|
|
|
299
|
+
| achievements | AchievementConfig | Achievement configuration object |
|
|
300
|
+
| storage | 'local' \| 'memory' \| AchievementStorage | Storage implementation |
|
|
301
|
+
| theme | ThemeConfig | Custom theme configuration |
|
|
302
|
+
| onUnlock | (achievement: Achievement) => void | Callback when achievement is unlocked |
|
|
430
303
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
-
```
|
|
304
|
+
### useAchievements Hook
|
|
305
|
+
|
|
306
|
+
Returns an object with:
|
|
307
|
+
|
|
308
|
+
- `update`: Function to update achievement metrics
|
|
309
|
+
- `achievements`: Object containing unlocked and locked achievements
|
|
310
|
+
- `reset`: Function to reset achievement storage
|
|
311
|
+
|
|
312
|
+
## License
|
|
313
|
+
|
|
314
|
+
MIT
|