react-achievements 1.0.1 → 1.0.3

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/badges.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- interface BadgeConfig {
1
+ export interface BadgeConfig {
2
2
  id: string;
3
3
  icon: string;
4
4
  title: string;
5
5
  description: string;
6
6
  }
7
7
  declare const defaultBadges: BadgeConfig[];
8
- export { defaultBadges, BadgeConfig };
8
+ export { defaultBadges };
@@ -3,7 +3,8 @@ interface AchievementProps {
3
3
  metric: number;
4
4
  threshold: number;
5
5
  onAchieve: () => void;
6
+ message: string;
6
7
  children: React.ReactNode;
7
8
  }
8
- declare const Achievement: React.FC<AchievementProps>;
9
- export default Achievement;
9
+ declare const _default: React.NamedExoticComponent<AchievementProps>;
10
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ interface AchievementModalProps {
3
+ show: boolean;
4
+ message: string;
5
+ onClose: () => void;
6
+ }
7
+ declare const _default: React.NamedExoticComponent<AchievementModalProps>;
8
+ export default _default;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface BadgesButtonProps {
3
+ onClick: () => void;
4
+ position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
5
+ }
6
+ declare const _default: React.NamedExoticComponent<BadgesButtonProps>;
7
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { BadgeConfig } from '../badges';
3
+ interface BadgesModalProps {
4
+ show: boolean;
5
+ badges: BadgeConfig[];
6
+ onClose: () => void;
7
+ }
8
+ declare const _default: React.NamedExoticComponent<BadgesModalProps>;
9
+ export default _default;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface ProgressProps {
3
+ style?: React.CSSProperties;
4
+ }
5
+ declare const _default: React.NamedExoticComponent<ProgressProps>;
6
+ export default _default;
@@ -7,9 +7,17 @@ interface AchievementContextProps {
7
7
  badges: BadgeConfig[];
8
8
  levels: LevelConfig[];
9
9
  achievedLevels: number[];
10
+ handleAchieve: (level: number, message: string) => void;
11
+ showAchievementModal: (message: string) => void;
12
+ showBadgesModal: () => void;
10
13
  }
11
- declare const AchievementProvider: React.FC<{
14
+ interface AchievementProviderProps {
12
15
  children: ReactNode;
13
- }>;
14
- declare const useAchievement: () => AchievementContextProps;
15
- export { AchievementProvider, useAchievement };
16
+ initialBadges?: BadgeConfig[];
17
+ initialLevels?: LevelConfig[];
18
+ storageKey?: string;
19
+ badgesButtonPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
20
+ }
21
+ export declare const AchievementProvider: React.FC<AchievementProviderProps>;
22
+ export declare const useAchievement: () => AchievementContextProps;
23
+ export {};
package/dist/index.cjs.js CHANGED
@@ -18,135 +18,145 @@ const defaultBadges = [
18
18
  // Add more badges as needed
19
19
  ];
20
20
 
21
- const levels = [
21
+ const defaultLevels = [
22
22
  { level: 1, threshold: 10, badgeId: 'beginner' },
23
23
  { level: 2, threshold: 50, badgeId: 'intermediate' },
24
24
  // Add more levels as needed
25
25
  ];
26
26
 
27
- var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
28
-
29
- function getDefaultExportFromCjs (x) {
30
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
31
- }
32
-
33
- var reactConfetti_min = {exports: {}};
34
-
35
- (function (module, exports) {
36
- !function(t,e){module.exports=e(React);}("undefined"!=typeof self?self:commonjsGlobal,(function(t){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r});},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0});},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(r,i,function(e){return t[e]}.bind(null,i));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=2)}([function(e,n){e.exports=t;},function(t,e,n){var r={linear:function(t,e,n,r){return (n-e)*t/r+e},easeInQuad:function(t,e,n,r){return (n-e)*(t/=r)*t+e},easeOutQuad:function(t,e,n,r){return -(n-e)*(t/=r)*(t-2)+e},easeInOutQuad:function(t,e,n,r){var i=n-e;return (t/=r/2)<1?i/2*t*t+e:-i/2*(--t*(t-2)-1)+e},easeInCubic:function(t,e,n,r){return (n-e)*(t/=r)*t*t+e},easeOutCubic:function(t,e,n,r){return (n-e)*((t=t/r-1)*t*t+1)+e},easeInOutCubic:function(t,e,n,r){var i=n-e;return (t/=r/2)<1?i/2*t*t*t+e:i/2*((t-=2)*t*t+2)+e},easeInQuart:function(t,e,n,r){return (n-e)*(t/=r)*t*t*t+e},easeOutQuart:function(t,e,n,r){return -(n-e)*((t=t/r-1)*t*t*t-1)+e},easeInOutQuart:function(t,e,n,r){var i=n-e;return (t/=r/2)<1?i/2*t*t*t*t+e:-i/2*((t-=2)*t*t*t-2)+e},easeInQuint:function(t,e,n,r){return (n-e)*(t/=r)*t*t*t*t+e},easeOutQuint:function(t,e,n,r){return (n-e)*((t=t/r-1)*t*t*t*t+1)+e},easeInOutQuint:function(t,e,n,r){var i=n-e;return (t/=r/2)<1?i/2*t*t*t*t*t+e:i/2*((t-=2)*t*t*t*t+2)+e},easeInSine:function(t,e,n,r){var i=n-e;return -i*Math.cos(t/r*(Math.PI/2))+i+e},easeOutSine:function(t,e,n,r){return (n-e)*Math.sin(t/r*(Math.PI/2))+e},easeInOutSine:function(t,e,n,r){return -(n-e)/2*(Math.cos(Math.PI*t/r)-1)+e},easeInExpo:function(t,e,n,r){return 0==t?e:(n-e)*Math.pow(2,10*(t/r-1))+e},easeOutExpo:function(t,e,n,r){var i=n-e;return t==r?e+i:i*(1-Math.pow(2,-10*t/r))+e},easeInOutExpo:function(t,e,n,r){var i=n-e;return 0===t?e:t===r?e+i:(t/=r/2)<1?i/2*Math.pow(2,10*(t-1))+e:i/2*(2-Math.pow(2,-10*--t))+e},easeInCirc:function(t,e,n,r){return -(n-e)*(Math.sqrt(1-(t/=r)*t)-1)+e},easeOutCirc:function(t,e,n,r){return (n-e)*Math.sqrt(1-(t=t/r-1)*t)+e},easeInOutCirc:function(t,e,n,r){var i=n-e;return (t/=r/2)<1?-i/2*(Math.sqrt(1-t*t)-1)+e:i/2*(Math.sqrt(1-(t-=2)*t)+1)+e},easeInElastic:function(t,e,n,r){var i,o,a,c=n-e;return a=1.70158,0===t?e:1==(t/=r)?e+c:((o=0)||(o=.3*r),(i=c)<Math.abs(c)?(i=c,a=o/4):a=o/(2*Math.PI)*Math.asin(c/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t*r-a)*(2*Math.PI)/o)+e)},easeOutElastic:function(t,e,n,r){var i,o,a,c=n-e;return a=1.70158,0===t?e:1==(t/=r)?e+c:((o=0)||(o=.3*r),(i=c)<Math.abs(c)?(i=c,a=o/4):a=o/(2*Math.PI)*Math.asin(c/i),i*Math.pow(2,-10*t)*Math.sin((t*r-a)*(2*Math.PI)/o)+c+e)},easeInOutElastic:function(t,e,n,r){var i,o,a,c=n-e;return a=1.70158,0===t?e:2==(t/=r/2)?e+c:((o=0)||(o=r*(.3*1.5)),(i=c)<Math.abs(c)?(i=c,a=o/4):a=o/(2*Math.PI)*Math.asin(c/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t*r-a)*(2*Math.PI)/o)*-.5+e:i*Math.pow(2,-10*(t-=1))*Math.sin((t*r-a)*(2*Math.PI)/o)*.5+c+e)},easeInBack:function(t,e,n,r,i){return void 0===i&&(i=1.70158),(n-e)*(t/=r)*t*((i+1)*t-i)+e},easeOutBack:function(t,e,n,r,i){return void 0===i&&(i=1.70158),(n-e)*((t=t/r-1)*t*((i+1)*t+i)+1)+e},easeInOutBack:function(t,e,n,r,i){var o=n-e;return void 0===i&&(i=1.70158),(t/=r/2)<1?o/2*(t*t*((1+(i*=1.525))*t-i))+e:o/2*((t-=2)*t*((1+(i*=1.525))*t+i)+2)+e},easeInBounce:function(t,e,n,i){var o=n-e;return o-r.easeOutBounce(i-t,0,o,i)+e},easeOutBounce:function(t,e,n,r){var i=n-e;return (t/=r)<1/2.75?i*(7.5625*t*t)+e:t<2/2.75?i*(7.5625*(t-=1.5/2.75)*t+.75)+e:t<2.5/2.75?i*(7.5625*(t-=2.25/2.75)*t+.9375)+e:i*(7.5625*(t-=2.625/2.75)*t+.984375)+e},easeInOutBounce:function(t,e,n,i){var o=n-e;return t<i/2?.5*r.easeInBounce(2*t,0,o,i)+e:.5*r.easeOutBounce(2*t-i,0,o,i)+.5*o+e}};t.exports=r;},function(t,e,n){t.exports=n(3);},function(t,e,n){n.r(e),n.d(e,"ReactConfetti",(function(){return Q}));var r,i,o=n(0),a=n.n(o),c=n(1),s=n.n(c);function u(t,e){return t+Math.random()*(e-t)}function f(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r);}}function h(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}!function(t){t[t.Circle=0]="Circle",t[t.Square=1]="Square",t[t.Strip=2]="Strip";}(r||(r={})),function(t){t[t.Positive=1]="Positive",t[t.Negative=-1]="Negative";}(i||(i={}));var l=function(){function t(e,n,r,o){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),h(this,"context",void 0),h(this,"radius",void 0),h(this,"x",void 0),h(this,"y",void 0),h(this,"w",void 0),h(this,"h",void 0),h(this,"vx",void 0),h(this,"vy",void 0),h(this,"shape",void 0),h(this,"angle",void 0),h(this,"angularSpin",void 0),h(this,"color",void 0),h(this,"rotateY",void 0),h(this,"rotationDirection",void 0),h(this,"getOptions",void 0),this.getOptions=n;var a,c,s=this.getOptions(),f=s.colors,l=s.initialVelocityX,p=s.initialVelocityY;this.context=e,this.x=r,this.y=o,this.w=u(5,20),this.h=u(5,20),this.radius=u(5,10),this.vx="number"==typeof l?u(-l,l):u(l.min,l.max),this.vy="number"==typeof p?u(-p,0):u(p.min,p.max),this.shape=(a=0,c=2,Math.floor(a+Math.random()*(c-a+1))),this.angle=u(0,360)*Math.PI/180,this.angularSpin=u(-.2,.2),this.color=f[Math.floor(Math.random()*f.length)],this.rotateY=u(0,1),this.rotationDirection=u(0,1)?i.Positive:i.Negative;}var e,n;return e=t,(n=[{key:"update",value:function(){var t=this.getOptions(),e=t.gravity,n=t.wind,o=t.friction,a=t.opacity,c=t.drawShape;this.x+=this.vx,this.y+=this.vy,this.vy+=e,this.vx+=n,this.vx*=o,this.vy*=o,this.rotateY>=1&&this.rotationDirection===i.Positive?this.rotationDirection=i.Negative:this.rotateY<=-1&&this.rotationDirection===i.Negative&&(this.rotationDirection=i.Positive);var s=.1*this.rotationDirection;if(this.rotateY+=s,this.angle+=this.angularSpin,this.context.save(),this.context.translate(this.x,this.y),this.context.rotate(this.angle),this.context.scale(1,this.rotateY),this.context.rotate(this.angle),this.context.beginPath(),this.context.fillStyle=this.color,this.context.strokeStyle=this.color,this.context.globalAlpha=a,this.context.lineCap="round",this.context.lineWidth=2,c&&"function"==typeof c)c.call(this,this.context);else switch(this.shape){case r.Circle:this.context.beginPath(),this.context.arc(0,0,this.radius,0,2*Math.PI),this.context.fill();break;case r.Square:this.context.fillRect(-this.w/2,-this.h/2,this.w,this.h);break;case r.Strip:this.context.fillRect(-this.w/6,-this.h/2,this.w/3,this.h);}this.context.closePath(),this.context.restore();}}])&&f(e.prototype,n),t}();function p(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}var v=function t(e,n){var r=this;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),p(this,"canvas",void 0),p(this,"context",void 0),p(this,"getOptions",void 0),p(this,"x",0),p(this,"y",0),p(this,"w",0),p(this,"h",0),p(this,"lastNumberOfPieces",0),p(this,"tweenInitTime",Date.now()),p(this,"particles",[]),p(this,"particlesGenerated",0),p(this,"removeParticleAt",(function(t){r.particles.splice(t,1);})),p(this,"getParticle",(function(){var t=u(r.x,r.w+r.x),e=u(r.y,r.h+r.y);return new l(r.context,r.getOptions,t,e)})),p(this,"animate",(function(){var t=r.canvas,e=r.context,n=r.particlesGenerated,i=r.lastNumberOfPieces,o=r.getOptions(),a=o.run,c=o.recycle,s=o.numberOfPieces,u=o.debug,f=o.tweenFunction,h=o.tweenDuration;if(!a)return !1;var l=r.particles.length,p=c?l:n,v=Date.now();if(p<s){i!==s&&(r.tweenInitTime=v,r.lastNumberOfPieces=s);for(var y=r.tweenInitTime,d=f(v-y>h?h:Math.max(0,v-y),p,s,h),b=Math.round(d-p),g=0;g<b;g++)r.particles.push(r.getParticle());r.particlesGenerated+=b;}return u&&(e.font="12px sans-serif",e.fillStyle="#333",e.textAlign="right",e.fillText("Particles: ".concat(l),t.width-10,t.height-20)),r.particles.forEach((function(e,n){e.update(),(e.y>t.height||e.y<-100||e.x>t.width+100||e.x<-100)&&(c&&p<=s?r.particles[n]=r.getParticle():r.removeParticleAt(n));})),l>0||p<s})),this.canvas=e;var i=this.canvas.getContext("2d");if(!i)throw new Error("Could not get canvas context");this.context=i,this.getOptions=n;};function y(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r);}return n}function d(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{};e%2?y(Object(n),!0).forEach((function(e){g(t,e,n[e]);})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):y(Object(n)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e));}));}return t}function b(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r);}}function g(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}var O={width:"undefined"!=typeof window?window.innerWidth:300,height:"undefined"!=typeof window?window.innerHeight:200,numberOfPieces:200,friction:.99,wind:0,gravity:.1,initialVelocityX:4,initialVelocityY:10,colors:["#f44336","#e91e63","#9c27b0","#673ab7","#3f51b5","#2196f3","#03a9f4","#00bcd4","#009688","#4CAF50","#8BC34A","#CDDC39","#FFEB3B","#FFC107","#FF9800","#FF5722","#795548"],opacity:1,debug:!1,tweenFunction:s.a.easeInOutQuad,tweenDuration:5e3,recycle:!0,run:!0},w=function(){function t(e,n){var r=this;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),g(this,"canvas",void 0),g(this,"context",void 0),g(this,"_options",void 0),g(this,"generator",void 0),g(this,"rafId",void 0),g(this,"setOptionsWithDefaults",(function(t){var e={confettiSource:{x:0,y:0,w:r.canvas.width,h:0}};r._options=d(d(d({},e),O),t),Object.assign(r,t.confettiSource);})),g(this,"update",(function(){var t=r.options,e=t.run,n=t.onConfettiComplete,i=r.canvas,o=r.context;e&&(o.fillStyle="white",o.clearRect(0,0,i.width,i.height)),r.generator.animate()?r.rafId=requestAnimationFrame(r.update):(n&&"function"==typeof n&&r.generator.particlesGenerated>0&&n.call(r,r),r._options.run=!1);})),g(this,"reset",(function(){r.generator&&r.generator.particlesGenerated>0&&(r.generator.particlesGenerated=0,r.generator.particles=[],r.generator.lastNumberOfPieces=0);})),g(this,"stop",(function(){r.options={run:!1},r.rafId&&(cancelAnimationFrame(r.rafId),r.rafId=void 0);})),this.canvas=e;var i=this.canvas.getContext("2d");if(!i)throw new Error("Could not get canvas context");this.context=i,this.generator=new v(this.canvas,(function(){return r.options})),this.options=n,this.update();}var e,n;return e=t,(n=[{key:"options",get:function(){return this._options},set:function(t){var e=this._options&&this._options.run,n=this._options&&this._options.recycle;this.setOptionsWithDefaults(t),this.generator&&(Object.assign(this.generator,this.options.confettiSource),"boolean"==typeof t.recycle&&t.recycle&&!1===n&&(this.generator.lastNumberOfPieces=this.generator.particles.length)),"boolean"==typeof t.run&&t.run&&!1===e&&this.update();}}])&&b(e.prototype,n),t}();function m(t){return function(t){if(Array.isArray(t))return C(t)}(t)||function(t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(t))return Array.from(t)}(t)||S(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function x(t){return (x="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function P(){return (P=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r]);}return t}).apply(this,arguments)}function j(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r);}return n}function M(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{};e%2?j(Object(n),!0).forEach((function(e){T(t,e,n[e]);})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):j(Object(n)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e));}));}return t}function I(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(t)))return;var n=[],r=!0,i=!1,o=void 0;try{for(var a,c=t[Symbol.iterator]();!(r=(a=c.next()).done)&&(n.push(a.value),!e||n.length!==e);r=!0);}catch(t){i=!0,o=t;}finally{try{r||null==c.return||c.return();}finally{if(i)throw o}}return n}(t,e)||S(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function S(t,e){if(t){if("string"==typeof t)return C(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return "Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?C(t,e):void 0}}function C(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}function D(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function E(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r);}}function _(t,e){return (_=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function R(t){var e=function(){if("undefined"==typeof Reflect||!Reflect.construct)return !1;if(Reflect.construct.sham)return !1;if("function"==typeof Proxy)return !0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(t){return !1}}();return function(){var n,r=F(t);if(e){var i=F(this).constructor;n=Reflect.construct(r,arguments,i);}else n=r.apply(this,arguments);return k(this,n)}}function k(t,e){return !e||"object"!==x(e)&&"function"!=typeof e?A(t):e}function A(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function F(t){return (F=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function T(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}var B=a.a.createRef(),N=function(t){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&_(t,e);}(o,t);var e,n,i=R(o);function o(t){var e;D(this,o);for(var n=arguments.length,r=new Array(n>1?n-1:0),c=1;c<n;c++)r[c-1]=arguments[c];return T(A(e=i.call.apply(i,[this,t].concat(r))),"canvas",a.a.createRef()),T(A(e),"confetti",void 0),e.canvas=t.canvasRef||B,e}return e=o,(n=[{key:"componentDidMount",value:function(){if(this.canvas.current){var t=q(this.props)[0];this.confetti=new w(this.canvas.current,t);}}},{key:"componentDidUpdate",value:function(){var t=q(this.props)[0];this.confetti&&(this.confetti.options=t);}},{key:"componentWillUnmount",value:function(){this.confetti&&this.confetti.stop(),this.confetti=void 0;}},{key:"render",value:function(){var t=I(q(this.props),2),e=t[0],n=t[1],r=M({zIndex:2,position:"absolute",pointerEvents:"none",top:0,left:0,bottom:0,right:0},n.style);return a.a.createElement("canvas",P({width:e.width,height:e.height,ref:this.canvas},n,{style:r}))}}])&&E(e.prototype,n),o}(o.Component);function q(t){var e={},n={},r=[].concat(m(Object.keys(O)),["confettiSource","drawShape","onConfettiComplete"]),i=["canvasRef"];for(var o in t){var a=t[o];r.includes(o)?e[o]=a:i.includes(o)?i[o]=a:n[o]=a;}return [e,n,{}]}T(N,"defaultProps",M({},O)),T(N,"displayName","ReactConfetti");var Q=a.a.forwardRef((function(t,e){return a.a.createElement(N,P({canvasRef:e},t))}));e.default=Q;}]).default}));
37
-
38
- } (reactConfetti_min));
39
-
40
- var reactConfetti_minExports = reactConfetti_min.exports;
41
- var Confetti = /*@__PURE__*/getDefaultExportFromCjs(reactConfetti_minExports);
42
-
43
- function on(obj) {
44
- var args = [];
45
- for (var _i = 1; _i < arguments.length; _i++) {
46
- args[_i - 1] = arguments[_i];
47
- }
48
- if (obj && obj.addEventListener) {
49
- obj.addEventListener.apply(obj, args);
50
- }
51
- }
52
- function off(obj) {
53
- var args = [];
54
- for (var _i = 1; _i < arguments.length; _i++) {
55
- args[_i - 1] = arguments[_i];
56
- }
57
- if (obj && obj.removeEventListener) {
58
- obj.removeEventListener.apply(obj, args);
59
- }
60
- }
61
- var isBrowser = typeof window !== 'undefined';
62
-
63
- var useEffectOnce = function (effect) {
64
- React.useEffect(effect, []);
65
- };
66
-
67
- var useUnmount = function (fn) {
68
- var fnRef = React.useRef(fn);
69
- // update the ref each render so if it change the newest callback will be invoked
70
- fnRef.current = fn;
71
- useEffectOnce(function () { return function () { return fnRef.current(); }; });
72
- };
73
-
74
- var useRafState = function (initialState) {
75
- var frame = React.useRef(0);
76
- var _a = React.useState(initialState), state = _a[0], setState = _a[1];
77
- var setRafState = React.useCallback(function (value) {
78
- cancelAnimationFrame(frame.current);
79
- frame.current = requestAnimationFrame(function () {
80
- setState(value);
81
- });
82
- }, []);
83
- useUnmount(function () {
84
- cancelAnimationFrame(frame.current);
85
- });
86
- return [state, setRafState];
87
- };
88
-
89
- var useWindowSize = function (initialWidth, initialHeight) {
90
- if (initialWidth === void 0) { initialWidth = Infinity; }
91
- if (initialHeight === void 0) { initialHeight = Infinity; }
92
- var _a = useRafState({
93
- width: isBrowser ? window.innerWidth : initialWidth,
94
- height: isBrowser ? window.innerHeight : initialHeight,
95
- }), state = _a[0], setState = _a[1];
96
- React.useEffect(function () {
97
- if (isBrowser) {
98
- var handler_1 = function () {
99
- setState({
100
- width: window.innerWidth,
101
- height: window.innerHeight,
102
- });
103
- };
104
- on(window, 'resize', handler_1);
105
- return function () {
106
- off(window, 'resize', handler_1);
107
- };
108
- }
109
- }, []);
110
- return state;
111
- };
112
-
113
- const ConfettiWrapper = ({ show }) => {
114
- const { width, height } = useWindowSize();
115
- return show ? React.createElement(Confetti, { width: width, height: height }) : null;
27
+ const AchievementModal = ({ show, message, onClose }) => {
28
+ if (!show)
29
+ return null;
30
+ const modalStyle = {
31
+ position: 'fixed',
32
+ top: '50%',
33
+ left: '50%',
34
+ transform: 'translate(-50%, -50%)',
35
+ backgroundColor: '#fff',
36
+ padding: '20px',
37
+ borderRadius: '8px',
38
+ boxShadow: '0 0 10px rgba(0,0,0,0.1)',
39
+ zIndex: 1000,
40
+ };
41
+ const overlayStyle = {
42
+ position: 'fixed',
43
+ top: 0,
44
+ left: 0,
45
+ right: 0,
46
+ bottom: 0,
47
+ backgroundColor: 'rgba(0,0,0,0.5)',
48
+ zIndex: 999,
49
+ };
50
+ return (React.createElement(React.Fragment, null,
51
+ React.createElement("div", { style: overlayStyle, onClick: onClose }),
52
+ React.createElement("div", { style: modalStyle, role: "dialog", "aria-modal": "true", "aria-labelledby": "achievement-title" },
53
+ React.createElement("h2", { id: "achievement-title" }, "Achievement Unlocked!"),
54
+ React.createElement("p", null, message),
55
+ React.createElement("button", { onClick: onClose }, "Okay"))));
116
56
  };
57
+ var AchievementModal$1 = React.memo(AchievementModal);
117
58
 
118
- const Badge = ({ icon, title, description, position = 'top-right' }) => {
119
- const badgeStyle = {
59
+ const BadgesModal = ({ show, badges, onClose }) => {
60
+ if (!show)
61
+ return null;
62
+ const modalStyle = {
120
63
  position: 'fixed',
121
- [position.split('-')[0]]: '10px',
122
- [position.split('-')[1]]: '10px',
123
- display: 'flex',
124
- flexDirection: 'column',
125
- alignItems: 'center',
64
+ top: '50%',
65
+ left: '50%',
66
+ transform: 'translate(-50%, -50%)',
126
67
  backgroundColor: '#fff',
127
- border: '1px solid #ccc',
68
+ padding: '20px',
128
69
  borderRadius: '8px',
129
- padding: '10px',
130
70
  boxShadow: '0 0 10px rgba(0,0,0,0.1)',
71
+ zIndex: 1000,
72
+ maxWidth: '80%',
73
+ maxHeight: '80%',
74
+ overflow: 'auto',
75
+ };
76
+ const overlayStyle = {
77
+ position: 'fixed',
78
+ top: 0,
79
+ left: 0,
80
+ right: 0,
81
+ bottom: 0,
82
+ backgroundColor: 'rgba(0,0,0,0.5)',
83
+ zIndex: 999,
84
+ };
85
+ return (React.createElement(React.Fragment, null,
86
+ React.createElement("div", { style: overlayStyle, onClick: onClose }),
87
+ React.createElement("div", { style: modalStyle, role: "dialog", "aria-modal": "true", "aria-labelledby": "badges-title" },
88
+ React.createElement("h2", { id: "badges-title" }, "Your Achievements"),
89
+ React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', justifyContent: 'center' } }, badges.map(badge => (React.createElement("div", { key: badge.id, style: { margin: '10px', textAlign: 'center' } },
90
+ React.createElement("img", { src: badge.icon, alt: badge.title, style: { width: '50px', height: '50px' } }),
91
+ React.createElement("h4", null, badge.title),
92
+ React.createElement("p", null, badge.description))))),
93
+ React.createElement("button", { onClick: onClose, style: { marginTop: '20px' } }, "Close"))));
94
+ };
95
+ var BadgesModal$1 = React.memo(BadgesModal);
96
+
97
+ const BadgesButton = ({ onClick, position }) => {
98
+ const buttonStyle = {
99
+ position: 'fixed',
100
+ [position.split('-')[0]]: '20px',
101
+ [position.split('-')[1]]: '20px',
102
+ padding: '10px 20px',
103
+ backgroundColor: '#007bff',
104
+ color: '#fff',
105
+ border: 'none',
106
+ borderRadius: '5px',
107
+ cursor: 'pointer',
108
+ zIndex: 998,
131
109
  };
132
- return (React.createElement("div", { style: badgeStyle },
133
- React.createElement("img", { src: icon, alt: title, style: { width: '50px', height: '50px' } }),
134
- React.createElement("h4", null, title),
135
- React.createElement("p", null, description)));
110
+ return (React.createElement("button", { style: buttonStyle, onClick: onClick }, "View Achievements"));
136
111
  };
112
+ var BadgesButton$1 = React.memo(BadgesButton);
137
113
 
138
114
  const AchievementContext = React.createContext(undefined);
139
- const AchievementProvider = ({ children }) => {
140
- const [metric, setMetric] = React.useState(0);
141
- const [achievedLevels, setAchievedLevels] = React.useState([]);
142
- const [showConfetti, setShowConfetti] = React.useState(false);
143
- return (React.createElement(AchievementContext.Provider, { value: { metric, setMetric, badges: defaultBadges, levels, achievedLevels } },
115
+ const AchievementProvider = ({ children, initialBadges = defaultBadges, initialLevels = defaultLevels, storageKey = 'react-achievements', badgesButtonPosition = 'top-right' }) => {
116
+ const [metric, setMetric] = React.useState(() => {
117
+ const saved = localStorage.getItem(`${storageKey}-metric`);
118
+ return saved ? parseInt(saved, 10) : 0;
119
+ });
120
+ const [achievedLevels, setAchievedLevels] = React.useState(() => {
121
+ const saved = localStorage.getItem(`${storageKey}-levels`);
122
+ return saved ? JSON.parse(saved) : [];
123
+ });
124
+ const [badges] = React.useState(initialBadges);
125
+ const [levels] = React.useState(initialLevels);
126
+ const [achievementModalMessage, setAchievementModalMessage] = React.useState('');
127
+ const [showBadges, setShowBadges] = React.useState(false);
128
+ const handleAchieve = React.useCallback((level, message) => {
129
+ if (!achievedLevels.includes(level)) {
130
+ const newAchievedLevels = [...achievedLevels, level];
131
+ setAchievedLevels(newAchievedLevels);
132
+ localStorage.setItem(`${storageKey}-levels`, JSON.stringify(newAchievedLevels));
133
+ setAchievementModalMessage(message);
134
+ }
135
+ }, [achievedLevels, storageKey]);
136
+ const showAchievementModal = React.useCallback((message) => {
137
+ setAchievementModalMessage(message);
138
+ }, []);
139
+ const showBadgesModal = React.useCallback(() => {
140
+ setShowBadges(true);
141
+ }, []);
142
+ const contextValue = React.useMemo(() => ({
143
+ metric,
144
+ setMetric: (value) => {
145
+ setMetric(value);
146
+ localStorage.setItem(`${storageKey}-metric`, value.toString());
147
+ },
148
+ badges,
149
+ levels,
150
+ achievedLevels,
151
+ handleAchieve,
152
+ showAchievementModal,
153
+ showBadgesModal
154
+ }), [metric, badges, levels, achievedLevels, handleAchieve, showAchievementModal, showBadgesModal, storageKey]);
155
+ return (React.createElement(AchievementContext.Provider, { value: contextValue },
144
156
  children,
145
- levels.map(levelConfig => {
146
- var _a, _b, _c;
147
- 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
- }),
149
- React.createElement(ConfettiWrapper, { show: showConfetti })));
157
+ React.createElement(AchievementModal$1, { show: !!achievementModalMessage, message: achievementModalMessage, onClose: () => setAchievementModalMessage('') }),
158
+ React.createElement(BadgesModal$1, { show: showBadges, badges: badges.filter(badge => { var _a; return achievedLevels.includes(((_a = levels.find(level => level.badgeId === badge.id)) === null || _a === void 0 ? void 0 : _a.level) || -1); }), onClose: () => setShowBadges(false) }),
159
+ React.createElement(BadgesButton$1, { onClick: showBadgesModal, position: badgesButtonPosition })));
150
160
  };
151
161
  const useAchievement = () => {
152
162
  const context = React.useContext(AchievementContext);
@@ -156,20 +166,22 @@ const useAchievement = () => {
156
166
  return context;
157
167
  };
158
168
 
159
- const Achievement = ({ metric, threshold, onAchieve, children }) => {
160
- const { setMetric, achievedLevels, levels } = useAchievement();
169
+ const Achievement = ({ metric, threshold, onAchieve, message, children }) => {
170
+ const { setMetric, achievedLevels, levels, handleAchieve } = useAchievement();
161
171
  React.useEffect(() => {
162
172
  if (metric >= threshold && !achievedLevels.includes(threshold)) {
163
173
  onAchieve();
164
174
  const levelConfig = levels.find(level => level.threshold === threshold);
165
175
  if (levelConfig) {
166
- setMetric(metric); // Update the metric in the context
176
+ setMetric(metric);
177
+ handleAchieve(levelConfig.level, message);
167
178
  }
168
179
  }
169
- }, [metric, threshold, onAchieve, setMetric, achievedLevels, levels]);
170
- return React.createElement("div", null, children);
180
+ }, [metric, threshold, onAchieve, achievedLevels, levels, message, setMetric, handleAchieve]);
181
+ return React.createElement(React.Fragment, null, children);
171
182
  };
183
+ var Achievement$1 = React.memo(Achievement);
172
184
 
173
- exports.Achievement = Achievement;
185
+ exports.Achievement = Achievement$1;
174
186
  exports.AchievementProvider = AchievementProvider;
175
187
  exports.useAchievement = useAchievement;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  import { AchievementProvider, useAchievement } from './context/AchievementContext';
2
2
  import Achievement from './components/Achievement';
3
- export { AchievementProvider, useAchievement, Achievement };
3
+ import { BadgeConfig } from './badges';
4
+ import { LevelConfig } from './levels';
5
+ export { AchievementProvider, useAchievement, Achievement, BadgeConfig, LevelConfig };
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useRef, useState, useCallback, createContext, useContext } from 'react';
1
+ import React, { createContext, useState, useCallback, useMemo, useContext, useEffect } from 'react';
2
2
 
3
3
  const defaultBadges = [
4
4
  {
@@ -16,135 +16,145 @@ const defaultBadges = [
16
16
  // Add more badges as needed
17
17
  ];
18
18
 
19
- const levels = [
19
+ const defaultLevels = [
20
20
  { level: 1, threshold: 10, badgeId: 'beginner' },
21
21
  { level: 2, threshold: 50, badgeId: 'intermediate' },
22
22
  // Add more levels as needed
23
23
  ];
24
24
 
25
- var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
26
-
27
- function getDefaultExportFromCjs (x) {
28
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
29
- }
30
-
31
- var reactConfetti_min = {exports: {}};
32
-
33
- (function (module, exports) {
34
- !function(t,e){module.exports=e(React);}("undefined"!=typeof self?self:commonjsGlobal,(function(t){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r});},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0});},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(r,i,function(e){return t[e]}.bind(null,i));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=2)}([function(e,n){e.exports=t;},function(t,e,n){var r={linear:function(t,e,n,r){return (n-e)*t/r+e},easeInQuad:function(t,e,n,r){return (n-e)*(t/=r)*t+e},easeOutQuad:function(t,e,n,r){return -(n-e)*(t/=r)*(t-2)+e},easeInOutQuad:function(t,e,n,r){var i=n-e;return (t/=r/2)<1?i/2*t*t+e:-i/2*(--t*(t-2)-1)+e},easeInCubic:function(t,e,n,r){return (n-e)*(t/=r)*t*t+e},easeOutCubic:function(t,e,n,r){return (n-e)*((t=t/r-1)*t*t+1)+e},easeInOutCubic:function(t,e,n,r){var i=n-e;return (t/=r/2)<1?i/2*t*t*t+e:i/2*((t-=2)*t*t+2)+e},easeInQuart:function(t,e,n,r){return (n-e)*(t/=r)*t*t*t+e},easeOutQuart:function(t,e,n,r){return -(n-e)*((t=t/r-1)*t*t*t-1)+e},easeInOutQuart:function(t,e,n,r){var i=n-e;return (t/=r/2)<1?i/2*t*t*t*t+e:-i/2*((t-=2)*t*t*t-2)+e},easeInQuint:function(t,e,n,r){return (n-e)*(t/=r)*t*t*t*t+e},easeOutQuint:function(t,e,n,r){return (n-e)*((t=t/r-1)*t*t*t*t+1)+e},easeInOutQuint:function(t,e,n,r){var i=n-e;return (t/=r/2)<1?i/2*t*t*t*t*t+e:i/2*((t-=2)*t*t*t*t+2)+e},easeInSine:function(t,e,n,r){var i=n-e;return -i*Math.cos(t/r*(Math.PI/2))+i+e},easeOutSine:function(t,e,n,r){return (n-e)*Math.sin(t/r*(Math.PI/2))+e},easeInOutSine:function(t,e,n,r){return -(n-e)/2*(Math.cos(Math.PI*t/r)-1)+e},easeInExpo:function(t,e,n,r){return 0==t?e:(n-e)*Math.pow(2,10*(t/r-1))+e},easeOutExpo:function(t,e,n,r){var i=n-e;return t==r?e+i:i*(1-Math.pow(2,-10*t/r))+e},easeInOutExpo:function(t,e,n,r){var i=n-e;return 0===t?e:t===r?e+i:(t/=r/2)<1?i/2*Math.pow(2,10*(t-1))+e:i/2*(2-Math.pow(2,-10*--t))+e},easeInCirc:function(t,e,n,r){return -(n-e)*(Math.sqrt(1-(t/=r)*t)-1)+e},easeOutCirc:function(t,e,n,r){return (n-e)*Math.sqrt(1-(t=t/r-1)*t)+e},easeInOutCirc:function(t,e,n,r){var i=n-e;return (t/=r/2)<1?-i/2*(Math.sqrt(1-t*t)-1)+e:i/2*(Math.sqrt(1-(t-=2)*t)+1)+e},easeInElastic:function(t,e,n,r){var i,o,a,c=n-e;return a=1.70158,0===t?e:1==(t/=r)?e+c:((o=0)||(o=.3*r),(i=c)<Math.abs(c)?(i=c,a=o/4):a=o/(2*Math.PI)*Math.asin(c/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t*r-a)*(2*Math.PI)/o)+e)},easeOutElastic:function(t,e,n,r){var i,o,a,c=n-e;return a=1.70158,0===t?e:1==(t/=r)?e+c:((o=0)||(o=.3*r),(i=c)<Math.abs(c)?(i=c,a=o/4):a=o/(2*Math.PI)*Math.asin(c/i),i*Math.pow(2,-10*t)*Math.sin((t*r-a)*(2*Math.PI)/o)+c+e)},easeInOutElastic:function(t,e,n,r){var i,o,a,c=n-e;return a=1.70158,0===t?e:2==(t/=r/2)?e+c:((o=0)||(o=r*(.3*1.5)),(i=c)<Math.abs(c)?(i=c,a=o/4):a=o/(2*Math.PI)*Math.asin(c/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t*r-a)*(2*Math.PI)/o)*-.5+e:i*Math.pow(2,-10*(t-=1))*Math.sin((t*r-a)*(2*Math.PI)/o)*.5+c+e)},easeInBack:function(t,e,n,r,i){return void 0===i&&(i=1.70158),(n-e)*(t/=r)*t*((i+1)*t-i)+e},easeOutBack:function(t,e,n,r,i){return void 0===i&&(i=1.70158),(n-e)*((t=t/r-1)*t*((i+1)*t+i)+1)+e},easeInOutBack:function(t,e,n,r,i){var o=n-e;return void 0===i&&(i=1.70158),(t/=r/2)<1?o/2*(t*t*((1+(i*=1.525))*t-i))+e:o/2*((t-=2)*t*((1+(i*=1.525))*t+i)+2)+e},easeInBounce:function(t,e,n,i){var o=n-e;return o-r.easeOutBounce(i-t,0,o,i)+e},easeOutBounce:function(t,e,n,r){var i=n-e;return (t/=r)<1/2.75?i*(7.5625*t*t)+e:t<2/2.75?i*(7.5625*(t-=1.5/2.75)*t+.75)+e:t<2.5/2.75?i*(7.5625*(t-=2.25/2.75)*t+.9375)+e:i*(7.5625*(t-=2.625/2.75)*t+.984375)+e},easeInOutBounce:function(t,e,n,i){var o=n-e;return t<i/2?.5*r.easeInBounce(2*t,0,o,i)+e:.5*r.easeOutBounce(2*t-i,0,o,i)+.5*o+e}};t.exports=r;},function(t,e,n){t.exports=n(3);},function(t,e,n){n.r(e),n.d(e,"ReactConfetti",(function(){return Q}));var r,i,o=n(0),a=n.n(o),c=n(1),s=n.n(c);function u(t,e){return t+Math.random()*(e-t)}function f(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r);}}function h(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}!function(t){t[t.Circle=0]="Circle",t[t.Square=1]="Square",t[t.Strip=2]="Strip";}(r||(r={})),function(t){t[t.Positive=1]="Positive",t[t.Negative=-1]="Negative";}(i||(i={}));var l=function(){function t(e,n,r,o){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),h(this,"context",void 0),h(this,"radius",void 0),h(this,"x",void 0),h(this,"y",void 0),h(this,"w",void 0),h(this,"h",void 0),h(this,"vx",void 0),h(this,"vy",void 0),h(this,"shape",void 0),h(this,"angle",void 0),h(this,"angularSpin",void 0),h(this,"color",void 0),h(this,"rotateY",void 0),h(this,"rotationDirection",void 0),h(this,"getOptions",void 0),this.getOptions=n;var a,c,s=this.getOptions(),f=s.colors,l=s.initialVelocityX,p=s.initialVelocityY;this.context=e,this.x=r,this.y=o,this.w=u(5,20),this.h=u(5,20),this.radius=u(5,10),this.vx="number"==typeof l?u(-l,l):u(l.min,l.max),this.vy="number"==typeof p?u(-p,0):u(p.min,p.max),this.shape=(a=0,c=2,Math.floor(a+Math.random()*(c-a+1))),this.angle=u(0,360)*Math.PI/180,this.angularSpin=u(-.2,.2),this.color=f[Math.floor(Math.random()*f.length)],this.rotateY=u(0,1),this.rotationDirection=u(0,1)?i.Positive:i.Negative;}var e,n;return e=t,(n=[{key:"update",value:function(){var t=this.getOptions(),e=t.gravity,n=t.wind,o=t.friction,a=t.opacity,c=t.drawShape;this.x+=this.vx,this.y+=this.vy,this.vy+=e,this.vx+=n,this.vx*=o,this.vy*=o,this.rotateY>=1&&this.rotationDirection===i.Positive?this.rotationDirection=i.Negative:this.rotateY<=-1&&this.rotationDirection===i.Negative&&(this.rotationDirection=i.Positive);var s=.1*this.rotationDirection;if(this.rotateY+=s,this.angle+=this.angularSpin,this.context.save(),this.context.translate(this.x,this.y),this.context.rotate(this.angle),this.context.scale(1,this.rotateY),this.context.rotate(this.angle),this.context.beginPath(),this.context.fillStyle=this.color,this.context.strokeStyle=this.color,this.context.globalAlpha=a,this.context.lineCap="round",this.context.lineWidth=2,c&&"function"==typeof c)c.call(this,this.context);else switch(this.shape){case r.Circle:this.context.beginPath(),this.context.arc(0,0,this.radius,0,2*Math.PI),this.context.fill();break;case r.Square:this.context.fillRect(-this.w/2,-this.h/2,this.w,this.h);break;case r.Strip:this.context.fillRect(-this.w/6,-this.h/2,this.w/3,this.h);}this.context.closePath(),this.context.restore();}}])&&f(e.prototype,n),t}();function p(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}var v=function t(e,n){var r=this;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),p(this,"canvas",void 0),p(this,"context",void 0),p(this,"getOptions",void 0),p(this,"x",0),p(this,"y",0),p(this,"w",0),p(this,"h",0),p(this,"lastNumberOfPieces",0),p(this,"tweenInitTime",Date.now()),p(this,"particles",[]),p(this,"particlesGenerated",0),p(this,"removeParticleAt",(function(t){r.particles.splice(t,1);})),p(this,"getParticle",(function(){var t=u(r.x,r.w+r.x),e=u(r.y,r.h+r.y);return new l(r.context,r.getOptions,t,e)})),p(this,"animate",(function(){var t=r.canvas,e=r.context,n=r.particlesGenerated,i=r.lastNumberOfPieces,o=r.getOptions(),a=o.run,c=o.recycle,s=o.numberOfPieces,u=o.debug,f=o.tweenFunction,h=o.tweenDuration;if(!a)return !1;var l=r.particles.length,p=c?l:n,v=Date.now();if(p<s){i!==s&&(r.tweenInitTime=v,r.lastNumberOfPieces=s);for(var y=r.tweenInitTime,d=f(v-y>h?h:Math.max(0,v-y),p,s,h),b=Math.round(d-p),g=0;g<b;g++)r.particles.push(r.getParticle());r.particlesGenerated+=b;}return u&&(e.font="12px sans-serif",e.fillStyle="#333",e.textAlign="right",e.fillText("Particles: ".concat(l),t.width-10,t.height-20)),r.particles.forEach((function(e,n){e.update(),(e.y>t.height||e.y<-100||e.x>t.width+100||e.x<-100)&&(c&&p<=s?r.particles[n]=r.getParticle():r.removeParticleAt(n));})),l>0||p<s})),this.canvas=e;var i=this.canvas.getContext("2d");if(!i)throw new Error("Could not get canvas context");this.context=i,this.getOptions=n;};function y(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r);}return n}function d(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{};e%2?y(Object(n),!0).forEach((function(e){g(t,e,n[e]);})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):y(Object(n)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e));}));}return t}function b(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r);}}function g(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}var O={width:"undefined"!=typeof window?window.innerWidth:300,height:"undefined"!=typeof window?window.innerHeight:200,numberOfPieces:200,friction:.99,wind:0,gravity:.1,initialVelocityX:4,initialVelocityY:10,colors:["#f44336","#e91e63","#9c27b0","#673ab7","#3f51b5","#2196f3","#03a9f4","#00bcd4","#009688","#4CAF50","#8BC34A","#CDDC39","#FFEB3B","#FFC107","#FF9800","#FF5722","#795548"],opacity:1,debug:!1,tweenFunction:s.a.easeInOutQuad,tweenDuration:5e3,recycle:!0,run:!0},w=function(){function t(e,n){var r=this;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),g(this,"canvas",void 0),g(this,"context",void 0),g(this,"_options",void 0),g(this,"generator",void 0),g(this,"rafId",void 0),g(this,"setOptionsWithDefaults",(function(t){var e={confettiSource:{x:0,y:0,w:r.canvas.width,h:0}};r._options=d(d(d({},e),O),t),Object.assign(r,t.confettiSource);})),g(this,"update",(function(){var t=r.options,e=t.run,n=t.onConfettiComplete,i=r.canvas,o=r.context;e&&(o.fillStyle="white",o.clearRect(0,0,i.width,i.height)),r.generator.animate()?r.rafId=requestAnimationFrame(r.update):(n&&"function"==typeof n&&r.generator.particlesGenerated>0&&n.call(r,r),r._options.run=!1);})),g(this,"reset",(function(){r.generator&&r.generator.particlesGenerated>0&&(r.generator.particlesGenerated=0,r.generator.particles=[],r.generator.lastNumberOfPieces=0);})),g(this,"stop",(function(){r.options={run:!1},r.rafId&&(cancelAnimationFrame(r.rafId),r.rafId=void 0);})),this.canvas=e;var i=this.canvas.getContext("2d");if(!i)throw new Error("Could not get canvas context");this.context=i,this.generator=new v(this.canvas,(function(){return r.options})),this.options=n,this.update();}var e,n;return e=t,(n=[{key:"options",get:function(){return this._options},set:function(t){var e=this._options&&this._options.run,n=this._options&&this._options.recycle;this.setOptionsWithDefaults(t),this.generator&&(Object.assign(this.generator,this.options.confettiSource),"boolean"==typeof t.recycle&&t.recycle&&!1===n&&(this.generator.lastNumberOfPieces=this.generator.particles.length)),"boolean"==typeof t.run&&t.run&&!1===e&&this.update();}}])&&b(e.prototype,n),t}();function m(t){return function(t){if(Array.isArray(t))return C(t)}(t)||function(t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(t))return Array.from(t)}(t)||S(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function x(t){return (x="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function P(){return (P=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r]);}return t}).apply(this,arguments)}function j(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r);}return n}function M(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{};e%2?j(Object(n),!0).forEach((function(e){T(t,e,n[e]);})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):j(Object(n)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e));}));}return t}function I(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(t)))return;var n=[],r=!0,i=!1,o=void 0;try{for(var a,c=t[Symbol.iterator]();!(r=(a=c.next()).done)&&(n.push(a.value),!e||n.length!==e);r=!0);}catch(t){i=!0,o=t;}finally{try{r||null==c.return||c.return();}finally{if(i)throw o}}return n}(t,e)||S(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function S(t,e){if(t){if("string"==typeof t)return C(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return "Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?C(t,e):void 0}}function C(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}function D(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function E(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r);}}function _(t,e){return (_=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function R(t){var e=function(){if("undefined"==typeof Reflect||!Reflect.construct)return !1;if(Reflect.construct.sham)return !1;if("function"==typeof Proxy)return !0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(t){return !1}}();return function(){var n,r=F(t);if(e){var i=F(this).constructor;n=Reflect.construct(r,arguments,i);}else n=r.apply(this,arguments);return k(this,n)}}function k(t,e){return !e||"object"!==x(e)&&"function"!=typeof e?A(t):e}function A(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function F(t){return (F=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function T(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}var B=a.a.createRef(),N=function(t){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&_(t,e);}(o,t);var e,n,i=R(o);function o(t){var e;D(this,o);for(var n=arguments.length,r=new Array(n>1?n-1:0),c=1;c<n;c++)r[c-1]=arguments[c];return T(A(e=i.call.apply(i,[this,t].concat(r))),"canvas",a.a.createRef()),T(A(e),"confetti",void 0),e.canvas=t.canvasRef||B,e}return e=o,(n=[{key:"componentDidMount",value:function(){if(this.canvas.current){var t=q(this.props)[0];this.confetti=new w(this.canvas.current,t);}}},{key:"componentDidUpdate",value:function(){var t=q(this.props)[0];this.confetti&&(this.confetti.options=t);}},{key:"componentWillUnmount",value:function(){this.confetti&&this.confetti.stop(),this.confetti=void 0;}},{key:"render",value:function(){var t=I(q(this.props),2),e=t[0],n=t[1],r=M({zIndex:2,position:"absolute",pointerEvents:"none",top:0,left:0,bottom:0,right:0},n.style);return a.a.createElement("canvas",P({width:e.width,height:e.height,ref:this.canvas},n,{style:r}))}}])&&E(e.prototype,n),o}(o.Component);function q(t){var e={},n={},r=[].concat(m(Object.keys(O)),["confettiSource","drawShape","onConfettiComplete"]),i=["canvasRef"];for(var o in t){var a=t[o];r.includes(o)?e[o]=a:i.includes(o)?i[o]=a:n[o]=a;}return [e,n,{}]}T(N,"defaultProps",M({},O)),T(N,"displayName","ReactConfetti");var Q=a.a.forwardRef((function(t,e){return a.a.createElement(N,P({canvasRef:e},t))}));e.default=Q;}]).default}));
35
-
36
- } (reactConfetti_min));
37
-
38
- var reactConfetti_minExports = reactConfetti_min.exports;
39
- var Confetti = /*@__PURE__*/getDefaultExportFromCjs(reactConfetti_minExports);
40
-
41
- function on(obj) {
42
- var args = [];
43
- for (var _i = 1; _i < arguments.length; _i++) {
44
- args[_i - 1] = arguments[_i];
45
- }
46
- if (obj && obj.addEventListener) {
47
- obj.addEventListener.apply(obj, args);
48
- }
49
- }
50
- function off(obj) {
51
- var args = [];
52
- for (var _i = 1; _i < arguments.length; _i++) {
53
- args[_i - 1] = arguments[_i];
54
- }
55
- if (obj && obj.removeEventListener) {
56
- obj.removeEventListener.apply(obj, args);
57
- }
58
- }
59
- var isBrowser = typeof window !== 'undefined';
60
-
61
- var useEffectOnce = function (effect) {
62
- useEffect(effect, []);
63
- };
64
-
65
- var useUnmount = function (fn) {
66
- var fnRef = useRef(fn);
67
- // update the ref each render so if it change the newest callback will be invoked
68
- fnRef.current = fn;
69
- useEffectOnce(function () { return function () { return fnRef.current(); }; });
70
- };
71
-
72
- var useRafState = function (initialState) {
73
- var frame = useRef(0);
74
- var _a = useState(initialState), state = _a[0], setState = _a[1];
75
- var setRafState = useCallback(function (value) {
76
- cancelAnimationFrame(frame.current);
77
- frame.current = requestAnimationFrame(function () {
78
- setState(value);
79
- });
80
- }, []);
81
- useUnmount(function () {
82
- cancelAnimationFrame(frame.current);
83
- });
84
- return [state, setRafState];
85
- };
86
-
87
- var useWindowSize = function (initialWidth, initialHeight) {
88
- if (initialWidth === void 0) { initialWidth = Infinity; }
89
- if (initialHeight === void 0) { initialHeight = Infinity; }
90
- var _a = useRafState({
91
- width: isBrowser ? window.innerWidth : initialWidth,
92
- height: isBrowser ? window.innerHeight : initialHeight,
93
- }), state = _a[0], setState = _a[1];
94
- useEffect(function () {
95
- if (isBrowser) {
96
- var handler_1 = function () {
97
- setState({
98
- width: window.innerWidth,
99
- height: window.innerHeight,
100
- });
101
- };
102
- on(window, 'resize', handler_1);
103
- return function () {
104
- off(window, 'resize', handler_1);
105
- };
106
- }
107
- }, []);
108
- return state;
109
- };
110
-
111
- const ConfettiWrapper = ({ show }) => {
112
- const { width, height } = useWindowSize();
113
- return show ? React.createElement(Confetti, { width: width, height: height }) : null;
25
+ const AchievementModal = ({ show, message, onClose }) => {
26
+ if (!show)
27
+ return null;
28
+ const modalStyle = {
29
+ position: 'fixed',
30
+ top: '50%',
31
+ left: '50%',
32
+ transform: 'translate(-50%, -50%)',
33
+ backgroundColor: '#fff',
34
+ padding: '20px',
35
+ borderRadius: '8px',
36
+ boxShadow: '0 0 10px rgba(0,0,0,0.1)',
37
+ zIndex: 1000,
38
+ };
39
+ const overlayStyle = {
40
+ position: 'fixed',
41
+ top: 0,
42
+ left: 0,
43
+ right: 0,
44
+ bottom: 0,
45
+ backgroundColor: 'rgba(0,0,0,0.5)',
46
+ zIndex: 999,
47
+ };
48
+ return (React.createElement(React.Fragment, null,
49
+ React.createElement("div", { style: overlayStyle, onClick: onClose }),
50
+ React.createElement("div", { style: modalStyle, role: "dialog", "aria-modal": "true", "aria-labelledby": "achievement-title" },
51
+ React.createElement("h2", { id: "achievement-title" }, "Achievement Unlocked!"),
52
+ React.createElement("p", null, message),
53
+ React.createElement("button", { onClick: onClose }, "Okay"))));
114
54
  };
55
+ var AchievementModal$1 = React.memo(AchievementModal);
115
56
 
116
- const Badge = ({ icon, title, description, position = 'top-right' }) => {
117
- const badgeStyle = {
57
+ const BadgesModal = ({ show, badges, onClose }) => {
58
+ if (!show)
59
+ return null;
60
+ const modalStyle = {
118
61
  position: 'fixed',
119
- [position.split('-')[0]]: '10px',
120
- [position.split('-')[1]]: '10px',
121
- display: 'flex',
122
- flexDirection: 'column',
123
- alignItems: 'center',
62
+ top: '50%',
63
+ left: '50%',
64
+ transform: 'translate(-50%, -50%)',
124
65
  backgroundColor: '#fff',
125
- border: '1px solid #ccc',
66
+ padding: '20px',
126
67
  borderRadius: '8px',
127
- padding: '10px',
128
68
  boxShadow: '0 0 10px rgba(0,0,0,0.1)',
69
+ zIndex: 1000,
70
+ maxWidth: '80%',
71
+ maxHeight: '80%',
72
+ overflow: 'auto',
73
+ };
74
+ const overlayStyle = {
75
+ position: 'fixed',
76
+ top: 0,
77
+ left: 0,
78
+ right: 0,
79
+ bottom: 0,
80
+ backgroundColor: 'rgba(0,0,0,0.5)',
81
+ zIndex: 999,
82
+ };
83
+ return (React.createElement(React.Fragment, null,
84
+ React.createElement("div", { style: overlayStyle, onClick: onClose }),
85
+ React.createElement("div", { style: modalStyle, role: "dialog", "aria-modal": "true", "aria-labelledby": "badges-title" },
86
+ React.createElement("h2", { id: "badges-title" }, "Your Achievements"),
87
+ React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', justifyContent: 'center' } }, badges.map(badge => (React.createElement("div", { key: badge.id, style: { margin: '10px', textAlign: 'center' } },
88
+ React.createElement("img", { src: badge.icon, alt: badge.title, style: { width: '50px', height: '50px' } }),
89
+ React.createElement("h4", null, badge.title),
90
+ React.createElement("p", null, badge.description))))),
91
+ React.createElement("button", { onClick: onClose, style: { marginTop: '20px' } }, "Close"))));
92
+ };
93
+ var BadgesModal$1 = React.memo(BadgesModal);
94
+
95
+ const BadgesButton = ({ onClick, position }) => {
96
+ const buttonStyle = {
97
+ position: 'fixed',
98
+ [position.split('-')[0]]: '20px',
99
+ [position.split('-')[1]]: '20px',
100
+ padding: '10px 20px',
101
+ backgroundColor: '#007bff',
102
+ color: '#fff',
103
+ border: 'none',
104
+ borderRadius: '5px',
105
+ cursor: 'pointer',
106
+ zIndex: 998,
129
107
  };
130
- return (React.createElement("div", { style: badgeStyle },
131
- React.createElement("img", { src: icon, alt: title, style: { width: '50px', height: '50px' } }),
132
- React.createElement("h4", null, title),
133
- React.createElement("p", null, description)));
108
+ return (React.createElement("button", { style: buttonStyle, onClick: onClick }, "View Achievements"));
134
109
  };
110
+ var BadgesButton$1 = React.memo(BadgesButton);
135
111
 
136
112
  const AchievementContext = createContext(undefined);
137
- const AchievementProvider = ({ children }) => {
138
- const [metric, setMetric] = useState(0);
139
- const [achievedLevels, setAchievedLevels] = useState([]);
140
- const [showConfetti, setShowConfetti] = useState(false);
141
- return (React.createElement(AchievementContext.Provider, { value: { metric, setMetric, badges: defaultBadges, levels, achievedLevels } },
113
+ const AchievementProvider = ({ children, initialBadges = defaultBadges, initialLevels = defaultLevels, storageKey = 'react-achievements', badgesButtonPosition = 'top-right' }) => {
114
+ const [metric, setMetric] = useState(() => {
115
+ const saved = localStorage.getItem(`${storageKey}-metric`);
116
+ return saved ? parseInt(saved, 10) : 0;
117
+ });
118
+ const [achievedLevels, setAchievedLevels] = useState(() => {
119
+ const saved = localStorage.getItem(`${storageKey}-levels`);
120
+ return saved ? JSON.parse(saved) : [];
121
+ });
122
+ const [badges] = useState(initialBadges);
123
+ const [levels] = useState(initialLevels);
124
+ const [achievementModalMessage, setAchievementModalMessage] = useState('');
125
+ const [showBadges, setShowBadges] = useState(false);
126
+ const handleAchieve = useCallback((level, message) => {
127
+ if (!achievedLevels.includes(level)) {
128
+ const newAchievedLevels = [...achievedLevels, level];
129
+ setAchievedLevels(newAchievedLevels);
130
+ localStorage.setItem(`${storageKey}-levels`, JSON.stringify(newAchievedLevels));
131
+ setAchievementModalMessage(message);
132
+ }
133
+ }, [achievedLevels, storageKey]);
134
+ const showAchievementModal = useCallback((message) => {
135
+ setAchievementModalMessage(message);
136
+ }, []);
137
+ const showBadgesModal = useCallback(() => {
138
+ setShowBadges(true);
139
+ }, []);
140
+ const contextValue = useMemo(() => ({
141
+ metric,
142
+ setMetric: (value) => {
143
+ setMetric(value);
144
+ localStorage.setItem(`${storageKey}-metric`, value.toString());
145
+ },
146
+ badges,
147
+ levels,
148
+ achievedLevels,
149
+ handleAchieve,
150
+ showAchievementModal,
151
+ showBadgesModal
152
+ }), [metric, badges, levels, achievedLevels, handleAchieve, showAchievementModal, showBadgesModal, storageKey]);
153
+ return (React.createElement(AchievementContext.Provider, { value: contextValue },
142
154
  children,
143
- levels.map(levelConfig => {
144
- var _a, _b, _c;
145
- 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
- }),
147
- React.createElement(ConfettiWrapper, { show: showConfetti })));
155
+ React.createElement(AchievementModal$1, { show: !!achievementModalMessage, message: achievementModalMessage, onClose: () => setAchievementModalMessage('') }),
156
+ React.createElement(BadgesModal$1, { show: showBadges, badges: badges.filter(badge => { var _a; return achievedLevels.includes(((_a = levels.find(level => level.badgeId === badge.id)) === null || _a === void 0 ? void 0 : _a.level) || -1); }), onClose: () => setShowBadges(false) }),
157
+ React.createElement(BadgesButton$1, { onClick: showBadgesModal, position: badgesButtonPosition })));
148
158
  };
149
159
  const useAchievement = () => {
150
160
  const context = useContext(AchievementContext);
@@ -154,18 +164,20 @@ const useAchievement = () => {
154
164
  return context;
155
165
  };
156
166
 
157
- const Achievement = ({ metric, threshold, onAchieve, children }) => {
158
- const { setMetric, achievedLevels, levels } = useAchievement();
159
- React.useEffect(() => {
167
+ const Achievement = ({ metric, threshold, onAchieve, message, children }) => {
168
+ const { setMetric, achievedLevels, levels, handleAchieve } = useAchievement();
169
+ useEffect(() => {
160
170
  if (metric >= threshold && !achievedLevels.includes(threshold)) {
161
171
  onAchieve();
162
172
  const levelConfig = levels.find(level => level.threshold === threshold);
163
173
  if (levelConfig) {
164
- setMetric(metric); // Update the metric in the context
174
+ setMetric(metric);
175
+ handleAchieve(levelConfig.level, message);
165
176
  }
166
177
  }
167
- }, [metric, threshold, onAchieve, setMetric, achievedLevels, levels]);
168
- return React.createElement("div", null, children);
178
+ }, [metric, threshold, onAchieve, achievedLevels, levels, message, setMetric, handleAchieve]);
179
+ return React.createElement(React.Fragment, null, children);
169
180
  };
181
+ var Achievement$1 = React.memo(Achievement);
170
182
 
171
- export { Achievement, AchievementProvider, useAchievement };
183
+ export { Achievement$1 as Achievement, AchievementProvider, useAchievement };
package/dist/levels.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- interface LevelConfig {
1
+ export interface LevelConfig {
2
2
  level: number;
3
3
  threshold: number;
4
4
  badgeId: string;
5
5
  }
6
- declare const levels: LevelConfig[];
7
- export { levels, LevelConfig };
6
+ declare const defaultLevels: LevelConfig[];
7
+ export { defaultLevels };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-achievements",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "This package allows users to transpose a React achievements engine over their React apps",
5
5
  "keywords": [
6
6
  "react",
@@ -12,7 +12,8 @@
12
12
  "types": "dist/index.d.ts",
13
13
  "scripts": {
14
14
  "test": "echo \"Error: no test specified\" && exit 1",
15
- "build": "rollup -c"
15
+ "build": "rollup -c",
16
+ "deploy": "npm run build && npm publish"
16
17
  },
17
18
  "author": "David Brown",
18
19
  "license": "ISC",
package/src/badges.ts CHANGED
@@ -1,4 +1,4 @@
1
- interface BadgeConfig {
1
+ export interface BadgeConfig {
2
2
  id: string;
3
3
  icon: string;
4
4
  title: string;
@@ -21,4 +21,4 @@ const defaultBadges: BadgeConfig[] = [
21
21
  // Add more badges as needed
22
22
  ];
23
23
 
24
- export { defaultBadges, BadgeConfig };
24
+ export { defaultBadges };
@@ -1,27 +1,29 @@
1
- import React from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import { useAchievement } from '../context/AchievementContext';
3
3
 
4
4
  interface AchievementProps {
5
5
  metric: number;
6
6
  threshold: number;
7
7
  onAchieve: () => void;
8
+ message: string;
8
9
  children: React.ReactNode;
9
10
  }
10
11
 
11
- const Achievement: React.FC<AchievementProps> = ({ metric, threshold, onAchieve, children }) => {
12
- const { setMetric, achievedLevels, levels } = useAchievement();
12
+ const Achievement: React.FC<AchievementProps> = ({ metric, threshold, onAchieve, message, children }) => {
13
+ const { setMetric, achievedLevels, levels, handleAchieve } = useAchievement();
13
14
 
14
- React.useEffect(() => {
15
+ useEffect(() => {
15
16
  if (metric >= threshold && !achievedLevels.includes(threshold)) {
16
17
  onAchieve();
17
18
  const levelConfig = levels.find(level => level.threshold === threshold);
18
19
  if (levelConfig) {
19
- setMetric(metric); // Update the metric in the context
20
+ setMetric(metric);
21
+ handleAchieve(levelConfig.level, message);
20
22
  }
21
23
  }
22
- }, [metric, threshold, onAchieve, setMetric, achievedLevels, levels]);
24
+ }, [metric, threshold, onAchieve, achievedLevels, levels, message, setMetric, handleAchieve]);
23
25
 
24
- return <div>{children}</div>;
26
+ return <>{children}</>;
25
27
  };
26
28
 
27
- export default Achievement;
29
+ export default React.memo(Achievement);
@@ -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} role="dialog" aria-modal="true" aria-labelledby="achievement-title">
38
+ <h2 id="achievement-title">Achievement Unlocked!</h2>
39
+ <p>{message}</p>
40
+ <button onClick={onClose}>Okay</button>
41
+ </div>
42
+ </>
43
+ );
44
+ };
45
+
46
+ export default React.memo(AchievementModal);
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+
3
+ interface BadgesButtonProps {
4
+ onClick: () => void;
5
+ position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
6
+ }
7
+
8
+ const BadgesButton: React.FC<BadgesButtonProps> = ({ onClick, position }) => {
9
+ const buttonStyle: React.CSSProperties = {
10
+ position: 'fixed',
11
+ [position.split('-')[0]]: '20px',
12
+ [position.split('-')[1]]: '20px',
13
+ padding: '10px 20px',
14
+ backgroundColor: '#007bff',
15
+ color: '#fff',
16
+ border: 'none',
17
+ borderRadius: '5px',
18
+ cursor: 'pointer',
19
+ zIndex: 998,
20
+ };
21
+
22
+ return (
23
+ <button style={buttonStyle} onClick={onClick}>
24
+ View Achievements
25
+ </button>
26
+ );
27
+ };
28
+
29
+ export default React.memo(BadgesButton);
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { BadgeConfig } from '../badges';
3
+
4
+ interface BadgesModalProps {
5
+ show: boolean;
6
+ badges: BadgeConfig[];
7
+ onClose: () => void;
8
+ }
9
+
10
+ const BadgesModal: React.FC<BadgesModalProps> = ({ show, badges, onClose }) => {
11
+ if (!show) return null;
12
+
13
+ const modalStyle: React.CSSProperties = {
14
+ position: 'fixed',
15
+ top: '50%',
16
+ left: '50%',
17
+ transform: 'translate(-50%, -50%)',
18
+ backgroundColor: '#fff',
19
+ padding: '20px',
20
+ borderRadius: '8px',
21
+ boxShadow: '0 0 10px rgba(0,0,0,0.1)',
22
+ zIndex: 1000,
23
+ maxWidth: '80%',
24
+ maxHeight: '80%',
25
+ overflow: 'auto',
26
+ };
27
+
28
+ const overlayStyle: React.CSSProperties = {
29
+ position: 'fixed',
30
+ top: 0,
31
+ left: 0,
32
+ right: 0,
33
+ bottom: 0,
34
+ backgroundColor: 'rgba(0,0,0,0.5)',
35
+ zIndex: 999,
36
+ };
37
+
38
+ return (
39
+ <>
40
+ <div style={overlayStyle} onClick={onClose} />
41
+ <div style={modalStyle} role="dialog" aria-modal="true" aria-labelledby="badges-title">
42
+ <h2 id="badges-title">Your Achievements</h2>
43
+ <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center' }}>
44
+ {badges.map(badge => (
45
+ <div key={badge.id} style={{ margin: '10px', textAlign: 'center' }}>
46
+ <img src={badge.icon} alt={badge.title} style={{ width: '50px', height: '50px' }} />
47
+ <h4>{badge.title}</h4>
48
+ <p>{badge.description}</p>
49
+ </div>
50
+ ))}
51
+ </div>
52
+ <button onClick={onClose} style={{ marginTop: '20px' }}>Close</button>
53
+ </div>
54
+ </>
55
+ );
56
+ };
57
+
58
+ export default React.memo(BadgesModal);
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { useAchievement } from '../context/AchievementContext';
3
+
4
+ interface ProgressProps {
5
+ style?: React.CSSProperties;
6
+ }
7
+
8
+ const Progress: React.FC<ProgressProps> = ({ style }) => {
9
+ const { metric, levels, achievedLevels } = useAchievement();
10
+ const maxLevel = levels[levels.length - 1];
11
+ const progress = (metric / maxLevel.threshold) * 100;
12
+
13
+ const containerStyle: React.CSSProperties = {
14
+ width: '100%',
15
+ backgroundColor: '#e0e0e0',
16
+ borderRadius: '8px',
17
+ overflow: 'hidden',
18
+ ...style,
19
+ };
20
+
21
+ const barStyle: React.CSSProperties = {
22
+ width: `${progress}%`,
23
+ height: '20px',
24
+ backgroundColor: '#4caf50',
25
+ transition: 'width 0.5s ease-in-out',
26
+ };
27
+
28
+ return (
29
+ <div style={containerStyle}>
30
+ <div style={barStyle} role="progressbar" aria-valuenow={metric} aria-valuemin={0} aria-valuemax={maxLevel.threshold} />
31
+ <p>Level: {achievedLevels.length} / {levels.length}</p>
32
+ <p>Progress: {metric} / {maxLevel.threshold}</p>
33
+ </div>
34
+ );
35
+ };
36
+
37
+ export default React.memo(Progress);
@@ -1,8 +1,9 @@
1
- import React, { createContext, useContext, useState, ReactNode } from 'react';
1
+ import React, { createContext, useContext, useState, ReactNode, useCallback, useMemo } from 'react';
2
2
  import { defaultBadges, BadgeConfig } from '../badges';
3
- import { levels, LevelConfig } from '../levels';
4
- import ConfettiWrapper from '../components/ConfettiWrapper';
5
- import Badge from '../components/Badge';
3
+ import { defaultLevels, LevelConfig } from '../levels';
4
+ import AchievementModal from '../components/AchievementModal';
5
+ import BadgesModal from '../components/BadgesModal';
6
+ import BadgesButton from '../components/BadgesButton';
6
7
 
7
8
  interface AchievementContextProps {
8
9
  metric: number;
@@ -10,47 +11,94 @@ interface AchievementContextProps {
10
11
  badges: BadgeConfig[];
11
12
  levels: LevelConfig[];
12
13
  achievedLevels: number[];
14
+ handleAchieve: (level: number, message: string) => void;
15
+ showAchievementModal: (message: string) => void;
16
+ showBadgesModal: () => void;
17
+ }
18
+
19
+ interface AchievementProviderProps {
20
+ children: ReactNode;
21
+ initialBadges?: BadgeConfig[];
22
+ initialLevels?: LevelConfig[];
23
+ storageKey?: string;
24
+ badgesButtonPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
13
25
  }
14
26
 
15
27
  const AchievementContext = createContext<AchievementContextProps | undefined>(undefined);
16
28
 
17
- const AchievementProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
18
- const [metric, setMetric] = useState(0);
19
- const [achievedLevels, setAchievedLevels] = useState<number[]>([]);
20
- const [showConfetti, setShowConfetti] = useState(false);
29
+ export const AchievementProvider: React.FC<AchievementProviderProps> = ({
30
+ children,
31
+ initialBadges = defaultBadges,
32
+ initialLevels = defaultLevels,
33
+ storageKey = 'react-achievements',
34
+ badgesButtonPosition = 'top-right'
35
+ }) => {
36
+ const [metric, setMetric] = useState(() => {
37
+ const saved = localStorage.getItem(`${storageKey}-metric`);
38
+ return saved ? parseInt(saved, 10) : 0;
39
+ });
40
+ const [achievedLevels, setAchievedLevels] = useState<number[]>(() => {
41
+ const saved = localStorage.getItem(`${storageKey}-levels`);
42
+ return saved ? JSON.parse(saved) : [];
43
+ });
44
+ const [badges] = useState<BadgeConfig[]>(initialBadges);
45
+ const [levels] = useState<LevelConfig[]>(initialLevels);
46
+ const [achievementModalMessage, setAchievementModalMessage] = useState('');
47
+ const [showBadges, setShowBadges] = useState(false);
21
48
 
22
- const handleAchieve = (level: number) => {
49
+ const handleAchieve = useCallback((level: number, message: string) => {
23
50
  if (!achievedLevels.includes(level)) {
24
- setAchievedLevels([...achievedLevels, level]);
25
- setShowConfetti(true);
26
- setTimeout(() => setShowConfetti(false), 5000); // Confetti for 5 seconds
51
+ const newAchievedLevels = [...achievedLevels, level];
52
+ setAchievedLevels(newAchievedLevels);
53
+ localStorage.setItem(`${storageKey}-levels`, JSON.stringify(newAchievedLevels));
54
+ setAchievementModalMessage(message);
27
55
  }
28
- };
56
+ }, [achievedLevels, storageKey]);
57
+
58
+ const showAchievementModal = useCallback((message: string) => {
59
+ setAchievementModalMessage(message);
60
+ }, []);
61
+
62
+ const showBadgesModal = useCallback(() => {
63
+ setShowBadges(true);
64
+ }, []);
65
+
66
+ const contextValue = useMemo(() => ({
67
+ metric,
68
+ setMetric: (value: number) => {
69
+ setMetric(value);
70
+ localStorage.setItem(`${storageKey}-metric`, value.toString());
71
+ },
72
+ badges,
73
+ levels,
74
+ achievedLevels,
75
+ handleAchieve,
76
+ showAchievementModal,
77
+ showBadgesModal
78
+ }), [metric, badges, levels, achievedLevels, handleAchieve, showAchievementModal, showBadgesModal, storageKey]);
29
79
 
30
80
  return (
31
- <AchievementContext.Provider value={{ metric, setMetric, badges: defaultBadges, levels, achievedLevels }}>
81
+ <AchievementContext.Provider value={contextValue}>
32
82
  {children}
33
- {levels.map(levelConfig =>
34
- achievedLevels.includes(levelConfig.level) ? (
35
- <Badge
36
- key={levelConfig.level}
37
- icon={defaultBadges.find(badge => badge.id === levelConfig.badgeId)?.icon || ''}
38
- title={defaultBadges.find(badge => badge.id === levelConfig.badgeId)?.title || ''}
39
- description={defaultBadges.find(badge => badge.id === levelConfig.badgeId)?.description || ''}
40
- />
41
- ) : null
42
- )}
43
- <ConfettiWrapper show={showConfetti} />
83
+ <AchievementModal
84
+ show={!!achievementModalMessage}
85
+ message={achievementModalMessage}
86
+ onClose={() => setAchievementModalMessage('')}
87
+ />
88
+ <BadgesModal
89
+ show={showBadges}
90
+ badges={badges.filter(badge => achievedLevels.includes(levels.find(level => level.badgeId === badge.id)?.level || -1))}
91
+ onClose={() => setShowBadges(false)}
92
+ />
93
+ <BadgesButton onClick={showBadgesModal} position={badgesButtonPosition} />
44
94
  </AchievementContext.Provider>
45
95
  );
46
96
  };
47
97
 
48
- const useAchievement = () => {
98
+ export const useAchievement = () => {
49
99
  const context = useContext(AchievementContext);
50
100
  if (context === undefined) {
51
101
  throw new Error('useAchievement must be used within an AchievementProvider');
52
102
  }
53
103
  return context;
54
- };
55
-
56
- export { AchievementProvider, useAchievement };
104
+ };
package/src/index.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { AchievementProvider, useAchievement } from './context/AchievementContext';
2
2
  import Achievement from './components/Achievement';
3
+ import { BadgeConfig } from './badges';
4
+ import { LevelConfig } from './levels';
3
5
 
4
- export { AchievementProvider, useAchievement, Achievement };
6
+ export { AchievementProvider, useAchievement, Achievement, BadgeConfig, LevelConfig };
package/src/levels.ts CHANGED
@@ -1,13 +1,13 @@
1
- interface LevelConfig {
1
+ export interface LevelConfig {
2
2
  level: number;
3
3
  threshold: number;
4
4
  badgeId: string;
5
5
  }
6
6
 
7
- const levels: LevelConfig[] = [
7
+ const defaultLevels: LevelConfig[] = [
8
8
  { level: 1, threshold: 10, badgeId: 'beginner' },
9
9
  { level: 2, threshold: 50, badgeId: 'intermediate' },
10
10
  // Add more levels as needed
11
11
  ];
12
12
 
13
- export { levels, LevelConfig };
13
+ export { defaultLevels };
@@ -1,34 +0,0 @@
1
- import React from 'react';
2
-
3
- interface BadgeProps {
4
- icon: string;
5
- title: string;
6
- description: string;
7
- position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
8
- }
9
-
10
- const Badge: React.FC<BadgeProps> = ({ icon, title, description, position = 'top-right' }) => {
11
- const badgeStyle: React.CSSProperties = {
12
- position: 'fixed',
13
- [position.split('-')[0]]: '10px',
14
- [position.split('-')[1]]: '10px',
15
- display: 'flex',
16
- flexDirection: 'column',
17
- alignItems: 'center',
18
- backgroundColor: '#fff',
19
- border: '1px solid #ccc',
20
- borderRadius: '8px',
21
- padding: '10px',
22
- boxShadow: '0 0 10px rgba(0,0,0,0.1)',
23
- };
24
-
25
- return (
26
- <div style={badgeStyle}>
27
- <img src={icon} alt={title} style={{ width: '50px', height: '50px' }} />
28
- <h4>{title}</h4>
29
- <p>{description}</p>
30
- </div>
31
- );
32
- };
33
-
34
- export default Badge;