@zoreal/oauth2-react 0.2.20 → 0.2.22

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.d.cts CHANGED
@@ -286,10 +286,10 @@ declare function PairingModal({ state, qrUrl, onCancel, locale, theme, timeoutMs
286
286
 
287
287
  declare function useZorealLogin(options: {
288
288
  flow?: 'browser-direct';
289
- } & BrowserDirectFlowOptions): () => void;
289
+ } & BrowserDirectFlowOptions): (event?: unknown) => void;
290
290
  declare function useZorealLogin(options: {
291
291
  flow: 'auth-code';
292
- } & AuthCodeFlowOptions): () => void;
292
+ } & AuthCodeFlowOptions): (event?: unknown) => void;
293
293
 
294
294
  /**
295
295
  * Silent re-auth with prompt=none. NOT One Tap and never could be: there is no
package/dist/index.d.ts CHANGED
@@ -286,10 +286,10 @@ declare function PairingModal({ state, qrUrl, onCancel, locale, theme, timeoutMs
286
286
 
287
287
  declare function useZorealLogin(options: {
288
288
  flow?: 'browser-direct';
289
- } & BrowserDirectFlowOptions): () => void;
289
+ } & BrowserDirectFlowOptions): (event?: unknown) => void;
290
290
  declare function useZorealLogin(options: {
291
291
  flow: 'auth-code';
292
- } & AuthCodeFlowOptions): () => void;
292
+ } & AuthCodeFlowOptions): (event?: unknown) => void;
293
293
 
294
294
  /**
295
295
  * Silent re-auth with prompt=none. NOT One Tap and never could be: there is no
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { createContext, useContext, useMemo, useState as useState2 } from "react
5
5
 
6
6
  // src/wire.ts
7
7
  var WIRE_VERSION = 1;
8
- var SDK_VERSION = "0.2.20";
8
+ var SDK_VERSION = "0.2.22";
9
9
  var DEFAULT_ISSUER = "https://id.zoreal.com";
10
10
  var POLL_INTERVAL_MS = 2e3;
11
11
  var POLL_INTERVAL_ENROLLING_MS = 5e3;
@@ -1360,6 +1360,16 @@ var CSS = `
1360
1360
  transition: opacity 200ms ease-out;
1361
1361
  }
1362
1362
  .${PREFIX}-ring[data-busy="true"] > .${PREFIX}-ring-svg { opacity: 1; }
1363
+ /* The same light as an overlay in the document body, placed over a site's
1364
+ own control by the package itself (busy.ts): nothing of the site's
1365
+ markup or CSS is touched, and neither an ancestor's overflow nor a
1366
+ selector on the control's parent is affected. */
1367
+ .${PREFIX}-ring-overlay {
1368
+ position: fixed;
1369
+ display: block;
1370
+ z-index: 2147483000;
1371
+ pointer-events: none;
1372
+ }
1363
1373
  .${PREFIX}-ring-svg rect {
1364
1374
  --zrl-l: var(--zrl-ring-len, 600px);
1365
1375
  x: 2px;
@@ -1595,11 +1605,101 @@ function useZorealOAuth() {
1595
1605
  }
1596
1606
 
1597
1607
  // src/ZorealLogin.tsx
1598
- import { useMemo as useMemo2, useState as useState4 } from "react";
1608
+ import { useMemo as useMemo2 } from "react";
1599
1609
 
1600
1610
  // src/useZorealLogin.ts
1601
1611
  import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
1602
1612
 
1613
+ // src/busy.ts
1614
+ var SVG_NS = "http://www.w3.org/2000/svg";
1615
+ var TAIL = 0.3;
1616
+ var STACK = Array.from({ length: 12 }, (_, i) => 12 - i);
1617
+ var HALO = [3, 2, 1];
1618
+ function radiusOf(control) {
1619
+ const value = parseFloat(getComputedStyle(control).borderTopLeftRadius);
1620
+ return Number.isFinite(value) ? value : 8;
1621
+ }
1622
+ function holdBusy(control) {
1623
+ if (typeof document === "undefined") return () => {
1624
+ };
1625
+ ensureStyles();
1626
+ const hadDisabled = control.hasAttribute("disabled");
1627
+ const hadBusy = control.getAttribute("aria-busy");
1628
+ if (control instanceof HTMLButtonElement || control instanceof HTMLInputElement) {
1629
+ control.disabled = true;
1630
+ } else {
1631
+ control.setAttribute("aria-disabled", "true");
1632
+ }
1633
+ control.setAttribute("aria-busy", "true");
1634
+ const overlay = document.createElement("div");
1635
+ overlay.className = `${cx("root")} ${cx("ring")} ${cx("ring-overlay")}`;
1636
+ overlay.dataset.theme = "auto";
1637
+ overlay.dataset.busy = "true";
1638
+ overlay.setAttribute("aria-hidden", "true");
1639
+ const svg = document.createElementNS(SVG_NS, "svg");
1640
+ svg.setAttribute("class", cx("ring-svg"));
1641
+ const rx = radiusOf(control) + 2;
1642
+ const layer = (name, len, alpha) => {
1643
+ const rect = document.createElementNS(SVG_NS, "rect");
1644
+ rect.setAttribute("class", cx(name));
1645
+ rect.setAttribute("rx", String(rx));
1646
+ rect.setAttribute("ry", String(rx));
1647
+ rect.style.strokeDasharray = `calc(var(--zrl-l) * ${len}) calc(var(--zrl-l) * ${1 - len})`;
1648
+ rect.style.setProperty("--zrl-s", `calc(var(--zrl-l) * ${-(TAIL - len)})`);
1649
+ rect.style.opacity = alpha;
1650
+ svg.appendChild(rect);
1651
+ return rect;
1652
+ };
1653
+ let measured = null;
1654
+ for (const n of HALO) {
1655
+ const rect = layer("ring-halo", TAIL * n / HALO.length, `calc(var(--zrl-glow-opacity) * 0.6 / ${n})`);
1656
+ measured ?? (measured = rect);
1657
+ }
1658
+ for (const n of STACK) layer(n <= 2 ? "ring-head" : "ring-tail", TAIL * n / STACK.length, String(1 / n));
1659
+ overlay.appendChild(svg);
1660
+ document.body.appendChild(overlay);
1661
+ let frame = 0;
1662
+ const place = () => {
1663
+ frame = 0;
1664
+ const box = control.getBoundingClientRect();
1665
+ overlay.style.left = `${box.left}px`;
1666
+ overlay.style.top = `${box.top}px`;
1667
+ overlay.style.width = `${box.width}px`;
1668
+ overlay.style.height = `${box.height}px`;
1669
+ const length = typeof measured?.getTotalLength === "function" ? measured.getTotalLength() : 0;
1670
+ if (length > 0) overlay.style.setProperty("--zrl-ring-len", `${length}px`);
1671
+ };
1672
+ const schedule = () => {
1673
+ if (!frame) frame = requestAnimationFrame(place);
1674
+ };
1675
+ place();
1676
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(schedule);
1677
+ observer?.observe(control);
1678
+ window.addEventListener("scroll", schedule, true);
1679
+ window.addEventListener("resize", schedule);
1680
+ let released = false;
1681
+ return () => {
1682
+ if (released) return;
1683
+ released = true;
1684
+ if (frame) cancelAnimationFrame(frame);
1685
+ observer?.disconnect();
1686
+ window.removeEventListener("scroll", schedule, true);
1687
+ window.removeEventListener("resize", schedule);
1688
+ overlay.remove();
1689
+ if (control instanceof HTMLButtonElement || control instanceof HTMLInputElement) {
1690
+ control.disabled = hadDisabled;
1691
+ } else {
1692
+ control.removeAttribute("aria-disabled");
1693
+ }
1694
+ if (hadBusy === null) control.removeAttribute("aria-busy");
1695
+ else control.setAttribute("aria-busy", hadBusy);
1696
+ };
1697
+ }
1698
+ function controlFrom(event) {
1699
+ const target = event?.currentTarget;
1700
+ return typeof HTMLElement !== "undefined" && target instanceof HTMLElement ? target : null;
1701
+ }
1702
+
1603
1703
  // src/return.ts
1604
1704
  var PREFIX2 = "zoreal:oauth2:return:";
1605
1705
  var DONE = "zoreal:oauth2:done:";
@@ -2064,6 +2164,7 @@ function useZorealFlow(options) {
2064
2164
  () => () => {
2065
2165
  abortRef.current?.abort();
2066
2166
  publishRef.current?.(null);
2167
+ releaseRef.current();
2067
2168
  },
2068
2169
  []
2069
2170
  );
@@ -2122,10 +2223,21 @@ function useZorealFlow(options) {
2122
2223
  }
2123
2224
  })();
2124
2225
  }, [clientId, issuer]);
2125
- const login = useCallback(() => {
2226
+ const releaseRef = useRef2(() => {
2227
+ });
2228
+ const login = useCallback((event) => {
2126
2229
  const opts = optionsRef.current;
2230
+ const control = controlFrom(event);
2127
2231
  const run = async () => {
2128
2232
  abortRef.current?.abort();
2233
+ releaseRef.current();
2234
+ releaseRef.current = control ? holdBusy(control) : () => {
2235
+ };
2236
+ const release = () => {
2237
+ releaseRef.current();
2238
+ releaseRef.current = () => {
2239
+ };
2240
+ };
2129
2241
  const controller = new AbortController();
2130
2242
  abortRef.current = controller;
2131
2243
  const flow = opts.flow;
@@ -2274,6 +2386,7 @@ function useZorealFlow(options) {
2274
2386
  setPairing(null);
2275
2387
  publishRef.current?.(null);
2276
2388
  if (returnId) markReturnDone(returnId);
2389
+ release();
2277
2390
  if (flow === "auth-code") {
2278
2391
  opts.onCode?.({
2279
2392
  code,
@@ -2298,6 +2411,7 @@ function useZorealFlow(options) {
2298
2411
  };
2299
2412
  opts.onCredential?.(response);
2300
2413
  } catch (e) {
2414
+ release();
2301
2415
  setPairing(null);
2302
2416
  publishRef.current?.(null);
2303
2417
  if (e instanceof DOMException && e.name === "AbortError") {
@@ -2343,79 +2457,8 @@ function useZorealLogin(options) {
2343
2457
  }).login;
2344
2458
  }
2345
2459
 
2346
- // src/ring.tsx
2347
- import { useEffect as useEffect3, useLayoutEffect, useRef as useRef3 } from "react";
2348
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2349
- var TAIL = 0.3;
2350
- var STACK = Array.from({ length: 12 }, (_, i) => 12 - i);
2351
- var HALO = [3, 2, 1];
2352
- function ZorealBusyRing({
2353
- busy,
2354
- radius = 8,
2355
- theme = "auto",
2356
- block = false,
2357
- className,
2358
- style,
2359
- children
2360
- }) {
2361
- useEffect3(() => {
2362
- ensureStyles();
2363
- }, []);
2364
- const wrapRef = useRef3(null);
2365
- const measureRef = useRef3(null);
2366
- useLayoutEffect(() => {
2367
- const wrap = wrapRef.current;
2368
- const rect = measureRef.current;
2369
- if (!wrap || !rect) return;
2370
- const measure = () => {
2371
- const length = rect.getTotalLength();
2372
- if (length > 0) wrap.style.setProperty("--zrl-ring-len", `${length}px`);
2373
- };
2374
- measure();
2375
- if (typeof ResizeObserver === "undefined") return;
2376
- const observer = new ResizeObserver(measure);
2377
- observer.observe(wrap);
2378
- return () => observer.disconnect();
2379
- }, [radius]);
2380
- const rx = radius + 2;
2381
- const layer = (key, name, len, alpha, ref) => /* @__PURE__ */ jsx5(
2382
- "rect",
2383
- {
2384
- ref,
2385
- className: cx(name),
2386
- rx,
2387
- ry: rx,
2388
- style: {
2389
- strokeDasharray: `calc(var(--zrl-l) * ${len}) calc(var(--zrl-l) * ${1 - len})`,
2390
- "--zrl-s": `calc(var(--zrl-l) * ${-(TAIL - len)})`,
2391
- opacity: alpha
2392
- }
2393
- },
2394
- key
2395
- );
2396
- return /* @__PURE__ */ jsxs5(
2397
- "span",
2398
- {
2399
- ref: wrapRef,
2400
- className: className ? `${cx("root")} ${cx("ring")} ${className}` : `${cx("root")} ${cx("ring")}`,
2401
- "data-theme": theme,
2402
- "data-busy": busy,
2403
- style: block ? { display: "flex", ...style } : style,
2404
- children: [
2405
- children,
2406
- /* @__PURE__ */ jsxs5("svg", { className: cx("ring-svg"), "aria-hidden": "true", children: [
2407
- HALO.map(
2408
- (n, i) => layer(`h${n}`, "ring-halo", TAIL * n / HALO.length, `calc(var(--zrl-glow-opacity) * 0.6 / ${n})`, i === 0 ? measureRef : void 0)
2409
- ),
2410
- STACK.map((n) => layer(`t${n}`, n <= 2 ? "ring-head" : "ring-tail", TAIL * n / STACK.length, String(1 / n)))
2411
- ] })
2412
- ]
2413
- }
2414
- );
2415
- }
2416
-
2417
2460
  // src/ZorealLogin.tsx
2418
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2461
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2419
2462
  var TEXTS = {
2420
2463
  continue_with: null,
2421
2464
  signin_with: "Sign in with ZOREAL",
@@ -2446,26 +2489,13 @@ function ZorealLogin(props) {
2446
2489
  } = props;
2447
2490
  const { locale } = useZorealOAuth();
2448
2491
  const label = TEXTS[text] ?? strings(locale).buttonContinue;
2449
- const [busy, setBusy] = useState4(false);
2450
2492
  const { login } = useZorealFlow({
2451
2493
  ...request,
2452
2494
  flow,
2453
- onCredential: flow === "browser-direct" ? (r) => {
2454
- setBusy(false);
2455
- onSuccess(r);
2456
- } : void 0,
2457
- onCode: flow === "auth-code" ? (r) => {
2458
- setBusy(false);
2459
- onSuccess(r);
2460
- } : void 0,
2461
- onError: (e) => {
2462
- setBusy(false);
2463
- onError?.({ type: "unknown", description: e.description ?? e.error });
2464
- },
2465
- onNonOAuthError: (e) => {
2466
- setBusy(false);
2467
- onError?.(e);
2468
- }
2495
+ onCredential: flow === "browser-direct" ? onSuccess : void 0,
2496
+ onCode: flow === "auth-code" ? onSuccess : void 0,
2497
+ onError: (e) => onError?.({ type: "unknown", description: e.description ?? e.error }),
2498
+ onNonOAuthError: (e) => onError?.(e)
2469
2499
  });
2470
2500
  const s = SIZES[size];
2471
2501
  const radius = shape === "pill" ? s.height / 2 : shape === "square" ? 4 : s.radius;
@@ -2488,25 +2518,92 @@ function ZorealLogin(props) {
2488
2518
  }),
2489
2519
  [logo_alignment, s, radius, theme, width]
2490
2520
  );
2491
- return /* @__PURE__ */ jsx6("div", { ...containerProps, children: /* @__PURE__ */ jsx6(ZorealBusyRing, { busy, radius, theme: theme === "outline" ? "auto" : "light", children: /* @__PURE__ */ jsxs6(
2521
+ return /* @__PURE__ */ jsx5("div", { ...containerProps, children: /* @__PURE__ */ jsxs5(
2492
2522
  "button",
2493
2523
  {
2494
2524
  type: "button",
2495
- style: busy ? { ...style, cursor: "progress" } : style,
2496
- disabled: busy,
2497
- "aria-busy": busy,
2498
- onClick: () => {
2499
- if (busy) return;
2525
+ style,
2526
+ onClick: (event) => {
2500
2527
  click_listener?.();
2501
- setBusy(true);
2502
- login();
2528
+ login(event);
2503
2529
  },
2504
2530
  children: [
2505
- /* @__PURE__ */ jsx6(ZorealMark, { size: s.mark, brand: brandMark }),
2531
+ /* @__PURE__ */ jsx5(ZorealMark, { size: s.mark, brand: brandMark }),
2506
2532
  type === "standard" && label
2507
2533
  ]
2508
2534
  }
2509
- ) }) });
2535
+ ) });
2536
+ }
2537
+
2538
+ // src/ring.tsx
2539
+ import { useEffect as useEffect3, useLayoutEffect, useRef as useRef3 } from "react";
2540
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2541
+ var TAIL2 = 0.3;
2542
+ var STACK2 = Array.from({ length: 12 }, (_, i) => 12 - i);
2543
+ var HALO2 = [3, 2, 1];
2544
+ function ZorealBusyRing({
2545
+ busy,
2546
+ radius = 8,
2547
+ theme = "auto",
2548
+ block = false,
2549
+ className,
2550
+ style,
2551
+ children
2552
+ }) {
2553
+ useEffect3(() => {
2554
+ ensureStyles();
2555
+ }, []);
2556
+ const wrapRef = useRef3(null);
2557
+ const measureRef = useRef3(null);
2558
+ useLayoutEffect(() => {
2559
+ const wrap = wrapRef.current;
2560
+ const rect = measureRef.current;
2561
+ if (!wrap || !rect) return;
2562
+ const measure = () => {
2563
+ const length = rect.getTotalLength();
2564
+ if (length > 0) wrap.style.setProperty("--zrl-ring-len", `${length}px`);
2565
+ };
2566
+ measure();
2567
+ if (typeof ResizeObserver === "undefined") return;
2568
+ const observer = new ResizeObserver(measure);
2569
+ observer.observe(wrap);
2570
+ return () => observer.disconnect();
2571
+ }, [radius]);
2572
+ const rx = radius + 2;
2573
+ const layer = (key, name, len, alpha, ref) => /* @__PURE__ */ jsx6(
2574
+ "rect",
2575
+ {
2576
+ ref,
2577
+ className: cx(name),
2578
+ rx,
2579
+ ry: rx,
2580
+ style: {
2581
+ strokeDasharray: `calc(var(--zrl-l) * ${len}) calc(var(--zrl-l) * ${1 - len})`,
2582
+ "--zrl-s": `calc(var(--zrl-l) * ${-(TAIL2 - len)})`,
2583
+ opacity: alpha
2584
+ }
2585
+ },
2586
+ key
2587
+ );
2588
+ return /* @__PURE__ */ jsxs6(
2589
+ "span",
2590
+ {
2591
+ ref: wrapRef,
2592
+ className: className ? `${cx("root")} ${cx("ring")} ${className}` : `${cx("root")} ${cx("ring")}`,
2593
+ "data-theme": theme,
2594
+ "data-busy": busy,
2595
+ style: block ? { display: "flex", ...style } : style,
2596
+ children: [
2597
+ children,
2598
+ /* @__PURE__ */ jsxs6("svg", { className: cx("ring-svg"), "aria-hidden": "true", children: [
2599
+ HALO2.map(
2600
+ (n, i) => layer(`h${n}`, "ring-halo", TAIL2 * n / HALO2.length, `calc(var(--zrl-glow-opacity) * 0.6 / ${n})`, i === 0 ? measureRef : void 0)
2601
+ ),
2602
+ STACK2.map((n) => layer(`t${n}`, n <= 2 ? "ring-head" : "ring-tail", TAIL2 * n / STACK2.length, String(1 / n)))
2603
+ ] })
2604
+ ]
2605
+ }
2606
+ );
2510
2607
  }
2511
2608
 
2512
2609
  // src/useZorealAutoLogin.ts