@opexa/portal-components 0.0.459 → 0.0.461
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Quests/MultiWageringQuest.js +22 -6
- package/dist/components/Quests/Quests.client.js +8 -6
- package/dist/components/TopProgress/TopProgressBar.d.ts +6 -0
- package/dist/components/TopProgress/TopProgressBar.js +145 -0
- package/dist/components/TopProgress/index.d.ts +1 -0
- package/dist/components/TopProgress/index.js +1 -0
- package/package.json +1 -1
|
@@ -19,6 +19,18 @@ export function MultiWageringQuest() {
|
|
|
19
19
|
const quest = useQuestContext();
|
|
20
20
|
const { stages, totalStage, currentStage, progressPercentage } = useMemo(() => {
|
|
21
21
|
const currentStage = quest.stages.filter((m) => m.cleared).length;
|
|
22
|
+
const getStageItemProgress = (overallProgress, stageNumber) => {
|
|
23
|
+
const stageSize = 100 / quest.stages.length;
|
|
24
|
+
const stageStart = (stageNumber - 1) * stageSize;
|
|
25
|
+
const stageEnd = stageStart + stageSize;
|
|
26
|
+
if (overallProgress >= stageEnd) {
|
|
27
|
+
return 100;
|
|
28
|
+
}
|
|
29
|
+
else if (overallProgress > stageStart) {
|
|
30
|
+
return ((overallProgress - stageStart) / stageSize) * 100;
|
|
31
|
+
}
|
|
32
|
+
return 0;
|
|
33
|
+
};
|
|
22
34
|
if (!quest?.stages) {
|
|
23
35
|
return {
|
|
24
36
|
stages: [],
|
|
@@ -27,8 +39,12 @@ export function MultiWageringQuest() {
|
|
|
27
39
|
progressPercentage: 0,
|
|
28
40
|
};
|
|
29
41
|
}
|
|
42
|
+
const transformedStages = quest.stages.map((stage, idx) => ({
|
|
43
|
+
...stage,
|
|
44
|
+
progressValue: getStageItemProgress(parseFloat(quest.progressPercentage), idx + 1),
|
|
45
|
+
}));
|
|
30
46
|
return {
|
|
31
|
-
stages:
|
|
47
|
+
stages: transformedStages,
|
|
32
48
|
totalStage: quest.stages.length,
|
|
33
49
|
currentStage: currentStage === 0 ? 1 : currentStage,
|
|
34
50
|
progressPercentage: +quest.progressPercentage,
|
|
@@ -68,23 +84,23 @@ function Stages(props) {
|
|
|
68
84
|
const stage = props?.stages?.findLastIndex((stage) => stage.cleared) ?? 0;
|
|
69
85
|
return stage !== -1 ? stage + 1 : 1;
|
|
70
86
|
}, [props.stages]);
|
|
71
|
-
return (_jsx(Dialog.Root, { lazyMount: true, unmountOnExit: true, open: open, onOpenChange: (details) => setOpen(details.open), closeOnEscape: false, closeOnInteractOutside: false, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, { className: "!z-[calc(var(--z-dialog)+1)]" }), _jsx(Dialog.Positioner, { className: "!z-[calc(var(--z-dialog)+2)] flex items-center justify-center", children: _jsxs(Dialog.Content, { className: "mx-auto mt-40 min-h-auto min-w-[23rem] max-w-[25rem] overflow-y-auto rounded-xl px-4 py-5", children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsxs("div", { className: "group flex flex-col space-y-5 text-sm text-text-quarterary-brand", children: [_jsx("h2", { className: "font-semibold text-lg text-text-primary-900", children: props.quest?.name || 'Quest' }), _jsx("div", { dangerouslySetInnerHTML: {
|
|
87
|
+
return (_jsx(Dialog.Root, { lazyMount: true, unmountOnExit: true, open: open, onOpenChange: (details) => setOpen(details.open), closeOnEscape: false, closeOnInteractOutside: false, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, { className: "!z-[calc(var(--z-dialog)+1)]" }), _jsx(Dialog.Positioner, { className: "!z-[calc(var(--z-dialog)+2)] flex items-center justify-center", children: _jsxs(Dialog.Content, { className: "mx-auto mt-40 min-h-auto min-w-[23rem] max-w-[25rem] overflow-y-auto rounded-xl px-4 py-5", children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsxs("div", { className: "group flex flex-col space-y-5 text-sm text-text-quarterary-brand", children: [_jsx("h2", { className: "font-semibold text-lg text-text-primary-900", children: props.quest?.name || 'Quest' }), _jsx("div", { className: "font-normal text-[#94969C] text-sm", dangerouslySetInnerHTML: {
|
|
72
88
|
__html: props.quest?.description ||
|
|
73
89
|
'Ready for a challenge? Complete them all and get amazing rewards!',
|
|
74
90
|
} }), _jsx("div", { className: "flex w-full flex-col space-y-3", children: props.stages.map((stage, idx) => {
|
|
75
91
|
if (currentStage >= idx + 1) {
|
|
76
|
-
return (_jsx(QuestWageringStageUnlocked, { data: stage, bonus: stage.bonusAmount, stage: idx, isCompleted: stage.cleared }, idx));
|
|
92
|
+
return (_jsx(QuestWageringStageUnlocked, { data: stage, progressValue: stage.progressValue, bonus: stage.bonusAmount, stage: idx, isCompleted: stage.cleared }, idx));
|
|
77
93
|
}
|
|
78
94
|
else {
|
|
79
95
|
return _jsx(QuestWageringStageLocked, { stage: idx }, idx);
|
|
80
96
|
}
|
|
81
97
|
}) }), _jsx(Button, { size: "sm", onClick: () => setOpen(false), "aria-label": "Close", className: "w-full", children: "Close" })] })] }) })] }) }));
|
|
82
98
|
}
|
|
83
|
-
function QuestWageringStageUnlocked({ bonus, stage, data, isCompleted = false, }) {
|
|
99
|
+
function QuestWageringStageUnlocked({ bonus, stage, progressValue, data, isCompleted = false, }) {
|
|
84
100
|
const localeInfo = useLocaleInfo();
|
|
85
|
-
return (_jsxs("div", { className: "mb-4 flex flex-col justify-between rounded-xl bg-bg-secondary p-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("p", { className: "font-medium", children: ["Stage ", stage + 1] }), _jsx(Badge.Root, { colorScheme: isCompleted ? 'success' : 'blue', size: "md", round: true, children: _jsx(Badge.Label, { children: data.cleared ? 'Completed' : 'Ongoing' }) })] }), _jsxs("div", { className: "mt-2.5 flex items-center justify-between", children: [_jsxs("p", { children: ["Progress ",
|
|
101
|
+
return (_jsxs("div", { className: "mb-4 flex flex-col justify-between rounded-xl bg-bg-secondary p-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("p", { className: "font-medium", children: ["Stage ", stage + 1] }), _jsx(Badge.Root, { colorScheme: isCompleted ? 'success' : 'blue', size: "md", round: true, children: _jsx(Badge.Label, { children: data.cleared ? 'Completed' : 'Ongoing' }) })] }), _jsxs("div", { className: "mt-2.5 flex items-center justify-between", children: [_jsxs("p", { children: ["Progress ", progressValue, "%"] }), _jsx(Badge.Root, { size: "md", className: "border border-border-badge bg-bg-badge", round: false, children: _jsxs(Badge.Label, { className: "!text-text-badge", children: ["+", formatNumber(bonus, {
|
|
86
102
|
currency: localeInfo.currency.code,
|
|
87
|
-
}), ' ', "Bonus"] }) })] }), _jsx(Progress.Root, { max: 100, className: "mt-2 h-2 rounded-full bg-bg-primary", value:
|
|
103
|
+
}), ' ', "Bonus"] }) })] }), _jsx(Progress.Root, { max: 100, className: "mt-2 h-2 rounded-full bg-bg-primary", value: progressValue ?? 0, children: _jsx(Progress.Track, { children: _jsx(Progress.Range, { className: "bg-utility-brand-600" }) }) })] }));
|
|
88
104
|
}
|
|
89
105
|
function QuestWageringStageLocked({ stage }) {
|
|
90
106
|
return (_jsxs("div", { className: "mb-4 flex items-center justify-between rounded-xl bg-bg-secondary p-3", children: [_jsxs("p", { className: "font-medium", children: ["Stage ", stage + 1] }), _jsx("div", { className: "flex items-center justify-center rounded-full border border-border-lock bg-bg-lock p-1", children: _jsx(Lock01Icon, { className: "size-3 stroke-3 font-medium text-text-lock" }) })] }));
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { isString } from 'lodash-es';
|
|
4
4
|
import dynamic from 'next/dynamic';
|
|
5
5
|
import Image from 'next/image';
|
|
6
|
-
import { useState, } from 'react';
|
|
6
|
+
import { Suspense, useState, } from 'react';
|
|
7
7
|
import { twMerge } from 'tailwind-merge';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import { useAvailableQuestsQuery } from '../../client/hooks/useAvailableQuestsQuery.js';
|
|
@@ -62,10 +62,12 @@ export function Quests__client(props) {
|
|
|
62
62
|
const Quest = quest.stages?.length && featureFlag.enabled
|
|
63
63
|
? QUEST_COMPONENT_MAP['MULTI_WAGERING']
|
|
64
64
|
: QUEST_COMPONENT_MAP[quest.type];
|
|
65
|
-
return (_jsx(QuestContext, { value: quest, children: _jsx(Quest, { className: quest.type === 'WAGERING'
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
return (_jsx(QuestContext, { value: quest, children: _jsx(Suspense, { children: _jsx(Quest, { className: quest.type === 'WAGERING'
|
|
66
|
+
? classNames?.wageringQuest
|
|
67
|
+
: quest.type === 'DAILY_CHECKIN'
|
|
68
|
+
? classNames?.dailyCheckInQuest
|
|
69
|
+
: '', uncompletedIcon: props.uncompletedIcon, customComplete: quest.type === 'WAGERING'
|
|
70
|
+
? props.customComplete
|
|
71
|
+
: undefined, showTurnoverDecimals: props.showTurnoverDecimals }) }) }, quest.id));
|
|
70
72
|
})) : (_jsx(NoQuests, { heading: `${tab.toLowerCase()} Quests`, description: `You have no ${tab.toLowerCase()} quests yet.` })) })] }));
|
|
71
73
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type TopProgressBarPreset = 'Sunset' | 'Poppy' | 'Rosebud' | 'Sunshine' | 'Gold' | 'Twilight' | 'Powder' | 'Holly' | 'Northern Lights' | 'Raw Green' | 'Lime' | 'Nemesia' | 'Snowflake' | 'Blue Bird' | 'Blueprint' | 'Salvia' | 'Heartsease' | 'Amaranthus' | 'Candy' | 'Verbena';
|
|
2
|
+
export declare const TopProgressBar: ({ preset, customPreset, }: {
|
|
3
|
+
preset?: TopProgressBarPreset;
|
|
4
|
+
customPreset?: string;
|
|
5
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { usePathname, useSearchParams } from 'next/navigation';
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import { twMerge } from 'tailwind-merge';
|
|
6
|
+
// CSS gradient presets (inline styles), approximate Tailwind palette colors
|
|
7
|
+
// --> check presets here <3 https://www.creative-tim.com/twcomponents/gradient-generator
|
|
8
|
+
const PRESET_BG = {
|
|
9
|
+
Sunset: 'linear-gradient(90deg, #ef4444 0%, #f97316 100%)',
|
|
10
|
+
Poppy: 'linear-gradient(90deg, #fb7185 0%, #ef4444 100%)',
|
|
11
|
+
Rosebud: 'linear-gradient(90deg, #ec4899 0%, #f43f5e 100%)',
|
|
12
|
+
Sunshine: 'linear-gradient(90deg, #fde68a 0%, #facc15 100%)',
|
|
13
|
+
Gold: 'linear-gradient(90deg, #fde68a 0%, #eab308 100%)',
|
|
14
|
+
Twilight: 'linear-gradient(90deg, #f59e0b 0%, #ec4899 100%)',
|
|
15
|
+
Powder: 'linear-gradient(90deg, #ddd6fe 0%, #fbcfe8 100%)',
|
|
16
|
+
Holly: 'linear-gradient(90deg, #bfdbfe 0%, #a5f3fc 100%)',
|
|
17
|
+
'Northern Lights': 'linear-gradient(90deg, #99f6e4 0%, #14b8a6 100%)',
|
|
18
|
+
'Raw Green': 'linear-gradient(90deg, #a3e635 0%, #84cc16 100%)',
|
|
19
|
+
Lime: 'linear-gradient(90deg, #2dd4bf 0%, #fef08a 100%)',
|
|
20
|
+
Nemesia: 'linear-gradient(90deg, #34d399 0%, #22d3ee 100%)',
|
|
21
|
+
Snowflake: 'linear-gradient(90deg, #d946ef 0%, #22d3ee 100%)',
|
|
22
|
+
'Blue Bird': 'linear-gradient(90deg, #06b6d4 0%, #3b82f6 100%)',
|
|
23
|
+
Blueprint: 'linear-gradient(90deg, #6366f1 0%, #3b82f6 100%)',
|
|
24
|
+
Salvia: 'linear-gradient(90deg, #2563eb 0%, #7c3aed 100%)',
|
|
25
|
+
Heartsease: 'linear-gradient(90deg, #c026d3 0%, #db2777 100%)',
|
|
26
|
+
Amaranthus: 'linear-gradient(90deg, #c026d3 0%, #9333ea 100%)',
|
|
27
|
+
Candy: 'linear-gradient(90deg, #d946ef 0%, #ec4899 100%)',
|
|
28
|
+
Verbena: 'linear-gradient(90deg, #8b5cf6 0%, #a855f7 100%)',
|
|
29
|
+
};
|
|
30
|
+
export const TopProgressBar = ({ preset = 'Gold', customPreset, }) => {
|
|
31
|
+
const [progress, setProgress] = useState(0);
|
|
32
|
+
const [visible, setVisible] = useState(false);
|
|
33
|
+
const incrementTimerRef = useRef(null);
|
|
34
|
+
const hideTimerRef = useRef(null);
|
|
35
|
+
const pathname = usePathname();
|
|
36
|
+
const searchParams = useSearchParams();
|
|
37
|
+
const currentUrl = useMemo(() => {
|
|
38
|
+
const sp = searchParams?.toString();
|
|
39
|
+
return `${pathname ?? ''}${sp ? `?${sp}` : ''}`;
|
|
40
|
+
}, [pathname, searchParams]);
|
|
41
|
+
const clearTimers = useCallback(() => {
|
|
42
|
+
if (incrementTimerRef.current) {
|
|
43
|
+
clearInterval(incrementTimerRef.current);
|
|
44
|
+
incrementTimerRef.current = null;
|
|
45
|
+
}
|
|
46
|
+
if (hideTimerRef.current) {
|
|
47
|
+
clearTimeout(hideTimerRef.current);
|
|
48
|
+
hideTimerRef.current = null;
|
|
49
|
+
}
|
|
50
|
+
}, []);
|
|
51
|
+
const startProgress = useCallback(() => {
|
|
52
|
+
clearTimers();
|
|
53
|
+
setVisible(true);
|
|
54
|
+
setProgress(8);
|
|
55
|
+
incrementTimerRef.current = window.setInterval(() => {
|
|
56
|
+
setProgress((prev) => {
|
|
57
|
+
if (prev >= 90)
|
|
58
|
+
return prev;
|
|
59
|
+
const delta = Math.max(1, Math.min(10, 10 * Math.random()));
|
|
60
|
+
return Math.min(prev + delta, 90);
|
|
61
|
+
});
|
|
62
|
+
}, 250);
|
|
63
|
+
}, [clearTimers]);
|
|
64
|
+
const finishProgress = useCallback(() => {
|
|
65
|
+
setProgress(100);
|
|
66
|
+
if (incrementTimerRef.current) {
|
|
67
|
+
clearInterval(incrementTimerRef.current);
|
|
68
|
+
incrementTimerRef.current = null;
|
|
69
|
+
}
|
|
70
|
+
if (hideTimerRef.current) {
|
|
71
|
+
window.clearTimeout(hideTimerRef.current);
|
|
72
|
+
}
|
|
73
|
+
hideTimerRef.current = window.setTimeout(() => {
|
|
74
|
+
setVisible(false);
|
|
75
|
+
// Reset for next time after fade-out transition
|
|
76
|
+
hideTimerRef.current = window.setTimeout(() => setProgress(0), 200);
|
|
77
|
+
}, 150);
|
|
78
|
+
}, []);
|
|
79
|
+
// Trigger progress completion when URL changes
|
|
80
|
+
const previousUrl = useRef('');
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (previousUrl.current !== currentUrl) {
|
|
83
|
+
previousUrl.current = currentUrl;
|
|
84
|
+
finishProgress();
|
|
85
|
+
}
|
|
86
|
+
}, [currentUrl, finishProgress]);
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
const handleAnchorClick = (event) => {
|
|
89
|
+
const target = event.currentTarget;
|
|
90
|
+
if (!target)
|
|
91
|
+
return;
|
|
92
|
+
const href = target.getAttribute('href');
|
|
93
|
+
if (!href)
|
|
94
|
+
return;
|
|
95
|
+
const targetUrl = new URL(href, window.location.origin);
|
|
96
|
+
const current = new URL(window.location.href);
|
|
97
|
+
const isDifferent = targetUrl.origin !== current.origin ||
|
|
98
|
+
targetUrl.pathname !== current.pathname ||
|
|
99
|
+
targetUrl.search !== current.search;
|
|
100
|
+
if (isDifferent) {
|
|
101
|
+
startProgress();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const attachListeners = () => {
|
|
105
|
+
const anchors = document.querySelectorAll('a[href]');
|
|
106
|
+
anchors.forEach((a) => {
|
|
107
|
+
a.removeEventListener('click', handleAnchorClick);
|
|
108
|
+
a.addEventListener('click', handleAnchorClick);
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
const observer = new MutationObserver(() => attachListeners());
|
|
112
|
+
observer.observe(document, { childList: true, subtree: true });
|
|
113
|
+
attachListeners();
|
|
114
|
+
const originalPush = window.history.pushState;
|
|
115
|
+
const originalReplace = window.history.replaceState;
|
|
116
|
+
window.history.pushState = new Proxy(originalPush, {
|
|
117
|
+
apply: (target, thisArg, argArray) => {
|
|
118
|
+
const result = Reflect.apply(target, thisArg, argArray);
|
|
119
|
+
requestAnimationFrame(() => finishProgress());
|
|
120
|
+
return result;
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
window.history.replaceState = new Proxy(originalReplace, {
|
|
124
|
+
apply: (target, thisArg, argArray) => {
|
|
125
|
+
const result = Reflect.apply(target, thisArg, argArray);
|
|
126
|
+
requestAnimationFrame(() => finishProgress());
|
|
127
|
+
return result;
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
// Cleanup
|
|
131
|
+
return () => {
|
|
132
|
+
observer.disconnect();
|
|
133
|
+
const anchors = document.querySelectorAll('a[href]');
|
|
134
|
+
anchors.forEach((a) => a.removeEventListener('click', handleAnchorClick));
|
|
135
|
+
window.history.pushState = originalPush;
|
|
136
|
+
window.history.replaceState = originalReplace;
|
|
137
|
+
clearTimers();
|
|
138
|
+
};
|
|
139
|
+
}, [clearTimers, finishProgress, startProgress]);
|
|
140
|
+
return (_jsx("div", { "aria-hidden": true, className: "pointer-events-none fixed top-0 right-0 left-0 z-[9999]", children: _jsx("div", { className: twMerge('h-[3px] transition-[width,opacity] duration-200 ease-out', visible ? 'opacity-100' : 'opacity-0'), style: {
|
|
141
|
+
width: `${progress}%`,
|
|
142
|
+
background: customPreset ?? PRESET_BG[preset],
|
|
143
|
+
boxShadow: '0 0 10px rgba(255, 255, 255, 0.6)',
|
|
144
|
+
} }) }));
|
|
145
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TopProgressBar';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TopProgressBar.js';
|