@zoreal/oauth2-react 0.2.13 → 0.2.15

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
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
2
+ import { ReactNode, CSSProperties } from 'react';
3
3
 
4
4
  /**
5
5
  * The public types of @zoreal/oauth2-react.
@@ -243,6 +243,36 @@ declare function useZorealOAuth(): ZorealOAuthContextProps;
243
243
 
244
244
  declare function ZorealLogin(props: ZorealLoginProps): react.JSX.Element;
245
245
 
246
+ /**
247
+ * The light of the pairing dialog's QR well, run around whatever this wraps
248
+ * while `busy` is true: the well's colours, line, halo and lap, on a dash
249
+ * that travels the outline at one speed whatever the shape.
250
+ *
251
+ * The SDK's own button uses it for the gap on a phone between the tap and the
252
+ * hand-over to the app. A site with its own sign-in button MAY wrap that
253
+ * button the same way: set your busy state on the tap, clear it when
254
+ * `onSuccess` or `onError` fires. `radius` is the wrapped control's corner
255
+ * radius in pixels, so the light follows its shape; `theme` picks the light
256
+ * and dark strengths the dialog uses, following the OS by default.
257
+ *
258
+ * It is a wrapper, and a wrapper is a change to your markup, so know what it
259
+ * does before you use it: it is inline by default and shrinks to the button,
260
+ * so a full-width button needs `block`; the light sits outside the button,
261
+ * so an ancestor with `overflow: hidden` clips it; and a selector that relies
262
+ * on the button's parent (`.card > button`) no longer matches. A site that
263
+ * would rather draw its own busy state needs nothing from here.
264
+ */
265
+ declare function ZorealBusyRing({ busy, radius, theme, block, className, style, children, }: {
266
+ busy: boolean;
267
+ radius?: number;
268
+ theme?: ZorealTheme;
269
+ /** Lay the wrapper out as a block, for a button that fills its row. */
270
+ block?: boolean;
271
+ className?: string;
272
+ style?: CSSProperties;
273
+ children: ReactNode;
274
+ }): react.JSX.Element;
275
+
246
276
  /**
247
277
  * The pairing modal, rendered by the SDK rather than by every integrator.
248
278
  *
@@ -312,4 +342,4 @@ declare function hasGrantedAnyScopeZoreal(response: Pick<ZorealCodeResponse, 'sc
312
342
  */
313
343
  declare function resolveIntent(intent: LoginIntent | undefined, scope: string | undefined, acrValues: string | readonly string[] | undefined): LoginIntent;
314
344
 
315
- export { type AcrValue, type AuthCodeFlowOptions, type BrowserDirectFlowOptions, DEFAULT_PAIRING_TIMEOUT_MS, type ErrorCode, type LoginIntent, type NonOAuthError, PairingModal, type PairingModalProps, type PairingState, type PairingUI, type SelectBy, type UseZorealAutoLoginOptions, type ZorealButtonConfiguration, type ZorealCodeResponse, type ZorealCredentialResponse, ZorealLogin, type ZorealLoginProps, type ZorealLoginRequestOptions, type ZorealOAuthContextProps, ZorealOAuthProvider, type ZorealOAuthProviderProps, type ZorealTheme, hasGrantedAllScopesZoreal, hasGrantedAnyScopeZoreal, resolveIntent, useZorealAutoLogin, useZorealLogin, useZorealOAuth, zorealLogout };
345
+ export { type AcrValue, type AuthCodeFlowOptions, type BrowserDirectFlowOptions, DEFAULT_PAIRING_TIMEOUT_MS, type ErrorCode, type LoginIntent, type NonOAuthError, PairingModal, type PairingModalProps, type PairingState, type PairingUI, type SelectBy, type UseZorealAutoLoginOptions, ZorealBusyRing, type ZorealButtonConfiguration, type ZorealCodeResponse, type ZorealCredentialResponse, ZorealLogin, type ZorealLoginProps, type ZorealLoginRequestOptions, type ZorealOAuthContextProps, ZorealOAuthProvider, type ZorealOAuthProviderProps, type ZorealTheme, hasGrantedAllScopesZoreal, hasGrantedAnyScopeZoreal, resolveIntent, useZorealAutoLogin, useZorealLogin, useZorealOAuth, zorealLogout };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
2
+ import { ReactNode, CSSProperties } from 'react';
3
3
 
4
4
  /**
5
5
  * The public types of @zoreal/oauth2-react.
@@ -243,6 +243,36 @@ declare function useZorealOAuth(): ZorealOAuthContextProps;
243
243
 
244
244
  declare function ZorealLogin(props: ZorealLoginProps): react.JSX.Element;
245
245
 
246
+ /**
247
+ * The light of the pairing dialog's QR well, run around whatever this wraps
248
+ * while `busy` is true: the well's colours, line, halo and lap, on a dash
249
+ * that travels the outline at one speed whatever the shape.
250
+ *
251
+ * The SDK's own button uses it for the gap on a phone between the tap and the
252
+ * hand-over to the app. A site with its own sign-in button MAY wrap that
253
+ * button the same way: set your busy state on the tap, clear it when
254
+ * `onSuccess` or `onError` fires. `radius` is the wrapped control's corner
255
+ * radius in pixels, so the light follows its shape; `theme` picks the light
256
+ * and dark strengths the dialog uses, following the OS by default.
257
+ *
258
+ * It is a wrapper, and a wrapper is a change to your markup, so know what it
259
+ * does before you use it: it is inline by default and shrinks to the button,
260
+ * so a full-width button needs `block`; the light sits outside the button,
261
+ * so an ancestor with `overflow: hidden` clips it; and a selector that relies
262
+ * on the button's parent (`.card > button`) no longer matches. A site that
263
+ * would rather draw its own busy state needs nothing from here.
264
+ */
265
+ declare function ZorealBusyRing({ busy, radius, theme, block, className, style, children, }: {
266
+ busy: boolean;
267
+ radius?: number;
268
+ theme?: ZorealTheme;
269
+ /** Lay the wrapper out as a block, for a button that fills its row. */
270
+ block?: boolean;
271
+ className?: string;
272
+ style?: CSSProperties;
273
+ children: ReactNode;
274
+ }): react.JSX.Element;
275
+
246
276
  /**
247
277
  * The pairing modal, rendered by the SDK rather than by every integrator.
248
278
  *
@@ -312,4 +342,4 @@ declare function hasGrantedAnyScopeZoreal(response: Pick<ZorealCodeResponse, 'sc
312
342
  */
313
343
  declare function resolveIntent(intent: LoginIntent | undefined, scope: string | undefined, acrValues: string | readonly string[] | undefined): LoginIntent;
314
344
 
315
- export { type AcrValue, type AuthCodeFlowOptions, type BrowserDirectFlowOptions, DEFAULT_PAIRING_TIMEOUT_MS, type ErrorCode, type LoginIntent, type NonOAuthError, PairingModal, type PairingModalProps, type PairingState, type PairingUI, type SelectBy, type UseZorealAutoLoginOptions, type ZorealButtonConfiguration, type ZorealCodeResponse, type ZorealCredentialResponse, ZorealLogin, type ZorealLoginProps, type ZorealLoginRequestOptions, type ZorealOAuthContextProps, ZorealOAuthProvider, type ZorealOAuthProviderProps, type ZorealTheme, hasGrantedAllScopesZoreal, hasGrantedAnyScopeZoreal, resolveIntent, useZorealAutoLogin, useZorealLogin, useZorealOAuth, zorealLogout };
345
+ export { type AcrValue, type AuthCodeFlowOptions, type BrowserDirectFlowOptions, DEFAULT_PAIRING_TIMEOUT_MS, type ErrorCode, type LoginIntent, type NonOAuthError, PairingModal, type PairingModalProps, type PairingState, type PairingUI, type SelectBy, type UseZorealAutoLoginOptions, ZorealBusyRing, type ZorealButtonConfiguration, type ZorealCodeResponse, type ZorealCredentialResponse, ZorealLogin, type ZorealLoginProps, type ZorealLoginRequestOptions, type ZorealOAuthContextProps, ZorealOAuthProvider, type ZorealOAuthProviderProps, type ZorealTheme, hasGrantedAllScopesZoreal, hasGrantedAnyScopeZoreal, resolveIntent, useZorealAutoLogin, useZorealLogin, useZorealOAuth, zorealLogout };
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.13";
8
+ var SDK_VERSION = "0.2.15";
9
9
  var DEFAULT_ISSUER = "https://id.zoreal.com";
10
10
  var POLL_INTERVAL_MS = 2e3;
11
11
  var POLL_INTERVAL_ENROLLING_MS = 5e3;
@@ -1327,6 +1327,65 @@ var CSS = `
1327
1327
  }
1328
1328
 
1329
1329
  @keyframes ${PREFIX}-orbit { to { transform: rotate(360deg) } }
1330
+ /* THE BUSY RING. The light of the QR well, around any control that is
1331
+ waiting on the provider: the button on a phone between the tap and the
1332
+ hand-over to the app. The well's sweep is a cone from the centre, which is
1333
+ even on a square and useless on a wide button: it crawls along the long
1334
+ sides and lights two edges at once near the ends. So here the light is a
1335
+ dash on an SVG outline, which moves at one speed the whole way round
1336
+ whatever the shape, drawn with the well's tokens: its colour and head
1337
+ tint, its line width, its halo, its four second lap. pathLength makes the
1338
+ outline 100 units long, so every dash and offset is a percentage of the
1339
+ way round; the three layers share one head at 30 and differ in length
1340
+ and brightness, which is the comet's tail. Shown only while busy. */
1341
+ .${PREFIX}-ring {
1342
+ position: relative;
1343
+ display: inline-flex;
1344
+ vertical-align: middle;
1345
+ --zrl-beam-time: 4s;
1346
+ }
1347
+ .${PREFIX}-ring-svg {
1348
+ position: absolute;
1349
+ inset: -4px;
1350
+ width: calc(100% + 8px);
1351
+ height: calc(100% + 8px);
1352
+ overflow: visible;
1353
+ pointer-events: none;
1354
+ opacity: 0;
1355
+ transition: opacity 200ms ease-out;
1356
+ }
1357
+ .${PREFIX}-ring[data-busy="true"] > .${PREFIX}-ring-svg { opacity: 1; }
1358
+ .${PREFIX}-ring-svg rect {
1359
+ x: 2px;
1360
+ y: 2px;
1361
+ width: calc(100% - 4px);
1362
+ height: calc(100% - 4px);
1363
+ fill: none;
1364
+ stroke: var(--zrl-beam);
1365
+ stroke-width: var(--zrl-beam-line);
1366
+ stroke-linecap: round;
1367
+ animation: ${PREFIX}-dash var(--zrl-beam-time) linear infinite;
1368
+ }
1369
+ .${PREFIX}-ring-tail { stroke-dasharray: 30 70; stroke-dashoffset: 0; opacity: 0.35; }
1370
+ .${PREFIX}-ring-body { stroke-dasharray: 16 84; stroke-dashoffset: -14; opacity: 0.8; }
1371
+ .${PREFIX}-ring-head { stroke-dasharray: 5 95; stroke-dashoffset: -25; stroke: var(--zrl-beam-head); }
1372
+ .${PREFIX}-ring-halo {
1373
+ stroke-dasharray: 30 70;
1374
+ stroke-dashoffset: 0;
1375
+ stroke-width: calc(var(--zrl-glow-core) * 2 + var(--zrl-beam-line));
1376
+ opacity: calc(var(--zrl-glow-opacity) * 0.5);
1377
+ filter: blur(var(--zrl-glow-blur));
1378
+ }
1379
+ @keyframes ${PREFIX}-dash {
1380
+ to { stroke-dashoffset: calc(var(--zrl-dash-start, 0) - 100); }
1381
+ }
1382
+ .${PREFIX}-ring-body { --zrl-dash-start: -14; }
1383
+ .${PREFIX}-ring-head { --zrl-dash-start: -25; }
1384
+ @media (prefers-reduced-motion: reduce) {
1385
+ .${PREFIX}-ring-svg rect { animation: none; stroke-dasharray: none; opacity: 0.45; }
1386
+ .${PREFIX}-ring-halo, .${PREFIX}-ring-head { display: none; }
1387
+ }
1388
+
1330
1389
 
1331
1390
  @media (prefers-reduced-motion: reduce) {
1332
1391
  .${PREFIX}-scrim,
@@ -1535,7 +1594,7 @@ function useZorealOAuth() {
1535
1594
  }
1536
1595
 
1537
1596
  // src/ZorealLogin.tsx
1538
- import { useMemo as useMemo2 } from "react";
1597
+ import { useMemo as useMemo2, useState as useState4 } from "react";
1539
1598
 
1540
1599
  // src/useZorealLogin.ts
1541
1600
  import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
@@ -1902,8 +1961,45 @@ function useZorealLogin(options) {
1902
1961
  }).login;
1903
1962
  }
1904
1963
 
1905
- // src/ZorealLogin.tsx
1964
+ // src/ring.tsx
1965
+ import { useEffect as useEffect3 } from "react";
1906
1966
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1967
+ function ZorealBusyRing({
1968
+ busy,
1969
+ radius = 8,
1970
+ theme = "auto",
1971
+ block = false,
1972
+ className,
1973
+ style,
1974
+ children
1975
+ }) {
1976
+ useEffect3(() => {
1977
+ ensureStyles();
1978
+ }, []);
1979
+ const rx = radius + 2;
1980
+ const layer = (name) => /* @__PURE__ */ jsx5("rect", { className: cx(name), rx, ry: rx, pathLength: 100 });
1981
+ return /* @__PURE__ */ jsxs5(
1982
+ "span",
1983
+ {
1984
+ className: className ? `${cx("root")} ${cx("ring")} ${className}` : `${cx("root")} ${cx("ring")}`,
1985
+ "data-theme": theme,
1986
+ "data-busy": busy,
1987
+ style: block ? { display: "flex", ...style } : style,
1988
+ children: [
1989
+ children,
1990
+ /* @__PURE__ */ jsxs5("svg", { className: cx("ring-svg"), "aria-hidden": "true", children: [
1991
+ layer("ring-halo"),
1992
+ layer("ring-tail"),
1993
+ layer("ring-body"),
1994
+ layer("ring-head")
1995
+ ] })
1996
+ ]
1997
+ }
1998
+ );
1999
+ }
2000
+
2001
+ // src/ZorealLogin.tsx
2002
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1907
2003
  var TEXTS = {
1908
2004
  continue_with: null,
1909
2005
  signin_with: "Sign in with ZOREAL",
@@ -1912,9 +2008,9 @@ var TEXTS = {
1912
2008
  verify_with: "Verify with ZOREAL ID"
1913
2009
  };
1914
2010
  var SIZES = {
1915
- large: { height: 44, font: 15, pad: 20 },
1916
- medium: { height: 38, font: 14, pad: 16 },
1917
- small: { height: 32, font: 12, pad: 12 }
2011
+ large: { height: 50, font: 14, pad: 20, mark: 22, gap: 12, radius: 12 },
2012
+ medium: { height: 42, font: 14, pad: 16, mark: 20, gap: 10, radius: 10 },
2013
+ small: { height: 34, font: 12, pad: 12, mark: 16, gap: 8, radius: 8 }
1918
2014
  };
1919
2015
  function ZorealLogin(props) {
1920
2016
  const {
@@ -1922,11 +2018,11 @@ function ZorealLogin(props) {
1922
2018
  onError,
1923
2019
  containerProps,
1924
2020
  type = "standard",
1925
- theme = "filled",
2021
+ theme = "outline",
1926
2022
  size = "large",
1927
2023
  text = "continue_with",
1928
2024
  shape = "rectangular",
1929
- logo_alignment = "left",
2025
+ logo_alignment = "center",
1930
2026
  width,
1931
2027
  click_listener,
1932
2028
  flow = "browser-direct",
@@ -1934,21 +2030,36 @@ function ZorealLogin(props) {
1934
2030
  } = props;
1935
2031
  const { locale } = useZorealOAuth();
1936
2032
  const label = TEXTS[text] ?? strings(locale).buttonContinue;
2033
+ const [busy, setBusy] = useState4(false);
1937
2034
  const { login } = useZorealFlow({
1938
2035
  ...request,
1939
2036
  flow,
1940
- onCredential: flow === "browser-direct" ? onSuccess : void 0,
1941
- onCode: flow === "auth-code" ? onSuccess : void 0,
1942
- onError: (e) => onError?.({ type: "unknown", description: e.description ?? e.error }),
1943
- onNonOAuthError: (e) => onError?.(e)
2037
+ onCredential: flow === "browser-direct" ? (r) => {
2038
+ setBusy(false);
2039
+ onSuccess(r);
2040
+ } : void 0,
2041
+ onCode: flow === "auth-code" ? (r) => {
2042
+ setBusy(false);
2043
+ onSuccess(r);
2044
+ } : void 0,
2045
+ onError: (e) => {
2046
+ setBusy(false);
2047
+ onError?.({ type: "unknown", description: e.description ?? e.error });
2048
+ },
2049
+ onNonOAuthError: (e) => {
2050
+ setBusy(false);
2051
+ onError?.(e);
2052
+ }
1944
2053
  });
1945
2054
  const s = SIZES[size];
2055
+ const radius = shape === "pill" ? s.height / 2 : shape === "square" ? 4 : s.radius;
2056
+ const brandMark = theme !== "filled";
1946
2057
  const style = useMemo2(
1947
2058
  () => ({
1948
2059
  display: "inline-flex",
1949
2060
  alignItems: "center",
1950
2061
  justifyContent: logo_alignment === "center" ? "center" : "flex-start",
1951
- gap: 10,
2062
+ gap: s.gap,
1952
2063
  height: s.height,
1953
2064
  padding: `0 ${s.pad}px`,
1954
2065
  width,
@@ -1956,30 +2067,34 @@ function ZorealLogin(props) {
1956
2067
  fontFamily: "inherit",
1957
2068
  fontWeight: 500,
1958
2069
  cursor: "pointer",
1959
- borderRadius: shape === "pill" ? s.height / 2 : shape === "square" ? 4 : 8,
1960
- ...theme === "outline" ? { background: "transparent", color: "inherit", border: "1px solid rgba(128,128,128,0.5)" } : theme === "filled_black" ? { background: "#111", color: "#fff", border: "1px solid #111" } : { background: "#00b4d9", color: "#fff", border: "1px solid #00b4d9" }
2070
+ borderRadius: radius,
2071
+ ...theme === "outline" ? { background: "#ffffff", color: "#16181c", border: "1px solid #e2e4de" } : theme === "filled_black" ? { background: "#111", color: "#fff", border: "1px solid #111" } : { background: "#00b4d9", color: "#fff", border: "1px solid #00b4d9" }
1961
2072
  }),
1962
- [logo_alignment, s, shape, theme, width]
2073
+ [logo_alignment, s, radius, theme, width]
1963
2074
  );
1964
- return /* @__PURE__ */ jsx5("div", { ...containerProps, children: /* @__PURE__ */ jsxs5(
2075
+ return /* @__PURE__ */ jsx6("div", { ...containerProps, children: /* @__PURE__ */ jsx6(ZorealBusyRing, { busy, radius, theme: theme === "outline" ? "auto" : "light", children: /* @__PURE__ */ jsxs6(
1965
2076
  "button",
1966
2077
  {
1967
2078
  type: "button",
1968
- style,
2079
+ style: busy ? { ...style, cursor: "progress" } : style,
2080
+ disabled: busy,
2081
+ "aria-busy": busy,
1969
2082
  onClick: () => {
2083
+ if (busy) return;
1970
2084
  click_listener?.();
2085
+ setBusy(true);
1971
2086
  login();
1972
2087
  },
1973
2088
  children: [
1974
- /* @__PURE__ */ jsx5(ZorealMark, { size: Math.round(s.font * 1.25) }),
2089
+ /* @__PURE__ */ jsx6(ZorealMark, { size: s.mark, brand: brandMark }),
1975
2090
  type === "standard" && label
1976
2091
  ]
1977
2092
  }
1978
- ) });
2093
+ ) }) });
1979
2094
  }
1980
2095
 
1981
2096
  // src/useZorealAutoLogin.ts
1982
- import { useEffect as useEffect3, useRef as useRef3 } from "react";
2097
+ import { useEffect as useEffect4, useRef as useRef3 } from "react";
1983
2098
  function useZorealAutoLogin(options) {
1984
2099
  const { login } = useZorealFlow({
1985
2100
  flow: "browser-direct",
@@ -1997,7 +2112,7 @@ function useZorealAutoLogin(options) {
1997
2112
  onNonOAuthError: (e) => options.onError?.(e)
1998
2113
  });
1999
2114
  const fired = useRef3(false);
2000
- useEffect3(() => {
2115
+ useEffect4(() => {
2001
2116
  if (options.disabled || fired.current) return;
2002
2117
  fired.current = true;
2003
2118
  login();
@@ -2020,6 +2135,7 @@ function hasGrantedAnyScopeZoreal(response, firstScope, ...restScopes) {
2020
2135
  export {
2021
2136
  DEFAULT_PAIRING_TIMEOUT_MS,
2022
2137
  PairingModal,
2138
+ ZorealBusyRing,
2023
2139
  ZorealLogin,
2024
2140
  ZorealOAuthProvider,
2025
2141
  hasGrantedAllScopesZoreal,