@authon/js 0.1.7 → 0.1.8

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,42 @@ var Authon = class {
551
605
  "authon-oauth",
552
606
  `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no`
553
607
  );
554
- 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)));
608
+ let callbackReceived = false;
609
+ const pollTimer = setInterval(() => {
610
+ if (callbackReceived) return;
611
+ try {
612
+ if (!popup || popup.closed) {
613
+ clearInterval(pollTimer);
614
+ if (!callbackReceived) {
615
+ window.removeEventListener("message", handler);
616
+ this.modal?.hideLoading();
617
+ }
573
618
  }
619
+ } catch {
620
+ }
621
+ }, 500);
622
+ const handler = async (e) => {
623
+ if (e.data?.type !== "authon-oauth-callback") return;
624
+ callbackReceived = true;
625
+ window.removeEventListener("message", handler);
626
+ clearInterval(pollTimer);
627
+ try {
628
+ const tokens = await this.apiPost("/v1/auth/oauth/callback", {
629
+ code: e.data.code,
630
+ state: e.data.state,
631
+ provider
632
+ });
633
+ this.session.setSession(tokens);
634
+ this.modal?.close();
635
+ this.emit("signedIn", tokens.user);
636
+ } catch (err) {
637
+ this.modal?.hideLoading();
638
+ this.emit("error", err instanceof Error ? err : new Error(String(err)));
574
639
  }
575
640
  };
576
641
  window.addEventListener("message", handler);
577
642
  } catch (err) {
643
+ this.modal?.hideLoading();
578
644
  this.emit("error", err instanceof Error ? err : new Error(String(err)));
579
645
  }
580
646
  }
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,42 @@ var Authon = class {
524
578
  "authon-oauth",
525
579
  `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no`
526
580
  );
527
- 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)));
581
+ let callbackReceived = false;
582
+ const pollTimer = setInterval(() => {
583
+ if (callbackReceived) return;
584
+ try {
585
+ if (!popup || popup.closed) {
586
+ clearInterval(pollTimer);
587
+ if (!callbackReceived) {
588
+ window.removeEventListener("message", handler);
589
+ this.modal?.hideLoading();
590
+ }
546
591
  }
592
+ } catch {
593
+ }
594
+ }, 500);
595
+ const handler = async (e) => {
596
+ if (e.data?.type !== "authon-oauth-callback") return;
597
+ callbackReceived = true;
598
+ window.removeEventListener("message", handler);
599
+ clearInterval(pollTimer);
600
+ try {
601
+ const tokens = await this.apiPost("/v1/auth/oauth/callback", {
602
+ code: e.data.code,
603
+ state: e.data.state,
604
+ provider
605
+ });
606
+ this.session.setSession(tokens);
607
+ this.modal?.close();
608
+ this.emit("signedIn", tokens.user);
609
+ } catch (err) {
610
+ this.modal?.hideLoading();
611
+ this.emit("error", err instanceof Error ? err : new Error(String(err)));
547
612
  }
548
613
  };
549
614
  window.addEventListener("message", handler);
550
615
  } catch (err) {
616
+ this.modal?.hideLoading();
551
617
  this.emit("error", err instanceof Error ? err : new Error(String(err)));
552
618
  }
553
619
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authon/js",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Authon core SDK — ShadowDOM login modal for any app",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",