react-achievements 4.1.1 → 4.3.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 +6 -5
- package/dist/headless.cjs +37 -50
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.d.ts +11 -10
- package/dist/headless.esm.js +37 -50
- package/dist/headless.esm.js.map +1 -1
- package/dist/index.cjs +223 -128
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +36 -16
- package/dist/index.esm.js +224 -129
- package/dist/index.esm.js.map +1 -1
- package/dist/web.cjs +223 -128
- package/dist/web.cjs.map +1 -1
- package/dist/web.d.ts +36 -16
- package/dist/web.esm.js +224 -129
- package/dist/web.esm.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -64,15 +64,12 @@ function warnDeprecation(message) {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
const AchievementContext = React.createContext(undefined);
|
|
67
|
-
const getAllAchievementRecord = (
|
|
68
|
-
return Object.fromEntries(
|
|
69
|
-
achievement.achievementId,
|
|
70
|
-
achievement,
|
|
71
|
-
]));
|
|
67
|
+
const getAllAchievementRecord = (achievements) => {
|
|
68
|
+
return Object.fromEntries(achievements.map((achievement) => [achievement.achievementId, achievement]));
|
|
72
69
|
};
|
|
73
70
|
const AchievementProvider$1 = ({ achievements: achievementsConfig, storage = 'local', children, onError, useBuiltInUI, restApiConfig, engine: externalEngine, eventMapping, icons = {}, }) => {
|
|
74
71
|
if (useBuiltInUI !== undefined) {
|
|
75
|
-
warnDeprecation('`useBuiltInUI` is deprecated and is now a no-op because built-in UI is the default. It will be removed in
|
|
72
|
+
warnDeprecation('`useBuiltInUI` is deprecated and is now a no-op because built-in UI is the default. It will be removed in 5.0.');
|
|
76
73
|
}
|
|
77
74
|
if (achievementsConfig && externalEngine) {
|
|
78
75
|
throw new Error('Cannot provide both "achievements" and "engine" props to AchievementProvider.\n\n' +
|
|
@@ -98,15 +95,9 @@ const AchievementProvider$1 = ({ achievements: achievementsConfig, storage = 'lo
|
|
|
98
95
|
eventMapping,
|
|
99
96
|
});
|
|
100
97
|
});
|
|
101
|
-
const [
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}));
|
|
105
|
-
const syncAchievementState = React.useCallback(() => {
|
|
106
|
-
setAchievementState({
|
|
107
|
-
unlocked: [...engine.getUnlocked()],
|
|
108
|
-
all: getAllAchievementRecord(engine),
|
|
109
|
-
});
|
|
98
|
+
const [achievementSnapshot, setAchievementSnapshot] = React.useState(() => engine.getSnapshot());
|
|
99
|
+
const syncAchievementState = React.useCallback((snapshot) => {
|
|
100
|
+
setAchievementSnapshot(snapshot || engine.getSnapshot());
|
|
110
101
|
}, [engine]);
|
|
111
102
|
React.useEffect(() => {
|
|
112
103
|
return () => {
|
|
@@ -116,10 +107,17 @@ const AchievementProvider$1 = ({ achievements: achievementsConfig, storage = 'lo
|
|
|
116
107
|
};
|
|
117
108
|
}, [engine, externalEngine]);
|
|
118
109
|
React.useEffect(() => {
|
|
119
|
-
|
|
120
|
-
const unsubscribeStateChanged = engine.on('state:changed',
|
|
110
|
+
let isMounted = true;
|
|
111
|
+
const unsubscribeStateChanged = engine.on('state:changed', (event) => {
|
|
112
|
+
syncAchievementState(event);
|
|
113
|
+
});
|
|
114
|
+
engine.ready().then(() => {
|
|
115
|
+
if (isMounted) {
|
|
116
|
+
syncAchievementState();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
121
119
|
return () => {
|
|
122
|
-
|
|
120
|
+
isMounted = false;
|
|
123
121
|
unsubscribeStateChanged();
|
|
124
122
|
};
|
|
125
123
|
}, [engine, syncAchievementState]);
|
|
@@ -128,18 +126,12 @@ const AchievementProvider$1 = ({ achievements: achievementsConfig, storage = 'lo
|
|
|
128
126
|
};
|
|
129
127
|
const reset = () => {
|
|
130
128
|
engine.reset();
|
|
131
|
-
syncAchievementState();
|
|
132
129
|
};
|
|
133
130
|
const getState = () => {
|
|
134
|
-
const
|
|
135
|
-
const unlocked = engine.getUnlocked();
|
|
136
|
-
const metricsInArrayFormat = {};
|
|
137
|
-
Object.entries(metrics).forEach(([key, value]) => {
|
|
138
|
-
metricsInArrayFormat[key] = Array.isArray(value) ? value : [value];
|
|
139
|
-
});
|
|
131
|
+
const snapshot = engine.getSnapshot();
|
|
140
132
|
return {
|
|
141
|
-
metrics:
|
|
142
|
-
unlocked:
|
|
133
|
+
metrics: snapshot.metrics,
|
|
134
|
+
unlocked: snapshot.unlockedIds,
|
|
143
135
|
};
|
|
144
136
|
};
|
|
145
137
|
const exportData = () => {
|
|
@@ -151,11 +143,16 @@ const AchievementProvider$1 = ({ achievements: achievementsConfig, storage = 'lo
|
|
|
151
143
|
return result;
|
|
152
144
|
};
|
|
153
145
|
const getAllAchievements = () => {
|
|
154
|
-
return engine.
|
|
146
|
+
return engine.getSnapshot().allAchievements;
|
|
147
|
+
};
|
|
148
|
+
const achievements = {
|
|
149
|
+
unlocked: achievementSnapshot.unlockedIds,
|
|
150
|
+
all: getAllAchievementRecord(achievementSnapshot.allAchievements),
|
|
155
151
|
};
|
|
156
152
|
return (React.createElement(AchievementContext.Provider, { value: {
|
|
157
153
|
update,
|
|
158
|
-
achievements
|
|
154
|
+
achievements,
|
|
155
|
+
snapshot: achievementSnapshot,
|
|
159
156
|
reset,
|
|
160
157
|
getState,
|
|
161
158
|
exportData,
|
|
@@ -320,24 +317,36 @@ const BuiltInNotification = ({ achievement, onClose, duration = 5000, position =
|
|
|
320
317
|
var _a, _b, _c;
|
|
321
318
|
const [isVisible, setIsVisible] = React.useState(false);
|
|
322
319
|
const [isExiting, setIsExiting] = React.useState(false);
|
|
320
|
+
const onCloseRef = React.useRef(onClose);
|
|
321
|
+
const exitTimerRef = React.useRef(null);
|
|
323
322
|
// Merge custom icons with defaults
|
|
324
323
|
const mergedIcons = Object.assign(Object.assign({}, defaultAchievementIcons), icons);
|
|
325
324
|
// Get theme configuration
|
|
326
325
|
const themeConfig = getTheme(theme) || builtInThemes.modern;
|
|
327
326
|
const { notification: themeStyles } = themeConfig;
|
|
327
|
+
React.useEffect(() => {
|
|
328
|
+
onCloseRef.current = onClose;
|
|
329
|
+
}, [onClose]);
|
|
330
|
+
const closeAfterExit = React.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
|
+
}, []);
|
|
328
337
|
React.useEffect(() => {
|
|
329
338
|
// Slide in animation
|
|
330
339
|
const showTimer = setTimeout(() => setIsVisible(true), 10);
|
|
331
340
|
// Auto-dismiss
|
|
332
|
-
const dismissTimer = setTimeout(
|
|
333
|
-
setIsExiting(true);
|
|
334
|
-
setTimeout(() => onClose === null || onClose === void 0 ? void 0 : onClose(), 300);
|
|
335
|
-
}, duration);
|
|
341
|
+
const dismissTimer = setTimeout(closeAfterExit, duration);
|
|
336
342
|
return () => {
|
|
337
343
|
clearTimeout(showTimer);
|
|
338
344
|
clearTimeout(dismissTimer);
|
|
345
|
+
if (exitTimerRef.current) {
|
|
346
|
+
clearTimeout(exitTimerRef.current);
|
|
347
|
+
}
|
|
339
348
|
};
|
|
340
|
-
}, [duration,
|
|
349
|
+
}, [duration, closeAfterExit]);
|
|
341
350
|
const getPositionStyles = () => {
|
|
342
351
|
const stackedOffset = 20 + stackIndex * 104;
|
|
343
352
|
const base = {
|
|
@@ -425,10 +434,7 @@ const BuiltInNotification = ({ achievement, onClose, duration = 5000, position =
|
|
|
425
434
|
React.createElement("div", { style: headerStyles }, "Achievement Unlocked!"),
|
|
426
435
|
React.createElement("div", { style: titleStyles }, achievement.achievementTitle),
|
|
427
436
|
achievement.achievementDescription && (React.createElement("div", { style: descriptionStyles }, achievement.achievementDescription))),
|
|
428
|
-
React.createElement("button", { onClick: () =>
|
|
429
|
-
setIsExiting(true);
|
|
430
|
-
setTimeout(() => onClose === null || onClose === void 0 ? void 0 : onClose(), 300);
|
|
431
|
-
}, 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")));
|
|
432
438
|
};
|
|
433
439
|
|
|
434
440
|
/**
|
|
@@ -538,17 +544,20 @@ const BuiltInConfetti = ({ show, duration = 5000, particleCount = 50, colors = [
|
|
|
538
544
|
React.createElement("div", { style: containerStyles, "data-testid": "built-in-confetti" }, particles)));
|
|
539
545
|
};
|
|
540
546
|
|
|
541
|
-
const
|
|
547
|
+
const DEFAULT_NOTIFICATION_DURATION_MS = 5000;
|
|
548
|
+
const CONFETTI_DURATION_MS = 5000;
|
|
542
549
|
const AchievementUIContext = React.createContext({
|
|
543
550
|
icons: {},
|
|
544
551
|
ui: {},
|
|
545
552
|
});
|
|
546
553
|
const AchievementEffects = ({ icons, ui }) => {
|
|
554
|
+
var _a;
|
|
547
555
|
const engine = useAchievementEngine();
|
|
548
556
|
const seenAchievementsRef = React.useRef(new Set(engine.getUnlocked()));
|
|
549
557
|
const confettiTimerRef = React.useRef(null);
|
|
550
558
|
const [showConfetti, setShowConfetti] = React.useState(false);
|
|
551
559
|
const [notifications, setNotifications] = React.useState([]);
|
|
560
|
+
const notificationDuration = (_a = ui.notificationDuration) !== null && _a !== void 0 ? _a : DEFAULT_NOTIFICATION_DURATION_MS;
|
|
552
561
|
React.useEffect(() => {
|
|
553
562
|
const unsubscribeUnlocked = engine.on('achievement:unlocked', (event) => {
|
|
554
563
|
if (seenAchievementsRef.current.has(event.achievementId)) {
|
|
@@ -578,7 +587,7 @@ const AchievementEffects = ({ icons, ui }) => {
|
|
|
578
587
|
confettiTimerRef.current = setTimeout(() => {
|
|
579
588
|
setShowConfetti(false);
|
|
580
589
|
confettiTimerRef.current = null;
|
|
581
|
-
},
|
|
590
|
+
}, CONFETTI_DURATION_MS);
|
|
582
591
|
}
|
|
583
592
|
});
|
|
584
593
|
const unsubscribeStateChanged = engine.on('state:changed', () => {
|
|
@@ -601,13 +610,13 @@ const AchievementEffects = ({ icons, ui }) => {
|
|
|
601
610
|
const ConfettiComponentResolved = ui.ConfettiComponent || BuiltInConfetti;
|
|
602
611
|
return (React.createElement(React.Fragment, null,
|
|
603
612
|
ui.enableNotifications !== false &&
|
|
604
|
-
notifications.map((notification, index) => (React.createElement(NotificationComponent, { key: notification.achievementId, achievement: notification, onClose: () => setNotifications((currentNotifications) => currentNotifications.filter((currentNotification) => currentNotification.achievementId !== notification.achievementId)), duration:
|
|
605
|
-
ui.enableConfetti !== false && (React.createElement(ConfettiComponentResolved, { show: showConfetti, duration:
|
|
613
|
+
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 }))),
|
|
614
|
+
ui.enableConfetti !== false && (React.createElement(ConfettiComponentResolved, { show: showConfetti, duration: CONFETTI_DURATION_MS }))));
|
|
606
615
|
};
|
|
607
616
|
const AchievementProvider = (_a) => {
|
|
608
617
|
var { children, icons = {}, ui = {}, useBuiltInUI } = _a, providerProps = __rest(_a, ["children", "icons", "ui", "useBuiltInUI"]);
|
|
609
618
|
if (useBuiltInUI !== undefined) {
|
|
610
|
-
warnDeprecation('`useBuiltInUI` is deprecated and is now a no-op because built-in UI is the default. It will be removed in
|
|
619
|
+
warnDeprecation('`useBuiltInUI` is deprecated and is now a no-op because built-in UI is the default. It will be removed in 5.0.');
|
|
611
620
|
}
|
|
612
621
|
const uiContextValue = React.useMemo(() => ({ icons, ui }), [icons, ui]);
|
|
613
622
|
return (React.createElement(AchievementUIContext.Provider, { value: uiContextValue },
|
|
@@ -625,18 +634,14 @@ const useAchievements = () => {
|
|
|
625
634
|
};
|
|
626
635
|
|
|
627
636
|
const useAchievementState = () => {
|
|
628
|
-
const {
|
|
629
|
-
const allAchievements = getAllAchievements();
|
|
630
|
-
const unlockedIds = achievements.unlocked;
|
|
631
|
-
const unlockedAchievementSet = new Set(unlockedIds);
|
|
632
|
-
const unlockedAchievements = allAchievements.filter((achievement) => unlockedAchievementSet.has(achievement.achievementId));
|
|
637
|
+
const { snapshot } = useAchievements();
|
|
633
638
|
return {
|
|
634
|
-
unlockedIds,
|
|
635
|
-
unlockedAchievements,
|
|
636
|
-
allAchievements,
|
|
637
|
-
unlockedCount:
|
|
638
|
-
totalCount:
|
|
639
|
-
metrics:
|
|
639
|
+
unlockedIds: snapshot.unlockedIds,
|
|
640
|
+
unlockedAchievements: snapshot.unlockedAchievements,
|
|
641
|
+
allAchievements: snapshot.allAchievements,
|
|
642
|
+
unlockedCount: snapshot.unlockedCount,
|
|
643
|
+
totalCount: snapshot.totalCount,
|
|
644
|
+
metrics: snapshot.metrics,
|
|
640
645
|
};
|
|
641
646
|
};
|
|
642
647
|
|
|
@@ -777,13 +782,98 @@ const defaultStyles = {
|
|
|
777
782
|
},
|
|
778
783
|
};
|
|
779
784
|
|
|
785
|
+
const isNumericString = (value) => /^-?\d+(\.\d+)?$/.test(value);
|
|
786
|
+
const getBackdropBlurFilter = (backdropBlur) => {
|
|
787
|
+
if (backdropBlur === undefined) {
|
|
788
|
+
return undefined;
|
|
789
|
+
}
|
|
790
|
+
if (typeof backdropBlur === 'number') {
|
|
791
|
+
if (backdropBlur <= 0) {
|
|
792
|
+
return undefined;
|
|
793
|
+
}
|
|
794
|
+
return `blur(${backdropBlur}px)`;
|
|
795
|
+
}
|
|
796
|
+
const trimmedBlur = backdropBlur.trim();
|
|
797
|
+
if (!trimmedBlur ||
|
|
798
|
+
trimmedBlur === '0' ||
|
|
799
|
+
trimmedBlur === '0px' ||
|
|
800
|
+
trimmedBlur === 'none') {
|
|
801
|
+
return undefined;
|
|
802
|
+
}
|
|
803
|
+
const blurValue = isNumericString(trimmedBlur) ? `${trimmedBlur}px` : trimmedBlur;
|
|
804
|
+
return blurValue.startsWith('blur(') ? blurValue : `blur(${blurValue})`;
|
|
805
|
+
};
|
|
806
|
+
const getBackdropBlurStyles = (backdropBlur) => {
|
|
807
|
+
const backdropFilter = getBackdropBlurFilter(backdropBlur);
|
|
808
|
+
if (!backdropFilter) {
|
|
809
|
+
return {};
|
|
810
|
+
}
|
|
811
|
+
return {
|
|
812
|
+
backdropFilter,
|
|
813
|
+
WebkitBackdropFilter: backdropFilter,
|
|
814
|
+
};
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
const compactAchievementStyles = {
|
|
818
|
+
achievementList: {
|
|
819
|
+
display: 'grid',
|
|
820
|
+
gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))',
|
|
821
|
+
gap: '10px',
|
|
822
|
+
},
|
|
823
|
+
achievementItem: {
|
|
824
|
+
display: 'flex',
|
|
825
|
+
flexDirection: 'column',
|
|
826
|
+
alignItems: 'center',
|
|
827
|
+
justifyContent: 'center',
|
|
828
|
+
gap: '6px',
|
|
829
|
+
padding: '12px 10px',
|
|
830
|
+
borderRadius: '8px',
|
|
831
|
+
aspectRatio: '1 / 1',
|
|
832
|
+
minHeight: '120px',
|
|
833
|
+
textAlign: 'center',
|
|
834
|
+
},
|
|
835
|
+
lockedAchievementItem: {
|
|
836
|
+
display: 'flex',
|
|
837
|
+
flexDirection: 'column',
|
|
838
|
+
alignItems: 'center',
|
|
839
|
+
justifyContent: 'center',
|
|
840
|
+
gap: '6px',
|
|
841
|
+
padding: '12px 10px',
|
|
842
|
+
borderRadius: '8px',
|
|
843
|
+
aspectRatio: '1 / 1',
|
|
844
|
+
minHeight: '120px',
|
|
845
|
+
textAlign: 'center',
|
|
846
|
+
},
|
|
847
|
+
achievementIcon: {
|
|
848
|
+
fontSize: '34px',
|
|
849
|
+
lineHeight: 1,
|
|
850
|
+
flexShrink: 0,
|
|
851
|
+
},
|
|
852
|
+
achievementTitle: {
|
|
853
|
+
margin: '0',
|
|
854
|
+
fontSize: '13px',
|
|
855
|
+
lineHeight: 1.2,
|
|
856
|
+
},
|
|
857
|
+
achievementDescription: {
|
|
858
|
+
margin: '0',
|
|
859
|
+
fontSize: '11px',
|
|
860
|
+
lineHeight: 1.25,
|
|
861
|
+
},
|
|
862
|
+
lockIcon: {
|
|
863
|
+
fontSize: '15px',
|
|
864
|
+
top: '8px',
|
|
865
|
+
right: '8px',
|
|
866
|
+
transform: 'none',
|
|
867
|
+
},
|
|
868
|
+
};
|
|
869
|
+
const getDensityStyles = (density) => (density === 'compact' ? compactAchievementStyles : {});
|
|
780
870
|
const resolveIcon = (achievement, icons) => {
|
|
781
871
|
return ((achievement.achievementIconKey && icons[achievement.achievementIconKey]) ||
|
|
782
872
|
achievement.achievementIconKey ||
|
|
783
873
|
icons.default ||
|
|
784
874
|
'⭐');
|
|
785
875
|
};
|
|
786
|
-
const AchievementsList = ({ achievements, showLocked = true, showUnlockConditions = false, icons = {}, styles = {}, emptyState, className, renderAchievement, }) => {
|
|
876
|
+
const AchievementsList = ({ achievements, showLocked = true, showUnlockConditions = false, icons = {}, styles = {}, emptyState, className, density = 'comfortable', renderAchievement, }) => {
|
|
787
877
|
const context = React.useContext(AchievementContext);
|
|
788
878
|
const uiContext = React.useContext(AchievementUIContext);
|
|
789
879
|
const mergedIcons = Object.assign(Object.assign(Object.assign(Object.assign({}, defaultAchievementIcons), context === null || context === void 0 ? void 0 : context.icons), uiContext.icons), icons);
|
|
@@ -794,36 +884,37 @@ const AchievementsList = ({ achievements, showLocked = true, showUnlockCondition
|
|
|
794
884
|
const achievementsToDisplay = showLocked
|
|
795
885
|
? sourceAchievements
|
|
796
886
|
: sourceAchievements.filter((achievement) => achievement.isUnlocked);
|
|
887
|
+
const densityStyles = getDensityStyles(density);
|
|
797
888
|
if (achievementsToDisplay.length === 0) {
|
|
798
889
|
return (React.createElement("p", { style: { textAlign: 'center', color: '#666' } }, emptyState || 'No achievements configured.'));
|
|
799
890
|
}
|
|
800
|
-
return (React.createElement("div", { className: className, style: Object.assign(Object.assign({}, defaultStyles.badgesModal.achievementList), styles === null || styles === void 0 ? void 0 : styles.achievementList), "data-testid": "achievements-list" }, achievementsToDisplay.map((achievement, index) => {
|
|
891
|
+
return (React.createElement("div", { className: className, style: Object.assign(Object.assign(Object.assign({}, defaultStyles.badgesModal.achievementList), densityStyles === null || densityStyles === void 0 ? void 0 : densityStyles.achievementList), styles === null || styles === void 0 ? void 0 : styles.achievementList), "data-density": density, "data-testid": "achievements-list" }, achievementsToDisplay.map((achievement, index) => {
|
|
801
892
|
const isLocked = !achievement.isUnlocked;
|
|
802
893
|
const icon = resolveIcon(achievement, mergedIcons);
|
|
803
894
|
if (renderAchievement) {
|
|
804
|
-
return (React.createElement(React.Fragment, { key: achievement.achievementId }, renderAchievement({ achievement, isLocked, icon, index })));
|
|
895
|
+
return (React.createElement(React.Fragment, { key: achievement.achievementId }, renderAchievement({ achievement, isLocked, icon, index, density })));
|
|
805
896
|
}
|
|
806
897
|
return (React.createElement("div", { key: achievement.achievementId, style: Object.assign(Object.assign(Object.assign({}, (isLocked
|
|
807
|
-
? Object.assign(Object.assign({}, defaultStyles.badgesModal.lockedAchievementItem), styles === null || styles === void 0 ? void 0 : styles.lockedAchievementItem) : defaultStyles.badgesModal.achievementItem)), styles === null || styles === void 0 ? void 0 : styles.achievementItem), { position: 'relative' }), "data-testid": "achievement-list-item", "data-unlocked": achievement.isUnlocked ? 'true' : 'false' },
|
|
808
|
-
React.createElement("div", { style: Object.assign(Object.assign(Object.assign({}, defaultStyles.badgesModal.achievementIcon), styles === null || styles === void 0 ? void 0 : styles.achievementIcon), { opacity: isLocked ? 0.4 : 1 }) }, icon),
|
|
809
|
-
React.createElement("div", { style: { flex: 1 } },
|
|
810
|
-
React.createElement("h3", { style: Object.assign(Object.assign(Object.assign({}, defaultStyles.badgesModal.achievementTitle), styles === null || styles === void 0 ? void 0 : styles.achievementTitle), { color: isLocked ? '#999' : undefined }) }, achievement.achievementTitle),
|
|
811
|
-
achievement.achievementDescription && (React.createElement("p", { style: Object.assign(Object.assign(Object.assign({}, defaultStyles.badgesModal.achievementDescription), styles === null || styles === void 0 ? void 0 : styles.achievementDescription), { color: isLocked ? '#aaa' : '#666' }) },
|
|
898
|
+
? Object.assign(Object.assign(Object.assign({}, defaultStyles.badgesModal.lockedAchievementItem), densityStyles === null || densityStyles === void 0 ? void 0 : densityStyles.lockedAchievementItem), styles === null || styles === void 0 ? void 0 : styles.lockedAchievementItem) : Object.assign(Object.assign({}, defaultStyles.badgesModal.achievementItem), densityStyles === null || densityStyles === void 0 ? void 0 : densityStyles.achievementItem))), styles === null || styles === void 0 ? void 0 : styles.achievementItem), { position: 'relative' }), "data-testid": "achievement-list-item", "data-unlocked": achievement.isUnlocked ? 'true' : 'false' },
|
|
899
|
+
React.createElement("div", { style: Object.assign(Object.assign(Object.assign(Object.assign({}, defaultStyles.badgesModal.achievementIcon), densityStyles === null || densityStyles === void 0 ? void 0 : densityStyles.achievementIcon), styles === null || styles === void 0 ? void 0 : styles.achievementIcon), { opacity: isLocked ? 0.4 : 1 }) }, icon),
|
|
900
|
+
React.createElement("div", { style: density === 'compact' ? { width: '100%', minWidth: 0 } : { flex: 1 } },
|
|
901
|
+
React.createElement("h3", { style: Object.assign(Object.assign(Object.assign(Object.assign({}, defaultStyles.badgesModal.achievementTitle), densityStyles === null || densityStyles === void 0 ? void 0 : densityStyles.achievementTitle), styles === null || styles === void 0 ? void 0 : styles.achievementTitle), { color: isLocked ? '#999' : undefined }) }, achievement.achievementTitle),
|
|
902
|
+
achievement.achievementDescription && (React.createElement("p", { style: Object.assign(Object.assign(Object.assign(Object.assign({}, defaultStyles.badgesModal.achievementDescription), densityStyles === null || densityStyles === void 0 ? void 0 : densityStyles.achievementDescription), styles === null || styles === void 0 ? void 0 : styles.achievementDescription), { color: isLocked ? '#aaa' : '#666' }) },
|
|
812
903
|
achievement.achievementDescription,
|
|
813
904
|
showUnlockConditions && isLocked && (React.createElement("span", { style: {
|
|
814
905
|
display: 'block',
|
|
815
|
-
fontSize: '12px',
|
|
816
|
-
marginTop: '4px',
|
|
906
|
+
fontSize: density === 'compact' ? '11px' : '12px',
|
|
907
|
+
marginTop: density === 'compact' ? '2px' : '4px',
|
|
817
908
|
fontStyle: 'italic',
|
|
818
909
|
color: '#888',
|
|
819
910
|
} },
|
|
820
911
|
"\uD83D\uDD13 ",
|
|
821
912
|
achievement.achievementDescription))))),
|
|
822
|
-
isLocked && (React.createElement("div", { style: Object.assign(Object.assign({}, defaultStyles.badgesModal.lockIcon), styles === null || styles === void 0 ? void 0 : styles.lockIcon) }, "\uD83D\uDD12"))));
|
|
913
|
+
isLocked && (React.createElement("div", { style: Object.assign(Object.assign(Object.assign({}, defaultStyles.badgesModal.lockIcon), densityStyles === null || densityStyles === void 0 ? void 0 : densityStyles.lockIcon), styles === null || styles === void 0 ? void 0 : styles.lockIcon) }, "\uD83D\uDD12"))));
|
|
823
914
|
})));
|
|
824
915
|
};
|
|
825
916
|
|
|
826
|
-
const AchievementsModal = ({ isOpen, onClose, achievements, title = '🏆 Achievements', styles = {}, icons = {}, showLocked = true, showUnlockConditions = false, emptyState, renderAchievement, theme, }) => {
|
|
917
|
+
const AchievementsModal = ({ isOpen, onClose, achievements, title = '🏆 Achievements', styles = {}, icons = {}, showLocked = true, showUnlockConditions = false, emptyState, renderAchievement, theme, hideScrollbar = false, density = 'comfortable', backdropBlur, }) => {
|
|
827
918
|
const context = React.useContext(AchievementContext);
|
|
828
919
|
const uiContext = React.useContext(AchievementUIContext);
|
|
829
920
|
React.useEffect(() => {
|
|
@@ -851,19 +942,30 @@ const AchievementsModal = ({ isOpen, onClose, achievements, title = '🏆 Achiev
|
|
|
851
942
|
? sourceAchievements
|
|
852
943
|
: sourceAchievements === null || sourceAchievements === void 0 ? void 0 : sourceAchievements.filter((achievement) => achievement.isUnlocked);
|
|
853
944
|
const resolvedTheme = theme || uiContext.ui.theme || 'modern';
|
|
945
|
+
const backdropBlurFilter = getBackdropBlurFilter(backdropBlur);
|
|
854
946
|
const mergedIcons = Object.assign(Object.assign(Object.assign(Object.assign({}, defaultAchievementIcons), context === null || context === void 0 ? void 0 : context.icons), uiContext.icons), icons);
|
|
855
947
|
if (CustomModal) {
|
|
856
948
|
if (!modalAchievements) {
|
|
857
949
|
throw new Error('AchievementsModal requires either an achievements prop or an AchievementProvider parent.');
|
|
858
950
|
}
|
|
859
|
-
return (React.createElement(CustomModal, { isOpen: isOpen, onClose: onClose, achievements: modalAchievements, icons: mergedIcons, theme: resolvedTheme }));
|
|
951
|
+
return (React.createElement(CustomModal, { isOpen: isOpen, onClose: onClose, achievements: modalAchievements, icons: mergedIcons, theme: resolvedTheme, hideScrollbar: hideScrollbar, density: density, backdropBlur: backdropBlur }));
|
|
860
952
|
}
|
|
861
|
-
return (React.createElement("div", { style: Object.assign(Object.assign({}, defaultStyles.badgesModal.overlay), styles === null || styles === void 0 ? void 0 : styles.overlay), role: "presentation", onClick: onClose, "data-testid": "achievements-modal-overlay" },
|
|
862
|
-
React.createElement("
|
|
953
|
+
return (React.createElement("div", { style: Object.assign(Object.assign(Object.assign({}, defaultStyles.badgesModal.overlay), styles === null || styles === void 0 ? void 0 : styles.overlay), getBackdropBlurStyles(backdropBlur)), role: "presentation", onClick: onClose, "data-backdrop-blur": backdropBlurFilter, "data-testid": "achievements-modal-overlay" },
|
|
954
|
+
hideScrollbar && (React.createElement("style", null, `
|
|
955
|
+
[data-react-achievements-modal-content][data-hide-scrollbar="true"] {
|
|
956
|
+
scrollbar-width: none;
|
|
957
|
+
-ms-overflow-style: none;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
[data-react-achievements-modal-content][data-hide-scrollbar="true"]::-webkit-scrollbar {
|
|
961
|
+
display: none;
|
|
962
|
+
}
|
|
963
|
+
`)),
|
|
964
|
+
React.createElement("div", { style: Object.assign(Object.assign({}, defaultStyles.badgesModal.content), styles === null || styles === void 0 ? void 0 : styles.content), role: "dialog", "aria-modal": "true", "aria-labelledby": "achievements-modal-title", onClick: (event) => event.stopPropagation(), "data-hide-scrollbar": hideScrollbar ? 'true' : undefined, "data-density": density, "data-react-achievements-modal-content": true, "data-testid": "achievements-modal" },
|
|
863
965
|
React.createElement("div", { style: Object.assign(Object.assign({}, defaultStyles.badgesModal.header), styles === null || styles === void 0 ? void 0 : styles.header) },
|
|
864
966
|
React.createElement("h2", { id: "achievements-modal-title", style: { margin: 0 } }, title),
|
|
865
967
|
React.createElement("button", { onClick: onClose, style: Object.assign(Object.assign({}, defaultStyles.badgesModal.closeButton), styles === null || styles === void 0 ? void 0 : styles.closeButton), "aria-label": "Close" }, "\u00D7")),
|
|
866
|
-
React.createElement(AchievementsList, { achievements: modalAchievements, showLocked: showLocked, showUnlockConditions: showUnlockConditions, icons: icons, styles: styles, emptyState: emptyState, renderAchievement: renderAchievement }))));
|
|
968
|
+
React.createElement(AchievementsList, { achievements: modalAchievements, showLocked: showLocked, showUnlockConditions: showUnlockConditions, icons: icons, styles: styles, emptyState: emptyState, renderAchievement: renderAchievement, density: density }))));
|
|
867
969
|
};
|
|
868
970
|
|
|
869
971
|
const getPositionStyles$1 = (position) => {
|
|
@@ -883,7 +985,7 @@ const getPositionStyles$1 = (position) => {
|
|
|
883
985
|
return Object.assign(Object.assign({}, base), { bottom: 0, right: 0 });
|
|
884
986
|
}
|
|
885
987
|
};
|
|
886
|
-
const AchievementsWidget = ({ position = 'bottom-right', placement = 'fixed', showAllAchievements = true, showUnlockConditions = false, showCount = true, icons, theme, label = 'Achievements', icon = '🏆', triggerClassName, renderTrigger, buttonStyles, modalStyles, modalTitle, emptyState, renderAchievement, }) => {
|
|
988
|
+
const AchievementsWidget = ({ position = 'bottom-right', placement = 'fixed', showAllAchievements = true, showUnlockConditions = false, showCount = true, icons, theme, density = 'comfortable', label = 'Achievements', icon = '🏆', triggerClassName, renderTrigger, buttonStyles, modalStyles, modalTitle, emptyState, renderAchievement, hideModalScrollbar = false, modalBackdropBlur, }) => {
|
|
887
989
|
const uiContext = React.useContext(AchievementUIContext);
|
|
888
990
|
const [isModalOpen, setIsModalOpen] = React.useState(false);
|
|
889
991
|
const { unlockedAchievements, allAchievements, unlockedCount, totalCount } = useAchievementState();
|
|
@@ -923,7 +1025,7 @@ const AchievementsWidget = ({ position = 'bottom-right', placement = 'fixed', sh
|
|
|
923
1025
|
React.createElement("span", null, icon),
|
|
924
1026
|
React.createElement("span", { style: { flex: 1 } }, label),
|
|
925
1027
|
showCount && React.createElement("span", null, unlockedCount))),
|
|
926
|
-
React.createElement(AchievementsModal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), achievements: modalAchievements, showUnlockConditions: showUnlockConditions, icons: icons, styles: modalStyles, title: modalTitle, emptyState: emptyState, renderAchievement: renderAchievement, theme: resolvedTheme })));
|
|
1028
|
+
React.createElement(AchievementsModal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), achievements: modalAchievements, showUnlockConditions: showUnlockConditions, icons: icons, styles: modalStyles, title: modalTitle, emptyState: emptyState, renderAchievement: renderAchievement, theme: resolvedTheme, hideScrollbar: hideModalScrollbar, density: density, backdropBlur: modalBackdropBlur })));
|
|
927
1029
|
};
|
|
928
1030
|
|
|
929
1031
|
const getPositionStyles = (position) => {
|
|
@@ -945,11 +1047,11 @@ const getPositionStyles = (position) => {
|
|
|
945
1047
|
};
|
|
946
1048
|
/**
|
|
947
1049
|
* @deprecated Use `AchievementsWidget` for new integrations. This v3
|
|
948
|
-
* compatibility wrapper will be removed in
|
|
1050
|
+
* compatibility wrapper will be removed in 5.0.
|
|
949
1051
|
*/
|
|
950
1052
|
const BadgesButton = ({ onClick, position = 'bottom-right', placement = 'fixed', styles = {}, unlockedAchievements, theme = 'modern', }) => {
|
|
951
1053
|
React.useEffect(() => {
|
|
952
|
-
warnDeprecation('`BadgesButton` is deprecated. Use `AchievementsWidget` instead. `BadgesButton` will be removed in
|
|
1054
|
+
warnDeprecation('`BadgesButton` is deprecated. Use `AchievementsWidget` instead. `BadgesButton` will be removed in 5.0.');
|
|
953
1055
|
}, []);
|
|
954
1056
|
// Get theme configuration for consistent styling
|
|
955
1057
|
const themeConfig = getTheme(theme) || builtInThemes.modern;
|
|
@@ -986,11 +1088,11 @@ const BadgesButton = ({ onClick, position = 'bottom-right', placement = 'fixed',
|
|
|
986
1088
|
/**
|
|
987
1089
|
* @deprecated Use `AchievementsModal`, `AchievementsWidget`, or
|
|
988
1090
|
* `AchievementsList` for new integrations. This v3 compatibility wrapper will
|
|
989
|
-
* be removed in
|
|
1091
|
+
* be removed in 5.0.
|
|
990
1092
|
*/
|
|
991
1093
|
const BadgesModal = ({ isOpen, onClose, achievements, styles = {}, icons = {}, showAllAchievements = false, showUnlockConditions = false, allAchievements, }) => {
|
|
992
1094
|
React.useEffect(() => {
|
|
993
|
-
warnDeprecation('`BadgesModal` is deprecated. Use `AchievementsWidget` or `AchievementsList` instead. `BadgesModal` will be removed in
|
|
1095
|
+
warnDeprecation('`BadgesModal` is deprecated. Use `AchievementsWidget` or `AchievementsList` instead. `BadgesModal` will be removed in 5.0.');
|
|
994
1096
|
}, []);
|
|
995
1097
|
const achievementsToDisplay = showAllAchievements && allAchievements
|
|
996
1098
|
? allAchievements
|
|
@@ -1000,7 +1102,7 @@ const BadgesModal = ({ isOpen, onClose, achievements, styles = {}, icons = {}, s
|
|
|
1000
1102
|
|
|
1001
1103
|
/**
|
|
1002
1104
|
* @deprecated Use `AchievementsWidget` for new integrations. This v3
|
|
1003
|
-
* compatibility wrapper will be removed in
|
|
1105
|
+
* compatibility wrapper will be removed in 5.0.
|
|
1004
1106
|
*/
|
|
1005
1107
|
const BadgesButtonWithModal = ({ unlockedAchievements, position = 'bottom-right', placement = 'fixed', showAllAchievements = false, allAchievements, showUnlockConditions = false, icons, theme = 'modern', buttonStyles, modalStyles, }) => {
|
|
1006
1108
|
const [isModalOpen, setIsModalOpen] = React.useState(false);
|
|
@@ -1011,11 +1113,11 @@ const BadgesButtonWithModal = ({ unlockedAchievements, position = 'bottom-right'
|
|
|
1011
1113
|
|
|
1012
1114
|
/**
|
|
1013
1115
|
* @deprecated Use the provider `ui.ConfettiComponent` option or the built-in
|
|
1014
|
-
* confetti default. This v3 compatibility wrapper will be removed in
|
|
1116
|
+
* confetti default. This v3 compatibility wrapper will be removed in 5.0.
|
|
1015
1117
|
*/
|
|
1016
1118
|
const ConfettiWrapper = ({ show }) => {
|
|
1017
1119
|
React.useEffect(() => {
|
|
1018
|
-
warnDeprecation('`ConfettiWrapper` is deprecated. Use the provider `ui.ConfettiComponent` option or built-in confetti defaults instead. `ConfettiWrapper` will be removed in
|
|
1120
|
+
warnDeprecation('`ConfettiWrapper` is deprecated. Use the provider `ui.ConfettiComponent` option or built-in confetti defaults instead. `ConfettiWrapper` will be removed in 5.0.');
|
|
1019
1121
|
}, []);
|
|
1020
1122
|
return React.createElement(BuiltInConfetti, { show: show, particleCount: 200 });
|
|
1021
1123
|
};
|
|
@@ -1050,17 +1152,11 @@ const LevelProgress = ({ level, currentXP, nextLevelXP, label = 'Level', valueLa
|
|
|
1050
1152
|
* Provides the v4 happy path for direct metric updates plus explicit state names.
|
|
1051
1153
|
*/
|
|
1052
1154
|
const useSimpleAchievements = () => {
|
|
1053
|
-
const { update, reset, getState, exportData, importData } = useAchievements();
|
|
1155
|
+
const { update, reset, getState, exportData, importData, engine } = useAchievements();
|
|
1054
1156
|
const achievementState = useAchievementState();
|
|
1055
1157
|
const track = (metric, value) => update({ [metric]: value });
|
|
1056
1158
|
const increment = (metric, amount = 1) => {
|
|
1057
|
-
|
|
1058
|
-
const currentMetricArray = currentState.metrics[metric] || [0];
|
|
1059
|
-
const currentValue = Array.isArray(currentMetricArray)
|
|
1060
|
-
? currentMetricArray[0]
|
|
1061
|
-
: currentMetricArray;
|
|
1062
|
-
const newValue = (typeof currentValue === 'number' ? currentValue : 0) + amount;
|
|
1063
|
-
update({ [metric]: newValue });
|
|
1159
|
+
return engine.increment(metric, amount);
|
|
1064
1160
|
};
|
|
1065
1161
|
const trackMultiple = (metrics) => update(metrics);
|
|
1066
1162
|
return {
|
|
@@ -1079,11 +1175,11 @@ const useSimpleAchievements = () => {
|
|
|
1079
1175
|
importData,
|
|
1080
1176
|
getAllAchievements: () => achievementState.allAchievements,
|
|
1081
1177
|
/**
|
|
1082
|
-
* @deprecated Use `unlockedIds` instead. This alias will be removed in
|
|
1178
|
+
* @deprecated Use `unlockedIds` instead. This alias will be removed in 5.0.
|
|
1083
1179
|
*/
|
|
1084
1180
|
unlocked: achievementState.unlockedIds,
|
|
1085
1181
|
/**
|
|
1086
|
-
* @deprecated Use `allAchievements` instead. This alias will be removed in
|
|
1182
|
+
* @deprecated Use `allAchievements` instead. This alias will be removed in 5.0.
|
|
1087
1183
|
*/
|
|
1088
1184
|
all: achievementState.allAchievements,
|
|
1089
1185
|
};
|
|
@@ -1093,12 +1189,14 @@ const useSimpleAchievements = () => {
|
|
|
1093
1189
|
* Built-in modal component
|
|
1094
1190
|
* Modern, theme-aware achievement modal with smooth animations
|
|
1095
1191
|
*/
|
|
1096
|
-
const BuiltInModal = ({ isOpen, onClose, achievements, icons = {}, theme = 'modern', }) => {
|
|
1192
|
+
const BuiltInModal = ({ isOpen, onClose, achievements, icons = {}, theme = 'modern', hideScrollbar = false, density = 'comfortable', backdropBlur, }) => {
|
|
1097
1193
|
// Merge custom icons with defaults
|
|
1098
1194
|
const mergedIcons = Object.assign(Object.assign({}, defaultAchievementIcons), icons);
|
|
1099
1195
|
// Get theme configuration
|
|
1100
1196
|
const themeConfig = getTheme(theme) || builtInThemes.modern;
|
|
1101
1197
|
const { modal: themeStyles } = themeConfig;
|
|
1198
|
+
const isCompact = density === 'compact';
|
|
1199
|
+
const backdropBlurFilter = getBackdropBlurFilter(backdropBlur);
|
|
1102
1200
|
React.useEffect(() => {
|
|
1103
1201
|
if (isOpen) {
|
|
1104
1202
|
// Lock body scroll when modal is open
|
|
@@ -1114,24 +1212,12 @@ const BuiltInModal = ({ isOpen, onClose, achievements, icons = {}, theme = 'mode
|
|
|
1114
1212
|
}, [isOpen]);
|
|
1115
1213
|
if (!isOpen)
|
|
1116
1214
|
return null;
|
|
1117
|
-
const overlayStyles = {
|
|
1118
|
-
position: 'fixed',
|
|
1119
|
-
top: 0,
|
|
1120
|
-
left: 0,
|
|
1121
|
-
right: 0,
|
|
1122
|
-
bottom: 0,
|
|
1123
|
-
backgroundColor: themeStyles.overlayColor,
|
|
1124
|
-
display: 'flex',
|
|
1125
|
-
alignItems: 'center',
|
|
1126
|
-
justifyContent: 'center',
|
|
1127
|
-
zIndex: 10000,
|
|
1128
|
-
animation: 'fadeIn 0.3s ease-in-out',
|
|
1129
|
-
};
|
|
1215
|
+
const overlayStyles = Object.assign({ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: themeStyles.overlayColor, display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 10000, animation: 'fadeIn 0.3s ease-in-out' }, getBackdropBlurStyles(backdropBlur));
|
|
1130
1216
|
const modalStyles = {
|
|
1131
1217
|
background: themeStyles.background,
|
|
1132
1218
|
borderRadius: themeStyles.borderRadius,
|
|
1133
|
-
padding: '32px',
|
|
1134
|
-
maxWidth: '600px',
|
|
1219
|
+
padding: isCompact ? '18px' : '32px',
|
|
1220
|
+
maxWidth: isCompact ? '520px' : '600px',
|
|
1135
1221
|
width: '90%',
|
|
1136
1222
|
maxHeight: '80vh',
|
|
1137
1223
|
overflow: 'auto',
|
|
@@ -1143,18 +1229,18 @@ const BuiltInModal = ({ isOpen, onClose, achievements, icons = {}, theme = 'mode
|
|
|
1143
1229
|
display: 'flex',
|
|
1144
1230
|
justifyContent: 'space-between',
|
|
1145
1231
|
alignItems: 'center',
|
|
1146
|
-
marginBottom: '24px',
|
|
1232
|
+
marginBottom: isCompact ? '16px' : '24px',
|
|
1147
1233
|
};
|
|
1148
1234
|
const titleStyles = {
|
|
1149
1235
|
margin: 0,
|
|
1150
1236
|
color: themeStyles.textColor,
|
|
1151
|
-
fontSize: themeStyles.headerFontSize || '28px',
|
|
1237
|
+
fontSize: isCompact ? '22px' : themeStyles.headerFontSize || '28px',
|
|
1152
1238
|
fontWeight: 'bold',
|
|
1153
1239
|
};
|
|
1154
1240
|
const closeButtonStyles = {
|
|
1155
1241
|
background: 'none',
|
|
1156
1242
|
border: 'none',
|
|
1157
|
-
fontSize: '32px',
|
|
1243
|
+
fontSize: isCompact ? '26px' : '32px',
|
|
1158
1244
|
cursor: 'pointer',
|
|
1159
1245
|
color: themeStyles.textColor,
|
|
1160
1246
|
opacity: 0.6,
|
|
@@ -1162,17 +1248,17 @@ const BuiltInModal = ({ isOpen, onClose, achievements, icons = {}, theme = 'mode
|
|
|
1162
1248
|
padding: 0,
|
|
1163
1249
|
lineHeight: 1,
|
|
1164
1250
|
};
|
|
1165
|
-
const isBadgeLayout = themeStyles.achievementLayout === 'badge';
|
|
1251
|
+
const isBadgeLayout = isCompact || themeStyles.achievementLayout === 'badge';
|
|
1166
1252
|
const listStyles = isBadgeLayout
|
|
1167
1253
|
? {
|
|
1168
1254
|
display: 'grid',
|
|
1169
|
-
gridTemplateColumns:
|
|
1170
|
-
gap: '16px',
|
|
1255
|
+
gridTemplateColumns: `repeat(auto-fill, minmax(${isCompact ? '112px' : '140px'}, 1fr))`,
|
|
1256
|
+
gap: isCompact ? '10px' : '16px',
|
|
1171
1257
|
}
|
|
1172
1258
|
: {
|
|
1173
1259
|
display: 'flex',
|
|
1174
1260
|
flexDirection: 'column',
|
|
1175
|
-
gap: '12px',
|
|
1261
|
+
gap: isCompact ? '8px' : '12px',
|
|
1176
1262
|
};
|
|
1177
1263
|
const getAchievementItemStyles = (isUnlocked) => {
|
|
1178
1264
|
const baseStyles = {
|
|
@@ -1186,24 +1272,24 @@ const BuiltInModal = ({ isOpen, onClose, achievements, icons = {}, theme = 'mode
|
|
|
1186
1272
|
};
|
|
1187
1273
|
if (isBadgeLayout) {
|
|
1188
1274
|
// Badge layout: vertical, centered, square-ish
|
|
1189
|
-
return Object.assign(Object.assign({}, baseStyles), { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center', padding: '20px 12px', aspectRatio: '1 / 1.1', minHeight: '160px' });
|
|
1275
|
+
return Object.assign(Object.assign({}, baseStyles), { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center', padding: isCompact ? '12px 8px' : '20px 12px', aspectRatio: '1 / 1.1', minHeight: isCompact ? '118px' : '160px' });
|
|
1190
1276
|
}
|
|
1191
1277
|
else {
|
|
1192
1278
|
// Horizontal layout (default)
|
|
1193
|
-
return Object.assign(Object.assign({}, baseStyles), { display: 'flex', gap: '16px', padding: '16px' });
|
|
1279
|
+
return Object.assign(Object.assign({}, baseStyles), { display: 'flex', gap: isCompact ? '10px' : '16px', padding: isCompact ? '10px 12px' : '16px' });
|
|
1194
1280
|
}
|
|
1195
1281
|
};
|
|
1196
1282
|
const getIconContainerStyles = (isUnlocked) => {
|
|
1197
1283
|
if (isBadgeLayout) {
|
|
1198
1284
|
return {
|
|
1199
|
-
fontSize: '48px',
|
|
1285
|
+
fontSize: isCompact ? '32px' : '48px',
|
|
1200
1286
|
lineHeight: 1,
|
|
1201
|
-
marginBottom: '8px',
|
|
1287
|
+
marginBottom: isCompact ? '6px' : '8px',
|
|
1202
1288
|
opacity: isUnlocked ? 1 : 0.3,
|
|
1203
1289
|
};
|
|
1204
1290
|
}
|
|
1205
1291
|
return {
|
|
1206
|
-
fontSize: '40px',
|
|
1292
|
+
fontSize: isCompact ? '28px' : '40px',
|
|
1207
1293
|
flexShrink: 0,
|
|
1208
1294
|
lineHeight: 1,
|
|
1209
1295
|
opacity: isUnlocked ? 1 : 0.3,
|
|
@@ -1221,14 +1307,14 @@ const BuiltInModal = ({ isOpen, onClose, achievements, icons = {}, theme = 'mode
|
|
|
1221
1307
|
? {
|
|
1222
1308
|
margin: '0 0 4px 0',
|
|
1223
1309
|
color: themeStyles.textColor,
|
|
1224
|
-
fontSize: '14px',
|
|
1310
|
+
fontSize: isCompact ? '12px' : '14px',
|
|
1225
1311
|
fontWeight: 'bold',
|
|
1226
1312
|
lineHeight: '1.3',
|
|
1227
1313
|
}
|
|
1228
1314
|
: {
|
|
1229
|
-
margin: '0 0 8px 0',
|
|
1315
|
+
margin: isCompact ? '0 0 4px 0' : '0 0 8px 0',
|
|
1230
1316
|
color: themeStyles.textColor,
|
|
1231
|
-
fontSize: '18px',
|
|
1317
|
+
fontSize: isCompact ? '14px' : '18px',
|
|
1232
1318
|
fontWeight: 'bold',
|
|
1233
1319
|
overflow: 'hidden',
|
|
1234
1320
|
textOverflow: 'ellipsis',
|
|
@@ -1239,27 +1325,27 @@ const BuiltInModal = ({ isOpen, onClose, achievements, icons = {}, theme = 'mode
|
|
|
1239
1325
|
margin: 0,
|
|
1240
1326
|
color: themeStyles.textColor,
|
|
1241
1327
|
opacity: 0.7,
|
|
1242
|
-
fontSize: '11px',
|
|
1328
|
+
fontSize: isCompact ? '10px' : '11px',
|
|
1243
1329
|
lineHeight: '1.3',
|
|
1244
1330
|
}
|
|
1245
1331
|
: {
|
|
1246
1332
|
margin: 0,
|
|
1247
1333
|
color: themeStyles.textColor,
|
|
1248
1334
|
opacity: 0.8,
|
|
1249
|
-
fontSize: '14px',
|
|
1335
|
+
fontSize: isCompact ? '12px' : '14px',
|
|
1250
1336
|
};
|
|
1251
1337
|
const getLockIconStyles = () => {
|
|
1252
1338
|
if (isBadgeLayout) {
|
|
1253
1339
|
return {
|
|
1254
1340
|
position: 'absolute',
|
|
1255
|
-
top: '8px',
|
|
1256
|
-
right: '8px',
|
|
1257
|
-
fontSize: '18px',
|
|
1341
|
+
top: isCompact ? '6px' : '8px',
|
|
1342
|
+
right: isCompact ? '6px' : '8px',
|
|
1343
|
+
fontSize: isCompact ? '14px' : '18px',
|
|
1258
1344
|
opacity: 0.6,
|
|
1259
1345
|
};
|
|
1260
1346
|
}
|
|
1261
1347
|
return {
|
|
1262
|
-
fontSize: '24px',
|
|
1348
|
+
fontSize: isCompact ? '18px' : '24px',
|
|
1263
1349
|
flexShrink: 0,
|
|
1264
1350
|
opacity: 0.5,
|
|
1265
1351
|
};
|
|
@@ -1280,9 +1366,18 @@ const BuiltInModal = ({ isOpen, onClose, achievements, icons = {}, theme = 'mode
|
|
|
1280
1366
|
opacity: 1;
|
|
1281
1367
|
}
|
|
1282
1368
|
}
|
|
1369
|
+
|
|
1370
|
+
[data-react-achievements-built-in-modal][data-hide-scrollbar="true"] {
|
|
1371
|
+
scrollbar-width: none;
|
|
1372
|
+
-ms-overflow-style: none;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
[data-react-achievements-built-in-modal][data-hide-scrollbar="true"]::-webkit-scrollbar {
|
|
1376
|
+
display: none;
|
|
1377
|
+
}
|
|
1283
1378
|
`),
|
|
1284
|
-
React.createElement("div", { style: overlayStyles, onClick: onClose, "data-testid": "built-in-modal-overlay" },
|
|
1285
|
-
React.createElement("div", { style: modalStyles, onClick: (e) => e.stopPropagation(), "data-testid": "built-in-modal" },
|
|
1379
|
+
React.createElement("div", { style: overlayStyles, onClick: onClose, "data-backdrop-blur": backdropBlurFilter, "data-testid": "built-in-modal-overlay" },
|
|
1380
|
+
React.createElement("div", { style: modalStyles, onClick: (e) => e.stopPropagation(), "data-hide-scrollbar": hideScrollbar ? 'true' : undefined, "data-density": density, "data-react-achievements-built-in-modal": true, "data-testid": "built-in-modal" },
|
|
1286
1381
|
React.createElement("div", { style: headerStyles },
|
|
1287
1382
|
React.createElement("h2", { style: titleStyles }, "\uD83C\uDFC6 Achievements"),
|
|
1288
1383
|
React.createElement("button", { onClick: onClose, style: closeButtonStyles, onMouseEnter: (e) => (e.currentTarget.style.opacity = '1'), onMouseLeave: (e) => (e.currentTarget.style.opacity = '0.6'), "aria-label": "Close modal" }, "\u00D7")),
|