@auth-link/web 1.1.0 → 2.0.0

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/styles.css CHANGED
@@ -1,4 +1,4 @@
1
- /* All variants share the same protocol, accessible controls, and theme tokens. */
1
+ /* A single responsive minimal dialog with accessible controls and theme tokens. */
2
2
  .authlink-dialog {
3
3
  --authlink-line: #e7ebe9;
4
4
  --authlink-soft: #f0f5f2;
@@ -26,7 +26,7 @@
26
26
  top: 8px; left: 1px; width: 16px; height: 1.5px; border-radius: 1px; background: currentColor; transform: rotate(45deg); }
27
27
  .authlink-close-icon::after { transform: rotate(-45deg); }
28
28
  .authlink-dialog .authlink-close:hover { background: var(--authlink-soft); color: var(--authlink-foreground, #173f35); }
29
- .authlink-dialog .authlink-close:focus-visible, .authlink-cancel:focus-visible, .authlink-button:focus-visible {
29
+ .authlink-dialog .authlink-close:focus-visible, .authlink-button:focus-visible {
30
30
  outline: 2px solid var(--authlink-accent, #277456); outline-offset: 2px; }
31
31
  .authlink-widget { box-sizing: border-box; width: 100%; min-width: 0; padding: 24px;
32
32
  color: var(--authlink-foreground, #173f35); background: var(--authlink-background, #fff);
@@ -64,64 +64,20 @@
64
64
  .authlink-widget[data-status="error"] .authlink-status, .authlink-widget[data-status="rejected"] .authlink-status { color: #a04432; }
65
65
  .authlink-widget[data-status="confirmed"] .authlink-status { color: var(--authlink-accent, #277456); }
66
66
  .authlink-footer { margin-top: 20px; }
67
- .authlink-widget[data-variant="A"] .authlink-footer .authlink-status { border-top: 1px solid var(--authlink-line, #e7ebe9); padding-top: 16px; }
67
+ .authlink-footer .authlink-status { border-top: 1px solid var(--authlink-line, #e7ebe9); padding-top: 16px; }
68
68
  .authlink-button { margin-top: 18px; border: 0; border-radius: 8px; min-height: 44px; padding: 10px 20px; font: inherit;
69
69
  cursor: pointer; color: #fff; background: var(--authlink-accent, #277456); }
70
- .authlink-cancel { display: inline-flex; align-items: center; justify-content: flex-start; min-height: 44px;
71
- min-width: 44px; padding: 8px 0; border: 0; border-radius: 4px; background: transparent;
72
- color: var(--authlink-muted, #697780); font: inherit; font-size: 12px; text-align: start; cursor: pointer; }
73
- .authlink-cancel:hover { color: var(--authlink-accent, #277456); text-decoration: underline; text-underline-offset: 3px; }
74
- /* B: wide desktop layout, vertical layout on narrow viewports. */
75
- .authlink-dialog[data-variant="B"] { width: min(650px, calc(100vw - 32px)); border-radius: 16px; }
76
- .authlink-dialog[data-variant="B"][data-has-qr="false"] { width: min(460px, calc(100vw - 32px)); }
77
- .authlink-dialog[data-variant="B"] .authlink-header { margin: 0; padding: 10px 24px; }
78
- .authlink-dialog[data-variant="B"] .authlink-close { margin-right: -6px; }
79
- .authlink-dialog[data-variant="B"] .authlink-close::before { content: ''; position: absolute; inset: 7px; border-radius: 7px; background: #f0f2f3; }
80
- .authlink-widget[data-variant="B"] { text-align: left; }
81
- .authlink-widget[data-variant="B"] .authlink-title { font-size: 24px; }
82
- .authlink-widget[data-variant="B"] .authlink-subtitle { display: none; }
83
- .authlink-widget[data-variant="B"][data-has-qr="true"] .authlink-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); align-items: center; gap: 24px; }
84
- .authlink-widget[data-variant="B"] .authlink-qr-wrap { width: 100%; margin: 0; text-align: center; }
85
- .authlink-widget[data-variant="B"] .authlink-qr { width: min(100%, 196px); }
86
- .authlink-widget[data-variant="B"] .authlink-qr-caption { display: block; }
87
- .authlink-widget[data-variant="B"][data-has-qr="true"] .authlink-details { min-width: 0; padding-left: 24px; border-left: 1px solid var(--authlink-line, #e7ebe9); }
88
- .authlink-widget[data-variant="B"] .authlink-code-panel { display: block; padding: 0; background: transparent; }
89
- .authlink-widget[data-variant="B"] .authlink-code { margin: 10px 0 0; font-size: clamp(30px, 5vw, 40px); }
90
- .authlink-widget[data-variant="B"] .authlink-status { justify-content: flex-start; }
91
- .authlink-widget[data-variant="B"] .authlink-footer { display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: center;
92
- justify-content: space-between; border-top: 1px solid var(--authlink-line, #e7ebe9); padding-top: 8px; margin-top: 24px; }
93
- /* C: compact card; becomes a bottom sheet on mobile. */
94
- .authlink-dialog[data-variant="C"] { width: min(390px, calc(100vw - 32px)); border-radius: 14px; }
95
- .authlink-dialog[data-variant="C"] .authlink-header { min-height: 58px; border: 0; padding-bottom: 0; }
96
- .authlink-widget[data-variant="C"] { padding-top: 12px; text-align: left; }
97
- .authlink-widget[data-variant="C"] .authlink-title { font-size: 22px; }
98
- .authlink-widget[data-variant="C"] .authlink-qr { width: min(100%, 180px); }
99
- .authlink-widget[data-variant="C"] .authlink-qr-wrap { margin-bottom: 18px; }
100
- .authlink-widget[data-variant="C"] .authlink-code-panel { grid-template-columns: minmax(0, 1fr); padding: 14px 16px; gap: 10px; background: #f5f7f8; }
101
- .authlink-widget[data-variant="C"] .authlink-code { font-size: clamp(22px, 6.8vw, 26px); margin: 4px 0 0; }
102
- .authlink-widget[data-variant="C"] .authlink-progress { grid-column: 1 / -1; }
103
- .authlink-widget[data-variant="C"] .authlink-help { font-size: 11px; margin-top: 12px; }
104
- .authlink-widget[data-variant="C"] .authlink-footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; border-top: 1px solid var(--authlink-line, #e7ebe9); padding-top: 16px; margin-top: 18px; }
105
- .authlink-widget[data-variant="C"] .authlink-status { justify-content: flex-start; margin: 0; }
106
70
  @media (max-width: 560px) {
107
- .authlink-dialog, .authlink-dialog[data-variant="B"], .authlink-dialog[data-variant="B"][data-has-qr="false"] { width: calc(100vw - 24px); max-height: calc(100dvh - 24px); }
71
+ .authlink-dialog { width: calc(100vw - 24px); max-height: calc(100dvh - 24px); }
108
72
  .authlink-header { margin: 0 20px; }
109
73
  .authlink-widget { padding: 20px; }
110
- .authlink-title, .authlink-widget[data-variant="B"] .authlink-title { font-size: 22px; }
74
+ .authlink-title { font-size: 22px; }
111
75
  .authlink-code-panel { padding: 14px 16px; gap: 0 10px; }
112
- .authlink-widget[data-variant="B"][data-has-qr="true"] .authlink-body { display: block; }
113
- .authlink-widget[data-variant="B"][data-has-qr="true"] .authlink-details { border: 0; padding: 22px 0 0; }
114
- .authlink-widget[data-variant="B"] .authlink-code { font-size: 36px; }
115
- .authlink-widget[data-variant="B"] .authlink-qr { width: min(100%, 180px); }
116
- .authlink-dialog[data-variant="C"] { width: 100%; max-width: none; margin: auto 0 0; max-height: calc(100dvh - 12px); border-radius: 20px 20px 0 0; border-bottom: 0; }
117
- .authlink-dialog[data-variant="C"] .authlink-widget { padding-bottom: max(16px, env(safe-area-inset-bottom)); }
118
76
  }
119
77
  @media (max-width: 350px) {
120
78
  .authlink-widget { padding: 16px; }
121
79
  .authlink-header { margin: 0 16px; }
122
80
  .authlink-code-panel { padding: 12px; }
123
- .authlink-widget[data-variant="C"] .authlink-code-panel { grid-template-columns: minmax(0, 1fr); }
124
- .authlink-widget[data-variant="C"] .authlink-code { margin-bottom: 4px; }
125
81
  }
126
82
  @media (prefers-reduced-motion: reduce) { .authlink-dialog * { animation: none !important; transition: none !important; } }
127
83
 
@@ -1,5 +1,5 @@
1
1
  import type { ViewNode } from './view.js';
2
- import type { DialogVariant, Locale, WidgetOptions } from './types.js';
2
+ import type { Locale, WidgetOptions } from './types.js';
3
3
  export interface AuthLinkModalOptions extends WidgetOptions {
4
4
  className?: string;
5
5
  onClose?: () => void;
@@ -10,8 +10,6 @@ export interface AuthLinkModal {
10
10
  /** Component cleanup: close without delivering a user-close callback. */
11
11
  destroy(): void;
12
12
  setLocale(locale?: Locale): void;
13
- /** Change presentation without creating or restarting a request. */
14
- setVariant(variant?: DialogVariant): void;
15
13
  retry(): void;
16
14
  }
17
15
  export declare function element(value: ViewNode | string, svg?: boolean): Node;
@@ -1,6 +1,6 @@
1
1
  import type { ClientOptions } from './core.js';
2
2
  import type { AuthLinkModal } from './modal.js';
3
- import type { DialogVariant, Locale } from './types.js';
3
+ import type { Locale } from './types.js';
4
4
  export interface OTPVerification {
5
5
  status: 'confirmed';
6
6
  verificationId: string;
@@ -15,7 +15,6 @@ export interface PasscodeOptions {
15
15
  /** The CMS's single pending action deadline, not a TOTP time step. */
16
16
  expiresAt: string;
17
17
  locale?: Locale;
18
- variant?: DialogVariant;
19
18
  /** First-time enrollment only. Ordinary verification has no QR or request ID. */
20
19
  bindingQr?: string;
21
20
  authLinkOrigin?: string;
@@ -3,7 +3,7 @@ import type { AuthLinkModalOptions } from './modal.js';
3
3
  import type { PasscodeOptions } from './passcode.js';
4
4
  export type { PasscodeOptions, PasscodeClient, OTPVerification } from './passcode.js';
5
5
  export declare function AuthLinkPasscode(props: PasscodeOptions): null;
6
- export type { Ticket, Confirmation, Rejection, Snapshot, AuthLinkClient, Locale, DialogVariant } from './types.js';
6
+ export type { Ticket, Confirmation, Rejection, Snapshot, AuthLinkClient, Locale } from './types.js';
7
7
  export declare function useAuthLink(options: WidgetOptions): {
8
8
  snapshot: Snapshot;
9
9
  retry: () => void;
@@ -1,5 +1,4 @@
1
1
  export type Mode = 'bind' | 'confirm';
2
- export type DialogVariant = 'A' | 'B' | 'C';
3
2
  export type Status = 'idle' | 'pending' | 'confirmed' | 'rejected' | 'expired' | 'error';
4
3
  export type Locale = 'en' | 'zh-CN' | 'zh-TW' | 'ja' | 'bn' | 'es' | 'fil' | 'hi' | 'id' | 'ms' | 'pt' | 'th' | 'vi';
5
4
  /** Current display only. The generation secret must never reach the browser. */
@@ -64,8 +63,6 @@ export interface WidgetOptions extends Events {
64
63
  ticket: Ticket;
65
64
  client: AuthLinkClient;
66
65
  locale?: Locale;
67
- /** A: minimal (default), B: split, C: compact / mobile bottom sheet. */
68
- variant?: DialogVariant;
69
66
  showQr?: boolean;
70
67
  intervalMs?: number;
71
68
  onRefresh?: () => void;
@@ -1,14 +1,12 @@
1
- import type { DialogVariant, Locale, Mode, Snapshot } from './types.js';
1
+ import type { Locale, Mode, Snapshot } from './types.js';
2
2
  export interface ViewNode {
3
3
  tag: string;
4
4
  props: Record<string, unknown>;
5
5
  children: (ViewNode | string)[];
6
6
  }
7
- export declare function resolveVariant(variant?: string): DialogVariant;
8
7
  export declare function widgetView(options: {
9
8
  mode: Mode;
10
9
  locale?: Locale;
11
- variant?: DialogVariant;
12
10
  snapshot: Snapshot;
13
11
  passcodeMode?: boolean;
14
12
  qr?: string;
@@ -18,6 +16,5 @@ export declare function widgetView(options: {
18
16
  setCode?: (code: string) => void;
19
17
  retry: () => void;
20
18
  refresh?: () => void;
21
- close?: () => void;
22
19
  className?: string;
23
20
  }): ViewNode;
@@ -4,7 +4,7 @@ import type { AuthLinkModalOptions } from './modal.js';
4
4
  import type { PasscodeOptions } from './passcode.js';
5
5
  export type { PasscodeOptions, PasscodeClient, OTPVerification } from './passcode.js';
6
6
  export declare const AuthLinkPasscode: DefineComponent<PasscodeOptions>;
7
- export type { Ticket, Confirmation, Rejection, Snapshot, AuthLinkClient, Locale, DialogVariant } from './types.js';
7
+ export type { Ticket, Confirmation, Rejection, Snapshot, AuthLinkClient, Locale } from './types.js';
8
8
  export declare function useAuthLink(getOptions: () => WidgetOptions): {
9
9
  snapshot: ShallowRef<Snapshot>;
10
10
  retry: () => void;
package/dist/vue.js CHANGED
@@ -2256,12 +2256,9 @@ function getMessages(locale) {
2256
2256
 
2257
2257
  // src/view.ts
2258
2258
  var node = (tag, props, ...children) => ({ tag, props, children });
2259
- function resolveVariant(variant) {
2260
- return variant === "B" || variant === "C" ? variant : "A";
2261
- }
2262
2259
  function widgetView(options) {
2263
2260
  const { snapshot: s, mode, retry, refresh } = options;
2264
- const locale = resolveLocale(options.locale), variant = resolveVariant(options.variant);
2261
+ const locale = resolveLocale(options.locale);
2265
2262
  const l = getMessages(locale);
2266
2263
  const pending = s.status === "pending", hasQr = pending && (mode === "bind" || Boolean(options.showQr)) && Boolean(options.qr);
2267
2264
  const status = { idle: l.loading, pending: options.passcodeMode ? l.otpWaiting : l.waiting, confirmed: l.confirmed, rejected: l.rejected, expired: l.expired, error: l.error }[s.status];
@@ -2278,12 +2275,6 @@ function widgetView(options) {
2278
2275
  expiring ? l.expiring : status,
2279
2276
  ...expiring ? [node("span", { class: "authlink-expiry", "aria-hidden": "true" }, ` \xB7 ${expiryTime}`)] : []
2280
2277
  );
2281
- const cancel = () => options.close ? [node("button", {
2282
- type: "button",
2283
- class: "authlink-cancel",
2284
- "data-authlink-focus": "cancel",
2285
- onClick: options.close
2286
- }, l.cancel)] : [];
2287
2278
  const children = [node("h3", { class: "authlink-title" }, l[mode])];
2288
2279
  if (hasQr) children.push(node("p", { class: "authlink-subtitle" }, l.scan));
2289
2280
  if (pending) {
@@ -2326,9 +2317,7 @@ function widgetView(options) {
2326
2317
  ...s.error ? [node("p", { class: "authlink-input-error", role: "alert" }, options.passcodeMode ? s.error.message === "OTP_NOT_CONFIGURED" ? l.otpSetup : l.otpError : l.codeError)] : []
2327
2318
  ];
2328
2319
  children.push(node("div", { class: "authlink-body" }, ...qr, node("div", { class: "authlink-details" }, ...detail)));
2329
- if (variant === "A") children.push(node("div", { class: "authlink-footer" }, statusNode()));
2330
- if (variant === "B") children.push(node("div", { class: "authlink-footer" }, statusNode(), ...cancel()));
2331
- if (variant === "C") children.push(node("div", { class: "authlink-footer" }, statusNode(), ...cancel()));
2320
+ children.push(node("div", { class: "authlink-footer" }, statusNode()));
2332
2321
  } else {
2333
2322
  children.push(statusNode());
2334
2323
  if (s.error?.message === "CONFIRMATION_CODE_LOCKED") children.push(node("p", { class: "authlink-input-error", role: "alert" }, l.codeLocked));
@@ -2349,7 +2338,6 @@ function widgetView(options) {
2349
2338
  lang: locale,
2350
2339
  class: `authlink-widget ${options.className ?? ""}`.trim(),
2351
2340
  "data-status": s.status,
2352
- "data-variant": variant,
2353
2341
  "data-has-qr": String(hasQr),
2354
2342
  "aria-label": l[mode]
2355
2343
  }, ...children);
@@ -2393,7 +2381,7 @@ function openAuthLink(options) {
2393
2381
  content.className = "authlink-modal-content";
2394
2382
  header.append(brand, closeButton);
2395
2383
  dialog.append(header, content);
2396
- let locale = options.locale, variant = resolveVariant(options.variant), snapshot = initialSnapshot(), closed = false;
2384
+ let locale = options.locale, snapshot = initialSnapshot(), closed = false;
2397
2385
  let abort;
2398
2386
  let inputCode = "";
2399
2387
  let submitCode;
@@ -2423,7 +2411,6 @@ function openAuthLink(options) {
2423
2411
  const language = resolveLocale(locale), messages2 = getMessages(language);
2424
2412
  dialog.lang = language;
2425
2413
  dialog.setAttribute("aria-label", messages2[mode]);
2426
- dialog.dataset.variant = variant;
2427
2414
  dialog.dataset.mode = mode;
2428
2415
  dialog.dataset.hasQr = String(snapshot.status === "pending" && (mode === "bind" || Boolean(options.showQr)));
2429
2416
  closeButton.setAttribute("aria-label", messages2.close);
@@ -2432,7 +2419,6 @@ function openAuthLink(options) {
2432
2419
  const next = element(widgetView({
2433
2420
  mode,
2434
2421
  locale,
2435
- variant,
2436
2422
  snapshot,
2437
2423
  qr: image,
2438
2424
  showQr: options.showQr,
@@ -2442,8 +2428,7 @@ function openAuthLink(options) {
2442
2428
  },
2443
2429
  submitCode: (code) => submitCode?.(code) ?? Promise.resolve(),
2444
2430
  retry,
2445
- refresh: options.onRefresh,
2446
- close: () => dispose(true)
2431
+ refresh: options.onRefresh
2447
2432
  }));
2448
2433
  const oldCode = content.querySelector(".authlink-code");
2449
2434
  const nextCode = next.querySelector(".authlink-code");
@@ -2556,10 +2541,6 @@ function openAuthLink(options) {
2556
2541
  locale = value;
2557
2542
  render();
2558
2543
  },
2559
- setVariant: (value) => {
2560
- variant = resolveVariant(value);
2561
- render();
2562
- },
2563
2544
  retry
2564
2545
  };
2565
2546
  }
@@ -2597,7 +2578,7 @@ function openPasscode(options) {
2597
2578
  content.className = "authlink-modal-content";
2598
2579
  header.append(brand, close);
2599
2580
  dialog.append(header, content);
2600
- let locale = options.locale, variant = resolveVariant(options.variant), closed = false, busy = false, code = "", error;
2581
+ let locale = options.locale, closed = false, busy = false, code = "", error;
2601
2582
  const mode = options.bindingQr ? "bind" : "confirm";
2602
2583
  let image;
2603
2584
  if (options.bindingQr) {
@@ -2624,7 +2605,6 @@ function openPasscode(options) {
2624
2605
  if (closed) return;
2625
2606
  const l = getMessages(locale);
2626
2607
  dialog.lang = resolveLocale(locale);
2627
- dialog.dataset.variant = variant;
2628
2608
  dialog.dataset.mode = mode;
2629
2609
  dialog.dataset.hasQr = String(Boolean(image));
2630
2610
  dialog.setAttribute("aria-label", l[mode]);
@@ -2635,7 +2615,6 @@ function openPasscode(options) {
2635
2615
  content.replaceChildren(element(widgetView({
2636
2616
  mode,
2637
2617
  locale,
2638
- variant,
2639
2618
  passcodeMode: true,
2640
2619
  snapshot,
2641
2620
  qr: image,
@@ -2648,8 +2627,7 @@ function openPasscode(options) {
2648
2627
  retry: () => {
2649
2628
  error = void 0;
2650
2629
  render();
2651
- },
2652
- close: () => dispose(true)
2630
+ }
2653
2631
  })));
2654
2632
  if (focused) content.querySelector("input")?.focus();
2655
2633
  };
@@ -2711,10 +2689,6 @@ function openPasscode(options) {
2711
2689
  locale = value;
2712
2690
  render();
2713
2691
  },
2714
- setVariant: (value) => {
2715
- variant = resolveVariant(value);
2716
- render();
2717
- },
2718
2692
  retry: () => {
2719
2693
  error = void 0;
2720
2694
  render();
@@ -2730,8 +2704,7 @@ var AuthLinkPasscode = defineComponent({
2730
2704
  expiresAt: { type: String, required: true },
2731
2705
  bindingQr: String,
2732
2706
  authLinkOrigin: String,
2733
- locale: String,
2734
- variant: String
2707
+ locale: String
2735
2708
  },
2736
2709
  emits: ["verified", "expired", "error", "close"],
2737
2710
  setup(props, { emit }) {
@@ -2753,7 +2726,6 @@ var AuthLinkPasscode = defineComponent({
2753
2726
  });
2754
2727
  watch([() => props.client, () => props.expiresAt, () => props.bindingQr, () => props.authLinkOrigin], open);
2755
2728
  watch(() => props.locale, (value) => modal?.setLocale(value));
2756
- watch(() => props.variant, (value) => modal?.setVariant(value));
2757
2729
  onBeforeUnmount(() => {
2758
2730
  mounted = false;
2759
2731
  modal?.destroy();
@@ -2819,7 +2791,6 @@ function widget(mode) {
2819
2791
  ticket: { type: Object, required: true },
2820
2792
  client: { type: Object, required: true },
2821
2793
  locale: { type: String, default: defaultLocale },
2822
- variant: { type: String, default: "A" },
2823
2794
  intervalMs: Number,
2824
2795
  showQr: Boolean,
2825
2796
  className: String,
@@ -2863,7 +2834,6 @@ function widget(mode) {
2863
2834
  () => Boolean(props.onRefresh)
2864
2835
  ], open);
2865
2836
  watch(() => props.locale, (value) => modal?.setLocale(value));
2866
- watch(() => props.variant, (value) => modal?.setVariant(value));
2867
2837
  onBeforeUnmount(() => {
2868
2838
  mounted = false;
2869
2839
  modal?.destroy();