@glideidentity/web-client-sdk 5.0.1 → 5.1.1-beta.1
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/README.md +8 -108
- package/dist/adapters/angular/index.js +1 -0
- package/dist/adapters/angular/phone-auth.service.d.ts +18 -0
- package/dist/adapters/angular/phone-auth.service.js +26 -0
- package/dist/adapters/react/index.js +3 -0
- package/dist/adapters/react/useClient.js +1 -0
- package/dist/adapters/react/usePhoneAuth.js +16 -1
- package/dist/adapters/vanilla/client.js +1 -0
- package/dist/adapters/vanilla/index.js +1 -0
- package/dist/adapters/vanilla/phone-auth.js +31 -0
- package/dist/adapters/vue/index.js +4 -0
- package/dist/adapters/vue/useClient.js +5 -0
- package/dist/adapters/vue/usePhoneAuth.js +20 -1
- package/dist/browser/web-client-sdk.min.js +1 -2
- package/dist/browser.js +6 -0
- package/dist/core/client.js +12 -0
- package/dist/core/logger.js +81 -1
- package/dist/core/phone-auth/api-types.d.ts +1 -4
- package/dist/core/phone-auth/api-types.js +83 -0
- package/dist/core/phone-auth/client.js +374 -38
- package/dist/core/phone-auth/error-utils.js +83 -1
- package/dist/core/phone-auth/index.d.ts +1 -1
- package/dist/core/phone-auth/index.js +2 -2
- package/dist/core/phone-auth/status-types.d.ts +78 -0
- package/dist/core/phone-auth/status-types.js +17 -0
- package/dist/core/phone-auth/strategies/desktop.d.ts +2 -0
- package/dist/core/phone-auth/strategies/desktop.js +136 -13
- package/dist/core/phone-auth/strategies/index.d.ts +4 -0
- package/dist/core/phone-auth/strategies/index.js +4 -0
- package/dist/core/phone-auth/strategies/link.d.ts +2 -0
- package/dist/core/phone-auth/strategies/link.js +97 -13
- package/dist/core/phone-auth/strategies/ts43.d.ts +19 -0
- package/dist/core/phone-auth/strategies/ts43.js +33 -2
- package/dist/core/phone-auth/strategies/types.js +4 -0
- package/dist/core/phone-auth/type-guards.js +131 -0
- package/dist/core/phone-auth/types.d.ts +5 -0
- package/dist/core/phone-auth/types.js +32 -0
- package/dist/core/phone-auth/ui/mobile-debug-console.js +28 -2
- package/dist/core/phone-auth/ui/modal.d.ts +55 -33
- package/dist/core/phone-auth/ui/modal.js +422 -889
- package/dist/core/phone-auth/validation-utils.d.ts +0 -9
- package/dist/core/phone-auth/validation-utils.js +34 -25
- package/dist/core/version.js +2 -1
- package/dist/esm/adapters/angular/index.js +1 -0
- package/dist/esm/adapters/angular/phone-auth.service.d.ts +18 -0
- package/dist/esm/adapters/angular/phone-auth.service.js +26 -0
- package/dist/esm/adapters/react/index.js +3 -0
- package/dist/esm/adapters/react/useClient.js +1 -0
- package/dist/esm/adapters/react/usePhoneAuth.js +16 -1
- package/dist/esm/adapters/vanilla/client.js +1 -0
- package/dist/esm/adapters/vanilla/index.js +1 -0
- package/dist/esm/adapters/vanilla/phone-auth.d.ts +24 -0
- package/dist/esm/adapters/vanilla/phone-auth.js +31 -0
- package/dist/esm/adapters/vue/index.js +4 -0
- package/dist/esm/adapters/vue/useClient.js +5 -0
- package/dist/esm/adapters/vue/usePhoneAuth.js +20 -1
- package/dist/esm/browser.js +6 -0
- package/dist/esm/core/client.d.ts +10 -0
- package/dist/esm/core/client.js +12 -0
- package/dist/esm/core/logger.d.ts +53 -0
- package/dist/esm/core/logger.js +81 -1
- package/dist/esm/core/phone-auth/api-types.d.ts +313 -1
- package/dist/esm/core/phone-auth/api-types.js +83 -0
- package/dist/esm/core/phone-auth/client.d.ts +144 -0
- package/dist/esm/core/phone-auth/client.js +375 -39
- package/dist/esm/core/phone-auth/error-utils.d.ts +29 -0
- package/dist/esm/core/phone-auth/error-utils.js +83 -1
- package/dist/esm/core/phone-auth/index.d.ts +1 -1
- package/dist/esm/core/phone-auth/index.js +4 -2
- package/dist/esm/core/phone-auth/status-types.d.ts +78 -0
- package/dist/esm/core/phone-auth/status-types.js +17 -0
- package/dist/esm/core/phone-auth/strategies/desktop.d.ts +65 -0
- package/dist/esm/core/phone-auth/strategies/desktop.js +136 -13
- package/dist/esm/core/phone-auth/strategies/index.d.ts +4 -0
- package/dist/esm/core/phone-auth/strategies/index.js +4 -0
- package/dist/esm/core/phone-auth/strategies/link.d.ts +50 -0
- package/dist/esm/core/phone-auth/strategies/link.js +97 -13
- package/dist/esm/core/phone-auth/strategies/ts43.d.ts +19 -0
- package/dist/esm/core/phone-auth/strategies/ts43.js +33 -2
- package/dist/esm/core/phone-auth/strategies/types.d.ts +13 -0
- package/dist/esm/core/phone-auth/strategies/types.js +4 -0
- package/dist/esm/core/phone-auth/type-guards.d.ts +128 -0
- package/dist/esm/core/phone-auth/type-guards.js +131 -0
- package/dist/esm/core/phone-auth/types.d.ts +113 -0
- package/dist/esm/core/phone-auth/types.js +32 -0
- package/dist/esm/core/phone-auth/ui/mobile-debug-console.d.ts +4 -0
- package/dist/esm/core/phone-auth/ui/mobile-debug-console.js +28 -2
- package/dist/esm/core/phone-auth/ui/modal.d.ts +68 -27
- package/dist/esm/core/phone-auth/ui/modal.js +422 -889
- package/dist/esm/core/phone-auth/validation-utils.d.ts +26 -4
- package/dist/esm/core/phone-auth/validation-utils.js +34 -24
- package/dist/esm/core/types.d.ts +35 -0
- package/dist/esm/core/version.js +2 -1
- package/dist/esm/index.js +9 -1
- package/dist/index.js +7 -0
- package/package.json +1 -1
- package/dist/browser/web-client-sdk.min.js.LICENSE.txt +0 -1
|
@@ -1,37 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal UI Component for Phone Authentication
|
|
3
|
+
*
|
|
4
|
+
* This file creates the UI components (modals, buttons) that are shown
|
|
5
|
+
* when the SDK is NOT in headless mode. Think of it like a popup window
|
|
6
|
+
* that handles the authentication flow for you.
|
|
7
|
+
*/
|
|
8
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
9
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
10
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
11
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
12
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
13
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
14
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Creates and manages a modal dialog for authentication
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const modal = new AuthModal({
|
|
22
|
+
* title: "Verify Your Phone",
|
|
23
|
+
* description: "Complete authentication to continue"
|
|
24
|
+
* });
|
|
25
|
+
* modal.show();
|
|
26
|
+
*/
|
|
1
27
|
export class AuthModal {
|
|
2
28
|
constructor(options, callbacks) {
|
|
3
29
|
this.container = null;
|
|
4
30
|
this.backdrop = null;
|
|
5
31
|
this.isOpen = false;
|
|
6
|
-
this.currentStep = 'os-choice';
|
|
7
|
-
this.qrCodeData = null;
|
|
8
|
-
this.statusMessage = '';
|
|
9
|
-
this.originalBodyOverflow = '';
|
|
10
|
-
this.isClosing = false;
|
|
11
|
-
this.iconApple = `<svg class="glide-icon glide-icon-os" viewBox="0 0 384 512" fill="currentColor"><path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 52.3-11.4 69.5-34.3z"/></svg>`;
|
|
12
|
-
this.iconAndroid = `<svg class="glide-icon glide-icon-os" viewBox="0 0 576 512" fill="currentColor"><path d="M420.55,301.93a24,24,0,1,1,24-24,24,24,0,0,1-24,24m-265.1,0a24,24,0,1,1,24-24,24,24,0,0,1-24,24m273.7-144.48,47.94-83a10,10,0,1,0-17.27-10h0l-48.54,84.07a301.25,301.25,0,0,0-246.56,0L116.18,64.45a10,10,0,1,0-17.27,10h0l47.94,83C64.53,202.22,8.24,285.55,0,384H576c-8.24-98.45-64.54-181.78-146.85-226.55"/></svg>`;
|
|
13
|
-
this.iconBack = `<svg class="glide-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>`;
|
|
14
32
|
this.options = options || {};
|
|
15
33
|
this.callbacks = callbacks || {};
|
|
16
|
-
|
|
34
|
+
// Bind escape key handler
|
|
17
35
|
this.handleEscapeKey = this.handleEscapeKey.bind(this);
|
|
18
36
|
}
|
|
19
|
-
shouldUseDarkMode() {
|
|
20
|
-
if (this.theme === 'dark')
|
|
21
|
-
return true;
|
|
22
|
-
if (this.theme === 'light')
|
|
23
|
-
return false;
|
|
24
|
-
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
25
|
-
}
|
|
26
37
|
handleEscapeKey(event) {
|
|
27
38
|
var _a, _b;
|
|
28
39
|
if (event.key === 'Escape' && this.isOpen) {
|
|
40
|
+
// Check if escape closing is enabled (default true)
|
|
29
41
|
if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.closeOnEscape) !== false) {
|
|
30
|
-
(_b = this.closeCallback) === null || _b === void 0 ? void 0 : _b.call(this);
|
|
42
|
+
(_b = this.closeCallback) === null || _b === void 0 ? void 0 : _b.call(this); // Call the cancellation callback if set
|
|
31
43
|
this.close();
|
|
32
44
|
}
|
|
33
45
|
}
|
|
34
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Escape HTML to prevent XSS attacks
|
|
49
|
+
*/
|
|
35
50
|
escapeHtml(unsafe) {
|
|
36
51
|
return unsafe
|
|
37
52
|
.replace(/&/g, "&")
|
|
@@ -40,1022 +55,540 @@ export class AuthModal {
|
|
|
40
55
|
.replace(/"/g, """)
|
|
41
56
|
.replace(/'/g, "'");
|
|
42
57
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Shows the modal with a QR code for desktop authentication
|
|
60
|
+
* Supports both single QR code (legacy) and dual-platform QR codes (iOS + Android)
|
|
61
|
+
*/
|
|
62
|
+
showQRCode(qrCodeData, statusMessage = 'Scan QR code with your phone') {
|
|
63
|
+
console.log('[Modal] showQRCode called with:', qrCodeData);
|
|
64
|
+
// If modal is already open, don't recreate it
|
|
65
|
+
if (this.isOpen) {
|
|
66
|
+
console.log('[Modal] Modal already open, skipping recreation');
|
|
67
|
+
return;
|
|
49
68
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
break;
|
|
69
|
+
// Check if it's the new dual-platform format with VALID Android QR code
|
|
70
|
+
if (typeof qrCodeData === 'object' && qrCodeData.iosQRCode) {
|
|
71
|
+
const hasValidAndroidQR = qrCodeData.androidQRCode &&
|
|
72
|
+
qrCodeData.androidQRCode.length > 0 &&
|
|
73
|
+
qrCodeData.androidQRCode !== qrCodeData.iosQRCode;
|
|
74
|
+
console.log('[Modal] Has valid Android QR?', hasValidAndroidQR);
|
|
75
|
+
if (hasValidAndroidQR) {
|
|
76
|
+
console.log('[Modal] Using dual-platform modal');
|
|
77
|
+
this.createDualPlatformQRModal(qrCodeData, statusMessage);
|
|
78
|
+
// Note: createDualPlatformQRModal calls show() internally
|
|
79
|
+
return;
|
|
62
80
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (isError) {
|
|
73
|
-
el.style.color = '#ff3b30';
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
el.style.color = '';
|
|
77
|
-
}
|
|
81
|
+
else {
|
|
82
|
+
console.log('[Modal] Android QR missing/empty, using single iOS QR');
|
|
83
|
+
// Only iOS QR code available - show single QR
|
|
84
|
+
this.createModal(`
|
|
85
|
+
<div class="glide-auth-qr-container">
|
|
86
|
+
<img src="${this.escapeHtml(qrCodeData.iosQRCode)}" alt="QR Code" class="glide-auth-qr-code" />
|
|
87
|
+
<p class="glide-auth-status">Scan with your iPhone to authenticate</p>
|
|
88
|
+
</div>
|
|
89
|
+
`);
|
|
78
90
|
}
|
|
79
91
|
}
|
|
80
|
-
}
|
|
81
|
-
setCloseCallback(callback) {
|
|
82
|
-
this.closeCallback = callback;
|
|
83
|
-
}
|
|
84
|
-
renderToggleMode(data, msg) {
|
|
85
|
-
let iosQR = '';
|
|
86
|
-
let androidQR = '';
|
|
87
|
-
if (typeof data === 'string') {
|
|
88
|
-
iosQR = data;
|
|
89
|
-
androidQR = data;
|
|
90
|
-
}
|
|
91
92
|
else {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
<div class="glide-toggle-slider"></div>
|
|
99
|
-
<button class="glide-btn glide-toggle-btn active" data-platform="ios">${this.iconApple}<span>iOS</span></button>
|
|
100
|
-
<button class="glide-btn glide-toggle-btn" data-platform="android">${this.iconAndroid}<span>Android</span></button>
|
|
93
|
+
console.log('[Modal] Using legacy single QR code modal');
|
|
94
|
+
// Legacy single QR code
|
|
95
|
+
this.createModal(`
|
|
96
|
+
<div class="glide-auth-qr-container">
|
|
97
|
+
<img src="${this.escapeHtml(qrCodeData)}" alt="QR Code" class="glide-auth-qr-code" />
|
|
98
|
+
<p class="glide-auth-status">${this.escapeHtml(statusMessage)}</p>
|
|
101
99
|
</div>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
class="glide-qr-img"
|
|
106
|
-
id="glide-qr-img"
|
|
107
|
-
src="${this.escapeHtml(iosQR)}"
|
|
108
|
-
alt="QR Code"
|
|
109
|
-
class="glide-qr-img"
|
|
110
|
-
data-ios="${this.escapeHtml(iosQR)}"
|
|
111
|
-
data-android="${this.escapeHtml(androidQR)}"
|
|
112
|
-
/>
|
|
113
|
-
|
|
114
|
-
<!-- Animation Overlay (Inside QR Area for centering) -->
|
|
115
|
-
<div id="glide-phone-overlay" class="glide-phone-animation-overlay">
|
|
116
|
-
<div class="glide-outlined-phone">
|
|
117
|
-
<div class="glide-scan-line"></div>
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
`;
|
|
123
|
-
this.createModal(content, '', true);
|
|
124
|
-
this.setupPlatformToggles();
|
|
125
|
-
this.setupHelpInteraction();
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
102
|
+
this.show();
|
|
126
103
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<div class="glide-scan-line"></div>
|
|
132
|
-
</div>
|
|
133
|
-
</div>
|
|
134
|
-
`;
|
|
104
|
+
/**
|
|
105
|
+
* Creates a modal with iOS/Android platform toggle
|
|
106
|
+
*/
|
|
107
|
+
createDualPlatformQRModal(qrCodeData, statusMessage) {
|
|
135
108
|
this.createModal(`
|
|
136
|
-
<div class="glide-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
<div class="glide-separator-line"></div>
|
|
152
|
-
</div>
|
|
153
|
-
<div class="glide-dual-item">
|
|
154
|
-
<div class="glide-os-logo">
|
|
155
|
-
${this.iconAndroid}
|
|
156
|
-
<span>Android</span>
|
|
157
|
-
</div>
|
|
158
|
-
<div class="glide-qr-area">
|
|
159
|
-
<img src="${this.escapeHtml(data.androidQRCode || data.iosQRCode)}" alt="Android QR" class="glide-qr-img" />
|
|
160
|
-
${qrOverlayHtml}
|
|
161
|
-
</div>
|
|
162
|
-
</div>
|
|
109
|
+
<div class="glide-auth-qr-container">
|
|
110
|
+
<!-- Platform Switcher -->
|
|
111
|
+
<div class="glide-platform-switcher">
|
|
112
|
+
<button class="glide-platform-btn glide-platform-ios active" data-platform="ios">
|
|
113
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
|
114
|
+
<path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
|
|
115
|
+
</svg>
|
|
116
|
+
<span>iOS</span>
|
|
117
|
+
</button>
|
|
118
|
+
<button class="glide-platform-btn glide-platform-android" data-platform="android">
|
|
119
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
|
120
|
+
<path d="M17.6 9.48l1.84-3.18c.16-.31.04-.69-.26-.85-.29-.15-.65-.06-.83.22l-1.88 3.24c-2.86-1.21-6.08-1.21-8.94 0L5.65 5.67c-.19-.28-.54-.38-.83-.22-.3.16-.42.54-.26.85l1.84 3.18C4.08 11.08 2.4 13.97 2.4 17.2h19.2c0-3.23-1.68-6.12-4.0-7.72zM7.0 14.8c-.66 0-1.2-.54-1.2-1.2s.54-1.2 1.2-1.2 1.2.54 1.2 1.2-.54 1.2-1.2 1.2zm10 0c-.66 0-1.2-.54-1.2-1.2s.54-1.2 1.2-1.2 1.2.54 1.2 1.2-.54 1.2-1.2 1.2z"/>
|
|
121
|
+
</svg>
|
|
122
|
+
<span>Android</span>
|
|
123
|
+
</button>
|
|
163
124
|
</div>
|
|
125
|
+
|
|
126
|
+
<!-- QR Code Image -->
|
|
127
|
+
<img
|
|
128
|
+
id="glide-qr-code-img"
|
|
129
|
+
src="${this.escapeHtml(qrCodeData.iosQRCode)}"
|
|
130
|
+
alt="QR Code"
|
|
131
|
+
class="glide-auth-qr-code"
|
|
132
|
+
data-ios="${this.escapeHtml(qrCodeData.iosQRCode)}"
|
|
133
|
+
data-android="${this.escapeHtml(qrCodeData.androidQRCode || '')}"
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
<!-- Status Message -->
|
|
137
|
+
<p class="glide-auth-status" id="glide-platform-message">Scan with your iPhone to authenticate</p>
|
|
164
138
|
</div>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
renderPreStepMode(data, msg) {
|
|
169
|
-
this.currentStep = 'os-choice';
|
|
170
|
-
const contentHtml = `
|
|
171
|
-
<div class="glide-pre-step-container">
|
|
172
|
-
<button class="glide-os-choice-btn" id="glide-btn-ios">
|
|
173
|
-
${this.iconApple}
|
|
174
|
-
<span>iOS</span>
|
|
175
|
-
</button>
|
|
176
|
-
<button class="glide-os-choice-btn" id="glide-btn-android">
|
|
177
|
-
${this.iconAndroid}
|
|
178
|
-
<span>Android</span>
|
|
179
|
-
</button>
|
|
180
|
-
</div>
|
|
181
|
-
`;
|
|
182
|
-
this.createModal(contentHtml, '', false);
|
|
183
|
-
this.setupPreStepListeners();
|
|
139
|
+
`);
|
|
140
|
+
// IMPORTANT: Call show() to actually display the modal!
|
|
184
141
|
this.show();
|
|
185
142
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
this.currentStep = 'ios-qr';
|
|
192
|
-
this.updatePreStepUI();
|
|
193
|
-
});
|
|
194
|
-
(_b = this.container.querySelector('#glide-btn-android')) === null || _b === void 0 ? void 0 : _b.addEventListener('click', () => {
|
|
195
|
-
this.currentStep = 'android-qr';
|
|
196
|
-
this.updatePreStepUI();
|
|
197
|
-
});
|
|
143
|
+
/**
|
|
144
|
+
* Sets a callback to be called when the modal is cancelled/closed
|
|
145
|
+
*/
|
|
146
|
+
setCloseCallback(callback) {
|
|
147
|
+
this.closeCallback = callback;
|
|
198
148
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Shows the modal with a button for Link authentication (App Clips)
|
|
151
|
+
* IMPORTANT: The button click is required for iOS to recognize the app link
|
|
152
|
+
*/
|
|
153
|
+
showLinkButton(url, buttonText) {
|
|
154
|
+
return new Promise((resolve) => {
|
|
155
|
+
var _a, _b, _c;
|
|
156
|
+
const text = buttonText || ((_a = this.options) === null || _a === void 0 ? void 0 : _a.buttonText) || 'Open Verification App';
|
|
157
|
+
this.createModal(`
|
|
158
|
+
<div class="glide-auth-link-container">
|
|
159
|
+
<p class="glide-auth-description">
|
|
160
|
+
${((_b = this.options) === null || _b === void 0 ? void 0 : _b.description) || 'Click below to verify your phone number'}
|
|
161
|
+
</p>
|
|
162
|
+
<button class="glide-auth-button" id="glide-auth-link-button">
|
|
163
|
+
${text}
|
|
214
164
|
</button>
|
|
165
|
+
<p class="glide-auth-helper-text">
|
|
166
|
+
You'll be redirected to complete verification
|
|
167
|
+
</p>
|
|
215
168
|
</div>
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
this.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
</div>
|
|
237
|
-
`;
|
|
238
|
-
this.createModal(contentHtml, '', true, true);
|
|
239
|
-
this.setupBackButton();
|
|
240
|
-
this.setupHelpInteraction();
|
|
241
|
-
}
|
|
242
|
-
else if (this.currentStep === 'android-qr') {
|
|
243
|
-
const qr = (typeof this.qrCodeData === 'object' && ((_b = this.qrCodeData) === null || _b === void 0 ? void 0 : _b.androidQRCode))
|
|
244
|
-
? this.qrCodeData.androidQRCode
|
|
245
|
-
: (typeof this.qrCodeData === 'object' && ((_c = this.qrCodeData) === null || _c === void 0 ? void 0 : _c.iosQRCode))
|
|
246
|
-
? this.qrCodeData.iosQRCode
|
|
247
|
-
: this.qrCodeData;
|
|
248
|
-
contentHtml = `
|
|
249
|
-
<div class="glide-content">
|
|
250
|
-
<div class="glide-qr-area">
|
|
251
|
-
<img src="${this.escapeHtml(qr)}" alt="QR Code" class="glide-qr-img" />
|
|
252
|
-
|
|
253
|
-
<!-- Animation Overlay -->
|
|
254
|
-
<div id="glide-phone-overlay" class="glide-phone-animation-overlay">
|
|
255
|
-
<div class="glide-outlined-phone">
|
|
256
|
-
<div class="glide-scan-line"></div>
|
|
257
|
-
</div>
|
|
258
|
-
</div>
|
|
259
|
-
</div>
|
|
260
|
-
</div>
|
|
261
|
-
`;
|
|
262
|
-
this.createModal(contentHtml, '', true, true);
|
|
263
|
-
this.setupBackButton();
|
|
264
|
-
this.setupHelpInteraction();
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
setupBackButton() {
|
|
268
|
-
var _a;
|
|
269
|
-
if (!this.container)
|
|
270
|
-
return;
|
|
271
|
-
(_a = this.container.querySelector('#glide-back-btn')) === null || _a === void 0 ? void 0 : _a.addEventListener('click', () => {
|
|
272
|
-
this.currentStep = 'os-choice';
|
|
273
|
-
this.updatePreStepUI();
|
|
169
|
+
`);
|
|
170
|
+
// Show modal first so elements are in the DOM
|
|
171
|
+
this.show();
|
|
172
|
+
// Add click handler for the button AFTER modal is in DOM
|
|
173
|
+
// CRITICAL: This click event is required for iOS to recognize the app link
|
|
174
|
+
// Do NOT call window.open automatically - it must be triggered by user interaction
|
|
175
|
+
const button = (_c = this.container) === null || _c === void 0 ? void 0 : _c.querySelector('#glide-auth-link-button');
|
|
176
|
+
if (button) {
|
|
177
|
+
button.addEventListener('click', (event) => {
|
|
178
|
+
var _a, _b;
|
|
179
|
+
(_b = (_a = this.callbacks) === null || _a === void 0 ? void 0 : _a.onAuthStart) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
180
|
+
// User-initiated click is required for app links to work properly on iOS
|
|
181
|
+
// This ensures the OS recognizes it should open an app, not just a browser tab
|
|
182
|
+
window.open(url, '_blank');
|
|
183
|
+
resolve();
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
console.error('[Modal] Link button not found in modal');
|
|
188
|
+
}
|
|
274
189
|
});
|
|
275
190
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
this.
|
|
284
|
-
this.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Shows the modal with a button for TS43 authentication
|
|
193
|
+
* IMPORTANT: The button click is required for Digital Credentials API (transient activation)
|
|
194
|
+
*/
|
|
195
|
+
showTS43Button(onAuthenticate) {
|
|
196
|
+
return new Promise((resolve, reject) => {
|
|
197
|
+
var _a, _b, _c;
|
|
198
|
+
const text = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.buttonText) || 'Verify with Carrier';
|
|
199
|
+
this.createModal(`
|
|
200
|
+
<div class="glide-auth-ts43-container">
|
|
201
|
+
<p class="glide-auth-description">
|
|
202
|
+
${((_b = this.options) === null || _b === void 0 ? void 0 : _b.description) || 'Verify using your carrier credentials'}
|
|
203
|
+
</p>
|
|
204
|
+
<button class="glide-auth-button" id="glide-auth-ts43-button">
|
|
205
|
+
${text}
|
|
206
|
+
</button>
|
|
207
|
+
<p class="glide-auth-helper-text">
|
|
208
|
+
Secure verification through your mobile carrier
|
|
209
|
+
</p>
|
|
210
|
+
</div>
|
|
211
|
+
`);
|
|
212
|
+
// Show modal first so elements are in the DOM
|
|
213
|
+
this.show();
|
|
214
|
+
// Add click handler for the button AFTER modal is in DOM
|
|
215
|
+
// CRITICAL: Digital Credentials API requires "transient activation" (user interaction)
|
|
216
|
+
// Do NOT call onAuthenticate automatically - it must be triggered by user click
|
|
217
|
+
const button = (_c = this.container) === null || _c === void 0 ? void 0 : _c.querySelector('#glide-auth-ts43-button');
|
|
218
|
+
if (button) {
|
|
219
|
+
button.addEventListener('click', (event) => __awaiter(this, void 0, void 0, function* () {
|
|
220
|
+
var _a, _b, _c, _d, _e, _f;
|
|
221
|
+
(_b = (_a = this.callbacks) === null || _a === void 0 ? void 0 : _a.onAuthStart) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
222
|
+
button.setAttribute('disabled', 'true');
|
|
223
|
+
button.textContent = 'Verifying...';
|
|
224
|
+
try {
|
|
225
|
+
const result = yield onAuthenticate();
|
|
226
|
+
(_d = (_c = this.callbacks) === null || _c === void 0 ? void 0 : _c.onAuthComplete) === null || _d === void 0 ? void 0 : _d.call(_c, result);
|
|
227
|
+
resolve(result);
|
|
228
|
+
this.close();
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
(_f = (_e = this.callbacks) === null || _e === void 0 ? void 0 : _e.onError) === null || _f === void 0 ? void 0 : _f.call(_e, error);
|
|
232
|
+
button.removeAttribute('disabled');
|
|
233
|
+
button.textContent = text;
|
|
234
|
+
reject(error);
|
|
235
|
+
}
|
|
236
|
+
}));
|
|
295
237
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
this.container.className = `glide-modal ${((_b = this.options) === null || _b === void 0 ? void 0 : _b.className) || ''} ${extraClass}`;
|
|
299
|
-
if (this.shouldUseDarkMode()) {
|
|
300
|
-
this.container.classList.add('dark');
|
|
238
|
+
else {
|
|
239
|
+
console.error('[Modal] TS43 button not found in modal');
|
|
301
240
|
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Updates the status message in the modal
|
|
245
|
+
*/
|
|
246
|
+
updateStatus(message, isError = false) {
|
|
247
|
+
var _a;
|
|
248
|
+
const statusEl = (_a = this.container) === null || _a === void 0 ? void 0 : _a.querySelector('.glide-auth-status');
|
|
249
|
+
if (statusEl) {
|
|
250
|
+
statusEl.textContent = message;
|
|
251
|
+
statusEl.className = isError ? 'glide-auth-status glide-auth-error' : 'glide-auth-status';
|
|
302
252
|
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Creates the modal HTML structure
|
|
256
|
+
*/
|
|
257
|
+
createModal(content) {
|
|
258
|
+
var _a, _b, _c;
|
|
259
|
+
// Clean up any existing modal
|
|
260
|
+
this.cleanup();
|
|
261
|
+
// Create backdrop (dark overlay)
|
|
262
|
+
this.backdrop = document.createElement('div');
|
|
263
|
+
this.backdrop.className = 'glide-auth-backdrop';
|
|
264
|
+
this.backdrop.style.cssText = `
|
|
265
|
+
position: fixed;
|
|
266
|
+
top: 0;
|
|
267
|
+
left: 0;
|
|
268
|
+
right: 0;
|
|
269
|
+
bottom: 0;
|
|
270
|
+
background: rgba(0, 0, 0, 0.5);
|
|
271
|
+
z-index: 9998;
|
|
272
|
+
opacity: 0;
|
|
273
|
+
transition: opacity 0.3s ease;
|
|
274
|
+
`;
|
|
275
|
+
// Create modal container
|
|
276
|
+
this.container = document.createElement('div');
|
|
277
|
+
this.container.className = `glide-auth-modal ${((_a = this.options) === null || _a === void 0 ? void 0 : _a.className) || ''}`;
|
|
278
|
+
this.container.style.cssText = `
|
|
279
|
+
position: fixed;
|
|
280
|
+
top: 50%;
|
|
281
|
+
left: 50%;
|
|
282
|
+
transform: translate(-50%, -50%) scale(0.9);
|
|
283
|
+
background: white;
|
|
284
|
+
border-radius: 12px;
|
|
285
|
+
padding: 24px;
|
|
286
|
+
max-width: 400px;
|
|
287
|
+
width: 90%;
|
|
288
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
289
|
+
z-index: 9999;
|
|
290
|
+
opacity: 0;
|
|
291
|
+
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
292
|
+
`;
|
|
293
|
+
// Add modal content
|
|
303
294
|
this.container.innerHTML = `
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
<div id="glide-modal-content">${content}</div>
|
|
319
|
-
<p class="glide-status" id="glide-status">${((_e = this.options) === null || _e === void 0 ? void 0 : _e.description) || ''}</p>
|
|
320
|
-
${includeHelpButton ? `
|
|
321
|
-
<button class="glide-btn glide-btn-help" id="glide-help-btn">?</button>
|
|
322
|
-
` : ''}
|
|
295
|
+
<div class="glide-auth-header">
|
|
296
|
+
${((_b = this.options) === null || _b === void 0 ? void 0 : _b.showCloseButton) !== false ? `
|
|
297
|
+
<button class="glide-auth-close" aria-label="Close">
|
|
298
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
299
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
300
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
301
|
+
</svg>
|
|
302
|
+
</button>
|
|
303
|
+
` : ''}
|
|
304
|
+
<h2 class="glide-auth-title">${((_c = this.options) === null || _c === void 0 ? void 0 : _c.title) || 'Phone Verification'}</h2>
|
|
305
|
+
</div>
|
|
306
|
+
<div class="glide-auth-content">
|
|
307
|
+
${content}
|
|
308
|
+
</div>
|
|
323
309
|
`;
|
|
310
|
+
// Add styles
|
|
324
311
|
this.injectStyles();
|
|
325
|
-
|
|
312
|
+
// Add close button handler
|
|
313
|
+
const closeBtn = this.container.querySelector('.glide-auth-close');
|
|
326
314
|
if (closeBtn) {
|
|
327
315
|
closeBtn.addEventListener('click', () => {
|
|
328
316
|
var _a;
|
|
329
|
-
(_a = this.closeCallback) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
317
|
+
(_a = this.closeCallback) === null || _a === void 0 ? void 0 : _a.call(this); // Call cancellation callback if set
|
|
330
318
|
this.close();
|
|
331
319
|
});
|
|
332
320
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
setupHelpInteraction() {
|
|
344
|
-
var _a;
|
|
345
|
-
const trigger = (_a = this.container) === null || _a === void 0 ? void 0 : _a.querySelector('#glide-help-btn');
|
|
346
|
-
if (trigger) {
|
|
347
|
-
trigger.addEventListener('click', () => {
|
|
348
|
-
var _a;
|
|
349
|
-
const overlays = (_a = this.container) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.glide-phone-animation-overlay');
|
|
350
|
-
if (overlays) {
|
|
351
|
-
overlays.forEach(overlay => {
|
|
352
|
-
overlay.classList.remove('playing');
|
|
353
|
-
void overlay.offsetWidth;
|
|
354
|
-
overlay.classList.add('playing');
|
|
355
|
-
setTimeout(() => {
|
|
356
|
-
overlay.classList.remove('playing');
|
|
357
|
-
}, 3000);
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
}
|
|
321
|
+
// Add backdrop click handler
|
|
322
|
+
this.backdrop.addEventListener('click', (e) => {
|
|
323
|
+
var _a, _b;
|
|
324
|
+
// Only close if backdrop itself was clicked and closeOnBackdrop is enabled (default true)
|
|
325
|
+
if (e.target === this.backdrop && ((_a = this.options) === null || _a === void 0 ? void 0 : _a.closeOnBackdrop) !== false) {
|
|
326
|
+
(_b = this.closeCallback) === null || _b === void 0 ? void 0 : _b.call(this); // Call cancellation callback if set
|
|
327
|
+
this.close();
|
|
328
|
+
}
|
|
329
|
+
});
|
|
362
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* Injects CSS styles for the modal
|
|
333
|
+
*/
|
|
363
334
|
injectStyles() {
|
|
364
|
-
if (document.getElementById('glide-
|
|
335
|
+
if (document.getElementById('glide-auth-styles'))
|
|
365
336
|
return;
|
|
366
337
|
const styles = document.createElement('style');
|
|
367
|
-
styles.id = 'glide-
|
|
338
|
+
styles.id = 'glide-auth-styles';
|
|
368
339
|
styles.textContent = `
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
--glide-text: #1d1d1f;
|
|
372
|
-
--glide-bg-light: rgba(255, 255, 255, 0.6);
|
|
373
|
-
--glide-bg-dark: rgba(30, 30, 30, 0.6);
|
|
374
|
-
--glide-phone-border: #000000; /* High Contrast Black */
|
|
375
|
-
|
|
376
|
-
/* Button sizes */
|
|
377
|
-
--glide-btn-size: 28px;
|
|
378
|
-
--glide-help-btn-size: 24px;
|
|
379
|
-
--glide-toggle-icon-size: 14px;
|
|
380
|
-
--glide-dual-icon-size: 18px;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
#glide-backdrop {
|
|
384
|
-
position: fixed;
|
|
385
|
-
top: 0;
|
|
386
|
-
left: 0;
|
|
387
|
-
right: 0;
|
|
388
|
-
bottom: 0;
|
|
389
|
-
background: rgba(0, 0, 0, 0.2);
|
|
390
|
-
backdrop-filter: blur(8px);
|
|
391
|
-
-webkit-backdrop-filter: blur(8px);
|
|
392
|
-
z-index: 9998;
|
|
393
|
-
opacity: 0;
|
|
394
|
-
transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
|
340
|
+
.glide-auth-backdrop {
|
|
341
|
+
backdrop-filter: blur(4px);
|
|
395
342
|
}
|
|
396
343
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
top: 20px;
|
|
400
|
-
left: 50%;
|
|
401
|
-
transform: translateX(-50%) scale(0.94);
|
|
402
|
-
background: var(--glide-bg-light);
|
|
403
|
-
backdrop-filter: blur(24px) saturate(180%);
|
|
404
|
-
-webkit-backdrop-filter: blur(24px) saturate(180%);
|
|
405
|
-
border-radius: 24px;
|
|
406
|
-
padding: 32px;
|
|
407
|
-
width: 360px;
|
|
408
|
-
max-width: 90%;
|
|
409
|
-
box-shadow:
|
|
410
|
-
0 20px 40px rgba(0,0,0,0.2),
|
|
411
|
-
0 0 0 1px rgba(255,255,255,0.6) inset,
|
|
412
|
-
0 0 0 1px rgba(0,0,0,0.05);
|
|
413
|
-
z-index: 9999;
|
|
414
|
-
opacity: 0;
|
|
415
|
-
transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
|
416
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
417
|
-
text-align: center;
|
|
418
|
-
color: var(--glide-text);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
#glide-modal.glide-modal-wide {
|
|
422
|
-
width: 600px;
|
|
423
|
-
max-width: 95vw;
|
|
344
|
+
.glide-auth-modal {
|
|
345
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
424
346
|
}
|
|
425
347
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
box-shadow:
|
|
430
|
-
0 20px 40px rgba(0,0,0,0.4),
|
|
431
|
-
0 0 0 1px rgba(255,255,255,0.15) inset,
|
|
432
|
-
0 0 0 1px rgba(0,0,0,0.5);
|
|
433
|
-
--glide-phone-border: #ffffff; /* High Contrast White */
|
|
348
|
+
.glide-auth-header {
|
|
349
|
+
position: relative;
|
|
350
|
+
margin-bottom: 20px;
|
|
434
351
|
}
|
|
435
352
|
|
|
436
|
-
|
|
353
|
+
.glide-auth-close {
|
|
437
354
|
position: absolute;
|
|
438
|
-
top:
|
|
439
|
-
right:
|
|
440
|
-
|
|
441
|
-
height: var(--glide-btn-size);
|
|
442
|
-
min-width: var(--glide-btn-size);
|
|
443
|
-
min-height: var(--glide-btn-size);
|
|
444
|
-
max-width: var(--glide-btn-size);
|
|
445
|
-
max-height: var(--glide-btn-size);
|
|
446
|
-
background: rgba(118, 118, 128, 0.12);
|
|
355
|
+
top: 0;
|
|
356
|
+
right: 0;
|
|
357
|
+
background: none;
|
|
447
358
|
border: none;
|
|
448
|
-
border-radius: 50%;
|
|
449
|
-
display: flex;
|
|
450
|
-
align-items: center;
|
|
451
|
-
justify-content: center;
|
|
452
359
|
cursor: pointer;
|
|
453
|
-
color: rgba(0,0,0,0.5);
|
|
454
|
-
transition: background 0.2s;
|
|
455
360
|
padding: 0;
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
flex-shrink: 0;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
#glide-modal.dark .glide-btn-close {
|
|
462
|
-
background: rgba(255, 255, 255, 0.1);
|
|
463
|
-
color: rgba(255,255,255,0.5);
|
|
361
|
+
color: #666;
|
|
362
|
+
transition: color 0.2s;
|
|
464
363
|
}
|
|
465
364
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
#glide-modal.dark .glide-btn-close:hover {
|
|
471
|
-
background: rgba(255, 255, 255, 0.2);
|
|
365
|
+
.glide-auth-close:hover {
|
|
366
|
+
color: #000;
|
|
472
367
|
}
|
|
473
368
|
|
|
474
|
-
.glide-title {
|
|
475
|
-
margin: 0
|
|
476
|
-
font-size:
|
|
369
|
+
.glide-auth-title {
|
|
370
|
+
margin: 0;
|
|
371
|
+
font-size: 24px;
|
|
477
372
|
font-weight: 600;
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
.glide-status {
|
|
482
|
-
margin: 12px 0 0 0;
|
|
483
|
-
font-size: 13px;
|
|
484
|
-
color: rgba(0,0,0,0.5);
|
|
373
|
+
color: #333;
|
|
485
374
|
text-align: center;
|
|
486
|
-
min-height: 18px;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
.glide-status:empty {
|
|
490
|
-
display: none;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
#glide-modal.dark .glide-status {
|
|
494
|
-
color: rgba(255,255,255,0.5);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
.glide-content {
|
|
498
|
-
display: flex;
|
|
499
|
-
flex-direction: column;
|
|
500
|
-
align-items: center;
|
|
501
|
-
position: relative;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/* --- Sliding Toggle Switch --- */
|
|
505
|
-
#glide-toggle {
|
|
506
|
-
background: rgba(118, 118, 128, 0.12);
|
|
507
|
-
padding: 2px;
|
|
508
|
-
border-radius: 8px;
|
|
509
|
-
display: inline-flex;
|
|
510
|
-
margin-bottom: 24px;
|
|
511
|
-
margin-top: 16px;
|
|
512
|
-
position: relative;
|
|
513
|
-
height: 32px;
|
|
514
|
-
width: 200px;
|
|
515
|
-
box-sizing: border-box;
|
|
516
375
|
}
|
|
517
376
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/* The sliding background */
|
|
523
|
-
.glide-toggle-slider {
|
|
524
|
-
position: absolute;
|
|
525
|
-
top: 2px;
|
|
526
|
-
left: 2px;
|
|
527
|
-
width: calc(50% - 2px);
|
|
528
|
-
height: calc(100% - 4px);
|
|
529
|
-
background: white;
|
|
530
|
-
border-radius: 6px;
|
|
531
|
-
box-shadow: 0 3px 8px rgba(0,0,0,0.12), 0 3px 1px rgba(0,0,0,0.04);
|
|
532
|
-
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
533
|
-
z-index: 0;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
#glide-modal.dark .glide-toggle-slider {
|
|
537
|
-
background: #636366;
|
|
377
|
+
.glide-auth-content {
|
|
378
|
+
text-align: center;
|
|
538
379
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
380
|
+
|
|
381
|
+
.glide-auth-description {
|
|
382
|
+
color: #666;
|
|
383
|
+
margin: 0 0 20px 0;
|
|
384
|
+
font-size: 16px;
|
|
385
|
+
line-height: 1.5;
|
|
543
386
|
}
|
|
544
387
|
|
|
545
|
-
.glide-
|
|
546
|
-
|
|
547
|
-
|
|
388
|
+
.glide-auth-button {
|
|
389
|
+
background: #007AFF;
|
|
390
|
+
color: white;
|
|
548
391
|
border: none;
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
font-size:
|
|
392
|
+
border-radius: 8px;
|
|
393
|
+
padding: 12px 24px;
|
|
394
|
+
font-size: 16px;
|
|
552
395
|
font-weight: 500;
|
|
553
|
-
color: inherit;
|
|
554
396
|
cursor: pointer;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
transition: color 0.2s;
|
|
558
|
-
display: flex;
|
|
559
|
-
align-items: center;
|
|
560
|
-
justify-content: center;
|
|
561
|
-
gap: 6px;
|
|
562
|
-
min-height: auto;
|
|
563
|
-
height: auto;
|
|
564
|
-
min-width: auto;
|
|
565
|
-
border-radius: 0;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
#glide-modal .glide-toggle-btn .glide-icon-os,
|
|
569
|
-
#glide-modal .glide-toggle-btn svg {
|
|
570
|
-
width: var(--glide-toggle-icon-size);
|
|
571
|
-
height: var(--glide-toggle-icon-size);
|
|
572
|
-
flex-shrink: 0;
|
|
573
|
-
margin: 0;
|
|
574
|
-
padding: 0;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
.glide-toggle-btn span {
|
|
578
|
-
margin: 0;
|
|
579
|
-
padding: 0;
|
|
580
|
-
line-height: 1;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
#glide-modal.dark .glide-toggle-btn {
|
|
584
|
-
color: rgba(255,255,255,0.6);
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
#glide-toggle[data-active="ios"] .glide-toggle-btn[data-platform="ios"],
|
|
588
|
-
#glide-toggle[data-active="android"] .glide-toggle-btn[data-platform="android"] {
|
|
589
|
-
color: #1d1d1f;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
#glide-modal.dark #glide-toggle[data-active="ios"] .glide-toggle-btn[data-platform="ios"],
|
|
593
|
-
#glide-modal.dark #glide-toggle[data-active="android"] .glide-toggle-btn[data-platform="android"] {
|
|
594
|
-
color: white;
|
|
397
|
+
transition: background 0.2s, transform 0.1s;
|
|
398
|
+
min-width: 200px;
|
|
595
399
|
}
|
|
596
400
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
margin: 0 auto;
|
|
401
|
+
.glide-auth-button:hover:not(:disabled) {
|
|
402
|
+
background: #0051D5;
|
|
403
|
+
transform: translateY(-1px);
|
|
601
404
|
}
|
|
602
405
|
|
|
603
|
-
.glide-
|
|
604
|
-
|
|
605
|
-
height: 200px;
|
|
606
|
-
object-fit: contain;
|
|
607
|
-
display: block;
|
|
608
|
-
border-radius: 16px;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/* Dual Mode QR Area - no extra padding, same as single mode */
|
|
612
|
-
.glide-dual-mode .glide-qr-area {
|
|
613
|
-
background: transparent;
|
|
614
|
-
padding: 0;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
.glide-dual-mode .glide-qr-img {
|
|
618
|
-
width: 180px;
|
|
619
|
-
height: 180px;
|
|
620
|
-
border-radius: 16px;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
/* --- Help Icon & Interaction --- */
|
|
624
|
-
.glide-btn-help {
|
|
625
|
-
position: absolute;
|
|
626
|
-
bottom: 20px;
|
|
627
|
-
right: 20px;
|
|
628
|
-
width: var(--glide-help-btn-size);
|
|
629
|
-
height: var(--glide-help-btn-size);
|
|
630
|
-
min-width: var(--glide-help-btn-size);
|
|
631
|
-
min-height: var(--glide-help-btn-size);
|
|
632
|
-
max-width: var(--glide-help-btn-size);
|
|
633
|
-
max-height: var(--glide-help-btn-size);
|
|
634
|
-
border-radius: 50%;
|
|
635
|
-
border: 1.5px solid rgba(0,0,0,0.2);
|
|
636
|
-
color: rgba(0,0,0,0.4);
|
|
637
|
-
display: flex;
|
|
638
|
-
align-items: center;
|
|
639
|
-
justify-content: center;
|
|
640
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
641
|
-
font-size: 14px;
|
|
642
|
-
font-weight: 600;
|
|
643
|
-
line-height: 1;
|
|
644
|
-
cursor: pointer;
|
|
645
|
-
transition: all 0.2s;
|
|
646
|
-
background: transparent;
|
|
647
|
-
padding: 0;
|
|
648
|
-
margin: 0;
|
|
649
|
-
box-sizing: border-box;
|
|
650
|
-
z-index: 10;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
#glide-modal.dark .glide-btn-help {
|
|
654
|
-
border-color: rgba(255,255,255,0.3);
|
|
655
|
-
color: rgba(255,255,255,0.5);
|
|
406
|
+
.glide-auth-button:active {
|
|
407
|
+
transform: translateY(0);
|
|
656
408
|
}
|
|
657
409
|
|
|
658
|
-
.glide-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
background: rgba(0, 122, 255, 0.1);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
/* Tooltip */
|
|
665
|
-
.glide-btn-help::after {
|
|
666
|
-
content: "Need help?";
|
|
667
|
-
position: absolute;
|
|
668
|
-
bottom: 100%;
|
|
669
|
-
left: 50%;
|
|
670
|
-
transform: translateX(-50%) translateY(-8px);
|
|
671
|
-
background: rgba(0,0,0,0.8);
|
|
672
|
-
color: white;
|
|
673
|
-
padding: 4px 8px;
|
|
674
|
-
border-radius: 4px;
|
|
675
|
-
font-size: 11px;
|
|
676
|
-
white-space: nowrap;
|
|
677
|
-
opacity: 0;
|
|
678
|
-
pointer-events: none;
|
|
679
|
-
transition: opacity 0.2s;
|
|
410
|
+
.glide-auth-button:disabled {
|
|
411
|
+
opacity: 0.6;
|
|
412
|
+
cursor: not-allowed;
|
|
680
413
|
}
|
|
681
|
-
|
|
682
|
-
.glide-
|
|
683
|
-
|
|
414
|
+
|
|
415
|
+
.glide-auth-helper-text {
|
|
416
|
+
color: #999;
|
|
417
|
+
font-size: 14px;
|
|
418
|
+
margin: 16px 0 0 0;
|
|
684
419
|
}
|
|
685
420
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
position: absolute;
|
|
689
|
-
top: 0;
|
|
690
|
-
left: 0;
|
|
421
|
+
.glide-auth-qr-code {
|
|
422
|
+
max-width: 256px;
|
|
691
423
|
width: 100%;
|
|
692
|
-
height:
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
justify-content: center;
|
|
696
|
-
pointer-events: none;
|
|
697
|
-
opacity: 0;
|
|
698
|
-
transition: opacity 0.3s;
|
|
699
|
-
border-radius: 16px;
|
|
700
|
-
background: rgba(255,255,255,0.8);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
#glide-modal.dark .glide-phone-animation-overlay {
|
|
704
|
-
background: rgba(0,0,0,0.6);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
.glide-phone-animation-overlay.playing {
|
|
708
|
-
opacity: 1;
|
|
424
|
+
height: auto;
|
|
425
|
+
margin: 20px auto;
|
|
426
|
+
display: block;
|
|
709
427
|
}
|
|
710
428
|
|
|
711
|
-
.glide-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
border-radius: 16px;
|
|
716
|
-
position: relative;
|
|
717
|
-
background: transparent;
|
|
718
|
-
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
719
|
-
transform: translateY(20px);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
/* Notch */
|
|
723
|
-
.glide-outlined-phone::before {
|
|
724
|
-
content: '';
|
|
725
|
-
position: absolute;
|
|
726
|
-
top: -1px;
|
|
727
|
-
left: 50%;
|
|
728
|
-
transform: translateX(-50%);
|
|
729
|
-
width: 40%;
|
|
730
|
-
height: 12px;
|
|
731
|
-
background: var(--glide-phone-border);
|
|
732
|
-
border-bottom-left-radius: 8px;
|
|
733
|
-
border-bottom-right-radius: 8px;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
/* Scanning Line */
|
|
737
|
-
.glide-scan-line {
|
|
738
|
-
position: absolute;
|
|
739
|
-
top: 10%;
|
|
740
|
-
left: 5%;
|
|
741
|
-
width: 90%;
|
|
742
|
-
height: 2px;
|
|
743
|
-
background: var(--glide-primary);
|
|
744
|
-
box-shadow: 0 0 8px var(--glide-primary);
|
|
745
|
-
opacity: 0;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
/* Animation Keyframes */
|
|
749
|
-
@keyframes glide-scan-motion {
|
|
750
|
-
0% { top: 10%; opacity: 0; }
|
|
751
|
-
10% { opacity: 1; }
|
|
752
|
-
90% { opacity: 1; }
|
|
753
|
-
100% { top: 90%; opacity: 0; }
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
.glide-phone-animation-overlay.playing .glide-outlined-phone {
|
|
757
|
-
animation: glide-phone-appear 3s ease-in-out forwards;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
.glide-phone-animation-overlay.playing .glide-scan-line {
|
|
761
|
-
animation: glide-scan-motion 2s ease-in-out 0.5s infinite;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
@keyframes glide-phone-appear {
|
|
765
|
-
0% { transform: translateY(20px); opacity: 0; }
|
|
766
|
-
10% { transform: translateY(0); opacity: 1; }
|
|
767
|
-
90% { transform: translateY(0); opacity: 1; }
|
|
768
|
-
100% { transform: translateY(20px); opacity: 0; }
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
/* Dual Mode */
|
|
772
|
-
.glide-dual-container {
|
|
773
|
-
display: flex;
|
|
774
|
-
justify-content: center;
|
|
775
|
-
align-items: stretch;
|
|
776
|
-
gap: 32px;
|
|
777
|
-
margin-top: 20px;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
.glide-dual-item {
|
|
781
|
-
display: flex;
|
|
782
|
-
flex-direction: column;
|
|
783
|
-
align-items: center;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
.glide-os-logo {
|
|
787
|
-
display: flex;
|
|
788
|
-
align-items: center;
|
|
789
|
-
gap: 8px;
|
|
790
|
-
font-weight: 600;
|
|
791
|
-
margin-bottom: 12px;
|
|
792
|
-
font-size: 15px;
|
|
793
|
-
color: inherit;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
/* Dual mode OS logo - aligned icons with text, spacing to QR */
|
|
797
|
-
.glide-dual-mode .glide-os-logo {
|
|
798
|
-
margin-bottom: 12px; /* Space between label and QR code */
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
#glide-modal .glide-dual-mode .glide-os-logo .glide-icon-os,
|
|
802
|
-
#glide-modal .glide-dual-mode .glide-os-logo svg {
|
|
803
|
-
width: var(--glide-dual-icon-size);
|
|
804
|
-
height: var(--glide-dual-icon-size);
|
|
805
|
-
margin: 0;
|
|
806
|
-
padding: 0;
|
|
807
|
-
flex-shrink: 0;
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
.glide-dual-mode .glide-os-logo span {
|
|
811
|
-
margin: 0;
|
|
812
|
-
padding: 0;
|
|
813
|
-
line-height: 1;
|
|
429
|
+
.glide-auth-status {
|
|
430
|
+
color: #666;
|
|
431
|
+
font-size: 16px;
|
|
432
|
+
margin: 16px 0;
|
|
814
433
|
}
|
|
815
|
-
|
|
816
|
-
.glide-
|
|
817
|
-
|
|
818
|
-
height: 20px;
|
|
819
|
-
opacity: 0.8;
|
|
434
|
+
|
|
435
|
+
.glide-auth-error {
|
|
436
|
+
color: #FF3B30;
|
|
820
437
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
.glide-
|
|
824
|
-
|
|
825
|
-
flex-direction: column;
|
|
826
|
-
align-items: center;
|
|
827
|
-
justify-content: center;
|
|
828
|
-
align-self: stretch;
|
|
829
|
-
/* Offset for OS label (18px icon + 12px margin = ~30px) */
|
|
830
|
-
margin-top: 30px;
|
|
438
|
+
|
|
439
|
+
.glide-auth-qr-container,
|
|
440
|
+
.glide-auth-link-container,
|
|
441
|
+
.glide-auth-ts43-container {
|
|
831
442
|
padding: 20px 0;
|
|
832
443
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
flex: 1;
|
|
837
|
-
background: linear-gradient(to bottom, transparent, rgba(128,128,128,0.3), transparent);
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
.glide-separator-text {
|
|
841
|
-
padding: 8px 0;
|
|
842
|
-
font-size: 11px;
|
|
843
|
-
font-weight: 500;
|
|
844
|
-
color: rgba(128,128,128,0.5);
|
|
845
|
-
text-transform: uppercase;
|
|
846
|
-
letter-spacing: 0.5px;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
#glide-modal.dark .glide-separator-line {
|
|
850
|
-
background: linear-gradient(to bottom, transparent, rgba(255,255,255,0.2), transparent);
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
#glide-modal.dark .glide-separator-text {
|
|
854
|
-
color: rgba(255,255,255,0.4);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
/* Pre-Step */
|
|
858
|
-
.glide-pre-step-container {
|
|
444
|
+
|
|
445
|
+
/* Platform Switcher Styles */
|
|
446
|
+
.glide-platform-switcher {
|
|
859
447
|
display: flex;
|
|
860
|
-
gap: 20px;
|
|
861
448
|
justify-content: center;
|
|
862
|
-
|
|
449
|
+
gap: 10px;
|
|
450
|
+
margin-bottom: 20px;
|
|
863
451
|
}
|
|
864
|
-
|
|
865
|
-
.glide-
|
|
452
|
+
|
|
453
|
+
.glide-platform-btn {
|
|
866
454
|
display: flex;
|
|
867
|
-
flex-direction: column;
|
|
868
455
|
align-items: center;
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
border-radius:
|
|
874
|
-
background: rgba(255,255,255,0.4);
|
|
456
|
+
gap: 6px;
|
|
457
|
+
padding: 10px 20px;
|
|
458
|
+
border: 2px solid;
|
|
459
|
+
background: transparent;
|
|
460
|
+
border-radius: 8px;
|
|
875
461
|
cursor: pointer;
|
|
876
|
-
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
877
|
-
position: relative;
|
|
878
|
-
overflow: hidden;
|
|
879
462
|
font-size: 14px;
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
#glide-modal.dark .glide-os-choice-btn {
|
|
884
|
-
background: rgba(255,255,255,0.05);
|
|
885
|
-
border-color: rgba(255,255,255,0.1);
|
|
886
|
-
color: white;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
.glide-os-choice-btn:hover {
|
|
890
|
-
background: rgba(255,255,255,0.9);
|
|
891
|
-
transform: translateY(-4px);
|
|
892
|
-
box-shadow: 0 12px 24px rgba(0,0,0,0.1), 0 4px 8px rgba(0,0,0,0.04);
|
|
893
|
-
border-color: var(--glide-primary);
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
#glide-modal.dark .glide-os-choice-btn:hover {
|
|
897
|
-
background: rgba(255,255,255,0.15);
|
|
898
|
-
box-shadow: 0 12px 24px rgba(0,0,0,0.3);
|
|
899
|
-
border-color: var(--glide-primary);
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
.glide-icon-os {
|
|
903
|
-
width: 40px;
|
|
904
|
-
height: 40px;
|
|
905
|
-
margin-bottom: 12px;
|
|
906
|
-
transition: transform 0.3s;
|
|
907
|
-
fill: currentColor;
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
.glide-os-choice-btn:hover .glide-icon-os {
|
|
911
|
-
transform: scale(1.1);
|
|
463
|
+
font-weight: 600;
|
|
464
|
+
transition: all 0.2s ease;
|
|
912
465
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
position: absolute;
|
|
916
|
-
top: 16px;
|
|
917
|
-
left: 16px;
|
|
918
|
-
width: var(--glide-btn-size);
|
|
919
|
-
height: var(--glide-btn-size);
|
|
920
|
-
min-width: var(--glide-btn-size);
|
|
921
|
-
min-height: var(--glide-btn-size);
|
|
922
|
-
max-width: var(--glide-btn-size);
|
|
923
|
-
max-height: var(--glide-btn-size);
|
|
924
|
-
background: rgba(118, 118, 128, 0.12);
|
|
925
|
-
border: none;
|
|
926
|
-
border-radius: 50%;
|
|
927
|
-
display: flex;
|
|
928
|
-
align-items: center;
|
|
929
|
-
justify-content: center;
|
|
930
|
-
cursor: pointer;
|
|
931
|
-
color: rgba(0,0,0,0.5);
|
|
932
|
-
transition: background 0.2s;
|
|
933
|
-
z-index: 20;
|
|
934
|
-
padding: 0;
|
|
935
|
-
margin: 0;
|
|
936
|
-
box-sizing: border-box;
|
|
466
|
+
|
|
467
|
+
.glide-platform-btn svg {
|
|
937
468
|
flex-shrink: 0;
|
|
938
469
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
color:
|
|
470
|
+
|
|
471
|
+
.glide-platform-ios {
|
|
472
|
+
border-color: #007AFF;
|
|
473
|
+
color: #007AFF;
|
|
943
474
|
}
|
|
944
475
|
|
|
945
|
-
.glide-
|
|
946
|
-
background:
|
|
476
|
+
.glide-platform-ios.active {
|
|
477
|
+
background: #007AFF;
|
|
478
|
+
color: white;
|
|
947
479
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
background: rgba(
|
|
480
|
+
|
|
481
|
+
.glide-platform-ios:hover:not(.active) {
|
|
482
|
+
background: rgba(0, 122, 255, 0.1);
|
|
951
483
|
}
|
|
952
|
-
|
|
953
|
-
.glide-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
border: 3px solid rgba(0,0,0,0.1);
|
|
957
|
-
border-top-color: var(--glide-primary);
|
|
958
|
-
border-radius: 50%;
|
|
959
|
-
animation: glide-spin 1s linear infinite;
|
|
960
|
-
margin: 40px auto;
|
|
484
|
+
|
|
485
|
+
.glide-platform-android {
|
|
486
|
+
border-color: #3DDC84;
|
|
487
|
+
color: #3DDC84;
|
|
961
488
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
489
|
+
|
|
490
|
+
.glide-platform-android.active {
|
|
491
|
+
background: #3DDC84;
|
|
492
|
+
color: white;
|
|
966
493
|
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
494
|
+
|
|
495
|
+
.glide-platform-android:hover:not(.active) {
|
|
496
|
+
background: rgba(61, 220, 132, 0.1);
|
|
970
497
|
}
|
|
971
498
|
`;
|
|
972
499
|
document.head.appendChild(styles);
|
|
973
500
|
}
|
|
501
|
+
/**
|
|
502
|
+
* Shows the modal with animation
|
|
503
|
+
*/
|
|
974
504
|
show() {
|
|
975
505
|
var _a, _b;
|
|
976
|
-
if (!this.container || !this.backdrop || this.isOpen)
|
|
977
|
-
if (this.isOpen && this.container && this.backdrop) {
|
|
978
|
-
document.removeEventListener('keydown', this.handleEscapeKey);
|
|
979
|
-
document.addEventListener('keydown', this.handleEscapeKey);
|
|
980
|
-
}
|
|
506
|
+
if (!this.container || !this.backdrop || this.isOpen)
|
|
981
507
|
return;
|
|
982
|
-
}
|
|
983
508
|
document.body.appendChild(this.backdrop);
|
|
984
509
|
document.body.appendChild(this.container);
|
|
985
|
-
|
|
510
|
+
// Add escape key listener for closing modal
|
|
986
511
|
document.addEventListener('keydown', this.handleEscapeKey);
|
|
512
|
+
// Setup platform toggle handlers if they exist
|
|
513
|
+
this.setupPlatformToggles();
|
|
514
|
+
// Trigger animation
|
|
987
515
|
requestAnimationFrame(() => {
|
|
988
516
|
if (this.backdrop && this.container) {
|
|
989
517
|
this.backdrop.style.opacity = '1';
|
|
990
518
|
this.container.style.opacity = '1';
|
|
991
|
-
this.container.style.transform = '
|
|
519
|
+
this.container.style.transform = 'translate(-50%, -50%) scale(1)';
|
|
992
520
|
}
|
|
993
521
|
});
|
|
994
522
|
this.isOpen = true;
|
|
995
523
|
(_b = (_a = this.callbacks) === null || _a === void 0 ? void 0 : _a.onOpen) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
996
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* Setup click handlers for iOS/Android platform toggle
|
|
527
|
+
*/
|
|
997
528
|
setupPlatformToggles() {
|
|
998
|
-
var _a, _b, _c
|
|
999
|
-
const
|
|
1000
|
-
const
|
|
1001
|
-
const
|
|
1002
|
-
const message = (_d = this.container) === null || _d === void 0 ? void 0 : _d.querySelector('#glide-platform-message');
|
|
529
|
+
var _a, _b, _c;
|
|
530
|
+
const platformBtns = (_a = this.container) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.glide-platform-btn');
|
|
531
|
+
const qrImg = (_b = this.container) === null || _b === void 0 ? void 0 : _b.querySelector('#glide-qr-code-img');
|
|
532
|
+
const message = (_c = this.container) === null || _c === void 0 ? void 0 : _c.querySelector('#glide-platform-message');
|
|
1003
533
|
if (!platformBtns || !qrImg)
|
|
1004
534
|
return;
|
|
1005
535
|
platformBtns.forEach((btn) => {
|
|
1006
536
|
btn.addEventListener('click', (e) => {
|
|
1007
537
|
const target = e.currentTarget;
|
|
1008
538
|
const platform = target.getAttribute('data-platform');
|
|
1009
|
-
|
|
1010
|
-
container.setAttribute('data-active', platform);
|
|
1011
|
-
}
|
|
539
|
+
// Update active state
|
|
1012
540
|
platformBtns.forEach(b => b.classList.remove('active'));
|
|
1013
541
|
target.classList.add('active');
|
|
542
|
+
// Switch QR code
|
|
1014
543
|
if (platform === 'ios') {
|
|
1015
544
|
qrImg.src = qrImg.getAttribute('data-ios') || '';
|
|
1016
545
|
if (message)
|
|
1017
|
-
message.textContent = 'Scan with your iPhone
|
|
546
|
+
message.textContent = 'Scan with your iPhone to authenticate';
|
|
1018
547
|
}
|
|
1019
548
|
else if (platform === 'android') {
|
|
1020
549
|
qrImg.src = qrImg.getAttribute('data-android') || '';
|
|
1021
550
|
if (message)
|
|
1022
|
-
message.textContent = 'Scan with your Android
|
|
551
|
+
message.textContent = 'Scan with your Android device to authenticate';
|
|
1023
552
|
}
|
|
1024
553
|
});
|
|
1025
554
|
});
|
|
1026
555
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
}
|
|
1031
|
-
unlockBodyScroll() {
|
|
1032
|
-
document.body.style.overflow = this.originalBodyOverflow;
|
|
1033
|
-
}
|
|
556
|
+
/**
|
|
557
|
+
* Closes the modal with animation
|
|
558
|
+
*/
|
|
1034
559
|
close() {
|
|
1035
560
|
if (!this.container || !this.backdrop || !this.isOpen)
|
|
1036
561
|
return;
|
|
1037
|
-
|
|
562
|
+
// Remove escape key listener
|
|
1038
563
|
document.removeEventListener('keydown', this.handleEscapeKey);
|
|
564
|
+
// Animate out
|
|
1039
565
|
this.backdrop.style.opacity = '0';
|
|
1040
566
|
this.container.style.opacity = '0';
|
|
1041
|
-
this.container.style.transform = '
|
|
567
|
+
this.container.style.transform = 'translate(-50%, -50%) scale(0.9)';
|
|
568
|
+
// Clear any stored callbacks
|
|
1042
569
|
this.closeCallback = undefined;
|
|
570
|
+
// Remove after animation
|
|
1043
571
|
setTimeout(() => {
|
|
1044
572
|
var _a, _b;
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
(_b = (_a = this.callbacks) === null || _a === void 0 ? void 0 : _a.onClose) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
1050
|
-
}
|
|
1051
|
-
}, 400);
|
|
573
|
+
this.cleanup();
|
|
574
|
+
this.isOpen = false;
|
|
575
|
+
(_b = (_a = this.callbacks) === null || _a === void 0 ? void 0 : _a.onClose) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
576
|
+
}, 300);
|
|
1052
577
|
}
|
|
578
|
+
/**
|
|
579
|
+
* Removes modal elements from DOM
|
|
580
|
+
*/
|
|
1053
581
|
cleanup() {
|
|
1054
582
|
var _a, _b;
|
|
1055
583
|
(_a = this.container) === null || _a === void 0 ? void 0 : _a.remove();
|
|
1056
584
|
(_b = this.backdrop) === null || _b === void 0 ? void 0 : _b.remove();
|
|
1057
585
|
this.container = null;
|
|
1058
586
|
this.backdrop = null;
|
|
1059
|
-
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Check if modal is currently open
|
|
590
|
+
*/
|
|
591
|
+
isModalOpen() {
|
|
592
|
+
return this.isOpen;
|
|
1060
593
|
}
|
|
1061
594
|
}
|