react-achievements 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +305 -235
- package/dist/index.d.ts +146 -74
- package/dist/index.js +316 -110
- package/dist/index.js.map +1 -1
- package/dist/types/core/icons/defaultIcons.d.ts +0 -73
- package/dist/types/index.d.ts +2 -0
- package/dist/types/utils/achievementHelpers.d.ts +135 -0
- package/package.json +7 -2
package/dist/index.d.ts
CHANGED
|
@@ -104,6 +104,17 @@ declare class LocalStorage implements AchievementStorage {
|
|
|
104
104
|
clear(): void;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
declare class MemoryStorage implements AchievementStorage {
|
|
108
|
+
private metrics;
|
|
109
|
+
private unlockedAchievements;
|
|
110
|
+
constructor();
|
|
111
|
+
getMetrics(): AchievementMetrics;
|
|
112
|
+
setMetrics(metrics: AchievementMetrics): void;
|
|
113
|
+
getUnlockedAchievements(): string[];
|
|
114
|
+
setUnlockedAchievements(achievements: string[]): void;
|
|
115
|
+
clear(): void;
|
|
116
|
+
}
|
|
117
|
+
|
|
107
118
|
interface BadgesButtonProps {
|
|
108
119
|
onClick: () => void;
|
|
109
120
|
position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
@@ -193,88 +204,149 @@ declare const useSimpleAchievements: () => {
|
|
|
193
204
|
declare const defaultStyles: Required<StylesProps>;
|
|
194
205
|
|
|
195
206
|
declare const defaultAchievementIcons: {
|
|
196
|
-
levelUp: string;
|
|
197
|
-
questComplete: string;
|
|
198
|
-
monsterDefeated: string;
|
|
199
|
-
itemCollected: string;
|
|
200
|
-
challengeCompleted: string;
|
|
201
|
-
milestoneReached: string;
|
|
202
|
-
firstStep: string;
|
|
203
|
-
newBeginnings: string;
|
|
204
|
-
breakthrough: string;
|
|
205
|
-
growth: string;
|
|
206
|
-
shared: string;
|
|
207
|
-
liked: string;
|
|
208
|
-
commented: string;
|
|
209
|
-
followed: string;
|
|
210
|
-
invited: string;
|
|
211
|
-
communityMember: string;
|
|
212
|
-
supporter: string;
|
|
213
|
-
connected: string;
|
|
214
|
-
participant: string;
|
|
215
|
-
influencer: string;
|
|
216
|
-
activeDay: string;
|
|
217
|
-
activeWeek: string;
|
|
218
|
-
activeMonth: string;
|
|
219
|
-
earlyBird: string;
|
|
220
|
-
nightOwl: string;
|
|
221
|
-
streak: string;
|
|
222
|
-
dedicated: string;
|
|
223
|
-
punctual: string;
|
|
224
|
-
consistent: string;
|
|
225
|
-
marathon: string;
|
|
226
|
-
artist: string;
|
|
227
|
-
writer: string;
|
|
228
|
-
innovator: string;
|
|
229
|
-
creator: string;
|
|
230
|
-
expert: string;
|
|
231
|
-
master: string;
|
|
232
|
-
pioneer: string;
|
|
233
|
-
performer: string;
|
|
234
|
-
thinker: string;
|
|
235
|
-
explorer: string;
|
|
236
|
-
bronze: string;
|
|
237
|
-
silver: string;
|
|
238
|
-
gold: string;
|
|
239
|
-
diamond: string;
|
|
240
|
-
legendary: string;
|
|
241
|
-
epic: string;
|
|
242
|
-
rare: string;
|
|
243
|
-
common: string;
|
|
244
|
-
special: string;
|
|
245
|
-
hidden: string;
|
|
246
|
-
one: string;
|
|
247
|
-
ten: string;
|
|
248
|
-
hundred: string;
|
|
249
|
-
thousand: string;
|
|
250
|
-
clicked: string;
|
|
251
|
-
used: string;
|
|
252
|
-
found: string;
|
|
253
|
-
built: string;
|
|
254
|
-
solved: string;
|
|
255
|
-
discovered: string;
|
|
256
|
-
unlocked: string;
|
|
257
|
-
upgraded: string;
|
|
258
|
-
repaired: string;
|
|
259
|
-
defended: string;
|
|
260
207
|
default: string;
|
|
261
208
|
loading: string;
|
|
262
209
|
error: string;
|
|
263
210
|
success: string;
|
|
264
|
-
failure: string;
|
|
265
211
|
trophy: string;
|
|
266
212
|
star: string;
|
|
267
|
-
flag: string;
|
|
268
|
-
puzzle: string;
|
|
269
|
-
gem: string;
|
|
270
|
-
crown: string;
|
|
271
|
-
medal: string;
|
|
272
|
-
ribbon: string;
|
|
273
|
-
badge: string;
|
|
274
|
-
shield: string;
|
|
275
213
|
};
|
|
276
214
|
|
|
277
215
|
declare function isSimpleConfig(config: AchievementConfigurationType): config is SimpleAchievementConfig;
|
|
278
216
|
declare function normalizeAchievements(config: AchievementConfigurationType): AchievementConfiguration;
|
|
279
217
|
|
|
280
|
-
|
|
218
|
+
/**
|
|
219
|
+
* Helper interface for cleaner achievement award definitions
|
|
220
|
+
*/
|
|
221
|
+
interface AwardDetails {
|
|
222
|
+
title?: string;
|
|
223
|
+
description?: string;
|
|
224
|
+
icon?: string;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Base class for chainable achievement configuration (Tier 2)
|
|
228
|
+
*/
|
|
229
|
+
declare abstract class Achievement {
|
|
230
|
+
protected metric: string;
|
|
231
|
+
protected award: AwardDetails;
|
|
232
|
+
constructor(metric: string, defaultAward: AwardDetails);
|
|
233
|
+
/**
|
|
234
|
+
* Customize the award details for this achievement
|
|
235
|
+
* @param award - Custom award details
|
|
236
|
+
* @returns This achievement for chaining
|
|
237
|
+
*/
|
|
238
|
+
withAward(award: AwardDetails): Achievement;
|
|
239
|
+
/**
|
|
240
|
+
* Convert this achievement to a SimpleAchievementConfig
|
|
241
|
+
*/
|
|
242
|
+
abstract toConfig(): SimpleAchievementConfig;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Threshold-based achievement (score, level, etc.)
|
|
246
|
+
*/
|
|
247
|
+
declare class ThresholdAchievement extends Achievement {
|
|
248
|
+
private threshold;
|
|
249
|
+
constructor(metric: string, threshold: number, defaultAward: AwardDetails);
|
|
250
|
+
toConfig(): SimpleAchievementConfig;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Boolean achievement (tutorial completion, first login, etc.)
|
|
254
|
+
*/
|
|
255
|
+
declare class BooleanAchievement extends Achievement {
|
|
256
|
+
toConfig(): SimpleAchievementConfig;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Value-based achievement (character class, difficulty, etc.)
|
|
260
|
+
*/
|
|
261
|
+
declare class ValueAchievement extends Achievement {
|
|
262
|
+
private value;
|
|
263
|
+
constructor(metric: string, value: string, defaultAward: AwardDetails);
|
|
264
|
+
toConfig(): SimpleAchievementConfig;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Complex achievement builder for power users (Tier 3)
|
|
268
|
+
*/
|
|
269
|
+
declare class ComplexAchievementBuilder {
|
|
270
|
+
private id;
|
|
271
|
+
private metric;
|
|
272
|
+
private condition;
|
|
273
|
+
private award;
|
|
274
|
+
/**
|
|
275
|
+
* Set the unique identifier for this achievement
|
|
276
|
+
*/
|
|
277
|
+
withId(id: string): ComplexAchievementBuilder;
|
|
278
|
+
/**
|
|
279
|
+
* Set the metric this achievement tracks
|
|
280
|
+
*/
|
|
281
|
+
withMetric(metric: string): ComplexAchievementBuilder;
|
|
282
|
+
/**
|
|
283
|
+
* Set the condition function that determines if achievement is unlocked
|
|
284
|
+
*/
|
|
285
|
+
withCondition(fn: (value: AchievementMetricValue, state: AchievementState) => boolean): ComplexAchievementBuilder;
|
|
286
|
+
/**
|
|
287
|
+
* Set the award details for this achievement
|
|
288
|
+
*/
|
|
289
|
+
withAward(award: AwardDetails): ComplexAchievementBuilder;
|
|
290
|
+
/**
|
|
291
|
+
* Build the final achievement configuration
|
|
292
|
+
*/
|
|
293
|
+
build(): SimpleAchievementConfig;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Main AchievementBuilder with three-tier API
|
|
297
|
+
* Tier 1: Simple static methods with smart defaults
|
|
298
|
+
* Tier 2: Chainable customization
|
|
299
|
+
* Tier 3: Full builder for complex logic
|
|
300
|
+
*/
|
|
301
|
+
declare class AchievementBuilder {
|
|
302
|
+
/**
|
|
303
|
+
* Create a single score achievement with smart defaults
|
|
304
|
+
* @param threshold - Score threshold to achieve
|
|
305
|
+
* @returns Chainable ThresholdAchievement
|
|
306
|
+
*/
|
|
307
|
+
static createScoreAchievement(threshold: number): ThresholdAchievement;
|
|
308
|
+
/**
|
|
309
|
+
* Create multiple score achievements
|
|
310
|
+
* @param thresholds - Array of thresholds or [threshold, award] tuples
|
|
311
|
+
* @returns Complete SimpleAchievementConfig
|
|
312
|
+
*/
|
|
313
|
+
static createScoreAchievements(thresholds: (number | [number, AwardDetails])[]): SimpleAchievementConfig;
|
|
314
|
+
/**
|
|
315
|
+
* Create a single level achievement with smart defaults
|
|
316
|
+
* @param level - Level threshold to achieve
|
|
317
|
+
* @returns Chainable ThresholdAchievement
|
|
318
|
+
*/
|
|
319
|
+
static createLevelAchievement(level: number): ThresholdAchievement;
|
|
320
|
+
/**
|
|
321
|
+
* Create multiple level achievements
|
|
322
|
+
* @param levels - Array of levels or [level, award] tuples
|
|
323
|
+
* @returns Complete SimpleAchievementConfig
|
|
324
|
+
*/
|
|
325
|
+
static createLevelAchievements(levels: (number | [number, AwardDetails])[]): SimpleAchievementConfig;
|
|
326
|
+
/**
|
|
327
|
+
* Create a boolean achievement with smart defaults
|
|
328
|
+
* @param metric - The metric name (e.g., 'completedTutorial')
|
|
329
|
+
* @returns Chainable BooleanAchievement
|
|
330
|
+
*/
|
|
331
|
+
static createBooleanAchievement(metric: string): BooleanAchievement;
|
|
332
|
+
/**
|
|
333
|
+
* Create a value-based achievement with smart defaults
|
|
334
|
+
* @param metric - The metric name (e.g., 'characterClass')
|
|
335
|
+
* @param value - The value to match (e.g., 'wizard')
|
|
336
|
+
* @returns Chainable ValueAchievement
|
|
337
|
+
*/
|
|
338
|
+
static createValueAchievement(metric: string, value: string): ValueAchievement;
|
|
339
|
+
/**
|
|
340
|
+
* Create a complex achievement builder for power users
|
|
341
|
+
* @returns ComplexAchievementBuilder for full control
|
|
342
|
+
*/
|
|
343
|
+
static create(): ComplexAchievementBuilder;
|
|
344
|
+
/**
|
|
345
|
+
* Combine multiple achievement configurations
|
|
346
|
+
* @param achievements - Array of SimpleAchievementConfig objects or Achievement instances
|
|
347
|
+
* @returns Combined SimpleAchievementConfig
|
|
348
|
+
*/
|
|
349
|
+
static combine(achievements: (SimpleAchievementConfig | Achievement)[]): SimpleAchievementConfig;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export { AchievementBuilder, AchievementCondition, AchievementConfiguration, AchievementConfigurationType, AchievementContext, AchievementContextValue, AchievementDetails, AchievementMetricArrayValue, AchievementMetricValue, AchievementMetrics, AchievementProvider, AchievementProviderProps$1 as AchievementProviderProps, AchievementState, AchievementStorage, AwardDetails, BadgesButton, BadgesModal, ConfettiWrapper, CustomAchievementDetails, InitialAchievementMetrics, LocalStorage, MemoryStorage, SimpleAchievementConfig, SimpleAchievementDetails, StorageType, StylesProps, defaultAchievementIcons, defaultStyles, isSimpleConfig, normalizeAchievements, useAchievements, useSimpleAchievements };
|
package/dist/index.js
CHANGED
|
@@ -99,6 +99,29 @@ class LocalStorage {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
class MemoryStorage {
|
|
103
|
+
constructor() {
|
|
104
|
+
this.metrics = {};
|
|
105
|
+
this.unlockedAchievements = [];
|
|
106
|
+
}
|
|
107
|
+
getMetrics() {
|
|
108
|
+
return this.metrics;
|
|
109
|
+
}
|
|
110
|
+
setMetrics(metrics) {
|
|
111
|
+
this.metrics = metrics;
|
|
112
|
+
}
|
|
113
|
+
getUnlockedAchievements() {
|
|
114
|
+
return this.unlockedAchievements;
|
|
115
|
+
}
|
|
116
|
+
setUnlockedAchievements(achievements) {
|
|
117
|
+
this.unlockedAchievements = achievements;
|
|
118
|
+
}
|
|
119
|
+
clear() {
|
|
120
|
+
this.metrics = {};
|
|
121
|
+
this.unlockedAchievements = [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
102
125
|
const getPositionStyles = (position) => {
|
|
103
126
|
const base = {
|
|
104
127
|
position: 'fixed',
|
|
@@ -129,94 +152,14 @@ const BadgesButton = ({ onClick, position, styles = {}, unlockedAchievements })
|
|
|
129
152
|
};
|
|
130
153
|
|
|
131
154
|
const defaultAchievementIcons = {
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
milestoneReached: '🏅',
|
|
139
|
-
firstStep: '👣',
|
|
140
|
-
newBeginnings: '🌱',
|
|
141
|
-
breakthrough: '💡',
|
|
142
|
-
growth: '📈',
|
|
143
|
-
// Social & Engagement
|
|
144
|
-
shared: '🔗',
|
|
145
|
-
liked: '❤️',
|
|
146
|
-
commented: '💬',
|
|
147
|
-
followed: '👥',
|
|
148
|
-
invited: '🤝',
|
|
149
|
-
communityMember: '🏘️',
|
|
150
|
-
supporter: '🌟',
|
|
151
|
-
connected: '🌐',
|
|
152
|
-
participant: '🙋',
|
|
153
|
-
influencer: '📣',
|
|
154
|
-
// Time & Activity
|
|
155
|
-
activeDay: '☀️',
|
|
156
|
-
activeWeek: '📅',
|
|
157
|
-
activeMonth: '🗓️',
|
|
158
|
-
earlyBird: '⏰',
|
|
159
|
-
nightOwl: '🌙',
|
|
160
|
-
streak: '🔥',
|
|
161
|
-
dedicated: '⏳',
|
|
162
|
-
punctual: '⏱️',
|
|
163
|
-
consistent: '🔄',
|
|
164
|
-
marathon: '🏃',
|
|
165
|
-
// Creativity & Skill
|
|
166
|
-
artist: '🎨',
|
|
167
|
-
writer: '✍️',
|
|
168
|
-
innovator: '🔬',
|
|
169
|
-
creator: '🛠️',
|
|
170
|
-
expert: '🎓',
|
|
171
|
-
master: '👑',
|
|
172
|
-
pioneer: '🚀',
|
|
173
|
-
performer: '🎭',
|
|
174
|
-
thinker: '🧠',
|
|
175
|
-
explorer: '🗺️',
|
|
176
|
-
// Achievement Types
|
|
177
|
-
bronze: '🥉',
|
|
178
|
-
silver: '🥈',
|
|
179
|
-
gold: '🥇',
|
|
180
|
-
diamond: '💎',
|
|
181
|
-
legendary: '✨',
|
|
182
|
-
epic: '💥',
|
|
183
|
-
rare: '🔮',
|
|
184
|
-
common: '🔘',
|
|
185
|
-
special: '🎁',
|
|
186
|
-
hidden: '❓',
|
|
187
|
-
// Numbers & Counters
|
|
188
|
-
one: '1️⃣',
|
|
189
|
-
ten: '🔟',
|
|
190
|
-
hundred: '💯',
|
|
191
|
-
thousand: '🔢',
|
|
192
|
-
// Actions & Interactions
|
|
193
|
-
clicked: '🖱️',
|
|
194
|
-
used: '🔑',
|
|
195
|
-
found: '🔍',
|
|
196
|
-
built: '🧱',
|
|
197
|
-
solved: '🧩',
|
|
198
|
-
discovered: '🔭',
|
|
199
|
-
unlocked: '🔓',
|
|
200
|
-
upgraded: '⬆️',
|
|
201
|
-
repaired: '🔧',
|
|
202
|
-
defended: '🛡️',
|
|
203
|
-
// Placeholders
|
|
204
|
-
default: '⭐', // A fallback icon
|
|
205
|
-
loading: '⏳',
|
|
206
|
-
error: '⚠️',
|
|
207
|
-
success: '✅',
|
|
208
|
-
failure: '❌',
|
|
209
|
-
// Miscellaneous
|
|
155
|
+
// Essential fallback icons for system use
|
|
156
|
+
default: '⭐', // Fallback when no icon is provided
|
|
157
|
+
loading: '⏳', // For loading states
|
|
158
|
+
error: '⚠️', // For error states
|
|
159
|
+
success: '✅', // For success states
|
|
160
|
+
// A few common icons for backward compatibility
|
|
210
161
|
trophy: '🏆',
|
|
211
162
|
star: '⭐',
|
|
212
|
-
flag: '🚩',
|
|
213
|
-
puzzle: '🧩',
|
|
214
|
-
gem: '💎',
|
|
215
|
-
crown: '👑',
|
|
216
|
-
medal: '🏅',
|
|
217
|
-
ribbon: '🎗️',
|
|
218
|
-
badge: '🎖️',
|
|
219
|
-
shield: '🛡️',
|
|
220
163
|
};
|
|
221
164
|
|
|
222
165
|
const BadgesModal = ({ isOpen, onClose, achievements, styles = {}, icons = {} }) => {
|
|
@@ -406,29 +349,6 @@ function normalizeAchievements(config) {
|
|
|
406
349
|
return normalized;
|
|
407
350
|
}
|
|
408
351
|
|
|
409
|
-
class MemoryStorage {
|
|
410
|
-
constructor() {
|
|
411
|
-
this.metrics = {};
|
|
412
|
-
this.unlockedAchievements = [];
|
|
413
|
-
}
|
|
414
|
-
getMetrics() {
|
|
415
|
-
return this.metrics;
|
|
416
|
-
}
|
|
417
|
-
setMetrics(metrics) {
|
|
418
|
-
this.metrics = metrics;
|
|
419
|
-
}
|
|
420
|
-
getUnlockedAchievements() {
|
|
421
|
-
return this.unlockedAchievements;
|
|
422
|
-
}
|
|
423
|
-
setUnlockedAchievements(achievements) {
|
|
424
|
-
this.unlockedAchievements = achievements;
|
|
425
|
-
}
|
|
426
|
-
clear() {
|
|
427
|
-
this.metrics = {};
|
|
428
|
-
this.unlockedAchievements = [];
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
352
|
const AchievementContext = createContext(undefined);
|
|
433
353
|
const AchievementProvider = ({ achievements: achievementsConfig, storage = StorageType.Local, children, icons = {}, }) => {
|
|
434
354
|
// Normalize the configuration to the complex format
|
|
@@ -764,5 +684,291 @@ const defaultStyles = {
|
|
|
764
684
|
},
|
|
765
685
|
};
|
|
766
686
|
|
|
767
|
-
|
|
687
|
+
/**
|
|
688
|
+
* Base class for chainable achievement configuration (Tier 2)
|
|
689
|
+
*/
|
|
690
|
+
class Achievement {
|
|
691
|
+
constructor(metric, defaultAward) {
|
|
692
|
+
this.metric = metric;
|
|
693
|
+
this.award = defaultAward;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Customize the award details for this achievement
|
|
697
|
+
* @param award - Custom award details
|
|
698
|
+
* @returns This achievement for chaining
|
|
699
|
+
*/
|
|
700
|
+
withAward(award) {
|
|
701
|
+
this.award = Object.assign(Object.assign({}, this.award), award);
|
|
702
|
+
return this;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Threshold-based achievement (score, level, etc.)
|
|
707
|
+
*/
|
|
708
|
+
class ThresholdAchievement extends Achievement {
|
|
709
|
+
constructor(metric, threshold, defaultAward) {
|
|
710
|
+
super(metric, defaultAward);
|
|
711
|
+
this.threshold = threshold;
|
|
712
|
+
}
|
|
713
|
+
toConfig() {
|
|
714
|
+
return {
|
|
715
|
+
[this.metric]: {
|
|
716
|
+
[this.threshold]: {
|
|
717
|
+
title: this.award.title,
|
|
718
|
+
description: this.award.description,
|
|
719
|
+
icon: this.award.icon
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Boolean achievement (tutorial completion, first login, etc.)
|
|
727
|
+
*/
|
|
728
|
+
class BooleanAchievement extends Achievement {
|
|
729
|
+
toConfig() {
|
|
730
|
+
return {
|
|
731
|
+
[this.metric]: {
|
|
732
|
+
true: {
|
|
733
|
+
title: this.award.title,
|
|
734
|
+
description: this.award.description,
|
|
735
|
+
icon: this.award.icon
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Value-based achievement (character class, difficulty, etc.)
|
|
743
|
+
*/
|
|
744
|
+
class ValueAchievement extends Achievement {
|
|
745
|
+
constructor(metric, value, defaultAward) {
|
|
746
|
+
super(metric, defaultAward);
|
|
747
|
+
this.value = value;
|
|
748
|
+
}
|
|
749
|
+
toConfig() {
|
|
750
|
+
return {
|
|
751
|
+
[this.metric]: {
|
|
752
|
+
[this.value]: {
|
|
753
|
+
title: this.award.title,
|
|
754
|
+
description: this.award.description,
|
|
755
|
+
icon: this.award.icon
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Complex achievement builder for power users (Tier 3)
|
|
763
|
+
*/
|
|
764
|
+
class ComplexAchievementBuilder {
|
|
765
|
+
constructor() {
|
|
766
|
+
this.id = '';
|
|
767
|
+
this.metric = '';
|
|
768
|
+
this.condition = null;
|
|
769
|
+
this.award = {};
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Set the unique identifier for this achievement
|
|
773
|
+
*/
|
|
774
|
+
withId(id) {
|
|
775
|
+
this.id = id;
|
|
776
|
+
return this;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Set the metric this achievement tracks
|
|
780
|
+
*/
|
|
781
|
+
withMetric(metric) {
|
|
782
|
+
this.metric = metric;
|
|
783
|
+
return this;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Set the condition function that determines if achievement is unlocked
|
|
787
|
+
*/
|
|
788
|
+
withCondition(fn) {
|
|
789
|
+
this.condition = fn;
|
|
790
|
+
return this;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Set the award details for this achievement
|
|
794
|
+
*/
|
|
795
|
+
withAward(award) {
|
|
796
|
+
this.award = Object.assign(Object.assign({}, this.award), award);
|
|
797
|
+
return this;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Build the final achievement configuration
|
|
801
|
+
*/
|
|
802
|
+
build() {
|
|
803
|
+
if (!this.id || !this.metric || !this.condition) {
|
|
804
|
+
throw new Error('Complex achievement requires id, metric, and condition');
|
|
805
|
+
}
|
|
806
|
+
// Convert our two-parameter condition function to the single-parameter format
|
|
807
|
+
// expected by the existing CustomAchievementDetails type
|
|
808
|
+
const compatibleCondition = (metrics) => {
|
|
809
|
+
const state = {
|
|
810
|
+
metrics: {}, // We don't have access to the full metrics structure here
|
|
811
|
+
unlockedAchievements: []
|
|
812
|
+
};
|
|
813
|
+
return this.condition(metrics[this.metric], state);
|
|
814
|
+
};
|
|
815
|
+
return {
|
|
816
|
+
[this.id]: {
|
|
817
|
+
custom: {
|
|
818
|
+
title: this.award.title || this.id,
|
|
819
|
+
description: this.award.description || `Achieve ${this.award.title || this.id}`,
|
|
820
|
+
icon: this.award.icon || '💎',
|
|
821
|
+
condition: compatibleCondition
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Main AchievementBuilder with three-tier API
|
|
829
|
+
* Tier 1: Simple static methods with smart defaults
|
|
830
|
+
* Tier 2: Chainable customization
|
|
831
|
+
* Tier 3: Full builder for complex logic
|
|
832
|
+
*/
|
|
833
|
+
class AchievementBuilder {
|
|
834
|
+
// TIER 1: Simple Static Methods (90% of use cases)
|
|
835
|
+
/**
|
|
836
|
+
* Create a single score achievement with smart defaults
|
|
837
|
+
* @param threshold - Score threshold to achieve
|
|
838
|
+
* @returns Chainable ThresholdAchievement
|
|
839
|
+
*/
|
|
840
|
+
static createScoreAchievement(threshold) {
|
|
841
|
+
return new ThresholdAchievement('score', threshold, {
|
|
842
|
+
title: `Score ${threshold}!`,
|
|
843
|
+
description: `Score ${threshold} points`,
|
|
844
|
+
icon: '🏆'
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Create multiple score achievements
|
|
849
|
+
* @param thresholds - Array of thresholds or [threshold, award] tuples
|
|
850
|
+
* @returns Complete SimpleAchievementConfig
|
|
851
|
+
*/
|
|
852
|
+
static createScoreAchievements(thresholds) {
|
|
853
|
+
const config = { score: {} };
|
|
854
|
+
thresholds.forEach(item => {
|
|
855
|
+
if (typeof item === 'number') {
|
|
856
|
+
// Use default award
|
|
857
|
+
config.score[item] = {
|
|
858
|
+
title: `Score ${item}!`,
|
|
859
|
+
description: `Score ${item} points`,
|
|
860
|
+
icon: '🏆'
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
else {
|
|
864
|
+
// Custom award
|
|
865
|
+
const [threshold, award] = item;
|
|
866
|
+
config.score[threshold] = {
|
|
867
|
+
title: award.title || `Score ${threshold}!`,
|
|
868
|
+
description: award.description || `Score ${threshold} points`,
|
|
869
|
+
icon: award.icon || '🏆'
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
return config;
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Create a single level achievement with smart defaults
|
|
877
|
+
* @param level - Level threshold to achieve
|
|
878
|
+
* @returns Chainable ThresholdAchievement
|
|
879
|
+
*/
|
|
880
|
+
static createLevelAchievement(level) {
|
|
881
|
+
return new ThresholdAchievement('level', level, {
|
|
882
|
+
title: `Level ${level}!`,
|
|
883
|
+
description: `Reach level ${level}`,
|
|
884
|
+
icon: '📈'
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Create multiple level achievements
|
|
889
|
+
* @param levels - Array of levels or [level, award] tuples
|
|
890
|
+
* @returns Complete SimpleAchievementConfig
|
|
891
|
+
*/
|
|
892
|
+
static createLevelAchievements(levels) {
|
|
893
|
+
const config = { level: {} };
|
|
894
|
+
levels.forEach(item => {
|
|
895
|
+
if (typeof item === 'number') {
|
|
896
|
+
// Use default award
|
|
897
|
+
config.level[item] = {
|
|
898
|
+
title: `Level ${item}!`,
|
|
899
|
+
description: `Reach level ${item}`,
|
|
900
|
+
icon: '📈'
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
// Custom award
|
|
905
|
+
const [level, award] = item;
|
|
906
|
+
config.level[level] = {
|
|
907
|
+
title: award.title || `Level ${level}!`,
|
|
908
|
+
description: award.description || `Reach level ${level}`,
|
|
909
|
+
icon: award.icon || '📈'
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
return config;
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Create a boolean achievement with smart defaults
|
|
917
|
+
* @param metric - The metric name (e.g., 'completedTutorial')
|
|
918
|
+
* @returns Chainable BooleanAchievement
|
|
919
|
+
*/
|
|
920
|
+
static createBooleanAchievement(metric) {
|
|
921
|
+
// Convert camelCase to Title Case
|
|
922
|
+
const formattedMetric = metric.replace(/([A-Z])/g, ' $1').toLowerCase();
|
|
923
|
+
const titleCase = formattedMetric.charAt(0).toUpperCase() + formattedMetric.slice(1);
|
|
924
|
+
return new BooleanAchievement(metric, {
|
|
925
|
+
title: `${titleCase}!`,
|
|
926
|
+
description: `Complete ${formattedMetric}`,
|
|
927
|
+
icon: '✅'
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Create a value-based achievement with smart defaults
|
|
932
|
+
* @param metric - The metric name (e.g., 'characterClass')
|
|
933
|
+
* @param value - The value to match (e.g., 'wizard')
|
|
934
|
+
* @returns Chainable ValueAchievement
|
|
935
|
+
*/
|
|
936
|
+
static createValueAchievement(metric, value) {
|
|
937
|
+
const formattedValue = value.charAt(0).toUpperCase() + value.slice(1);
|
|
938
|
+
return new ValueAchievement(metric, value, {
|
|
939
|
+
title: `${formattedValue}!`,
|
|
940
|
+
description: `Choose ${formattedValue.toLowerCase()} for ${metric}`,
|
|
941
|
+
icon: '🎯'
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
// TIER 3: Full Builder for Complex Logic
|
|
945
|
+
/**
|
|
946
|
+
* Create a complex achievement builder for power users
|
|
947
|
+
* @returns ComplexAchievementBuilder for full control
|
|
948
|
+
*/
|
|
949
|
+
static create() {
|
|
950
|
+
return new ComplexAchievementBuilder();
|
|
951
|
+
}
|
|
952
|
+
// UTILITY METHODS
|
|
953
|
+
/**
|
|
954
|
+
* Combine multiple achievement configurations
|
|
955
|
+
* @param achievements - Array of SimpleAchievementConfig objects or Achievement instances
|
|
956
|
+
* @returns Combined SimpleAchievementConfig
|
|
957
|
+
*/
|
|
958
|
+
static combine(achievements) {
|
|
959
|
+
const combined = {};
|
|
960
|
+
achievements.forEach(achievement => {
|
|
961
|
+
const config = achievement instanceof Achievement ? achievement.toConfig() : achievement;
|
|
962
|
+
Object.keys(config).forEach(key => {
|
|
963
|
+
if (!combined[key]) {
|
|
964
|
+
combined[key] = {};
|
|
965
|
+
}
|
|
966
|
+
Object.assign(combined[key], config[key]);
|
|
967
|
+
});
|
|
968
|
+
});
|
|
969
|
+
return combined;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
export { AchievementBuilder, AchievementContext, AchievementProvider, BadgesButton, BadgesModal, ConfettiWrapper, LocalStorage, MemoryStorage, StorageType, defaultAchievementIcons, defaultStyles, isSimpleConfig, normalizeAchievements, useAchievements, useSimpleAchievements };
|
|
768
974
|
//# sourceMappingURL=index.js.map
|