@usethrottle/auth 0.11.0 → 0.12.1

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.
Files changed (68) hide show
  1. package/README.md +52 -0
  2. package/dist/AccountDashboard-V63ghvjY.d.ts +90 -0
  3. package/dist/AccountModal-KA54WUFW.js +13 -0
  4. package/dist/AccountModal-KA54WUFW.js.map +1 -0
  5. package/dist/AccountModal-pIMgledL.d.ts +141 -0
  6. package/dist/AuthModal-T2GYI6ZF.js +13 -0
  7. package/dist/AuthModal-T2GYI6ZF.js.map +1 -0
  8. package/dist/SignUpPage-BJcMUb2V.d.ts +160 -0
  9. package/dist/chunk-3B4DNX5T.js +2 -0
  10. package/dist/chunk-3B4DNX5T.js.map +1 -0
  11. package/dist/chunk-5EUZYJJL.js +29 -0
  12. package/dist/chunk-5EUZYJJL.js.map +1 -0
  13. package/dist/chunk-6WN3J5MM.js +2075 -0
  14. package/dist/chunk-6WN3J5MM.js.map +1 -0
  15. package/dist/chunk-CUNMJ2PH.js +1320 -0
  16. package/dist/chunk-CUNMJ2PH.js.map +1 -0
  17. package/dist/chunk-ETYOOYYV.js +2 -0
  18. package/dist/chunk-ETYOOYYV.js.map +1 -0
  19. package/dist/chunk-JSDZMPCV.js +520 -0
  20. package/dist/chunk-JSDZMPCV.js.map +1 -0
  21. package/dist/chunk-QPVS6JXP.js +39 -0
  22. package/dist/chunk-QPVS6JXP.js.map +1 -0
  23. package/dist/chunk-RCNUWITB.js +747 -0
  24. package/dist/chunk-RCNUWITB.js.map +1 -0
  25. package/dist/{chunk-YY6YATXT.js → chunk-TDQC3LMQ.js} +17 -3
  26. package/dist/chunk-TDQC3LMQ.js.map +1 -0
  27. package/dist/{chunk-EFMKBOTL.js → chunk-UFGW76AZ.js} +2 -2
  28. package/dist/chunk-VDK23T6L.js +230 -0
  29. package/dist/chunk-VDK23T6L.js.map +1 -0
  30. package/dist/chunk-X6D35EAX.js +19 -0
  31. package/dist/chunk-X6D35EAX.js.map +1 -0
  32. package/dist/client-7LVKfuXO.d.ts +319 -0
  33. package/dist/hooks-DkpLrEiC.d.ts +148 -0
  34. package/dist/index.cjs +16 -2
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +34 -3
  37. package/dist/index.d.ts +34 -3
  38. package/dist/index.js +16 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/localization-SnIAZXJn.d.ts +389 -0
  41. package/dist/react/components/account/index.d.ts +37 -0
  42. package/dist/react/components/account/index.js +44 -0
  43. package/dist/react/components/account/index.js.map +1 -0
  44. package/dist/react/components/auth/index.d.ts +17 -0
  45. package/dist/react/components/auth/index.js +40 -0
  46. package/dist/react/components/auth/index.js.map +1 -0
  47. package/dist/react/components/guards.d.ts +21 -0
  48. package/dist/react/components/guards.js +13 -0
  49. package/dist/react/components/guards.js.map +1 -0
  50. package/dist/react/components/index.cjs +3561 -2805
  51. package/dist/react/components/index.cjs.map +1 -1
  52. package/dist/react/components/index.d.cts +14 -2
  53. package/dist/react/components/index.d.ts +14 -821
  54. package/dist/react/components/index.js +90 -4680
  55. package/dist/react/components/index.js.map +1 -1
  56. package/dist/react/components/provider.d.ts +79 -0
  57. package/dist/react/components/provider.js +18 -0
  58. package/dist/react/components/provider.js.map +1 -0
  59. package/dist/react/forms/index.js +1 -1
  60. package/dist/react/index.cjs +16 -2
  61. package/dist/react/index.cjs.map +1 -1
  62. package/dist/react/index.d.cts +34 -3
  63. package/dist/react/index.d.ts +5 -433
  64. package/dist/react/index.js +3 -2
  65. package/dist/styles.css +7 -4
  66. package/package.json +4 -1
  67. package/dist/chunk-YY6YATXT.js.map +0 -1
  68. /package/dist/{chunk-EFMKBOTL.js.map → chunk-UFGW76AZ.js.map} +0 -0
@@ -0,0 +1,747 @@
1
+ "use client";
2
+ import {
3
+ BrandLogoProvider
4
+ } from "./chunk-X6D35EAX.js";
5
+ import {
6
+ AppearanceProvider,
7
+ ConfirmProvider,
8
+ LocalizationProvider
9
+ } from "./chunk-CUNMJ2PH.js";
10
+ import {
11
+ ThrottleAuthProvider,
12
+ createAccount,
13
+ createThrottleAuth
14
+ } from "./chunk-TDQC3LMQ.js";
15
+
16
+ // src/react/components/provider.tsx
17
+ import { Component, createContext, lazy, Suspense, useCallback, useContext, useMemo, useRef, useState } from "react";
18
+
19
+ // src/react/components/styles.ts
20
+ import { useInsertionEffect } from "react";
21
+ var THROTTLE_STYLE_ID = "throttle-auth-styles";
22
+ var THROTTLE_STYLES = `
23
+ .throttle-root {
24
+ --throttle-color-primary: #18181b;
25
+ --throttle-color-primary-foreground: #ffffff;
26
+ --throttle-color-background: #ffffff;
27
+ --throttle-color-surface: #f8fafc;
28
+ --throttle-color-foreground: #0e1116;
29
+ --throttle-color-muted: #eceef2;
30
+ --throttle-color-muted-foreground: #5b6472;
31
+ --throttle-color-border: #e2e8f0;
32
+ /* The boundary of an INTERACTIVE control, which WCAG 1.4.11 requires to
33
+ reach 3:1 against its adjacent colour \u2014 an input identified only by a
34
+ 1.23:1 hairline is not identifiable at all. Decorative separators (card
35
+ edges, table rules, dividers) keep --throttle-color-border: they do not
36
+ identify a control, so the requirement does not apply and darkening
37
+ them would only add noise. */
38
+ /* The account dashboard paints nothing by default so it can drop into a
39
+ merchant's page without fighting their background. The accountSurface and
40
+ accountPadding appearance variables opt back into a card.
41
+ No backticks in this comment: the sheet is a TS template literal. */
42
+ --throttle-card-width: 400px;
43
+ --throttle-color-account-surface: transparent;
44
+ --throttle-account-padding: 0;
45
+ --throttle-color-border-control: #8b93a1;
46
+ --throttle-color-danger: #c2321f;
47
+ --throttle-color-danger-surface: #fef2f2;
48
+ --throttle-color-danger-border: #fecaca;
49
+ --throttle-color-success: #127a4a;
50
+ --throttle-color-success-surface: #ecfdf5;
51
+ --throttle-color-success-border: #a7f3d0;
52
+ --throttle-color-warning: #92400e;
53
+ --throttle-color-warning-surface: #fffbeb;
54
+ --throttle-color-warning-border: #fde68a;
55
+ --throttle-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
56
+ --throttle-font-size: 16px;
57
+ --throttle-font-size-xs: 12px;
58
+ --throttle-font-size-sm: 14px;
59
+ --throttle-font-size-lg: 20px;
60
+ --throttle-font-size-xl: 24px;
61
+ --throttle-line-height: 1.5;
62
+ --throttle-line-height-tight: 1.25;
63
+ --throttle-radius: 12px;
64
+ --throttle-radius-sm: 8px;
65
+ --throttle-radius-full: 999px;
66
+ --throttle-border-width: 1px;
67
+ --throttle-spacing: 8px;
68
+ --throttle-space-1: 4px;
69
+ --throttle-space-2: var(--throttle-spacing);
70
+ --throttle-space-3: 12px;
71
+ --throttle-space-4: 16px;
72
+ --throttle-space-5: 24px;
73
+ --throttle-space-6: 32px;
74
+ --throttle-control-height: 48px;
75
+ --throttle-control-padding-x: 16px;
76
+ --throttle-shadow-card: 0 1px 3px rgba(15,23,42,.08), 0 1px 2px rgba(15,23,42,.04);
77
+ --throttle-shadow-popover: 0 10px 24px rgba(15,23,42,.12), 0 2px 6px rgba(15,23,42,.06);
78
+ --throttle-transition: 150ms cubic-bezier(.2,0,.2,1);
79
+ --throttle-z-modal: 2147483000;
80
+ --throttle-z-popover: 2147482000;
81
+ font-family: var(--throttle-font-family);
82
+ font-size: var(--throttle-font-size);
83
+ line-height: var(--throttle-line-height);
84
+ color: var(--throttle-color-foreground);
85
+ box-sizing: border-box;
86
+ }
87
+ .throttle-root *, .throttle-root *::before, .throttle-root *::after { box-sizing: inherit; }
88
+
89
+ .throttle-root[data-throttle-theme="dark"] {
90
+ --throttle-color-primary: #e6edf3;
91
+ --throttle-color-primary-foreground: #0d1117;
92
+ --throttle-color-background: #0d1117;
93
+ --throttle-color-surface: #1c2129;
94
+ --throttle-color-foreground: #e6edf3;
95
+ --throttle-color-muted: #21262d;
96
+ --throttle-color-muted-foreground: #9aa4b2;
97
+ --throttle-color-border: #30363d;
98
+ --throttle-color-border-control: #5c6570;
99
+ --throttle-color-danger: #ff6b58;
100
+ --throttle-color-danger-surface: #2a1513;
101
+ --throttle-color-danger-border: #5c2b25;
102
+ --throttle-color-success: #45c08a;
103
+ --throttle-color-success-surface: #0f2419;
104
+ --throttle-color-success-border: #1f4d36;
105
+ --throttle-color-warning: #e0a53a;
106
+ --throttle-color-warning-surface: #2a2113;
107
+ --throttle-color-warning-border: #5c4a25;
108
+ --throttle-shadow-card: 0 1px 3px rgba(0,0,0,.6), 0 1px 2px rgba(0,0,0,.4);
109
+ --throttle-shadow-popover: 0 10px 24px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.35);
110
+ }
111
+ @media (prefers-color-scheme: dark) {
112
+ .throttle-root[data-throttle-theme="auto"] {
113
+ --throttle-color-primary: #e6edf3;
114
+ --throttle-color-primary-foreground: #0d1117;
115
+ --throttle-color-background: #0d1117;
116
+ --throttle-color-surface: #1c2129;
117
+ --throttle-color-foreground: #e6edf3;
118
+ --throttle-color-muted: #21262d;
119
+ --throttle-color-muted-foreground: #9aa4b2;
120
+ --throttle-color-border: #30363d;
121
+ --throttle-color-border-control: #5c6570;
122
+ --throttle-color-danger: #ff6b58;
123
+ --throttle-color-danger-surface: #2a1513;
124
+ --throttle-color-danger-border: #5c2b25;
125
+ --throttle-color-success: #45c08a;
126
+ --throttle-color-success-surface: #0f2419;
127
+ --throttle-color-success-border: #1f4d36;
128
+ --throttle-color-warning: #e0a53a;
129
+ --throttle-color-warning-surface: #2a2113;
130
+ --throttle-color-warning-border: #5c4a25;
131
+ --throttle-shadow-card: 0 1px 3px rgba(0,0,0,.6), 0 1px 2px rgba(0,0,0,.4);
132
+ --throttle-shadow-popover: 0 10px 24px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.35);
133
+ }
134
+ }
135
+
136
+ .throttle-card {
137
+ background: var(--throttle-color-background);
138
+ border: var(--throttle-border-width) solid var(--throttle-color-border);
139
+ border-radius: var(--throttle-radius);
140
+ box-shadow: var(--throttle-shadow-card);
141
+ padding: var(--throttle-space-5);
142
+ width: 100%;
143
+ max-width: var(--throttle-card-width);
144
+ margin-inline: auto;
145
+ }
146
+ /* Inside a Modal the dialog already provides the surface \u2014 background, radius
147
+ and elevation. A card that draws its own on top is the "box inside a box":
148
+ two shadowed rounded rectangles nested, with the card's max-width stranding
149
+ it in the middle of a much wider dialog. */
150
+ .throttle-card--bare { border: none; box-shadow: none; background: none; max-width: none; }
151
+ .throttle-card__header { margin-bottom: var(--throttle-space-4); }
152
+ .throttle-card__header--branded { text-align: center; }
153
+ .throttle-card__logo { display: flex; justify-content: center; margin-bottom: var(--throttle-space-3); }
154
+ .throttle-card__title { font-size: var(--throttle-font-size-xl); line-height: var(--throttle-line-height-tight); font-weight: 600; letter-spacing: -0.02em; margin: 0; }
155
+ .throttle-card__title--branded { font-size: var(--throttle-font-size-xl); }
156
+ .throttle-card__description { color: var(--throttle-color-muted-foreground); margin: var(--throttle-space-1) 0 0; }
157
+ .throttle-card__body { display: flex; flex-direction: column; gap: var(--throttle-space-3); }
158
+ .throttle-card__footer { margin-top: var(--throttle-space-4); text-align: center; }
159
+ .throttle-card__footer--ruled { border-top: var(--throttle-border-width) solid var(--throttle-color-border); padding-top: var(--throttle-space-4); }
160
+
161
+ .throttle-field { display: flex; flex-direction: column; gap: var(--throttle-space-1); }
162
+ .throttle-field__labelrow { display: flex; align-items: baseline; justify-content: space-between; gap: var(--throttle-space-2); }
163
+ .throttle-field__label { font-weight: 500; }
164
+ .throttle-field__input {
165
+ font: inherit;
166
+ font-size: var(--throttle-font-size);
167
+ color: var(--throttle-color-foreground);
168
+ background: var(--throttle-color-background);
169
+ border: var(--throttle-border-width) solid var(--throttle-color-border-control);
170
+ border-radius: var(--throttle-radius-sm);
171
+ /* padding-block stays 0 so min-height alone owns the height, exactly as
172
+ .throttle-button does. With 12px vertical padding the box computed to
173
+ 50px against the button's 48px \u2014 a 2px mismatch, measured in a browser
174
+ across 19 controls. An input element centres its own text vertically. */
175
+ padding-block: 0;
176
+ padding-inline: var(--throttle-control-padding-x);
177
+ min-height: var(--throttle-control-height);
178
+ width: 100%;
179
+ transition: border-color var(--throttle-transition), box-shadow var(--throttle-transition);
180
+ }
181
+ .throttle-field__input:focus-visible {
182
+ outline: none;
183
+ border-color: var(--throttle-color-primary);
184
+ box-shadow: 0 0 0 2px var(--throttle-color-background), 0 0 0 4px var(--throttle-color-primary);
185
+ }
186
+ .throttle-field__input:disabled { background: var(--throttle-color-muted); color: var(--throttle-color-muted-foreground); cursor: not-allowed; }
187
+ .throttle-field__input::placeholder { color: var(--throttle-color-muted-foreground); opacity: 1; }
188
+ .throttle-field__input--grouped { border: none; border-radius: 0; padding-inline: 0; }
189
+ .throttle-field__hint { color: var(--throttle-color-muted-foreground); font-size: var(--throttle-font-size-xs); }
190
+ /* Always mounted, so it reserves its line whether or not there is an error:
191
+ an error appearing on submit must not push the submit button out from
192
+ under the buyer's pointer. */
193
+ .throttle-field__error { color: var(--throttle-color-danger); font-size: var(--throttle-font-size-xs); line-height: 1rem; }
194
+ /* Reserved a full line under EVERY input even with nothing to say, which
195
+ with the stack gap put 28px of dead space beneath each field, on every
196
+ form and all seven account panels. The element stays mounted when empty
197
+ because role=alert only announces changes to a live region that was
198
+ already there; it just stops occupying a line. */
199
+ .throttle-field__error:empty { display: none; }
200
+
201
+ .throttle-field-group {
202
+ display: flex;
203
+ flex-direction: column;
204
+ border: var(--throttle-border-width) solid var(--throttle-color-border);
205
+ border-radius: var(--throttle-radius-sm);
206
+ overflow: hidden;
207
+ }
208
+ .throttle-field-group__item { padding: var(--throttle-space-3) var(--throttle-control-padding-x); }
209
+ .throttle-field-group__divider { height: var(--throttle-border-width); background: var(--throttle-color-border); }
210
+ .throttle-field-group__forgot { margin-left: auto; }
211
+
212
+ .throttle-button {
213
+ font: inherit;
214
+ font-weight: 500;
215
+ border-radius: var(--throttle-radius-sm);
216
+ border: var(--throttle-border-width) solid transparent;
217
+ padding-inline: var(--throttle-control-padding-x);
218
+ padding-block: 0;
219
+ min-height: var(--throttle-control-height);
220
+ display: inline-flex;
221
+ align-items: center;
222
+ justify-content: center;
223
+ cursor: pointer;
224
+ /* A label that wraps turns a control into a two-line block of text. */
225
+ white-space: nowrap;
226
+ transition: opacity var(--throttle-transition), background-color var(--throttle-transition), border-color var(--throttle-transition), filter var(--throttle-transition), box-shadow var(--throttle-transition);
227
+ }
228
+ .throttle-button:disabled { opacity: .55; cursor: not-allowed; }
229
+ .throttle-button:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--throttle-color-background), 0 0 0 4px var(--throttle-color-primary); }
230
+ .throttle-button--primary { background: var(--throttle-color-primary); color: var(--throttle-color-primary-foreground); width: var(--throttle-button-width, 100%); }
231
+ .throttle-button--primary:hover:not(:disabled) { filter: brightness(0.92); }
232
+ .throttle-button--secondary { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); border-color: var(--throttle-color-border-control); }
233
+ .throttle-button--secondary:hover:not(:disabled) { background: var(--throttle-color-muted); }
234
+ .throttle-button--danger { background: transparent; color: var(--throttle-color-danger); border-color: var(--throttle-color-border); }
235
+ .throttle-button--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--throttle-color-danger) 10%, transparent); }
236
+ /* Link-style actions are the rows panel default, and they carry Delete,
237
+ Remove, Cancel and Sign out. Zero padding with min-height 0 made those
238
+ roughly 20px tall and ~8px apart \u2014 a mis-tap on a phone lands on something
239
+ irreversible. The target is expanded with vertical padding and pulled back
240
+ with an equal negative margin, so the hit box clears 44px while the text
241
+ sits exactly where it did. Collapsing this back to zero padding shrinks the
242
+ target again. */
243
+ .throttle-button--link { background: none; border: none; color: var(--throttle-color-primary); padding-inline: 0; padding-block: var(--throttle-space-3); margin-block: calc(var(--throttle-space-3) * -1); min-height: 0; text-decoration: underline; width: auto; }
244
+ .throttle-button--link:hover:not(:disabled) { opacity: .8; }
245
+ .throttle-button--linkDanger { background: none; border: none; color: var(--throttle-color-danger); padding-inline: 0; padding-block: var(--throttle-space-3); margin-block: calc(var(--throttle-space-3) * -1); min-height: 0; text-decoration: underline; width: auto; }
246
+ .throttle-button--linkDanger:hover:not(:disabled) { opacity: .8; }
247
+ .throttle-button--xs { font-size: var(--throttle-font-size-xs); }
248
+
249
+ .throttle-alert { border-radius: var(--throttle-radius-sm); padding: var(--throttle-space-3); font-size: var(--throttle-font-size-sm); }
250
+ .throttle-alert--error { background: var(--throttle-color-danger-surface); border: var(--throttle-border-width) solid var(--throttle-color-danger-border); color: var(--throttle-color-danger); }
251
+ .throttle-alert--success { background: var(--throttle-color-success-surface); border: var(--throttle-border-width) solid var(--throttle-color-success-border); color: var(--throttle-color-success); }
252
+ .throttle-alert--info { background: var(--throttle-color-surface); color: var(--throttle-color-muted-foreground); }
253
+
254
+ .throttle-spinner {
255
+ display: inline-block; width: 1em; height: 1em;
256
+ border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
257
+ animation: throttle-spin 700ms linear infinite;
258
+ }
259
+ @keyframes throttle-spin { to { transform: rotate(360deg); } }
260
+ .throttle-button--loading { position: relative; }
261
+ .throttle-button__label { display: inline-flex; align-items: center; justify-content: center; gap: var(--throttle-space-2); }
262
+ .throttle-button__label--hidden { visibility: hidden; }
263
+ .throttle-spinner--centred { position: absolute; top: 50%; left: 50%; margin: -0.5em 0 0 -0.5em; }
264
+
265
+ @media (prefers-reduced-motion: reduce) { .throttle-spinner { animation-duration: 2.4s; } }
266
+
267
+ .throttle-skeleton {
268
+ background: var(--throttle-color-muted);
269
+ border-radius: var(--throttle-radius-sm);
270
+ animation: throttle-pulse 1.4s ease-in-out infinite; height: 2.5em; }
271
+ @keyframes throttle-pulse { 50% { opacity: .5; } }
272
+ @media (prefers-reduced-motion: reduce) { .throttle-skeleton { animation: none; } }
273
+
274
+ .throttle-modal__overlay {
275
+ position: fixed; inset: 0; z-index: var(--throttle-z-modal);
276
+ background: rgba(14,17,22,.45);
277
+ display: flex; align-items: center; justify-content: center;
278
+ padding: var(--throttle-space-4);
279
+ overflow-y: auto;
280
+ }
281
+ .throttle-modal__container {
282
+ background: var(--throttle-color-background);
283
+ border-radius: var(--throttle-radius);
284
+ box-shadow: var(--throttle-shadow-popover);
285
+ max-width: 720px; width: 100%; max-height: 90vh; overflow-y: auto;
286
+ position: relative;
287
+ }
288
+ /* After the base rule on purpose: both are single-class, so equal specificity
289
+ means source order decides. Declared before it, this silently lost. */
290
+ .throttle-modal__container--compact { max-width: 440px; }
291
+ .throttle-modal__close {
292
+ position: absolute; top: var(--throttle-space-3); right: var(--throttle-space-3);
293
+ background: none; border: none; cursor: pointer; font-size: var(--throttle-font-size-lg);
294
+ color: var(--throttle-color-muted-foreground);
295
+ transition: color var(--throttle-transition);
296
+ }
297
+ .throttle-modal__close:hover { color: var(--throttle-color-foreground); }
298
+ .throttle-modal__header { padding: var(--throttle-space-4) var(--throttle-space-4) 0; }
299
+ .throttle-modal__title { font-size: var(--throttle-font-size-lg); line-height: var(--throttle-line-height-tight); font-weight: 600; margin: 0; }
300
+ .throttle-modal__body { padding: var(--throttle-space-4); }
301
+
302
+ .throttle-dropdown__trigger { background: none; border: none; cursor: pointer; padding: 0; border-radius: var(--throttle-radius-full); transition: background-color var(--throttle-transition); }
303
+ .throttle-dropdown__trigger:hover { background: var(--throttle-color-surface); }
304
+ .throttle-dropdown__trigger:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: 2px; }
305
+ .throttle-dropdown__menu {
306
+ position: absolute; right: 0; top: calc(100% + var(--throttle-space-2));
307
+ z-index: var(--throttle-z-popover);
308
+ min-width: 260px;
309
+ background: var(--throttle-color-background);
310
+ border: var(--throttle-border-width) solid var(--throttle-color-border);
311
+ border-radius: var(--throttle-radius);
312
+ box-shadow: var(--throttle-shadow-popover);
313
+ padding: var(--throttle-space-1);
314
+ }
315
+ .throttle-dropdown__item {
316
+ display: flex; align-items: center; gap: var(--throttle-space-2);
317
+ width: 100%; text-align: left; font: inherit; color: inherit;
318
+ background: none; border: none; cursor: pointer;
319
+ padding: var(--throttle-space-3);
320
+ border-radius: var(--throttle-radius-sm);
321
+ text-decoration: none;
322
+ transition: background-color var(--throttle-transition);
323
+ }
324
+ .throttle-dropdown__item:hover { background: var(--throttle-color-surface); }
325
+ .throttle-dropdown__item:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
326
+ .throttle-dropdown__separator { height: 1px; background: var(--throttle-color-border); margin: var(--throttle-space-1) 0; border: 0; }
327
+
328
+ .throttle-tabs__list {
329
+ display: flex; gap: var(--throttle-space-1);
330
+ border-bottom: var(--throttle-border-width) solid var(--throttle-color-border);
331
+ overflow-x: auto; scrollbar-width: thin;
332
+ }
333
+ .throttle-tabs__tab {
334
+ font: inherit; background: none; border: none; cursor: pointer; white-space: nowrap;
335
+ padding: var(--throttle-space-3);
336
+ color: var(--throttle-color-muted-foreground);
337
+ border-bottom: 2px solid transparent;
338
+ transition: color var(--throttle-transition), border-color var(--throttle-transition);
339
+ }
340
+ .throttle-tabs__tab:hover { color: var(--throttle-color-foreground); }
341
+ .throttle-tabs__tab--active { color: var(--throttle-color-foreground); border-bottom-color: var(--throttle-color-primary); }
342
+ .throttle-tabs__panel { padding-top: var(--throttle-space-4); }
343
+
344
+ /* Paints its own ground. A UserButton in a navbar should stay transparent, but
345
+ the dashboard is a large surface of themed text: in dark theme its foreground
346
+ is near-white, so on a light host page it was rendering white-on-white. */
347
+ .throttle-cardframe { width: 100%; min-height: 420px; border: 0; display: block; }
348
+ .throttle-centre { display: flex; align-items: center; justify-content: center; min-height: 160px; }
349
+ .throttle-fieldrow { display: grid; grid-template-columns: 1fr 1fr; gap: var(--throttle-space-4); align-items: start; }
350
+ .throttle-section__title { font-size: var(--throttle-font-size-xs); text-transform: uppercase; letter-spacing: .06em; color: var(--throttle-color-muted-foreground); margin: 0 0 var(--throttle-space-2); font-weight: 600; }
351
+ .throttle-addresses { display: grid; grid-template-columns: 1fr 1fr; gap: var(--throttle-space-5); }
352
+ .throttle-addressblock__line { margin: 0 0 2px; }
353
+ .throttle-timeline { display: flex; flex-direction: column; gap: var(--throttle-space-2); }
354
+ .throttle-timeline__row { display: flex; align-items: center; gap: var(--throttle-space-2); }
355
+ .throttle-timeline__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--throttle-color-border-control); flex: none; }
356
+ .throttle-timeline__label { flex: 1; }
357
+ .throttle-account-root { container-type: inline-size; background: var(--throttle-color-account-surface); color: var(--throttle-color-foreground); padding: var(--throttle-account-padding); }
358
+ .throttle-account { display: flex; gap: var(--throttle-space-5); align-items: flex-start; }
359
+ .throttle-account__content { flex: 1; min-width: 0; }
360
+ .throttle-tabs__rail { flex: none; width: 220px; display: flex; flex-direction: column; gap: 2px; }
361
+ .throttle-tabs__railitem {
362
+ font: inherit;
363
+ font-size: var(--throttle-font-size-sm);
364
+ display: flex;
365
+ align-items: center;
366
+ gap: var(--throttle-space-3);
367
+ width: 100%;
368
+ text-align: left;
369
+ background: none;
370
+ border: none;
371
+ border-radius: var(--throttle-radius-sm);
372
+ padding: 0 var(--throttle-space-3);
373
+ min-height: var(--throttle-control-height);
374
+ color: var(--throttle-color-muted-foreground);
375
+ cursor: pointer;
376
+ transition: background var(--throttle-transition), color var(--throttle-transition);
377
+ }
378
+ .throttle-tabs__railitem:hover { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); }
379
+ .throttle-tabs__railitem:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
380
+ .throttle-tabs__tab:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
381
+ .throttle-tabs__railitem--active { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); font-weight: 500; }
382
+ .throttle-tabs__railicon { flex: none; opacity: .75; }
383
+
384
+ /* A CONTAINER query, not a viewport one: these components render inside a
385
+ merchant's page, so the viewport says nothing about how much room the
386
+ dashboard actually has \u2014 it could sit in a 400px column on a 1920px screen.
387
+ Below ~560px of container the rail becomes a full-width list above the
388
+ panel: all seven still visible, each still a 44px target. */
389
+ @container (max-width: 560px) {
390
+ /* align-items: flex-start is right in row direction and wrong in column \u2014
391
+ it shrinks the rail to its content width, so width:100% alone cannot make
392
+ it span. Stretch it explicitly when stacked. */
393
+ .throttle-account { flex-direction: column; align-items: stretch; gap: var(--throttle-space-4); }
394
+ .throttle-addresses { grid-template-columns: 1fr; }
395
+ .throttle-fieldrow { grid-template-columns: 1fr; }
396
+ .throttle-tabs__rail { width: 100%; }
397
+ }
398
+
399
+ .throttle-table { width: 100%; border-collapse: collapse; text-align: left; }
400
+ .throttle-table__head { color: var(--throttle-color-muted-foreground); font-weight: 500; font-size: var(--throttle-font-size-sm); }
401
+ .throttle-table__row { border-top: var(--throttle-border-width) solid var(--throttle-color-border); cursor: pointer; transition: background-color var(--throttle-transition); }
402
+ .throttle-table__row:hover { background: var(--throttle-color-surface); }
403
+ .throttle-table__row:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
404
+ .throttle-table__cell { padding: var(--throttle-space-3) var(--throttle-space-2); height: 56px; box-sizing: border-box; }
405
+ .throttle-table__scroll { overflow-x: auto; }
406
+
407
+ .throttle-empty { text-align: center; padding: var(--throttle-space-6) var(--throttle-space-4); }
408
+ .throttle-empty__title { font-weight: 600; margin: 0 0 var(--throttle-space-1); }
409
+ .throttle-empty__description { color: var(--throttle-color-muted-foreground); margin: 0; }
410
+
411
+ .throttle-avatar {
412
+ display: inline-flex; align-items: center; justify-content: center;
413
+ width: 32px; height: 32px; border-radius: var(--throttle-radius-full);
414
+ background: var(--throttle-color-primary); color: var(--throttle-color-primary-foreground);
415
+ font-size: var(--throttle-font-size-xs); font-weight: 600; text-transform: uppercase;
416
+ }
417
+
418
+ .throttle-badge {
419
+ display: inline-block; font-size: var(--throttle-font-size-xs); font-weight: 500;
420
+ padding: 2px var(--throttle-space-2); border-radius: var(--throttle-radius-full);
421
+ background: var(--throttle-color-muted); color: var(--throttle-color-muted-foreground);
422
+ }
423
+ .throttle-badge--muted { background: none; border: var(--throttle-border-width) solid var(--throttle-color-border); color: var(--throttle-color-muted-foreground); font-weight: 400; padding: 1px calc(var(--throttle-space-2) - 1px); }
424
+ .throttle-badge--success { background: color-mix(in srgb, var(--throttle-color-success) 15%, transparent); color: var(--throttle-color-success); }
425
+ .throttle-badge--warning { background: color-mix(in srgb, var(--throttle-color-warning) 15%, transparent); color: var(--throttle-color-warning); }
426
+ .throttle-badge--danger { background: color-mix(in srgb, var(--throttle-color-danger) 15%, transparent); color: var(--throttle-color-danger); }
427
+
428
+ .throttle-stack { display: flex; flex-direction: column; gap: var(--throttle-space-3); }
429
+ .throttle-row { display: flex; align-items: center; justify-content: space-between; gap: var(--throttle-space-2); flex-wrap: wrap; }
430
+ .throttle-muted { color: var(--throttle-color-muted-foreground); }
431
+ .throttle-banner {
432
+ display: flex; align-items: center; justify-content: space-between; gap: var(--throttle-space-2);
433
+ flex-wrap: wrap;
434
+ background: color-mix(in srgb, var(--throttle-color-warning) 12%, transparent);
435
+ color: var(--throttle-color-warning);
436
+ border-radius: var(--throttle-radius-sm);
437
+ padding: var(--throttle-space-3);
438
+ margin-bottom: var(--throttle-space-4);
439
+ }
440
+ .throttle-panel { display: flex; flex-direction: column; gap: var(--throttle-space-4); }
441
+ /* 100dvh, not 100vh: on mobile browsers 100vh includes the space behind the
442
+ dynamic address/toolbar, so the page would be taller than the visible
443
+ viewport and the card would sit off-centre until the toolbar collapsed. */
444
+ .throttle-page {
445
+ min-height: 100dvh;
446
+ width: 100%;
447
+ background: var(--throttle-color-surface);
448
+ display: flex;
449
+ flex-direction: column;
450
+ align-items: center;
451
+ justify-content: center;
452
+ gap: var(--throttle-space-5);
453
+ padding: var(--throttle-space-5);
454
+ }
455
+ .throttle-page__logo { display: flex; justify-content: center; }
456
+ .throttle-page__footer { color: var(--throttle-color-muted-foreground); text-align: center; font-size: var(--throttle-font-size-sm); }
457
+ .throttle-listitem {
458
+ display: flex; align-items: flex-start; justify-content: space-between; gap: var(--throttle-space-2);
459
+ flex-wrap: wrap;
460
+ border: var(--throttle-border-width) solid var(--throttle-color-border);
461
+ border-radius: var(--throttle-radius-sm);
462
+ padding: var(--throttle-space-3);
463
+ }
464
+ /* 'rows' variant: hairline rules between items instead of each item carrying
465
+ its own border+radius, so a panel does not read as a card inside a card. */
466
+ .throttle-rows { display: flex; flex-direction: column; }
467
+ .throttle-rowitem {
468
+ display: flex; align-items: flex-start; justify-content: space-between; gap: var(--throttle-space-2);
469
+ flex-wrap: wrap;
470
+ border-top: var(--throttle-border-width) solid var(--throttle-color-border);
471
+ padding: var(--throttle-space-3) 0;
472
+ }
473
+ .throttle-rowitem:first-child { border-top: none; padding-top: 0; }
474
+ .throttle-actions {
475
+ display: flex; gap: var(--throttle-space-2); flex-wrap: wrap; align-items: center;
476
+ /* Primary buttons are full-width so a form's submit fills its card. In a row
477
+ of sibling actions that same rule made every button claim 100% and then
478
+ shrink to share the line, squeezing Resume to 59px so its label wrapped to
479
+ two lines inside a 48px box. A custom property INHERITS to the buttons, so
480
+ the row opts them back to content width without a descendant selector \u2014
481
+ those outrank the merchant overrides in appearance.elements. */
482
+ --throttle-button-width: auto;
483
+ }
484
+ .throttle-table__money { display: block; text-align: right; font-variant-numeric: tabular-nums; }
485
+ .throttle-lineitem { display: flex; align-items: flex-start; gap: var(--throttle-space-3); padding: var(--throttle-space-2) 0; }
486
+ /* The UA's 1em paragraph margins pushed a two-line item to 96px, so five
487
+ items filled a screen with mostly whitespace. These carry the spacing
488
+ instead. Separate classes, not a ".throttle-lineitem p" descendant
489
+ selector -- those outrank the merchant's appearance.elements overrides. */
490
+ .throttle-lineitem__desc { margin: var(--throttle-space-1) 0 0; color: var(--throttle-color-muted-foreground); }
491
+ .throttle-lineitem__meta { margin: var(--throttle-space-1) 0 0; color: var(--throttle-color-muted-foreground); font-size: var(--throttle-font-size-sm); }
492
+ .throttle-lineitem__image { width: 48px; height: 48px; object-fit: cover; border-radius: var(--throttle-radius-sm); flex: none; }
493
+ .throttle-totals { display: flex; flex-direction: column; gap: var(--throttle-space-1); }
494
+
495
+ /* Order and invoice detail: the record on the left, what it costs and what
496
+ you can do about it on the right. The rail sticks so the total and the
497
+ actions stay reachable on an order with thirty lines, which is exactly when
498
+ a buyer most wants them. Collapses below, where a sticky rail would eat a
499
+ phone screen. */
500
+ .throttle-detail { display: grid; grid-template-columns: minmax(0, 1fr) 17rem; gap: var(--throttle-space-6); align-items: start; }
501
+ .throttle-detail__main { display: flex; flex-direction: column; gap: var(--throttle-space-5); min-width: 0; }
502
+ .throttle-detail__rail {
503
+ display: flex; flex-direction: column; gap: var(--throttle-space-4);
504
+ position: sticky; top: var(--throttle-space-4);
505
+ padding: var(--throttle-space-4);
506
+ border: var(--throttle-border-width) solid var(--throttle-color-border);
507
+ border-radius: var(--throttle-radius-md);
508
+ background: var(--throttle-color-surface);
509
+ }
510
+ /* One column, and the rail stops sticking \u2014 on a phone a sticky summary covers
511
+ the very line items it is summarising. Placed AFTER the base rules above: a
512
+ container query adds no specificity, so the later rule wins. */
513
+ @container (max-width: 560px) {
514
+ .throttle-detail { grid-template-columns: 1fr; }
515
+ .throttle-detail__rail { position: static; }
516
+ }
517
+ /* The one number the buyer came for. */
518
+ .throttle-detail__headline { font-size: var(--throttle-font-size-xl); font-weight: 600; line-height: 1.2; }
519
+ .throttle-detail__section { display: flex; flex-direction: column; gap: var(--throttle-space-2); }
520
+ .throttle-detail__label { font-size: var(--throttle-font-size-sm); font-weight: 500; color: var(--throttle-color-muted-foreground); letter-spacing: .04em; text-transform: uppercase; }
521
+ /* A rail action column: full width, stacked, so the primary reads as primary. */
522
+ .throttle-detail__actions { display: flex; flex-direction: column; gap: var(--throttle-space-2); --throttle-button-width: 100%; }
523
+ .throttle-detail__link { display: inline-flex; align-items: center; gap: var(--throttle-space-1); }
524
+
525
+ @media (prefers-reduced-motion: reduce) {
526
+ .throttle-button, .throttle-field__input, .throttle-dropdown__item, .throttle-dropdown__trigger, .throttle-tabs__tab, .throttle-table__row, .throttle-modal__close {
527
+ transition: none;
528
+ }
529
+ }
530
+ `;
531
+ function useInjectStyles(enabled) {
532
+ useInsertionEffect(() => {
533
+ if (!enabled) return;
534
+ if (typeof document === "undefined") return;
535
+ if (document.getElementById(THROTTLE_STYLE_ID)) return;
536
+ const tag = document.createElement("style");
537
+ tag.id = THROTTLE_STYLE_ID;
538
+ tag.textContent = THROTTLE_STYLES;
539
+ document.head.appendChild(tag);
540
+ }, [enabled]);
541
+ }
542
+
543
+ // src/react/components/provider.tsx
544
+ import { jsx, jsxs } from "react/jsx-runtime";
545
+ var loadAuthModal = () => import("./AuthModal-T2GYI6ZF.js").then((m) => ({ default: m.AuthModal }));
546
+ var loadAccountModal = () => import("./AccountModal-KA54WUFW.js").then((m) => ({ default: m.AccountModal }));
547
+ var AuthModal = lazy(loadAuthModal);
548
+ var AccountModal = lazy(loadAccountModal);
549
+ function resetModal(which) {
550
+ if (which === "auth") AuthModal = lazy(loadAuthModal);
551
+ else AccountModal = lazy(loadAccountModal);
552
+ }
553
+ var modalLoadLogged = false;
554
+ var ModalLoadBoundary = class extends Component {
555
+ state = { failed: false };
556
+ static getDerivedStateFromError() {
557
+ return { failed: true };
558
+ }
559
+ componentDidCatch(error) {
560
+ resetModal(this.props.which);
561
+ if (!modalLoadLogged) {
562
+ modalLoadLogged = true;
563
+ console.warn(`[@usethrottle/auth] the ${this.props.which} dialog failed to load or render: ${error?.message ?? "unknown error"}`);
564
+ }
565
+ this.props.onFail();
566
+ }
567
+ render() {
568
+ return this.state.failed ? null : this.props.children;
569
+ }
570
+ };
571
+ var warned = /* @__PURE__ */ new Set();
572
+ function warnOnce(key, message) {
573
+ if (process.env.NODE_ENV === "production") return;
574
+ if (warned.has(key)) return;
575
+ warned.add(key);
576
+ console.warn(`[@usethrottle/auth] ${message}`);
577
+ }
578
+ var ThrottleUIContext = createContext(null);
579
+ function useThrottle() {
580
+ const ctx = useContext(ThrottleUIContext);
581
+ if (!ctx) {
582
+ warnOnce(
583
+ "use-throttle-no-provider",
584
+ "useThrottle() was called without a <ThrottleProvider> above it in the tree. openSignIn/openSignUp/openAccount will do nothing \u2014 wrap your app in <ThrottleProvider> to use them."
585
+ );
586
+ return { openSignIn: () => {
587
+ }, openSignUp: () => {
588
+ }, openAccount: () => {
589
+ }, close: () => {
590
+ }, isOpen: false };
591
+ }
592
+ return {
593
+ openSignIn: () => ctx.openSignIn(),
594
+ openSignUp: () => ctx.openSignUp(),
595
+ openAccount: ctx.openAccount,
596
+ close: ctx.close,
597
+ isOpen: ctx.overlay !== null
598
+ };
599
+ }
600
+ function ThrottleProvider(props) {
601
+ const {
602
+ publishableKey,
603
+ baseUrl,
604
+ storage,
605
+ appearance,
606
+ localization,
607
+ logo,
608
+ injectStyles = true,
609
+ claimCartOnSignIn,
610
+ auth: injected,
611
+ children,
612
+ __captureStepUp
613
+ } = props;
614
+ useInjectStyles(injectStyles);
615
+ const [stepUpOpen, setStepUpOpen] = useState(false);
616
+ const resolveStepUp = useRef(null);
617
+ const claimRef = useRef(claimCartOnSignIn);
618
+ claimRef.current = claimCartOnSignIn;
619
+ const [overlay, setOverlay] = useState(null);
620
+ const overlayRestoreFocus = useRef(null);
621
+ const [accountTabs, setAccountTabs] = useState(void 0);
622
+ const onOverlaySuccess = useRef(null);
623
+ const openSignIn = useCallback((onSuccess) => {
624
+ onOverlaySuccess.current = onSuccess ?? null;
625
+ setOverlay("signIn");
626
+ }, []);
627
+ const openSignUp = useCallback((onSuccess) => {
628
+ onOverlaySuccess.current = onSuccess ?? null;
629
+ setOverlay("signUp");
630
+ }, []);
631
+ const openAccount = useCallback((opts) => {
632
+ onOverlaySuccess.current = null;
633
+ overlayRestoreFocus.current = opts?.restoreFocusTo ?? null;
634
+ setAccountTabs(opts?.tabs);
635
+ setOverlay("account");
636
+ }, []);
637
+ const closeOverlay = useCallback(() => {
638
+ onOverlaySuccess.current = null;
639
+ overlayRestoreFocus.current = null;
640
+ setOverlay(null);
641
+ }, []);
642
+ const handleOverlaySuccess = useCallback(() => {
643
+ const fn = onOverlaySuccess.current;
644
+ onOverlaySuccess.current = null;
645
+ setOverlay(null);
646
+ fn?.();
647
+ }, []);
648
+ const ui = useMemo(
649
+ () => ({ overlay, openSignIn, openSignUp, openAccount, close: closeOverlay }),
650
+ [overlay, openSignIn, openSignUp, openAccount, closeOverlay]
651
+ );
652
+ const askForStepUp = useCallback(
653
+ () => new Promise((resolve) => {
654
+ resolveStepUp.current = resolve;
655
+ setStepUpOpen(true);
656
+ }),
657
+ []
658
+ );
659
+ const pendingRef = useRef(null);
660
+ const auth = useMemo(() => {
661
+ __captureStepUp?.(askForStepUp);
662
+ const client = injected ?? createThrottleAuth({ publishableKey, baseUrl, storage, onStepUpRequired: askForStepUp });
663
+ const account = createAccount(client);
664
+ const claim = async () => {
665
+ const cartId = claimRef.current?.();
666
+ if (!cartId) return;
667
+ try {
668
+ await account.claimCart(cartId);
669
+ } catch (err) {
670
+ warnOnce("claim-cart", `claimCartOnSignIn failed: ${err?.message ?? err}`);
671
+ }
672
+ };
673
+ const track = (run) => {
674
+ const prior = pendingRef.current;
675
+ let done;
676
+ const mine = new Promise((r) => {
677
+ done = r;
678
+ }).finally(() => {
679
+ if (pendingRef.current === mine) pendingRef.current = null;
680
+ });
681
+ pendingRef.current = mine;
682
+ const op = (async () => {
683
+ if (prior) await prior;
684
+ return run();
685
+ })();
686
+ op.then(done, done);
687
+ return op;
688
+ };
689
+ return {
690
+ ...client,
691
+ signIn: (email, password) => track(async () => {
692
+ await client.signIn(email, password);
693
+ await claim();
694
+ }),
695
+ register: (email, password) => track(async () => {
696
+ const result = await client.register(email, password);
697
+ if (result.status === "active") await claim();
698
+ return result;
699
+ }),
700
+ whenClaimSettled: () => pendingRef.current ?? Promise.resolve()
701
+ };
702
+ }, [injected, publishableKey, baseUrl]);
703
+ const finishStepUp = (password) => {
704
+ setStepUpOpen(false);
705
+ const resolve = resolveStepUp.current;
706
+ resolveStepUp.current = null;
707
+ resolve?.(password);
708
+ };
709
+ const authOverlayOpen = overlay === "signIn" || overlay === "signUp";
710
+ const [authSeen, setAuthSeen] = useState(false);
711
+ const [accountSeen, setAccountSeen] = useState(false);
712
+ if (authOverlayOpen && !authSeen) setAuthSeen(true);
713
+ if (overlay === "account" && !accountSeen) setAccountSeen(true);
714
+ const failAuthLoad = () => {
715
+ setAuthSeen(false);
716
+ setOverlay(null);
717
+ };
718
+ const failAccountLoad = () => {
719
+ setAccountSeen(false);
720
+ setOverlay(null);
721
+ };
722
+ return /* @__PURE__ */ jsx(AppearanceProvider, { value: appearance ?? {}, children: /* @__PURE__ */ jsx(LocalizationProvider, { value: localization ?? {}, children: /* @__PURE__ */ jsx(BrandLogoProvider, { value: logo ?? null, children: /* @__PURE__ */ jsx(ThrottleAuthProvider, { auth, publishableKey, children: /* @__PURE__ */ jsx(ConfirmProvider, { stepUpOpen, onStepUpResolve: finishStepUp, children: /* @__PURE__ */ jsxs(ThrottleUIContext.Provider, { value: ui, children: [
723
+ children,
724
+ authSeen && /* @__PURE__ */ jsx(ModalLoadBoundary, { which: "auth", onFail: failAuthLoad, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
725
+ AuthModal,
726
+ {
727
+ open: authOverlayOpen,
728
+ onClose: closeOverlay,
729
+ initialView: overlay === "signUp" ? "signUp" : "signIn",
730
+ afterSignIn: handleOverlaySuccess,
731
+ afterSignUp: handleOverlaySuccess
732
+ },
733
+ overlay === "signUp" ? "signUp" : "signIn"
734
+ ) }) }),
735
+ accountSeen && /* @__PURE__ */ jsx(ModalLoadBoundary, { which: "account", onFail: failAccountLoad, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(AccountModal, { open: overlay === "account", onClose: closeOverlay, tabs: accountTabs, restoreFocusTo: overlayRestoreFocus.current }) }) })
736
+ ] }) }) }) }) }) });
737
+ }
738
+
739
+ export {
740
+ THROTTLE_STYLE_ID,
741
+ THROTTLE_STYLES,
742
+ warnOnce,
743
+ ThrottleUIContext,
744
+ useThrottle,
745
+ ThrottleProvider
746
+ };
747
+ //# sourceMappingURL=chunk-RCNUWITB.js.map