@elvix.is/sdk 0.5.4 → 0.5.6
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/react.d.ts +92 -34
- package/dist/react.js +1005 -304
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
// src/react/size.ts
|
|
3
|
+
function sizeStyle(p) {
|
|
4
|
+
const s = {};
|
|
5
|
+
if (p.width !== void 0) s.width = p.width;
|
|
6
|
+
if (p.height !== void 0) s.height = p.height;
|
|
7
|
+
if (p.minWidth !== void 0) s.minWidth = p.minWidth;
|
|
8
|
+
if (p.maxWidth !== void 0) s.maxWidth = p.maxWidth;
|
|
9
|
+
if (p.minHeight !== void 0) s.minHeight = p.minHeight;
|
|
10
|
+
if (p.maxHeight !== void 0) s.maxHeight = p.maxHeight;
|
|
11
|
+
return s;
|
|
12
|
+
}
|
|
13
|
+
|
|
2
14
|
// src/react/elvix-card.tsx
|
|
3
15
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
16
|
function ElvixCard({
|
|
5
17
|
title,
|
|
6
18
|
footer,
|
|
7
19
|
className = "",
|
|
8
|
-
|
|
20
|
+
style,
|
|
21
|
+
children,
|
|
22
|
+
width,
|
|
23
|
+
height,
|
|
24
|
+
minWidth,
|
|
25
|
+
maxWidth,
|
|
26
|
+
minHeight,
|
|
27
|
+
maxHeight
|
|
9
28
|
}) {
|
|
29
|
+
const sized = sizeStyle({ width, height, minWidth, maxWidth, minHeight, maxHeight });
|
|
10
30
|
return /* @__PURE__ */ jsxs(
|
|
11
31
|
"div",
|
|
12
32
|
{
|
|
@@ -19,7 +39,9 @@ function ElvixCard({
|
|
|
19
39
|
display: "flex",
|
|
20
40
|
flexDirection: "column",
|
|
21
41
|
maxWidth: "440px",
|
|
22
|
-
width: "100%"
|
|
42
|
+
width: "100%",
|
|
43
|
+
...style,
|
|
44
|
+
...sized
|
|
23
45
|
},
|
|
24
46
|
children: [
|
|
25
47
|
title && /* @__PURE__ */ jsx(
|
|
@@ -182,6 +204,7 @@ import { useState as useState2 } from "react";
|
|
|
182
204
|
var DEFAULT_COPY = {
|
|
183
205
|
subtitle: "Pick how you want to continue.",
|
|
184
206
|
googleButton: "Continue with Google",
|
|
207
|
+
passkeyButton: "Continue with passkey",
|
|
185
208
|
emailPlaceholder: "you@example.com",
|
|
186
209
|
sendCodeButton: "Send code",
|
|
187
210
|
sendingLabel: "Sending\u2026",
|
|
@@ -250,14 +273,112 @@ function isSameOrigin(baseUrl) {
|
|
|
250
273
|
}
|
|
251
274
|
}
|
|
252
275
|
|
|
276
|
+
// src/react/passkey.ts
|
|
277
|
+
function b64urlToBuf(b64url) {
|
|
278
|
+
const b64 = b64url.replace(/-/g, "+").replace(/_/g, "/");
|
|
279
|
+
const pad = b64.length % 4 === 0 ? "" : "=".repeat(4 - b64.length % 4);
|
|
280
|
+
const bin = atob(b64 + pad);
|
|
281
|
+
const bytes = new Uint8Array(bin.length);
|
|
282
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
283
|
+
return bytes.buffer;
|
|
284
|
+
}
|
|
285
|
+
function bufToB64url(buf) {
|
|
286
|
+
const bytes = new Uint8Array(buf);
|
|
287
|
+
let bin = "";
|
|
288
|
+
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
|
289
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
290
|
+
}
|
|
291
|
+
async function runPasskeySignIn(baseUrl, clientId) {
|
|
292
|
+
if (!clientId) return { ok: false, error: "missing_client_id", message: "ElvixProvider needs a clientId." };
|
|
293
|
+
if (typeof window === "undefined" || !window.PublicKeyCredential || !navigator.credentials?.get) {
|
|
294
|
+
return { ok: false, error: "passkey_unsupported", message: "This browser can't use passkeys." };
|
|
295
|
+
}
|
|
296
|
+
const credentials = isSameOrigin(baseUrl) ? "include" : "omit";
|
|
297
|
+
let options;
|
|
298
|
+
try {
|
|
299
|
+
const res = await fetch(`${baseUrl}/api/auth/passkey/sign-in/start`, {
|
|
300
|
+
method: "POST",
|
|
301
|
+
headers: { "content-type": "application/json" },
|
|
302
|
+
credentials,
|
|
303
|
+
body: JSON.stringify({ intent: "app", clientId })
|
|
304
|
+
});
|
|
305
|
+
const body = await res.json();
|
|
306
|
+
if (!res.ok || !body.success || !body.data?.options) {
|
|
307
|
+
return { ok: false, error: body.errorMessage ?? "passkey_start_failed" };
|
|
308
|
+
}
|
|
309
|
+
options = body.data.options;
|
|
310
|
+
} catch (e) {
|
|
311
|
+
return { ok: false, error: "network", message: e instanceof Error ? e.message : void 0 };
|
|
312
|
+
}
|
|
313
|
+
let assertion;
|
|
314
|
+
try {
|
|
315
|
+
const publicKey = {
|
|
316
|
+
challenge: b64urlToBuf(options.challenge),
|
|
317
|
+
timeout: options.timeout,
|
|
318
|
+
rpId: options.rpId,
|
|
319
|
+
userVerification: options.userVerification,
|
|
320
|
+
allowCredentials: options.allowCredentials?.map((c) => ({
|
|
321
|
+
id: b64urlToBuf(c.id),
|
|
322
|
+
type: c.type,
|
|
323
|
+
transports: c.transports
|
|
324
|
+
}))
|
|
325
|
+
};
|
|
326
|
+
const cred = await navigator.credentials.get({ publicKey });
|
|
327
|
+
if (!cred) return { ok: false, error: "passkey_cancelled" };
|
|
328
|
+
const resp = cred.response;
|
|
329
|
+
assertion = {
|
|
330
|
+
id: cred.id,
|
|
331
|
+
rawId: bufToB64url(cred.rawId),
|
|
332
|
+
type: "public-key",
|
|
333
|
+
clientExtensionResults: cred.getClientExtensionResults(),
|
|
334
|
+
authenticatorAttachment: cred.authenticatorAttachment ?? void 0,
|
|
335
|
+
response: {
|
|
336
|
+
clientDataJSON: bufToB64url(resp.clientDataJSON),
|
|
337
|
+
authenticatorData: bufToB64url(resp.authenticatorData),
|
|
338
|
+
signature: bufToB64url(resp.signature),
|
|
339
|
+
userHandle: resp.userHandle ? bufToB64url(resp.userHandle) : void 0
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
} catch (e) {
|
|
343
|
+
const name = e?.name;
|
|
344
|
+
if (name === "NotAllowedError" || name === "AbortError") {
|
|
345
|
+
return { ok: false, error: "passkey_cancelled" };
|
|
346
|
+
}
|
|
347
|
+
return { ok: false, error: "passkey_failed", message: e instanceof Error ? e.message : void 0 };
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
const res = await fetch(`${baseUrl}/api/auth/passkey/sign-in/finish`, {
|
|
351
|
+
method: "POST",
|
|
352
|
+
headers: { "content-type": "application/json" },
|
|
353
|
+
credentials,
|
|
354
|
+
body: JSON.stringify({ intent: "app", clientId, ...assertion })
|
|
355
|
+
});
|
|
356
|
+
const body = await res.json();
|
|
357
|
+
if (!res.ok || !body.success) {
|
|
358
|
+
return { ok: false, error: body.errorMessage ?? "passkey_verify_failed" };
|
|
359
|
+
}
|
|
360
|
+
if (body.data?.token) setElvixToken(body.data.token);
|
|
361
|
+
return { ok: true, redirect: body.data?.redirect, token: body.data?.token };
|
|
362
|
+
} catch (e) {
|
|
363
|
+
return { ok: false, error: "network", message: e instanceof Error ? e.message : void 0 };
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
253
367
|
// src/react/elvix-sign-in.tsx
|
|
254
368
|
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
255
369
|
function ElvixSignIn({
|
|
256
370
|
onResult,
|
|
257
371
|
redirectAfterSignIn,
|
|
258
372
|
copy: copyProp,
|
|
259
|
-
className = ""
|
|
373
|
+
className = "",
|
|
374
|
+
width,
|
|
375
|
+
height,
|
|
376
|
+
minWidth,
|
|
377
|
+
maxWidth,
|
|
378
|
+
minHeight,
|
|
379
|
+
maxHeight
|
|
260
380
|
}) {
|
|
381
|
+
const sized = sizeStyle({ width, height, minWidth, maxWidth, minHeight, maxHeight });
|
|
261
382
|
const ctx = useElvixContext();
|
|
262
383
|
const app = useElvixApp();
|
|
263
384
|
const copy = resolveCopy(app?.strings, copyProp);
|
|
@@ -281,6 +402,29 @@ function ElvixSignIn({
|
|
|
281
402
|
`${ctx.baseUrl}/api/auth/google/start?intent=app&clientId=${encodeURIComponent(ctx.clientId)}`
|
|
282
403
|
);
|
|
283
404
|
}
|
|
405
|
+
async function startPasskey() {
|
|
406
|
+
setBusy(true);
|
|
407
|
+
setError(null);
|
|
408
|
+
try {
|
|
409
|
+
const result = await runPasskeySignIn(ctx.baseUrl, ctx.clientId);
|
|
410
|
+
if (!result.ok) {
|
|
411
|
+
if (result.error === "passkey_cancelled") {
|
|
412
|
+
onResult?.({ ok: false, error: result.error });
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
return fail(result.error, result.message);
|
|
416
|
+
}
|
|
417
|
+
setStep("done");
|
|
418
|
+
onResult?.({
|
|
419
|
+
ok: true,
|
|
420
|
+
method: "passkey",
|
|
421
|
+
redirect: result.redirect ?? redirectAfterSignIn,
|
|
422
|
+
token: result.token
|
|
423
|
+
});
|
|
424
|
+
} finally {
|
|
425
|
+
setBusy(false);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
284
428
|
async function startOtp(e) {
|
|
285
429
|
e.preventDefault();
|
|
286
430
|
if (!email.trim()) return fail("invalid_input", copy.errorEnterEmail);
|
|
@@ -342,9 +486,9 @@ function ElvixSignIn({
|
|
|
342
486
|
}
|
|
343
487
|
const card = `elvix-card ${className}`.trim();
|
|
344
488
|
if (step === "done") {
|
|
345
|
-
return /* @__PURE__ */ jsx3("div", { className: card, "data-elvix-pane": "done", children: /* @__PURE__ */ jsx3("p", { children: copy.signedInText }) });
|
|
489
|
+
return /* @__PURE__ */ jsx3("div", { className: card, style: sized, "data-elvix-pane": "done", children: /* @__PURE__ */ jsx3("p", { children: copy.signedInText }) });
|
|
346
490
|
}
|
|
347
|
-
return /* @__PURE__ */ jsxs2("div", { className: card, "data-elvix-pane": step, children: [
|
|
491
|
+
return /* @__PURE__ */ jsxs2("div", { className: card, style: sized, "data-elvix-pane": step, children: [
|
|
348
492
|
/* @__PURE__ */ jsx3("h2", { className: "elvix-h", children: title }),
|
|
349
493
|
copy.subtitle && /* @__PURE__ */ jsx3("p", { className: "elvix-muted elvix-subtitle", children: copy.subtitle }),
|
|
350
494
|
step === "identify" && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
@@ -359,6 +503,17 @@ function ElvixSignIn({
|
|
|
359
503
|
children: copy.googleButton
|
|
360
504
|
}
|
|
361
505
|
),
|
|
506
|
+
app?.methodPasskey && /* @__PURE__ */ jsx3(
|
|
507
|
+
"button",
|
|
508
|
+
{
|
|
509
|
+
type: "button",
|
|
510
|
+
onClick: startPasskey,
|
|
511
|
+
disabled: busy,
|
|
512
|
+
className: "elvix-btn elvix-btn-passkey",
|
|
513
|
+
"data-elvix-method": "passkey",
|
|
514
|
+
children: copy.passkeyButton
|
|
515
|
+
}
|
|
516
|
+
),
|
|
362
517
|
app?.methodEmailOtp && /* @__PURE__ */ jsxs2("form", { onSubmit: startOtp, "data-elvix-method": "email_otp", className: "elvix-otp-form", children: [
|
|
363
518
|
/* @__PURE__ */ jsx3(
|
|
364
519
|
"input",
|
|
@@ -394,36 +549,557 @@ function ElvixSignIn({
|
|
|
394
549
|
),
|
|
395
550
|
/* @__PURE__ */ jsx3("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary", children: busy ? copy.verifyingLabel : submitLabel })
|
|
396
551
|
] }),
|
|
397
|
-
error && /* @__PURE__ */ jsx3("p", { role: "alert", className: "elvix-error", children: error })
|
|
552
|
+
error && /* @__PURE__ */ jsx3("p", { role: "alert", className: "elvix-error", children: error })
|
|
553
|
+
] });
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// src/react/elvix-sign-in-form.tsx
|
|
557
|
+
import { useMemo as useMemo2, useState as useState3 } from "react";
|
|
558
|
+
|
|
559
|
+
// src/react/elvix-shield.tsx
|
|
560
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
561
|
+
function ElvixShield({
|
|
562
|
+
size,
|
|
563
|
+
fill,
|
|
564
|
+
accent
|
|
565
|
+
}) {
|
|
566
|
+
return /* @__PURE__ */ jsxs3("svg", { width: size, height: size, viewBox: "2 2 20 20", "aria-hidden": true, style: { display: "block" }, children: [
|
|
567
|
+
/* @__PURE__ */ jsx4(
|
|
568
|
+
"path",
|
|
569
|
+
{
|
|
570
|
+
fill,
|
|
571
|
+
fillRule: "evenodd",
|
|
572
|
+
d: "M 6 2.5 C 4.34 2.5 3 3.84 3 5.5 L 3 12.5 C 3 17.5 7 20.7 12 22 C 17 20.7 21 17.5 21 12.5 L 21 5.5 C 21 3.84 19.66 2.5 18 2.5 L 6 2.5 Z M 12 8.4 C 9.79 8.4 8 10.19 8 12.4 C 8 14.61 9.79 16.4 12 16.4 C 13.21 16.4 14.3 15.86 15.04 15 L 13.6 13.77 C 13.21 14.23 12.64 14.5 12 14.5 C 11.04 14.5 10.21 13.86 9.91 13 L 15.95 13 C 15.98 12.8 16 12.6 16 12.4 C 16 10.19 14.21 8.4 12 8.4 Z M 9.91 11.8 L 14.09 11.8 C 13.79 10.94 12.96 10.3 12 10.3 C 11.04 10.3 10.21 10.94 9.91 11.8 Z"
|
|
573
|
+
}
|
|
574
|
+
),
|
|
575
|
+
/* @__PURE__ */ jsx4("circle", { cx: "19.5", cy: "4.5", r: "2.4", fill: accent })
|
|
576
|
+
] });
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// src/react/elvix-secured-badge.tsx
|
|
580
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
581
|
+
var ELVIX_URL = "https://elvix.is";
|
|
582
|
+
var SIZE = {
|
|
583
|
+
sm: { height: 28, padX: 10, font: 11.5, icon: 14, gap: 6 },
|
|
584
|
+
md: { height: 32, padX: 12, font: 12.5, icon: 16, gap: 7 },
|
|
585
|
+
lg: { height: 36, padX: 14, font: 13, icon: 18, gap: 8 }
|
|
586
|
+
};
|
|
587
|
+
var TONE = {
|
|
588
|
+
white: {
|
|
589
|
+
light: { bg: "#ffffff", border: "#e4e4e7", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" },
|
|
590
|
+
dark: { bg: "#ffffff", border: "transparent", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" }
|
|
591
|
+
},
|
|
592
|
+
dark: {
|
|
593
|
+
light: { bg: "#0a0a0b", border: "rgba(0,0,0,0.1)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" },
|
|
594
|
+
dark: { bg: "#0a0a0b", border: "rgba(255,255,255,0.1)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" }
|
|
595
|
+
},
|
|
596
|
+
outline: {
|
|
597
|
+
light: { bg: "transparent", border: "rgba(0,0,0,0.15)", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" },
|
|
598
|
+
dark: { bg: "transparent", border: "rgba(142,125,255,0.4)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" }
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
function ElvixSecuredBadge({
|
|
602
|
+
variant = "white",
|
|
603
|
+
size = "md",
|
|
604
|
+
theme = "dark",
|
|
605
|
+
accentColor = "#8e7dff",
|
|
606
|
+
href = ELVIX_URL,
|
|
607
|
+
className = "",
|
|
608
|
+
width,
|
|
609
|
+
height,
|
|
610
|
+
minWidth,
|
|
611
|
+
maxWidth,
|
|
612
|
+
minHeight,
|
|
613
|
+
maxHeight
|
|
614
|
+
}) {
|
|
615
|
+
const s = SIZE[size];
|
|
616
|
+
const t = TONE[variant][theme];
|
|
617
|
+
const style = {
|
|
618
|
+
display: "inline-flex",
|
|
619
|
+
alignItems: "center",
|
|
620
|
+
gap: s.gap,
|
|
621
|
+
height: s.height,
|
|
622
|
+
paddingLeft: s.padX,
|
|
623
|
+
paddingRight: s.padX,
|
|
624
|
+
fontSize: s.font,
|
|
625
|
+
fontWeight: 500,
|
|
626
|
+
borderRadius: 9999,
|
|
627
|
+
background: t.bg,
|
|
628
|
+
border: `1px solid ${t.border}`,
|
|
629
|
+
color: t.brand,
|
|
630
|
+
textDecoration: "none",
|
|
631
|
+
userSelect: "none",
|
|
632
|
+
lineHeight: 1,
|
|
633
|
+
// Dimensional overrides win over the size preset above.
|
|
634
|
+
...sizeStyle({ width, height, minWidth, maxWidth, minHeight, maxHeight })
|
|
635
|
+
};
|
|
636
|
+
return /* @__PURE__ */ jsxs4(
|
|
637
|
+
"a",
|
|
638
|
+
{
|
|
639
|
+
href,
|
|
640
|
+
target: "_blank",
|
|
641
|
+
rel: "noopener noreferrer",
|
|
642
|
+
className,
|
|
643
|
+
style,
|
|
644
|
+
"data-elvix-secured-badge": "",
|
|
645
|
+
children: [
|
|
646
|
+
/* @__PURE__ */ jsx5(ElvixShield, { size: s.icon, fill: t.shield, accent: accentColor }),
|
|
647
|
+
/* @__PURE__ */ jsxs4("span", { style: { display: "inline-flex", alignItems: "baseline", gap: 4 }, children: [
|
|
648
|
+
/* @__PURE__ */ jsx5("span", { style: { color: t.lead }, children: "Secured by" }),
|
|
649
|
+
/* @__PURE__ */ jsx5("span", { style: { color: t.brand, fontWeight: 600 }, children: "elvix" })
|
|
650
|
+
] })
|
|
651
|
+
]
|
|
652
|
+
}
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// src/react/elvix-sign-in-form.tsx
|
|
657
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
658
|
+
function PasskeyIcon({ size = 18 }) {
|
|
659
|
+
return /* @__PURE__ */ jsxs5(
|
|
660
|
+
"svg",
|
|
661
|
+
{
|
|
662
|
+
width: size,
|
|
663
|
+
height: size,
|
|
664
|
+
viewBox: "0 0 24 24",
|
|
665
|
+
fill: "none",
|
|
666
|
+
stroke: "currentColor",
|
|
667
|
+
strokeWidth: 1.8,
|
|
668
|
+
strokeLinecap: "round",
|
|
669
|
+
strokeLinejoin: "round",
|
|
670
|
+
"aria-hidden": true,
|
|
671
|
+
style: { display: "block" },
|
|
672
|
+
children: [
|
|
673
|
+
/* @__PURE__ */ jsx6("circle", { cx: "9", cy: "8", r: "4" }),
|
|
674
|
+
/* @__PURE__ */ jsx6("path", { d: "M4 20c0-3 2.5-5 5-5 1 0 1.9.3 2.7.8" }),
|
|
675
|
+
/* @__PURE__ */ jsx6("path", { d: "M17 12.5a2.5 2.5 0 1 0-2.5 2.5v5l1.2-1.2 1.3 1.2v-5a2.5 2.5 0 0 0 0-2.5Z" })
|
|
676
|
+
]
|
|
677
|
+
}
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
function GoogleG({ size = 18 }) {
|
|
681
|
+
return /* @__PURE__ */ jsxs5("svg", { width: size, height: size, viewBox: "0 0 18 18", "aria-hidden": true, style: { display: "block" }, children: [
|
|
682
|
+
/* @__PURE__ */ jsx6(
|
|
683
|
+
"path",
|
|
684
|
+
{
|
|
685
|
+
fill: "#4285F4",
|
|
686
|
+
d: "M17.64 9.2c0-.64-.06-1.25-.16-1.84H9v3.48h4.84a4.14 4.14 0 0 1-1.8 2.72v2.26h2.92c1.7-1.57 2.68-3.88 2.68-6.62Z"
|
|
687
|
+
}
|
|
688
|
+
),
|
|
689
|
+
/* @__PURE__ */ jsx6(
|
|
690
|
+
"path",
|
|
691
|
+
{
|
|
692
|
+
fill: "#34A853",
|
|
693
|
+
d: "M9 18c2.43 0 4.47-.8 5.96-2.18l-2.92-2.26c-.8.54-1.84.86-3.04.86-2.34 0-4.32-1.58-5.03-3.7H.96v2.33A9 9 0 0 0 9 18Z"
|
|
694
|
+
}
|
|
695
|
+
),
|
|
696
|
+
/* @__PURE__ */ jsx6(
|
|
697
|
+
"path",
|
|
698
|
+
{
|
|
699
|
+
fill: "#FBBC05",
|
|
700
|
+
d: "M3.97 10.72A5.4 5.4 0 0 1 3.68 9c0-.6.1-1.18.29-1.72V4.95H.96A9 9 0 0 0 0 9c0 1.45.35 2.83.96 4.05l3.01-2.33Z"
|
|
701
|
+
}
|
|
702
|
+
),
|
|
703
|
+
/* @__PURE__ */ jsx6(
|
|
704
|
+
"path",
|
|
705
|
+
{
|
|
706
|
+
fill: "#EA4335",
|
|
707
|
+
d: "M9 3.58c1.32 0 2.5.45 3.44 1.35l2.58-2.59C13.46.89 11.43 0 9 0A9 9 0 0 0 .96 4.95l3.01 2.33C4.68 5.16 6.66 3.58 9 3.58Z"
|
|
708
|
+
}
|
|
709
|
+
)
|
|
710
|
+
] });
|
|
711
|
+
}
|
|
712
|
+
function ElvixSignInForm({
|
|
713
|
+
onResult,
|
|
714
|
+
redirectAfterSignIn,
|
|
715
|
+
copy: copyProp,
|
|
716
|
+
className = "",
|
|
717
|
+
minWidth,
|
|
718
|
+
maxWidth,
|
|
719
|
+
minHeight,
|
|
720
|
+
maxHeight,
|
|
721
|
+
width,
|
|
722
|
+
height
|
|
723
|
+
}) {
|
|
724
|
+
const ctx = useElvixContext();
|
|
725
|
+
const app = useElvixApp();
|
|
726
|
+
const copy = resolveCopy(app?.strings, copyProp);
|
|
727
|
+
const [step, setStep] = useState3("identify");
|
|
728
|
+
const [email, setEmail] = useState3("");
|
|
729
|
+
const [code, setCode] = useState3("");
|
|
730
|
+
const [challengeId, setChallengeId] = useState3(null);
|
|
731
|
+
const [busy, setBusy] = useState3(false);
|
|
732
|
+
const [error, setError] = useState3(null);
|
|
733
|
+
const brand = app?.brandColor || "#6c5ce7";
|
|
734
|
+
const onBrand = app?.onBrandColor || "#fff";
|
|
735
|
+
const appName = app?.appName ?? "your app";
|
|
736
|
+
const verb = app?.signInVerb === "login" ? "Log in" : "Sign in";
|
|
737
|
+
const defaultTitle = app?.appName ? `${verb} to ${app.appName}` : verb;
|
|
738
|
+
const title = copy.title ? fillCopy(copy.title, { app: app?.appName ?? "" }) : defaultTitle;
|
|
739
|
+
const submitLabel = copy.submitButton ?? verb;
|
|
740
|
+
const showGoogle = Boolean(app?.methodGoogle);
|
|
741
|
+
const showPasskey = Boolean(app?.methodPasskey);
|
|
742
|
+
const showEmail = Boolean(app?.methodEmailOtp);
|
|
743
|
+
const showDivider = (showGoogle || showPasskey) && showEmail;
|
|
744
|
+
const logoSrc = app?.iconUrl || app?.logoUrl || null;
|
|
745
|
+
const privacyUrl = app?.privacyPolicyUrl || null;
|
|
746
|
+
const termsUrl = app?.termsOfServiceUrl || null;
|
|
747
|
+
const hasLegal = Boolean(privacyUrl || termsUrl);
|
|
748
|
+
const cardStyle = useMemo2(
|
|
749
|
+
() => ({
|
|
750
|
+
boxSizing: "border-box",
|
|
751
|
+
// Defaults first; the shared sizeStyle() overrides only the keys the host set,
|
|
752
|
+
// so the form keeps its width "100%" / maxWidth 400 defaults when unsized.
|
|
753
|
+
width: "100%",
|
|
754
|
+
maxWidth: 400,
|
|
755
|
+
...sizeStyle({ width, height, minWidth, maxWidth, minHeight, maxHeight }),
|
|
756
|
+
margin: "0 auto",
|
|
757
|
+
display: "flex",
|
|
758
|
+
flexDirection: "column",
|
|
759
|
+
gap: 18,
|
|
760
|
+
background: "#fff",
|
|
761
|
+
color: "#18181b",
|
|
762
|
+
border: "1px solid rgba(0,0,0,0.08)",
|
|
763
|
+
borderRadius: 16,
|
|
764
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04), 0 12px 32px -12px rgba(0,0,0,0.18)",
|
|
765
|
+
padding: 28,
|
|
766
|
+
fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
|
|
767
|
+
textAlign: "center"
|
|
768
|
+
}),
|
|
769
|
+
[width, maxWidth, minWidth, height, minHeight, maxHeight]
|
|
770
|
+
);
|
|
771
|
+
function fail(error2, message) {
|
|
772
|
+
setError(message ?? error2);
|
|
773
|
+
onResult?.({ ok: false, error: error2, message });
|
|
774
|
+
}
|
|
775
|
+
async function startGoogle() {
|
|
776
|
+
if (!ctx.clientId) return fail("missing_client_id", "ElvixProvider needs a clientId.");
|
|
777
|
+
window.location.assign(
|
|
778
|
+
`${ctx.baseUrl}/api/auth/google/start?intent=app&clientId=${encodeURIComponent(ctx.clientId)}`
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
async function startPasskey() {
|
|
782
|
+
setBusy(true);
|
|
783
|
+
setError(null);
|
|
784
|
+
try {
|
|
785
|
+
const result = await runPasskeySignIn(ctx.baseUrl, ctx.clientId);
|
|
786
|
+
if (!result.ok) {
|
|
787
|
+
if (result.error === "passkey_cancelled") {
|
|
788
|
+
onResult?.({ ok: false, error: result.error });
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
return fail(result.error, result.message);
|
|
792
|
+
}
|
|
793
|
+
setStep("done");
|
|
794
|
+
onResult?.({
|
|
795
|
+
ok: true,
|
|
796
|
+
method: "passkey",
|
|
797
|
+
redirect: result.redirect ?? redirectAfterSignIn,
|
|
798
|
+
token: result.token
|
|
799
|
+
});
|
|
800
|
+
} finally {
|
|
801
|
+
setBusy(false);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
async function startOtp(e) {
|
|
805
|
+
e.preventDefault();
|
|
806
|
+
if (!email.trim()) return fail("invalid_input", copy.errorEnterEmail);
|
|
807
|
+
setBusy(true);
|
|
808
|
+
setError(null);
|
|
809
|
+
try {
|
|
810
|
+
const res = await fetch(`${ctx.baseUrl}/api/auth/otp/start`, {
|
|
811
|
+
method: "POST",
|
|
812
|
+
headers: { "content-type": "application/json" },
|
|
813
|
+
credentials: isSameOrigin(ctx.baseUrl) ? "include" : "omit",
|
|
814
|
+
body: JSON.stringify({
|
|
815
|
+
email: email.trim(),
|
|
816
|
+
intent: "app",
|
|
817
|
+
clientId: ctx.clientId
|
|
818
|
+
})
|
|
819
|
+
});
|
|
820
|
+
const body = await res.json();
|
|
821
|
+
if (!res.ok || !body.success || !body.data?.challengeId) {
|
|
822
|
+
return fail(body.errorMessage ?? "otp_start_failed");
|
|
823
|
+
}
|
|
824
|
+
setChallengeId(body.data.challengeId);
|
|
825
|
+
setStep("code");
|
|
826
|
+
} catch (e2) {
|
|
827
|
+
fail("network", e2 instanceof Error ? e2.message : void 0);
|
|
828
|
+
} finally {
|
|
829
|
+
setBusy(false);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
async function verifyOtp(e) {
|
|
833
|
+
e.preventDefault();
|
|
834
|
+
if (!challengeId) return;
|
|
835
|
+
if (code.trim().length !== 6) return fail("invalid_input", copy.errorEnterCode);
|
|
836
|
+
setBusy(true);
|
|
837
|
+
setError(null);
|
|
838
|
+
try {
|
|
839
|
+
const res = await fetch(`${ctx.baseUrl}/api/auth/otp/verify`, {
|
|
840
|
+
method: "POST",
|
|
841
|
+
headers: { "content-type": "application/json" },
|
|
842
|
+
credentials: isSameOrigin(ctx.baseUrl) ? "include" : "omit",
|
|
843
|
+
body: JSON.stringify({ challengeId, code: code.trim() })
|
|
844
|
+
});
|
|
845
|
+
const body = await res.json();
|
|
846
|
+
if (!res.ok || !body.success) {
|
|
847
|
+
return fail(body.errorMessage ?? "otp_verify_failed");
|
|
848
|
+
}
|
|
849
|
+
if (body.data?.token) setElvixToken(body.data.token);
|
|
850
|
+
setStep("done");
|
|
851
|
+
onResult?.({
|
|
852
|
+
ok: true,
|
|
853
|
+
method: "email_otp",
|
|
854
|
+
redirect: body.data?.redirect ?? redirectAfterSignIn,
|
|
855
|
+
token: body.data?.token
|
|
856
|
+
});
|
|
857
|
+
} catch (e2) {
|
|
858
|
+
fail("network", e2 instanceof Error ? e2.message : void 0);
|
|
859
|
+
} finally {
|
|
860
|
+
setBusy(false);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
const labelStyle = {
|
|
864
|
+
display: "block",
|
|
865
|
+
textAlign: "left",
|
|
866
|
+
fontSize: 12.5,
|
|
867
|
+
fontWeight: 500,
|
|
868
|
+
color: "#52525b",
|
|
869
|
+
marginBottom: 6
|
|
870
|
+
};
|
|
871
|
+
const inputStyle = {
|
|
872
|
+
boxSizing: "border-box",
|
|
873
|
+
width: "100%",
|
|
874
|
+
height: 44,
|
|
875
|
+
padding: "0 14px",
|
|
876
|
+
borderRadius: 10,
|
|
877
|
+
border: "1px solid rgba(0,0,0,0.14)",
|
|
878
|
+
background: "#fff",
|
|
879
|
+
color: "#18181b",
|
|
880
|
+
fontSize: 14,
|
|
881
|
+
outline: "none"
|
|
882
|
+
};
|
|
883
|
+
const primaryBtnStyle = {
|
|
884
|
+
boxSizing: "border-box",
|
|
885
|
+
width: "100%",
|
|
886
|
+
height: 44,
|
|
887
|
+
marginTop: 10,
|
|
888
|
+
border: "1px solid rgba(0,0,0,0.08)",
|
|
889
|
+
borderRadius: 10,
|
|
890
|
+
background: brand,
|
|
891
|
+
color: onBrand,
|
|
892
|
+
fontSize: 14,
|
|
893
|
+
fontWeight: 600,
|
|
894
|
+
cursor: busy ? "not-allowed" : "pointer",
|
|
895
|
+
opacity: busy ? 0.65 : 1,
|
|
896
|
+
backgroundImage: "linear-gradient(to bottom, rgba(255,255,255,0.12), rgba(255,255,255,0) 40%)",
|
|
897
|
+
boxShadow: "0 1px 0 rgba(255,255,255,0.06) inset, 0 2px 3px -1px rgba(0,0,0,0.18), 0 0 0 1px rgba(25,28,33,0.06)"
|
|
898
|
+
};
|
|
899
|
+
const googleBtnStyle = {
|
|
900
|
+
boxSizing: "border-box",
|
|
901
|
+
width: "100%",
|
|
902
|
+
height: 44,
|
|
903
|
+
display: "inline-flex",
|
|
904
|
+
alignItems: "center",
|
|
905
|
+
justifyContent: "center",
|
|
906
|
+
gap: 10,
|
|
907
|
+
border: "1px solid rgba(0,0,0,0.14)",
|
|
908
|
+
borderRadius: 10,
|
|
909
|
+
background: "#fff",
|
|
910
|
+
color: "#18181b",
|
|
911
|
+
fontSize: 14,
|
|
912
|
+
fontWeight: 500,
|
|
913
|
+
cursor: busy ? "not-allowed" : "pointer",
|
|
914
|
+
opacity: busy ? 0.65 : 1
|
|
915
|
+
};
|
|
916
|
+
const tileTint = hexToRgba(brand, 0.12);
|
|
917
|
+
const root = `${className}`.trim() || void 0;
|
|
918
|
+
return /* @__PURE__ */ jsxs5("div", { className: root, style: cardStyle, "data-elvix-pane": step, children: [
|
|
919
|
+
/* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
|
|
920
|
+
/* @__PURE__ */ jsx6(
|
|
921
|
+
"div",
|
|
922
|
+
{
|
|
923
|
+
style: {
|
|
924
|
+
width: 52,
|
|
925
|
+
height: 52,
|
|
926
|
+
borderRadius: 14,
|
|
927
|
+
display: "grid",
|
|
928
|
+
placeItems: "center",
|
|
929
|
+
overflow: "hidden",
|
|
930
|
+
background: logoSrc ? "#fff" : tileTint,
|
|
931
|
+
border: "1px solid rgba(0,0,0,0.08)"
|
|
932
|
+
},
|
|
933
|
+
children: logoSrc ? (
|
|
934
|
+
// biome-ignore lint/a11y/useAltText: alt is set
|
|
935
|
+
/* @__PURE__ */ jsx6(
|
|
936
|
+
"img",
|
|
937
|
+
{
|
|
938
|
+
src: logoSrc,
|
|
939
|
+
alt: appName,
|
|
940
|
+
style: { width: "100%", height: "100%", objectFit: "cover" }
|
|
941
|
+
}
|
|
942
|
+
)
|
|
943
|
+
) : /* @__PURE__ */ jsx6("span", { style: { fontSize: 22, fontWeight: 700, color: brand }, children: appName.charAt(0).toUpperCase() || "?" })
|
|
944
|
+
}
|
|
945
|
+
),
|
|
946
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
947
|
+
/* @__PURE__ */ jsx6("h2", { style: { margin: 0, fontSize: 19, fontWeight: 600, letterSpacing: "-0.01em" }, children: step === "code" ? "Check your inbox" : title }),
|
|
948
|
+
step !== "done" && /* @__PURE__ */ jsx6("p", { style: { margin: "4px 0 0", fontSize: 13, color: "#71717a" }, children: step === "code" ? fillCopy(copy.codeSentSubtitle ?? "", { email }) : copy.subtitle })
|
|
949
|
+
] })
|
|
950
|
+
] }),
|
|
951
|
+
step === "done" && /* @__PURE__ */ jsx6("p", { style: { margin: 0, fontSize: 14, color: "#18181b" }, children: copy.signedInText }),
|
|
952
|
+
step === "identify" && /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: 14 }, children: [
|
|
953
|
+
showGoogle && /* @__PURE__ */ jsxs5(
|
|
954
|
+
"button",
|
|
955
|
+
{
|
|
956
|
+
type: "button",
|
|
957
|
+
onClick: startGoogle,
|
|
958
|
+
disabled: busy,
|
|
959
|
+
style: googleBtnStyle,
|
|
960
|
+
"data-elvix-method": "google",
|
|
961
|
+
children: [
|
|
962
|
+
/* @__PURE__ */ jsx6(GoogleG, {}),
|
|
963
|
+
/* @__PURE__ */ jsx6("span", { children: copy.googleButton })
|
|
964
|
+
]
|
|
965
|
+
}
|
|
966
|
+
),
|
|
967
|
+
showPasskey && /* @__PURE__ */ jsxs5(
|
|
968
|
+
"button",
|
|
969
|
+
{
|
|
970
|
+
type: "button",
|
|
971
|
+
onClick: startPasskey,
|
|
972
|
+
disabled: busy,
|
|
973
|
+
style: googleBtnStyle,
|
|
974
|
+
"data-elvix-method": "passkey",
|
|
975
|
+
children: [
|
|
976
|
+
/* @__PURE__ */ jsx6(PasskeyIcon, {}),
|
|
977
|
+
/* @__PURE__ */ jsx6("span", { children: copy.passkeyButton })
|
|
978
|
+
]
|
|
979
|
+
}
|
|
980
|
+
),
|
|
981
|
+
showDivider && /* @__PURE__ */ jsxs5(
|
|
982
|
+
"div",
|
|
983
|
+
{
|
|
984
|
+
"aria-hidden": true,
|
|
985
|
+
style: { display: "flex", alignItems: "center", gap: 12, color: "#a1a1aa" },
|
|
986
|
+
children: [
|
|
987
|
+
/* @__PURE__ */ jsx6("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.08)" } }),
|
|
988
|
+
/* @__PURE__ */ jsx6("span", { style: { fontSize: 11, fontWeight: 600, letterSpacing: "0.08em" }, children: "OR" }),
|
|
989
|
+
/* @__PURE__ */ jsx6("span", { style: { flex: 1, height: 1, background: "rgba(0,0,0,0.08)" } })
|
|
990
|
+
]
|
|
991
|
+
}
|
|
992
|
+
),
|
|
993
|
+
showEmail && /* @__PURE__ */ jsxs5("form", { onSubmit: startOtp, "data-elvix-method": "email_otp", children: [
|
|
994
|
+
/* @__PURE__ */ jsx6("label", { htmlFor: "elvix-email", style: labelStyle, children: "Email" }),
|
|
995
|
+
/* @__PURE__ */ jsx6(
|
|
996
|
+
"input",
|
|
997
|
+
{
|
|
998
|
+
id: "elvix-email",
|
|
999
|
+
type: "email",
|
|
1000
|
+
value: email,
|
|
1001
|
+
onChange: (ev) => setEmail(ev.target.value),
|
|
1002
|
+
placeholder: copy.emailPlaceholder,
|
|
1003
|
+
required: true,
|
|
1004
|
+
disabled: busy,
|
|
1005
|
+
autoComplete: "email",
|
|
1006
|
+
style: inputStyle
|
|
1007
|
+
}
|
|
1008
|
+
),
|
|
1009
|
+
/* @__PURE__ */ jsx6("button", { type: "submit", disabled: busy, style: primaryBtnStyle, children: busy ? copy.sendingLabel : copy.sendCodeButton })
|
|
1010
|
+
] })
|
|
1011
|
+
] }),
|
|
1012
|
+
step === "code" && /* @__PURE__ */ jsxs5("form", { onSubmit: verifyOtp, children: [
|
|
1013
|
+
/* @__PURE__ */ jsx6("label", { htmlFor: "elvix-code", style: labelStyle, children: "Verification code" }),
|
|
1014
|
+
/* @__PURE__ */ jsx6(
|
|
1015
|
+
"input",
|
|
1016
|
+
{
|
|
1017
|
+
id: "elvix-code",
|
|
1018
|
+
type: "text",
|
|
1019
|
+
inputMode: "numeric",
|
|
1020
|
+
pattern: "[0-9]*",
|
|
1021
|
+
maxLength: 6,
|
|
1022
|
+
value: code,
|
|
1023
|
+
onChange: (ev) => setCode(ev.target.value.replace(/\D/g, "")),
|
|
1024
|
+
placeholder: copy.codePlaceholder,
|
|
1025
|
+
required: true,
|
|
1026
|
+
disabled: busy,
|
|
1027
|
+
autoComplete: "one-time-code",
|
|
1028
|
+
autoFocus: true,
|
|
1029
|
+
style: { ...inputStyle, letterSpacing: "0.3em", textAlign: "center", fontSize: 18 }
|
|
1030
|
+
}
|
|
1031
|
+
),
|
|
1032
|
+
/* @__PURE__ */ jsx6("button", { type: "submit", disabled: busy, style: primaryBtnStyle, children: busy ? copy.verifyingLabel : submitLabel }),
|
|
1033
|
+
/* @__PURE__ */ jsx6(
|
|
1034
|
+
"button",
|
|
1035
|
+
{
|
|
1036
|
+
type: "button",
|
|
1037
|
+
onClick: () => {
|
|
1038
|
+
setStep("identify");
|
|
1039
|
+
setCode("");
|
|
1040
|
+
setError(null);
|
|
1041
|
+
},
|
|
1042
|
+
disabled: busy,
|
|
1043
|
+
style: {
|
|
1044
|
+
marginTop: 12,
|
|
1045
|
+
background: "none",
|
|
1046
|
+
border: "none",
|
|
1047
|
+
color: "#71717a",
|
|
1048
|
+
fontSize: 12.5,
|
|
1049
|
+
cursor: busy ? "not-allowed" : "pointer"
|
|
1050
|
+
},
|
|
1051
|
+
children: "Use a different email"
|
|
1052
|
+
}
|
|
1053
|
+
)
|
|
1054
|
+
] }),
|
|
1055
|
+
error && /* @__PURE__ */ jsx6("p", { role: "alert", style: { margin: 0, fontSize: 12.5, color: "#dc2626" }, children: error }),
|
|
1056
|
+
hasLegal && step !== "done" && /* @__PURE__ */ jsxs5("p", { style: { margin: 0, fontSize: 11.5, lineHeight: 1.5, color: "#a1a1aa" }, children: [
|
|
1057
|
+
"By continuing, you agree to ",
|
|
1058
|
+
appName,
|
|
1059
|
+
"'s",
|
|
1060
|
+
" ",
|
|
1061
|
+
termsUrl && /* @__PURE__ */ jsx6(
|
|
1062
|
+
"a",
|
|
1063
|
+
{
|
|
1064
|
+
href: termsUrl,
|
|
1065
|
+
target: "_blank",
|
|
1066
|
+
rel: "noopener noreferrer",
|
|
1067
|
+
style: { color: "#71717a", textDecoration: "underline" },
|
|
1068
|
+
children: "Terms of Service"
|
|
1069
|
+
}
|
|
1070
|
+
),
|
|
1071
|
+
termsUrl && privacyUrl && " \xB7 ",
|
|
1072
|
+
privacyUrl && /* @__PURE__ */ jsx6(
|
|
1073
|
+
"a",
|
|
1074
|
+
{
|
|
1075
|
+
href: privacyUrl,
|
|
1076
|
+
target: "_blank",
|
|
1077
|
+
rel: "noopener noreferrer",
|
|
1078
|
+
style: { color: "#71717a", textDecoration: "underline" },
|
|
1079
|
+
children: "Privacy Policy"
|
|
1080
|
+
}
|
|
1081
|
+
)
|
|
1082
|
+
] }),
|
|
1083
|
+
/* @__PURE__ */ jsx6("div", { style: { display: "flex", justifyContent: "center", paddingTop: 2 }, children: /* @__PURE__ */ jsx6(ElvixSecuredBadge, { variant: "outline", theme: "light", size: "sm", accentColor: brand }) })
|
|
398
1084
|
] });
|
|
399
1085
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
return /* @__PURE__ */ jsxs3("svg", { width: size, height: size, viewBox: "2 2 20 20", "aria-hidden": true, style: { display: "block" }, children: [
|
|
412
|
-
/* @__PURE__ */ jsx4(
|
|
413
|
-
"path",
|
|
414
|
-
{
|
|
415
|
-
fill,
|
|
416
|
-
fillRule: "evenodd",
|
|
417
|
-
d: "M 6 2.5 C 4.34 2.5 3 3.84 3 5.5 L 3 12.5 C 3 17.5 7 20.7 12 22 C 17 20.7 21 17.5 21 12.5 L 21 5.5 C 21 3.84 19.66 2.5 18 2.5 L 6 2.5 Z M 12 8.4 C 9.79 8.4 8 10.19 8 12.4 C 8 14.61 9.79 16.4 12 16.4 C 13.21 16.4 14.3 15.86 15.04 15 L 13.6 13.77 C 13.21 14.23 12.64 14.5 12 14.5 C 11.04 14.5 10.21 13.86 9.91 13 L 15.95 13 C 15.98 12.8 16 12.6 16 12.4 C 16 10.19 14.21 8.4 12 8.4 Z M 9.91 11.8 L 14.09 11.8 C 13.79 10.94 12.96 10.3 12 10.3 C 11.04 10.3 10.21 10.94 9.91 11.8 Z"
|
|
418
|
-
}
|
|
419
|
-
),
|
|
420
|
-
/* @__PURE__ */ jsx4("circle", { cx: "19.5", cy: "4.5", r: "2.4", fill: accent })
|
|
421
|
-
] });
|
|
1086
|
+
function hexToRgba(hex, alpha) {
|
|
1087
|
+
const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
|
|
1088
|
+
if (!m) return hex;
|
|
1089
|
+
let h = m[1];
|
|
1090
|
+
if (h.length === 3)
|
|
1091
|
+
h = h.split("").map((c) => c + c).join("");
|
|
1092
|
+
const n = Number.parseInt(h, 16);
|
|
1093
|
+
const r = n >> 16 & 255;
|
|
1094
|
+
const g = n >> 8 & 255;
|
|
1095
|
+
const b = n & 255;
|
|
1096
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
422
1097
|
}
|
|
423
1098
|
|
|
424
1099
|
// src/react/elvix-sign-in-button.tsx
|
|
425
|
-
import {
|
|
426
|
-
|
|
1100
|
+
import { useState as useState4 } from "react";
|
|
1101
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1102
|
+
var ELVIX_URL2 = "https://elvix.is";
|
|
427
1103
|
var PRESET_LABEL = {
|
|
428
1104
|
"sign-in-with-elvix": "Sign in with elvix",
|
|
429
1105
|
"continue-with-elvix": "Continue with elvix",
|
|
@@ -462,7 +1138,7 @@ function shieldColor(variant, theme) {
|
|
|
462
1138
|
}
|
|
463
1139
|
function ElvixSignInButton({
|
|
464
1140
|
clientId,
|
|
465
|
-
baseUrl =
|
|
1141
|
+
baseUrl = ELVIX_URL2,
|
|
466
1142
|
returnUrl,
|
|
467
1143
|
type = "standard",
|
|
468
1144
|
variant = "filled",
|
|
@@ -475,9 +1151,16 @@ function ElvixSignInButton({
|
|
|
475
1151
|
href,
|
|
476
1152
|
mode = "redirect",
|
|
477
1153
|
onClick,
|
|
478
|
-
onResult
|
|
1154
|
+
onResult,
|
|
1155
|
+
width,
|
|
1156
|
+
height,
|
|
1157
|
+
minWidth,
|
|
1158
|
+
maxWidth,
|
|
1159
|
+
minHeight,
|
|
1160
|
+
maxHeight
|
|
479
1161
|
}) {
|
|
480
|
-
const
|
|
1162
|
+
const sized = sizeStyle({ width, height, minWidth, maxWidth, minHeight, maxHeight });
|
|
1163
|
+
const [embedOpen, setEmbedOpen] = useState4(false);
|
|
481
1164
|
const isIcon = type === "icon";
|
|
482
1165
|
const resolvedLabel = label ?? PRESET_LABEL[preset];
|
|
483
1166
|
const tone = variantTone(variant, theme);
|
|
@@ -498,18 +1181,20 @@ function ElvixSignInButton({
|
|
|
498
1181
|
border: tone.border,
|
|
499
1182
|
boxShadow: tone.shadow,
|
|
500
1183
|
transition: "background 0.15s, border-color 0.15s",
|
|
501
|
-
...isIcon ? { height: SIZE_ICON[size], width: SIZE_ICON[size] } : { height: std.height, paddingLeft: std.padX, paddingRight: std.padX, gap: std.gap }
|
|
1184
|
+
...isIcon ? { height: SIZE_ICON[size], width: SIZE_ICON[size] } : { height: std.height, paddingLeft: std.padX, paddingRight: std.padX, gap: std.gap },
|
|
1185
|
+
// Dimensional overrides win over the size preset above.
|
|
1186
|
+
...sized
|
|
502
1187
|
};
|
|
503
|
-
const content = /* @__PURE__ */
|
|
504
|
-
/* @__PURE__ */
|
|
505
|
-
isIcon ? null : /* @__PURE__ */
|
|
1188
|
+
const content = /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1189
|
+
/* @__PURE__ */ jsx7(ElvixShield, { size: ICON_SIZE[size], fill: shieldColor(variant, theme), accent: "#8e7dff" }),
|
|
1190
|
+
isIcon ? null : /* @__PURE__ */ jsx7("span", { children: resolvedLabel })
|
|
506
1191
|
] });
|
|
507
1192
|
if (mode === "callback") {
|
|
508
|
-
return /* @__PURE__ */
|
|
1193
|
+
return /* @__PURE__ */ jsx7("button", { type: "button", onClick, className, style, "aria-label": isIcon ? resolvedLabel : void 0, children: content });
|
|
509
1194
|
}
|
|
510
1195
|
if (mode === "embed") {
|
|
511
|
-
return /* @__PURE__ */
|
|
512
|
-
!embedOpen && /* @__PURE__ */
|
|
1196
|
+
return /* @__PURE__ */ jsxs6("div", { "data-elvix-signin-button-embed": "", style: sized, children: [
|
|
1197
|
+
!embedOpen && /* @__PURE__ */ jsx7(
|
|
513
1198
|
"button",
|
|
514
1199
|
{
|
|
515
1200
|
type: "button",
|
|
@@ -520,7 +1205,7 @@ function ElvixSignInButton({
|
|
|
520
1205
|
children: content
|
|
521
1206
|
}
|
|
522
1207
|
),
|
|
523
|
-
embedOpen && /* @__PURE__ */
|
|
1208
|
+
embedOpen && /* @__PURE__ */ jsx7(
|
|
524
1209
|
ElvixSignIn,
|
|
525
1210
|
{
|
|
526
1211
|
onResult: (r) => {
|
|
@@ -537,86 +1222,17 @@ function ElvixSignInButton({
|
|
|
537
1222
|
const sep = base.includes("?") ? "&" : "?";
|
|
538
1223
|
return `${base}${sep}return=${encodeURIComponent(returnUrl)}`;
|
|
539
1224
|
})();
|
|
540
|
-
return /* @__PURE__ */
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// src/react/elvix-secured-badge.tsx
|
|
544
|
-
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
545
|
-
var ELVIX_URL2 = "https://elvix.is";
|
|
546
|
-
var SIZE = {
|
|
547
|
-
sm: { height: 28, padX: 10, font: 11.5, icon: 14, gap: 6 },
|
|
548
|
-
md: { height: 32, padX: 12, font: 12.5, icon: 16, gap: 7 },
|
|
549
|
-
lg: { height: 36, padX: 14, font: 13, icon: 18, gap: 8 }
|
|
550
|
-
};
|
|
551
|
-
var TONE = {
|
|
552
|
-
white: {
|
|
553
|
-
light: { bg: "#ffffff", border: "#e4e4e7", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" },
|
|
554
|
-
dark: { bg: "#ffffff", border: "transparent", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" }
|
|
555
|
-
},
|
|
556
|
-
dark: {
|
|
557
|
-
light: { bg: "#0a0a0b", border: "rgba(0,0,0,0.1)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" },
|
|
558
|
-
dark: { bg: "#0a0a0b", border: "rgba(255,255,255,0.1)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" }
|
|
559
|
-
},
|
|
560
|
-
outline: {
|
|
561
|
-
light: { bg: "transparent", border: "rgba(0,0,0,0.15)", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" },
|
|
562
|
-
dark: { bg: "transparent", border: "rgba(142,125,255,0.4)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" }
|
|
563
|
-
}
|
|
564
|
-
};
|
|
565
|
-
function ElvixSecuredBadge({
|
|
566
|
-
variant = "white",
|
|
567
|
-
size = "md",
|
|
568
|
-
theme = "dark",
|
|
569
|
-
accentColor = "#8e7dff",
|
|
570
|
-
href = ELVIX_URL2,
|
|
571
|
-
className = ""
|
|
572
|
-
}) {
|
|
573
|
-
const s = SIZE[size];
|
|
574
|
-
const t = TONE[variant][theme];
|
|
575
|
-
const style = {
|
|
576
|
-
display: "inline-flex",
|
|
577
|
-
alignItems: "center",
|
|
578
|
-
gap: s.gap,
|
|
579
|
-
height: s.height,
|
|
580
|
-
paddingLeft: s.padX,
|
|
581
|
-
paddingRight: s.padX,
|
|
582
|
-
fontSize: s.font,
|
|
583
|
-
fontWeight: 500,
|
|
584
|
-
borderRadius: 9999,
|
|
585
|
-
background: t.bg,
|
|
586
|
-
border: `1px solid ${t.border}`,
|
|
587
|
-
color: t.brand,
|
|
588
|
-
textDecoration: "none",
|
|
589
|
-
userSelect: "none",
|
|
590
|
-
lineHeight: 1
|
|
591
|
-
};
|
|
592
|
-
return /* @__PURE__ */ jsxs5(
|
|
593
|
-
"a",
|
|
594
|
-
{
|
|
595
|
-
href,
|
|
596
|
-
target: "_blank",
|
|
597
|
-
rel: "noopener noreferrer",
|
|
598
|
-
className,
|
|
599
|
-
style,
|
|
600
|
-
"data-elvix-secured-badge": "",
|
|
601
|
-
children: [
|
|
602
|
-
/* @__PURE__ */ jsx6(ElvixShield, { size: s.icon, fill: t.shield, accent: accentColor }),
|
|
603
|
-
/* @__PURE__ */ jsxs5("span", { style: { display: "inline-flex", alignItems: "baseline", gap: 4 }, children: [
|
|
604
|
-
/* @__PURE__ */ jsx6("span", { style: { color: t.lead }, children: "Secured by" }),
|
|
605
|
-
/* @__PURE__ */ jsx6("span", { style: { color: t.brand, fontWeight: 600 }, children: "elvix" })
|
|
606
|
-
] })
|
|
607
|
-
]
|
|
608
|
-
}
|
|
609
|
-
);
|
|
1225
|
+
return /* @__PURE__ */ jsx7("a", { href: destination, className, style, "aria-label": isIcon ? resolvedLabel : void 0, children: content });
|
|
610
1226
|
}
|
|
611
1227
|
|
|
612
1228
|
// src/react/hooks.ts
|
|
613
|
-
import { useCallback, useEffect as useEffect2, useState as
|
|
1229
|
+
import { useCallback, useEffect as useEffect2, useState as useState5 } from "react";
|
|
614
1230
|
var POLL_MS = 7e3;
|
|
615
1231
|
function useUserList(kind, opts) {
|
|
616
1232
|
const { applicationId, baseUrl = "", pollMs = POLL_MS } = opts;
|
|
617
|
-
const [slugs, setSlugs] =
|
|
618
|
-
const [loading, setLoading] =
|
|
619
|
-
const [error, setError] =
|
|
1233
|
+
const [slugs, setSlugs] = useState5([]);
|
|
1234
|
+
const [loading, setLoading] = useState5(true);
|
|
1235
|
+
const [error, setError] = useState5(null);
|
|
620
1236
|
const refresh = useCallback(async () => {
|
|
621
1237
|
setError(null);
|
|
622
1238
|
try {
|
|
@@ -683,7 +1299,7 @@ function ElvixLifecycleWatcher({
|
|
|
683
1299
|
}
|
|
684
1300
|
|
|
685
1301
|
// src/react/elvix-username.tsx
|
|
686
|
-
import { useState as
|
|
1302
|
+
import { useState as useState6 } from "react";
|
|
687
1303
|
|
|
688
1304
|
// src/react/lib.ts
|
|
689
1305
|
async function appPost(opts, path, body) {
|
|
@@ -741,15 +1357,22 @@ async function appDelete(opts, path) {
|
|
|
741
1357
|
}
|
|
742
1358
|
|
|
743
1359
|
// src/react/elvix-username.tsx
|
|
744
|
-
import { jsx as
|
|
1360
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
745
1361
|
function ElvixUsername({
|
|
746
|
-
onResult
|
|
1362
|
+
onResult,
|
|
1363
|
+
width,
|
|
1364
|
+
height,
|
|
1365
|
+
minWidth,
|
|
1366
|
+
maxWidth,
|
|
1367
|
+
minHeight,
|
|
1368
|
+
maxHeight
|
|
747
1369
|
}) {
|
|
1370
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
748
1371
|
const ctx = useElvixContext();
|
|
749
|
-
const [value, setValue] =
|
|
750
|
-
const [busy, setBusy] =
|
|
751
|
-
const [error, setError] =
|
|
752
|
-
const [done, setDone] =
|
|
1372
|
+
const [value, setValue] = useState6("");
|
|
1373
|
+
const [busy, setBusy] = useState6(false);
|
|
1374
|
+
const [error, setError] = useState6(null);
|
|
1375
|
+
const [done, setDone] = useState6(null);
|
|
753
1376
|
async function submit(e) {
|
|
754
1377
|
e.preventDefault();
|
|
755
1378
|
if (!ctx.app) return;
|
|
@@ -769,17 +1392,17 @@ function ElvixUsername({
|
|
|
769
1392
|
onResult?.(result);
|
|
770
1393
|
}
|
|
771
1394
|
if (done) {
|
|
772
|
-
return /* @__PURE__ */
|
|
1395
|
+
return /* @__PURE__ */ jsx8(ElvixCard, { title: "Username saved", ...sizeProps, children: /* @__PURE__ */ jsxs7("p", { children: [
|
|
773
1396
|
"You are now ",
|
|
774
|
-
/* @__PURE__ */
|
|
1397
|
+
/* @__PURE__ */ jsxs7("strong", { children: [
|
|
775
1398
|
"@",
|
|
776
1399
|
done
|
|
777
1400
|
] }),
|
|
778
1401
|
"."
|
|
779
1402
|
] }) });
|
|
780
1403
|
}
|
|
781
|
-
return /* @__PURE__ */
|
|
782
|
-
/* @__PURE__ */
|
|
1404
|
+
return /* @__PURE__ */ jsx8(ElvixCard, { title: "Choose a username", ...sizeProps, children: /* @__PURE__ */ jsxs7("form", { onSubmit: submit, className: "elvix-form", children: [
|
|
1405
|
+
/* @__PURE__ */ jsx8(
|
|
783
1406
|
"input",
|
|
784
1407
|
{
|
|
785
1408
|
type: "text",
|
|
@@ -792,21 +1415,28 @@ function ElvixUsername({
|
|
|
792
1415
|
className: "elvix-input"
|
|
793
1416
|
}
|
|
794
1417
|
),
|
|
795
|
-
/* @__PURE__ */
|
|
796
|
-
error && /* @__PURE__ */
|
|
1418
|
+
/* @__PURE__ */ jsx8("button", { type: "submit", disabled: busy || value.length < 4, className: "elvix-btn elvix-btn-primary", children: busy ? "Saving\u2026" : "Claim" }),
|
|
1419
|
+
error && /* @__PURE__ */ jsx8("p", { role: "alert", className: "elvix-error", children: error })
|
|
797
1420
|
] }) });
|
|
798
1421
|
}
|
|
799
1422
|
|
|
800
1423
|
// src/react/elvix-avatar.tsx
|
|
801
|
-
import { useState as
|
|
802
|
-
import { jsx as
|
|
1424
|
+
import { useState as useState7 } from "react";
|
|
1425
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
803
1426
|
function ElvixAvatar({
|
|
804
|
-
onResult
|
|
1427
|
+
onResult,
|
|
1428
|
+
width,
|
|
1429
|
+
height,
|
|
1430
|
+
minWidth,
|
|
1431
|
+
maxWidth,
|
|
1432
|
+
minHeight,
|
|
1433
|
+
maxHeight
|
|
805
1434
|
}) {
|
|
1435
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
806
1436
|
const ctx = useElvixContext();
|
|
807
|
-
const [busy, setBusy] =
|
|
808
|
-
const [error, setError] =
|
|
809
|
-
const [preview, setPreview] =
|
|
1437
|
+
const [busy, setBusy] = useState7(false);
|
|
1438
|
+
const [error, setError] = useState7(null);
|
|
1439
|
+
const [preview, setPreview] = useState7(null);
|
|
810
1440
|
async function onFile(e) {
|
|
811
1441
|
const file = e.target.files?.[0];
|
|
812
1442
|
if (!file || !ctx.app) return;
|
|
@@ -829,8 +1459,8 @@ function ElvixAvatar({
|
|
|
829
1459
|
if (!result.ok) setError(result.error);
|
|
830
1460
|
onResult?.(result);
|
|
831
1461
|
}
|
|
832
|
-
return /* @__PURE__ */
|
|
833
|
-
preview && /* @__PURE__ */
|
|
1462
|
+
return /* @__PURE__ */ jsxs8(ElvixCard, { title: "Avatar", ...sizeProps, children: [
|
|
1463
|
+
preview && /* @__PURE__ */ jsx9(
|
|
834
1464
|
"img",
|
|
835
1465
|
{
|
|
836
1466
|
src: preview,
|
|
@@ -838,22 +1468,29 @@ function ElvixAvatar({
|
|
|
838
1468
|
style: { width: 96, height: 96, borderRadius: "50%", objectFit: "cover", marginBottom: 12 }
|
|
839
1469
|
}
|
|
840
1470
|
),
|
|
841
|
-
/* @__PURE__ */
|
|
842
|
-
busy && /* @__PURE__ */
|
|
843
|
-
error && /* @__PURE__ */
|
|
1471
|
+
/* @__PURE__ */ jsx9("input", { type: "file", accept: "image/png,image/jpeg,image/webp", onChange: onFile, disabled: busy }),
|
|
1472
|
+
busy && /* @__PURE__ */ jsx9("p", { children: "Uploading\u2026" }),
|
|
1473
|
+
error && /* @__PURE__ */ jsx9("p", { role: "alert", className: "elvix-error", children: error })
|
|
844
1474
|
] });
|
|
845
1475
|
}
|
|
846
1476
|
|
|
847
1477
|
// src/react/elvix-banner.tsx
|
|
848
|
-
import { useState as
|
|
849
|
-
import { jsx as
|
|
1478
|
+
import { useState as useState8 } from "react";
|
|
1479
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
850
1480
|
function ElvixBanner({
|
|
851
|
-
onResult
|
|
1481
|
+
onResult,
|
|
1482
|
+
width,
|
|
1483
|
+
height,
|
|
1484
|
+
minWidth,
|
|
1485
|
+
maxWidth,
|
|
1486
|
+
minHeight,
|
|
1487
|
+
maxHeight
|
|
852
1488
|
}) {
|
|
1489
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
853
1490
|
const ctx = useElvixContext();
|
|
854
|
-
const [busy, setBusy] =
|
|
855
|
-
const [error, setError] =
|
|
856
|
-
const [preview, setPreview] =
|
|
1491
|
+
const [busy, setBusy] = useState8(false);
|
|
1492
|
+
const [error, setError] = useState8(null);
|
|
1493
|
+
const [preview, setPreview] = useState8(null);
|
|
857
1494
|
async function onFile(e) {
|
|
858
1495
|
const file = e.target.files?.[0];
|
|
859
1496
|
if (!file || !ctx.app) return;
|
|
@@ -876,8 +1513,8 @@ function ElvixBanner({
|
|
|
876
1513
|
if (!result.ok) setError(result.error);
|
|
877
1514
|
onResult?.(result);
|
|
878
1515
|
}
|
|
879
|
-
return /* @__PURE__ */
|
|
880
|
-
preview && /* @__PURE__ */
|
|
1516
|
+
return /* @__PURE__ */ jsxs9(ElvixCard, { title: "Banner", ...sizeProps, children: [
|
|
1517
|
+
preview && /* @__PURE__ */ jsx10(
|
|
881
1518
|
"img",
|
|
882
1519
|
{
|
|
883
1520
|
src: preview,
|
|
@@ -885,26 +1522,33 @@ function ElvixBanner({
|
|
|
885
1522
|
style: { width: "100%", aspectRatio: "16/9", objectFit: "cover", borderRadius: 10, marginBottom: 12 }
|
|
886
1523
|
}
|
|
887
1524
|
),
|
|
888
|
-
/* @__PURE__ */
|
|
889
|
-
busy && /* @__PURE__ */
|
|
890
|
-
error && /* @__PURE__ */
|
|
1525
|
+
/* @__PURE__ */ jsx10("input", { type: "file", accept: "image/png,image/jpeg,image/webp", onChange: onFile, disabled: busy }),
|
|
1526
|
+
busy && /* @__PURE__ */ jsx10("p", { children: "Uploading\u2026" }),
|
|
1527
|
+
error && /* @__PURE__ */ jsx10("p", { role: "alert", className: "elvix-error", children: error })
|
|
891
1528
|
] });
|
|
892
1529
|
}
|
|
893
1530
|
|
|
894
1531
|
// src/react/elvix-identity-form.tsx
|
|
895
|
-
import { useState as
|
|
896
|
-
import { jsx as
|
|
1532
|
+
import { useState as useState9 } from "react";
|
|
1533
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
897
1534
|
function ElvixIdentityForm({
|
|
898
1535
|
initialName = "",
|
|
899
1536
|
initialBio = "",
|
|
900
|
-
onResult
|
|
1537
|
+
onResult,
|
|
1538
|
+
width,
|
|
1539
|
+
height,
|
|
1540
|
+
minWidth,
|
|
1541
|
+
maxWidth,
|
|
1542
|
+
minHeight,
|
|
1543
|
+
maxHeight
|
|
901
1544
|
}) {
|
|
1545
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
902
1546
|
const ctx = useElvixContext();
|
|
903
|
-
const [name, setName] =
|
|
904
|
-
const [bio, setBio] =
|
|
905
|
-
const [busy, setBusy] =
|
|
906
|
-
const [error, setError] =
|
|
907
|
-
const [saved, setSaved] =
|
|
1547
|
+
const [name, setName] = useState9(initialName);
|
|
1548
|
+
const [bio, setBio] = useState9(initialBio);
|
|
1549
|
+
const [busy, setBusy] = useState9(false);
|
|
1550
|
+
const [error, setError] = useState9(null);
|
|
1551
|
+
const [saved, setSaved] = useState9(false);
|
|
908
1552
|
async function submit(e) {
|
|
909
1553
|
e.preventDefault();
|
|
910
1554
|
if (!ctx.app) return;
|
|
@@ -920,35 +1564,42 @@ function ElvixIdentityForm({
|
|
|
920
1564
|
else setSaved(true);
|
|
921
1565
|
onResult?.(result);
|
|
922
1566
|
}
|
|
923
|
-
return /* @__PURE__ */
|
|
924
|
-
/* @__PURE__ */
|
|
1567
|
+
return /* @__PURE__ */ jsx11(ElvixCard, { title: "Identity", ...sizeProps, children: /* @__PURE__ */ jsxs10("form", { onSubmit: submit, className: "elvix-form", children: [
|
|
1568
|
+
/* @__PURE__ */ jsxs10("label", { children: [
|
|
925
1569
|
"Name",
|
|
926
|
-
/* @__PURE__ */
|
|
1570
|
+
/* @__PURE__ */ jsx11("input", { value: name, onChange: (e) => setName(e.target.value), maxLength: 80, disabled: busy, className: "elvix-input" })
|
|
927
1571
|
] }),
|
|
928
|
-
/* @__PURE__ */
|
|
1572
|
+
/* @__PURE__ */ jsxs10("label", { children: [
|
|
929
1573
|
"Bio",
|
|
930
|
-
/* @__PURE__ */
|
|
1574
|
+
/* @__PURE__ */ jsx11("textarea", { value: bio, onChange: (e) => setBio(e.target.value), maxLength: 500, rows: 3, disabled: busy, className: "elvix-input" })
|
|
931
1575
|
] }),
|
|
932
|
-
/* @__PURE__ */
|
|
933
|
-
saved && /* @__PURE__ */
|
|
934
|
-
error && /* @__PURE__ */
|
|
1576
|
+
/* @__PURE__ */ jsx11("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary", children: busy ? "Saving\u2026" : "Save" }),
|
|
1577
|
+
saved && /* @__PURE__ */ jsx11("p", { className: "elvix-muted", children: "Saved." }),
|
|
1578
|
+
error && /* @__PURE__ */ jsx11("p", { role: "alert", className: "elvix-error", children: error })
|
|
935
1579
|
] }) });
|
|
936
1580
|
}
|
|
937
1581
|
|
|
938
1582
|
// src/react/elvix-region.tsx
|
|
939
|
-
import { useState as
|
|
940
|
-
import { jsx as
|
|
1583
|
+
import { useState as useState10 } from "react";
|
|
1584
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
941
1585
|
function ElvixRegion({
|
|
942
1586
|
initialCountry = "",
|
|
943
1587
|
initialTimezone = "",
|
|
944
|
-
onResult
|
|
1588
|
+
onResult,
|
|
1589
|
+
width,
|
|
1590
|
+
height,
|
|
1591
|
+
minWidth,
|
|
1592
|
+
maxWidth,
|
|
1593
|
+
minHeight,
|
|
1594
|
+
maxHeight
|
|
945
1595
|
}) {
|
|
1596
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
946
1597
|
const ctx = useElvixContext();
|
|
947
|
-
const [country, setCountry] =
|
|
948
|
-
const [timezone, setTimezone] =
|
|
949
|
-
const [busy, setBusy] =
|
|
950
|
-
const [error, setError] =
|
|
951
|
-
const [saved, setSaved] =
|
|
1598
|
+
const [country, setCountry] = useState10(initialCountry);
|
|
1599
|
+
const [timezone, setTimezone] = useState10(initialTimezone);
|
|
1600
|
+
const [busy, setBusy] = useState10(false);
|
|
1601
|
+
const [error, setError] = useState10(null);
|
|
1602
|
+
const [saved, setSaved] = useState10(false);
|
|
952
1603
|
async function submit(e) {
|
|
953
1604
|
e.preventDefault();
|
|
954
1605
|
if (!ctx.app) return;
|
|
@@ -964,33 +1615,40 @@ function ElvixRegion({
|
|
|
964
1615
|
else setSaved(true);
|
|
965
1616
|
onResult?.(result);
|
|
966
1617
|
}
|
|
967
|
-
return /* @__PURE__ */
|
|
968
|
-
/* @__PURE__ */
|
|
1618
|
+
return /* @__PURE__ */ jsx12(ElvixCard, { title: "Region", ...sizeProps, children: /* @__PURE__ */ jsxs11("form", { onSubmit: submit, className: "elvix-form", children: [
|
|
1619
|
+
/* @__PURE__ */ jsxs11("label", { children: [
|
|
969
1620
|
"Country (ISO-2)",
|
|
970
|
-
/* @__PURE__ */
|
|
1621
|
+
/* @__PURE__ */ jsx12("input", { value: country, onChange: (e) => setCountry(e.target.value.toUpperCase()), maxLength: 2, pattern: "[A-Z]{2}", disabled: busy, className: "elvix-input" })
|
|
971
1622
|
] }),
|
|
972
|
-
/* @__PURE__ */
|
|
1623
|
+
/* @__PURE__ */ jsxs11("label", { children: [
|
|
973
1624
|
"Timezone",
|
|
974
|
-
/* @__PURE__ */
|
|
1625
|
+
/* @__PURE__ */ jsx12("input", { value: timezone, onChange: (e) => setTimezone(e.target.value), placeholder: "Europe/Berlin", disabled: busy, className: "elvix-input" })
|
|
975
1626
|
] }),
|
|
976
|
-
/* @__PURE__ */
|
|
977
|
-
saved && /* @__PURE__ */
|
|
978
|
-
error && /* @__PURE__ */
|
|
1627
|
+
/* @__PURE__ */ jsx12("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary", children: busy ? "Saving\u2026" : "Save" }),
|
|
1628
|
+
saved && /* @__PURE__ */ jsx12("p", { className: "elvix-muted", children: "Saved." }),
|
|
1629
|
+
error && /* @__PURE__ */ jsx12("p", { role: "alert", className: "elvix-error", children: error })
|
|
979
1630
|
] }) });
|
|
980
1631
|
}
|
|
981
1632
|
|
|
982
1633
|
// src/react/elvix-languages.tsx
|
|
983
|
-
import { useState as
|
|
984
|
-
import { jsx as
|
|
1634
|
+
import { useState as useState11 } from "react";
|
|
1635
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
985
1636
|
function ElvixLanguages({
|
|
986
1637
|
initial = [],
|
|
987
|
-
onResult
|
|
1638
|
+
onResult,
|
|
1639
|
+
width,
|
|
1640
|
+
height,
|
|
1641
|
+
minWidth,
|
|
1642
|
+
maxWidth,
|
|
1643
|
+
minHeight,
|
|
1644
|
+
maxHeight
|
|
988
1645
|
}) {
|
|
1646
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
989
1647
|
const ctx = useElvixContext();
|
|
990
|
-
const [raw, setRaw] =
|
|
991
|
-
const [busy, setBusy] =
|
|
992
|
-
const [error, setError] =
|
|
993
|
-
const [saved, setSaved] =
|
|
1648
|
+
const [raw, setRaw] = useState11(initial.join(", "));
|
|
1649
|
+
const [busy, setBusy] = useState11(false);
|
|
1650
|
+
const [error, setError] = useState11(null);
|
|
1651
|
+
const [saved, setSaved] = useState11(false);
|
|
994
1652
|
async function submit(e) {
|
|
995
1653
|
e.preventDefault();
|
|
996
1654
|
if (!ctx.app) return;
|
|
@@ -1007,27 +1665,34 @@ function ElvixLanguages({
|
|
|
1007
1665
|
else setSaved(true);
|
|
1008
1666
|
onResult?.(result);
|
|
1009
1667
|
}
|
|
1010
|
-
return /* @__PURE__ */
|
|
1011
|
-
/* @__PURE__ */
|
|
1668
|
+
return /* @__PURE__ */ jsx13(ElvixCard, { title: "Languages", ...sizeProps, children: /* @__PURE__ */ jsxs12("form", { onSubmit: submit, className: "elvix-form", children: [
|
|
1669
|
+
/* @__PURE__ */ jsxs12("label", { children: [
|
|
1012
1670
|
"Preferred languages (comma-separated BCP-47 tags)",
|
|
1013
|
-
/* @__PURE__ */
|
|
1671
|
+
/* @__PURE__ */ jsx13("input", { value: raw, onChange: (e) => setRaw(e.target.value), placeholder: "en-GB, de-DE", disabled: busy, className: "elvix-input" })
|
|
1014
1672
|
] }),
|
|
1015
|
-
/* @__PURE__ */
|
|
1016
|
-
saved && /* @__PURE__ */
|
|
1017
|
-
error && /* @__PURE__ */
|
|
1673
|
+
/* @__PURE__ */ jsx13("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary", children: busy ? "Saving\u2026" : "Save" }),
|
|
1674
|
+
saved && /* @__PURE__ */ jsx13("p", { className: "elvix-muted", children: "Saved." }),
|
|
1675
|
+
error && /* @__PURE__ */ jsx13("p", { role: "alert", className: "elvix-error", children: error })
|
|
1018
1676
|
] }) });
|
|
1019
1677
|
}
|
|
1020
1678
|
|
|
1021
1679
|
// src/react/elvix-sessions.tsx
|
|
1022
|
-
import { useEffect as useEffect4, useState as
|
|
1023
|
-
import { jsx as
|
|
1680
|
+
import { useEffect as useEffect4, useState as useState12 } from "react";
|
|
1681
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1024
1682
|
function ElvixSessions({
|
|
1025
|
-
onResult
|
|
1683
|
+
onResult,
|
|
1684
|
+
width,
|
|
1685
|
+
height,
|
|
1686
|
+
minWidth,
|
|
1687
|
+
maxWidth,
|
|
1688
|
+
minHeight,
|
|
1689
|
+
maxHeight
|
|
1026
1690
|
}) {
|
|
1691
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
1027
1692
|
const ctx = useElvixContext();
|
|
1028
|
-
const [rows, setRows] =
|
|
1029
|
-
const [error, setError] =
|
|
1030
|
-
const [busy, setBusy] =
|
|
1693
|
+
const [rows, setRows] = useState12(null);
|
|
1694
|
+
const [error, setError] = useState12(null);
|
|
1695
|
+
const [busy, setBusy] = useState12(false);
|
|
1031
1696
|
useEffect4(() => {
|
|
1032
1697
|
if (!ctx.app) return;
|
|
1033
1698
|
fetch(`${ctx.baseUrl}/api/account/apps/${ctx.app.applicationId}/sessions`, {
|
|
@@ -1048,36 +1713,43 @@ function ElvixSessions({
|
|
|
1048
1713
|
if (result.ok) setRows((prev) => prev?.filter((s) => s.id !== id) ?? null);
|
|
1049
1714
|
onResult?.(result);
|
|
1050
1715
|
}
|
|
1051
|
-
return /* @__PURE__ */
|
|
1052
|
-
error && /* @__PURE__ */
|
|
1053
|
-
!rows && !error && /* @__PURE__ */
|
|
1054
|
-
rows && /* @__PURE__ */
|
|
1055
|
-
/* @__PURE__ */
|
|
1056
|
-
/* @__PURE__ */
|
|
1716
|
+
return /* @__PURE__ */ jsxs13(ElvixCard, { title: "Active sessions", ...sizeProps, children: [
|
|
1717
|
+
error && /* @__PURE__ */ jsx14("p", { role: "alert", className: "elvix-error", children: error }),
|
|
1718
|
+
!rows && !error && /* @__PURE__ */ jsx14("p", { children: "Loading\u2026" }),
|
|
1719
|
+
rows && /* @__PURE__ */ jsx14("ul", { style: { listStyle: "none", padding: 0, margin: 0 }, children: rows.map((s) => /* @__PURE__ */ jsx14("li", { style: { padding: "10px 0", borderBottom: "1px solid rgba(0,0,0,0.06)" }, children: /* @__PURE__ */ jsxs13("div", { style: { display: "flex", justifyContent: "space-between", gap: 12 }, children: [
|
|
1720
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
1721
|
+
/* @__PURE__ */ jsxs13("div", { style: { fontSize: 13, fontWeight: 500 }, children: [
|
|
1057
1722
|
s.device,
|
|
1058
|
-
s.current && /* @__PURE__ */
|
|
1723
|
+
s.current && /* @__PURE__ */ jsx14("span", { style: { marginLeft: 8, color: "var(--elvix-primary-strong)", fontSize: 11 }, children: "\xB7 this device" })
|
|
1059
1724
|
] }),
|
|
1060
|
-
/* @__PURE__ */
|
|
1725
|
+
/* @__PURE__ */ jsxs13("div", { style: { fontSize: 11, color: "rgba(0,0,0,0.55)" }, children: [
|
|
1061
1726
|
s.country ?? "\u2014",
|
|
1062
1727
|
" \xB7 since ",
|
|
1063
1728
|
new Date(s.createdAt).toLocaleDateString()
|
|
1064
1729
|
] })
|
|
1065
1730
|
] }),
|
|
1066
|
-
!s.current && /* @__PURE__ */
|
|
1731
|
+
!s.current && /* @__PURE__ */ jsx14("button", { type: "button", disabled: busy, onClick: () => revoke(s.id), className: "elvix-btn elvix-btn-ghost", children: "Revoke" })
|
|
1067
1732
|
] }) }, s.id)) })
|
|
1068
1733
|
] });
|
|
1069
1734
|
}
|
|
1070
1735
|
|
|
1071
1736
|
// src/react/elvix-export.tsx
|
|
1072
|
-
import { useState as
|
|
1073
|
-
import { jsx as
|
|
1737
|
+
import { useState as useState13 } from "react";
|
|
1738
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1074
1739
|
function ElvixExport({
|
|
1075
|
-
onResult
|
|
1740
|
+
onResult,
|
|
1741
|
+
width,
|
|
1742
|
+
height,
|
|
1743
|
+
minWidth,
|
|
1744
|
+
maxWidth,
|
|
1745
|
+
minHeight,
|
|
1746
|
+
maxHeight
|
|
1076
1747
|
}) {
|
|
1748
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
1077
1749
|
const ctx = useElvixContext();
|
|
1078
|
-
const [busy, setBusy] =
|
|
1079
|
-
const [done, setDone] =
|
|
1080
|
-
const [error, setError] =
|
|
1750
|
+
const [busy, setBusy] = useState13(false);
|
|
1751
|
+
const [done, setDone] = useState13(false);
|
|
1752
|
+
const [error, setError] = useState13(null);
|
|
1081
1753
|
async function start() {
|
|
1082
1754
|
if (!ctx.app) return;
|
|
1083
1755
|
setBusy(true);
|
|
@@ -1092,25 +1764,32 @@ function ElvixExport({
|
|
|
1092
1764
|
else setDone(true);
|
|
1093
1765
|
onResult?.(result);
|
|
1094
1766
|
}
|
|
1095
|
-
return /* @__PURE__ */
|
|
1096
|
-
/* @__PURE__ */
|
|
1097
|
-
done ? /* @__PURE__ */
|
|
1098
|
-
error && /* @__PURE__ */
|
|
1767
|
+
return /* @__PURE__ */ jsxs14(ElvixCard, { title: "Export my data", ...sizeProps, children: [
|
|
1768
|
+
/* @__PURE__ */ jsx15("p", { style: { fontSize: 13, color: "rgba(0,0,0,0.6)" }, children: "Request a zip of every record we hold for you in this app. Delivery by email; single-use download link valid for 24h." }),
|
|
1769
|
+
done ? /* @__PURE__ */ jsx15("p", { className: "elvix-muted", children: "Request queued. Check your email." }) : /* @__PURE__ */ jsx15("button", { type: "button", onClick: start, disabled: busy, className: "elvix-btn elvix-btn-primary", children: busy ? "Queuing\u2026" : "Request export" }),
|
|
1770
|
+
error && /* @__PURE__ */ jsx15("p", { role: "alert", className: "elvix-error", children: error })
|
|
1099
1771
|
] });
|
|
1100
1772
|
}
|
|
1101
1773
|
|
|
1102
1774
|
// src/react/elvix-deactivate.tsx
|
|
1103
|
-
import { useState as
|
|
1104
|
-
import { Fragment as Fragment3, jsx as
|
|
1775
|
+
import { useState as useState14 } from "react";
|
|
1776
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1105
1777
|
function ElvixDeactivate({
|
|
1106
|
-
onResult
|
|
1778
|
+
onResult,
|
|
1779
|
+
width,
|
|
1780
|
+
height,
|
|
1781
|
+
minWidth,
|
|
1782
|
+
maxWidth,
|
|
1783
|
+
minHeight,
|
|
1784
|
+
maxHeight
|
|
1107
1785
|
}) {
|
|
1786
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
1108
1787
|
const ctx = useElvixContext();
|
|
1109
|
-
const [pane, setPane] =
|
|
1110
|
-
const [challengeId, setChallengeId] =
|
|
1111
|
-
const [code, setCode] =
|
|
1112
|
-
const [busy, setBusy] =
|
|
1113
|
-
const [error, setError] =
|
|
1788
|
+
const [pane, setPane] = useState14("warn");
|
|
1789
|
+
const [challengeId, setChallengeId] = useState14(null);
|
|
1790
|
+
const [code, setCode] = useState14("");
|
|
1791
|
+
const [busy, setBusy] = useState14(false);
|
|
1792
|
+
const [error, setError] = useState14(null);
|
|
1114
1793
|
async function startChallenge() {
|
|
1115
1794
|
if (!ctx.app) return;
|
|
1116
1795
|
setBusy(true);
|
|
@@ -1148,16 +1827,16 @@ function ElvixDeactivate({
|
|
|
1148
1827
|
onResult?.(result);
|
|
1149
1828
|
}
|
|
1150
1829
|
if (pane === "done") {
|
|
1151
|
-
return /* @__PURE__ */
|
|
1830
|
+
return /* @__PURE__ */ jsx16(ElvixCard, { title: "Deactivated", ...sizeProps, children: /* @__PURE__ */ jsx16("p", { children: "Your access has been paused. Sign in again to restore it." }) });
|
|
1152
1831
|
}
|
|
1153
|
-
return /* @__PURE__ */
|
|
1154
|
-
pane === "warn" && /* @__PURE__ */
|
|
1155
|
-
/* @__PURE__ */
|
|
1156
|
-
/* @__PURE__ */
|
|
1832
|
+
return /* @__PURE__ */ jsxs15(ElvixCard, { title: "Deactivate account", ...sizeProps, children: [
|
|
1833
|
+
pane === "warn" && /* @__PURE__ */ jsxs15(Fragment3, { children: [
|
|
1834
|
+
/* @__PURE__ */ jsx16("p", { style: { fontSize: 13, color: "rgba(0,0,0,0.6)" }, children: "Pause your membership. You can restore it any time by signing in again. No data is deleted." }),
|
|
1835
|
+
/* @__PURE__ */ jsx16("button", { type: "button", onClick: startChallenge, disabled: busy, className: "elvix-btn elvix-btn-danger", children: busy ? "Sending\u2026" : "Send code" })
|
|
1157
1836
|
] }),
|
|
1158
|
-
pane === "otp" && /* @__PURE__ */
|
|
1159
|
-
/* @__PURE__ */
|
|
1160
|
-
/* @__PURE__ */
|
|
1837
|
+
pane === "otp" && /* @__PURE__ */ jsxs15("form", { onSubmit: confirm, className: "elvix-form", children: [
|
|
1838
|
+
/* @__PURE__ */ jsx16("p", { className: "elvix-muted", children: "We sent a 6-digit code to your email." }),
|
|
1839
|
+
/* @__PURE__ */ jsx16(
|
|
1161
1840
|
"input",
|
|
1162
1841
|
{
|
|
1163
1842
|
type: "text",
|
|
@@ -1171,24 +1850,31 @@ function ElvixDeactivate({
|
|
|
1171
1850
|
className: "elvix-input"
|
|
1172
1851
|
}
|
|
1173
1852
|
),
|
|
1174
|
-
/* @__PURE__ */
|
|
1853
|
+
/* @__PURE__ */ jsx16("button", { type: "submit", disabled: busy || code.length !== 6, className: "elvix-btn elvix-btn-danger", children: busy ? "Deactivating\u2026" : "Confirm" })
|
|
1175
1854
|
] }),
|
|
1176
|
-
error && /* @__PURE__ */
|
|
1855
|
+
error && /* @__PURE__ */ jsx16("p", { role: "alert", className: "elvix-error", children: error })
|
|
1177
1856
|
] });
|
|
1178
1857
|
}
|
|
1179
1858
|
|
|
1180
1859
|
// src/react/elvix-leave.tsx
|
|
1181
|
-
import { useState as
|
|
1182
|
-
import { Fragment as Fragment4, jsx as
|
|
1860
|
+
import { useState as useState15 } from "react";
|
|
1861
|
+
import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1183
1862
|
function ElvixLeave({
|
|
1184
|
-
onResult
|
|
1863
|
+
onResult,
|
|
1864
|
+
width,
|
|
1865
|
+
height,
|
|
1866
|
+
minWidth,
|
|
1867
|
+
maxWidth,
|
|
1868
|
+
minHeight,
|
|
1869
|
+
maxHeight
|
|
1185
1870
|
}) {
|
|
1871
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
1186
1872
|
const ctx = useElvixContext();
|
|
1187
|
-
const [pane, setPane] =
|
|
1188
|
-
const [challengeId, setChallengeId] =
|
|
1189
|
-
const [code, setCode] =
|
|
1190
|
-
const [busy, setBusy] =
|
|
1191
|
-
const [error, setError] =
|
|
1873
|
+
const [pane, setPane] = useState15("warn");
|
|
1874
|
+
const [challengeId, setChallengeId] = useState15(null);
|
|
1875
|
+
const [code, setCode] = useState15("");
|
|
1876
|
+
const [busy, setBusy] = useState15(false);
|
|
1877
|
+
const [error, setError] = useState15(null);
|
|
1192
1878
|
async function startChallenge() {
|
|
1193
1879
|
if (!ctx.app) return;
|
|
1194
1880
|
setBusy(true);
|
|
@@ -1226,16 +1912,16 @@ function ElvixLeave({
|
|
|
1226
1912
|
onResult?.(result);
|
|
1227
1913
|
}
|
|
1228
1914
|
if (pane === "done") {
|
|
1229
|
-
return /* @__PURE__ */
|
|
1915
|
+
return /* @__PURE__ */ jsx17(ElvixCard, { title: "You've left", ...sizeProps, children: /* @__PURE__ */ jsx17("p", { children: "You've left this app. Your data is archived; sign in again to rejoin." }) });
|
|
1230
1916
|
}
|
|
1231
|
-
return /* @__PURE__ */
|
|
1232
|
-
pane === "warn" && /* @__PURE__ */
|
|
1233
|
-
/* @__PURE__ */
|
|
1234
|
-
/* @__PURE__ */
|
|
1917
|
+
return /* @__PURE__ */ jsxs16(ElvixCard, { title: "Leave this app", ...sizeProps, children: [
|
|
1918
|
+
pane === "warn" && /* @__PURE__ */ jsxs16(Fragment4, { children: [
|
|
1919
|
+
/* @__PURE__ */ jsx17("p", { style: { fontSize: 13, color: "rgba(0,0,0,0.6)" }, children: "Remove yourself from this app. Audit trail is preserved; you can sign back in any time to rejoin." }),
|
|
1920
|
+
/* @__PURE__ */ jsx17("button", { type: "button", onClick: startChallenge, disabled: busy, className: "elvix-btn elvix-btn-danger", children: busy ? "Sending\u2026" : "Send code" })
|
|
1235
1921
|
] }),
|
|
1236
|
-
pane === "otp" && /* @__PURE__ */
|
|
1237
|
-
/* @__PURE__ */
|
|
1238
|
-
/* @__PURE__ */
|
|
1922
|
+
pane === "otp" && /* @__PURE__ */ jsxs16("form", { onSubmit: confirm, className: "elvix-form", children: [
|
|
1923
|
+
/* @__PURE__ */ jsx17("p", { className: "elvix-muted", children: "We sent a 6-digit code to your email." }),
|
|
1924
|
+
/* @__PURE__ */ jsx17(
|
|
1239
1925
|
"input",
|
|
1240
1926
|
{
|
|
1241
1927
|
type: "text",
|
|
@@ -1249,24 +1935,31 @@ function ElvixLeave({
|
|
|
1249
1935
|
className: "elvix-input"
|
|
1250
1936
|
}
|
|
1251
1937
|
),
|
|
1252
|
-
/* @__PURE__ */
|
|
1938
|
+
/* @__PURE__ */ jsx17("button", { type: "submit", disabled: busy || code.length !== 6, className: "elvix-btn elvix-btn-danger", children: busy ? "Leaving\u2026" : "Confirm leave" })
|
|
1253
1939
|
] }),
|
|
1254
|
-
error && /* @__PURE__ */
|
|
1940
|
+
error && /* @__PURE__ */ jsx17("p", { role: "alert", className: "elvix-error", children: error })
|
|
1255
1941
|
] });
|
|
1256
1942
|
}
|
|
1257
1943
|
|
|
1258
1944
|
// src/react/elvix-address-book.tsx
|
|
1259
|
-
import { useEffect as useEffect5, useState as
|
|
1260
|
-
import { jsx as
|
|
1945
|
+
import { useEffect as useEffect5, useState as useState16 } from "react";
|
|
1946
|
+
import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1261
1947
|
function ElvixAddressBook({
|
|
1262
|
-
onResult
|
|
1948
|
+
onResult,
|
|
1949
|
+
width,
|
|
1950
|
+
height,
|
|
1951
|
+
minWidth,
|
|
1952
|
+
maxWidth,
|
|
1953
|
+
minHeight,
|
|
1954
|
+
maxHeight
|
|
1263
1955
|
}) {
|
|
1956
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
1264
1957
|
const ctx = useElvixContext();
|
|
1265
|
-
const [rows, setRows] =
|
|
1266
|
-
const [error, setError] =
|
|
1267
|
-
const [busy, setBusy] =
|
|
1268
|
-
const [adding, setAdding] =
|
|
1269
|
-
const [form, setForm] =
|
|
1958
|
+
const [rows, setRows] = useState16(null);
|
|
1959
|
+
const [error, setError] = useState16(null);
|
|
1960
|
+
const [busy, setBusy] = useState16(false);
|
|
1961
|
+
const [adding, setAdding] = useState16(false);
|
|
1962
|
+
const [form, setForm] = useState16({
|
|
1270
1963
|
label: "Home",
|
|
1271
1964
|
line1: "",
|
|
1272
1965
|
postalCode: "",
|
|
@@ -1315,14 +2008,14 @@ function ElvixAddressBook({
|
|
|
1315
2008
|
if (result.ok) setRows((prev) => prev?.filter((a) => a.id !== id) ?? null);
|
|
1316
2009
|
onResult?.(result);
|
|
1317
2010
|
}
|
|
1318
|
-
return /* @__PURE__ */
|
|
1319
|
-
error && /* @__PURE__ */
|
|
1320
|
-
!rows && !error && /* @__PURE__ */
|
|
1321
|
-
rows && rows.length === 0 && /* @__PURE__ */
|
|
1322
|
-
rows?.map((a) => /* @__PURE__ */
|
|
1323
|
-
/* @__PURE__ */
|
|
1324
|
-
/* @__PURE__ */
|
|
1325
|
-
/* @__PURE__ */
|
|
2011
|
+
return /* @__PURE__ */ jsxs17(ElvixCard, { title: "Addresses", ...sizeProps, children: [
|
|
2012
|
+
error && /* @__PURE__ */ jsx18("p", { role: "alert", className: "elvix-error", children: error }),
|
|
2013
|
+
!rows && !error && /* @__PURE__ */ jsx18("p", { children: "Loading\u2026" }),
|
|
2014
|
+
rows && rows.length === 0 && /* @__PURE__ */ jsx18("p", { className: "elvix-muted", children: "No addresses yet." }),
|
|
2015
|
+
rows?.map((a) => /* @__PURE__ */ jsxs17("div", { style: { padding: "8px 0", borderBottom: "1px solid rgba(0,0,0,0.06)", display: "flex", justifyContent: "space-between", gap: 12 }, children: [
|
|
2016
|
+
/* @__PURE__ */ jsxs17("div", { style: { fontSize: 13 }, children: [
|
|
2017
|
+
/* @__PURE__ */ jsx18("div", { style: { fontWeight: 500 }, children: a.label }),
|
|
2018
|
+
/* @__PURE__ */ jsxs17("div", { style: { color: "rgba(0,0,0,0.55)" }, children: [
|
|
1326
2019
|
a.line1,
|
|
1327
2020
|
a.line2 ? `, ${a.line2}` : "",
|
|
1328
2021
|
", ",
|
|
@@ -1333,32 +2026,39 @@ function ElvixAddressBook({
|
|
|
1333
2026
|
a.country
|
|
1334
2027
|
] })
|
|
1335
2028
|
] }),
|
|
1336
|
-
/* @__PURE__ */
|
|
2029
|
+
/* @__PURE__ */ jsx18("button", { type: "button", disabled: busy, onClick: () => remove(a.id), className: "elvix-btn elvix-btn-ghost", children: "Remove" })
|
|
1337
2030
|
] }, a.id)),
|
|
1338
|
-
!adding && /* @__PURE__ */
|
|
1339
|
-
adding && /* @__PURE__ */
|
|
1340
|
-
/* @__PURE__ */
|
|
1341
|
-
/* @__PURE__ */
|
|
1342
|
-
/* @__PURE__ */
|
|
1343
|
-
/* @__PURE__ */
|
|
1344
|
-
/* @__PURE__ */
|
|
1345
|
-
/* @__PURE__ */
|
|
2031
|
+
!adding && /* @__PURE__ */ jsx18("button", { type: "button", onClick: () => setAdding(true), className: "elvix-btn elvix-btn-primary", style: { marginTop: 12 }, children: "Add address" }),
|
|
2032
|
+
adding && /* @__PURE__ */ jsxs17("form", { onSubmit: add, className: "elvix-form", style: { marginTop: 12 }, children: [
|
|
2033
|
+
/* @__PURE__ */ jsx18("input", { value: form.label, onChange: (e) => setForm({ ...form, label: e.target.value }), placeholder: "Label", className: "elvix-input" }),
|
|
2034
|
+
/* @__PURE__ */ jsx18("input", { value: form.line1, onChange: (e) => setForm({ ...form, line1: e.target.value }), placeholder: "Street", required: true, className: "elvix-input" }),
|
|
2035
|
+
/* @__PURE__ */ jsx18("input", { value: form.postalCode, onChange: (e) => setForm({ ...form, postalCode: e.target.value }), placeholder: "Postal code", required: true, className: "elvix-input" }),
|
|
2036
|
+
/* @__PURE__ */ jsx18("input", { value: form.city, onChange: (e) => setForm({ ...form, city: e.target.value }), placeholder: "City", required: true, className: "elvix-input" }),
|
|
2037
|
+
/* @__PURE__ */ jsx18("input", { value: form.country, onChange: (e) => setForm({ ...form, country: e.target.value.toUpperCase() }), placeholder: "Country (ISO-2)", maxLength: 2, required: true, className: "elvix-input" }),
|
|
2038
|
+
/* @__PURE__ */ jsx18("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary", children: busy ? "Saving\u2026" : "Save" })
|
|
1346
2039
|
] })
|
|
1347
2040
|
] });
|
|
1348
2041
|
}
|
|
1349
2042
|
|
|
1350
2043
|
// src/react/elvix-legal-entities.tsx
|
|
1351
|
-
import { useEffect as useEffect6, useState as
|
|
1352
|
-
import { jsx as
|
|
2044
|
+
import { useEffect as useEffect6, useState as useState17 } from "react";
|
|
2045
|
+
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1353
2046
|
function ElvixLegalEntities({
|
|
1354
|
-
onResult
|
|
2047
|
+
onResult,
|
|
2048
|
+
width,
|
|
2049
|
+
height,
|
|
2050
|
+
minWidth,
|
|
2051
|
+
maxWidth,
|
|
2052
|
+
minHeight,
|
|
2053
|
+
maxHeight
|
|
1355
2054
|
}) {
|
|
2055
|
+
const sizeProps = { width, height, minWidth, maxWidth, minHeight, maxHeight };
|
|
1356
2056
|
const ctx = useElvixContext();
|
|
1357
|
-
const [rows, setRows] =
|
|
1358
|
-
const [error, setError] =
|
|
1359
|
-
const [busy, setBusy] =
|
|
1360
|
-
const [adding, setAdding] =
|
|
1361
|
-
const [form, setForm] =
|
|
2057
|
+
const [rows, setRows] = useState17(null);
|
|
2058
|
+
const [error, setError] = useState17(null);
|
|
2059
|
+
const [busy, setBusy] = useState17(false);
|
|
2060
|
+
const [adding, setAdding] = useState17(false);
|
|
2061
|
+
const [form, setForm] = useState17({
|
|
1362
2062
|
legalName: "",
|
|
1363
2063
|
taxId: "",
|
|
1364
2064
|
country: ""
|
|
@@ -1405,27 +2105,27 @@ function ElvixLegalEntities({
|
|
|
1405
2105
|
if (result.ok) setRows((prev) => prev?.filter((a) => a.id !== id) ?? null);
|
|
1406
2106
|
onResult?.(result);
|
|
1407
2107
|
}
|
|
1408
|
-
return /* @__PURE__ */
|
|
1409
|
-
error && /* @__PURE__ */
|
|
1410
|
-
!rows && !error && /* @__PURE__ */
|
|
1411
|
-
rows && rows.length === 0 && /* @__PURE__ */
|
|
1412
|
-
rows?.map((e) => /* @__PURE__ */
|
|
1413
|
-
/* @__PURE__ */
|
|
1414
|
-
/* @__PURE__ */
|
|
1415
|
-
/* @__PURE__ */
|
|
2108
|
+
return /* @__PURE__ */ jsxs18(ElvixCard, { title: "Legal entities", ...sizeProps, children: [
|
|
2109
|
+
error && /* @__PURE__ */ jsx19("p", { role: "alert", className: "elvix-error", children: error }),
|
|
2110
|
+
!rows && !error && /* @__PURE__ */ jsx19("p", { children: "Loading\u2026" }),
|
|
2111
|
+
rows && rows.length === 0 && /* @__PURE__ */ jsx19("p", { className: "elvix-muted", children: "No legal entities yet." }),
|
|
2112
|
+
rows?.map((e) => /* @__PURE__ */ jsxs18("div", { style: { padding: "8px 0", borderBottom: "1px solid rgba(0,0,0,0.06)", display: "flex", justifyContent: "space-between", gap: 12 }, children: [
|
|
2113
|
+
/* @__PURE__ */ jsxs18("div", { style: { fontSize: 13 }, children: [
|
|
2114
|
+
/* @__PURE__ */ jsx19("div", { style: { fontWeight: 500 }, children: e.legalName }),
|
|
2115
|
+
/* @__PURE__ */ jsxs18("div", { style: { color: "rgba(0,0,0,0.55)" }, children: [
|
|
1416
2116
|
e.taxId,
|
|
1417
2117
|
" \xB7 ",
|
|
1418
2118
|
e.country
|
|
1419
2119
|
] })
|
|
1420
2120
|
] }),
|
|
1421
|
-
/* @__PURE__ */
|
|
2121
|
+
/* @__PURE__ */ jsx19("button", { type: "button", disabled: busy, onClick: () => remove(e.id), className: "elvix-btn elvix-btn-ghost", children: "Remove" })
|
|
1422
2122
|
] }, e.id)),
|
|
1423
|
-
!adding && /* @__PURE__ */
|
|
1424
|
-
adding && /* @__PURE__ */
|
|
1425
|
-
/* @__PURE__ */
|
|
1426
|
-
/* @__PURE__ */
|
|
1427
|
-
/* @__PURE__ */
|
|
1428
|
-
/* @__PURE__ */
|
|
2123
|
+
!adding && /* @__PURE__ */ jsx19("button", { type: "button", onClick: () => setAdding(true), className: "elvix-btn elvix-btn-primary", style: { marginTop: 12 }, children: "Add entity" }),
|
|
2124
|
+
adding && /* @__PURE__ */ jsxs18("form", { onSubmit: add, className: "elvix-form", style: { marginTop: 12 }, children: [
|
|
2125
|
+
/* @__PURE__ */ jsx19("input", { value: form.legalName, onChange: (e) => setForm({ ...form, legalName: e.target.value }), placeholder: "Legal name", required: true, className: "elvix-input" }),
|
|
2126
|
+
/* @__PURE__ */ jsx19("input", { value: form.taxId, onChange: (e) => setForm({ ...form, taxId: e.target.value }), placeholder: "Tax / VAT ID", required: true, className: "elvix-input" }),
|
|
2127
|
+
/* @__PURE__ */ jsx19("input", { value: form.country, onChange: (e) => setForm({ ...form, country: e.target.value.toUpperCase() }), placeholder: "Country (ISO-2)", maxLength: 2, required: true, className: "elvix-input" }),
|
|
2128
|
+
/* @__PURE__ */ jsx19("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary", children: busy ? "Saving\u2026" : "Save" })
|
|
1429
2129
|
] })
|
|
1430
2130
|
] });
|
|
1431
2131
|
}
|
|
@@ -1448,6 +2148,7 @@ export {
|
|
|
1448
2148
|
ElvixSessions,
|
|
1449
2149
|
ElvixSignIn,
|
|
1450
2150
|
ElvixSignInButton,
|
|
2151
|
+
ElvixSignInForm,
|
|
1451
2152
|
ElvixUsername,
|
|
1452
2153
|
getElvixToken,
|
|
1453
2154
|
setElvixToken,
|