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 • 8.46 kB
JavaScript
'use strict';var chunkPADZRWVT_js=require('./chunk-PADZRWVT.js'),react=require('react'),jsxRuntime=require('react/jsx-runtime');var y=class{constructor(r=[],o={}){this.events=chunkPADZRWVT_js.a();this.on=this.events.on;this.emit=this.events.emit;this.config={userId:o.userId,maxEntries:o.maxEntries??100};let i=this.sortAndRankEntries(r);this.state={entries:i,currentUserEntry:o.userId?i.find(n=>n.userId===o.userId):void 0,period:o.period??"all-time",metric:o.metric??"points",lastUpdated:chunkPADZRWVT_js.b()};}sortAndRankEntries(r){return [...r].sort((i,n)=>n.score-i.score).map((i,n)=>({...i,rank:n+1}))}notifyStateChange(){this.state.lastUpdated=chunkPADZRWVT_js.b(),this.emit("leaderboard-updated",this.state);}getState(){return {...this.state,entries:[...this.state.entries]}}getTopEntries(r=10){return this.state.entries.slice(0,r)}getUserEntry(r){return this.state.entries.find(o=>o.userId===r)}getCurrentUserEntry(){return this.state.currentUserEntry}updateScore(r,o,i){let n=this.state.entries.findIndex(d=>d.userId===r);if(n!==-1){let d=this.state.entries[n],u=d.rank;d.score=o,d.updatedAt=chunkPADZRWVT_js.b(),i&&(d.username=i),this.state.entries=this.sortAndRankEntries(this.state.entries);let s=this.state.entries.find(v=>v.userId===r);s&&s.rank!==u&&this.emit("rank-changed",s),this.config.userId===r&&(this.state.currentUserEntry=s);}else this.addEntry({userId:r,username:i??"Unknown",score:o});this.notifyStateChange();}addEntry(r){let o={...r,rank:0,updatedAt:chunkPADZRWVT_js.b()};this.state.entries.push(o),this.state.entries=this.sortAndRankEntries(this.state.entries),this.state.entries.length>this.config.maxEntries&&(this.state.entries=this.state.entries.slice(0,this.config.maxEntries)),this.config.userId===r.userId&&(this.state.currentUserEntry=this.state.entries.find(i=>i.userId===r.userId)),this.notifyStateChange();}removeEntry(r){this.state.entries=this.state.entries.filter(o=>o.userId!==r),this.state.entries=this.sortAndRankEntries(this.state.entries),this.config.userId===r&&(this.state.currentUserEntry=void 0),this.notifyStateChange();}setPeriod(r){this.state.period=r,this.notifyStateChange();}setMetric(r){this.state.metric=r,this.notifyStateChange();}reset(){this.state={entries:[],currentUserEntry:void 0,period:this.state.period,metric:this.state.metric,lastUpdated:chunkPADZRWVT_js.b()},this.notifyStateChange();}};var g=react.createContext(null);function V({children:e,entries:r=[],config:o={}}){let i=react.useMemo(()=>new y(r,o),[]),n=react.useMemo(()=>({service:i}),[i]);return jsxRuntime.jsx(g.Provider,{value:n,children:e})}function x(){let e=react.useContext(g);if(!e)throw new Error("useLeaderboardContext must be used within LeaderboardProvider");return e}function q(){let{service:e}=x(),[r,o]=react.useState(e.getState().entries),[i,n]=react.useState(e.getCurrentUserEntry()),[d,u]=react.useState(e.getState().period),[s,v]=react.useState(e.getState().metric);react.useEffect(()=>{let c=e.on("leaderboard-updated",f=>{o(f.entries),n(f.currentUserEntry),u(f.period),v(f.metric);});return ()=>c()},[e]);let E=react.useCallback(c=>e.getTopEntries(c),[e]),C=react.useCallback(c=>e.getUserEntry(c),[e]),k=react.useCallback((c,f,P)=>{e.updateScore(c,f,P);},[e]),U=react.useCallback(c=>{e.setPeriod(c);},[e]),S=react.useCallback(c=>{e.setMetric(c);},[e]);return {entries:r,topEntries:E,currentUserEntry:i,getUserEntry:C,updateScore:k,period:d,metric:s,setPeriod:U,setMetric:S}}function Y({entries:e,currentUserId:r,maxEntries:o=10,showAvatar:i=true,className:n=""}){let d=e.slice(0,o);return jsxRuntime.jsxs("div",{className:`leaderboard ${n}`,style:t.container,children:[jsxRuntime.jsx("div",{style:t.list,children:d.map(u=>jsxRuntime.jsx(A,{entry:u,isCurrentUser:u.userId===r,showAvatar:i},u.userId))}),e.length===0&&jsxRuntime.jsx("div",{style:t.empty,children:"No entries yet"})]})}function A({entry:e,isCurrentUser:r,showAvatar:o}){let n=(d=>d===1?"\u{1F947}":d===2?"\u{1F948}":d===3?"\u{1F949}":null)(e.rank);return jsxRuntime.jsxs("div",{style:{...t.row,...r?t.rowHighlight:{}},children:[jsxRuntime.jsx("div",{style:t.rank,children:n||`#${e.rank}`}),o&&jsxRuntime.jsx("div",{style:t.avatar,children:e.avatar?jsxRuntime.jsx("img",{src:e.avatar,alt:e.username,style:t.avatarImg}):jsxRuntime.jsx("div",{style:t.avatarPlaceholder,children:e.username.charAt(0).toUpperCase()})}),jsxRuntime.jsx("div",{style:t.username,children:e.username}),jsxRuntime.jsx("div",{style:t.score,children:e.score.toLocaleString()})]})}function H({period:e,metric:r,onPeriodChange:o,onMetricChange:i,className:n=""}){let d=["daily","weekly","monthly","all-time"],u=["points","badges","quests-completed"];return jsxRuntime.jsxs("div",{className:`leaderboard-filters ${n}`,style:t.filters,children:[jsxRuntime.jsxs("div",{style:t.filterGroup,children:[jsxRuntime.jsx("label",{style:t.filterLabel,children:"Period:"}),jsxRuntime.jsx("div",{style:t.filterButtons,children:d.map(s=>jsxRuntime.jsx("button",{onClick:()=>o(s),style:{...t.filterButton,...e===s?t.filterButtonActive:{}},children:s.replace("-"," ")},s))})]}),jsxRuntime.jsxs("div",{style:t.filterGroup,children:[jsxRuntime.jsx("label",{style:t.filterLabel,children:"Metric:"}),jsxRuntime.jsx("div",{style:t.filterButtons,children:u.map(s=>jsxRuntime.jsx("button",{onClick:()=>i(s),style:{...t.filterButton,...r===s?t.filterButtonActive:{}},children:s.replace("-"," ")},s))})]})]})}function D({entry:e,totalEntries:r,className:o=""}){if(!e)return jsxRuntime.jsx("div",{className:`current-user-rank ${o}`,style:t.currentUserRank,children:jsxRuntime.jsx("p",{style:t.notRanked,children:"You are not ranked yet"})});let i=r>0?Math.round((1-e.rank/r)*100):0;return jsxRuntime.jsxs("div",{className:`current-user-rank ${o}`,style:t.currentUserRank,children:[jsxRuntime.jsxs("div",{style:t.currentUserInfo,children:[jsxRuntime.jsx("div",{style:t.currentUserLabel,children:"Your Rank"}),jsxRuntime.jsxs("div",{style:t.currentUserValue,children:["#",e.rank]})]}),jsxRuntime.jsxs("div",{style:t.currentUserInfo,children:[jsxRuntime.jsx("div",{style:t.currentUserLabel,children:"Your Score"}),jsxRuntime.jsx("div",{style:t.currentUserValue,children:e.score.toLocaleString()})]}),jsxRuntime.jsxs("div",{style:t.currentUserInfo,children:[jsxRuntime.jsx("div",{style:t.currentUserLabel,children:"Percentile"}),jsxRuntime.jsxs("div",{style:t.currentUserValue,children:["Top ",i,"%"]})]})]})}var t={container:{border:"1px solid #e5e7eb",borderRadius:"8px",backgroundColor:"#ffffff",overflow:"hidden"},list:{display:"flex",flexDirection:"column"},row:{display:"flex",alignItems:"center",padding:"12px 16px",borderBottom:"1px solid #f3f4f6",transition:"background-color 0.2s"},rowHighlight:{backgroundColor:"#eff6ff",borderLeft:"4px solid #3b82f6"},rank:{fontSize:"18px",fontWeight:700,minWidth:"50px",color:"#111827"},avatar:{marginRight:"12px"},avatarImg:{width:"40px",height:"40px",borderRadius:"50%",objectFit:"cover"},avatarPlaceholder:{width:"40px",height:"40px",borderRadius:"50%",backgroundColor:"#e5e7eb",display:"flex",alignItems:"center",justifyContent:"center",fontSize:"18px",fontWeight:600,color:"#6b7280"},username:{flex:1,fontSize:"16px",fontWeight:500,color:"#374151"},score:{fontSize:"18px",fontWeight:700,color:"#111827"},empty:{padding:"32px",textAlign:"center",color:"#9ca3af",fontSize:"14px"},filters:{padding:"16px",backgroundColor:"#f9fafb",borderRadius:"8px",marginBottom:"16px"},filterGroup:{marginBottom:"12px"},filterLabel:{display:"block",fontSize:"14px",fontWeight:600,marginBottom:"8px",color:"#374151"},filterButtons:{display:"flex",gap:"8px",flexWrap:"wrap"},filterButton:{padding:"6px 12px",borderRadius:"6px",border:"1px solid #d1d5db",backgroundColor:"#ffffff",fontSize:"14px",fontWeight:500,color:"#6b7280",cursor:"pointer",transition:"all 0.2s",textTransform:"capitalize"},filterButtonActive:{backgroundColor:"#3b82f6",color:"#ffffff",borderColor:"#3b82f6"},currentUserRank:{display:"flex",justifyContent:"space-around",padding:"20px",backgroundColor:"#f9fafb",borderRadius:"8px",marginBottom:"16px",border:"2px solid #3b82f6"},currentUserInfo:{textAlign:"center"},currentUserLabel:{fontSize:"12px",color:"#6b7280",marginBottom:"4px",textTransform:"uppercase"},currentUserValue:{fontSize:"24px",fontWeight:700,color:"#111827"},notRanked:{margin:0,color:"#9ca3af",fontSize:"14px"}};exports.a=y;exports.b=V;exports.c=x;exports.d=q;exports.e=Y;exports.f=H;exports.g=D;//# sourceMappingURL=chunk-SCANIE7H.js.map
//# sourceMappingURL=chunk-SCANIE7H.js.map