@usethrottle/auth 0.2.1 → 0.2.2
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/README.md +1 -0
- package/dist/react/components/index.cjs +90 -15
- package/dist/react/components/index.cjs.map +1 -1
- package/dist/react/components/index.d.cts +2 -1
- package/dist/react/components/index.d.ts +2 -1
- package/dist/react/components/index.js +101 -26
- package/dist/react/components/index.js.map +1 -1
- package/dist/styles.css +11 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -305,6 +305,7 @@ every top-level component renders:
|
|
|
305
305
|
| `colorMuted` | `--throttle-color-muted` | `#eceef2` |
|
|
306
306
|
| `colorMutedForeground` | `--throttle-color-muted-foreground` | `#5b6472` |
|
|
307
307
|
| `colorBorder` | `--throttle-color-border` | `#e2e8f0` |
|
|
308
|
+
| `colorBorderControl` | `--throttle-color-border-control` | `#8b93a1` |
|
|
308
309
|
| `colorDanger` | `--throttle-color-danger` | `#c2321f` |
|
|
309
310
|
| `colorDangerSurface` | `--throttle-color-danger-surface` | `#fef2f2` |
|
|
310
311
|
| `colorDangerBorder` | `--throttle-color-danger-border` | `#fecaca` |
|
|
@@ -595,6 +595,7 @@ var VARIABLE_PROPERTY = {
|
|
|
595
595
|
colorMuted: "--throttle-color-muted",
|
|
596
596
|
colorMutedForeground: "--throttle-color-muted-foreground",
|
|
597
597
|
colorBorder: "--throttle-color-border",
|
|
598
|
+
colorBorderControl: "--throttle-color-border-control",
|
|
598
599
|
colorDanger: "--throttle-color-danger",
|
|
599
600
|
colorDangerSurface: "--throttle-color-danger-surface",
|
|
600
601
|
colorDangerBorder: "--throttle-color-danger-border",
|
|
@@ -1209,6 +1210,13 @@ var THROTTLE_STYLES = `
|
|
|
1209
1210
|
--throttle-color-muted: #eceef2;
|
|
1210
1211
|
--throttle-color-muted-foreground: #5b6472;
|
|
1211
1212
|
--throttle-color-border: #e2e8f0;
|
|
1213
|
+
/* The boundary of an INTERACTIVE control, which WCAG 1.4.11 requires to
|
|
1214
|
+
reach 3:1 against its adjacent colour \u2014 an input identified only by a
|
|
1215
|
+
1.23:1 hairline is not identifiable at all. Decorative separators (card
|
|
1216
|
+
edges, table rules, dividers) keep --throttle-color-border: they do not
|
|
1217
|
+
identify a control, so the requirement does not apply and darkening
|
|
1218
|
+
them would only add noise. */
|
|
1219
|
+
--throttle-color-border-control: #8b93a1;
|
|
1212
1220
|
--throttle-color-danger: #c2321f;
|
|
1213
1221
|
--throttle-color-danger-surface: #fef2f2;
|
|
1214
1222
|
--throttle-color-danger-border: #fecaca;
|
|
@@ -1261,6 +1269,7 @@ var THROTTLE_STYLES = `
|
|
|
1261
1269
|
--throttle-color-muted: #21262d;
|
|
1262
1270
|
--throttle-color-muted-foreground: #9aa4b2;
|
|
1263
1271
|
--throttle-color-border: #30363d;
|
|
1272
|
+
--throttle-color-border-control: #5c6570;
|
|
1264
1273
|
--throttle-color-danger: #ff6b58;
|
|
1265
1274
|
--throttle-color-danger-surface: #2a1513;
|
|
1266
1275
|
--throttle-color-danger-border: #5c2b25;
|
|
@@ -1283,6 +1292,7 @@ var THROTTLE_STYLES = `
|
|
|
1283
1292
|
--throttle-color-muted: #21262d;
|
|
1284
1293
|
--throttle-color-muted-foreground: #9aa4b2;
|
|
1285
1294
|
--throttle-color-border: #30363d;
|
|
1295
|
+
--throttle-color-border-control: #5c6570;
|
|
1286
1296
|
--throttle-color-danger: #ff6b58;
|
|
1287
1297
|
--throttle-color-danger-surface: #2a1513;
|
|
1288
1298
|
--throttle-color-danger-border: #5c2b25;
|
|
@@ -1330,7 +1340,7 @@ var THROTTLE_STYLES = `
|
|
|
1330
1340
|
font-size: var(--throttle-font-size);
|
|
1331
1341
|
color: var(--throttle-color-foreground);
|
|
1332
1342
|
background: var(--throttle-color-background);
|
|
1333
|
-
border: var(--throttle-border-width) solid var(--throttle-color-border);
|
|
1343
|
+
border: var(--throttle-border-width) solid var(--throttle-color-border-control);
|
|
1334
1344
|
border-radius: var(--throttle-radius-sm);
|
|
1335
1345
|
/* padding-block stays 0 so min-height alone owns the height, exactly as
|
|
1336
1346
|
.throttle-button does. With 12px vertical padding the box computed to
|
|
@@ -1385,7 +1395,7 @@ var THROTTLE_STYLES = `
|
|
|
1385
1395
|
.throttle-button:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--throttle-color-background), 0 0 0 4px var(--throttle-color-primary); }
|
|
1386
1396
|
.throttle-button--primary { background: var(--throttle-color-primary); color: var(--throttle-color-primary-foreground); width: 100%; }
|
|
1387
1397
|
.throttle-button--primary:hover:not(:disabled) { filter: brightness(0.92); }
|
|
1388
|
-
.throttle-button--secondary { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); border-color: var(--throttle-color-border); }
|
|
1398
|
+
.throttle-button--secondary { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); border-color: var(--throttle-color-border-control); }
|
|
1389
1399
|
.throttle-button--secondary:hover:not(:disabled) { background: var(--throttle-color-muted); }
|
|
1390
1400
|
.throttle-button--danger { background: transparent; color: var(--throttle-color-danger); border-color: var(--throttle-color-border); }
|
|
1391
1401
|
.throttle-button--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--throttle-color-danger) 10%, transparent); }
|
|
@@ -1808,14 +1818,14 @@ function Modal(props) {
|
|
|
1808
1818
|
e.preventDefault();
|
|
1809
1819
|
return;
|
|
1810
1820
|
}
|
|
1811
|
-
const
|
|
1821
|
+
const first2 = items[0];
|
|
1812
1822
|
const last = items[items.length - 1];
|
|
1813
|
-
if (e.shiftKey && document.activeElement ===
|
|
1823
|
+
if (e.shiftKey && document.activeElement === first2) {
|
|
1814
1824
|
e.preventDefault();
|
|
1815
1825
|
last.focus();
|
|
1816
1826
|
} else if (!e.shiftKey && document.activeElement === last) {
|
|
1817
1827
|
e.preventDefault();
|
|
1818
|
-
|
|
1828
|
+
first2.focus();
|
|
1819
1829
|
}
|
|
1820
1830
|
};
|
|
1821
1831
|
document.addEventListener("keydown", onKeyDown);
|
|
@@ -2900,9 +2910,10 @@ function ProfilePanel(props) {
|
|
|
2900
2910
|
const resource = useCustomer();
|
|
2901
2911
|
const { submitting, error, notice, run } = useAction();
|
|
2902
2912
|
const [form, setForm] = (0, import_react21.useState)({ firstName: "", lastName: "", phone: "", company: "", acceptsMarketing: false });
|
|
2913
|
+
const edited = (0, import_react21.useRef)(false);
|
|
2903
2914
|
(0, import_react21.useEffect)(() => {
|
|
2904
2915
|
const c = resource.data;
|
|
2905
|
-
if (!c) return;
|
|
2916
|
+
if (!c || edited.current) return;
|
|
2906
2917
|
setForm({
|
|
2907
2918
|
firstName: c.firstName ?? "",
|
|
2908
2919
|
lastName: c.lastName ?? "",
|
|
@@ -2926,6 +2937,7 @@ function ProfilePanel(props) {
|
|
|
2926
2937
|
onSubmit: (e) => {
|
|
2927
2938
|
e.preventDefault();
|
|
2928
2939
|
void run(async () => {
|
|
2940
|
+
edited.current = false;
|
|
2929
2941
|
await resource.update({
|
|
2930
2942
|
firstName: form.firstName,
|
|
2931
2943
|
lastName: form.lastName,
|
|
@@ -2955,7 +2967,10 @@ function ProfilePanel(props) {
|
|
|
2955
2967
|
label: t.profile.firstName,
|
|
2956
2968
|
value: form.firstName,
|
|
2957
2969
|
autoComplete: "given-name",
|
|
2958
|
-
onChange: (v) =>
|
|
2970
|
+
onChange: (v) => {
|
|
2971
|
+
edited.current = true;
|
|
2972
|
+
setForm((f) => ({ ...f, firstName: v }));
|
|
2973
|
+
}
|
|
2959
2974
|
}
|
|
2960
2975
|
),
|
|
2961
2976
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
@@ -2964,7 +2979,10 @@ function ProfilePanel(props) {
|
|
|
2964
2979
|
label: t.profile.lastName,
|
|
2965
2980
|
value: form.lastName,
|
|
2966
2981
|
autoComplete: "family-name",
|
|
2967
|
-
onChange: (v) =>
|
|
2982
|
+
onChange: (v) => {
|
|
2983
|
+
edited.current = true;
|
|
2984
|
+
setForm((f) => ({ ...f, lastName: v }));
|
|
2985
|
+
}
|
|
2968
2986
|
}
|
|
2969
2987
|
),
|
|
2970
2988
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
@@ -2974,7 +2992,10 @@ function ProfilePanel(props) {
|
|
|
2974
2992
|
type: "tel",
|
|
2975
2993
|
value: form.phone,
|
|
2976
2994
|
autoComplete: "tel",
|
|
2977
|
-
onChange: (v) =>
|
|
2995
|
+
onChange: (v) => {
|
|
2996
|
+
edited.current = true;
|
|
2997
|
+
setForm((f) => ({ ...f, phone: v }));
|
|
2998
|
+
}
|
|
2978
2999
|
}
|
|
2979
3000
|
),
|
|
2980
3001
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
@@ -2983,7 +3004,10 @@ function ProfilePanel(props) {
|
|
|
2983
3004
|
label: t.profile.company,
|
|
2984
3005
|
value: form.company,
|
|
2985
3006
|
autoComplete: "organization",
|
|
2986
|
-
onChange: (v) =>
|
|
3007
|
+
onChange: (v) => {
|
|
3008
|
+
edited.current = true;
|
|
3009
|
+
setForm((f) => ({ ...f, company: v }));
|
|
3010
|
+
}
|
|
2987
3011
|
}
|
|
2988
3012
|
),
|
|
2989
3013
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("label", { className: "throttle-row", children: [
|
|
@@ -2992,7 +3016,10 @@ function ProfilePanel(props) {
|
|
|
2992
3016
|
{
|
|
2993
3017
|
type: "checkbox",
|
|
2994
3018
|
checked: form.acceptsMarketing,
|
|
2995
|
-
onChange: (e) =>
|
|
3019
|
+
onChange: (e) => {
|
|
3020
|
+
edited.current = true;
|
|
3021
|
+
setForm((f) => ({ ...f, acceptsMarketing: e.target.checked }));
|
|
3022
|
+
}
|
|
2996
3023
|
}
|
|
2997
3024
|
),
|
|
2998
3025
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { children: t.profile.acceptsMarketing })
|
|
@@ -3433,7 +3460,15 @@ function OrderDetail(props) {
|
|
|
3433
3460
|
const { t, locale } = useCopy(props.localization);
|
|
3434
3461
|
const resource = useOrder(props.orderId);
|
|
3435
3462
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { ...a.el("orderDetail.root", "throttle-panel"), children: [
|
|
3436
|
-
props.onBack ?
|
|
3463
|
+
props.onBack ? (
|
|
3464
|
+
// Wrapped on purpose. The panel is a column flex container, so a
|
|
3465
|
+
// direct child stretches to its full width however `width: auto`
|
|
3466
|
+
// the link variant is — the control rendered 748px wide with a
|
|
3467
|
+
// centred label. The wrapper takes the stretch; the button keeps
|
|
3468
|
+
// its own width. A CSS fix would need a compound selector, and
|
|
3469
|
+
// those outrank the merchant overrides in `appearance.elements`.
|
|
3470
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Button, { variant: "link", elementKey: "orderDetail.backButton", onClick: props.onBack, children: t.common.back }) })
|
|
3471
|
+
) : null,
|
|
3437
3472
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
3438
3473
|
PanelState,
|
|
3439
3474
|
{
|
|
@@ -3575,7 +3610,15 @@ function InvoiceDetail(props) {
|
|
|
3575
3610
|
const resource = useInvoice(props.invoiceId);
|
|
3576
3611
|
const { submitting, error, run } = useAction();
|
|
3577
3612
|
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { ...a.el("invoiceDetail.root", "throttle-panel"), children: [
|
|
3578
|
-
props.onBack ?
|
|
3613
|
+
props.onBack ? (
|
|
3614
|
+
// Wrapped on purpose. The panel is a column flex container, so a
|
|
3615
|
+
// direct child stretches to its full width however `width: auto`
|
|
3616
|
+
// the link variant is — the control rendered 748px wide with a
|
|
3617
|
+
// centred label. The wrapper takes the stretch; the button keeps
|
|
3618
|
+
// its own width. A CSS fix would need a compound selector, and
|
|
3619
|
+
// those outrank the merchant overrides in `appearance.elements`.
|
|
3620
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Button, { variant: "link", elementKey: "invoiceDetail.backButton", onClick: props.onBack, children: t.common.back }) })
|
|
3621
|
+
) : null,
|
|
3579
3622
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PanelState, { resource, isEmpty: (i) => i === null, emptyTitle: t.invoices.empty, skeletonRows: 4, children: (invoice) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
|
|
3580
3623
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "throttle-row", children: [
|
|
3581
3624
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h3", { children: invoice.number }),
|
|
@@ -3825,6 +3868,38 @@ function SubscriptionsPanel(props) {
|
|
|
3825
3868
|
|
|
3826
3869
|
// src/react/components/account/SecurityPanel.tsx
|
|
3827
3870
|
var import_react26 = require("react");
|
|
3871
|
+
|
|
3872
|
+
// src/react/components/account/device.ts
|
|
3873
|
+
var BROWSERS = [
|
|
3874
|
+
[/\bEdg(?:e|A|iOS)?\//, "Edge"],
|
|
3875
|
+
[/\bOPR\/|\bOpera\b/, "Opera"],
|
|
3876
|
+
// No leading \b: real agents ship prefixed builds (HeadlessChrome), and a
|
|
3877
|
+
// word boundary there sends them to the Safari rule instead.
|
|
3878
|
+
[/Chrome\/|CriOS\//, "Chrome"],
|
|
3879
|
+
[/\bFirefox\/|\bFxiOS\//, "Firefox"],
|
|
3880
|
+
[/\bSafari\//, "Safari"]
|
|
3881
|
+
];
|
|
3882
|
+
var PLATFORMS = [
|
|
3883
|
+
[/\biPhone\b|\biPad\b|\biPod\b/, "iOS"],
|
|
3884
|
+
[/\bAndroid\b/, "Android"],
|
|
3885
|
+
[/\bMac OS X\b|\bMacintosh\b/, "macOS"],
|
|
3886
|
+
[/\bWindows\b/, "Windows"],
|
|
3887
|
+
[/\bLinux\b|\bX11\b/, "Linux"]
|
|
3888
|
+
];
|
|
3889
|
+
function first(ua, table) {
|
|
3890
|
+
for (const [pattern, label] of table) if (pattern.test(ua)) return label;
|
|
3891
|
+
return null;
|
|
3892
|
+
}
|
|
3893
|
+
function describeDevice(userAgent) {
|
|
3894
|
+
const ua = (userAgent ?? "").trim();
|
|
3895
|
+
if (!ua) return "";
|
|
3896
|
+
const browser = first(ua, BROWSERS);
|
|
3897
|
+
if (!browser) return ua;
|
|
3898
|
+
const platform = first(ua, PLATFORMS);
|
|
3899
|
+
return platform ? `${browser} on ${platform}` : browser;
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
// src/react/components/account/SecurityPanel.tsx
|
|
3828
3903
|
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
3829
3904
|
function SecurityPanel(props) {
|
|
3830
3905
|
const a = useAppearance(props.appearance);
|
|
@@ -3905,7 +3980,7 @@ function SecurityPanel(props) {
|
|
|
3905
3980
|
rows: sessions,
|
|
3906
3981
|
columns: [
|
|
3907
3982
|
{ key: "device", label: t.security.columnDevice, render: (session) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "throttle-row", children: [
|
|
3908
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { children: session.userAgent || session.ipPrefix || session.id }),
|
|
3983
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { children: describeDevice(session.userAgent) || session.ipPrefix || session.id }),
|
|
3909
3984
|
session.current ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Badge, { tone: "success", children: t.security.thisDevice }) : null
|
|
3910
3985
|
] }) },
|
|
3911
3986
|
{ key: "lastActive", label: t.security.lastActive, render: (session) => formatDate(session.lastUsedAt, locale) },
|
|
@@ -3927,7 +4002,7 @@ function SecurityPanel(props) {
|
|
|
3927
4002
|
) : (sessions ?? []).map((session) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { ...a.el("securityPanel.sessionItem", itemBase), children: [
|
|
3928
4003
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { children: [
|
|
3929
4004
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "throttle-row", children: [
|
|
3930
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: session.userAgent || session.ipPrefix || session.id }),
|
|
4005
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: describeDevice(session.userAgent) || session.ipPrefix || session.id }),
|
|
3931
4006
|
session.current ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Badge, { tone: "success", children: t.security.thisDevice }) : null
|
|
3932
4007
|
] }),
|
|
3933
4008
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { className: "throttle-muted", children: [
|