react-achievements 1.0.0 → 1.0.2
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/dist/components/Achievement.d.ts +1 -0
- package/dist/components/AchievementModal.d.ts +8 -0
- package/dist/context/AchievementContext.d.ts +2 -0
- package/dist/index.cjs.js +55 -5
- package/dist/index.esm.js +55 -5
- package/package.json +2 -2
- package/src/components/Achievement.tsx +12 -3
- package/src/components/AchievementModal.tsx +46 -0
- package/src/context/AchievementContext.tsx +13 -3
|
@@ -6,6 +6,8 @@ interface AchievementContextProps {
|
|
|
6
6
|
setMetric: (value: number) => void;
|
|
7
7
|
badges: BadgeConfig[];
|
|
8
8
|
levels: LevelConfig[];
|
|
9
|
+
achievedLevels: number[];
|
|
10
|
+
handleAchieve: (level: number, message: string) => void;
|
|
9
11
|
}
|
|
10
12
|
declare const AchievementProvider: React.FC<{
|
|
11
13
|
children: ReactNode;
|
package/dist/index.cjs.js
CHANGED
|
@@ -115,6 +115,37 @@ const ConfettiWrapper = ({ show }) => {
|
|
|
115
115
|
return show ? React.createElement(Confetti, { width: width, height: height }) : null;
|
|
116
116
|
};
|
|
117
117
|
|
|
118
|
+
const AchievementModal = ({ show, message, onClose }) => {
|
|
119
|
+
if (!show)
|
|
120
|
+
return null;
|
|
121
|
+
const modalStyle = {
|
|
122
|
+
position: 'fixed',
|
|
123
|
+
top: '50%',
|
|
124
|
+
left: '50%',
|
|
125
|
+
transform: 'translate(-50%, -50%)',
|
|
126
|
+
backgroundColor: '#fff',
|
|
127
|
+
padding: '20px',
|
|
128
|
+
borderRadius: '8px',
|
|
129
|
+
boxShadow: '0 0 10px rgba(0,0,0,0.1)',
|
|
130
|
+
zIndex: 1000,
|
|
131
|
+
};
|
|
132
|
+
const overlayStyle = {
|
|
133
|
+
position: 'fixed',
|
|
134
|
+
top: 0,
|
|
135
|
+
left: 0,
|
|
136
|
+
right: 0,
|
|
137
|
+
bottom: 0,
|
|
138
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
139
|
+
zIndex: 999,
|
|
140
|
+
};
|
|
141
|
+
return (React.createElement(React.Fragment, null,
|
|
142
|
+
React.createElement("div", { style: overlayStyle, onClick: onClose }),
|
|
143
|
+
React.createElement("div", { style: modalStyle },
|
|
144
|
+
React.createElement("h2", null, "Achievement Unlocked!"),
|
|
145
|
+
React.createElement("p", null, message),
|
|
146
|
+
React.createElement("button", { onClick: onClose }, "Okay"))));
|
|
147
|
+
};
|
|
148
|
+
|
|
118
149
|
const Badge = ({ icon, title, description, position = 'top-right' }) => {
|
|
119
150
|
const badgeStyle = {
|
|
120
151
|
position: 'fixed',
|
|
@@ -140,13 +171,26 @@ const AchievementProvider = ({ children }) => {
|
|
|
140
171
|
const [metric, setMetric] = React.useState(0);
|
|
141
172
|
const [achievedLevels, setAchievedLevels] = React.useState([]);
|
|
142
173
|
const [showConfetti, setShowConfetti] = React.useState(false);
|
|
143
|
-
|
|
174
|
+
const [modalMessage, setModalMessage] = React.useState('');
|
|
175
|
+
const handleAchieve = (level, message) => {
|
|
176
|
+
if (!achievedLevels.includes(level)) {
|
|
177
|
+
setAchievedLevels([...achievedLevels, level]);
|
|
178
|
+
setShowConfetti(true);
|
|
179
|
+
setModalMessage(message);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
const handleCloseModal = () => {
|
|
183
|
+
setShowConfetti(false);
|
|
184
|
+
setModalMessage('');
|
|
185
|
+
};
|
|
186
|
+
return (React.createElement(AchievementContext.Provider, { value: { metric, setMetric, badges: defaultBadges, levels, achievedLevels, handleAchieve } },
|
|
144
187
|
children,
|
|
145
188
|
levels.map(levelConfig => {
|
|
146
189
|
var _a, _b, _c;
|
|
147
190
|
return achievedLevels.includes(levelConfig.level) ? (React.createElement(Badge, { key: levelConfig.level, icon: ((_a = defaultBadges.find(badge => badge.id === levelConfig.badgeId)) === null || _a === void 0 ? void 0 : _a.icon) || '', title: ((_b = defaultBadges.find(badge => badge.id === levelConfig.badgeId)) === null || _b === void 0 ? void 0 : _b.title) || '', description: ((_c = defaultBadges.find(badge => badge.id === levelConfig.badgeId)) === null || _c === void 0 ? void 0 : _c.description) || '' })) : null;
|
|
148
191
|
}),
|
|
149
|
-
React.createElement(ConfettiWrapper, { show: showConfetti })
|
|
192
|
+
React.createElement(ConfettiWrapper, { show: showConfetti }),
|
|
193
|
+
React.createElement(AchievementModal, { show: !!modalMessage, message: modalMessage, onClose: handleCloseModal })));
|
|
150
194
|
};
|
|
151
195
|
const useAchievement = () => {
|
|
152
196
|
const context = React.useContext(AchievementContext);
|
|
@@ -156,12 +200,18 @@ const useAchievement = () => {
|
|
|
156
200
|
return context;
|
|
157
201
|
};
|
|
158
202
|
|
|
159
|
-
const Achievement = ({ metric, threshold, onAchieve, children }) => {
|
|
203
|
+
const Achievement = ({ metric, threshold, onAchieve, message, children }) => {
|
|
204
|
+
const { setMetric, achievedLevels, levels, handleAchieve } = useAchievement();
|
|
160
205
|
React.useEffect(() => {
|
|
161
|
-
if (metric >= threshold) {
|
|
206
|
+
if (metric >= threshold && !achievedLevels.includes(threshold)) {
|
|
162
207
|
onAchieve();
|
|
208
|
+
const levelConfig = levels.find(level => level.threshold === threshold);
|
|
209
|
+
if (levelConfig) {
|
|
210
|
+
setMetric(metric);
|
|
211
|
+
handleAchieve(levelConfig.level, message);
|
|
212
|
+
}
|
|
163
213
|
}
|
|
164
|
-
}, [metric, threshold, onAchieve]);
|
|
214
|
+
}, [metric, threshold, onAchieve, setMetric, achievedLevels, levels, message, handleAchieve]);
|
|
165
215
|
return React.createElement("div", null, children);
|
|
166
216
|
};
|
|
167
217
|
|
package/dist/index.esm.js
CHANGED
|
@@ -113,6 +113,37 @@ const ConfettiWrapper = ({ show }) => {
|
|
|
113
113
|
return show ? React.createElement(Confetti, { width: width, height: height }) : null;
|
|
114
114
|
};
|
|
115
115
|
|
|
116
|
+
const AchievementModal = ({ show, message, onClose }) => {
|
|
117
|
+
if (!show)
|
|
118
|
+
return null;
|
|
119
|
+
const modalStyle = {
|
|
120
|
+
position: 'fixed',
|
|
121
|
+
top: '50%',
|
|
122
|
+
left: '50%',
|
|
123
|
+
transform: 'translate(-50%, -50%)',
|
|
124
|
+
backgroundColor: '#fff',
|
|
125
|
+
padding: '20px',
|
|
126
|
+
borderRadius: '8px',
|
|
127
|
+
boxShadow: '0 0 10px rgba(0,0,0,0.1)',
|
|
128
|
+
zIndex: 1000,
|
|
129
|
+
};
|
|
130
|
+
const overlayStyle = {
|
|
131
|
+
position: 'fixed',
|
|
132
|
+
top: 0,
|
|
133
|
+
left: 0,
|
|
134
|
+
right: 0,
|
|
135
|
+
bottom: 0,
|
|
136
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
137
|
+
zIndex: 999,
|
|
138
|
+
};
|
|
139
|
+
return (React.createElement(React.Fragment, null,
|
|
140
|
+
React.createElement("div", { style: overlayStyle, onClick: onClose }),
|
|
141
|
+
React.createElement("div", { style: modalStyle },
|
|
142
|
+
React.createElement("h2", null, "Achievement Unlocked!"),
|
|
143
|
+
React.createElement("p", null, message),
|
|
144
|
+
React.createElement("button", { onClick: onClose }, "Okay"))));
|
|
145
|
+
};
|
|
146
|
+
|
|
116
147
|
const Badge = ({ icon, title, description, position = 'top-right' }) => {
|
|
117
148
|
const badgeStyle = {
|
|
118
149
|
position: 'fixed',
|
|
@@ -138,13 +169,26 @@ const AchievementProvider = ({ children }) => {
|
|
|
138
169
|
const [metric, setMetric] = useState(0);
|
|
139
170
|
const [achievedLevels, setAchievedLevels] = useState([]);
|
|
140
171
|
const [showConfetti, setShowConfetti] = useState(false);
|
|
141
|
-
|
|
172
|
+
const [modalMessage, setModalMessage] = useState('');
|
|
173
|
+
const handleAchieve = (level, message) => {
|
|
174
|
+
if (!achievedLevels.includes(level)) {
|
|
175
|
+
setAchievedLevels([...achievedLevels, level]);
|
|
176
|
+
setShowConfetti(true);
|
|
177
|
+
setModalMessage(message);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
const handleCloseModal = () => {
|
|
181
|
+
setShowConfetti(false);
|
|
182
|
+
setModalMessage('');
|
|
183
|
+
};
|
|
184
|
+
return (React.createElement(AchievementContext.Provider, { value: { metric, setMetric, badges: defaultBadges, levels, achievedLevels, handleAchieve } },
|
|
142
185
|
children,
|
|
143
186
|
levels.map(levelConfig => {
|
|
144
187
|
var _a, _b, _c;
|
|
145
188
|
return achievedLevels.includes(levelConfig.level) ? (React.createElement(Badge, { key: levelConfig.level, icon: ((_a = defaultBadges.find(badge => badge.id === levelConfig.badgeId)) === null || _a === void 0 ? void 0 : _a.icon) || '', title: ((_b = defaultBadges.find(badge => badge.id === levelConfig.badgeId)) === null || _b === void 0 ? void 0 : _b.title) || '', description: ((_c = defaultBadges.find(badge => badge.id === levelConfig.badgeId)) === null || _c === void 0 ? void 0 : _c.description) || '' })) : null;
|
|
146
189
|
}),
|
|
147
|
-
React.createElement(ConfettiWrapper, { show: showConfetti })
|
|
190
|
+
React.createElement(ConfettiWrapper, { show: showConfetti }),
|
|
191
|
+
React.createElement(AchievementModal, { show: !!modalMessage, message: modalMessage, onClose: handleCloseModal })));
|
|
148
192
|
};
|
|
149
193
|
const useAchievement = () => {
|
|
150
194
|
const context = useContext(AchievementContext);
|
|
@@ -154,12 +198,18 @@ const useAchievement = () => {
|
|
|
154
198
|
return context;
|
|
155
199
|
};
|
|
156
200
|
|
|
157
|
-
const Achievement = ({ metric, threshold, onAchieve, children }) => {
|
|
201
|
+
const Achievement = ({ metric, threshold, onAchieve, message, children }) => {
|
|
202
|
+
const { setMetric, achievedLevels, levels, handleAchieve } = useAchievement();
|
|
158
203
|
React.useEffect(() => {
|
|
159
|
-
if (metric >= threshold) {
|
|
204
|
+
if (metric >= threshold && !achievedLevels.includes(threshold)) {
|
|
160
205
|
onAchieve();
|
|
206
|
+
const levelConfig = levels.find(level => level.threshold === threshold);
|
|
207
|
+
if (levelConfig) {
|
|
208
|
+
setMetric(metric);
|
|
209
|
+
handleAchieve(levelConfig.level, message);
|
|
210
|
+
}
|
|
161
211
|
}
|
|
162
|
-
}, [metric, threshold, onAchieve]);
|
|
212
|
+
}, [metric, threshold, onAchieve, setMetric, achievedLevels, levels, message, handleAchieve]);
|
|
163
213
|
return React.createElement("div", null, children);
|
|
164
214
|
};
|
|
165
215
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-achievements",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "This package allows users to transpose a React
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "This package allows users to transpose a React achievements engine over their React apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
7
7
|
"badge",
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { useAchievement } from '../context/AchievementContext';
|
|
2
3
|
|
|
3
4
|
interface AchievementProps {
|
|
4
5
|
metric: number;
|
|
5
6
|
threshold: number;
|
|
6
7
|
onAchieve: () => void;
|
|
8
|
+
message: string;
|
|
7
9
|
children: React.ReactNode;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
|
-
const Achievement: React.FC<AchievementProps> = ({ metric, threshold, onAchieve, children }) => {
|
|
12
|
+
const Achievement: React.FC<AchievementProps> = ({ metric, threshold, onAchieve, message, children }) => {
|
|
13
|
+
const { setMetric, achievedLevels, levels, handleAchieve } = useAchievement();
|
|
14
|
+
|
|
11
15
|
React.useEffect(() => {
|
|
12
|
-
if (metric >= threshold) {
|
|
16
|
+
if (metric >= threshold && !achievedLevels.includes(threshold)) {
|
|
13
17
|
onAchieve();
|
|
18
|
+
const levelConfig = levels.find(level => level.threshold === threshold);
|
|
19
|
+
if (levelConfig) {
|
|
20
|
+
setMetric(metric);
|
|
21
|
+
handleAchieve(levelConfig.level, message);
|
|
22
|
+
}
|
|
14
23
|
}
|
|
15
|
-
}, [metric, threshold, onAchieve]);
|
|
24
|
+
}, [metric, threshold, onAchieve, setMetric, achievedLevels, levels, message, handleAchieve]);
|
|
16
25
|
|
|
17
26
|
return <div>{children}</div>;
|
|
18
27
|
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface AchievementModalProps {
|
|
4
|
+
show: boolean;
|
|
5
|
+
message: string;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const AchievementModal: React.FC<AchievementModalProps> = ({ show, message, onClose }) => {
|
|
10
|
+
if (!show) return null;
|
|
11
|
+
|
|
12
|
+
const modalStyle: React.CSSProperties = {
|
|
13
|
+
position: 'fixed',
|
|
14
|
+
top: '50%',
|
|
15
|
+
left: '50%',
|
|
16
|
+
transform: 'translate(-50%, -50%)',
|
|
17
|
+
backgroundColor: '#fff',
|
|
18
|
+
padding: '20px',
|
|
19
|
+
borderRadius: '8px',
|
|
20
|
+
boxShadow: '0 0 10px rgba(0,0,0,0.1)',
|
|
21
|
+
zIndex: 1000,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const overlayStyle: React.CSSProperties = {
|
|
25
|
+
position: 'fixed',
|
|
26
|
+
top: 0,
|
|
27
|
+
left: 0,
|
|
28
|
+
right: 0,
|
|
29
|
+
bottom: 0,
|
|
30
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
31
|
+
zIndex: 999,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<div style={overlayStyle} onClick={onClose} />
|
|
37
|
+
<div style={modalStyle}>
|
|
38
|
+
<h2>Achievement Unlocked!</h2>
|
|
39
|
+
<p>{message}</p>
|
|
40
|
+
<button onClick={onClose}>Okay</button>
|
|
41
|
+
</div>
|
|
42
|
+
</>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default AchievementModal;
|
|
@@ -2,6 +2,7 @@ import React, { createContext, useContext, useState, ReactNode } from 'react';
|
|
|
2
2
|
import { defaultBadges, BadgeConfig } from '../badges';
|
|
3
3
|
import { levels, LevelConfig } from '../levels';
|
|
4
4
|
import ConfettiWrapper from '../components/ConfettiWrapper';
|
|
5
|
+
import AchievementModal from '../components/AchievementModal';
|
|
5
6
|
import Badge from '../components/Badge';
|
|
6
7
|
|
|
7
8
|
interface AchievementContextProps {
|
|
@@ -9,6 +10,8 @@ interface AchievementContextProps {
|
|
|
9
10
|
setMetric: (value: number) => void;
|
|
10
11
|
badges: BadgeConfig[];
|
|
11
12
|
levels: LevelConfig[];
|
|
13
|
+
achievedLevels: number[];
|
|
14
|
+
handleAchieve: (level: number, message: string) => void;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
const AchievementContext = createContext<AchievementContextProps | undefined>(undefined);
|
|
@@ -17,17 +20,23 @@ const AchievementProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|
|
17
20
|
const [metric, setMetric] = useState(0);
|
|
18
21
|
const [achievedLevels, setAchievedLevels] = useState<number[]>([]);
|
|
19
22
|
const [showConfetti, setShowConfetti] = useState(false);
|
|
23
|
+
const [modalMessage, setModalMessage] = useState('');
|
|
20
24
|
|
|
21
|
-
const handleAchieve = (level: number) => {
|
|
25
|
+
const handleAchieve = (level: number, message: string) => {
|
|
22
26
|
if (!achievedLevels.includes(level)) {
|
|
23
27
|
setAchievedLevels([...achievedLevels, level]);
|
|
24
28
|
setShowConfetti(true);
|
|
25
|
-
|
|
29
|
+
setModalMessage(message);
|
|
26
30
|
}
|
|
27
31
|
};
|
|
28
32
|
|
|
33
|
+
const handleCloseModal = () => {
|
|
34
|
+
setShowConfetti(false);
|
|
35
|
+
setModalMessage('');
|
|
36
|
+
};
|
|
37
|
+
|
|
29
38
|
return (
|
|
30
|
-
<AchievementContext.Provider value={{ metric, setMetric, badges: defaultBadges, levels }}>
|
|
39
|
+
<AchievementContext.Provider value={{ metric, setMetric, badges: defaultBadges, levels, achievedLevels, handleAchieve }}>
|
|
31
40
|
{children}
|
|
32
41
|
{levels.map(levelConfig =>
|
|
33
42
|
achievedLevels.includes(levelConfig.level) ? (
|
|
@@ -40,6 +49,7 @@ const AchievementProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|
|
40
49
|
) : null
|
|
41
50
|
)}
|
|
42
51
|
<ConfettiWrapper show={showConfetti} />
|
|
52
|
+
<AchievementModal show={!!modalMessage} message={modalMessage} onClose={handleCloseModal} />
|
|
43
53
|
</AchievementContext.Provider>
|
|
44
54
|
);
|
|
45
55
|
};
|