UNPKG

questro

Version:

A lightweight, modular gamification library for React with unique visual components. Features combo meters, daily challenges, achievement toasts, and progress rings. Add points, badges, quests, leaderboards, levels/XP, streaks, and notifications with zero

2 lines 10.5 kB
import {a,b as b$1,c}from'./chunk-3QNKEEDV.mjs';import {c as c$1}from'./chunk-R2GD7YTX.mjs';import {createContext,useState,useMemo,useEffect,useContext,useCallback}from'react';import {jsx,jsxs}from'react/jsx-runtime';var w=[{id:"earn-points-easy",title:"Point Collector",description:"Earn 100 points",difficulty:"easy",targetValue:100,reward:{points:50,xp:25}},{id:"earn-points-medium",title:"Point Master",description:"Earn 500 points",difficulty:"medium",targetValue:500,reward:{points:150,xp:75}},{id:"earn-points-hard",title:"Point Legend",description:"Earn 1000 points",difficulty:"hard",targetValue:1e3,reward:{points:300,xp:150}},{id:"complete-actions-easy",title:"Action Starter",description:"Complete 10 actions",difficulty:"easy",targetValue:10,reward:{points:40,xp:20}},{id:"complete-actions-medium",title:"Action Hero",description:"Complete 50 actions",difficulty:"medium",targetValue:50,reward:{points:120,xp:60}}],h=class t{constructor(e){this.events=a();this.config={userId:e.userId,templates:e.templates??w,resetHour:e.resetHour??0,maxHistory:e.maxHistory??30,onChallengeComplete:e.onChallengeComplete??(()=>{}),onNewDay:e.onNewDay??(()=>{})},this.state={userId:e.userId,currentChallenge:null,completedToday:false,streak:0,totalCompleted:0,history:[],lastResetDate:this.getCurrentDate()},this.checkReset();}getCurrentChallenge(){return this.state.currentChallenge}getStreak(){return this.state.streak}getTotalCompleted(){return this.state.totalCompleted}getHistory(){return [...this.state.history]}getTimeUntilReset(){let e=new Date,a=new Date(e);return a.setHours(this.config.resetHour,0,0,0),e>=a&&a.setDate(a.getDate()+1),a.getTime()-e.getTime()}addProgress(e){if(!this.state.currentChallenge||this.state.completedToday)return null;let a=this.state.currentChallenge;return a.status!=="active"?null:(a.currentValue=Math.min(a.currentValue+e,a.targetValue),a.updatedAt=b$1(),this.events.emit("progressUpdated",{challenge:a,delta:e}),a.currentValue>=a.targetValue?this.completeChallenge():a)}completeChallenge(){if(!this.state.currentChallenge||this.state.completedToday)return null;let e=this.state.currentChallenge;return e.status!=="active"?null:(e.status="completed",e.completedAt=b$1(),e.updatedAt=b$1(),this.state.completedToday=true,this.state.totalCompleted+=1,this.state.streak+=1,this.state.history.unshift(e),this.state.history.length>this.config.maxHistory&&(this.state.history=this.state.history.slice(0,this.config.maxHistory)),this.events.emit("challengeCompleted",{challenge:e}),this.events.emit("streakIncreased",{streak:this.state.streak}),this.config.onChallengeComplete(e),e)}checkReset(){let e=this.getCurrentDate();if(e===this.state.lastResetDate)return false;let a=this.getYesterdayDate();if(!(this.state.lastResetDate===a)&&!this.state.completedToday&&this.state.streak>0){let p=this.state.streak;this.state.streak=0,this.events.emit("streakBroken",{oldStreak:p});}this.state.currentChallenge&&this.state.currentChallenge.status==="active"&&(this.state.currentChallenge.status="expired",this.events.emit("challengeExpired",{challenge:this.state.currentChallenge}));let n=this.generateChallenge();return this.state.currentChallenge=n,this.state.completedToday=false,this.state.lastResetDate=e,this.events.emit("challengeGenerated",{challenge:n}),this.config.onNewDay(n),true}generateChallenge(){let e=this.config.templates,a=e[Math.floor(Math.random()*e.length)]??w[0],r=this.getNextResetTime();if(!a)throw new Error("No challenge templates available");return {id:c(),userId:this.state.userId,title:a.title,description:a.description,difficulty:a.difficulty,targetValue:a.targetValue,currentValue:0,reward:a.reward,status:"active",expiresAt:r,createdAt:b$1(),updatedAt:b$1()}}getCurrentDate(){return new Date().toISOString().split("T")[0]??""}getYesterdayDate(){let e=new Date;return e.setDate(e.getDate()-1),e.toISOString().split("T")[0]??""}getNextResetTime(){let e=new Date;return e.setHours(this.config.resetHour,0,0,0),e.setDate(e.getDate()+1),e.getTime()}toJSON(){return {...this.state}}static fromJSON(e,a){let r=new t(e);return r.state=a,r.checkReset(),r}};var T=createContext(null);function B({children:t,config:e,storage:a=new c$1,storageKey:r=`daily-challenge:${e.userId}`}){let[n,p]=useState(true),i=useMemo(()=>new h(e),[e.userId]);return useEffect(()=>{let d=true;return (async()=>{try{let o=await a.get(r);o&&d&&Object.assign(i,h.fromJSON(e,o));}catch(o){console.error("Failed to load daily challenge state:",o);}finally{d&&p(false);}})(),()=>{d=false;}},[a,r,e,i]),useEffect(()=>{if(n)return;let d=i.events.on("progressUpdated",async()=>{try{await a.set(r,i.toJSON());}catch(o){console.error("Failed to save daily challenge state:",o);}}),u=i.events.on("challengeCompleted",async()=>{try{await a.set(r,i.toJSON());}catch(o){console.error("Failed to save daily challenge state:",o);}});return ()=>{d(),u();}},[i,a,r,n]),useEffect(()=>{if(n)return;let d=setInterval(()=>{i.checkReset();},6e4);return ()=>clearInterval(d)},[i,n]),n?null:jsx(T.Provider,{value:i,children:t})}function x(){let t=useContext(T);if(!t)throw new Error("useDailyChallengeContext must be used within a DailyChallengeProvider");return t}function J(){let t=x(),[e,a]=useState(t.getCurrentChallenge()),[r,n]=useState(t.getStreak()),[p,i]=useState(t.getTotalCompleted()),[d,u]=useState(t.getTimeUntilReset()),o=e?.status==="completed",y=e?Math.min(e.currentValue/e.targetValue*100,100):0;useEffect(()=>{let m=t.events.on("challengeGenerated",({challenge:c})=>{a(c);}),V=t.events.on("progressUpdated",({challenge:c})=>{a({...c});}),E=t.events.on("challengeCompleted",({challenge:c})=>{a({...c}),i(t.getTotalCompleted());}),z=t.events.on("streakIncreased",({streak:c})=>{n(c);}),A=t.events.on("streakBroken",()=>{n(0);});return ()=>{m(),V(),E(),z(),A();}},[t]),useEffect(()=>{let m=setInterval(()=>{u(t.getTimeUntilReset()),t.checkReset();},1e3);return ()=>clearInterval(m)},[t]);let C=useCallback(m=>t.addProgress(m),[t]),N=useCallback(()=>t.completeChallenge(),[t]);return {challenge:e,streak:r,totalCompleted:p,completedToday:o,timeUntilReset:d,progress:y,addProgress:C,complete:N}}function L({challenge:t,progress:e,timeUntilReset:a,onAction:r,style:n,className:p}){if(!t)return jsx("div",{className:p,style:{padding:"24px",borderRadius:"12px",background:"#f8fafc",border:"1px solid #e2e8f0",textAlign:"center",color:"#64748b",...n},children:"No challenge available"});let i=t.status==="completed",d={easy:"#10b981",medium:"#f59e0b",hard:"#ef4444"},u=o=>{let y=Math.floor(o/36e5),C=Math.floor(o%36e5/6e4);return `${y}h ${C}m`};return jsxs("div",{className:p,style:{padding:"24px",borderRadius:"12px",background:i?"linear-gradient(135deg, #10b981 0%, #059669 100%)":"linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)",border:"none",boxShadow:"0 4px 20px rgba(59, 130, 246, 0.3)",color:"#fff",position:"relative",overflow:"hidden",...n},children:[jsx("div",{style:{position:"absolute",top:"12px",right:"12px",padding:"4px 12px",borderRadius:"12px",background:d[t.difficulty],fontSize:"12px",fontWeight:600,textTransform:"uppercase"},children:t.difficulty}),jsxs("div",{style:{marginBottom:"16px"},children:[jsxs("h3",{style:{fontSize:"20px",fontWeight:"bold",marginBottom:"8px",margin:0},children:[i?"\u2705 ":"\u{1F3AF} ",t.title]}),jsx("p",{style:{fontSize:"14px",opacity:.9,margin:0},children:t.description})]}),jsxs("div",{style:{marginBottom:"16px"},children:[jsxs("div",{style:{display:"flex",justifyContent:"space-between",fontSize:"14px",fontWeight:600,marginBottom:"8px"},children:[jsx("span",{children:"Progress"}),jsxs("span",{children:[t.currentValue," / ",t.targetValue]})]}),jsx("div",{style:{height:"8px",background:"rgba(255,255,255,0.2)",borderRadius:"4px",overflow:"hidden"},children:jsx("div",{style:{width:`${e}%`,height:"100%",background:"#fff",borderRadius:"4px",transition:"width 0.3s ease"}})})]}),jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"12px",background:"rgba(0,0,0,0.1)",borderRadius:"8px",marginBottom:"16px"},children:[jsx("div",{style:{fontSize:"14px",fontWeight:600},children:"Reward:"}),jsxs("div",{style:{display:"flex",gap:"12px",fontSize:"14px"},children:[jsxs("span",{children:["\u{1F4B0} ",t.reward.points]}),t.reward.xp&&jsxs("span",{children:["\u2B50 ",t.reward.xp," XP"]})]})]}),jsxs("div",{style:{textAlign:"center",fontSize:"12px",opacity:.8},children:[i?"Completed! New challenge in:":"Time remaining:"," ",jsx("strong",{children:u(a)})]}),!i&&r&&jsx("button",{onClick:r,style:{width:"100%",marginTop:"16px",padding:"12px",background:"#fff",color:"#3b82f6",border:"none",borderRadius:"8px",fontSize:"14px",fontWeight:600,cursor:"pointer"},children:"Complete Challenge"})]})}function F({streak:t,totalCompleted:e,style:a,className:r}){return jsxs("div",{className:r,style:{display:"flex",gap:"16px",...a},children:[jsxs("div",{style:{flex:1,padding:"16px",borderRadius:"8px",background:"#fef3c7",border:"1px solid #fbbf24"},children:[jsxs("div",{style:{fontSize:"28px",fontWeight:"bold",color:"#d97706"},children:[t," \u{1F525}"]}),jsx("div",{style:{fontSize:"12px",color:"#92400e",fontWeight:600},children:"Day Streak"})]}),jsxs("div",{style:{flex:1,padding:"16px",borderRadius:"8px",background:"#dbeafe",border:"1px solid #3b82f6"},children:[jsx("div",{style:{fontSize:"28px",fontWeight:"bold",color:"#2563eb"},children:e}),jsx("div",{style:{fontSize:"12px",color:"#1e40af",fontWeight:600},children:"Total Completed"})]})]})}function $({timeUntilReset:t,style:e,className:a}){let r=Math.floor(t/36e5),n=Math.floor(t%36e5/6e4),p=Math.floor(t%6e4/1e3);return jsxs("div",{className:a,style:{display:"flex",gap:"8px",justifyContent:"center",...e},children:[jsx(b,{value:r,label:"Hours"}),jsx(I,{}),jsx(b,{value:n,label:"Minutes"}),jsx(I,{}),jsx(b,{value:p,label:"Seconds"})]})}function b({value:t,label:e}){return jsxs("div",{style:{display:"flex",flexDirection:"column",alignItems:"center",padding:"12px",background:"#1e293b",borderRadius:"8px",minWidth:"60px"},children:[jsx("div",{style:{fontSize:"24px",fontWeight:"bold",color:"#fff"},children:String(t).padStart(2,"0")}),jsx("div",{style:{fontSize:"10px",color:"#94a3b8",textTransform:"uppercase"},children:e})]})}function I(){return jsx("div",{style:{display:"flex",alignItems:"center",color:"#64748b",fontSize:"24px",fontWeight:"bold"},children:":"})}export{h as a,B as b,x as c,J as d,L as e,F as f,$ as g};//# sourceMappingURL=chunk-K4A6Q2TN.mjs.map //# sourceMappingURL=chunk-K4A6Q2TN.mjs.map