@poly-x/react 0.1.0-alpha.2 → 0.1.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +221 -47
- package/dist/index.d.cts +38 -2
- package/dist/index.d.mts +38 -2
- package/dist/index.mjs +222 -48
- package/dist/styles.css +379 -0
- package/package.json +8 -5
package/dist/index.cjs
CHANGED
|
@@ -353,35 +353,63 @@ function Protected({ children, fallback = null, loading = null }) {
|
|
|
353
353
|
//#endregion
|
|
354
354
|
//#region src/components/sign-in.tsx
|
|
355
355
|
const DEFAULT_LABELS = {
|
|
356
|
+
heading: "Sign in",
|
|
357
|
+
subheading: "Enter your credentials to continue.",
|
|
358
|
+
tagline: "",
|
|
356
359
|
emailLabel: "Email",
|
|
360
|
+
emailPlaceholder: "you@company.com",
|
|
357
361
|
passwordLabel: "Password",
|
|
362
|
+
passwordPlaceholder: "••••••••",
|
|
358
363
|
submit: "Sign in",
|
|
359
364
|
submitting: "Signing in…",
|
|
360
365
|
chooseWorkspace: "Choose a workspace",
|
|
366
|
+
chooseWorkspaceSubheading: "You have access to more than one workspace.",
|
|
367
|
+
securedBy: "Secured by PolyX",
|
|
368
|
+
setPasswordHeading: "Choose a new password",
|
|
369
|
+
setPasswordSubheading: "Your temporary password must be replaced before you continue.",
|
|
370
|
+
newPasswordLabel: "New password",
|
|
371
|
+
confirmPasswordLabel: "Confirm new password",
|
|
372
|
+
setPasswordSubmit: "Set password and sign in",
|
|
373
|
+
passwordMismatch: "Those passwords don't match.",
|
|
374
|
+
weakPassword: "Password must be at least 6 characters.",
|
|
361
375
|
invalidCredentials: "Invalid email or password.",
|
|
362
376
|
noAccess: "You don't have access to this application.",
|
|
363
377
|
passwordChangeRequired: "You must reset your password before you can sign in.",
|
|
364
378
|
genericError: "Something went wrong. Please try again."
|
|
365
379
|
};
|
|
380
|
+
/** Mirrors poly-auth's own floor (Joi: min 6). */
|
|
381
|
+
const MIN_PASSWORD_LENGTH = 6;
|
|
366
382
|
/**
|
|
367
383
|
* The embedded, Clerk-style sign-in form (D2b). Renders email/password inside the
|
|
368
384
|
* consuming app and POSTs to the SDK's BFF (`@poly-x/next` `authenticate`), which
|
|
369
385
|
* runs the credential authorize server-side and seals an httpOnly session — no
|
|
370
386
|
* redirect to a hosted PolyX login page, no tokens or credentials in the browser
|
|
371
387
|
* after submit. Handles the workspace picker (select_tenant) inline.
|
|
388
|
+
*
|
|
389
|
+
* Ships styled: `import "@poly-x/react/styles.css"` once, then theme it by
|
|
390
|
+
* overriding the `--polyx-*` custom properties on any ancestor.
|
|
372
391
|
*/
|
|
373
392
|
function SignIn(props) {
|
|
374
393
|
const action = props.action ?? "/api/polyx/authenticate";
|
|
394
|
+
const variant = props.variant ?? "card";
|
|
375
395
|
const labels = {
|
|
376
396
|
...DEFAULT_LABELS,
|
|
377
397
|
...props.labels
|
|
378
398
|
};
|
|
379
399
|
const [email, setEmail] = (0, react.useState)("");
|
|
380
400
|
const [password, setPassword] = (0, react.useState)("");
|
|
401
|
+
const [newPassword, setNewPassword] = (0, react.useState)("");
|
|
402
|
+
const [confirmPassword, setConfirmPassword] = (0, react.useState)("");
|
|
403
|
+
const [mustSetPassword, setMustSetPassword] = (0, react.useState)(false);
|
|
381
404
|
const [tenants, setTenants] = (0, react.useState)(null);
|
|
382
405
|
const [submitting, setSubmitting] = (0, react.useState)(false);
|
|
383
406
|
const [error, setError] = (0, react.useState)(null);
|
|
384
|
-
|
|
407
|
+
/**
|
|
408
|
+
* One round-trip to the BFF. `tenantId` picks a workspace; `passwords` completes
|
|
409
|
+
* the temp-password flow (the BFF re-verifies the temporary password, sets the
|
|
410
|
+
* new one, and signs the user straight in).
|
|
411
|
+
*/
|
|
412
|
+
async function authenticate(options = {}) {
|
|
385
413
|
setSubmitting(true);
|
|
386
414
|
setError(null);
|
|
387
415
|
try {
|
|
@@ -392,7 +420,8 @@ function SignIn(props) {
|
|
|
392
420
|
email,
|
|
393
421
|
password,
|
|
394
422
|
organizationCode: props.organizationCode,
|
|
395
|
-
tenantId
|
|
423
|
+
tenantId: options.tenantId,
|
|
424
|
+
...options.passwords
|
|
396
425
|
})
|
|
397
426
|
})).json().catch(() => ({}));
|
|
398
427
|
switch (body.status) {
|
|
@@ -402,16 +431,22 @@ function SignIn(props) {
|
|
|
402
431
|
case "select_tenant":
|
|
403
432
|
setTenants(body.tenants ?? []);
|
|
404
433
|
return;
|
|
434
|
+
case "password_change_required":
|
|
435
|
+
setMustSetPassword(true);
|
|
436
|
+
if (body.userId) props.onPasswordChangeRequired?.(body.userId);
|
|
437
|
+
return;
|
|
438
|
+
case "password_mismatch":
|
|
439
|
+
setError(labels.passwordMismatch);
|
|
440
|
+
return;
|
|
441
|
+
case "weak_password":
|
|
442
|
+
setError(labels.weakPassword);
|
|
443
|
+
return;
|
|
405
444
|
case "invalid_credentials":
|
|
406
445
|
setError(labels.invalidCredentials);
|
|
407
446
|
return;
|
|
408
447
|
case "no_access":
|
|
409
448
|
setError(labels.noAccess);
|
|
410
449
|
return;
|
|
411
|
-
case "password_change_required":
|
|
412
|
-
setError(labels.passwordChangeRequired);
|
|
413
|
-
if (body.userId) props.onPasswordChangeRequired?.(body.userId);
|
|
414
|
-
return;
|
|
415
450
|
default:
|
|
416
451
|
setError(labels.genericError);
|
|
417
452
|
return;
|
|
@@ -426,73 +461,212 @@ function SignIn(props) {
|
|
|
426
461
|
event.preventDefault();
|
|
427
462
|
authenticate();
|
|
428
463
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
464
|
+
function onSetPasswordSubmit(event) {
|
|
465
|
+
event.preventDefault();
|
|
466
|
+
if (newPassword !== confirmPassword) {
|
|
467
|
+
setError(labels.passwordMismatch);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
authenticate({ passwords: {
|
|
471
|
+
newPassword,
|
|
472
|
+
confirmPassword
|
|
473
|
+
} });
|
|
474
|
+
}
|
|
475
|
+
const picking = tenants !== null;
|
|
476
|
+
const hasPanel = variant === "page" && Boolean(props.panel || props.logo || labels.tagline);
|
|
477
|
+
const showHeaderLogo = Boolean(props.logo) && !hasPanel;
|
|
478
|
+
const rootClass = [
|
|
479
|
+
"polyx-signin",
|
|
480
|
+
`polyx-signin--${variant}`,
|
|
481
|
+
hasPanel && "polyx-signin--split",
|
|
482
|
+
props.className
|
|
483
|
+
].filter(Boolean).join(" ");
|
|
484
|
+
const step = picking ? "select_tenant" : mustSetPassword ? "set_password" : "credentials";
|
|
485
|
+
const HEADINGS = {
|
|
486
|
+
credentials: {
|
|
487
|
+
heading: labels.heading,
|
|
488
|
+
subheading: labels.subheading
|
|
489
|
+
},
|
|
490
|
+
select_tenant: {
|
|
491
|
+
heading: labels.chooseWorkspace,
|
|
492
|
+
subheading: labels.chooseWorkspaceSubheading
|
|
493
|
+
},
|
|
494
|
+
set_password: {
|
|
495
|
+
heading: labels.setPasswordHeading,
|
|
496
|
+
subheading: labels.setPasswordSubheading
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
const header = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
500
|
+
className: "polyx-signin__header",
|
|
432
501
|
children: [
|
|
433
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
502
|
+
showHeaderLogo ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
503
|
+
className: "polyx-signin__logo",
|
|
504
|
+
children: props.logo
|
|
505
|
+
}) : null,
|
|
506
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
|
|
434
507
|
className: "polyx-signin__heading",
|
|
435
|
-
children:
|
|
508
|
+
children: HEADINGS[step].heading
|
|
436
509
|
}),
|
|
437
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
438
|
-
className: "polyx-
|
|
439
|
-
children:
|
|
440
|
-
|
|
441
|
-
className: "polyx-signin__tenant",
|
|
442
|
-
disabled: submitting,
|
|
443
|
-
onClick: () => void authenticate(tenant.tenantId),
|
|
444
|
-
children: tenant.name ?? tenant.slug ?? tenant.organizationCode ?? tenant.tenantId
|
|
445
|
-
}) }, tenant.tenantId))
|
|
446
|
-
}),
|
|
447
|
-
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
448
|
-
role: "alert",
|
|
449
|
-
className: "polyx-signin__error",
|
|
450
|
-
children: error
|
|
451
|
-
}) : null
|
|
510
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
511
|
+
className: "polyx-signin__subheading",
|
|
512
|
+
children: HEADINGS[step].subheading
|
|
513
|
+
})
|
|
452
514
|
]
|
|
453
515
|
});
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
"
|
|
457
|
-
|
|
516
|
+
const errorNode = error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
517
|
+
role: "alert",
|
|
518
|
+
className: "polyx-signin__error",
|
|
519
|
+
children: error
|
|
520
|
+
}) : null;
|
|
521
|
+
const footer = props.showSecuredBy === false ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
522
|
+
className: "polyx-signin__footer",
|
|
523
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
524
|
+
className: "polyx-signin__shield",
|
|
525
|
+
viewBox: "0 0 16 16",
|
|
526
|
+
fill: "currentColor",
|
|
527
|
+
"aria-hidden": "true",
|
|
528
|
+
focusable: "false",
|
|
529
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 0.5 2 3v4.2c0 3.5 2.5 6.7 6 7.8 3.5-1.1 6-4.3 6-7.8V3L8 .5Zm2.9 5.6-3.4 3.4a.7.7 0 0 1-1 0L5.1 8.1a.7.7 0 1 1 1-1l.9.9 2.9-2.9a.7.7 0 0 1 1 1Z" })
|
|
530
|
+
}), labels.securedBy]
|
|
531
|
+
});
|
|
532
|
+
const tenantPicker = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
533
|
+
className: "polyx-signin__form",
|
|
534
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
535
|
+
className: "polyx-signin__tenants",
|
|
536
|
+
children: (tenants ?? []).map((tenant) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
537
|
+
type: "button",
|
|
538
|
+
className: "polyx-signin__tenant",
|
|
539
|
+
disabled: submitting,
|
|
540
|
+
onClick: () => void authenticate({ tenantId: tenant.tenantId }),
|
|
541
|
+
children: tenant.name ?? tenant.slug ?? tenant.organizationCode ?? tenant.tenantId
|
|
542
|
+
}) }, tenant.tenantId))
|
|
543
|
+
}), errorNode]
|
|
544
|
+
});
|
|
545
|
+
const setPasswordForm = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
546
|
+
className: "polyx-signin__form",
|
|
547
|
+
onSubmit: onSetPasswordSubmit,
|
|
458
548
|
children: [
|
|
459
549
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
460
550
|
className: "polyx-signin__field",
|
|
461
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
551
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
552
|
+
className: "polyx-signin__label",
|
|
553
|
+
children: labels.newPasswordLabel
|
|
554
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
555
|
+
className: "polyx-signin__input",
|
|
556
|
+
type: "password",
|
|
557
|
+
name: "newPassword",
|
|
558
|
+
autoComplete: "new-password",
|
|
559
|
+
placeholder: labels.passwordPlaceholder,
|
|
465
560
|
required: true,
|
|
466
|
-
|
|
561
|
+
minLength: MIN_PASSWORD_LENGTH,
|
|
562
|
+
value: newPassword,
|
|
467
563
|
disabled: submitting,
|
|
468
|
-
onChange: (event) =>
|
|
564
|
+
onChange: (event) => setNewPassword(event.target.value)
|
|
469
565
|
})]
|
|
470
566
|
}),
|
|
471
567
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
472
568
|
className: "polyx-signin__field",
|
|
473
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
569
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
570
|
+
className: "polyx-signin__label",
|
|
571
|
+
children: labels.confirmPasswordLabel
|
|
572
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
573
|
+
className: "polyx-signin__input",
|
|
474
574
|
type: "password",
|
|
475
|
-
name: "
|
|
476
|
-
autoComplete: "
|
|
575
|
+
name: "confirmPassword",
|
|
576
|
+
autoComplete: "new-password",
|
|
577
|
+
placeholder: labels.passwordPlaceholder,
|
|
477
578
|
required: true,
|
|
478
|
-
|
|
579
|
+
minLength: MIN_PASSWORD_LENGTH,
|
|
580
|
+
value: confirmPassword,
|
|
479
581
|
disabled: submitting,
|
|
480
|
-
onChange: (event) =>
|
|
582
|
+
onChange: (event) => setConfirmPassword(event.target.value)
|
|
481
583
|
})]
|
|
482
584
|
}),
|
|
483
|
-
|
|
484
|
-
role: "alert",
|
|
485
|
-
className: "polyx-signin__error",
|
|
486
|
-
children: error
|
|
487
|
-
}) : null,
|
|
585
|
+
errorNode,
|
|
488
586
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
489
587
|
type: "submit",
|
|
490
588
|
className: "polyx-signin__submit",
|
|
491
589
|
disabled: submitting,
|
|
492
|
-
children: submitting ? labels.submitting : labels.
|
|
590
|
+
children: submitting ? labels.submitting : labels.setPasswordSubmit
|
|
493
591
|
})
|
|
494
592
|
]
|
|
495
593
|
});
|
|
594
|
+
const card = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
595
|
+
className: "polyx-signin__card",
|
|
596
|
+
children: [
|
|
597
|
+
header,
|
|
598
|
+
step === "select_tenant" ? tenantPicker : step === "set_password" ? setPasswordForm : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
599
|
+
className: "polyx-signin__form",
|
|
600
|
+
onSubmit,
|
|
601
|
+
children: [
|
|
602
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
603
|
+
className: "polyx-signin__field",
|
|
604
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
605
|
+
className: "polyx-signin__label",
|
|
606
|
+
children: labels.emailLabel
|
|
607
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
608
|
+
className: "polyx-signin__input",
|
|
609
|
+
type: "email",
|
|
610
|
+
name: "email",
|
|
611
|
+
autoComplete: "username",
|
|
612
|
+
placeholder: labels.emailPlaceholder,
|
|
613
|
+
required: true,
|
|
614
|
+
value: email,
|
|
615
|
+
disabled: submitting,
|
|
616
|
+
onChange: (event) => setEmail(event.target.value)
|
|
617
|
+
})]
|
|
618
|
+
}),
|
|
619
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
620
|
+
className: "polyx-signin__field",
|
|
621
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
622
|
+
className: "polyx-signin__label",
|
|
623
|
+
children: labels.passwordLabel
|
|
624
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
625
|
+
className: "polyx-signin__input",
|
|
626
|
+
type: "password",
|
|
627
|
+
name: "password",
|
|
628
|
+
autoComplete: "current-password",
|
|
629
|
+
placeholder: labels.passwordPlaceholder,
|
|
630
|
+
required: true,
|
|
631
|
+
value: password,
|
|
632
|
+
disabled: submitting,
|
|
633
|
+
onChange: (event) => setPassword(event.target.value)
|
|
634
|
+
})]
|
|
635
|
+
}),
|
|
636
|
+
errorNode,
|
|
637
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
638
|
+
type: "submit",
|
|
639
|
+
className: "polyx-signin__submit",
|
|
640
|
+
disabled: submitting,
|
|
641
|
+
children: submitting ? labels.submitting : labels.submit
|
|
642
|
+
})
|
|
643
|
+
]
|
|
644
|
+
}),
|
|
645
|
+
footer
|
|
646
|
+
]
|
|
647
|
+
});
|
|
648
|
+
if (variant === "page") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
649
|
+
className: rootClass,
|
|
650
|
+
"data-polyx-signin": step,
|
|
651
|
+
children: [hasPanel ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
|
|
652
|
+
className: "polyx-signin__panel",
|
|
653
|
+
children: props.panel ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [props.logo ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
654
|
+
className: "polyx-signin__panel-logo",
|
|
655
|
+
children: props.logo
|
|
656
|
+
}) : null, labels.tagline ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
657
|
+
className: "polyx-signin__tagline",
|
|
658
|
+
children: labels.tagline
|
|
659
|
+
}) : null] })
|
|
660
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
661
|
+
className: "polyx-signin__main",
|
|
662
|
+
children: card
|
|
663
|
+
})]
|
|
664
|
+
});
|
|
665
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
666
|
+
className: rootClass,
|
|
667
|
+
"data-polyx-signin": step,
|
|
668
|
+
children: card
|
|
669
|
+
});
|
|
496
670
|
}
|
|
497
671
|
//#endregion
|
|
498
672
|
//#region src/components/auth-callback.tsx
|
package/dist/index.d.cts
CHANGED
|
@@ -193,24 +193,57 @@ declare function Protected({
|
|
|
193
193
|
//#region src/components/sign-in.d.ts
|
|
194
194
|
/** Every user-facing string, overridable for i18n / white-labelling. */
|
|
195
195
|
interface SignInLabels {
|
|
196
|
+
heading: string;
|
|
197
|
+
subheading: string;
|
|
198
|
+
/** Headline on the `page` variant's brand panel. Empty renders a clean brand panel. */
|
|
199
|
+
tagline: string;
|
|
196
200
|
emailLabel: string;
|
|
201
|
+
emailPlaceholder: string;
|
|
197
202
|
passwordLabel: string;
|
|
203
|
+
passwordPlaceholder: string;
|
|
198
204
|
submit: string;
|
|
199
205
|
submitting: string;
|
|
200
206
|
chooseWorkspace: string;
|
|
207
|
+
chooseWorkspaceSubheading: string;
|
|
208
|
+
securedBy: string;
|
|
209
|
+
setPasswordHeading: string;
|
|
210
|
+
setPasswordSubheading: string;
|
|
211
|
+
newPasswordLabel: string;
|
|
212
|
+
confirmPasswordLabel: string;
|
|
213
|
+
setPasswordSubmit: string;
|
|
214
|
+
passwordMismatch: string;
|
|
215
|
+
weakPassword: string;
|
|
201
216
|
invalidCredentials: string;
|
|
202
217
|
noAccess: string;
|
|
203
218
|
passwordChangeRequired: string;
|
|
204
219
|
genericError: string;
|
|
205
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* `card` renders just the card — drop it into your own layout.
|
|
223
|
+
* `page` owns the viewport — a split layout with a brand panel beside the form
|
|
224
|
+
* (the panel collapses away under 64rem, leaving the form on its own).
|
|
225
|
+
*/
|
|
226
|
+
type SignInVariant = "card" | "page";
|
|
206
227
|
interface SignInProps {
|
|
228
|
+
/** Layout preset. Default `card`. */
|
|
229
|
+
variant?: SignInVariant;
|
|
230
|
+
/**
|
|
231
|
+
* Content for the `page` variant's brand panel. Defaults to your `logo` plus
|
|
232
|
+
* `labels.tagline`; pass your own node to take the panel over completely.
|
|
233
|
+
* Ignored by the `card` variant.
|
|
234
|
+
*/
|
|
235
|
+
panel?: ReactNode;
|
|
207
236
|
/** BFF endpoint backed by `createAuthHandlers().authenticate`. Default `/api/polyx/authenticate`. */
|
|
208
237
|
action?: string;
|
|
209
238
|
/** Where to send the browser after success. Overrides the server's default redirect. */
|
|
210
239
|
afterSignInPath?: string;
|
|
211
240
|
/** Pre-scope the login to a single organization (skips org discovery). */
|
|
212
241
|
organizationCode?: string;
|
|
213
|
-
/**
|
|
242
|
+
/** Rendered above the heading — your wordmark or logo. */
|
|
243
|
+
logo?: ReactNode;
|
|
244
|
+
/** Show the "Secured by PolyX" attribution under the form. Default true. */
|
|
245
|
+
showSecuredBy?: boolean;
|
|
246
|
+
/** Appended to the root element's class list, for escape-hatch styling. */
|
|
214
247
|
className?: string;
|
|
215
248
|
labels?: Partial<SignInLabels>;
|
|
216
249
|
/** Fired when the backend requires the user to set a new password before continuing. */
|
|
@@ -222,6 +255,9 @@ interface SignInProps {
|
|
|
222
255
|
* runs the credential authorize server-side and seals an httpOnly session — no
|
|
223
256
|
* redirect to a hosted PolyX login page, no tokens or credentials in the browser
|
|
224
257
|
* after submit. Handles the workspace picker (select_tenant) inline.
|
|
258
|
+
*
|
|
259
|
+
* Ships styled: `import "@poly-x/react/styles.css"` once, then theme it by
|
|
260
|
+
* overriding the `--polyx-*` custom properties on any ancestor.
|
|
225
261
|
*/
|
|
226
262
|
declare function SignIn(props: SignInProps): ReactNode;
|
|
227
263
|
//#endregion
|
|
@@ -243,4 +279,4 @@ declare function AuthCallback({
|
|
|
243
279
|
declare const PACKAGE_NAME = "@poly-x/react";
|
|
244
280
|
declare const version = "0.0.0";
|
|
245
281
|
//#endregion
|
|
246
|
-
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, SessionStoragePkceStore, SignIn, type SignInLabels, type SignInOptions, type SignInProps, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
|
282
|
+
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, SessionStoragePkceStore, SignIn, type SignInLabels, type SignInOptions, type SignInProps, type SignInVariant, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
package/dist/index.d.mts
CHANGED
|
@@ -193,24 +193,57 @@ declare function Protected({
|
|
|
193
193
|
//#region src/components/sign-in.d.ts
|
|
194
194
|
/** Every user-facing string, overridable for i18n / white-labelling. */
|
|
195
195
|
interface SignInLabels {
|
|
196
|
+
heading: string;
|
|
197
|
+
subheading: string;
|
|
198
|
+
/** Headline on the `page` variant's brand panel. Empty renders a clean brand panel. */
|
|
199
|
+
tagline: string;
|
|
196
200
|
emailLabel: string;
|
|
201
|
+
emailPlaceholder: string;
|
|
197
202
|
passwordLabel: string;
|
|
203
|
+
passwordPlaceholder: string;
|
|
198
204
|
submit: string;
|
|
199
205
|
submitting: string;
|
|
200
206
|
chooseWorkspace: string;
|
|
207
|
+
chooseWorkspaceSubheading: string;
|
|
208
|
+
securedBy: string;
|
|
209
|
+
setPasswordHeading: string;
|
|
210
|
+
setPasswordSubheading: string;
|
|
211
|
+
newPasswordLabel: string;
|
|
212
|
+
confirmPasswordLabel: string;
|
|
213
|
+
setPasswordSubmit: string;
|
|
214
|
+
passwordMismatch: string;
|
|
215
|
+
weakPassword: string;
|
|
201
216
|
invalidCredentials: string;
|
|
202
217
|
noAccess: string;
|
|
203
218
|
passwordChangeRequired: string;
|
|
204
219
|
genericError: string;
|
|
205
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* `card` renders just the card — drop it into your own layout.
|
|
223
|
+
* `page` owns the viewport — a split layout with a brand panel beside the form
|
|
224
|
+
* (the panel collapses away under 64rem, leaving the form on its own).
|
|
225
|
+
*/
|
|
226
|
+
type SignInVariant = "card" | "page";
|
|
206
227
|
interface SignInProps {
|
|
228
|
+
/** Layout preset. Default `card`. */
|
|
229
|
+
variant?: SignInVariant;
|
|
230
|
+
/**
|
|
231
|
+
* Content for the `page` variant's brand panel. Defaults to your `logo` plus
|
|
232
|
+
* `labels.tagline`; pass your own node to take the panel over completely.
|
|
233
|
+
* Ignored by the `card` variant.
|
|
234
|
+
*/
|
|
235
|
+
panel?: ReactNode;
|
|
207
236
|
/** BFF endpoint backed by `createAuthHandlers().authenticate`. Default `/api/polyx/authenticate`. */
|
|
208
237
|
action?: string;
|
|
209
238
|
/** Where to send the browser after success. Overrides the server's default redirect. */
|
|
210
239
|
afterSignInPath?: string;
|
|
211
240
|
/** Pre-scope the login to a single organization (skips org discovery). */
|
|
212
241
|
organizationCode?: string;
|
|
213
|
-
/**
|
|
242
|
+
/** Rendered above the heading — your wordmark or logo. */
|
|
243
|
+
logo?: ReactNode;
|
|
244
|
+
/** Show the "Secured by PolyX" attribution under the form. Default true. */
|
|
245
|
+
showSecuredBy?: boolean;
|
|
246
|
+
/** Appended to the root element's class list, for escape-hatch styling. */
|
|
214
247
|
className?: string;
|
|
215
248
|
labels?: Partial<SignInLabels>;
|
|
216
249
|
/** Fired when the backend requires the user to set a new password before continuing. */
|
|
@@ -222,6 +255,9 @@ interface SignInProps {
|
|
|
222
255
|
* runs the credential authorize server-side and seals an httpOnly session — no
|
|
223
256
|
* redirect to a hosted PolyX login page, no tokens or credentials in the browser
|
|
224
257
|
* after submit. Handles the workspace picker (select_tenant) inline.
|
|
258
|
+
*
|
|
259
|
+
* Ships styled: `import "@poly-x/react/styles.css"` once, then theme it by
|
|
260
|
+
* overriding the `--polyx-*` custom properties on any ancestor.
|
|
225
261
|
*/
|
|
226
262
|
declare function SignIn(props: SignInProps): ReactNode;
|
|
227
263
|
//#endregion
|
|
@@ -243,4 +279,4 @@ declare function AuthCallback({
|
|
|
243
279
|
declare const PACKAGE_NAME = "@poly-x/react";
|
|
244
280
|
declare const version = "0.0.0";
|
|
245
281
|
//#endregion
|
|
246
|
-
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, SessionStoragePkceStore, SignIn, type SignInLabels, type SignInOptions, type SignInProps, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
|
282
|
+
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, SessionStoragePkceStore, SignIn, type SignInLabels, type SignInOptions, type SignInProps, type SignInVariant, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { AuthClient, FetchTransport, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, createSessionEngine, generatePkce, randomState, resolveConfig } from "@poly-x/core";
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/auth-channel.ts
|
|
6
6
|
var BroadcastAuthChannel = class {
|
|
7
7
|
supported;
|
|
@@ -352,35 +352,63 @@ function Protected({ children, fallback = null, loading = null }) {
|
|
|
352
352
|
//#endregion
|
|
353
353
|
//#region src/components/sign-in.tsx
|
|
354
354
|
const DEFAULT_LABELS = {
|
|
355
|
+
heading: "Sign in",
|
|
356
|
+
subheading: "Enter your credentials to continue.",
|
|
357
|
+
tagline: "",
|
|
355
358
|
emailLabel: "Email",
|
|
359
|
+
emailPlaceholder: "you@company.com",
|
|
356
360
|
passwordLabel: "Password",
|
|
361
|
+
passwordPlaceholder: "••••••••",
|
|
357
362
|
submit: "Sign in",
|
|
358
363
|
submitting: "Signing in…",
|
|
359
364
|
chooseWorkspace: "Choose a workspace",
|
|
365
|
+
chooseWorkspaceSubheading: "You have access to more than one workspace.",
|
|
366
|
+
securedBy: "Secured by PolyX",
|
|
367
|
+
setPasswordHeading: "Choose a new password",
|
|
368
|
+
setPasswordSubheading: "Your temporary password must be replaced before you continue.",
|
|
369
|
+
newPasswordLabel: "New password",
|
|
370
|
+
confirmPasswordLabel: "Confirm new password",
|
|
371
|
+
setPasswordSubmit: "Set password and sign in",
|
|
372
|
+
passwordMismatch: "Those passwords don't match.",
|
|
373
|
+
weakPassword: "Password must be at least 6 characters.",
|
|
360
374
|
invalidCredentials: "Invalid email or password.",
|
|
361
375
|
noAccess: "You don't have access to this application.",
|
|
362
376
|
passwordChangeRequired: "You must reset your password before you can sign in.",
|
|
363
377
|
genericError: "Something went wrong. Please try again."
|
|
364
378
|
};
|
|
379
|
+
/** Mirrors poly-auth's own floor (Joi: min 6). */
|
|
380
|
+
const MIN_PASSWORD_LENGTH = 6;
|
|
365
381
|
/**
|
|
366
382
|
* The embedded, Clerk-style sign-in form (D2b). Renders email/password inside the
|
|
367
383
|
* consuming app and POSTs to the SDK's BFF (`@poly-x/next` `authenticate`), which
|
|
368
384
|
* runs the credential authorize server-side and seals an httpOnly session — no
|
|
369
385
|
* redirect to a hosted PolyX login page, no tokens or credentials in the browser
|
|
370
386
|
* after submit. Handles the workspace picker (select_tenant) inline.
|
|
387
|
+
*
|
|
388
|
+
* Ships styled: `import "@poly-x/react/styles.css"` once, then theme it by
|
|
389
|
+
* overriding the `--polyx-*` custom properties on any ancestor.
|
|
371
390
|
*/
|
|
372
391
|
function SignIn(props) {
|
|
373
392
|
const action = props.action ?? "/api/polyx/authenticate";
|
|
393
|
+
const variant = props.variant ?? "card";
|
|
374
394
|
const labels = {
|
|
375
395
|
...DEFAULT_LABELS,
|
|
376
396
|
...props.labels
|
|
377
397
|
};
|
|
378
398
|
const [email, setEmail] = useState("");
|
|
379
399
|
const [password, setPassword] = useState("");
|
|
400
|
+
const [newPassword, setNewPassword] = useState("");
|
|
401
|
+
const [confirmPassword, setConfirmPassword] = useState("");
|
|
402
|
+
const [mustSetPassword, setMustSetPassword] = useState(false);
|
|
380
403
|
const [tenants, setTenants] = useState(null);
|
|
381
404
|
const [submitting, setSubmitting] = useState(false);
|
|
382
405
|
const [error, setError] = useState(null);
|
|
383
|
-
|
|
406
|
+
/**
|
|
407
|
+
* One round-trip to the BFF. `tenantId` picks a workspace; `passwords` completes
|
|
408
|
+
* the temp-password flow (the BFF re-verifies the temporary password, sets the
|
|
409
|
+
* new one, and signs the user straight in).
|
|
410
|
+
*/
|
|
411
|
+
async function authenticate(options = {}) {
|
|
384
412
|
setSubmitting(true);
|
|
385
413
|
setError(null);
|
|
386
414
|
try {
|
|
@@ -391,7 +419,8 @@ function SignIn(props) {
|
|
|
391
419
|
email,
|
|
392
420
|
password,
|
|
393
421
|
organizationCode: props.organizationCode,
|
|
394
|
-
tenantId
|
|
422
|
+
tenantId: options.tenantId,
|
|
423
|
+
...options.passwords
|
|
395
424
|
})
|
|
396
425
|
})).json().catch(() => ({}));
|
|
397
426
|
switch (body.status) {
|
|
@@ -401,16 +430,22 @@ function SignIn(props) {
|
|
|
401
430
|
case "select_tenant":
|
|
402
431
|
setTenants(body.tenants ?? []);
|
|
403
432
|
return;
|
|
433
|
+
case "password_change_required":
|
|
434
|
+
setMustSetPassword(true);
|
|
435
|
+
if (body.userId) props.onPasswordChangeRequired?.(body.userId);
|
|
436
|
+
return;
|
|
437
|
+
case "password_mismatch":
|
|
438
|
+
setError(labels.passwordMismatch);
|
|
439
|
+
return;
|
|
440
|
+
case "weak_password":
|
|
441
|
+
setError(labels.weakPassword);
|
|
442
|
+
return;
|
|
404
443
|
case "invalid_credentials":
|
|
405
444
|
setError(labels.invalidCredentials);
|
|
406
445
|
return;
|
|
407
446
|
case "no_access":
|
|
408
447
|
setError(labels.noAccess);
|
|
409
448
|
return;
|
|
410
|
-
case "password_change_required":
|
|
411
|
-
setError(labels.passwordChangeRequired);
|
|
412
|
-
if (body.userId) props.onPasswordChangeRequired?.(body.userId);
|
|
413
|
-
return;
|
|
414
449
|
default:
|
|
415
450
|
setError(labels.genericError);
|
|
416
451
|
return;
|
|
@@ -425,73 +460,212 @@ function SignIn(props) {
|
|
|
425
460
|
event.preventDefault();
|
|
426
461
|
authenticate();
|
|
427
462
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
463
|
+
function onSetPasswordSubmit(event) {
|
|
464
|
+
event.preventDefault();
|
|
465
|
+
if (newPassword !== confirmPassword) {
|
|
466
|
+
setError(labels.passwordMismatch);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
authenticate({ passwords: {
|
|
470
|
+
newPassword,
|
|
471
|
+
confirmPassword
|
|
472
|
+
} });
|
|
473
|
+
}
|
|
474
|
+
const picking = tenants !== null;
|
|
475
|
+
const hasPanel = variant === "page" && Boolean(props.panel || props.logo || labels.tagline);
|
|
476
|
+
const showHeaderLogo = Boolean(props.logo) && !hasPanel;
|
|
477
|
+
const rootClass = [
|
|
478
|
+
"polyx-signin",
|
|
479
|
+
`polyx-signin--${variant}`,
|
|
480
|
+
hasPanel && "polyx-signin--split",
|
|
481
|
+
props.className
|
|
482
|
+
].filter(Boolean).join(" ");
|
|
483
|
+
const step = picking ? "select_tenant" : mustSetPassword ? "set_password" : "credentials";
|
|
484
|
+
const HEADINGS = {
|
|
485
|
+
credentials: {
|
|
486
|
+
heading: labels.heading,
|
|
487
|
+
subheading: labels.subheading
|
|
488
|
+
},
|
|
489
|
+
select_tenant: {
|
|
490
|
+
heading: labels.chooseWorkspace,
|
|
491
|
+
subheading: labels.chooseWorkspaceSubheading
|
|
492
|
+
},
|
|
493
|
+
set_password: {
|
|
494
|
+
heading: labels.setPasswordHeading,
|
|
495
|
+
subheading: labels.setPasswordSubheading
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
const header = /* @__PURE__ */ jsxs("div", {
|
|
499
|
+
className: "polyx-signin__header",
|
|
431
500
|
children: [
|
|
432
|
-
/* @__PURE__ */ jsx("
|
|
501
|
+
showHeaderLogo ? /* @__PURE__ */ jsx("div", {
|
|
502
|
+
className: "polyx-signin__logo",
|
|
503
|
+
children: props.logo
|
|
504
|
+
}) : null,
|
|
505
|
+
/* @__PURE__ */ jsx("h1", {
|
|
433
506
|
className: "polyx-signin__heading",
|
|
434
|
-
children:
|
|
507
|
+
children: HEADINGS[step].heading
|
|
435
508
|
}),
|
|
436
|
-
/* @__PURE__ */ jsx("
|
|
437
|
-
className: "polyx-
|
|
438
|
-
children:
|
|
439
|
-
|
|
440
|
-
className: "polyx-signin__tenant",
|
|
441
|
-
disabled: submitting,
|
|
442
|
-
onClick: () => void authenticate(tenant.tenantId),
|
|
443
|
-
children: tenant.name ?? tenant.slug ?? tenant.organizationCode ?? tenant.tenantId
|
|
444
|
-
}) }, tenant.tenantId))
|
|
445
|
-
}),
|
|
446
|
-
error ? /* @__PURE__ */ jsx("p", {
|
|
447
|
-
role: "alert",
|
|
448
|
-
className: "polyx-signin__error",
|
|
449
|
-
children: error
|
|
450
|
-
}) : null
|
|
509
|
+
/* @__PURE__ */ jsx("p", {
|
|
510
|
+
className: "polyx-signin__subheading",
|
|
511
|
+
children: HEADINGS[step].subheading
|
|
512
|
+
})
|
|
451
513
|
]
|
|
452
514
|
});
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
"
|
|
456
|
-
|
|
515
|
+
const errorNode = error ? /* @__PURE__ */ jsx("p", {
|
|
516
|
+
role: "alert",
|
|
517
|
+
className: "polyx-signin__error",
|
|
518
|
+
children: error
|
|
519
|
+
}) : null;
|
|
520
|
+
const footer = props.showSecuredBy === false ? null : /* @__PURE__ */ jsxs("p", {
|
|
521
|
+
className: "polyx-signin__footer",
|
|
522
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
523
|
+
className: "polyx-signin__shield",
|
|
524
|
+
viewBox: "0 0 16 16",
|
|
525
|
+
fill: "currentColor",
|
|
526
|
+
"aria-hidden": "true",
|
|
527
|
+
focusable: "false",
|
|
528
|
+
children: /* @__PURE__ */ jsx("path", { d: "M8 0.5 2 3v4.2c0 3.5 2.5 6.7 6 7.8 3.5-1.1 6-4.3 6-7.8V3L8 .5Zm2.9 5.6-3.4 3.4a.7.7 0 0 1-1 0L5.1 8.1a.7.7 0 1 1 1-1l.9.9 2.9-2.9a.7.7 0 0 1 1 1Z" })
|
|
529
|
+
}), labels.securedBy]
|
|
530
|
+
});
|
|
531
|
+
const tenantPicker = /* @__PURE__ */ jsxs("div", {
|
|
532
|
+
className: "polyx-signin__form",
|
|
533
|
+
children: [/* @__PURE__ */ jsx("ul", {
|
|
534
|
+
className: "polyx-signin__tenants",
|
|
535
|
+
children: (tenants ?? []).map((tenant) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
|
|
536
|
+
type: "button",
|
|
537
|
+
className: "polyx-signin__tenant",
|
|
538
|
+
disabled: submitting,
|
|
539
|
+
onClick: () => void authenticate({ tenantId: tenant.tenantId }),
|
|
540
|
+
children: tenant.name ?? tenant.slug ?? tenant.organizationCode ?? tenant.tenantId
|
|
541
|
+
}) }, tenant.tenantId))
|
|
542
|
+
}), errorNode]
|
|
543
|
+
});
|
|
544
|
+
const setPasswordForm = /* @__PURE__ */ jsxs("form", {
|
|
545
|
+
className: "polyx-signin__form",
|
|
546
|
+
onSubmit: onSetPasswordSubmit,
|
|
457
547
|
children: [
|
|
458
548
|
/* @__PURE__ */ jsxs("label", {
|
|
459
549
|
className: "polyx-signin__field",
|
|
460
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
550
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
551
|
+
className: "polyx-signin__label",
|
|
552
|
+
children: labels.newPasswordLabel
|
|
553
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
554
|
+
className: "polyx-signin__input",
|
|
555
|
+
type: "password",
|
|
556
|
+
name: "newPassword",
|
|
557
|
+
autoComplete: "new-password",
|
|
558
|
+
placeholder: labels.passwordPlaceholder,
|
|
464
559
|
required: true,
|
|
465
|
-
|
|
560
|
+
minLength: MIN_PASSWORD_LENGTH,
|
|
561
|
+
value: newPassword,
|
|
466
562
|
disabled: submitting,
|
|
467
|
-
onChange: (event) =>
|
|
563
|
+
onChange: (event) => setNewPassword(event.target.value)
|
|
468
564
|
})]
|
|
469
565
|
}),
|
|
470
566
|
/* @__PURE__ */ jsxs("label", {
|
|
471
567
|
className: "polyx-signin__field",
|
|
472
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
568
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
569
|
+
className: "polyx-signin__label",
|
|
570
|
+
children: labels.confirmPasswordLabel
|
|
571
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
572
|
+
className: "polyx-signin__input",
|
|
473
573
|
type: "password",
|
|
474
|
-
name: "
|
|
475
|
-
autoComplete: "
|
|
574
|
+
name: "confirmPassword",
|
|
575
|
+
autoComplete: "new-password",
|
|
576
|
+
placeholder: labels.passwordPlaceholder,
|
|
476
577
|
required: true,
|
|
477
|
-
|
|
578
|
+
minLength: MIN_PASSWORD_LENGTH,
|
|
579
|
+
value: confirmPassword,
|
|
478
580
|
disabled: submitting,
|
|
479
|
-
onChange: (event) =>
|
|
581
|
+
onChange: (event) => setConfirmPassword(event.target.value)
|
|
480
582
|
})]
|
|
481
583
|
}),
|
|
482
|
-
|
|
483
|
-
role: "alert",
|
|
484
|
-
className: "polyx-signin__error",
|
|
485
|
-
children: error
|
|
486
|
-
}) : null,
|
|
584
|
+
errorNode,
|
|
487
585
|
/* @__PURE__ */ jsx("button", {
|
|
488
586
|
type: "submit",
|
|
489
587
|
className: "polyx-signin__submit",
|
|
490
588
|
disabled: submitting,
|
|
491
|
-
children: submitting ? labels.submitting : labels.
|
|
589
|
+
children: submitting ? labels.submitting : labels.setPasswordSubmit
|
|
492
590
|
})
|
|
493
591
|
]
|
|
494
592
|
});
|
|
593
|
+
const card = /* @__PURE__ */ jsxs("div", {
|
|
594
|
+
className: "polyx-signin__card",
|
|
595
|
+
children: [
|
|
596
|
+
header,
|
|
597
|
+
step === "select_tenant" ? tenantPicker : step === "set_password" ? setPasswordForm : /* @__PURE__ */ jsxs("form", {
|
|
598
|
+
className: "polyx-signin__form",
|
|
599
|
+
onSubmit,
|
|
600
|
+
children: [
|
|
601
|
+
/* @__PURE__ */ jsxs("label", {
|
|
602
|
+
className: "polyx-signin__field",
|
|
603
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
604
|
+
className: "polyx-signin__label",
|
|
605
|
+
children: labels.emailLabel
|
|
606
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
607
|
+
className: "polyx-signin__input",
|
|
608
|
+
type: "email",
|
|
609
|
+
name: "email",
|
|
610
|
+
autoComplete: "username",
|
|
611
|
+
placeholder: labels.emailPlaceholder,
|
|
612
|
+
required: true,
|
|
613
|
+
value: email,
|
|
614
|
+
disabled: submitting,
|
|
615
|
+
onChange: (event) => setEmail(event.target.value)
|
|
616
|
+
})]
|
|
617
|
+
}),
|
|
618
|
+
/* @__PURE__ */ jsxs("label", {
|
|
619
|
+
className: "polyx-signin__field",
|
|
620
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
621
|
+
className: "polyx-signin__label",
|
|
622
|
+
children: labels.passwordLabel
|
|
623
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
624
|
+
className: "polyx-signin__input",
|
|
625
|
+
type: "password",
|
|
626
|
+
name: "password",
|
|
627
|
+
autoComplete: "current-password",
|
|
628
|
+
placeholder: labels.passwordPlaceholder,
|
|
629
|
+
required: true,
|
|
630
|
+
value: password,
|
|
631
|
+
disabled: submitting,
|
|
632
|
+
onChange: (event) => setPassword(event.target.value)
|
|
633
|
+
})]
|
|
634
|
+
}),
|
|
635
|
+
errorNode,
|
|
636
|
+
/* @__PURE__ */ jsx("button", {
|
|
637
|
+
type: "submit",
|
|
638
|
+
className: "polyx-signin__submit",
|
|
639
|
+
disabled: submitting,
|
|
640
|
+
children: submitting ? labels.submitting : labels.submit
|
|
641
|
+
})
|
|
642
|
+
]
|
|
643
|
+
}),
|
|
644
|
+
footer
|
|
645
|
+
]
|
|
646
|
+
});
|
|
647
|
+
if (variant === "page") return /* @__PURE__ */ jsxs("div", {
|
|
648
|
+
className: rootClass,
|
|
649
|
+
"data-polyx-signin": step,
|
|
650
|
+
children: [hasPanel ? /* @__PURE__ */ jsx("aside", {
|
|
651
|
+
className: "polyx-signin__panel",
|
|
652
|
+
children: props.panel ?? /* @__PURE__ */ jsxs(Fragment, { children: [props.logo ? /* @__PURE__ */ jsx("div", {
|
|
653
|
+
className: "polyx-signin__panel-logo",
|
|
654
|
+
children: props.logo
|
|
655
|
+
}) : null, labels.tagline ? /* @__PURE__ */ jsx("p", {
|
|
656
|
+
className: "polyx-signin__tagline",
|
|
657
|
+
children: labels.tagline
|
|
658
|
+
}) : null] })
|
|
659
|
+
}) : null, /* @__PURE__ */ jsx("div", {
|
|
660
|
+
className: "polyx-signin__main",
|
|
661
|
+
children: card
|
|
662
|
+
})]
|
|
663
|
+
});
|
|
664
|
+
return /* @__PURE__ */ jsx("div", {
|
|
665
|
+
className: rootClass,
|
|
666
|
+
"data-polyx-signin": step,
|
|
667
|
+
children: card
|
|
668
|
+
});
|
|
495
669
|
}
|
|
496
670
|
//#endregion
|
|
497
671
|
//#region src/components/auth-callback.tsx
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @poly-x/react — default component styles.
|
|
3
|
+
*
|
|
4
|
+
* Import once, near the root of your app:
|
|
5
|
+
* import "@poly-x/react/styles.css"; // or "@poly-x/next/styles.css"
|
|
6
|
+
*
|
|
7
|
+
* The SDK is a guest in your app: every rule is scoped under a `.polyx-*` class,
|
|
8
|
+
* there are no global element selectors, and nothing here leaks into your own
|
|
9
|
+
* markup. To theme it, override the custom properties below on any ancestor
|
|
10
|
+
* (`:root`, a wrapper div, …) — that is the supported theming surface. Dark mode
|
|
11
|
+
* follows the OS by default and defers to an explicit `.dark` / `[data-theme]`
|
|
12
|
+
* on a host ancestor when one is present.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
.polyx-signin {
|
|
16
|
+
/* Colors */
|
|
17
|
+
--polyx-brand: #4f46e5;
|
|
18
|
+
--polyx-brand-hover: #4338ca;
|
|
19
|
+
--polyx-brand-fg: #ffffff;
|
|
20
|
+
--polyx-bg: #ffffff;
|
|
21
|
+
--polyx-fg: #18181b;
|
|
22
|
+
--polyx-muted-fg: #71717a;
|
|
23
|
+
--polyx-border: #e4e4e7;
|
|
24
|
+
--polyx-input-bg: #ffffff;
|
|
25
|
+
--polyx-ring: #a5b4fc;
|
|
26
|
+
--polyx-danger: #dc2626;
|
|
27
|
+
--polyx-danger-bg: #fef2f2;
|
|
28
|
+
--polyx-page-bg: #fafafa;
|
|
29
|
+
|
|
30
|
+
/* Shape + type */
|
|
31
|
+
--polyx-radius: 0.75rem;
|
|
32
|
+
--polyx-radius-sm: 0.5rem;
|
|
33
|
+
--polyx-font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
34
|
+
--polyx-shadow: 0 1px 3px rgb(0 0 0 / 0.06), 0 10px 30px rgb(0 0 0 / 0.07);
|
|
35
|
+
|
|
36
|
+
font-family: var(--polyx-font);
|
|
37
|
+
color: var(--polyx-fg);
|
|
38
|
+
-webkit-font-smoothing: antialiased;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Scoped box model — applies only inside our own subtree. */
|
|
42
|
+
.polyx-signin,
|
|
43
|
+
.polyx-signin *,
|
|
44
|
+
.polyx-signin *::before,
|
|
45
|
+
.polyx-signin *::after {
|
|
46
|
+
box-sizing: border-box;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Dark: follow the OS… */
|
|
50
|
+
@media (prefers-color-scheme: dark) {
|
|
51
|
+
.polyx-signin {
|
|
52
|
+
--polyx-brand: #6366f1;
|
|
53
|
+
--polyx-brand-hover: #818cf8;
|
|
54
|
+
--polyx-bg: #18181b;
|
|
55
|
+
--polyx-fg: #fafafa;
|
|
56
|
+
--polyx-muted-fg: #a1a1aa;
|
|
57
|
+
--polyx-border: #3f3f46;
|
|
58
|
+
--polyx-input-bg: #27272a;
|
|
59
|
+
--polyx-ring: #4f46e5;
|
|
60
|
+
--polyx-danger: #f87171;
|
|
61
|
+
--polyx-danger-bg: #2a1616;
|
|
62
|
+
--polyx-page-bg: #09090b;
|
|
63
|
+
--polyx-shadow: 0 1px 3px rgb(0 0 0 / 0.4), 0 10px 30px rgb(0 0 0 / 0.35);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* …but an explicit host theme wins over the OS preference (`:where` keeps
|
|
68
|
+
specificity at 0 so these stay as easy to override as the base rule). */
|
|
69
|
+
:where(.dark, [data-theme="dark"]) .polyx-signin {
|
|
70
|
+
--polyx-brand: #6366f1;
|
|
71
|
+
--polyx-brand-hover: #818cf8;
|
|
72
|
+
--polyx-bg: #18181b;
|
|
73
|
+
--polyx-fg: #fafafa;
|
|
74
|
+
--polyx-muted-fg: #a1a1aa;
|
|
75
|
+
--polyx-border: #3f3f46;
|
|
76
|
+
--polyx-input-bg: #27272a;
|
|
77
|
+
--polyx-ring: #4f46e5;
|
|
78
|
+
--polyx-danger: #f87171;
|
|
79
|
+
--polyx-danger-bg: #2a1616;
|
|
80
|
+
--polyx-page-bg: #09090b;
|
|
81
|
+
--polyx-shadow: 0 1px 3px rgb(0 0 0 / 0.4), 0 10px 30px rgb(0 0 0 / 0.35);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
:where(.light, [data-theme="light"]) .polyx-signin {
|
|
85
|
+
--polyx-brand: #4f46e5;
|
|
86
|
+
--polyx-brand-hover: #4338ca;
|
|
87
|
+
--polyx-bg: #ffffff;
|
|
88
|
+
--polyx-fg: #18181b;
|
|
89
|
+
--polyx-muted-fg: #71717a;
|
|
90
|
+
--polyx-border: #e4e4e7;
|
|
91
|
+
--polyx-input-bg: #ffffff;
|
|
92
|
+
--polyx-ring: #a5b4fc;
|
|
93
|
+
--polyx-danger: #dc2626;
|
|
94
|
+
--polyx-danger-bg: #fef2f2;
|
|
95
|
+
--polyx-page-bg: #fafafa;
|
|
96
|
+
--polyx-shadow: 0 1px 3px rgb(0 0 0 / 0.06), 0 10px 30px rgb(0 0 0 / 0.07);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* --- Variants ------------------------------------------------------------ */
|
|
100
|
+
|
|
101
|
+
/* `card`: just the card — drop it anywhere in your own layout. */
|
|
102
|
+
.polyx-signin--card {
|
|
103
|
+
display: block;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.polyx-signin__card {
|
|
107
|
+
width: 100%;
|
|
108
|
+
max-width: 27rem;
|
|
109
|
+
margin-inline: auto;
|
|
110
|
+
padding: 2.25rem;
|
|
111
|
+
background: var(--polyx-bg);
|
|
112
|
+
border: 1px solid var(--polyx-border);
|
|
113
|
+
border-radius: var(--polyx-radius);
|
|
114
|
+
box-shadow: var(--polyx-shadow);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* `page`: owns the viewport. With brand content (a `logo`, a `tagline`, or a
|
|
119
|
+
* custom `panel`) it becomes a split layout — brand panel beside the form on
|
|
120
|
+
* wide screens, a slim brand band above it on narrow ones. With no brand content
|
|
121
|
+
* there's nothing to put in a panel, so it stays a single centered column.
|
|
122
|
+
*/
|
|
123
|
+
.polyx-signin--page {
|
|
124
|
+
display: grid;
|
|
125
|
+
grid-template-columns: 1fr;
|
|
126
|
+
min-height: 100dvh;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.polyx-signin--page.polyx-signin--split {
|
|
130
|
+
grid-template-rows: auto 1fr;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@media (min-width: 64rem) {
|
|
134
|
+
.polyx-signin--page.polyx-signin--split {
|
|
135
|
+
grid-template-columns: 1.1fr 1fr;
|
|
136
|
+
grid-template-rows: 1fr;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.polyx-signin__panel {
|
|
141
|
+
display: flex;
|
|
142
|
+
position: relative;
|
|
143
|
+
overflow: hidden;
|
|
144
|
+
align-items: center;
|
|
145
|
+
gap: 1rem;
|
|
146
|
+
padding: 1.25rem 1.5rem;
|
|
147
|
+
color: var(--polyx-brand-fg);
|
|
148
|
+
background: linear-gradient(145deg, var(--polyx-brand), color-mix(in srgb, var(--polyx-brand) 55%, #000));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@media (min-width: 64rem) {
|
|
152
|
+
.polyx-signin__panel {
|
|
153
|
+
flex-direction: column;
|
|
154
|
+
align-items: flex-start;
|
|
155
|
+
justify-content: space-between;
|
|
156
|
+
gap: 2rem;
|
|
157
|
+
padding: 3.5rem;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Soft light-bloom so a bare brand panel still reads as designed, not as a flat fill. */
|
|
162
|
+
.polyx-signin__panel::after {
|
|
163
|
+
content: "";
|
|
164
|
+
position: absolute;
|
|
165
|
+
inset: 0;
|
|
166
|
+
pointer-events: none;
|
|
167
|
+
background:
|
|
168
|
+
radial-gradient(circle at 82% 12%, rgb(255 255 255 / 0.18), transparent 45%),
|
|
169
|
+
radial-gradient(circle at 12% 88%, rgb(255 255 255 / 0.1), transparent 42%);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.polyx-signin__panel-logo,
|
|
173
|
+
.polyx-signin__tagline {
|
|
174
|
+
position: relative;
|
|
175
|
+
z-index: 1;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* The tagline is the panel's headline — it only earns its space on wide screens. */
|
|
179
|
+
.polyx-signin__tagline {
|
|
180
|
+
display: none;
|
|
181
|
+
margin: 0;
|
|
182
|
+
max-width: 18ch;
|
|
183
|
+
font-size: 2rem;
|
|
184
|
+
font-weight: 600;
|
|
185
|
+
line-height: 1.2;
|
|
186
|
+
letter-spacing: -0.02em;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@media (min-width: 64rem) {
|
|
190
|
+
.polyx-signin__tagline {
|
|
191
|
+
display: block;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.polyx-signin__main {
|
|
196
|
+
display: grid;
|
|
197
|
+
place-items: center;
|
|
198
|
+
padding: 2.5rem 1.5rem;
|
|
199
|
+
background: var(--polyx-page-bg);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* On the page variant the form isn't a floating card — it sits on the page itself. */
|
|
203
|
+
.polyx-signin--page .polyx-signin__card {
|
|
204
|
+
max-width: 25rem;
|
|
205
|
+
padding: 0;
|
|
206
|
+
background: transparent;
|
|
207
|
+
border: none;
|
|
208
|
+
box-shadow: none;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
/* --- Header -------------------------------------------------------------- */
|
|
213
|
+
|
|
214
|
+
.polyx-signin__header {
|
|
215
|
+
display: flex;
|
|
216
|
+
flex-direction: column;
|
|
217
|
+
gap: 0.375rem;
|
|
218
|
+
margin-bottom: 1.75rem;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.polyx-signin__logo {
|
|
222
|
+
display: flex;
|
|
223
|
+
margin-bottom: 0.75rem;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.polyx-signin__heading {
|
|
227
|
+
margin: 0;
|
|
228
|
+
font-size: 1.375rem;
|
|
229
|
+
font-weight: 600;
|
|
230
|
+
letter-spacing: -0.01em;
|
|
231
|
+
color: var(--polyx-fg);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.polyx-signin__subheading {
|
|
235
|
+
margin: 0;
|
|
236
|
+
font-size: 0.875rem;
|
|
237
|
+
color: var(--polyx-muted-fg);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* --- Form ---------------------------------------------------------------- */
|
|
241
|
+
|
|
242
|
+
.polyx-signin__form {
|
|
243
|
+
display: flex;
|
|
244
|
+
flex-direction: column;
|
|
245
|
+
gap: 1.125rem;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.polyx-signin__field {
|
|
249
|
+
display: flex;
|
|
250
|
+
flex-direction: column;
|
|
251
|
+
gap: 0.4375rem;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.polyx-signin__label {
|
|
255
|
+
font-size: 0.875rem;
|
|
256
|
+
font-weight: 500;
|
|
257
|
+
color: var(--polyx-fg);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.polyx-signin__input {
|
|
261
|
+
width: 100%;
|
|
262
|
+
height: 2.5rem;
|
|
263
|
+
padding: 0 0.75rem;
|
|
264
|
+
font: inherit;
|
|
265
|
+
font-size: 0.875rem;
|
|
266
|
+
color: var(--polyx-fg);
|
|
267
|
+
background: var(--polyx-input-bg);
|
|
268
|
+
border: 1px solid var(--polyx-border);
|
|
269
|
+
border-radius: var(--polyx-radius-sm);
|
|
270
|
+
outline: none;
|
|
271
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.polyx-signin__input::placeholder {
|
|
275
|
+
color: var(--polyx-muted-fg);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.polyx-signin__input:focus-visible {
|
|
279
|
+
border-color: var(--polyx-brand);
|
|
280
|
+
box-shadow: 0 0 0 3px var(--polyx-ring);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.polyx-signin__input:disabled {
|
|
284
|
+
opacity: 0.6;
|
|
285
|
+
cursor: not-allowed;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* --- Buttons ------------------------------------------------------------- */
|
|
289
|
+
|
|
290
|
+
.polyx-signin__submit,
|
|
291
|
+
.polyx-signin__tenant {
|
|
292
|
+
display: inline-flex;
|
|
293
|
+
align-items: center;
|
|
294
|
+
justify-content: center;
|
|
295
|
+
width: 100%;
|
|
296
|
+
min-height: 2.5rem;
|
|
297
|
+
padding: 0 1rem;
|
|
298
|
+
font: inherit;
|
|
299
|
+
font-size: 0.875rem;
|
|
300
|
+
font-weight: 500;
|
|
301
|
+
color: var(--polyx-brand-fg);
|
|
302
|
+
background: var(--polyx-brand);
|
|
303
|
+
border: 1px solid transparent;
|
|
304
|
+
border-radius: var(--polyx-radius-sm);
|
|
305
|
+
cursor: pointer;
|
|
306
|
+
transition: background-color 0.15s ease;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.polyx-signin__submit:hover:not(:disabled),
|
|
310
|
+
.polyx-signin__tenant:hover:not(:disabled) {
|
|
311
|
+
background: var(--polyx-brand-hover);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.polyx-signin__submit:focus-visible,
|
|
315
|
+
.polyx-signin__tenant:focus-visible {
|
|
316
|
+
outline: none;
|
|
317
|
+
box-shadow: 0 0 0 3px var(--polyx-ring);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.polyx-signin__submit:disabled,
|
|
321
|
+
.polyx-signin__tenant:disabled {
|
|
322
|
+
opacity: 0.6;
|
|
323
|
+
cursor: not-allowed;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* --- Error --------------------------------------------------------------- */
|
|
327
|
+
|
|
328
|
+
.polyx-signin__error {
|
|
329
|
+
margin: 0;
|
|
330
|
+
padding: 0.625rem 0.75rem;
|
|
331
|
+
font-size: 0.8125rem;
|
|
332
|
+
color: var(--polyx-danger);
|
|
333
|
+
background: var(--polyx-danger-bg);
|
|
334
|
+
border: 1px solid color-mix(in srgb, var(--polyx-danger) 30%, transparent);
|
|
335
|
+
border-radius: var(--polyx-radius-sm);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* --- Footer -------------------------------------------------------------- */
|
|
339
|
+
|
|
340
|
+
.polyx-signin__footer {
|
|
341
|
+
display: flex;
|
|
342
|
+
align-items: center;
|
|
343
|
+
justify-content: center;
|
|
344
|
+
gap: 0.375rem;
|
|
345
|
+
margin-top: 1.5rem;
|
|
346
|
+
font-size: 0.75rem;
|
|
347
|
+
color: var(--polyx-muted-fg);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.polyx-signin__shield {
|
|
351
|
+
flex: none;
|
|
352
|
+
width: 0.875rem;
|
|
353
|
+
height: 0.875rem;
|
|
354
|
+
opacity: 0.85;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* --- Workspace picker ---------------------------------------------------- */
|
|
358
|
+
|
|
359
|
+
.polyx-signin__tenants {
|
|
360
|
+
display: flex;
|
|
361
|
+
flex-direction: column;
|
|
362
|
+
gap: 0.625rem;
|
|
363
|
+
margin: 0;
|
|
364
|
+
padding: 0;
|
|
365
|
+
list-style: none;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* The picker's buttons read as choices, not as the primary CTA. */
|
|
369
|
+
.polyx-signin__tenant {
|
|
370
|
+
justify-content: flex-start;
|
|
371
|
+
color: var(--polyx-fg);
|
|
372
|
+
background: var(--polyx-input-bg);
|
|
373
|
+
border-color: var(--polyx-border);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.polyx-signin__tenant:hover:not(:disabled) {
|
|
377
|
+
background: var(--polyx-input-bg);
|
|
378
|
+
border-color: var(--polyx-brand);
|
|
379
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poly-x/react",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.4",
|
|
4
4
|
"description": "PolyX SDK for React SPAs - provider, hooks, drop-in auth UI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"sideEffects":
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"**/*.css"
|
|
9
|
+
],
|
|
8
10
|
"files": [
|
|
9
11
|
"dist"
|
|
10
12
|
],
|
|
@@ -18,13 +20,14 @@
|
|
|
18
20
|
"types": "./dist/index.d.cts",
|
|
19
21
|
"default": "./dist/index.cjs"
|
|
20
22
|
}
|
|
21
|
-
}
|
|
23
|
+
},
|
|
24
|
+
"./styles.css": "./dist/styles.css"
|
|
22
25
|
},
|
|
23
26
|
"main": "./dist/index.cjs",
|
|
24
27
|
"module": "./dist/index.mjs",
|
|
25
28
|
"types": "./dist/index.d.cts",
|
|
26
29
|
"dependencies": {
|
|
27
|
-
"@poly-x/core": "0.1.0-alpha.
|
|
30
|
+
"@poly-x/core": "0.1.0-alpha.4"
|
|
28
31
|
},
|
|
29
32
|
"peerDependencies": {
|
|
30
33
|
"react": ">=18",
|
|
@@ -51,7 +54,7 @@
|
|
|
51
54
|
"react-dom": "^19.2.7"
|
|
52
55
|
},
|
|
53
56
|
"scripts": {
|
|
54
|
-
"build": "tsdown",
|
|
57
|
+
"build": "tsdown && node scripts/copy-styles.mjs",
|
|
55
58
|
"test": "vitest run",
|
|
56
59
|
"lint": "eslint src"
|
|
57
60
|
}
|