@opexa/portal-components 0.0.460 → 0.0.462

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.
@@ -1 +1,26 @@
1
- export declare function Maya(): import("react/jsx-runtime").JSX.Element;
1
+ import { type ImageProps } from 'next/image';
2
+ export interface ClassNameEntries {
3
+ root?: string;
4
+ banner?: string;
5
+ logo?: string;
6
+ progressRoot?: string;
7
+ progressTrack?: string;
8
+ progressTrackCompleteBG?: string;
9
+ progressTrackIncompleteBG?: string;
10
+ progressTrackOuter?: string;
11
+ progressTrackInner?: string;
12
+ progressBar?: string;
13
+ progressLabel?: string;
14
+ licensedBy?: string;
15
+ pagcorLogo?: string;
16
+ responsibleGamingLogo?: string;
17
+ }
18
+ interface MayaProps {
19
+ logo: ImageProps['src'];
20
+ responsibleGamingLogo?: ImageProps['src'];
21
+ pagcorLogo?: ImageProps['src'];
22
+ banner?: ImageProps['src'];
23
+ className?: string | ClassNameEntries;
24
+ }
25
+ export declare function Maya({ logo, responsibleGamingLogo, pagcorLogo, banner, className, }: MayaProps): import("react/jsx-runtime").JSX.Element;
26
+ export {};
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import Image from 'next/image';
3
+ import { isString } from 'lodash-es';
4
+ import Image, {} from 'next/image';
4
5
  import { useRouter, useSearchParams } from 'next/navigation';
5
6
  import { useEffect, useState } from 'react';
6
7
  import { twMerge } from 'tailwind-merge';
@@ -9,8 +10,9 @@ import { useSessionQuery } from '../../client/hooks/useSessionQuery.js';
9
10
  import { useSignInMutation } from '../../client/hooks/useSignInMutation.js';
10
11
  import { signOut } from '../../client/services/signOut.js';
11
12
  import { toaster } from '../../client/utils/toaster.js';
12
- import pagcorLogo from '../../images/pagcor.png';
13
- export function Maya() {
13
+ import pagcorLogoDefault from '../../images/pagcor2.png';
14
+ import responsibleGamingLogoDefault from '../../images/responsible-gaming.png';
15
+ export function Maya({ logo, responsibleGamingLogo, pagcorLogo, banner, className, }) {
14
16
  const signIn = useSignInMutation();
15
17
  const router = useRouter();
16
18
  const searchParams = useSearchParams();
@@ -20,24 +22,22 @@ export function Maya() {
20
22
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
21
23
  const session = useSessionQuery();
22
24
  const mayaSessionId = searchParams.get('sessionId');
25
+ const classNames = isString(className)
26
+ ? { root: className }
27
+ : (className ?? {});
23
28
  const init = async () => {
24
29
  setProgress(25);
25
- /* Decide when a session already exists */
26
30
  if (session.data) {
27
- /* Is there a maya session id? */
28
31
  if (mayaSessionId) {
29
- /* Is the maya session id new? */
30
32
  if (localStorage.getItem(mayaSessIdKey) === mayaSessionId) {
31
33
  setProgress(100);
32
34
  return;
33
35
  }
34
36
  setProgress(50);
35
- /* Need to remove existing session and create a new one */
36
37
  await signOut();
37
38
  await sleep(1000);
38
39
  }
39
40
  else {
40
- /* No new maya session ID? use existing session */
41
41
  setProgress(100);
42
42
  return;
43
43
  }
@@ -47,10 +47,7 @@ export function Maya() {
47
47
  throw new Error('Maya session ID is required');
48
48
  }
49
49
  await new Promise((resolve, reject) => {
50
- signIn.mutate({
51
- type: 'MAYA',
52
- session: mayaSessionId,
53
- }, {
50
+ signIn.mutate({ type: 'MAYA', session: mayaSessionId }, {
54
51
  onSuccess: () => {
55
52
  setProgress(100);
56
53
  setComplete(true);
@@ -86,6 +83,5 @@ export function Maya() {
86
83
  };
87
84
  runInit();
88
85
  }, []);
89
- return (_jsx("div", { className: "relative flex min-h-screen flex-col border dark:bg-[#1F242F]", children: _jsxs("div", { className: "absolute bottom-0 w-full shrink-0 bg-[#F9FAFB] p-6 dark:bg-[#161B26]", children: [_jsxs("div", { className: "mt-4", children: [_jsx("div", { className: twMerge('overflow-hidden rounded-full p-px', !complete && 'bg-[#D0D5DD] dark:bg-[#333741]', complete &&
90
- 'bg-gradient-to-r from-[#FFDE21] via-[#FFD012] to-[#FFBF00]'), id: "progress:root", role: "progressbar", "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": progress, "aria-labelledby": "progress:label", children: _jsx("div", { className: "rounded-full bg-[#F2F4F7] p-px dark:bg-[#1F242F]", children: _jsx("div", { className: "relative h-4 w-full overflow-hidden rounded-full bg-white", children: _jsx("div", { className: twMerge('absolute left-0 h-full rounded-full bg-gradient-to-r from-[#FFDE21] via-[#FFBF00] to-[#FF9442] transition-all duration-150'), style: { width: `${progress}%` } }) }) }) }), _jsxs("span", { id: "progress:label", className: "mt-3 block text-center text-[#344054] text-sm dark:text-[#CECFD2]", children: [progress, "% Loading..."] })] }), _jsxs("div", { className: "mt-7 flex items-center justify-center gap-2", children: [_jsx("span", { className: "text-[#475467] text-xs dark:text-[#94969C]", children: "Officially Licensed by" }), _jsx(Image, { src: pagcorLogo, alt: "Pagcor Logo", height: 30, width: 30, className: "w-auto shrink-0" })] })] }) }));
86
+ return (_jsxs("div", { className: twMerge('relative flex min-h-screen flex-col', classNames.root), children: [banner && (_jsx(Image, { src: banner, alt: "brand logo", height: 50, width: 120, className: twMerge('w-full grow object-cover object-top', classNames.banner) })), _jsxs("div", { className: twMerge('absolute bottom-0 w-full shrink-0 bg-[#1F1F1F] p-6', classNames.progressRoot), children: [_jsxs("div", { children: [_jsx(Image, { src: logo, alt: "brand logo", height: 50, width: 120, className: twMerge('mx-auto mb-5 h-12.5 w-auto', classNames.logo) }), _jsx("div", { className: twMerge('overflow-hidden rounded-full p-px', !complete && (classNames.progressTrackIncompleteBG ?? 'bg-[#373A41]'), complete && (classNames.progressTrackCompleteBG ?? 'bg-[#ffb300]'), classNames.progressTrack), id: "progress:root", role: "progressbar", "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": progress, "aria-labelledby": "progress:label", children: _jsx("div", { className: twMerge('rounded-full bg-[#1F1F1F] p-px', classNames.progressTrackOuter), children: _jsx("div", { className: twMerge('relative h-4 w-full overflow-hidden rounded-full bg-[#22262e]', classNames.progressTrackInner), children: _jsx("div", { className: twMerge('absolute left-0 h-full rounded-full bg-[#ffb300] transition-all duration-150', classNames.progressBar), style: { width: `${progress}%` } }) }) }) }), _jsxs("span", { id: "progress:label", className: twMerge('mt-3 block text-center text-[#CECFD2] text-sm', classNames.progressLabel), children: [progress, "% Loading..."] })] }), _jsxs("div", { className: twMerge('mt-7 flex items-center justify-center gap-2 text-[#94969C] text-xs', classNames.licensedBy), children: [_jsx("span", { children: "Officially Licensed by" }), _jsx(Image, { src: pagcorLogo ?? pagcorLogoDefault, alt: "PAGCOR logo", height: 43, width: 88, className: twMerge('h-[43px] w-auto shrink-0', classNames.pagcorLogo), draggable: false, unoptimized: true }), _jsx(Image, { src: responsibleGamingLogo ?? responsibleGamingLogoDefault, alt: "Responsible Gaming logo", height: 50, width: 186, className: twMerge('h-[50px] w-auto shrink-0', classNames.responsibleGamingLogo), draggable: false, unoptimized: true })] })] })] }));
91
87
  }
@@ -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';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opexa/portal-components",
3
- "version": "0.0.460",
3
+ "version": "0.0.462",
4
4
  "exports": {
5
5
  "./ui/*": {
6
6
  "types": "./dist/ui/*/index.d.ts",