@authon/js 0.1.7 → 0.1.9

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/index.js CHANGED
@@ -131,6 +131,25 @@ var ModalRenderer = class {
131
131
  this.containerElement.innerHTML = "";
132
132
  }
133
133
  }
134
+ showLoading() {
135
+ if (!this.shadowRoot) return;
136
+ this.hideLoading();
137
+ const overlay = document.createElement("div");
138
+ overlay.id = "authon-loading-overlay";
139
+ overlay.innerHTML = `
140
+ <div class="loading-spinner">
141
+ <div class="loading-ring"></div>
142
+ <div class="loading-ring"></div>
143
+ <div class="loading-ring"></div>
144
+ </div>
145
+ <div class="loading-text">Signing in<span class="loading-dots"><span></span><span></span><span></span></span></div>
146
+ `;
147
+ this.shadowRoot.querySelector(".modal-container")?.appendChild(overlay);
148
+ }
149
+ hideLoading() {
150
+ if (!this.shadowRoot) return;
151
+ this.shadowRoot.getElementById("authon-loading-overlay")?.remove();
152
+ }
134
153
  render(view) {
135
154
  const host = document.createElement("div");
136
155
  host.setAttribute("data-authon-modal", "");
@@ -234,6 +253,7 @@ var ModalRenderer = class {
234
253
  border-radius: var(--authon-radius);
235
254
  padding: 32px;
236
255
  width: 400px; max-width: 100%;
256
+ position: ${this.mode === "popup" ? "fixed" : "relative"};
237
257
  ${this.mode === "popup" ? `box-shadow: 0 25px 50px -12px rgba(0,0,0,${dark ? "0.5" : "0.25"}); animation: slideIn 0.3s ease;` : ""}
238
258
  }
239
259
  .logo { display: block; margin: 0 auto 16px; max-height: 48px; }
@@ -288,6 +308,39 @@ var ModalRenderer = class {
288
308
  font-size: 11px; color: var(--authon-dim);
289
309
  }
290
310
  .secured-brand { font-weight: 600; color: var(--authon-muted); }
311
+ /* Loading overlay */
312
+ #authon-loading-overlay {
313
+ position: absolute; inset: 0; z-index: 10;
314
+ background: ${dark ? "rgba(15,23,42,0.92)" : "rgba(255,255,255,0.92)"};
315
+ backdrop-filter: blur(2px);
316
+ border-radius: var(--authon-radius);
317
+ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
318
+ animation: fadeIn 0.15s ease;
319
+ }
320
+ .loading-spinner { position: relative; width: 48px; height: 48px; }
321
+ .loading-ring {
322
+ position: absolute; inset: 0;
323
+ border: 2.5px solid transparent; border-top-color: var(--authon-primary-start);
324
+ border-radius: 50%; animation: spin 1s cubic-bezier(.55,.15,.45,.85) infinite;
325
+ }
326
+ .loading-ring:nth-child(2) {
327
+ inset: 5px; border-top-color: transparent; border-right-color: var(--authon-primary-end);
328
+ animation-duration: 1.2s; animation-direction: reverse; opacity: .7;
329
+ }
330
+ .loading-ring:nth-child(3) {
331
+ inset: 10px; border-top-color: transparent; border-bottom-color: var(--authon-primary-start);
332
+ animation-duration: .8s; opacity: .4;
333
+ }
334
+ .loading-text { font-size: 14px; font-weight: 500; color: var(--authon-muted); }
335
+ .loading-dots { display: inline-flex; gap: 2px; margin-left: 2px; }
336
+ .loading-dots span {
337
+ width: 3px; height: 3px; border-radius: 50%;
338
+ background: var(--authon-muted); animation: blink 1.4s infinite both;
339
+ }
340
+ .loading-dots span:nth-child(2) { animation-delay: .2s; }
341
+ .loading-dots span:nth-child(3) { animation-delay: .4s; }
342
+ @keyframes spin { to { transform: rotate(360deg); } }
343
+ @keyframes blink { 0%,80%,100% { opacity: .2; } 40% { opacity: 1; } }
291
344
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
292
345
  @keyframes slideIn { from { opacity: 0; transform: translate(-50%, -48%); } to { opacity: 1; transform: translate(-50%, -50%); } }
293
346
  ${b.customCss || ""}
@@ -542,6 +595,7 @@ var Authon = class {
542
595
  const { url } = await this.apiGet(
543
596
  `/v1/auth/oauth/${provider}/url?redirectUri=${encodeURIComponent(redirectUri)}`
544
597
  );
598
+ this.modal?.showLoading();
545
599
  const width = 500;
546
600
  const height = 700;
547
601
  const left = window.screenX + (window.outerWidth - width) / 2;
@@ -551,30 +605,56 @@ var Authon = class {
551
605
  "authon-oauth",
552
606
  `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no`
553
607
  );
608
+ let callbackReceived = false;
609
+ let cleaned = false;
610
+ const cleanup = () => {
611
+ if (cleaned) return;
612
+ cleaned = true;
613
+ window.removeEventListener("message", handler);
614
+ window.removeEventListener("focus", focusHandler);
615
+ if (maxTimer) clearTimeout(maxTimer);
616
+ };
617
+ const focusHandler = () => {
618
+ if (callbackReceived || cleaned) return;
619
+ setTimeout(() => {
620
+ if (callbackReceived || cleaned) return;
621
+ cleanup();
622
+ this.modal?.hideLoading();
623
+ }, 1500);
624
+ };
625
+ window.addEventListener("focus", focusHandler);
626
+ const maxTimer = setTimeout(() => {
627
+ if (callbackReceived || cleaned) return;
628
+ cleanup();
629
+ this.modal?.hideLoading();
630
+ }, 18e4);
631
+ if (!popup) {
632
+ cleanup();
633
+ this.modal?.hideLoading();
634
+ this.emit("error", new Error("Popup was blocked by the browser"));
635
+ return;
636
+ }
554
637
  const handler = async (e) => {
555
- if (e.data?.type === "authon-oauth-callback") {
556
- window.removeEventListener("message", handler);
557
- try {
558
- popup?.close();
559
- } catch {
560
- }
561
- try {
562
- const tokens = await this.apiPost("/v1/auth/oauth/callback", {
563
- code: e.data.code,
564
- state: e.data.state,
565
- codeVerifier: e.data.codeVerifier,
566
- provider
567
- });
568
- this.session.setSession(tokens);
569
- this.modal?.close();
570
- this.emit("signedIn", tokens.user);
571
- } catch (err) {
572
- this.emit("error", err instanceof Error ? err : new Error(String(err)));
573
- }
638
+ if (e.data?.type !== "authon-oauth-callback") return;
639
+ callbackReceived = true;
640
+ cleanup();
641
+ try {
642
+ const tokens = await this.apiPost("/v1/auth/oauth/callback", {
643
+ code: e.data.code,
644
+ state: e.data.state,
645
+ provider
646
+ });
647
+ this.session.setSession(tokens);
648
+ this.modal?.close();
649
+ this.emit("signedIn", tokens.user);
650
+ } catch (err) {
651
+ this.modal?.hideLoading();
652
+ this.emit("error", err instanceof Error ? err : new Error(String(err)));
574
653
  }
575
654
  };
576
655
  window.addEventListener("message", handler);
577
656
  } catch (err) {
657
+ this.modal?.hideLoading();
578
658
  this.emit("error", err instanceof Error ? err : new Error(String(err)));
579
659
  }
580
660
  }
package/dist/index.mjs CHANGED
@@ -104,6 +104,25 @@ var ModalRenderer = class {
104
104
  this.containerElement.innerHTML = "";
105
105
  }
106
106
  }
107
+ showLoading() {
108
+ if (!this.shadowRoot) return;
109
+ this.hideLoading();
110
+ const overlay = document.createElement("div");
111
+ overlay.id = "authon-loading-overlay";
112
+ overlay.innerHTML = `
113
+ <div class="loading-spinner">
114
+ <div class="loading-ring"></div>
115
+ <div class="loading-ring"></div>
116
+ <div class="loading-ring"></div>
117
+ </div>
118
+ <div class="loading-text">Signing in<span class="loading-dots"><span></span><span></span><span></span></span></div>
119
+ `;
120
+ this.shadowRoot.querySelector(".modal-container")?.appendChild(overlay);
121
+ }
122
+ hideLoading() {
123
+ if (!this.shadowRoot) return;
124
+ this.shadowRoot.getElementById("authon-loading-overlay")?.remove();
125
+ }
107
126
  render(view) {
108
127
  const host = document.createElement("div");
109
128
  host.setAttribute("data-authon-modal", "");
@@ -207,6 +226,7 @@ var ModalRenderer = class {
207
226
  border-radius: var(--authon-radius);
208
227
  padding: 32px;
209
228
  width: 400px; max-width: 100%;
229
+ position: ${this.mode === "popup" ? "fixed" : "relative"};
210
230
  ${this.mode === "popup" ? `box-shadow: 0 25px 50px -12px rgba(0,0,0,${dark ? "0.5" : "0.25"}); animation: slideIn 0.3s ease;` : ""}
211
231
  }
212
232
  .logo { display: block; margin: 0 auto 16px; max-height: 48px; }
@@ -261,6 +281,39 @@ var ModalRenderer = class {
261
281
  font-size: 11px; color: var(--authon-dim);
262
282
  }
263
283
  .secured-brand { font-weight: 600; color: var(--authon-muted); }
284
+ /* Loading overlay */
285
+ #authon-loading-overlay {
286
+ position: absolute; inset: 0; z-index: 10;
287
+ background: ${dark ? "rgba(15,23,42,0.92)" : "rgba(255,255,255,0.92)"};
288
+ backdrop-filter: blur(2px);
289
+ border-radius: var(--authon-radius);
290
+ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
291
+ animation: fadeIn 0.15s ease;
292
+ }
293
+ .loading-spinner { position: relative; width: 48px; height: 48px; }
294
+ .loading-ring {
295
+ position: absolute; inset: 0;
296
+ border: 2.5px solid transparent; border-top-color: var(--authon-primary-start);
297
+ border-radius: 50%; animation: spin 1s cubic-bezier(.55,.15,.45,.85) infinite;
298
+ }
299
+ .loading-ring:nth-child(2) {
300
+ inset: 5px; border-top-color: transparent; border-right-color: var(--authon-primary-end);
301
+ animation-duration: 1.2s; animation-direction: reverse; opacity: .7;
302
+ }
303
+ .loading-ring:nth-child(3) {
304
+ inset: 10px; border-top-color: transparent; border-bottom-color: var(--authon-primary-start);
305
+ animation-duration: .8s; opacity: .4;
306
+ }
307
+ .loading-text { font-size: 14px; font-weight: 500; color: var(--authon-muted); }
308
+ .loading-dots { display: inline-flex; gap: 2px; margin-left: 2px; }
309
+ .loading-dots span {
310
+ width: 3px; height: 3px; border-radius: 50%;
311
+ background: var(--authon-muted); animation: blink 1.4s infinite both;
312
+ }
313
+ .loading-dots span:nth-child(2) { animation-delay: .2s; }
314
+ .loading-dots span:nth-child(3) { animation-delay: .4s; }
315
+ @keyframes spin { to { transform: rotate(360deg); } }
316
+ @keyframes blink { 0%,80%,100% { opacity: .2; } 40% { opacity: 1; } }
264
317
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
265
318
  @keyframes slideIn { from { opacity: 0; transform: translate(-50%, -48%); } to { opacity: 1; transform: translate(-50%, -50%); } }
266
319
  ${b.customCss || ""}
@@ -515,6 +568,7 @@ var Authon = class {
515
568
  const { url } = await this.apiGet(
516
569
  `/v1/auth/oauth/${provider}/url?redirectUri=${encodeURIComponent(redirectUri)}`
517
570
  );
571
+ this.modal?.showLoading();
518
572
  const width = 500;
519
573
  const height = 700;
520
574
  const left = window.screenX + (window.outerWidth - width) / 2;
@@ -524,30 +578,56 @@ var Authon = class {
524
578
  "authon-oauth",
525
579
  `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no`
526
580
  );
581
+ let callbackReceived = false;
582
+ let cleaned = false;
583
+ const cleanup = () => {
584
+ if (cleaned) return;
585
+ cleaned = true;
586
+ window.removeEventListener("message", handler);
587
+ window.removeEventListener("focus", focusHandler);
588
+ if (maxTimer) clearTimeout(maxTimer);
589
+ };
590
+ const focusHandler = () => {
591
+ if (callbackReceived || cleaned) return;
592
+ setTimeout(() => {
593
+ if (callbackReceived || cleaned) return;
594
+ cleanup();
595
+ this.modal?.hideLoading();
596
+ }, 1500);
597
+ };
598
+ window.addEventListener("focus", focusHandler);
599
+ const maxTimer = setTimeout(() => {
600
+ if (callbackReceived || cleaned) return;
601
+ cleanup();
602
+ this.modal?.hideLoading();
603
+ }, 18e4);
604
+ if (!popup) {
605
+ cleanup();
606
+ this.modal?.hideLoading();
607
+ this.emit("error", new Error("Popup was blocked by the browser"));
608
+ return;
609
+ }
527
610
  const handler = async (e) => {
528
- if (e.data?.type === "authon-oauth-callback") {
529
- window.removeEventListener("message", handler);
530
- try {
531
- popup?.close();
532
- } catch {
533
- }
534
- try {
535
- const tokens = await this.apiPost("/v1/auth/oauth/callback", {
536
- code: e.data.code,
537
- state: e.data.state,
538
- codeVerifier: e.data.codeVerifier,
539
- provider
540
- });
541
- this.session.setSession(tokens);
542
- this.modal?.close();
543
- this.emit("signedIn", tokens.user);
544
- } catch (err) {
545
- this.emit("error", err instanceof Error ? err : new Error(String(err)));
546
- }
611
+ if (e.data?.type !== "authon-oauth-callback") return;
612
+ callbackReceived = true;
613
+ cleanup();
614
+ try {
615
+ const tokens = await this.apiPost("/v1/auth/oauth/callback", {
616
+ code: e.data.code,
617
+ state: e.data.state,
618
+ provider
619
+ });
620
+ this.session.setSession(tokens);
621
+ this.modal?.close();
622
+ this.emit("signedIn", tokens.user);
623
+ } catch (err) {
624
+ this.modal?.hideLoading();
625
+ this.emit("error", err instanceof Error ? err : new Error(String(err)));
547
626
  }
548
627
  };
549
628
  window.addEventListener("message", handler);
550
629
  } catch (err) {
630
+ this.modal?.hideLoading();
551
631
  this.emit("error", err instanceof Error ? err : new Error(String(err)));
552
632
  }
553
633
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authon/js",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Authon core SDK — ShadowDOM login modal for any app",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",