@yuno-payments/dashboard-embed-sdk 1.0.0 → 1.2.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 CHANGED
@@ -70,10 +70,13 @@ 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: `"/"`). Pass a clean route with no query string — see [Test mode](#test-mode) for why a `?test=` path breaks the embed |
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) |
74
75
  | `onReady` | `() => void` | No | Callback invoked when the dashboard is fully loaded and authenticated |
75
76
  | `onSessionExpired` | `() => void` | No | Callback invoked when the embedded session has expired. The host should re-authenticate and call `setToken(newToken)` to resume. |
77
+ | `onCheckoutPublished` | `(event: CheckoutPublishedEvent) => void \| Promise<void>` | No | Callback invoked when the user publishes the checkout configuration or styling. `event.source` is `"checkout"` or `"styling"`, `event.status` reports the publish call state (`"loading"` → `"success"` or `"error"`), and `event.payload` contains the configuration object that was published. |
76
78
  | `loading` | `HTMLElement` | No | Custom loading overlay element. If omitted, a default spinner is shown |
79
+ | `autoHeight` | `boolean` | No | When `true`, the iframe is resized to match the dashboard content height (no inner scroll). Default `false` (iframe fills its container at `100%`). See [Auto height](#auto-height) |
77
80
 
78
81
  ### Methods
79
82
 
@@ -81,6 +84,7 @@ destroyDashboard();
81
84
  - **`setLang(lang)`** — Change display language
82
85
  - **`setToken(token)`** — Update the auth token via PostMessage
83
86
  - **`navigate(path)`** — Navigate to a dashboard route
87
+ - **`setTestMode(enabled)`** — Toggle test/sandbox mode at runtime via PostMessage. See [Test mode](#test-mode)
84
88
  - **`destroy()`** — Remove iframe and clean up event listeners
85
89
 
86
90
  ### Types
@@ -128,6 +132,16 @@ interface DashboardTheme {
128
132
  mode?: "light" | "dark";
129
133
  styles?: string; // Custom CSS injected into the dashboard
130
134
  }
135
+
136
+ type CheckoutPublishedSource = "checkout" | "styling";
137
+
138
+ type CheckoutPublishedStatus = "loading" | "success" | "error";
139
+
140
+ interface CheckoutPublishedEvent {
141
+ source: CheckoutPublishedSource;
142
+ status: CheckoutPublishedStatus;
143
+ payload?: unknown; // The configuration object that was published
144
+ }
131
145
  ```
132
146
 
133
147
  ## Session timeouts
@@ -157,10 +171,97 @@ const sdk = initDashboard({
157
171
  })
158
172
  ```
159
173
 
174
+ ## Checkout published events
175
+
176
+ Whenever the user publishes from the embedded Checkout Builder (payment method
177
+ configuration) or the Styling editor, the iframe emits a message to the host.
178
+ Subscribe via `onCheckoutPublished`:
179
+
180
+ ```ts
181
+ const sdk = initDashboard({
182
+ baseUrl: "https://dashboard.y.uno",
183
+ container: document.getElementById("dashboard")!,
184
+ token: initialToken,
185
+ onCheckoutPublished: (event) => {
186
+ // event.source is "checkout" | "styling"
187
+ // event.status is "loading" | "success" | "error"
188
+ // event.payload is the configuration object that was published
189
+ switch (event.status) {
190
+ case "loading":
191
+ console.log(`Publish loading from ${event.source}`)
192
+ break
193
+ case "success":
194
+ console.log(`Checkout published from ${event.source}`, event.payload)
195
+ break
196
+ case "error":
197
+ console.log("Publish failed — please try again")
198
+ break
199
+ }
200
+ },
201
+ })
202
+ ```
203
+
204
+ The callback fires once per state transition of the publish call: first with
205
+ `status: "loading"` when the user triggers the publish, then a second time with
206
+ either `"success"` or `"error"` once the dashboard's publish endpoint responds.
207
+ `event.payload` is only meaningful on the `"success"` event.
208
+
209
+ ## Test mode
210
+
211
+ The dashboard can run in **test/sandbox** mode or **live** mode. Set it at mount time
212
+ via the `testMode` option, or toggle it at runtime via `setTestMode(enabled)`:
213
+
214
+ ```ts
215
+ const sdk = initDashboard({
216
+ baseUrl: "https://dashboard.y.uno",
217
+ container: document.getElementById("dashboard")!,
218
+ token,
219
+ path: "/connections", // clean route — no query string
220
+ testMode: false, // mount in live mode
221
+ });
222
+
223
+ // Later — flip to test mode without remounting
224
+ sdk.setTestMode(true);
225
+ ```
226
+
227
+ **Do not pass test mode through `path`.** The SDK builds the iframe URL by appending its
228
+ own query string (`embed`, `theme`, `lang`, and `test` when `testMode` is set) to the route:
229
+ `${baseUrl}${path}?embed=true&…`. A `path` that already carries a query (e.g.
230
+ `"/connections?test=false"`) produces a malformed double-`?` URL that swallows `embed` and
231
+ breaks the embed (the dashboard never mounts). Always pass a query-free `path` and use the
232
+ `testMode` option / `setTestMode()` method instead.
233
+
234
+ **Host/iframe contract:** when `testMode` is provided, the SDK appends `test=<bool>` to the
235
+ initial iframe URL — the embedded dashboard reads it from its own URL on load. For the runtime
236
+ toggle, `setTestMode(enabled)` sends `postMessage({ action: "setTestMode", testMode })` (accepted
237
+ only from the configured `baseUrl` origin); the embedded dashboard must handle that action to
238
+ switch mode without a reload.
239
+
160
240
  ## Loading overlay
161
241
 
162
242
  A loading overlay covers the iframe while initialization completes. You can provide a custom element via the `loading` config option, or the SDK renders a default spinner. The overlay fades out automatically (300ms transition) once the dashboard is authenticated and ready.
163
243
 
244
+ ## Auto height
245
+
246
+ By default the iframe fills its container (`height: 100%`) and the dashboard content
247
+ scrolls inside it. Pass `autoHeight: true` to size the iframe to the dashboard's content
248
+ height instead, so the whole host page scrolls naturally with no inner scrollbar:
249
+
250
+ ```ts
251
+ initDashboard({
252
+ baseUrl: "https://dashboard.y.uno",
253
+ container: document.getElementById("dashboard")!,
254
+ token,
255
+ autoHeight: true,
256
+ });
257
+ ```
258
+
259
+ **Host/iframe contract:** when `autoHeight` is enabled, the embedded dashboard reports its
260
+ content height to the host via `postMessage({ action: "resize", height })` whenever the
261
+ content resizes; the SDK applies that height to the iframe and its wrapper. Messages are
262
+ accepted only from the configured `baseUrl` origin. Your container must allow vertical growth
263
+ (avoid a fixed `height` / `overflow: hidden`) for the effect to be visible.
264
+
164
265
  ## Migrating from 0.x to 1.0
165
266
 
166
267
  `1.0.0` drops the `Yuno` brand prefix from the public API so the SDK reads cleanly
package/dist/index.d.ts CHANGED
@@ -37,6 +37,13 @@ interface DashboardTheme {
37
37
  mode?: "light" | "dark";
38
38
  styles?: string;
39
39
  }
40
+ type CheckoutPublishedSource = "checkout" | "styling";
41
+ type CheckoutPublishedStatus = "loading" | "success" | "error";
42
+ interface CheckoutPublishedEvent {
43
+ source: CheckoutPublishedSource;
44
+ status: CheckoutPublishedStatus;
45
+ payload?: unknown;
46
+ }
40
47
  interface DashboardConfig {
41
48
  baseUrl: string;
42
49
  container: HTMLElement;
@@ -44,9 +51,12 @@ interface DashboardConfig {
44
51
  theme?: DashboardTheme;
45
52
  lang?: string;
46
53
  path?: string;
54
+ testMode?: boolean;
47
55
  onReady?: () => void;
48
56
  onSessionExpired?: () => void | Promise<void>;
57
+ onCheckoutPublished?: (event: CheckoutPublishedEvent) => void | Promise<void>;
49
58
  loading?: HTMLElement;
59
+ autoHeight?: boolean;
50
60
  }
51
61
 
52
62
  declare class Dashboard {
@@ -60,14 +70,18 @@ declare class Dashboard {
60
70
  private initialTheme?;
61
71
  private initialLang?;
62
72
  private initialPath;
73
+ private initialTestMode?;
63
74
  private token?;
64
75
  private onReadyCallback?;
65
76
  private onSessionExpiredCallback?;
77
+ private onCheckoutPublishedCallback?;
78
+ private autoHeight;
66
79
  constructor(config: DashboardConfig);
67
80
  setTheme(theme: DashboardTheme): void;
68
81
  setLang(lang: string): void;
69
82
  setToken(token: string): void;
70
83
  navigate(path: string): void;
84
+ setTestMode(enabled: boolean): void;
71
85
  destroy(): void;
72
86
  private post;
73
87
  private flush;
@@ -82,4 +96,4 @@ declare function initDashboard(config: DashboardConfig): Dashboard;
82
96
  declare function getDashboard(): Dashboard | null;
83
97
  declare function destroyDashboard(): void;
84
98
 
85
- export { Dashboard, type DashboardConfig, type DashboardTheme, type ModeTokens, type ThemeColors, type ThemeTypography, destroyDashboard, getDashboard, initDashboard };
99
+ export { type CheckoutPublishedEvent, type CheckoutPublishedSource, type CheckoutPublishedStatus, Dashboard, type DashboardConfig, type DashboardTheme, type ModeTokens, type ThemeColors, type ThemeTypography, destroyDashboard, getDashboard, initDashboard };
package/dist/index.js CHANGED
@@ -8,9 +8,12 @@ var Dashboard = class {
8
8
  this.initialTheme = config.theme;
9
9
  this.initialLang = config.lang;
10
10
  this.initialPath = config.path ?? "/";
11
+ this.initialTestMode = config.testMode;
11
12
  this.token = config.token;
12
13
  this.onReadyCallback = config.onReady;
13
14
  this.onSessionExpiredCallback = config.onSessionExpired;
15
+ this.onCheckoutPublishedCallback = config.onCheckoutPublished;
16
+ this.autoHeight = config.autoHeight ?? false;
14
17
  this.wrapper = document.createElement("div");
15
18
  this.wrapper.style.position = "relative";
16
19
  this.wrapper.style.width = "100%";
@@ -29,6 +32,11 @@ var Dashboard = class {
29
32
  this.wrapper.appendChild(this.overlay);
30
33
  this.messageHandler = (e) => {
31
34
  if (e.origin !== this.baseUrl) return;
35
+ if (this.autoHeight && e.data?.action === "resize" && typeof e.data?.height === "number") {
36
+ this.iframe.style.height = `${e.data.height}px`;
37
+ this.wrapper.style.height = `${e.data.height}px`;
38
+ return;
39
+ }
32
40
  if (e.data?.type === "yuno-dashboard:session-expired") {
33
41
  try {
34
42
  const result = this.onSessionExpiredCallback?.();
@@ -45,6 +53,26 @@ var Dashboard = class {
45
53
  }
46
54
  return;
47
55
  }
56
+ if (e.data?.type === "yuno-dashboard:checkout-published") {
57
+ try {
58
+ const result = this.onCheckoutPublishedCallback?.({
59
+ source: e.data?.source,
60
+ status: e.data?.status,
61
+ payload: e.data?.payload
62
+ });
63
+ if (result && typeof result.catch === "function") {
64
+ result.catch(
65
+ (err) => (
66
+ // eslint-disable-next-line no-console
67
+ console.warn("[yuno-dashboard-sdk] onCheckoutPublished async callback rejected", err)
68
+ )
69
+ );
70
+ }
71
+ } catch (err) {
72
+ console.warn("[yuno-dashboard-sdk] onCheckoutPublished callback threw", err);
73
+ }
74
+ return;
75
+ }
48
76
  if (e.data?.action === "ready") {
49
77
  this.onReady();
50
78
  }
@@ -79,6 +107,9 @@ var Dashboard = class {
79
107
  navigate(path) {
80
108
  this.post({ action: "navigate", path });
81
109
  }
110
+ setTestMode(enabled) {
111
+ this.post({ action: "setTestMode", testMode: enabled });
112
+ }
82
113
  destroy() {
83
114
  window.removeEventListener("message", this.messageHandler);
84
115
  this.wrapper.remove();
@@ -115,9 +146,6 @@ var Dashboard = class {
115
146
  }
116
147
  onTokenApplied() {
117
148
  setTimeout(() => {
118
- if (this.initialPath !== "/") {
119
- this.post({ action: "navigate", path: this.initialPath });
120
- }
121
149
  this.hideOverlay();
122
150
  this.onReadyCallback?.();
123
151
  }, 1e3);
@@ -153,7 +181,11 @@ var Dashboard = class {
153
181
  theme: this.initialTheme?.mode ?? "light",
154
182
  lang: this.initialLang ?? "en"
155
183
  });
156
- return `${this.baseUrl}/?${params.toString()}`;
184
+ if (this.initialTestMode !== void 0) {
185
+ params.set("test", String(this.initialTestMode));
186
+ }
187
+ const path = !this.initialPath || this.initialPath === "/" ? "/" : this.initialPath;
188
+ return `${this.baseUrl}${path}?${params.toString()}`;
157
189
  }
158
190
  };
159
191
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dashboard.ts","../src/singleton.ts"],"sourcesContent":["import type { DashboardTheme, DashboardConfig } from \"./types\";\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 token?: string;\n private onReadyCallback?: () => void;\n private onSessionExpiredCallback?: () => void | Promise<void>;\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.token = config.token;\n this.onReadyCallback = config.onReady;\n this.onSessionExpiredCallback = config.onSessionExpired;\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 (e.data?.type === \"yuno-dashboard:session-expired\") {\n try {\n const result = this.onSessionExpiredCallback?.();\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] onSessionExpired async callback rejected\", err),\n );\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn(\"[yuno-dashboard-sdk] onSessionExpired callback threw\", err);\n }\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 destroy(): void {\n window.removeEventListener(\"message\", this.messageHandler);\n this.wrapper.remove();\n this.queue = [];\n this.ready = 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 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 if (this.initialPath !== \"/\") {\n this.post({ action: \"navigate\", path: this.initialPath });\n }\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 return `${this.baseUrl}/?${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, CheckoutPublishedEvent } from \"./types\";\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 onCheckoutPublishedCallback?: (event: CheckoutPublishedEvent) => void | Promise<void>;\n private autoHeight: boolean;\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.onCheckoutPublishedCallback = config.onCheckoutPublished;\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 try {\n const result = this.onSessionExpiredCallback?.();\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] onSessionExpired async callback rejected\", err),\n );\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn(\"[yuno-dashboard-sdk] onSessionExpired callback threw\", err);\n }\n return;\n }\n if (e.data?.type === \"yuno-dashboard:checkout-published\") {\n try {\n const result = this.onCheckoutPublishedCallback?.({\n source: e.data?.source,\n status: e.data?.status,\n payload: e.data?.payload,\n });\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] onCheckoutPublished async callback rejected\", err),\n );\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn(\"[yuno-dashboard-sdk] onCheckoutPublished callback threw\", err);\n }\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 }\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 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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuno-payments/dashboard-embed-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Lightweight SDK for embedding the Yuno Dashboard via iframe",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",