@benji-money/connect-sdk 1.1.0-beta.2 → 1.1.0-beta.3
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 +23 -3
- package/dist/connect-sdk.umd.global.js +363 -36
- package/dist/connect-sdk.umd.global.js.map +1 -1
- package/dist/index.cjs +377 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -3
- package/dist/index.d.ts +42 -3
- package/dist/index.js +375 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/connect-sdk.umd.d.ts +0 -14
package/README.md
CHANGED
|
@@ -86,6 +86,8 @@ The SDK accepts the following configuration options:
|
|
|
86
86
|
|
|
87
87
|
- `environment` (required): One of `local`, `development`, `sandbox`, or `production`
|
|
88
88
|
- `token` (required): Your API connect token
|
|
89
|
+
- `mode` (optional): `BenjiConnectMode` — `CONNECT` (default), `TRANSFER`, `REDEEM`, or `LOGIN`
|
|
90
|
+
- `authType` (optional): `PartnerIntegrationType` — LOGIN only; omit or set `DUMMY` for the visible modal iframe; set to your partner type (1–6) for popup-only auth
|
|
89
91
|
- `onSuccess` (optional): Callback function called when connect completed successfully
|
|
90
92
|
- `onError` (optional): Callback function called when error occurs in the connect flow
|
|
91
93
|
- `onExit` (optional): Callback function called when the user exits the connect flow
|
|
@@ -94,6 +96,14 @@ The SDK accepts the following configuration options:
|
|
|
94
96
|
- `placement` (optional): Placement relative to `anchor` — see [Placement](#placement) below
|
|
95
97
|
- `offset` (optional): Pixel gap after placement — `{ top?: number; left?: number }` (default `{ top: 0, left: 0 }`)
|
|
96
98
|
|
|
99
|
+
### Modes
|
|
100
|
+
|
|
101
|
+
| Mode | Visible UX | Notes |
|
|
102
|
+
| ---- | ---------- | ----- |
|
|
103
|
+
| **CONNECT** (default) | Centered or anchored modal iframe | No `authType`; standard connect / verify flow |
|
|
104
|
+
| **LOGIN + DUMMY / no `authType`** | Same visible modal iframe | Partner auth runs in a nested iframe inside Benji Connect |
|
|
105
|
+
| **LOGIN + non-DUMMY** | Partner popup only | Pass `authType`; Benji Connect iframe is headless — see [LOGIN popup example](#login-popup-example) below |
|
|
106
|
+
|
|
97
107
|
### Placement
|
|
98
108
|
|
|
99
109
|
Placement uses [Floating UI](https://floating-ui.com/docs/computePosition#placement) / CSS logical naming (`start` / `end`). In **LTR** layouts:
|
|
@@ -104,6 +114,11 @@ Placement uses [Floating UI](https://floating-ui.com/docs/computePosition#placem
|
|
|
104
114
|
| `bottom-end` | Below the anchor, right edge aligned with anchor's right edge |
|
|
105
115
|
| `bottom-start` | Below the anchor, left edge aligned with anchor's left edge |
|
|
106
116
|
|
|
117
|
+
**What `anchor` positions:**
|
|
118
|
+
|
|
119
|
+
- **CONNECT, TRANSFER, REDEEM, and LOGIN + DUMMY:** the modal iframe
|
|
120
|
+
- **LOGIN + non-DUMMY:** the partner-auth popup window (modal iframe stays headless)
|
|
121
|
+
|
|
107
122
|
**Defaults:**
|
|
108
123
|
|
|
109
124
|
- No `anchor` → centered modal (unchanged behavior for existing integrations)
|
|
@@ -129,30 +144,35 @@ Prefer selector, ref, or getter when the anchor can re-render (e.g. disabled/loa
|
|
|
129
144
|
- **Anchored** popovers use the full design size when width and height checks pass; otherwise they fall back to a scaled centered modal
|
|
130
145
|
- Scroll, resize, and visual viewport changes while open recompute placement and size
|
|
131
146
|
|
|
132
|
-
|
|
147
|
+
#### LOGIN popup example
|
|
148
|
+
|
|
149
|
+
Popup below a top-right login button; pass `authType` for non-DUMMY partners:
|
|
133
150
|
|
|
134
151
|
```typescript
|
|
135
152
|
import {
|
|
136
153
|
ConnectSDK,
|
|
137
154
|
BenjiConnectMode,
|
|
138
155
|
BenjiConnectPlacement,
|
|
156
|
+
PartnerIntegrationType,
|
|
139
157
|
} from "@benji-money/connect-sdk";
|
|
140
158
|
|
|
141
|
-
// Prefer a live binding (selector or ref) when the button may re-render before open()
|
|
142
159
|
const sdk = new ConnectSDK({
|
|
143
160
|
environment: "sandbox",
|
|
144
161
|
token: connectToken,
|
|
145
162
|
mode: BenjiConnectMode.LOGIN,
|
|
163
|
+
authType: PartnerIntegrationType.OAUTH_PKCE,
|
|
146
164
|
anchor: "#login-btn",
|
|
147
165
|
placement: BenjiConnectPlacement.BOTTOM_END,
|
|
148
166
|
offset: { top: 8 },
|
|
149
167
|
onSuccess: (token) => { /* ... */ },
|
|
168
|
+
onError: (error) => { /* popup blocked, etc. */ },
|
|
169
|
+
onExit: (metadata) => { /* POPUP_CLOSED when user dismisses popup */ },
|
|
150
170
|
});
|
|
151
171
|
|
|
152
172
|
await sdk.open();
|
|
153
173
|
```
|
|
154
174
|
|
|
155
|
-
> **`bottom-end`**
|
|
175
|
+
> **`bottom-end`** positions the **partner popup** below the anchor with its right edge aligned to the anchor's right edge. The Benji Connect iframe stays headless (not visible).
|
|
156
176
|
|
|
157
177
|
### Environments
|
|
158
178
|
|
|
@@ -110,7 +110,7 @@ var ConnectSDK = (() => {
|
|
|
110
110
|
// src/utils/config.ts
|
|
111
111
|
var buildContext = () => ({
|
|
112
112
|
namespace: "benji-connect-sdk" ? "benji-connect-sdk" : "benji-connect-sdk",
|
|
113
|
-
version: "1.1.0-beta.
|
|
113
|
+
version: "1.1.0-beta.3" ? String("1.1.0-beta.3") : "0.0.0"
|
|
114
114
|
});
|
|
115
115
|
function isBenjiConnectEnvironment(environment) {
|
|
116
116
|
return environment === "local" /* LOCAL */ || environment === "development" /* DEVELOPMENT */ || environment === "sandbox" /* SANDBOX */ || environment === "production" /* PRODUCTION */;
|
|
@@ -133,6 +133,8 @@ var ConnectSDK = (() => {
|
|
|
133
133
|
// src/utils/placement.ts
|
|
134
134
|
var IFRAME_WIDTH = 400;
|
|
135
135
|
var IFRAME_HEIGHT = 645;
|
|
136
|
+
var POPUP_WIDTH = 420;
|
|
137
|
+
var POPUP_HEIGHT = 640;
|
|
136
138
|
var IFRAME_STATIC_STYLE = {
|
|
137
139
|
border: "none",
|
|
138
140
|
borderRadius: "20px",
|
|
@@ -389,6 +391,165 @@ var ConnectSDK = (() => {
|
|
|
389
391
|
left: `${state.layout.left + viewportOffset.left}px`
|
|
390
392
|
});
|
|
391
393
|
}
|
|
394
|
+
function popupFitsBelow(anchorBottom, offsetTop, viewportHeight) {
|
|
395
|
+
const top = anchorBottom + offsetTop;
|
|
396
|
+
const bottom = top + POPUP_HEIGHT;
|
|
397
|
+
return top >= VIEWPORT_PADDING && bottom <= viewportHeight - VIEWPORT_PADDING;
|
|
398
|
+
}
|
|
399
|
+
function popupFitsHorizontally(viewportWidth) {
|
|
400
|
+
return viewportWidth >= POPUP_WIDTH + 2 * VIEWPORT_PADDING;
|
|
401
|
+
}
|
|
402
|
+
function clampPopupHorizontal(left, viewportWidth) {
|
|
403
|
+
const min = VIEWPORT_PADDING;
|
|
404
|
+
const max = viewportWidth - POPUP_WIDTH - VIEWPORT_PADDING;
|
|
405
|
+
if (max < min) {
|
|
406
|
+
return min;
|
|
407
|
+
}
|
|
408
|
+
return Math.min(Math.max(left, min), max);
|
|
409
|
+
}
|
|
410
|
+
function centerPopupViewportRect(viewport) {
|
|
411
|
+
const left = Math.max(
|
|
412
|
+
VIEWPORT_PADDING,
|
|
413
|
+
(viewport.width - POPUP_WIDTH) / 2
|
|
414
|
+
);
|
|
415
|
+
const top = Math.max(
|
|
416
|
+
VIEWPORT_PADDING,
|
|
417
|
+
(viewport.height - POPUP_HEIGHT) / 2
|
|
418
|
+
);
|
|
419
|
+
return {
|
|
420
|
+
top,
|
|
421
|
+
left,
|
|
422
|
+
width: POPUP_WIDTH,
|
|
423
|
+
height: POPUP_HEIGHT,
|
|
424
|
+
placement: "center" /* CENTER */
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function computePopupViewportRect(anchorElement, placement, offset, viewport, options = {}) {
|
|
428
|
+
const { warnOnInvalidAnchor = false } = options;
|
|
429
|
+
const resolvedOffset = defaultOffset(offset);
|
|
430
|
+
if (placement === "center" /* CENTER */) {
|
|
431
|
+
return centerPopupViewportRect(viewport);
|
|
432
|
+
}
|
|
433
|
+
if (!isAnchorVisible(anchorElement)) {
|
|
434
|
+
if (isPreservableAnchoredLayout(options.preserveAnchoredLayout)) {
|
|
435
|
+
return clampPreservedAnchoredLayout(
|
|
436
|
+
options.preserveAnchoredLayout,
|
|
437
|
+
viewport
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
if (warnOnInvalidAnchor) {
|
|
441
|
+
warnInvalidAnchor(
|
|
442
|
+
"anchor is hidden or detached; falling back to centered popup"
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
return centerPopupViewportRect(viewport);
|
|
446
|
+
}
|
|
447
|
+
const rect = anchorElement.getBoundingClientRect();
|
|
448
|
+
if (!popupFitsBelow(rect.bottom, resolvedOffset.top, viewport.height)) {
|
|
449
|
+
return centerPopupViewportRect(viewport);
|
|
450
|
+
}
|
|
451
|
+
if (!popupFitsHorizontally(viewport.width)) {
|
|
452
|
+
return centerPopupViewportRect(viewport);
|
|
453
|
+
}
|
|
454
|
+
const top = rect.bottom + resolvedOffset.top;
|
|
455
|
+
const idealLeft = placement === "bottom-end" /* BOTTOM_END */ ? rect.right - POPUP_WIDTH + resolvedOffset.left : rect.left + resolvedOffset.left;
|
|
456
|
+
if (idealLeft < VIEWPORT_PADDING) {
|
|
457
|
+
if (warnOnInvalidAnchor) {
|
|
458
|
+
warnInvalidAnchor(
|
|
459
|
+
"anchor rect is unusable for popup placement; falling back to centered popup"
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
return centerPopupViewportRect(viewport);
|
|
463
|
+
}
|
|
464
|
+
const left = clampPopupHorizontal(idealLeft, viewport.width);
|
|
465
|
+
return {
|
|
466
|
+
top,
|
|
467
|
+
left,
|
|
468
|
+
width: POPUP_WIDTH,
|
|
469
|
+
height: POPUP_HEIGHT,
|
|
470
|
+
placement
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function resolvePopupViewportLayout(config, viewport = {
|
|
474
|
+
width: window.innerWidth,
|
|
475
|
+
height: window.innerHeight
|
|
476
|
+
}, options = {}) {
|
|
477
|
+
const { warnOnInvalidAnchor = true } = options;
|
|
478
|
+
const placement = resolveEffectivePlacement(config);
|
|
479
|
+
const anchorElement = resolveAnchor(config.anchor);
|
|
480
|
+
if (warnOnInvalidAnchor && placement !== "center" /* CENTER */ && !anchorElement) {
|
|
481
|
+
warnInvalidAnchor("anchor not found; falling back to centered popup");
|
|
482
|
+
if (isPreservableAnchoredLayout(options.preserveAnchoredLayout)) {
|
|
483
|
+
return clampPreservedAnchoredLayout(
|
|
484
|
+
options.preserveAnchoredLayout,
|
|
485
|
+
viewport
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
return centerPopupViewportRect(viewport);
|
|
489
|
+
}
|
|
490
|
+
if (placement === "center" /* CENTER */ || !anchorElement) {
|
|
491
|
+
return centerPopupViewportRect(viewport);
|
|
492
|
+
}
|
|
493
|
+
return computePopupViewportRect(
|
|
494
|
+
anchorElement,
|
|
495
|
+
placement,
|
|
496
|
+
config.offset,
|
|
497
|
+
viewport,
|
|
498
|
+
options
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
function computePopupScreenPosition(config, state = { lastValidAnchoredLayout: null }, options = {}) {
|
|
502
|
+
const layout = resolvePopupViewportLayout(config, void 0, {
|
|
503
|
+
...options,
|
|
504
|
+
preserveAnchoredLayout: state.lastValidAnchoredLayout
|
|
505
|
+
});
|
|
506
|
+
if (layout.placement !== "center" /* CENTER */) {
|
|
507
|
+
state.lastValidAnchoredLayout = layout;
|
|
508
|
+
}
|
|
509
|
+
const viewportOffset = getVisualViewportOffset();
|
|
510
|
+
return {
|
|
511
|
+
left: window.screenX + layout.left + viewportOffset.left,
|
|
512
|
+
top: window.screenY + layout.top + viewportOffset.top,
|
|
513
|
+
width: layout.width,
|
|
514
|
+
height: layout.height
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
function buildLoginPopupFeatures(position) {
|
|
518
|
+
return [
|
|
519
|
+
`width=${position.width}`,
|
|
520
|
+
`height=${position.height}`,
|
|
521
|
+
`left=${Math.round(position.left)}`,
|
|
522
|
+
`top=${Math.round(position.top)}`,
|
|
523
|
+
"scrollbars=yes",
|
|
524
|
+
"resizable=yes"
|
|
525
|
+
].join(",");
|
|
526
|
+
}
|
|
527
|
+
function applyHeadlessContainerStyle(container) {
|
|
528
|
+
Object.assign(container.style, {
|
|
529
|
+
position: "fixed",
|
|
530
|
+
top: "0",
|
|
531
|
+
left: "-9999px",
|
|
532
|
+
width: "1px",
|
|
533
|
+
height: "1px",
|
|
534
|
+
overflow: "hidden",
|
|
535
|
+
pointerEvents: "none",
|
|
536
|
+
backgroundColor: "transparent",
|
|
537
|
+
opacity: "0",
|
|
538
|
+
zIndex: "-1"
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
function applyHeadlessIframeStyle(iframe) {
|
|
542
|
+
Object.assign(iframe.style, {
|
|
543
|
+
position: "absolute",
|
|
544
|
+
top: "0",
|
|
545
|
+
left: "0",
|
|
546
|
+
width: "1px",
|
|
547
|
+
height: "1px",
|
|
548
|
+
border: "none",
|
|
549
|
+
opacity: "0",
|
|
550
|
+
pointerEvents: "none"
|
|
551
|
+
});
|
|
552
|
+
}
|
|
392
553
|
function attachRepositionListeners(config, onReposition) {
|
|
393
554
|
const repositionHandler = onReposition;
|
|
394
555
|
const repositionScrollTargets = getRepositionScrollTargets(config.anchor);
|
|
@@ -418,6 +579,7 @@ var ConnectSDK = (() => {
|
|
|
418
579
|
BenjiConnectEventType2["FLOW_SUCCESS"] = "FLOW_SUCCESS";
|
|
419
580
|
BenjiConnectEventType2["EVENT"] = "EVENT";
|
|
420
581
|
BenjiConnectEventType2["ERROR"] = "ERROR";
|
|
582
|
+
BenjiConnectEventType2["PARTNER_LOGIN_OPEN"] = "PARTNER_LOGIN_OPEN";
|
|
421
583
|
return BenjiConnectEventType2;
|
|
422
584
|
})(BenjiConnectEventType || {});
|
|
423
585
|
|
|
@@ -549,6 +711,8 @@ var ConnectSDK = (() => {
|
|
|
549
711
|
this.onExit = config.onExit;
|
|
550
712
|
this.onEvent = config.onEvent;
|
|
551
713
|
this.close = config.close;
|
|
714
|
+
this.closeLoginPopup = config.closeLoginPopup;
|
|
715
|
+
this.navigateLoginPopup = config.navigateLoginPopup;
|
|
552
716
|
}
|
|
553
717
|
static setMode(mode) {
|
|
554
718
|
this.mode = mode;
|
|
@@ -586,7 +750,7 @@ var ConnectSDK = (() => {
|
|
|
586
750
|
}
|
|
587
751
|
}
|
|
588
752
|
static handleMessage(event) {
|
|
589
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
753
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
590
754
|
if (!isConnectMessageType(event.data)) return;
|
|
591
755
|
const expectedOrigin = new URL(Endpoints.benji_connect_auth_url).origin;
|
|
592
756
|
if (!event || event.origin !== expectedOrigin) {
|
|
@@ -634,6 +798,16 @@ var ConnectSDK = (() => {
|
|
|
634
798
|
(_g = this.onEvent) == null ? void 0 : _g.call(this, callbackData.type, callbackData.metadata);
|
|
635
799
|
break;
|
|
636
800
|
}
|
|
801
|
+
case "PARTNER_LOGIN_OPEN" /* PARTNER_LOGIN_OPEN */: {
|
|
802
|
+
const connectMessage = message;
|
|
803
|
+
const url = (_h = connectMessage.data) == null ? void 0 : _h.url;
|
|
804
|
+
if (url) {
|
|
805
|
+
(_i = this.navigateLoginPopup) == null ? void 0 : _i.call(this, url);
|
|
806
|
+
} else {
|
|
807
|
+
(_j = this.closeLoginPopup) == null ? void 0 : _j.call(this);
|
|
808
|
+
}
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
637
811
|
}
|
|
638
812
|
} finally {
|
|
639
813
|
}
|
|
@@ -650,6 +824,7 @@ var ConnectSDK = (() => {
|
|
|
650
824
|
var MessageRouter = _MessageRouter;
|
|
651
825
|
|
|
652
826
|
// src/connect-sdk.ts
|
|
827
|
+
var POPUP_CLOSED_POLL_MS = 500;
|
|
653
828
|
var ConnectSDK = class {
|
|
654
829
|
constructor(config) {
|
|
655
830
|
this.iframe = null;
|
|
@@ -664,7 +839,20 @@ var ConnectSDK = (() => {
|
|
|
664
839
|
},
|
|
665
840
|
lastValidAnchoredLayout: null
|
|
666
841
|
};
|
|
842
|
+
this.popupLayoutState = {
|
|
843
|
+
lastValidAnchoredLayout: null
|
|
844
|
+
};
|
|
667
845
|
this.repositionListeners = null;
|
|
846
|
+
this.loginPopup = null;
|
|
847
|
+
this.popupClosedPollId = null;
|
|
848
|
+
this.isClosing = false;
|
|
849
|
+
/**
|
|
850
|
+
* Aborts a pending open() when close() runs before the iframe mounts.
|
|
851
|
+
* Non-DUMMY LOGIN opens the partner popup synchronously, then awaits initialize()
|
|
852
|
+
* before mounting a headless iframe. Without this, close() can tear down the popup
|
|
853
|
+
* while the in-flight open() still registers listeners and mounts the iframe.
|
|
854
|
+
*/
|
|
855
|
+
this.openAbortController = null;
|
|
668
856
|
this.sdkConfig = config;
|
|
669
857
|
if (!this.sdkConfig.environment) throw new Error("Environment is required");
|
|
670
858
|
if (!this.sdkConfig.token) throw new Error("Connect token is required");
|
|
@@ -672,26 +860,125 @@ var ConnectSDK = (() => {
|
|
|
672
860
|
this.sdkConfig.environment = environment;
|
|
673
861
|
configureConfig(environment);
|
|
674
862
|
}
|
|
863
|
+
isLoginPopupOnly() {
|
|
864
|
+
var _a;
|
|
865
|
+
const mode = (_a = this.sdkConfig.mode) != null ? _a : 1 /* CONNECT */;
|
|
866
|
+
return mode === 5 /* LOGIN */ && this.sdkConfig.authType != null && this.sdkConfig.authType !== 0 /* DUMMY */;
|
|
867
|
+
}
|
|
868
|
+
isSessionActive() {
|
|
869
|
+
return this.iframe != null || this.loginPopup != null;
|
|
870
|
+
}
|
|
871
|
+
/** Cancels any open() still running between popup pre-open and iframe mount. */
|
|
872
|
+
abortInflightOpen() {
|
|
873
|
+
var _a;
|
|
874
|
+
(_a = this.openAbortController) == null ? void 0 : _a.abort();
|
|
875
|
+
this.openAbortController = null;
|
|
876
|
+
}
|
|
675
877
|
async initialize() {
|
|
676
878
|
MessageRouter.configureMessageRouter({
|
|
677
879
|
onSuccess: this.sdkConfig.onSuccess,
|
|
678
880
|
onError: this.sdkConfig.onError,
|
|
679
881
|
onExit: this.sdkConfig.onExit,
|
|
680
882
|
onEvent: this.sdkConfig.onEvent,
|
|
681
|
-
close: () => this.close()
|
|
883
|
+
close: () => this.close(),
|
|
884
|
+
closeLoginPopup: () => this.closeLoginPopup(),
|
|
885
|
+
navigateLoginPopup: (url) => this.navigateLoginPopup(url)
|
|
682
886
|
});
|
|
683
887
|
}
|
|
888
|
+
openLoginPopup() {
|
|
889
|
+
var _a, _b;
|
|
890
|
+
const position = computePopupScreenPosition(
|
|
891
|
+
this.sdkConfig,
|
|
892
|
+
this.popupLayoutState,
|
|
893
|
+
{ warnOnInvalidAnchor: true }
|
|
894
|
+
);
|
|
895
|
+
this.loginPopup = window.open(
|
|
896
|
+
"about:blank",
|
|
897
|
+
"benji_partner_auth",
|
|
898
|
+
buildLoginPopupFeatures(position)
|
|
899
|
+
);
|
|
900
|
+
if (!this.loginPopup) {
|
|
901
|
+
console.warn("[Connect SDK] Failed to pre-open LOGIN partner auth popup");
|
|
902
|
+
(_b = (_a = this.sdkConfig).onError) == null ? void 0 : _b.call(
|
|
903
|
+
_a,
|
|
904
|
+
new Error("Popup blocked. Allow popups for this site and try again."),
|
|
905
|
+
"400",
|
|
906
|
+
{ context: buildContext() }
|
|
907
|
+
);
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
this.startPopupClosedPoll();
|
|
911
|
+
return true;
|
|
912
|
+
}
|
|
913
|
+
startPopupClosedPoll() {
|
|
914
|
+
this.stopPopupClosedPoll();
|
|
915
|
+
this.popupClosedPollId = setInterval(() => {
|
|
916
|
+
var _a, _b, _c;
|
|
917
|
+
if (this.isClosing) return;
|
|
918
|
+
if ((_a = this.loginPopup) == null ? void 0 : _a.closed) {
|
|
919
|
+
(_c = (_b = this.sdkConfig).onExit) == null ? void 0 : _c.call(_b, {
|
|
920
|
+
context: buildContext(),
|
|
921
|
+
trigger: "POPUP_CLOSED" /* POPUP_CLOSED */
|
|
922
|
+
});
|
|
923
|
+
this.close();
|
|
924
|
+
}
|
|
925
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
926
|
+
}
|
|
927
|
+
stopPopupClosedPoll() {
|
|
928
|
+
if (this.popupClosedPollId != null) {
|
|
929
|
+
clearInterval(this.popupClosedPollId);
|
|
930
|
+
this.popupClosedPollId = null;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
repositionLoginPopup() {
|
|
934
|
+
if (!this.loginPopup || this.loginPopup.closed) return;
|
|
935
|
+
const position = computePopupScreenPosition(
|
|
936
|
+
this.sdkConfig,
|
|
937
|
+
this.popupLayoutState
|
|
938
|
+
);
|
|
939
|
+
try {
|
|
940
|
+
this.loginPopup.moveTo(
|
|
941
|
+
Math.round(position.left),
|
|
942
|
+
Math.round(position.top)
|
|
943
|
+
);
|
|
944
|
+
this.loginPopup.resizeTo(position.width, position.height);
|
|
945
|
+
} catch (error) {
|
|
946
|
+
console.warn("[Connect SDK] Failed to reposition LOGIN popup", error);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
684
949
|
async open() {
|
|
685
950
|
var _a;
|
|
951
|
+
if (this.isSessionActive()) return;
|
|
952
|
+
const mode = (_a = this.sdkConfig.mode) != null ? _a : 1 /* CONNECT */;
|
|
953
|
+
const abortController = new AbortController();
|
|
954
|
+
this.openAbortController = abortController;
|
|
955
|
+
const { signal } = abortController;
|
|
956
|
+
if (this.isLoginPopupOnly()) {
|
|
957
|
+
const opened = this.openLoginPopup();
|
|
958
|
+
if (!opened) {
|
|
959
|
+
this.abortInflightOpen();
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
686
963
|
await this.initialize();
|
|
687
|
-
|
|
688
|
-
MessageRouter.setMode(
|
|
964
|
+
if (signal.aborted) return;
|
|
965
|
+
MessageRouter.setMode(mode);
|
|
689
966
|
MessageRouter.addEventListeners();
|
|
967
|
+
if (signal.aborted) {
|
|
968
|
+
MessageRouter.removeEventListeners();
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
this.openUI();
|
|
690
972
|
}
|
|
691
973
|
openUI() {
|
|
692
974
|
if (this.iframe) return;
|
|
975
|
+
const headless = this.isLoginPopupOnly();
|
|
693
976
|
this.container = document.createElement("div");
|
|
694
|
-
|
|
977
|
+
if (headless) {
|
|
978
|
+
applyHeadlessContainerStyle(this.container);
|
|
979
|
+
} else {
|
|
980
|
+
applyOverlayBaseStyle(this.container);
|
|
981
|
+
}
|
|
695
982
|
console.debug("[Connect SDK] endpoints", Endpoints);
|
|
696
983
|
console.log("[Connect SDK] opeining URL", Endpoints.benji_connect_auth_url);
|
|
697
984
|
const url = new URL(Endpoints.benji_connect_auth_url);
|
|
@@ -705,48 +992,88 @@ var ConnectSDK = (() => {
|
|
|
705
992
|
this.iframe.src = url.toString();
|
|
706
993
|
this.container.appendChild(this.iframe);
|
|
707
994
|
document.body.appendChild(this.container);
|
|
708
|
-
|
|
709
|
-
this.
|
|
710
|
-
this.
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
)
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
995
|
+
if (headless) {
|
|
996
|
+
applyHeadlessIframeStyle(this.iframe);
|
|
997
|
+
this.repositionListeners = attachRepositionListeners(
|
|
998
|
+
this.sdkConfig,
|
|
999
|
+
() => this.repositionLoginPopup()
|
|
1000
|
+
);
|
|
1001
|
+
} else {
|
|
1002
|
+
applyIframeLayoutToElements(
|
|
1003
|
+
this.sdkConfig,
|
|
1004
|
+
this.container,
|
|
1005
|
+
this.iframe,
|
|
1006
|
+
this.layoutState,
|
|
1007
|
+
{ warnOnInvalidAnchor: true }
|
|
1008
|
+
);
|
|
1009
|
+
this.repositionListeners = attachRepositionListeners(
|
|
1010
|
+
this.sdkConfig,
|
|
1011
|
+
() => {
|
|
1012
|
+
if (!this.iframe || !this.container) return;
|
|
1013
|
+
applyIframeLayoutToElements(
|
|
1014
|
+
this.sdkConfig,
|
|
1015
|
+
this.container,
|
|
1016
|
+
this.iframe,
|
|
1017
|
+
this.layoutState
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
);
|
|
1021
|
+
this.container.addEventListener("click", (e) => {
|
|
1022
|
+
var _a, _b;
|
|
1023
|
+
if (e.target === this.container) {
|
|
1024
|
+
(_b = (_a = this.sdkConfig).onExit) == null ? void 0 : _b.call(_a, {
|
|
1025
|
+
context: buildContext(),
|
|
1026
|
+
trigger: "TAPPED_OUT_OF_BOUNDS" /* TAPPED_OUT_OF_BOUNDS */
|
|
1027
|
+
});
|
|
1028
|
+
this.close();
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
navigateLoginPopup(url) {
|
|
1034
|
+
if (!this.loginPopup || this.loginPopup.closed) {
|
|
1035
|
+
console.warn("[Connect SDK] Cannot navigate LOGIN popup \u2014 window unavailable");
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
try {
|
|
1039
|
+
this.loginPopup.location.href = url;
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
console.warn("[Connect SDK] Failed to navigate LOGIN popup", error);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
closeLoginPopup() {
|
|
1045
|
+
if (!this.loginPopup) return;
|
|
1046
|
+
try {
|
|
1047
|
+
if (!this.loginPopup.closed) {
|
|
1048
|
+
this.loginPopup.close();
|
|
735
1049
|
}
|
|
736
|
-
})
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
console.warn("[Connect SDK] Error closing LOGIN popup", error);
|
|
1052
|
+
} finally {
|
|
1053
|
+
this.loginPopup = null;
|
|
1054
|
+
}
|
|
737
1055
|
}
|
|
738
1056
|
close() {
|
|
739
1057
|
var _a, _b;
|
|
740
|
-
|
|
1058
|
+
this.abortInflightOpen();
|
|
1059
|
+
this.isClosing = true;
|
|
1060
|
+
this.stopPopupClosedPoll();
|
|
1061
|
+
this.closeLoginPopup();
|
|
1062
|
+
MessageRouter.removeEventListeners();
|
|
1063
|
+
if (!this.iframe) {
|
|
1064
|
+
this.isClosing = false;
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
741
1067
|
(_a = this.repositionListeners) == null ? void 0 : _a.remove();
|
|
742
1068
|
this.repositionListeners = null;
|
|
743
1069
|
this.layoutState.lastValidAnchoredLayout = null;
|
|
1070
|
+
this.popupLayoutState.lastValidAnchoredLayout = null;
|
|
744
1071
|
if ((_b = this.container) == null ? void 0 : _b.parentNode) {
|
|
745
1072
|
this.container.parentNode.removeChild(this.container);
|
|
746
1073
|
}
|
|
747
|
-
MessageRouter.removeEventListeners();
|
|
748
1074
|
this.iframe = null;
|
|
749
1075
|
this.container = null;
|
|
1076
|
+
this.isClosing = false;
|
|
750
1077
|
}
|
|
751
1078
|
cleanup() {
|
|
752
1079
|
if (this.iframe) this.close();
|