react-achievements 1.0.2 → 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 };
@@ -6,5 +6,5 @@ interface AchievementProps {
6
6
  message: string;
7
7
  children: React.ReactNode;
8
8
  }
9
- declare const Achievement: React.FC<AchievementProps>;
10
- export default Achievement;
9
+ declare const _default: React.NamedExoticComponent<AchievementProps>;
10
+ export default _default;
@@ -4,5 +4,5 @@ interface AchievementModalProps {
4
4
  message: string;
5
5
  onClose: () => void;
6
6
  }
7
- declare const AchievementModal: React.FC<AchievementModalProps>;
8
- export default AchievementModal;
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;
@@ -8,9 +8,16 @@ interface AchievementContextProps {
8
8
  levels: LevelConfig[];
9
9
  achievedLevels: number[];
10
10
  handleAchieve: (level: number, message: string) => void;
11
+ showAchievementModal: (message: string) => void;
12
+ showBadgesModal: () => void;
11
13
  }
12
- declare const AchievementProvider: React.FC<{
14
+ interface AchievementProviderProps {
13
15
  children: ReactNode;
14
- }>;
15
- declare const useAchievement: () => AchievementContextProps;
16
- 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,103 +18,12 @@ 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;
116
- };
117
-
118
27
  const AchievementModal = ({ show, message, onClose }) => {
119
28
  if (!show)
120
29
  return null;
@@ -140,57 +49,114 @@ const AchievementModal = ({ show, message, onClose }) => {
140
49
  };
141
50
  return (React.createElement(React.Fragment, null,
142
51
  React.createElement("div", { style: overlayStyle, onClick: onClose }),
143
- React.createElement("div", { style: modalStyle },
144
- React.createElement("h2", null, "Achievement Unlocked!"),
52
+ React.createElement("div", { style: modalStyle, role: "dialog", "aria-modal": "true", "aria-labelledby": "achievement-title" },
53
+ React.createElement("h2", { id: "achievement-title" }, "Achievement Unlocked!"),
145
54
  React.createElement("p", null, message),
146
55
  React.createElement("button", { onClick: onClose }, "Okay"))));
147
56
  };
57
+ var AchievementModal$1 = React.memo(AchievementModal);
148
58
 
149
- const Badge = ({ icon, title, description, position = 'top-right' }) => {
150
- const badgeStyle = {
59
+ const BadgesModal = ({ show, badges, onClose }) => {
60
+ if (!show)
61
+ return null;
62
+ const modalStyle = {
151
63
  position: 'fixed',
152
- [position.split('-')[0]]: '10px',
153
- [position.split('-')[1]]: '10px',
154
- display: 'flex',
155
- flexDirection: 'column',
156
- alignItems: 'center',
64
+ top: '50%',
65
+ left: '50%',
66
+ transform: 'translate(-50%, -50%)',
157
67
  backgroundColor: '#fff',
158
- border: '1px solid #ccc',
68
+ padding: '20px',
159
69
  borderRadius: '8px',
160
- padding: '10px',
161
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,
162
109
  };
163
- return (React.createElement("div", { style: badgeStyle },
164
- React.createElement("img", { src: icon, alt: title, style: { width: '50px', height: '50px' } }),
165
- React.createElement("h4", null, title),
166
- React.createElement("p", null, description)));
110
+ return (React.createElement("button", { style: buttonStyle, onClick: onClick }, "View Achievements"));
167
111
  };
112
+ var BadgesButton$1 = React.memo(BadgesButton);
168
113
 
169
114
  const AchievementContext = React.createContext(undefined);
170
- const AchievementProvider = ({ children }) => {
171
- const [metric, setMetric] = React.useState(0);
172
- const [achievedLevels, setAchievedLevels] = React.useState([]);
173
- const [showConfetti, setShowConfetti] = React.useState(false);
174
- const [modalMessage, setModalMessage] = React.useState('');
175
- const handleAchieve = (level, message) => {
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) => {
176
129
  if (!achievedLevels.includes(level)) {
177
- setAchievedLevels([...achievedLevels, level]);
178
- setShowConfetti(true);
179
- setModalMessage(message);
130
+ const newAchievedLevels = [...achievedLevels, level];
131
+ setAchievedLevels(newAchievedLevels);
132
+ localStorage.setItem(`${storageKey}-levels`, JSON.stringify(newAchievedLevels));
133
+ setAchievementModalMessage(message);
180
134
  }
181
- };
182
- const handleCloseModal = () => {
183
- setShowConfetti(false);
184
- setModalMessage('');
185
- };
186
- return (React.createElement(AchievementContext.Provider, { value: { metric, setMetric, badges: defaultBadges, levels, achievedLevels, handleAchieve } },
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 },
187
156
  children,
188
- levels.map(levelConfig => {
189
- var _a, _b, _c;
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;
191
- }),
192
- React.createElement(ConfettiWrapper, { show: showConfetti }),
193
- React.createElement(AchievementModal, { show: !!modalMessage, message: modalMessage, onClose: handleCloseModal })));
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 })));
194
160
  };
195
161
  const useAchievement = () => {
196
162
  const context = React.useContext(AchievementContext);
@@ -211,10 +177,11 @@ const Achievement = ({ metric, threshold, onAchieve, message, children }) => {
211
177
  handleAchieve(levelConfig.level, message);
212
178
  }
213
179
  }
214
- }, [metric, threshold, onAchieve, setMetric, achievedLevels, levels, message, handleAchieve]);
215
- return React.createElement("div", null, children);
180
+ }, [metric, threshold, onAchieve, achievedLevels, levels, message, setMetric, handleAchieve]);
181
+ return React.createElement(React.Fragment, null, children);
216
182
  };
183
+ var Achievement$1 = React.memo(Achievement);
217
184
 
218
- exports.Achievement = Achievement;
185
+ exports.Achievement = Achievement$1;
219
186
  exports.AchievementProvider = AchievementProvider;
220
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,103 +16,12 @@ 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;
114
- };
115
-
116
25
  const AchievementModal = ({ show, message, onClose }) => {
117
26
  if (!show)
118
27
  return null;
@@ -138,57 +47,114 @@ const AchievementModal = ({ show, message, onClose }) => {
138
47
  };
139
48
  return (React.createElement(React.Fragment, null,
140
49
  React.createElement("div", { style: overlayStyle, onClick: onClose }),
141
- React.createElement("div", { style: modalStyle },
142
- React.createElement("h2", null, "Achievement Unlocked!"),
50
+ React.createElement("div", { style: modalStyle, role: "dialog", "aria-modal": "true", "aria-labelledby": "achievement-title" },
51
+ React.createElement("h2", { id: "achievement-title" }, "Achievement Unlocked!"),
143
52
  React.createElement("p", null, message),
144
53
  React.createElement("button", { onClick: onClose }, "Okay"))));
145
54
  };
55
+ var AchievementModal$1 = React.memo(AchievementModal);
146
56
 
147
- const Badge = ({ icon, title, description, position = 'top-right' }) => {
148
- const badgeStyle = {
57
+ const BadgesModal = ({ show, badges, onClose }) => {
58
+ if (!show)
59
+ return null;
60
+ const modalStyle = {
149
61
  position: 'fixed',
150
- [position.split('-')[0]]: '10px',
151
- [position.split('-')[1]]: '10px',
152
- display: 'flex',
153
- flexDirection: 'column',
154
- alignItems: 'center',
62
+ top: '50%',
63
+ left: '50%',
64
+ transform: 'translate(-50%, -50%)',
155
65
  backgroundColor: '#fff',
156
- border: '1px solid #ccc',
66
+ padding: '20px',
157
67
  borderRadius: '8px',
158
- padding: '10px',
159
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,
160
107
  };
161
- return (React.createElement("div", { style: badgeStyle },
162
- React.createElement("img", { src: icon, alt: title, style: { width: '50px', height: '50px' } }),
163
- React.createElement("h4", null, title),
164
- React.createElement("p", null, description)));
108
+ return (React.createElement("button", { style: buttonStyle, onClick: onClick }, "View Achievements"));
165
109
  };
110
+ var BadgesButton$1 = React.memo(BadgesButton);
166
111
 
167
112
  const AchievementContext = createContext(undefined);
168
- const AchievementProvider = ({ children }) => {
169
- const [metric, setMetric] = useState(0);
170
- const [achievedLevels, setAchievedLevels] = useState([]);
171
- const [showConfetti, setShowConfetti] = useState(false);
172
- const [modalMessage, setModalMessage] = useState('');
173
- const handleAchieve = (level, message) => {
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) => {
174
127
  if (!achievedLevels.includes(level)) {
175
- setAchievedLevels([...achievedLevels, level]);
176
- setShowConfetti(true);
177
- setModalMessage(message);
128
+ const newAchievedLevels = [...achievedLevels, level];
129
+ setAchievedLevels(newAchievedLevels);
130
+ localStorage.setItem(`${storageKey}-levels`, JSON.stringify(newAchievedLevels));
131
+ setAchievementModalMessage(message);
178
132
  }
179
- };
180
- const handleCloseModal = () => {
181
- setShowConfetti(false);
182
- setModalMessage('');
183
- };
184
- return (React.createElement(AchievementContext.Provider, { value: { metric, setMetric, badges: defaultBadges, levels, achievedLevels, handleAchieve } },
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 },
185
154
  children,
186
- levels.map(levelConfig => {
187
- var _a, _b, _c;
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;
189
- }),
190
- React.createElement(ConfettiWrapper, { show: showConfetti }),
191
- React.createElement(AchievementModal, { show: !!modalMessage, message: modalMessage, onClose: handleCloseModal })));
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 })));
192
158
  };
193
159
  const useAchievement = () => {
194
160
  const context = useContext(AchievementContext);
@@ -200,7 +166,7 @@ const useAchievement = () => {
200
166
 
201
167
  const Achievement = ({ metric, threshold, onAchieve, message, children }) => {
202
168
  const { setMetric, achievedLevels, levels, handleAchieve } = useAchievement();
203
- React.useEffect(() => {
169
+ useEffect(() => {
204
170
  if (metric >= threshold && !achievedLevels.includes(threshold)) {
205
171
  onAchieve();
206
172
  const levelConfig = levels.find(level => level.threshold === threshold);
@@ -209,8 +175,9 @@ const Achievement = ({ metric, threshold, onAchieve, message, children }) => {
209
175
  handleAchieve(levelConfig.level, message);
210
176
  }
211
177
  }
212
- }, [metric, threshold, onAchieve, setMetric, achievedLevels, levels, message, handleAchieve]);
213
- return React.createElement("div", null, children);
178
+ }, [metric, threshold, onAchieve, achievedLevels, levels, message, setMetric, handleAchieve]);
179
+ return React.createElement(React.Fragment, null, children);
214
180
  };
181
+ var Achievement$1 = React.memo(Achievement);
215
182
 
216
- 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.2",
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,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import { useAchievement } from '../context/AchievementContext';
3
3
 
4
4
  interface AchievementProps {
@@ -12,7 +12,7 @@ interface AchievementProps {
12
12
  const Achievement: React.FC<AchievementProps> = ({ metric, threshold, onAchieve, message, children }) => {
13
13
  const { setMetric, achievedLevels, levels, handleAchieve } = useAchievement();
14
14
 
15
- React.useEffect(() => {
15
+ useEffect(() => {
16
16
  if (metric >= threshold && !achievedLevels.includes(threshold)) {
17
17
  onAchieve();
18
18
  const levelConfig = levels.find(level => level.threshold === threshold);
@@ -21,9 +21,9 @@ const Achievement: React.FC<AchievementProps> = ({ metric, threshold, onAchieve,
21
21
  handleAchieve(levelConfig.level, message);
22
22
  }
23
23
  }
24
- }, [metric, threshold, onAchieve, setMetric, achievedLevels, levels, message, handleAchieve]);
24
+ }, [metric, threshold, onAchieve, achievedLevels, levels, message, setMetric, handleAchieve]);
25
25
 
26
- return <div>{children}</div>;
26
+ return <>{children}</>;
27
27
  };
28
28
 
29
- export default Achievement;
29
+ export default React.memo(Achievement);
@@ -34,8 +34,8 @@ const AchievementModal: React.FC<AchievementModalProps> = ({ show, message, onCl
34
34
  return (
35
35
  <>
36
36
  <div style={overlayStyle} onClick={onClose} />
37
- <div style={modalStyle}>
38
- <h2>Achievement Unlocked!</h2>
37
+ <div style={modalStyle} role="dialog" aria-modal="true" aria-labelledby="achievement-title">
38
+ <h2 id="achievement-title">Achievement Unlocked!</h2>
39
39
  <p>{message}</p>
40
40
  <button onClick={onClose}>Okay</button>
41
41
  </div>
@@ -43,4 +43,4 @@ const AchievementModal: React.FC<AchievementModalProps> = ({ show, message, onCl
43
43
  );
44
44
  };
45
45
 
46
- export default AchievementModal;
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,9 +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';
3
+ import { defaultLevels, LevelConfig } from '../levels';
5
4
  import AchievementModal from '../components/AchievementModal';
6
- import Badge from '../components/Badge';
5
+ import BadgesModal from '../components/BadgesModal';
6
+ import BadgesButton from '../components/BadgesButton';
7
7
 
8
8
  interface AchievementContextProps {
9
9
  metric: number;
@@ -12,54 +12,93 @@ interface AchievementContextProps {
12
12
  levels: LevelConfig[];
13
13
  achievedLevels: number[];
14
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';
15
25
  }
16
26
 
17
27
  const AchievementContext = createContext<AchievementContextProps | undefined>(undefined);
18
28
 
19
- const AchievementProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
20
- const [metric, setMetric] = useState(0);
21
- const [achievedLevels, setAchievedLevels] = useState<number[]>([]);
22
- const [showConfetti, setShowConfetti] = useState(false);
23
- const [modalMessage, setModalMessage] = useState('');
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);
24
48
 
25
- const handleAchieve = (level: number, message: string) => {
49
+ const handleAchieve = useCallback((level: number, message: string) => {
26
50
  if (!achievedLevels.includes(level)) {
27
- setAchievedLevels([...achievedLevels, level]);
28
- setShowConfetti(true);
29
- setModalMessage(message);
51
+ const newAchievedLevels = [...achievedLevels, level];
52
+ setAchievedLevels(newAchievedLevels);
53
+ localStorage.setItem(`${storageKey}-levels`, JSON.stringify(newAchievedLevels));
54
+ setAchievementModalMessage(message);
30
55
  }
31
- };
56
+ }, [achievedLevels, storageKey]);
57
+
58
+ const showAchievementModal = useCallback((message: string) => {
59
+ setAchievementModalMessage(message);
60
+ }, []);
32
61
 
33
- const handleCloseModal = () => {
34
- setShowConfetti(false);
35
- setModalMessage('');
36
- };
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]);
37
79
 
38
80
  return (
39
- <AchievementContext.Provider value={{ metric, setMetric, badges: defaultBadges, levels, achievedLevels, handleAchieve }}>
81
+ <AchievementContext.Provider value={contextValue}>
40
82
  {children}
41
- {levels.map(levelConfig =>
42
- achievedLevels.includes(levelConfig.level) ? (
43
- <Badge
44
- key={levelConfig.level}
45
- icon={defaultBadges.find(badge => badge.id === levelConfig.badgeId)?.icon || ''}
46
- title={defaultBadges.find(badge => badge.id === levelConfig.badgeId)?.title || ''}
47
- description={defaultBadges.find(badge => badge.id === levelConfig.badgeId)?.description || ''}
48
- />
49
- ) : null
50
- )}
51
- <ConfettiWrapper show={showConfetti} />
52
- <AchievementModal show={!!modalMessage} message={modalMessage} onClose={handleCloseModal} />
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} />
53
94
  </AchievementContext.Provider>
54
95
  );
55
96
  };
56
97
 
57
- const useAchievement = () => {
98
+ export const useAchievement = () => {
58
99
  const context = useContext(AchievementContext);
59
100
  if (context === undefined) {
60
101
  throw new Error('useAchievement must be used within an AchievementProvider');
61
102
  }
62
103
  return context;
63
- };
64
-
65
- 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;