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