react-achievements 4.2.0 → 4.4.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 +37 -3
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.d.ts +25 -5
- package/dist/headless.esm.js.map +1 -1
- package/dist/index.cjs +202 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +38 -12
- package/dist/index.esm.js +204 -116
- package/dist/index.esm.js.map +1 -1
- package/dist/web.cjs +202 -114
- package/dist/web.cjs.map +1 -1
- package/dist/web.d.ts +38 -12
- package/dist/web.esm.js +204 -116
- package/dist/web.esm.js.map +1 -1
- package/package.json +6 -2
package/dist/web.esm.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { AchievementEngine } from 'achievements-engine';
|
|
1
|
+
import { AchievementEngine, isSimpleConfig, normalizeAchievements } from 'achievements-engine';
|
|
2
2
|
export { AchievementBuilder, AchievementEngine, AchievementError, AsyncStorageAdapter, ConfigurationError, ImportValidationError, IndexedDBStorage, LocalStorage, MemoryStorage, OfflineQueueStorage, RestApiStorage, StorageError, StorageQuotaError, StorageType, SyncError, createConfigHash, exportAchievementData, importAchievementData, isAchievementError, isRecoverableError, isSimpleConfig, normalizeAchievements } from 'achievements-engine';
|
|
3
|
-
import React, { createContext, useState, useCallback, useEffect, useContext,
|
|
3
|
+
import React, { createContext, useState, useCallback, useEffect, useContext, useRef, useMemo } from 'react';
|
|
4
|
+
import confetti from 'canvas-confetti';
|
|
4
5
|
|
|
5
6
|
// Type guard to detect async storage
|
|
6
7
|
function isAsyncStorage(storage) {
|
|
@@ -213,7 +214,7 @@ const builtInThemes = {
|
|
|
213
214
|
},
|
|
214
215
|
confetti: {
|
|
215
216
|
colors: ['#FFD700', '#4CAF50', '#2196F3', '#FF6B6B'],
|
|
216
|
-
particleCount:
|
|
217
|
+
particleCount: 80,
|
|
217
218
|
shapes: ['circle', 'square'],
|
|
218
219
|
},
|
|
219
220
|
},
|
|
@@ -245,7 +246,7 @@ const builtInThemes = {
|
|
|
245
246
|
},
|
|
246
247
|
confetti: {
|
|
247
248
|
colors: ['#4CAF50', '#2196F3'],
|
|
248
|
-
particleCount:
|
|
249
|
+
particleCount: 40,
|
|
249
250
|
shapes: ['circle'],
|
|
250
251
|
},
|
|
251
252
|
},
|
|
@@ -280,7 +281,7 @@ const builtInThemes = {
|
|
|
280
281
|
},
|
|
281
282
|
confetti: {
|
|
282
283
|
colors: ['#22d3ee', '#f97316', '#a855f7', '#eab308'], // Cyan, orange, purple, yellow
|
|
283
|
-
particleCount:
|
|
284
|
+
particleCount: 120,
|
|
284
285
|
shapes: ['circle', 'square'],
|
|
285
286
|
},
|
|
286
287
|
},
|
|
@@ -316,24 +317,36 @@ const BuiltInNotification = ({ achievement, onClose, duration = 5000, position =
|
|
|
316
317
|
var _a, _b, _c;
|
|
317
318
|
const [isVisible, setIsVisible] = useState(false);
|
|
318
319
|
const [isExiting, setIsExiting] = useState(false);
|
|
320
|
+
const onCloseRef = useRef(onClose);
|
|
321
|
+
const exitTimerRef = useRef(null);
|
|
319
322
|
// Merge custom icons with defaults
|
|
320
323
|
const mergedIcons = Object.assign(Object.assign({}, defaultAchievementIcons), icons);
|
|
321
324
|
// Get theme configuration
|
|
322
325
|
const themeConfig = getTheme(theme) || builtInThemes.modern;
|
|
323
326
|
const { notification: themeStyles } = themeConfig;
|
|
327
|
+
useEffect(() => {
|
|
328
|
+
onCloseRef.current = onClose;
|
|
329
|
+
}, [onClose]);
|
|
330
|
+
const closeAfterExit = useCallback(() => {
|
|
331
|
+
setIsExiting(true);
|
|
332
|
+
if (exitTimerRef.current) {
|
|
333
|
+
clearTimeout(exitTimerRef.current);
|
|
334
|
+
}
|
|
335
|
+
exitTimerRef.current = setTimeout(() => { var _a; return (_a = onCloseRef.current) === null || _a === void 0 ? void 0 : _a.call(onCloseRef); }, 300);
|
|
336
|
+
}, []);
|
|
324
337
|
useEffect(() => {
|
|
325
338
|
// Slide in animation
|
|
326
339
|
const showTimer = setTimeout(() => setIsVisible(true), 10);
|
|
327
340
|
// Auto-dismiss
|
|
328
|
-
const dismissTimer = setTimeout(
|
|
329
|
-
setIsExiting(true);
|
|
330
|
-
setTimeout(() => onClose === null || onClose === void 0 ? void 0 : onClose(), 300);
|
|
331
|
-
}, duration);
|
|
341
|
+
const dismissTimer = setTimeout(closeAfterExit, duration);
|
|
332
342
|
return () => {
|
|
333
343
|
clearTimeout(showTimer);
|
|
334
344
|
clearTimeout(dismissTimer);
|
|
345
|
+
if (exitTimerRef.current) {
|
|
346
|
+
clearTimeout(exitTimerRef.current);
|
|
347
|
+
}
|
|
335
348
|
};
|
|
336
|
-
}, [duration,
|
|
349
|
+
}, [duration, closeAfterExit]);
|
|
337
350
|
const getPositionStyles = () => {
|
|
338
351
|
const stackedOffset = 20 + stackIndex * 104;
|
|
339
352
|
const base = {
|
|
@@ -421,132 +434,162 @@ const BuiltInNotification = ({ achievement, onClose, duration = 5000, position =
|
|
|
421
434
|
React.createElement("div", { style: headerStyles }, "Achievement Unlocked!"),
|
|
422
435
|
React.createElement("div", { style: titleStyles }, achievement.achievementTitle),
|
|
423
436
|
achievement.achievementDescription && (React.createElement("div", { style: descriptionStyles }, achievement.achievementDescription))),
|
|
424
|
-
React.createElement("button", { onClick: () =>
|
|
425
|
-
setIsExiting(true);
|
|
426
|
-
setTimeout(() => onClose === null || onClose === void 0 ? void 0 : onClose(), 300);
|
|
427
|
-
}, style: closeButtonStyles, onMouseEnter: (e) => (e.currentTarget.style.opacity = '1'), onMouseLeave: (e) => (e.currentTarget.style.opacity = '0.6'), "aria-label": "Close notification" }, "\u00D7")));
|
|
437
|
+
React.createElement("button", { onClick: closeAfterExit, style: closeButtonStyles, onMouseEnter: (e) => (e.currentTarget.style.opacity = '1'), onMouseLeave: (e) => (e.currentTarget.style.opacity = '0.6'), "aria-label": "Close notification" }, "\u00D7")));
|
|
428
438
|
};
|
|
429
439
|
|
|
440
|
+
const DEFAULT_COLORS = ['#FFD700', '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'];
|
|
441
|
+
const DEFAULT_DURATION_MS = 5000;
|
|
442
|
+
const DEFAULT_PARTICLE_COUNT = 80;
|
|
443
|
+
const DEFAULT_SHAPES = ['square', 'circle'];
|
|
444
|
+
const DEFAULT_SPREAD = 70;
|
|
445
|
+
const DEFAULT_START_VELOCITY = 45;
|
|
446
|
+
const DEFAULT_GRAVITY = 1;
|
|
447
|
+
const DEFAULT_SCALAR = 1;
|
|
448
|
+
const DEFAULT_Z_INDEX = 10001;
|
|
449
|
+
const getSafeParticleCount = (count) => Math.max(0, Math.floor(count));
|
|
430
450
|
/**
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
* @returns Object with width and height properties
|
|
435
|
-
*
|
|
436
|
-
* @example
|
|
437
|
-
* ```tsx
|
|
438
|
-
* const { width, height } = useWindowSize();
|
|
439
|
-
* console.log(`Window size: ${width}x${height}`);
|
|
440
|
-
* ```
|
|
451
|
+
* Built-in confetti component
|
|
452
|
+
* Canvas-based confetti animation powered by canvas-confetti.
|
|
441
453
|
*/
|
|
442
|
-
|
|
443
|
-
const [
|
|
444
|
-
width: typeof window !== 'undefined' ? window.innerWidth : 0,
|
|
445
|
-
height: typeof window !== 'undefined' ? window.innerHeight : 0,
|
|
446
|
-
});
|
|
454
|
+
const BuiltInConfetti = ({ show, duration = DEFAULT_DURATION_MS, particleCount = DEFAULT_PARTICLE_COUNT, colors = DEFAULT_COLORS, shapes = DEFAULT_SHAPES, spread = DEFAULT_SPREAD, startVelocity = DEFAULT_START_VELOCITY, gravity = DEFAULT_GRAVITY, scalar = DEFAULT_SCALAR, zIndex = DEFAULT_Z_INDEX, }) => {
|
|
455
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
447
456
|
useEffect(() => {
|
|
448
|
-
|
|
449
|
-
|
|
457
|
+
if (!show) {
|
|
458
|
+
setIsVisible(false);
|
|
459
|
+
confetti.reset();
|
|
450
460
|
return;
|
|
451
461
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
462
|
+
setIsVisible(true);
|
|
463
|
+
const totalParticles = getSafeParticleCount(particleCount);
|
|
464
|
+
const resolvedColors = colors.length > 0 ? colors : DEFAULT_COLORS;
|
|
465
|
+
const resolvedShapes = shapes.length > 0 ? shapes : DEFAULT_SHAPES;
|
|
466
|
+
const baseOptions = {
|
|
467
|
+
colors: resolvedColors,
|
|
468
|
+
shapes: resolvedShapes,
|
|
469
|
+
spread,
|
|
470
|
+
startVelocity,
|
|
471
|
+
gravity,
|
|
472
|
+
scalar,
|
|
473
|
+
zIndex,
|
|
474
|
+
disableForReducedMotion: true,
|
|
457
475
|
};
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
};
|
|
466
|
-
}, []);
|
|
467
|
-
return size;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* Built-in confetti component
|
|
472
|
-
* Lightweight CSS-based confetti animation
|
|
473
|
-
*/
|
|
474
|
-
const BuiltInConfetti = ({ show, duration = 5000, particleCount = 50, colors = ['#FFD700', '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'], }) => {
|
|
475
|
-
const [isVisible, setIsVisible] = useState(false);
|
|
476
|
-
const { width, height } = useWindowSize();
|
|
477
|
-
useEffect(() => {
|
|
478
|
-
if (show) {
|
|
479
|
-
setIsVisible(true);
|
|
480
|
-
const timer = setTimeout(() => setIsVisible(false), duration);
|
|
481
|
-
return () => clearTimeout(timer);
|
|
476
|
+
if (totalParticles > 0) {
|
|
477
|
+
const centerParticles = Math.ceil(totalParticles * 0.5);
|
|
478
|
+
const leftParticles = Math.floor((totalParticles - centerParticles) / 2);
|
|
479
|
+
const rightParticles = totalParticles - centerParticles - leftParticles;
|
|
480
|
+
confetti(Object.assign(Object.assign({}, baseOptions), { particleCount: centerParticles, origin: { x: 0.5, y: 0.58 } }));
|
|
481
|
+
confetti(Object.assign(Object.assign({}, baseOptions), { particleCount: leftParticles, angle: 60, spread: Math.max(45, spread * 0.8), origin: { x: 0, y: 0.72 } }));
|
|
482
|
+
confetti(Object.assign(Object.assign({}, baseOptions), { particleCount: rightParticles, angle: 120, spread: Math.max(45, spread * 0.8), origin: { x: 1, y: 0.72 } }));
|
|
482
483
|
}
|
|
483
|
-
|
|
484
|
+
const timer = setTimeout(() => {
|
|
484
485
|
setIsVisible(false);
|
|
485
|
-
|
|
486
|
-
|
|
486
|
+
confetti.reset();
|
|
487
|
+
}, duration);
|
|
488
|
+
return () => {
|
|
489
|
+
clearTimeout(timer);
|
|
490
|
+
confetti.reset();
|
|
491
|
+
};
|
|
492
|
+
}, [
|
|
493
|
+
show,
|
|
494
|
+
duration,
|
|
495
|
+
particleCount,
|
|
496
|
+
colors,
|
|
497
|
+
shapes,
|
|
498
|
+
spread,
|
|
499
|
+
startVelocity,
|
|
500
|
+
gravity,
|
|
501
|
+
scalar,
|
|
502
|
+
zIndex,
|
|
503
|
+
]);
|
|
487
504
|
if (!isVisible)
|
|
488
505
|
return null;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const rotation = Math.random() * 360;
|
|
504
|
-
const fallDuration = 3 + Math.random() * 2; // 3-5 seconds
|
|
505
|
-
const delay = Math.random() * 0.5; // 0-0.5s delay
|
|
506
|
-
const shape = Math.random() > 0.5 ? 'circle' : 'square';
|
|
507
|
-
const particleStyles = {
|
|
508
|
-
position: 'absolute',
|
|
509
|
-
top: '-20px',
|
|
510
|
-
left: `${startX}px`,
|
|
511
|
-
width: '10px',
|
|
512
|
-
height: '10px',
|
|
513
|
-
backgroundColor: color,
|
|
514
|
-
borderRadius: shape === 'circle' ? '50%' : '0',
|
|
515
|
-
transform: `rotate(${rotation}deg)`,
|
|
516
|
-
animation: `confettiFall ${fallDuration}s linear ${delay}s forwards`,
|
|
517
|
-
opacity: 0.9,
|
|
518
|
-
};
|
|
519
|
-
return React.createElement("div", { key: i, style: particleStyles, "data-testid": "confetti-particle" });
|
|
520
|
-
});
|
|
521
|
-
return (React.createElement(React.Fragment, null,
|
|
522
|
-
React.createElement("style", null, `
|
|
523
|
-
@keyframes confettiFall {
|
|
524
|
-
0% {
|
|
525
|
-
transform: translateY(0) rotate(0deg);
|
|
526
|
-
opacity: 1;
|
|
506
|
+
return React.createElement("div", { "data-testid": "built-in-confetti", style: { display: 'none' } });
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
const DEFAULT_NOTIFICATION_DURATION_MS = 5000;
|
|
510
|
+
const CONFETTI_DURATION_MS = 5000;
|
|
511
|
+
const hasConfiguredConfetti = (confetti) => {
|
|
512
|
+
return confetti === false || (typeof confetti === 'object' && confetti !== null);
|
|
513
|
+
};
|
|
514
|
+
const collectComplexConfetti = (achievements, confettiByAchievementId) => {
|
|
515
|
+
Object.values(achievements).forEach((metricAchievements) => {
|
|
516
|
+
metricAchievements.forEach(({ achievementDetails }) => {
|
|
517
|
+
const confetti = achievementDetails.confetti;
|
|
518
|
+
if (hasConfiguredConfetti(confetti)) {
|
|
519
|
+
confettiByAchievementId.set(achievementDetails.achievementId, confetti);
|
|
527
520
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
};
|
|
524
|
+
const simpleConfigHasRewardConfetti = (achievements) => {
|
|
525
|
+
return Object.values(achievements).some((metricAchievements) => Object.values(metricAchievements).some((achievement) => hasConfiguredConfetti(achievement.confetti)));
|
|
526
|
+
};
|
|
527
|
+
const prepareAchievementsForConfetti = (achievements) => {
|
|
528
|
+
const confettiByAchievementId = new Map();
|
|
529
|
+
if (!achievements) {
|
|
530
|
+
return { achievements, confettiByAchievementId };
|
|
531
|
+
}
|
|
532
|
+
if (!isSimpleConfig(achievements)) {
|
|
533
|
+
collectComplexConfetti(achievements, confettiByAchievementId);
|
|
534
|
+
return { achievements, confettiByAchievementId };
|
|
535
|
+
}
|
|
536
|
+
const simpleAchievements = achievements;
|
|
537
|
+
if (!simpleConfigHasRewardConfetti(simpleAchievements)) {
|
|
538
|
+
return { achievements, confettiByAchievementId };
|
|
539
|
+
}
|
|
540
|
+
const normalizedAchievements = normalizeAchievements(simpleAchievements);
|
|
541
|
+
Object.entries(simpleAchievements).forEach(([metric, metricAchievements]) => {
|
|
542
|
+
const normalizedMetricAchievements = normalizedAchievements[metric] || [];
|
|
543
|
+
Object.values(metricAchievements).forEach((achievement, index) => {
|
|
544
|
+
const confetti = achievement.confetti;
|
|
545
|
+
const normalizedAchievement = normalizedMetricAchievements[index];
|
|
546
|
+
if (normalizedAchievement && hasConfiguredConfetti(confetti)) {
|
|
547
|
+
normalizedAchievement.achievementDetails.confetti = confetti;
|
|
548
|
+
confettiByAchievementId.set(normalizedAchievement.achievementDetails.achievementId, confetti);
|
|
531
549
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
return { achievements: normalizedAchievements, confettiByAchievementId };
|
|
535
553
|
};
|
|
536
|
-
|
|
537
|
-
const NOTIFICATION_DURATION_MS = 5000;
|
|
538
554
|
const AchievementUIContext = createContext({
|
|
539
555
|
icons: {},
|
|
540
556
|
ui: {},
|
|
541
557
|
});
|
|
542
|
-
const AchievementEffects = ({ icons, ui }) => {
|
|
558
|
+
const AchievementEffects = ({ icons, ui, achievementConfetti }) => {
|
|
559
|
+
var _a, _b;
|
|
543
560
|
const engine = useAchievementEngine();
|
|
544
561
|
const seenAchievementsRef = useRef(new Set(engine.getUnlocked()));
|
|
545
562
|
const confettiTimerRef = useRef(null);
|
|
563
|
+
const achievementConfettiRef = useRef(achievementConfetti);
|
|
546
564
|
const [showConfetti, setShowConfetti] = useState(false);
|
|
565
|
+
const [activeConfettiConfig, setActiveConfettiConfig] = useState(null);
|
|
547
566
|
const [notifications, setNotifications] = useState([]);
|
|
567
|
+
const notificationDuration = (_a = ui.notificationDuration) !== null && _a !== void 0 ? _a : DEFAULT_NOTIFICATION_DURATION_MS;
|
|
568
|
+
const themeName = ui.theme || 'modern';
|
|
569
|
+
const themeConfig = useMemo(() => getTheme(themeName) || builtInThemes.modern, [themeName]);
|
|
570
|
+
const globalConfettiConfig = useMemo(() => (Object.assign(Object.assign({}, themeConfig.confetti), ui.confetti)), [themeConfig, ui.confetti]);
|
|
571
|
+
const globalConfettiConfigRef = useRef(globalConfettiConfig);
|
|
572
|
+
const renderedConfettiConfig = showConfetti && activeConfettiConfig ? activeConfettiConfig : globalConfettiConfig;
|
|
573
|
+
const renderedConfettiDuration = (_b = renderedConfettiConfig.duration) !== null && _b !== void 0 ? _b : CONFETTI_DURATION_MS;
|
|
574
|
+
useEffect(() => {
|
|
575
|
+
achievementConfettiRef.current = achievementConfetti;
|
|
576
|
+
}, [achievementConfetti]);
|
|
577
|
+
useEffect(() => {
|
|
578
|
+
globalConfettiConfigRef.current = globalConfettiConfig;
|
|
579
|
+
}, [globalConfettiConfig]);
|
|
580
|
+
useEffect(() => {
|
|
581
|
+
if (ui.enableConfetti === false) {
|
|
582
|
+
if (confettiTimerRef.current) {
|
|
583
|
+
clearTimeout(confettiTimerRef.current);
|
|
584
|
+
confettiTimerRef.current = null;
|
|
585
|
+
}
|
|
586
|
+
setShowConfetti(false);
|
|
587
|
+
setActiveConfettiConfig(null);
|
|
588
|
+
}
|
|
589
|
+
}, [ui.enableConfetti]);
|
|
548
590
|
useEffect(() => {
|
|
549
591
|
const unsubscribeUnlocked = engine.on('achievement:unlocked', (event) => {
|
|
592
|
+
var _a;
|
|
550
593
|
if (seenAchievementsRef.current.has(event.achievementId)) {
|
|
551
594
|
return;
|
|
552
595
|
}
|
|
@@ -566,15 +609,19 @@ const AchievementEffects = ({ icons, ui }) => {
|
|
|
566
609
|
return [...currentNotifications, unlockedAchievement];
|
|
567
610
|
});
|
|
568
611
|
}
|
|
569
|
-
|
|
612
|
+
const rewardConfetti = achievementConfettiRef.current.get(event.achievementId);
|
|
613
|
+
if (ui.enableConfetti !== false && rewardConfetti !== false) {
|
|
570
614
|
if (confettiTimerRef.current) {
|
|
571
615
|
clearTimeout(confettiTimerRef.current);
|
|
572
616
|
}
|
|
617
|
+
const resolvedConfettiConfig = Object.assign(Object.assign({}, globalConfettiConfigRef.current), (rewardConfetti || {}));
|
|
618
|
+
const resolvedConfettiDuration = (_a = resolvedConfettiConfig.duration) !== null && _a !== void 0 ? _a : CONFETTI_DURATION_MS;
|
|
619
|
+
setActiveConfettiConfig(resolvedConfettiConfig);
|
|
573
620
|
setShowConfetti(true);
|
|
574
621
|
confettiTimerRef.current = setTimeout(() => {
|
|
575
622
|
setShowConfetti(false);
|
|
576
623
|
confettiTimerRef.current = null;
|
|
577
|
-
},
|
|
624
|
+
}, resolvedConfettiDuration);
|
|
578
625
|
}
|
|
579
626
|
});
|
|
580
627
|
const unsubscribeStateChanged = engine.on('state:changed', () => {
|
|
@@ -597,19 +644,20 @@ const AchievementEffects = ({ icons, ui }) => {
|
|
|
597
644
|
const ConfettiComponentResolved = ui.ConfettiComponent || BuiltInConfetti;
|
|
598
645
|
return (React.createElement(React.Fragment, null,
|
|
599
646
|
ui.enableNotifications !== false &&
|
|
600
|
-
notifications.map((notification, index) => (React.createElement(NotificationComponent, { key: notification.achievementId, achievement: notification, onClose: () => setNotifications((currentNotifications) => currentNotifications.filter((currentNotification) => currentNotification.achievementId !== notification.achievementId)), duration:
|
|
601
|
-
ui.enableConfetti !== false && (React.createElement(ConfettiComponentResolved, { show: showConfetti, duration:
|
|
647
|
+
notifications.map((notification, index) => (React.createElement(NotificationComponent, { key: notification.achievementId, achievement: notification, onClose: () => setNotifications((currentNotifications) => currentNotifications.filter((currentNotification) => currentNotification.achievementId !== notification.achievementId)), duration: notificationDuration, position: ui.notificationPosition || 'top-center', theme: ui.theme || 'modern', icons: icons, stackIndex: index }))),
|
|
648
|
+
ui.enableConfetti !== false && (React.createElement(ConfettiComponentResolved, Object.assign({ show: showConfetti }, renderedConfettiConfig, { duration: renderedConfettiDuration })))));
|
|
602
649
|
};
|
|
603
650
|
const AchievementProvider = (_a) => {
|
|
604
651
|
var { children, icons = {}, ui = {}, useBuiltInUI } = _a, providerProps = __rest(_a, ["children", "icons", "ui", "useBuiltInUI"]);
|
|
605
652
|
if (useBuiltInUI !== undefined) {
|
|
606
653
|
warnDeprecation('`useBuiltInUI` is deprecated and is now a no-op because built-in UI is the default. It will be removed in 5.0.');
|
|
607
654
|
}
|
|
655
|
+
const [{ achievements: preparedAchievements, confettiByAchievementId }] = useState(() => prepareAchievementsForConfetti(providerProps.achievements));
|
|
608
656
|
const uiContextValue = useMemo(() => ({ icons, ui }), [icons, ui]);
|
|
609
657
|
return (React.createElement(AchievementUIContext.Provider, { value: uiContextValue },
|
|
610
|
-
React.createElement(AchievementProvider$1, Object.assign({}, providerProps, { icons: icons }),
|
|
658
|
+
React.createElement(AchievementProvider$1, Object.assign({}, providerProps, { achievements: preparedAchievements, icons: icons }),
|
|
611
659
|
children,
|
|
612
|
-
React.createElement(AchievementEffects, { icons: icons, ui: ui }))));
|
|
660
|
+
React.createElement(AchievementEffects, { achievementConfetti: confettiByAchievementId, icons: icons, ui: ui }))));
|
|
613
661
|
};
|
|
614
662
|
|
|
615
663
|
const useAchievements = () => {
|
|
@@ -1385,5 +1433,45 @@ const BuiltInModal = ({ isOpen, onClose, achievements, icons = {}, theme = 'mode
|
|
|
1385
1433
|
})))))));
|
|
1386
1434
|
};
|
|
1387
1435
|
|
|
1436
|
+
/**
|
|
1437
|
+
* Hook to track window dimensions
|
|
1438
|
+
* Replacement for react-use's useWindowSize
|
|
1439
|
+
*
|
|
1440
|
+
* @returns Object with width and height properties
|
|
1441
|
+
*
|
|
1442
|
+
* @example
|
|
1443
|
+
* ```tsx
|
|
1444
|
+
* const { width, height } = useWindowSize();
|
|
1445
|
+
* console.log(`Window size: ${width}x${height}`);
|
|
1446
|
+
* ```
|
|
1447
|
+
*/
|
|
1448
|
+
function useWindowSize() {
|
|
1449
|
+
const [size, setSize] = useState({
|
|
1450
|
+
width: typeof window !== 'undefined' ? window.innerWidth : 0,
|
|
1451
|
+
height: typeof window !== 'undefined' ? window.innerHeight : 0,
|
|
1452
|
+
});
|
|
1453
|
+
useEffect(() => {
|
|
1454
|
+
// Handle SSR - window may not be defined
|
|
1455
|
+
if (typeof window === 'undefined') {
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
const handleResize = () => {
|
|
1459
|
+
setSize({
|
|
1460
|
+
width: window.innerWidth,
|
|
1461
|
+
height: window.innerHeight,
|
|
1462
|
+
});
|
|
1463
|
+
};
|
|
1464
|
+
// Set initial size
|
|
1465
|
+
handleResize();
|
|
1466
|
+
// Add event listener
|
|
1467
|
+
window.addEventListener('resize', handleResize);
|
|
1468
|
+
// Cleanup
|
|
1469
|
+
return () => {
|
|
1470
|
+
window.removeEventListener('resize', handleResize);
|
|
1471
|
+
};
|
|
1472
|
+
}, []);
|
|
1473
|
+
return size;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1388
1476
|
export { AchievementContext, AchievementProvider, AchievementsList, AchievementsModal, AchievementsWidget, BadgesButton, BadgesButtonWithModal, BadgesModal, BuiltInConfetti, BuiltInModal, BuiltInNotification, ConfettiWrapper, AchievementProvider$1 as HeadlessAchievementProvider, LevelProgress, defaultAchievementIcons, defaultStyles, isAsyncStorage, useAchievementEngine, useAchievementState, useAchievements, useSimpleAchievements, useWindowSize };
|
|
1389
1477
|
//# sourceMappingURL=web.esm.js.map
|