@civic/auth 0.6.0 → 0.6.1-beta.2
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/nextjs/config.d.ts.map +1 -1
- package/dist/nextjs/config.js +3 -1
- package/dist/nextjs/config.js.map +1 -1
- package/dist/nextjs/hooks/useUserCookie.d.ts.map +1 -1
- package/dist/nextjs/hooks/useUserCookie.js.map +1 -1
- package/dist/nextjs/providers/NextAuthProvider.d.ts.map +1 -1
- package/dist/nextjs/providers/NextAuthProvider.js +1 -0
- package/dist/nextjs/providers/NextAuthProvider.js.map +1 -1
- package/dist/shared/components/CivicAuthIframeContainer.js +1 -1
- package/dist/shared/components/CivicAuthIframeContainer.js.map +1 -1
- package/dist/shared/hooks/useSignIn.d.ts +9 -4
- package/dist/shared/hooks/useSignIn.d.ts.map +1 -1
- package/dist/shared/hooks/useSignIn.js +75 -42
- package/dist/shared/hooks/useSignIn.js.map +1 -1
- package/dist/shared/lib/BrowserAuthenticationRefresher.d.ts +7 -1
- package/dist/shared/lib/BrowserAuthenticationRefresher.d.ts.map +1 -1
- package/dist/shared/lib/BrowserAuthenticationRefresher.js +15 -2
- package/dist/shared/lib/BrowserAuthenticationRefresher.js.map +1 -1
- package/dist/shared/lib/util.d.ts +1 -1
- package/dist/shared/lib/util.d.ts.map +1 -1
- package/dist/shared/lib/util.js +6 -1
- package/dist/shared/lib/util.js.map +1 -1
- package/dist/shared/providers/AuthContext.d.ts +7 -2
- package/dist/shared/providers/AuthContext.d.ts.map +1 -1
- package/dist/shared/providers/AuthContext.js.map +1 -1
- package/dist/shared/providers/UserProvider.d.ts +5 -1
- package/dist/shared/providers/UserProvider.d.ts.map +1 -1
- package/dist/shared/providers/UserProvider.js.map +1 -1
- package/dist/shared/version.d.ts +1 -1
- package/dist/shared/version.d.ts.map +1 -1
- package/dist/shared/version.js +1 -1
- package/dist/shared/version.js.map +1 -1
- package/dist/vanillajs/auth/AuthenticationEvents.d.ts.map +1 -1
- package/dist/vanillajs/auth/AuthenticationEvents.js +2 -2
- package/dist/vanillajs/auth/AuthenticationEvents.js.map +1 -1
- package/dist/vanillajs/auth/CivicAuth.d.ts +111 -92
- package/dist/vanillajs/auth/CivicAuth.d.ts.map +1 -1
- package/dist/vanillajs/auth/CivicAuth.js +474 -321
- package/dist/vanillajs/auth/CivicAuth.js.map +1 -1
- package/dist/vanillajs/auth/SessionManager.d.ts +40 -9
- package/dist/vanillajs/auth/SessionManager.d.ts.map +1 -1
- package/dist/vanillajs/auth/SessionManager.js +96 -61
- package/dist/vanillajs/auth/SessionManager.js.map +1 -1
- package/dist/vanillajs/auth/TokenRefresher.d.ts +54 -0
- package/dist/vanillajs/auth/TokenRefresher.d.ts.map +1 -0
- package/dist/vanillajs/auth/TokenRefresher.js +166 -0
- package/dist/vanillajs/auth/TokenRefresher.js.map +1 -0
- package/dist/vanillajs/auth/config/ConfigProcessor.d.ts +6 -0
- package/dist/vanillajs/auth/config/ConfigProcessor.d.ts.map +1 -0
- package/dist/vanillajs/auth/config/ConfigProcessor.js +59 -0
- package/dist/vanillajs/auth/config/ConfigProcessor.js.map +1 -0
- package/dist/vanillajs/auth/handlers/IframeAuthHandler.d.ts +40 -0
- package/dist/vanillajs/auth/handlers/IframeAuthHandler.d.ts.map +1 -0
- package/dist/vanillajs/auth/handlers/IframeAuthHandler.js +388 -0
- package/dist/vanillajs/auth/handlers/IframeAuthHandler.js.map +1 -0
- package/dist/vanillajs/auth/handlers/MessageHandler.d.ts +170 -0
- package/dist/vanillajs/auth/handlers/MessageHandler.d.ts.map +1 -0
- package/dist/vanillajs/auth/handlers/MessageHandler.js +367 -0
- package/dist/vanillajs/auth/handlers/MessageHandler.js.map +1 -0
- package/dist/vanillajs/auth/handlers/OAuthCallbackHandler.d.ts +90 -0
- package/dist/vanillajs/auth/handlers/OAuthCallbackHandler.d.ts.map +1 -0
- package/dist/vanillajs/auth/handlers/OAuthCallbackHandler.js +301 -0
- package/dist/vanillajs/auth/handlers/OAuthCallbackHandler.js.map +1 -0
- package/dist/vanillajs/auth/handlers/PopupHandler.d.ts +108 -0
- package/dist/vanillajs/auth/handlers/PopupHandler.d.ts.map +1 -0
- package/dist/vanillajs/auth/handlers/PopupHandler.js +333 -0
- package/dist/vanillajs/auth/handlers/PopupHandler.js.map +1 -0
- package/dist/vanillajs/auth/types/AuthTypes.d.ts +128 -0
- package/dist/vanillajs/auth/types/AuthTypes.d.ts.map +1 -0
- package/dist/vanillajs/auth/types/AuthTypes.js +40 -0
- package/dist/vanillajs/auth/types/AuthTypes.js.map +1 -0
- package/dist/vanillajs/iframe/IframeManager.d.ts +115 -0
- package/dist/vanillajs/iframe/IframeManager.d.ts.map +1 -0
- package/dist/vanillajs/iframe/IframeManager.js +614 -0
- package/dist/vanillajs/iframe/IframeManager.js.map +1 -0
- package/dist/vanillajs/iframe/IframeResizer.d.ts +15 -0
- package/dist/vanillajs/iframe/IframeResizer.d.ts.map +1 -0
- package/dist/vanillajs/iframe/IframeResizer.js +127 -0
- package/dist/vanillajs/iframe/IframeResizer.js.map +1 -0
- package/dist/vanillajs/index.d.ts +8 -7
- package/dist/vanillajs/index.d.ts.map +1 -1
- package/dist/vanillajs/index.js +10 -7
- package/dist/vanillajs/index.js.map +1 -1
- package/dist/vanillajs/services/ApiService.d.ts.map +1 -1
- package/dist/vanillajs/services/ApiService.js +2 -2
- package/dist/vanillajs/services/ApiService.js.map +1 -1
- package/dist/vanillajs/types/index.d.ts +17 -12
- package/dist/vanillajs/types/index.d.ts.map +1 -1
- package/dist/vanillajs/types/index.js +15 -10
- package/dist/vanillajs/types/index.js.map +1 -1
- package/dist/vanillajs/ui/LoadingComponents.d.ts +51 -0
- package/dist/vanillajs/ui/LoadingComponents.d.ts.map +1 -0
- package/dist/vanillajs/ui/LoadingComponents.js +363 -0
- package/dist/vanillajs/ui/LoadingComponents.js.map +1 -0
- package/dist/vanillajs/utils/auth-utils.d.ts +2 -1
- package/dist/vanillajs/utils/auth-utils.d.ts.map +1 -1
- package/dist/vanillajs/utils/auth-utils.js +6 -3
- package/dist/vanillajs/utils/auth-utils.js.map +1 -1
- package/dist/vanillajs/utils/logger.d.ts +16 -15
- package/dist/vanillajs/utils/logger.d.ts.map +1 -1
- package/dist/vanillajs/utils/logger.js +35 -19
- package/dist/vanillajs/utils/logger.js.map +1 -1
- package/package.json +1 -1
- package/dist/vanillajs/iframe/domUtils.d.ts +0 -4
- package/dist/vanillajs/iframe/domUtils.d.ts.map +0 -1
- package/dist/vanillajs/iframe/domUtils.js +0 -25
- package/dist/vanillajs/iframe/domUtils.js.map +0 -1
- package/dist/vanillajs/storage/BrowserCookieStorageAdapter.d.ts +0 -19
- package/dist/vanillajs/storage/BrowserCookieStorageAdapter.d.ts.map +0 -1
- package/dist/vanillajs/storage/BrowserCookieStorageAdapter.js +0 -101
- package/dist/vanillajs/storage/BrowserCookieStorageAdapter.js.map +0 -1
- package/dist/vanillajs/storage/BrowserLocalStorageAdapter.d.ts +0 -9
- package/dist/vanillajs/storage/BrowserLocalStorageAdapter.d.ts.map +0 -1
- package/dist/vanillajs/storage/BrowserLocalStorageAdapter.js +0 -36
- package/dist/vanillajs/storage/BrowserLocalStorageAdapter.js.map +0 -1
- package/dist/vanillajs/storage/InMemoryStorageAdapter.d.ts +0 -9
- package/dist/vanillajs/storage/InMemoryStorageAdapter.d.ts.map +0 -1
- package/dist/vanillajs/storage/InMemoryStorageAdapter.js +0 -16
- package/dist/vanillajs/storage/InMemoryStorageAdapter.js.map +0 -1
- package/dist/vanillajs/storage/StorageAdapter.d.ts +0 -15
- package/dist/vanillajs/storage/StorageAdapter.d.ts.map +0 -1
- package/dist/vanillajs/storage/StorageAdapter.js +0 -16
- package/dist/vanillajs/storage/StorageAdapter.js.map +0 -1
- package/dist/vanillajs/utils/page-handlers.d.ts +0 -29
- package/dist/vanillajs/utils/page-handlers.d.ts.map +0 -1
- package/dist/vanillajs/utils/page-handlers.js +0 -165
- package/dist/vanillajs/utils/page-handlers.js.map +0 -1
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
import { CivicIframeResizer } from "./IframeResizer.js";
|
|
2
|
+
import { createIframeShimmerLoader, createCloseButton, } from "../ui/LoadingComponents.js";
|
|
3
|
+
import { createLogger } from "../utils/logger.js";
|
|
4
|
+
/**
|
|
5
|
+
* IframeManager - Manages iframe visual presentation and DOM lifecycle
|
|
6
|
+
* Handles styling, positioning, UI components, and iframe element creation/cleanup
|
|
7
|
+
*/
|
|
8
|
+
export class IframeManager {
|
|
9
|
+
// Constants for styling and configuration
|
|
10
|
+
static MODAL_Z_INDEX = "1050";
|
|
11
|
+
static MODAL_WIDTH = "20rem"; // 320px
|
|
12
|
+
static MODAL_BACKDROP_COLOR = "rgba(17, 24, 39, 0.5)";
|
|
13
|
+
static MODAL_BACKDROP_BLUR = "blur(4px)";
|
|
14
|
+
static BORDER_RADIUS = "24px";
|
|
15
|
+
static TRANSITION_DURATION = "250ms";
|
|
16
|
+
static MODAL_MIN_HEIGHT = "26px";
|
|
17
|
+
static EMBEDDED_MIN_HEIGHT = "225px";
|
|
18
|
+
static IFRAME_READY_DELAY = 100; // ms
|
|
19
|
+
iframe = null;
|
|
20
|
+
container;
|
|
21
|
+
resizer = null;
|
|
22
|
+
displayMode;
|
|
23
|
+
keydownHandler;
|
|
24
|
+
clickHandler;
|
|
25
|
+
iframeId;
|
|
26
|
+
onClose;
|
|
27
|
+
shimmerLoader = null;
|
|
28
|
+
closeButton = null;
|
|
29
|
+
isIframeLoaded = false;
|
|
30
|
+
iframeReadyMessageListener;
|
|
31
|
+
logger;
|
|
32
|
+
// Store references to wrapper elements for styling control
|
|
33
|
+
contentWrapper = null;
|
|
34
|
+
iframeWrapper = null;
|
|
35
|
+
constructor(config) {
|
|
36
|
+
this.container = config.container;
|
|
37
|
+
this.displayMode = config.displayMode;
|
|
38
|
+
this.iframeId = config.iframeId || "civic-auth-iframe";
|
|
39
|
+
this.onClose = config.onClose;
|
|
40
|
+
this.logger = createLogger("iframe");
|
|
41
|
+
this.initializeContainer(config);
|
|
42
|
+
this.setupKeyboardHandlers();
|
|
43
|
+
}
|
|
44
|
+
initializeContainer(config) {
|
|
45
|
+
if (this.displayMode === "modal") {
|
|
46
|
+
this.applyModalStyles();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// Embedded mode
|
|
50
|
+
this.container.style.position = "relative";
|
|
51
|
+
this.container.style.width = config.width || "auto";
|
|
52
|
+
this.container.style.height = config.height || "auto";
|
|
53
|
+
this.container.style.backgroundColor = "white";
|
|
54
|
+
this.container.style.borderRadius = IframeManager.BORDER_RADIUS;
|
|
55
|
+
this.container.style.overflow = "hidden";
|
|
56
|
+
this.container.style.transition = "all 0.5s ease-in-out";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Applies modal backdrop styles to create a full-screen overlay
|
|
60
|
+
* Uses !important to override any existing styles
|
|
61
|
+
*/
|
|
62
|
+
applyModalStyles() {
|
|
63
|
+
const modalStyles = {
|
|
64
|
+
position: "fixed",
|
|
65
|
+
left: "0",
|
|
66
|
+
top: "0",
|
|
67
|
+
width: "100vw",
|
|
68
|
+
height: "100vh",
|
|
69
|
+
"z-index": IframeManager.MODAL_Z_INDEX,
|
|
70
|
+
display: "flex",
|
|
71
|
+
"align-items": "center",
|
|
72
|
+
"justify-content": "center",
|
|
73
|
+
"background-color": IframeManager.MODAL_BACKDROP_COLOR,
|
|
74
|
+
"backdrop-filter": IframeManager.MODAL_BACKDROP_BLUR,
|
|
75
|
+
opacity: "1",
|
|
76
|
+
transition: `opacity ${IframeManager.TRANSITION_DURATION} ease`,
|
|
77
|
+
// Reset any conflicting styles that might prevent modal from working
|
|
78
|
+
margin: "0",
|
|
79
|
+
padding: "0",
|
|
80
|
+
border: "none",
|
|
81
|
+
"border-radius": "0",
|
|
82
|
+
"min-height": "100vh",
|
|
83
|
+
};
|
|
84
|
+
Object.entries(modalStyles).forEach(([property, value]) => {
|
|
85
|
+
this.container.style.setProperty(property, value, "important");
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
setupKeyboardHandlers() {
|
|
89
|
+
if (this.displayMode === "modal") {
|
|
90
|
+
this.keydownHandler = (event) => {
|
|
91
|
+
if (event.key === "Escape") {
|
|
92
|
+
this.onClose?.();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
window.addEventListener("keydown", this.keydownHandler);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
createIframe(url) {
|
|
99
|
+
// Clean up any existing iframe content, but preserve container styles for modal mode
|
|
100
|
+
this.cleanupIframeOnly();
|
|
101
|
+
// Create new iframe
|
|
102
|
+
this.iframe = document.createElement("iframe");
|
|
103
|
+
this.iframe.id = this.iframeId;
|
|
104
|
+
this.iframe.src = url;
|
|
105
|
+
this.iframe.setAttribute("data-testid", "civic-auth-iframe-with-resizer");
|
|
106
|
+
// Apply iframe styles
|
|
107
|
+
this.applyIframeStyles();
|
|
108
|
+
// Add attributes that prevent scrollbars
|
|
109
|
+
this.setIframeAttributes();
|
|
110
|
+
// Add CSS to prevent scrollbars
|
|
111
|
+
this.addScrollbarHidingStyles();
|
|
112
|
+
// Set up message listener for iframe ready events (more reliable than onload)
|
|
113
|
+
this.setupIframeMessageListener();
|
|
114
|
+
// Set up iframe onload handler
|
|
115
|
+
this.setupIframeOnloadHandler();
|
|
116
|
+
if (this.displayMode === "modal") {
|
|
117
|
+
this.setupModalIframe();
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this.setupEmbeddedIframe();
|
|
121
|
+
}
|
|
122
|
+
// Initialize resizer - this will handle all the message-based resizing
|
|
123
|
+
this.resizer = new CivicIframeResizer(this.iframe, this.container);
|
|
124
|
+
return this.iframe;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Applies base styles to the iframe element
|
|
128
|
+
*/
|
|
129
|
+
applyIframeStyles() {
|
|
130
|
+
if (!this.iframe)
|
|
131
|
+
return;
|
|
132
|
+
const baseStyles = {
|
|
133
|
+
border: "none",
|
|
134
|
+
display: "block",
|
|
135
|
+
pointerEvents: "auto",
|
|
136
|
+
width: "100%",
|
|
137
|
+
minWidth: "100%",
|
|
138
|
+
overflow: "hidden",
|
|
139
|
+
overflowX: "hidden",
|
|
140
|
+
overflowY: "hidden",
|
|
141
|
+
opacity: "0",
|
|
142
|
+
transition: `opacity ${IframeManager.TRANSITION_DURATION} ease, height ${IframeManager.TRANSITION_DURATION} ease`,
|
|
143
|
+
};
|
|
144
|
+
Object.entries(baseStyles).forEach(([property, value]) => {
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Required to handle dynamic CSS property assignment since CSSStyleDeclaration type doesn't fully capture all possible properties
|
|
146
|
+
this.iframe.style[property] = value;
|
|
147
|
+
});
|
|
148
|
+
// Set initial height based on display mode
|
|
149
|
+
if (this.displayMode === "modal") {
|
|
150
|
+
this.iframe.style.height = IframeManager.MODAL_MIN_HEIGHT;
|
|
151
|
+
this.iframe.style.minHeight = IframeManager.MODAL_MIN_HEIGHT;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
this.iframe.style.height = "auto";
|
|
155
|
+
this.iframe.style.minHeight = IframeManager.EMBEDDED_MIN_HEIGHT; // Embedded mode minimum
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Sets iframe attributes to prevent scrollbars
|
|
160
|
+
*/
|
|
161
|
+
setIframeAttributes() {
|
|
162
|
+
if (!this.iframe)
|
|
163
|
+
return;
|
|
164
|
+
this.iframe.setAttribute("scrolling", "no");
|
|
165
|
+
this.iframe.setAttribute("frameborder", "0");
|
|
166
|
+
this.iframe.setAttribute("seamless", "seamless");
|
|
167
|
+
// Add additional CSS to prevent scrollbars
|
|
168
|
+
this.iframe.style.cssText +=
|
|
169
|
+
"; -ms-overflow-style: none; scrollbar-width: none;";
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Adds CSS styles to hide scrollbars across different browsers
|
|
173
|
+
*/
|
|
174
|
+
addScrollbarHidingStyles() {
|
|
175
|
+
const iframeStyle = document.createElement("style");
|
|
176
|
+
iframeStyle.textContent = `
|
|
177
|
+
iframe[id="${this.iframeId}"] {
|
|
178
|
+
-webkit-overflow-scrolling: touch !important;
|
|
179
|
+
}
|
|
180
|
+
iframe[id="${this.iframeId}"]::webkit-scrollbar {
|
|
181
|
+
display: none !important;
|
|
182
|
+
width: 0 !important;
|
|
183
|
+
height: 0 !important;
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
document.head.appendChild(iframeStyle);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Sets up the message listener for iframe ready events
|
|
190
|
+
*/
|
|
191
|
+
setupIframeMessageListener() {
|
|
192
|
+
this.iframeReadyMessageListener = (event) => {
|
|
193
|
+
try {
|
|
194
|
+
// Only handle messages from our iframe
|
|
195
|
+
if (!this.iframe?.contentWindow ||
|
|
196
|
+
event.source !== this.iframe.contentWindow) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const message = event.data;
|
|
200
|
+
// Handle ready message from iframe resizer
|
|
201
|
+
if (message.type === "civic-iframe-ready") {
|
|
202
|
+
this.logger.debug("Iframe content is ready - hiding shimmer loader");
|
|
203
|
+
this.markIframeAsLoaded();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
this.logger.debug("Error processing iframe ready message:", error);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
window.addEventListener("message", this.iframeReadyMessageListener);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Sets up the iframe onload handler with CSS injection
|
|
214
|
+
*/
|
|
215
|
+
setupIframeOnloadHandler() {
|
|
216
|
+
if (!this.iframe)
|
|
217
|
+
return;
|
|
218
|
+
this.iframe.onload = () => {
|
|
219
|
+
// Use a small delay to allow ready message to arrive first
|
|
220
|
+
setTimeout(() => {
|
|
221
|
+
if (!this.isIframeLoaded) {
|
|
222
|
+
this.logger.debug("Iframe onload fired - hiding shimmer loader (fallback)");
|
|
223
|
+
this.markIframeAsLoaded();
|
|
224
|
+
}
|
|
225
|
+
}, IframeManager.IFRAME_READY_DELAY);
|
|
226
|
+
try {
|
|
227
|
+
const iframeDoc = this.iframe?.contentDocument || this.iframe?.contentWindow?.document;
|
|
228
|
+
if (iframeDoc && iframeDoc.head) {
|
|
229
|
+
const style = iframeDoc.createElement("style");
|
|
230
|
+
style.textContent = `
|
|
231
|
+
html, body {
|
|
232
|
+
overflow: hidden !important;
|
|
233
|
+
overflow-x: hidden !important;
|
|
234
|
+
overflow-y: hidden !important;
|
|
235
|
+
-ms-overflow-style: none !important;
|
|
236
|
+
scrollbar-width: none !important;
|
|
237
|
+
margin: 0 !important;
|
|
238
|
+
padding: 0 !important;
|
|
239
|
+
}
|
|
240
|
+
body::-webkit-scrollbar {
|
|
241
|
+
display: none !important;
|
|
242
|
+
}
|
|
243
|
+
* {
|
|
244
|
+
box-sizing: border-box !important;
|
|
245
|
+
}
|
|
246
|
+
`;
|
|
247
|
+
iframeDoc.head.appendChild(style);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
this.logger.debug("Could not inject CSS into iframe (likely cross-origin):", error);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Sets up the iframe for modal display mode
|
|
257
|
+
*/
|
|
258
|
+
setupModalIframe() {
|
|
259
|
+
if (!this.iframe)
|
|
260
|
+
return;
|
|
261
|
+
// Ensure modal is visible (in case it was hidden by cleanup)
|
|
262
|
+
this.container.style.setProperty("display", "flex", "important");
|
|
263
|
+
// Create content wrapper for modal
|
|
264
|
+
this.contentWrapper = document.createElement("div");
|
|
265
|
+
this.contentWrapper.style.position = "relative";
|
|
266
|
+
this.contentWrapper.style.overflow = "hidden";
|
|
267
|
+
this.contentWrapper.style.width = IframeManager.MODAL_WIDTH; // 320px like React version
|
|
268
|
+
// Apply initial visual styling
|
|
269
|
+
this.applyModalVisualStyling();
|
|
270
|
+
// Stop propagation on content wrapper to prevent backdrop clicks
|
|
271
|
+
this.contentWrapper.addEventListener("click", (e) => {
|
|
272
|
+
e.stopPropagation();
|
|
273
|
+
});
|
|
274
|
+
// Create inner iframe wrapper to match React structure
|
|
275
|
+
this.iframeWrapper = document.createElement("div");
|
|
276
|
+
this.iframeWrapper.style.position = "relative";
|
|
277
|
+
this.iframeWrapper.style.overflow = "hidden";
|
|
278
|
+
this.iframeWrapper.style.overflowX = "hidden";
|
|
279
|
+
this.iframeWrapper.style.overflowY = "hidden";
|
|
280
|
+
this.iframeWrapper.style.transition = `all ${IframeManager.TRANSITION_DURATION} ease`;
|
|
281
|
+
this.iframeWrapper.style.width = "100%";
|
|
282
|
+
this.iframeWrapper.style.height = "auto";
|
|
283
|
+
this.iframeWrapper.style.minHeight = IframeManager.MODAL_MIN_HEIGHT; // Start small for modal mode
|
|
284
|
+
// Apply initial wrapper styling
|
|
285
|
+
this.applyIframeWrapperStyling();
|
|
286
|
+
// Add shimmer loader for modal mode
|
|
287
|
+
this.showShimmerLoader(this.iframeWrapper, "white");
|
|
288
|
+
// Add close button for modal mode (initially hidden)
|
|
289
|
+
if (this.onClose) {
|
|
290
|
+
this.closeButton = createCloseButton(this.onClose);
|
|
291
|
+
this.contentWrapper.appendChild(this.closeButton);
|
|
292
|
+
}
|
|
293
|
+
// Add iframe to wrapper, then wrapper to content, then content to container
|
|
294
|
+
this.iframeWrapper.appendChild(this.iframe);
|
|
295
|
+
this.contentWrapper.appendChild(this.iframeWrapper);
|
|
296
|
+
this.container.appendChild(this.contentWrapper);
|
|
297
|
+
// Add click-to-close functionality after content is added
|
|
298
|
+
this.clickHandler = (e) => {
|
|
299
|
+
if (e.target === this.container) {
|
|
300
|
+
// Only close if clicking the backdrop, not the iframe content
|
|
301
|
+
this.onClose?.();
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
this.container.addEventListener("click", this.clickHandler);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Sets up the iframe for embedded display mode
|
|
308
|
+
*/
|
|
309
|
+
setupEmbeddedIframe() {
|
|
310
|
+
if (!this.iframe)
|
|
311
|
+
return;
|
|
312
|
+
// Embedded mode - create wrapper with border radius and overflow hidden
|
|
313
|
+
this.iframeWrapper = document.createElement("div");
|
|
314
|
+
this.iframeWrapper.style.position = "relative";
|
|
315
|
+
this.iframeWrapper.style.overflow = "hidden";
|
|
316
|
+
this.iframeWrapper.style.overflowX = "hidden";
|
|
317
|
+
this.iframeWrapper.style.overflowY = "hidden";
|
|
318
|
+
this.iframeWrapper.style.transition = "all 0.5s ease-in-out";
|
|
319
|
+
this.iframeWrapper.style.width = "100%";
|
|
320
|
+
this.iframeWrapper.style.height = "auto";
|
|
321
|
+
this.iframeWrapper.style.minHeight = IframeManager.EMBEDDED_MIN_HEIGHT; // Embedded mode minimum
|
|
322
|
+
// Apply initial wrapper styling
|
|
323
|
+
this.applyIframeWrapperStyling();
|
|
324
|
+
// Add shimmer loader for embedded mode with max width
|
|
325
|
+
this.showShimmerLoader(this.iframeWrapper, "white", IframeManager.MODAL_WIDTH);
|
|
326
|
+
// Add iframe to wrapper, then wrapper to container
|
|
327
|
+
this.iframeWrapper.appendChild(this.iframe);
|
|
328
|
+
this.container.appendChild(this.iframeWrapper);
|
|
329
|
+
}
|
|
330
|
+
showShimmerLoader(parentElement, backgroundColor = "white", maxWidth) {
|
|
331
|
+
if (!this.isIframeLoaded && !this.shimmerLoader) {
|
|
332
|
+
this.shimmerLoader = createIframeShimmerLoader(backgroundColor, maxWidth);
|
|
333
|
+
parentElement.appendChild(this.shimmerLoader);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
hideShimmerLoader() {
|
|
337
|
+
if (this.shimmerLoader && this.shimmerLoader.parentNode) {
|
|
338
|
+
// Add fade-out transition before removing
|
|
339
|
+
this.shimmerLoader.style.opacity = "0";
|
|
340
|
+
this.shimmerLoader.style.transition = `opacity ${IframeManager.TRANSITION_DURATION} ease`;
|
|
341
|
+
// Remove after transition completes
|
|
342
|
+
setTimeout(() => {
|
|
343
|
+
if (this.shimmerLoader && this.shimmerLoader.parentNode) {
|
|
344
|
+
this.shimmerLoader.parentNode.removeChild(this.shimmerLoader);
|
|
345
|
+
this.shimmerLoader = null;
|
|
346
|
+
}
|
|
347
|
+
}, parseInt(IframeManager.TRANSITION_DURATION));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
markIframeAsLoaded() {
|
|
351
|
+
if (!this.isIframeLoaded) {
|
|
352
|
+
this.isIframeLoaded = true;
|
|
353
|
+
this.hideShimmerLoader();
|
|
354
|
+
this.updateWrapperForLoadedContent();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
updateWrapperForLoadedContent() {
|
|
358
|
+
// Fade in the iframe
|
|
359
|
+
if (this.iframe) {
|
|
360
|
+
this.iframe.style.opacity = "1";
|
|
361
|
+
}
|
|
362
|
+
// Show close button for modal mode when content is loaded
|
|
363
|
+
if (this.displayMode === "modal" && this.closeButton) {
|
|
364
|
+
this.closeButton.style.opacity = "1";
|
|
365
|
+
}
|
|
366
|
+
// Find the iframe wrapper and update its styles for loaded content
|
|
367
|
+
const iframeWrapper = this.iframe?.parentElement;
|
|
368
|
+
if (iframeWrapper) {
|
|
369
|
+
// Remove minimum height constraints to allow natural sizing
|
|
370
|
+
if (this.displayMode === "modal") {
|
|
371
|
+
iframeWrapper.style.minHeight = "auto";
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
// For embedded mode, keep some minimum but allow natural growth
|
|
375
|
+
iframeWrapper.style.minHeight = "auto";
|
|
376
|
+
}
|
|
377
|
+
// Add border radius transition for loaded state (matching React implementation)
|
|
378
|
+
iframeWrapper.style.borderRadius = IframeManager.BORDER_RADIUS;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
hide() {
|
|
382
|
+
if (this.displayMode === "modal") {
|
|
383
|
+
// Add fade-out transition for modal backdrop
|
|
384
|
+
this.container.style.opacity = "0";
|
|
385
|
+
this.container.style.transition = `opacity ${IframeManager.TRANSITION_DURATION} ease`;
|
|
386
|
+
// Hide after transition completes
|
|
387
|
+
setTimeout(() => {
|
|
388
|
+
this.container.style.display = "none";
|
|
389
|
+
}, parseInt(IframeManager.TRANSITION_DURATION));
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
// For embedded mode, just hide immediately
|
|
393
|
+
this.container.style.display = "none";
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
cleanupIframeOnly() {
|
|
397
|
+
if (this.resizer) {
|
|
398
|
+
this.resizer.cleanup();
|
|
399
|
+
this.resizer = null;
|
|
400
|
+
}
|
|
401
|
+
// Clean up shimmer loader
|
|
402
|
+
this.hideShimmerLoader();
|
|
403
|
+
// Clean up close button
|
|
404
|
+
if (this.closeButton && this.closeButton.parentNode) {
|
|
405
|
+
this.closeButton.parentNode.removeChild(this.closeButton);
|
|
406
|
+
this.closeButton = null;
|
|
407
|
+
}
|
|
408
|
+
// Clean up message listener
|
|
409
|
+
if (this.iframeReadyMessageListener) {
|
|
410
|
+
window.removeEventListener("message", this.iframeReadyMessageListener);
|
|
411
|
+
this.iframeReadyMessageListener = undefined;
|
|
412
|
+
}
|
|
413
|
+
if (this.iframe && this.iframe.parentNode) {
|
|
414
|
+
this.iframe.parentNode.removeChild(this.iframe);
|
|
415
|
+
this.iframe = null;
|
|
416
|
+
}
|
|
417
|
+
// Reset loading state
|
|
418
|
+
this.isIframeLoaded = false;
|
|
419
|
+
// Clear wrapper references
|
|
420
|
+
this.contentWrapper = null;
|
|
421
|
+
this.iframeWrapper = null;
|
|
422
|
+
// Clear container content but DON'T reset styles for modal mode
|
|
423
|
+
this.container.innerHTML = "";
|
|
424
|
+
}
|
|
425
|
+
cleanup() {
|
|
426
|
+
if (this.resizer) {
|
|
427
|
+
this.resizer.cleanup();
|
|
428
|
+
this.resizer = null;
|
|
429
|
+
}
|
|
430
|
+
// Clean up shimmer loader
|
|
431
|
+
this.hideShimmerLoader();
|
|
432
|
+
// Clean up close button
|
|
433
|
+
if (this.closeButton && this.closeButton.parentNode) {
|
|
434
|
+
this.closeButton.parentNode.removeChild(this.closeButton);
|
|
435
|
+
this.closeButton = null;
|
|
436
|
+
}
|
|
437
|
+
// Clean up message listener
|
|
438
|
+
if (this.iframeReadyMessageListener) {
|
|
439
|
+
window.removeEventListener("message", this.iframeReadyMessageListener);
|
|
440
|
+
this.iframeReadyMessageListener = undefined;
|
|
441
|
+
}
|
|
442
|
+
if (this.iframe && this.iframe.parentNode) {
|
|
443
|
+
this.iframe.parentNode.removeChild(this.iframe);
|
|
444
|
+
this.iframe = null;
|
|
445
|
+
}
|
|
446
|
+
// Reset loading state
|
|
447
|
+
this.isIframeLoaded = false;
|
|
448
|
+
// Remove keyboard event listener
|
|
449
|
+
if (this.keydownHandler) {
|
|
450
|
+
window.removeEventListener("keydown", this.keydownHandler);
|
|
451
|
+
this.keydownHandler = undefined;
|
|
452
|
+
}
|
|
453
|
+
// Remove click event listener
|
|
454
|
+
if (this.clickHandler) {
|
|
455
|
+
this.container.removeEventListener("click", this.clickHandler);
|
|
456
|
+
this.clickHandler = undefined;
|
|
457
|
+
}
|
|
458
|
+
// Clear wrapper references
|
|
459
|
+
this.contentWrapper = null;
|
|
460
|
+
this.iframeWrapper = null;
|
|
461
|
+
// Clear container content and reset styles
|
|
462
|
+
this.container.innerHTML = "";
|
|
463
|
+
if (this.displayMode === "modal") {
|
|
464
|
+
// Reset all modal-specific styles to ensure clean state
|
|
465
|
+
this.resetModalStyles();
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Resets all modal-specific styles by removing CSS properties that were set with !important
|
|
470
|
+
*/
|
|
471
|
+
resetModalStyles() {
|
|
472
|
+
const modalStyleProperties = [
|
|
473
|
+
"position",
|
|
474
|
+
"left",
|
|
475
|
+
"top",
|
|
476
|
+
"width",
|
|
477
|
+
"height",
|
|
478
|
+
"z-index",
|
|
479
|
+
"display",
|
|
480
|
+
"align-items",
|
|
481
|
+
"justify-content",
|
|
482
|
+
"background-color",
|
|
483
|
+
"backdrop-filter",
|
|
484
|
+
"opacity",
|
|
485
|
+
"transition",
|
|
486
|
+
"margin",
|
|
487
|
+
"padding",
|
|
488
|
+
"border",
|
|
489
|
+
"border-radius",
|
|
490
|
+
"min-height",
|
|
491
|
+
];
|
|
492
|
+
modalStyleProperties.forEach((property) => {
|
|
493
|
+
this.container.style.removeProperty(property);
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
resize() {
|
|
497
|
+
if (this.resizer) {
|
|
498
|
+
this.resizer.resize();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Force show the shimmer loader and hide iframe content
|
|
503
|
+
* Used when we want to mask non-login content
|
|
504
|
+
*/
|
|
505
|
+
forceShowLoader() {
|
|
506
|
+
// Reset loading state to show shimmer
|
|
507
|
+
this.isIframeLoaded = false;
|
|
508
|
+
// Hide iframe content
|
|
509
|
+
if (this.iframe) {
|
|
510
|
+
this.iframe.style.opacity = "0";
|
|
511
|
+
}
|
|
512
|
+
// Hide close button
|
|
513
|
+
if (this.displayMode === "modal" && this.closeButton) {
|
|
514
|
+
this.closeButton.style.opacity = "0";
|
|
515
|
+
}
|
|
516
|
+
// Hide visual decorations (borders, shadows) when masking content
|
|
517
|
+
if (this.displayMode === "modal") {
|
|
518
|
+
this.removeModalVisualStyling();
|
|
519
|
+
}
|
|
520
|
+
this.removeIframeWrapperStyling();
|
|
521
|
+
// Show shimmer loader
|
|
522
|
+
if (this.iframeWrapper && !this.shimmerLoader) {
|
|
523
|
+
this.showShimmerLoader(this.iframeWrapper, "white", this.displayMode === "embedded" ? IframeManager.MODAL_WIDTH : undefined);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Force hide the shimmer loader and show iframe content
|
|
528
|
+
* Used when we want to show the login app
|
|
529
|
+
*/
|
|
530
|
+
forceHideLoader() {
|
|
531
|
+
// Restore visual decorations when showing content
|
|
532
|
+
if (this.displayMode === "modal") {
|
|
533
|
+
this.applyModalVisualStyling();
|
|
534
|
+
}
|
|
535
|
+
this.applyIframeWrapperStyling();
|
|
536
|
+
this.markIframeAsLoaded();
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Applies visual styling (borders, shadows) to modal content wrapper
|
|
540
|
+
*/
|
|
541
|
+
applyModalVisualStyling() {
|
|
542
|
+
if (!this.contentWrapper)
|
|
543
|
+
return;
|
|
544
|
+
this.contentWrapper.style.borderRadius = IframeManager.BORDER_RADIUS;
|
|
545
|
+
this.contentWrapper.style.boxShadow =
|
|
546
|
+
"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)";
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Removes visual styling (borders, shadows) from modal content wrapper
|
|
550
|
+
*/
|
|
551
|
+
removeModalVisualStyling() {
|
|
552
|
+
if (!this.contentWrapper)
|
|
553
|
+
return;
|
|
554
|
+
this.contentWrapper.style.borderRadius = "0";
|
|
555
|
+
this.contentWrapper.style.boxShadow = "none";
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Applies visual styling (borders) to iframe wrapper
|
|
559
|
+
*/
|
|
560
|
+
applyIframeWrapperStyling() {
|
|
561
|
+
if (!this.iframeWrapper)
|
|
562
|
+
return;
|
|
563
|
+
this.iframeWrapper.style.borderRadius = IframeManager.BORDER_RADIUS;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Removes visual styling (borders) from iframe wrapper
|
|
567
|
+
*/
|
|
568
|
+
removeIframeWrapperStyling() {
|
|
569
|
+
if (!this.iframeWrapper)
|
|
570
|
+
return;
|
|
571
|
+
this.iframeWrapper.style.borderRadius = "0";
|
|
572
|
+
}
|
|
573
|
+
showSubtlePopupFallbackIndicator() {
|
|
574
|
+
if (!this.iframeWrapper && !this.contentWrapper) {
|
|
575
|
+
this.logger.warn("Cannot show popup fallback indicator, no iframe wrapper or content wrapper found.");
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const targetElement = this.iframeWrapper || this.contentWrapper;
|
|
579
|
+
if (targetElement) {
|
|
580
|
+
this.logger.debug("Showing popup fallback overlay.");
|
|
581
|
+
// Create overlay element
|
|
582
|
+
const overlay = document.createElement("div");
|
|
583
|
+
overlay.id = "civic-popup-fallback-overlay";
|
|
584
|
+
overlay.style.cssText = `
|
|
585
|
+
position: absolute;
|
|
586
|
+
top: 0;
|
|
587
|
+
left: 0;
|
|
588
|
+
right: 0;
|
|
589
|
+
bottom: 0;
|
|
590
|
+
background: transparent;
|
|
591
|
+
display: flex;
|
|
592
|
+
flex-direction: column;
|
|
593
|
+
align-items: center;
|
|
594
|
+
justify-content: center;
|
|
595
|
+
z-index: 1000;
|
|
596
|
+
border-radius: ${IframeManager.BORDER_RADIUS};
|
|
597
|
+
backdrop-filter: blur(.4px);
|
|
598
|
+
opacity: 1;
|
|
599
|
+
`;
|
|
600
|
+
// Add overlay to target element
|
|
601
|
+
targetElement.style.position = "relative";
|
|
602
|
+
targetElement.appendChild(overlay);
|
|
603
|
+
// Remove overlay quickly
|
|
604
|
+
setTimeout(() => {
|
|
605
|
+
if (overlay.parentNode) {
|
|
606
|
+
overlay.parentNode.removeChild(overlay);
|
|
607
|
+
}
|
|
608
|
+
this.logger.debug("Popup fallback overlay removed.");
|
|
609
|
+
}, 500); // Remove after 500ms
|
|
610
|
+
this.logger.debug("Popup fallback overlay displayed.");
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
//# sourceMappingURL=IframeManager.js.map
|