@yuno-payments/dashboard-embed-sdk 1.4.0-BETA.9 → 1.4.0
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 +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +33 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ destroyDashboard();
|
|
|
70
70
|
| `token` | `string` | No | JWT auth token — sent via PostMessage after the iframe is ready |
|
|
71
71
|
| `theme` | `DashboardTheme` | No | Initial theme configuration |
|
|
72
72
|
| `lang` | `string` | No | Language code (default: `"en"`) |
|
|
73
|
-
| `path` | `string` | No | Initial navigation path (default: `"/"`).
|
|
73
|
+
| `path` | `string` | No | Initial navigation path (default: `"/"`). Any query string you pass is stripped — only the SDK sets the iframe query params (embed/theme/lang/test) |
|
|
74
74
|
| `testMode` | `boolean` | No | When set, the dashboard mounts in test/sandbox mode (`true`) or live mode (`false`). Omit to inherit the dashboard's own default. See [Test mode](#test-mode) |
|
|
75
75
|
| `onReady` | `() => void` | No | Callback invoked when the dashboard is fully loaded and authenticated |
|
|
76
76
|
| `onSessionExpired` | `() => void` | No | Callback invoked when the embedded session has expired. The host should re-authenticate and call `setToken(newToken)` to resume. |
|
|
@@ -298,7 +298,7 @@ modal would appear centered to the iframe panel, not the browser viewport. To fi
|
|
|
298
298
|
the embedded dashboard signals the SDK when an overlay opens or closes
|
|
299
299
|
(`embed-event` with `domain: "ui"`, `action: "overlay-open" | "overlay-close"`), and the
|
|
300
300
|
**SDK expands the iframe to the full viewport** (`position: fixed; inset: 0`, top
|
|
301
|
-
`z-index`) for as long as any overlay is open, then restores it
|
|
301
|
+
`z-index`) for as long as any overlay is open, then restores it. This is automatic — no
|
|
302
302
|
host code required. Signals are ref-counted, so stacked overlays are handled correctly.
|
|
303
303
|
|
|
304
304
|
While expanded the iframe is **transparent**, and the SDK captures the iframe's prior
|
package/dist/index.d.ts
CHANGED
|
@@ -93,7 +93,6 @@ declare class Dashboard {
|
|
|
93
93
|
private overlayCount;
|
|
94
94
|
private savedIframeCss;
|
|
95
95
|
private overlayRevealTimer;
|
|
96
|
-
private overlayCloseTimer;
|
|
97
96
|
constructor(config: DashboardConfig);
|
|
98
97
|
setTheme(theme: DashboardTheme): void;
|
|
99
98
|
setLang(lang: string): void;
|
|
@@ -106,6 +105,7 @@ declare class Dashboard {
|
|
|
106
105
|
private handleOverlaySignal;
|
|
107
106
|
private expandToViewport;
|
|
108
107
|
private restoreIframe;
|
|
108
|
+
private scheduleReveal;
|
|
109
109
|
private post;
|
|
110
110
|
private flush;
|
|
111
111
|
private onReady;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
// src/dashboard.ts
|
|
2
2
|
var EMBED_EVENT_MESSAGE_TYPE = "yuno-dashboard:embed-event";
|
|
3
3
|
var HOST_CAPABILITIES_MESSAGE_TYPE = "yuno-dashboard:host-capabilities";
|
|
4
|
+
var OVERLAY_FADE = "opacity 0.15s ease";
|
|
5
|
+
var OVERLAY_REVEAL_DELAY_MS = 100;
|
|
6
|
+
function stripQueryParams(path) {
|
|
7
|
+
const queryIndex = path.indexOf("?");
|
|
8
|
+
if (queryIndex < 0) return path;
|
|
9
|
+
const hashIndex = path.indexOf("#", queryIndex);
|
|
10
|
+
const hash = hashIndex >= 0 ? path.slice(hashIndex) : "";
|
|
11
|
+
return path.slice(0, queryIndex) + hash;
|
|
12
|
+
}
|
|
4
13
|
function buildCapabilities(events) {
|
|
5
14
|
const capabilities = {};
|
|
6
15
|
if (!events) return capabilities;
|
|
@@ -28,7 +37,6 @@ var Dashboard = class {
|
|
|
28
37
|
this.overlayCount = 0;
|
|
29
38
|
this.savedIframeCss = null;
|
|
30
39
|
this.overlayRevealTimer = null;
|
|
31
|
-
this.overlayCloseTimer = null;
|
|
32
40
|
this.baseUrl = config.baseUrl;
|
|
33
41
|
this.initialTheme = config.theme;
|
|
34
42
|
this.initialLang = config.lang;
|
|
@@ -57,7 +65,10 @@ var Dashboard = class {
|
|
|
57
65
|
this.wrapper.appendChild(this.overlay);
|
|
58
66
|
this.messageHandler = (e) => {
|
|
59
67
|
if (e.origin !== this.baseUrl) return;
|
|
60
|
-
if (this.autoHeight &&
|
|
68
|
+
if (this.autoHeight && // Ignore content-height resizes while an overlay is open — the iframe is
|
|
69
|
+
// pinned full-viewport, and applying a height would break the overlay
|
|
70
|
+
// and leave a stale wrapper height after it closes.
|
|
71
|
+
this.overlayCount === 0 && e.data?.action === "resize" && typeof e.data?.height === "number") {
|
|
61
72
|
this.iframe.style.height = `${e.data.height}px`;
|
|
62
73
|
this.wrapper.style.height = `${e.data.height}px`;
|
|
63
74
|
return;
|
|
@@ -105,7 +116,7 @@ var Dashboard = class {
|
|
|
105
116
|
this.post({ action: "setToken", token });
|
|
106
117
|
}
|
|
107
118
|
navigate(path) {
|
|
108
|
-
this.post({ action: "navigate", path });
|
|
119
|
+
this.post({ action: "navigate", path: stripQueryParams(path) });
|
|
109
120
|
}
|
|
110
121
|
setTestMode(enabled) {
|
|
111
122
|
this.post({ action: "setTestMode", testMode: enabled });
|
|
@@ -121,10 +132,6 @@ var Dashboard = class {
|
|
|
121
132
|
clearTimeout(this.overlayRevealTimer);
|
|
122
133
|
this.overlayRevealTimer = null;
|
|
123
134
|
}
|
|
124
|
-
if (this.overlayCloseTimer) {
|
|
125
|
-
clearTimeout(this.overlayCloseTimer);
|
|
126
|
-
this.overlayCloseTimer = null;
|
|
127
|
-
}
|
|
128
135
|
}
|
|
129
136
|
// Runs a host callback, swallowing sync throws and async rejections so a
|
|
130
137
|
// misbehaving host handler can never break the SDK's message loop.
|
|
@@ -164,22 +171,12 @@ var Dashboard = class {
|
|
|
164
171
|
// once the last overlay closes (ref-counted for stacked overlays).
|
|
165
172
|
handleOverlaySignal(action) {
|
|
166
173
|
if (action === "overlay-open") {
|
|
167
|
-
if (this.
|
|
168
|
-
clearTimeout(this.overlayCloseTimer);
|
|
169
|
-
this.overlayCloseTimer = null;
|
|
170
|
-
}
|
|
171
|
-
if (this.savedIframeCss === null) this.expandToViewport();
|
|
174
|
+
if (this.overlayCount === 0) this.expandToViewport();
|
|
172
175
|
this.overlayCount += 1;
|
|
173
176
|
} else if (action === "overlay-close") {
|
|
174
177
|
if (this.overlayCount === 0) return;
|
|
175
178
|
this.overlayCount -= 1;
|
|
176
|
-
if (this.overlayCount === 0)
|
|
177
|
-
if (this.overlayCloseTimer) clearTimeout(this.overlayCloseTimer);
|
|
178
|
-
this.overlayCloseTimer = setTimeout(() => {
|
|
179
|
-
this.overlayCloseTimer = null;
|
|
180
|
-
this.restoreIframe();
|
|
181
|
-
}, 250);
|
|
182
|
-
}
|
|
179
|
+
if (this.overlayCount === 0) this.restoreIframe();
|
|
183
180
|
}
|
|
184
181
|
}
|
|
185
182
|
expandToViewport() {
|
|
@@ -200,30 +197,30 @@ var Dashboard = class {
|
|
|
200
197
|
// Transparent so the host page shows through wherever the dashboard
|
|
201
198
|
// doesn't paint (the offset regions).
|
|
202
199
|
background: "transparent",
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
// animate the hide and leave the reflow visible mid-fade) and fade it
|
|
207
|
-
// back in once the inset has settled.
|
|
200
|
+
// Hide instantly (no transition — a transition would animate the hide and
|
|
201
|
+
// leave the reflow visible mid-fade), then fade back in once the inset has
|
|
202
|
+
// settled, masking the cross-document expand→inset reflow.
|
|
208
203
|
transition: "none",
|
|
209
204
|
opacity: "0"
|
|
210
205
|
});
|
|
211
206
|
this.post({ action: "overlay-mode", active: true, offset });
|
|
212
|
-
|
|
213
|
-
this.overlayRevealTimer = setTimeout(() => {
|
|
214
|
-
this.iframe.style.transition = "opacity 0.15s ease";
|
|
215
|
-
this.iframe.style.opacity = "1";
|
|
216
|
-
this.overlayRevealTimer = null;
|
|
217
|
-
}, 100);
|
|
207
|
+
this.scheduleReveal();
|
|
218
208
|
}
|
|
219
209
|
restoreIframe() {
|
|
220
|
-
if (this.overlayRevealTimer) {
|
|
221
|
-
clearTimeout(this.overlayRevealTimer);
|
|
222
|
-
this.overlayRevealTimer = null;
|
|
223
|
-
}
|
|
224
210
|
this.iframe.style.cssText = this.savedIframeCss ?? "";
|
|
225
211
|
this.savedIframeCss = null;
|
|
212
|
+
this.iframe.style.transition = "none";
|
|
213
|
+
this.iframe.style.opacity = "0";
|
|
226
214
|
this.post({ action: "overlay-mode", active: false });
|
|
215
|
+
this.scheduleReveal();
|
|
216
|
+
}
|
|
217
|
+
scheduleReveal() {
|
|
218
|
+
if (this.overlayRevealTimer) clearTimeout(this.overlayRevealTimer);
|
|
219
|
+
this.overlayRevealTimer = setTimeout(() => {
|
|
220
|
+
this.iframe.style.transition = OVERLAY_FADE;
|
|
221
|
+
this.iframe.style.opacity = "1";
|
|
222
|
+
this.overlayRevealTimer = null;
|
|
223
|
+
}, OVERLAY_REVEAL_DELAY_MS);
|
|
227
224
|
}
|
|
228
225
|
post(message) {
|
|
229
226
|
if (this.ready && this.iframe.contentWindow) {
|
|
@@ -297,7 +294,8 @@ var Dashboard = class {
|
|
|
297
294
|
if (this.initialTestMode !== void 0) {
|
|
298
295
|
params.set("test", String(this.initialTestMode));
|
|
299
296
|
}
|
|
300
|
-
const
|
|
297
|
+
const rawPath = !this.initialPath || this.initialPath === "/" ? "/" : this.initialPath;
|
|
298
|
+
const path = stripQueryParams(rawPath);
|
|
301
299
|
return `${this.baseUrl}${path}?${params.toString()}`;
|
|
302
300
|
}
|
|
303
301
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dashboard.ts","../src/singleton.ts"],"sourcesContent":["import type { DashboardTheme, DashboardConfig, DashboardEvents } from \"./types\";\n\nconst EMBED_EVENT_MESSAGE_TYPE = \"yuno-dashboard:embed-event\";\nconst HOST_CAPABILITIES_MESSAGE_TYPE = \"yuno-dashboard:host-capabilities\";\n\ntype EmbedEventHandler = (event: {\n status?: unknown;\n payload?: unknown;\n}) => void | Promise<void>;\n\n// Build the capability map the dashboard reads to decide which actions the\n// host owns: { connections: { created: true }, ... } for every provided handler.\nfunction buildCapabilities(\n events?: DashboardEvents,\n): Record<string, Record<string, boolean>> {\n const capabilities: Record<string, Record<string, boolean>> = {};\n if (!events) return capabilities;\n const groups = events as Record<string, Record<string, unknown> | undefined>;\n for (const domain of Object.keys(groups)) {\n const group = groups[domain];\n if (!group) continue;\n for (const action of Object.keys(group)) {\n if (typeof group[action] === \"function\") {\n capabilities[domain] = capabilities[domain] ?? {};\n capabilities[domain][action] = true;\n }\n }\n }\n return capabilities;\n}\n\nexport class Dashboard {\n private iframe: HTMLIFrameElement;\n private wrapper: HTMLDivElement;\n private overlay: HTMLElement | null = null;\n private baseUrl: string;\n private ready = false;\n private queue: Record<string, unknown>[] = [];\n private messageHandler: (e: MessageEvent) => void;\n private initialTheme?: DashboardTheme;\n private initialLang?: string;\n private initialPath: string;\n private initialTestMode?: boolean;\n private token?: string;\n private onReadyCallback?: () => void;\n private onSessionExpiredCallback?: () => void | Promise<void>;\n private events?: DashboardEvents;\n private autoHeight: boolean;\n // Overlay (modal/drawer) mode: while >0 the iframe is expanded to the full\n // viewport so the dashboard's overlay centers to the host page, not the\n // iframe. Ref-counted to survive stacked overlays.\n private overlayCount = 0;\n private savedIframeCss: string | null = null;\n private overlayRevealTimer: ReturnType<typeof setTimeout> | null = null;\n private overlayCloseTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(config: DashboardConfig) {\n this.baseUrl = config.baseUrl;\n this.initialTheme = config.theme;\n this.initialLang = config.lang;\n this.initialPath = config.path ?? \"/\";\n this.initialTestMode = config.testMode;\n this.token = config.token;\n this.onReadyCallback = config.onReady;\n this.onSessionExpiredCallback = config.onSessionExpired;\n this.events = config.events;\n this.autoHeight = config.autoHeight ?? false;\n\n this.wrapper = document.createElement(\"div\");\n this.wrapper.style.position = \"relative\";\n this.wrapper.style.width = \"100%\";\n this.wrapper.style.height = \"100%\";\n\n this.iframe = document.createElement(\"iframe\");\n this.iframe.style.width = \"100%\";\n this.iframe.style.height = \"100%\";\n this.iframe.style.border = \"none\";\n this.iframe.title = \"Yuno Dashboard\";\n this.iframe.src = this.buildSrc();\n\n this.wrapper.appendChild(this.iframe);\n\n this.overlay = config.loading instanceof HTMLElement\n ? config.loading\n : this.createDefaultOverlay();\n this.overlay.style.position = \"absolute\";\n this.overlay.style.inset = \"0\";\n this.overlay.style.zIndex = \"1\";\n this.wrapper.appendChild(this.overlay);\n\n this.messageHandler = (e: MessageEvent) => {\n if (e.origin !== this.baseUrl) return;\n if (\n this.autoHeight &&\n e.data?.action === \"resize\" &&\n typeof e.data?.height === \"number\"\n ) {\n this.iframe.style.height = `${e.data.height}px`;\n this.wrapper.style.height = `${e.data.height}px`;\n return;\n }\n if (e.data?.type === \"yuno-dashboard:session-expired\") {\n this.invokeCallback(\"onSessionExpired\", () =>\n this.onSessionExpiredCallback?.(),\n );\n return;\n }\n if (e.data?.type === EMBED_EVENT_MESSAGE_TYPE) {\n this.dispatchEmbedEvent(e.data);\n return;\n }\n if (e.data?.action === \"ready\") {\n this.onReady();\n }\n if (e.data?.action === \"tokenApplied\") {\n this.onTokenApplied();\n }\n };\n window.addEventListener(\"message\", this.messageHandler);\n\n config.container.appendChild(this.wrapper);\n }\n\n setTheme(theme: DashboardTheme): void {\n if (theme.tokens || theme.typography) {\n const themePayload: Record<string, unknown> = {};\n if (theme.tokens) themePayload.colors = theme.tokens;\n if (theme.typography) themePayload.typography = theme.typography;\n this.post({ action: \"setTheme\", theme: themePayload });\n }\n if (theme.mode) {\n this.post({ action: \"setMode\", mode: theme.mode });\n }\n if (theme.styles !== undefined) {\n this.post({ action: \"setExternalStyles\", css: theme.styles });\n }\n }\n\n setLang(lang: string): void {\n this.post({ action: \"setLang\", lang });\n }\n\n setToken(token: string): void {\n this.token = token;\n this.post({ action: \"setToken\", token });\n }\n\n navigate(path: string): void {\n this.post({ action: \"navigate\", path });\n }\n\n setTestMode(enabled: boolean): void {\n this.post({ action: \"setTestMode\", testMode: enabled });\n }\n\n destroy(): void {\n window.removeEventListener(\"message\", this.messageHandler);\n this.wrapper.remove();\n this.queue = [];\n this.ready = false;\n this.overlayCount = 0;\n this.savedIframeCss = null;\n if (this.overlayRevealTimer) {\n clearTimeout(this.overlayRevealTimer);\n this.overlayRevealTimer = null;\n }\n if (this.overlayCloseTimer) {\n clearTimeout(this.overlayCloseTimer);\n this.overlayCloseTimer = null;\n }\n }\n\n // Runs a host callback, swallowing sync throws and async rejections so a\n // misbehaving host handler can never break the SDK's message loop.\n private invokeCallback(\n name: string,\n run: () => void | Promise<void> | undefined,\n ): void {\n try {\n const result = run();\n if (result && typeof (result as Promise<unknown>).catch === \"function\") {\n (result as Promise<unknown>).catch((err) =>\n // eslint-disable-next-line no-console\n console.warn(`[yuno-dashboard-sdk] ${name} async callback rejected`, err),\n );\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn(`[yuno-dashboard-sdk] ${name} callback threw`, err);\n }\n }\n\n // Routes an embed-event envelope to the host's events[domain][action] handler.\n private dispatchEmbedEvent(data: Record<string, unknown>): void {\n const domain = data?.domain;\n const action = data?.action;\n if (typeof domain !== \"string\" || typeof action !== \"string\") return;\n\n // UI signals are handled by the SDK itself (iframe sizing), not routed to\n // a host callback.\n if (domain === \"ui\") {\n this.handleOverlaySignal(action);\n return;\n }\n\n const groups = this.events as\n | Record<string, Record<string, EmbedEventHandler | undefined> | undefined>\n | undefined;\n const handler = groups?.[domain]?.[action];\n if (!handler) return;\n\n this.invokeCallback(`events.${domain}.${action}`, () =>\n handler({ status: data?.status, payload: data?.payload }),\n );\n }\n\n // Expands the iframe to the full viewport on the first open and restores it\n // once the last overlay closes (ref-counted for stacked overlays).\n private handleOverlaySignal(action: string): void {\n if (action === \"overlay-open\") {\n // Re-opening during the close delay: cancel the pending restore and keep\n // the iframe expanded (don't re-expand — it's already expanded).\n if (this.overlayCloseTimer) {\n clearTimeout(this.overlayCloseTimer);\n this.overlayCloseTimer = null;\n }\n if (this.savedIframeCss === null) this.expandToViewport();\n this.overlayCount += 1;\n } else if (action === \"overlay-close\") {\n if (this.overlayCount === 0) return;\n this.overlayCount -= 1;\n if (this.overlayCount === 0) {\n // Delay the restore so the overlay finishes its close animation before\n // the iframe/content layout reverts — otherwise the modal shifts as\n // the offset is removed mid-disappear.\n if (this.overlayCloseTimer) clearTimeout(this.overlayCloseTimer);\n this.overlayCloseTimer = setTimeout(() => {\n this.overlayCloseTimer = null;\n this.restoreIframe();\n }, 250);\n }\n }\n }\n\n private expandToViewport(): void {\n // Capture where the iframe sits (its panel position) BEFORE expanding —\n // that's the offset of the host's chrome around it. The dashboard uses it\n // to leave those regions transparent so the host chrome shows through.\n const rect = this.iframe.getBoundingClientRect();\n const offset = {\n top: Math.max(0, Math.round(rect.top)),\n left: Math.max(0, Math.round(rect.left)),\n right: Math.max(0, Math.round(window.innerWidth - rect.right)),\n bottom: Math.max(0, Math.round(window.innerHeight - rect.bottom)),\n };\n\n this.savedIframeCss = this.iframe.style.cssText;\n Object.assign(this.iframe.style, {\n position: \"fixed\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n zIndex: \"2147483647\",\n // Transparent so the host page shows through wherever the dashboard\n // doesn't paint (the offset regions).\n background: \"transparent\",\n // Mask the cross-document reflow: expanding the iframe and the\n // dashboard insetting its content happen a frame apart, which flickers.\n // Hide the iframe INSTANTLY (no transition here — a transition would\n // animate the hide and leave the reflow visible mid-fade) and fade it\n // back in once the inset has settled.\n transition: \"none\",\n opacity: \"0\",\n });\n this.post({ action: \"overlay-mode\", active: true, offset });\n if (this.overlayRevealTimer) clearTimeout(this.overlayRevealTimer);\n this.overlayRevealTimer = setTimeout(() => {\n this.iframe.style.transition = \"opacity 0.15s ease\";\n this.iframe.style.opacity = \"1\";\n this.overlayRevealTimer = null;\n }, 100);\n }\n\n private restoreIframe(): void {\n if (this.overlayRevealTimer) {\n clearTimeout(this.overlayRevealTimer);\n this.overlayRevealTimer = null;\n }\n this.iframe.style.cssText = this.savedIframeCss ?? \"\";\n this.savedIframeCss = null;\n this.post({ action: \"overlay-mode\", active: false });\n }\n\n private post(message: Record<string, unknown>): void {\n if (this.ready && this.iframe.contentWindow) {\n this.iframe.contentWindow.postMessage(message, this.baseUrl);\n } else {\n this.queue.push(message);\n }\n }\n\n private flush(): void {\n const pending = this.queue.splice(0);\n for (const message of pending) {\n if (this.iframe.contentWindow) {\n this.iframe.contentWindow.postMessage(message, this.baseUrl);\n }\n }\n }\n\n private onReady(): void {\n this.ready = true;\n const capabilities = buildCapabilities(this.events);\n if (Object.keys(capabilities).length > 0) {\n this.post({ type: HOST_CAPABILITIES_MESSAGE_TYPE, capabilities });\n }\n if (this.token) {\n this.post({ action: \"setToken\", token: this.token });\n }\n if (this.initialTheme) {\n this.setTheme(this.initialTheme);\n }\n if (this.initialLang) {\n this.setLang(this.initialLang);\n }\n this.flush();\n }\n\n private onTokenApplied(): void {\n setTimeout(() => {\n this.hideOverlay();\n this.onReadyCallback?.();\n }, 1000);\n }\n\n private hideOverlay(): void {\n if (!this.overlay) return;\n this.overlay.style.transition = \"opacity 0.3s\";\n this.overlay.style.opacity = \"0\";\n setTimeout(() => this.overlay?.remove(), 300);\n }\n\n private createDefaultOverlay(): HTMLElement {\n const overlay = document.createElement(\"div\");\n overlay.style.background = \"#ffffff\";\n overlay.style.display = \"flex\";\n overlay.style.alignItems = \"center\";\n overlay.style.justifyContent = \"center\";\n\n const spinner = document.createElement(\"div\");\n spinner.style.width = \"32px\";\n spinner.style.height = \"32px\";\n spinner.style.border = \"3px solid #e0e0e0\";\n spinner.style.borderTopColor = \"#666\";\n spinner.style.borderRadius = \"50%\";\n spinner.style.animation = \"yuno-spin 0.8s linear infinite\";\n\n const style = document.createElement(\"style\");\n style.textContent = \"@keyframes yuno-spin { to { transform: rotate(360deg); } }\";\n\n overlay.appendChild(style);\n overlay.appendChild(spinner);\n return overlay;\n }\n\n private buildSrc(): string {\n const params = new URLSearchParams({\n embed: \"true\",\n theme: this.initialTheme?.mode ?? \"light\",\n lang: this.initialLang ?? \"en\",\n });\n if (this.initialTestMode !== undefined) {\n params.set(\"test\", String(this.initialTestMode));\n }\n const path = !this.initialPath || this.initialPath === \"/\" ? \"/\" : this.initialPath;\n return `${this.baseUrl}${path}?${params.toString()}`;\n }\n}\n","import { Dashboard } from \"./dashboard\";\nimport type { DashboardConfig } from \"./types\";\n\nlet instance: Dashboard | null = null;\n\nexport function initDashboard(config: DashboardConfig): Dashboard {\n if (instance) {\n instance.destroy();\n }\n instance = new Dashboard(config);\n return instance;\n}\n\nexport function getDashboard(): Dashboard | null {\n return instance;\n}\n\nexport function destroyDashboard(): void {\n if (instance) {\n instance.destroy();\n instance = null;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/dashboard.ts","../src/singleton.ts"],"sourcesContent":["import type { DashboardTheme, DashboardConfig, DashboardEvents } from \"./types\";\n\nconst EMBED_EVENT_MESSAGE_TYPE = \"yuno-dashboard:embed-event\";\nconst HOST_CAPABILITIES_MESSAGE_TYPE = \"yuno-dashboard:host-capabilities\";\n\nconst OVERLAY_FADE = \"opacity 0.15s ease\";\nconst OVERLAY_REVEAL_DELAY_MS = 100;\n\n// Strips the query string from a host-provided path so only the SDK controls\n// the iframe URL's query params (embed/theme/lang/test). Any host query params\n// are dropped before processing; the pathname (and a trailing hash, if any) is\n// kept. Returns the path unchanged when it has no query string.\nfunction stripQueryParams(path: string): string {\n const queryIndex = path.indexOf(\"?\");\n if (queryIndex < 0) return path;\n\n const hashIndex = path.indexOf(\"#\", queryIndex);\n const hash = hashIndex >= 0 ? path.slice(hashIndex) : \"\";\n return path.slice(0, queryIndex) + hash;\n}\n\ntype EmbedEventHandler = (event: {\n status?: unknown;\n payload?: unknown;\n}) => void | Promise<void>;\n\n// Build the capability map the dashboard reads to decide which actions the\n// host owns: { connections: { created: true }, ... } for every provided handler.\nfunction buildCapabilities(\n events?: DashboardEvents,\n): Record<string, Record<string, boolean>> {\n const capabilities: Record<string, Record<string, boolean>> = {};\n if (!events) return capabilities;\n const groups = events as Record<string, Record<string, unknown> | undefined>;\n for (const domain of Object.keys(groups)) {\n const group = groups[domain];\n if (!group) continue;\n for (const action of Object.keys(group)) {\n if (typeof group[action] === \"function\") {\n capabilities[domain] = capabilities[domain] ?? {};\n capabilities[domain][action] = true;\n }\n }\n }\n return capabilities;\n}\n\nexport class Dashboard {\n private iframe: HTMLIFrameElement;\n private wrapper: HTMLDivElement;\n private overlay: HTMLElement | null = null;\n private baseUrl: string;\n private ready = false;\n private queue: Record<string, unknown>[] = [];\n private messageHandler: (e: MessageEvent) => void;\n private initialTheme?: DashboardTheme;\n private initialLang?: string;\n private initialPath: string;\n private initialTestMode?: boolean;\n private token?: string;\n private onReadyCallback?: () => void;\n private onSessionExpiredCallback?: () => void | Promise<void>;\n private events?: DashboardEvents;\n private autoHeight: boolean;\n // Overlay (modal/drawer) mode: while >0 the iframe is expanded to the full\n // viewport so the dashboard's overlay centers to the host page, not the\n // iframe. Ref-counted to survive stacked overlays.\n private overlayCount = 0;\n private savedIframeCss: string | null = null;\n private overlayRevealTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(config: DashboardConfig) {\n this.baseUrl = config.baseUrl;\n this.initialTheme = config.theme;\n this.initialLang = config.lang;\n this.initialPath = config.path ?? \"/\";\n this.initialTestMode = config.testMode;\n this.token = config.token;\n this.onReadyCallback = config.onReady;\n this.onSessionExpiredCallback = config.onSessionExpired;\n this.events = config.events;\n this.autoHeight = config.autoHeight ?? false;\n\n this.wrapper = document.createElement(\"div\");\n this.wrapper.style.position = \"relative\";\n this.wrapper.style.width = \"100%\";\n this.wrapper.style.height = \"100%\";\n\n this.iframe = document.createElement(\"iframe\");\n this.iframe.style.width = \"100%\";\n this.iframe.style.height = \"100%\";\n this.iframe.style.border = \"none\";\n this.iframe.title = \"Yuno Dashboard\";\n this.iframe.src = this.buildSrc();\n\n this.wrapper.appendChild(this.iframe);\n\n this.overlay = config.loading instanceof HTMLElement\n ? config.loading\n : this.createDefaultOverlay();\n this.overlay.style.position = \"absolute\";\n this.overlay.style.inset = \"0\";\n this.overlay.style.zIndex = \"1\";\n this.wrapper.appendChild(this.overlay);\n\n this.messageHandler = (e: MessageEvent) => {\n if (e.origin !== this.baseUrl) return;\n if (\n this.autoHeight &&\n // Ignore content-height resizes while an overlay is open — the iframe is\n // pinned full-viewport, and applying a height would break the overlay\n // and leave a stale wrapper height after it closes.\n this.overlayCount === 0 &&\n e.data?.action === \"resize\" &&\n typeof e.data?.height === \"number\"\n ) {\n this.iframe.style.height = `${e.data.height}px`;\n this.wrapper.style.height = `${e.data.height}px`;\n return;\n }\n if (e.data?.type === \"yuno-dashboard:session-expired\") {\n this.invokeCallback(\"onSessionExpired\", () =>\n this.onSessionExpiredCallback?.(),\n );\n return;\n }\n if (e.data?.type === EMBED_EVENT_MESSAGE_TYPE) {\n this.dispatchEmbedEvent(e.data);\n return;\n }\n if (e.data?.action === \"ready\") {\n this.onReady();\n }\n if (e.data?.action === \"tokenApplied\") {\n this.onTokenApplied();\n }\n };\n window.addEventListener(\"message\", this.messageHandler);\n\n config.container.appendChild(this.wrapper);\n }\n\n setTheme(theme: DashboardTheme): void {\n if (theme.tokens || theme.typography) {\n const themePayload: Record<string, unknown> = {};\n if (theme.tokens) themePayload.colors = theme.tokens;\n if (theme.typography) themePayload.typography = theme.typography;\n this.post({ action: \"setTheme\", theme: themePayload });\n }\n if (theme.mode) {\n this.post({ action: \"setMode\", mode: theme.mode });\n }\n if (theme.styles !== undefined) {\n this.post({ action: \"setExternalStyles\", css: theme.styles });\n }\n }\n\n setLang(lang: string): void {\n this.post({ action: \"setLang\", lang });\n }\n\n setToken(token: string): void {\n this.token = token;\n this.post({ action: \"setToken\", token });\n }\n\n navigate(path: string): void {\n this.post({ action: \"navigate\", path: stripQueryParams(path) });\n }\n\n setTestMode(enabled: boolean): void {\n this.post({ action: \"setTestMode\", testMode: enabled });\n }\n\n destroy(): void {\n window.removeEventListener(\"message\", this.messageHandler);\n this.wrapper.remove();\n this.queue = [];\n this.ready = false;\n this.overlayCount = 0;\n this.savedIframeCss = null;\n if (this.overlayRevealTimer) {\n clearTimeout(this.overlayRevealTimer);\n this.overlayRevealTimer = null;\n }\n }\n\n // Runs a host callback, swallowing sync throws and async rejections so a\n // misbehaving host handler can never break the SDK's message loop.\n private invokeCallback(\n name: string,\n run: () => void | Promise<void> | undefined,\n ): void {\n try {\n const result = run();\n if (result && typeof (result as Promise<unknown>).catch === \"function\") {\n (result as Promise<unknown>).catch((err) =>\n // eslint-disable-next-line no-console\n console.warn(`[yuno-dashboard-sdk] ${name} async callback rejected`, err),\n );\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn(`[yuno-dashboard-sdk] ${name} callback threw`, err);\n }\n }\n\n // Routes an embed-event envelope to the host's events[domain][action] handler.\n private dispatchEmbedEvent(data: Record<string, unknown>): void {\n const domain = data?.domain;\n const action = data?.action;\n if (typeof domain !== \"string\" || typeof action !== \"string\") return;\n\n // UI signals are handled by the SDK itself (iframe sizing), not routed to\n // a host callback.\n if (domain === \"ui\") {\n this.handleOverlaySignal(action);\n return;\n }\n\n const groups = this.events as\n | Record<string, Record<string, EmbedEventHandler | undefined> | undefined>\n | undefined;\n const handler = groups?.[domain]?.[action];\n if (!handler) return;\n\n this.invokeCallback(`events.${domain}.${action}`, () =>\n handler({ status: data?.status, payload: data?.payload }),\n );\n }\n\n // Expands the iframe to the full viewport on the first open and restores it\n // once the last overlay closes (ref-counted for stacked overlays).\n private handleOverlaySignal(action: string): void {\n if (action === \"overlay-open\") {\n if (this.overlayCount === 0) this.expandToViewport();\n this.overlayCount += 1;\n } else if (action === \"overlay-close\") {\n if (this.overlayCount === 0) return;\n this.overlayCount -= 1;\n if (this.overlayCount === 0) this.restoreIframe();\n }\n }\n\n private expandToViewport(): void {\n // Capture where the iframe sits (its panel position) BEFORE expanding —\n // that's the offset of the host's chrome around it. The dashboard uses it\n // to leave those regions transparent so the host chrome shows through.\n const rect = this.iframe.getBoundingClientRect();\n const offset = {\n top: Math.max(0, Math.round(rect.top)),\n left: Math.max(0, Math.round(rect.left)),\n right: Math.max(0, Math.round(window.innerWidth - rect.right)),\n bottom: Math.max(0, Math.round(window.innerHeight - rect.bottom)),\n };\n\n this.savedIframeCss = this.iframe.style.cssText;\n Object.assign(this.iframe.style, {\n position: \"fixed\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n zIndex: \"2147483647\",\n // Transparent so the host page shows through wherever the dashboard\n // doesn't paint (the offset regions).\n background: \"transparent\",\n // Hide instantly (no transition — a transition would animate the hide and\n // leave the reflow visible mid-fade), then fade back in once the inset has\n // settled, masking the cross-document expand→inset reflow.\n transition: \"none\",\n opacity: \"0\",\n });\n this.post({ action: \"overlay-mode\", active: true, offset });\n this.scheduleReveal();\n }\n\n private restoreIframe(): void {\n // Restore the geometry while hidden, let the dashboard remove its inset,\n // then fade back in — same masking as the open, so nothing shifts.\n this.iframe.style.cssText = this.savedIframeCss ?? \"\";\n this.savedIframeCss = null;\n this.iframe.style.transition = \"none\";\n this.iframe.style.opacity = \"0\";\n this.post({ action: \"overlay-mode\", active: false });\n this.scheduleReveal();\n }\n\n private scheduleReveal(): void {\n if (this.overlayRevealTimer) clearTimeout(this.overlayRevealTimer);\n this.overlayRevealTimer = setTimeout(() => {\n this.iframe.style.transition = OVERLAY_FADE;\n this.iframe.style.opacity = \"1\";\n this.overlayRevealTimer = null;\n }, OVERLAY_REVEAL_DELAY_MS);\n }\n\n private post(message: Record<string, unknown>): void {\n if (this.ready && this.iframe.contentWindow) {\n this.iframe.contentWindow.postMessage(message, this.baseUrl);\n } else {\n this.queue.push(message);\n }\n }\n\n private flush(): void {\n const pending = this.queue.splice(0);\n for (const message of pending) {\n if (this.iframe.contentWindow) {\n this.iframe.contentWindow.postMessage(message, this.baseUrl);\n }\n }\n }\n\n private onReady(): void {\n this.ready = true;\n const capabilities = buildCapabilities(this.events);\n if (Object.keys(capabilities).length > 0) {\n this.post({ type: HOST_CAPABILITIES_MESSAGE_TYPE, capabilities });\n }\n if (this.token) {\n this.post({ action: \"setToken\", token: this.token });\n }\n if (this.initialTheme) {\n this.setTheme(this.initialTheme);\n }\n if (this.initialLang) {\n this.setLang(this.initialLang);\n }\n this.flush();\n }\n\n private onTokenApplied(): void {\n setTimeout(() => {\n this.hideOverlay();\n this.onReadyCallback?.();\n }, 1000);\n }\n\n private hideOverlay(): void {\n if (!this.overlay) return;\n this.overlay.style.transition = \"opacity 0.3s\";\n this.overlay.style.opacity = \"0\";\n setTimeout(() => this.overlay?.remove(), 300);\n }\n\n private createDefaultOverlay(): HTMLElement {\n const overlay = document.createElement(\"div\");\n overlay.style.background = \"#ffffff\";\n overlay.style.display = \"flex\";\n overlay.style.alignItems = \"center\";\n overlay.style.justifyContent = \"center\";\n\n const spinner = document.createElement(\"div\");\n spinner.style.width = \"32px\";\n spinner.style.height = \"32px\";\n spinner.style.border = \"3px solid #e0e0e0\";\n spinner.style.borderTopColor = \"#666\";\n spinner.style.borderRadius = \"50%\";\n spinner.style.animation = \"yuno-spin 0.8s linear infinite\";\n\n const style = document.createElement(\"style\");\n style.textContent = \"@keyframes yuno-spin { to { transform: rotate(360deg); } }\";\n\n overlay.appendChild(style);\n overlay.appendChild(spinner);\n return overlay;\n }\n\n private buildSrc(): string {\n const params = new URLSearchParams({\n embed: \"true\",\n theme: this.initialTheme?.mode ?? \"light\",\n lang: this.initialLang ?? \"en\",\n });\n if (this.initialTestMode !== undefined) {\n params.set(\"test\", String(this.initialTestMode));\n }\n // Drop any host-provided query params so only the SDK controls the query\n // string (avoids conflicts and a double \"?\").\n const rawPath =\n !this.initialPath || this.initialPath === \"/\" ? \"/\" : this.initialPath;\n const path = stripQueryParams(rawPath);\n return `${this.baseUrl}${path}?${params.toString()}`;\n }\n}\n","import { Dashboard } from \"./dashboard\";\nimport type { DashboardConfig } from \"./types\";\n\nlet instance: Dashboard | null = null;\n\nexport function initDashboard(config: DashboardConfig): Dashboard {\n if (instance) {\n instance.destroy();\n }\n instance = new Dashboard(config);\n return instance;\n}\n\nexport function getDashboard(): Dashboard | null {\n return instance;\n}\n\nexport function destroyDashboard(): void {\n if (instance) {\n instance.destroy();\n instance = null;\n }\n}\n"],"mappings":"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","names":[]}
|