@poly-x/react 0.1.0-alpha.7 → 0.1.0-alpha.9

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 CHANGED
@@ -360,8 +360,11 @@ const DEFAULT_LABELS$2 = {
360
360
  emailPlaceholder: "you@company.com",
361
361
  passwordLabel: "Password",
362
362
  passwordPlaceholder: "••••••••",
363
+ showPassword: "Show password",
364
+ hidePassword: "Hide password",
363
365
  submit: "Sign in",
364
366
  submitting: "Signing in…",
367
+ forgotPassword: "Forgot password?",
365
368
  chooseWorkspace: "Choose a workspace",
366
369
  chooseWorkspaceSubheading: "You have access to more than one workspace.",
367
370
  securedBy: "Secured by PolyX",
@@ -379,6 +382,77 @@ const DEFAULT_LABELS$2 = {
379
382
  };
380
383
  /** Mirrors poly-auth's own floor (Joi: min 6). */
381
384
  const MIN_PASSWORD_LENGTH = 6;
385
+ function EyeIcon() {
386
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
387
+ viewBox: "0 0 24 24",
388
+ fill: "none",
389
+ stroke: "currentColor",
390
+ strokeWidth: "2",
391
+ strokeLinecap: "round",
392
+ strokeLinejoin: "round",
393
+ "aria-hidden": "true",
394
+ focusable: "false",
395
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
396
+ cx: "12",
397
+ cy: "12",
398
+ r: "3"
399
+ })]
400
+ });
401
+ }
402
+ function EyeOffIcon() {
403
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
404
+ viewBox: "0 0 24 24",
405
+ fill: "none",
406
+ stroke: "currentColor",
407
+ strokeWidth: "2",
408
+ strokeLinecap: "round",
409
+ strokeLinejoin: "round",
410
+ "aria-hidden": "true",
411
+ focusable: "false",
412
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M9.88 4.24A9.1 9.1 0 0 1 12 4c6.5 0 10 7 10 7a13.2 13.2 0 0 1-1.67 2.68M6.61 6.61A13.5 13.5 0 0 0 2 12s3.5 7 10 7a9.7 9.7 0 0 0 5.39-1.61" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
413
+ x1: "2",
414
+ y1: "2",
415
+ x2: "22",
416
+ y2: "22"
417
+ })]
418
+ });
419
+ }
420
+ /** A password input with an accessible show/hide reveal toggle (own visibility state). */
421
+ function PasswordField(props) {
422
+ const [visible, setVisible] = (0, react.useState)(false);
423
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
424
+ className: "polyx-signin__field",
425
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
426
+ className: "polyx-signin__label",
427
+ htmlFor: props.id,
428
+ children: props.label
429
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
430
+ className: "polyx-signin__input-wrap",
431
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
432
+ id: props.id,
433
+ className: "polyx-signin__input polyx-signin__input--has-reveal",
434
+ type: visible ? "text" : "password",
435
+ name: props.name,
436
+ autoComplete: props.autoComplete,
437
+ placeholder: props.placeholder,
438
+ required: true,
439
+ minLength: props.minLength,
440
+ value: props.value,
441
+ disabled: props.disabled,
442
+ onChange: (event) => props.onChange(event.target.value)
443
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
444
+ type: "button",
445
+ className: "polyx-signin__reveal",
446
+ "aria-pressed": visible,
447
+ "aria-label": visible ? props.hideLabel : props.showLabel,
448
+ title: visible ? props.hideLabel : props.showLabel,
449
+ disabled: props.disabled,
450
+ onClick: () => setVisible((value) => !value),
451
+ children: visible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EyeOffIcon, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EyeIcon, {})
452
+ })]
453
+ })]
454
+ });
455
+ }
382
456
  /**
383
457
  * The embedded, Clerk-style sign-in form (D2b). Renders email/password inside the
384
458
  * consuming app and POSTs to the SDK's BFF (`@poly-x/next` `authenticate`), which
@@ -404,6 +478,29 @@ function SignIn(props) {
404
478
  const [tenants, setTenants] = (0, react.useState)(null);
405
479
  const [submitting, setSubmitting] = (0, react.useState)(false);
406
480
  const [error, setError] = (0, react.useState)(null);
481
+ const polyx = (0, react.useContext)(PolyXContext);
482
+ const [branding, setBranding] = (0, react.useState)({});
483
+ (0, react.useEffect)(() => {
484
+ if (!polyx) return;
485
+ let active = true;
486
+ polyx.authClient.fetchBranding(props.organizationCode ? { organizationCode: props.organizationCode } : {}).then((tokens) => {
487
+ if (active) setBranding(tokens);
488
+ });
489
+ return () => {
490
+ active = false;
491
+ };
492
+ }, [polyx, props.organizationCode]);
493
+ const brandingStyle = {};
494
+ if (branding.primaryColor) brandingStyle["--polyx-brand"] = branding.primaryColor;
495
+ if (branding.backgroundColor) {
496
+ brandingStyle["--polyx-bg"] = branding.backgroundColor;
497
+ brandingStyle["--polyx-page-bg"] = branding.backgroundColor;
498
+ }
499
+ const brandingLogo = props.logo ?? (branding.logoUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
500
+ className: "polyx-signin__brand-logo",
501
+ src: branding.logoUrl,
502
+ alt: branding.displayName ?? ""
503
+ }) : void 0);
407
504
  /**
408
505
  * One round-trip to the BFF. `tenantId` picks a workspace; `passwords` completes
409
506
  * the temp-password flow (the BFF re-verifies the temporary password, sets the
@@ -473,8 +570,7 @@ function SignIn(props) {
473
570
  } });
474
571
  }
475
572
  const picking = tenants !== null;
476
- const hasPanel = variant === "page";
477
- const showHeaderLogo = Boolean(props.logo) && !hasPanel;
573
+ const showHeaderLogo = Boolean(brandingLogo) && !(variant === "page");
478
574
  const rootClass = [
479
575
  "polyx-signin",
480
576
  `polyx-signin--${variant}`,
@@ -500,7 +596,7 @@ function SignIn(props) {
500
596
  children: [
501
597
  showHeaderLogo ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
502
598
  className: "polyx-signin__logo",
503
- children: props.logo
599
+ children: brandingLogo
504
600
  }) : null,
505
601
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
506
602
  className: "polyx-signin__heading",
@@ -545,41 +641,31 @@ function SignIn(props) {
545
641
  className: "polyx-signin__form",
546
642
  onSubmit: onSetPasswordSubmit,
547
643
  children: [
548
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
549
- className: "polyx-signin__field",
550
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
551
- className: "polyx-signin__label",
552
- children: labels.newPasswordLabel
553
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
554
- className: "polyx-signin__input",
555
- type: "password",
556
- name: "newPassword",
557
- autoComplete: "new-password",
558
- placeholder: labels.passwordPlaceholder,
559
- required: true,
560
- minLength: MIN_PASSWORD_LENGTH,
561
- value: newPassword,
562
- disabled: submitting,
563
- onChange: (event) => setNewPassword(event.target.value)
564
- })]
644
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PasswordField, {
645
+ id: "polyx-signin-new-password",
646
+ label: labels.newPasswordLabel,
647
+ name: "newPassword",
648
+ autoComplete: "new-password",
649
+ placeholder: labels.passwordPlaceholder,
650
+ minLength: MIN_PASSWORD_LENGTH,
651
+ value: newPassword,
652
+ disabled: submitting,
653
+ onChange: setNewPassword,
654
+ showLabel: labels.showPassword,
655
+ hideLabel: labels.hidePassword
565
656
  }),
566
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
567
- className: "polyx-signin__field",
568
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
569
- className: "polyx-signin__label",
570
- children: labels.confirmPasswordLabel
571
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
572
- className: "polyx-signin__input",
573
- type: "password",
574
- name: "confirmPassword",
575
- autoComplete: "new-password",
576
- placeholder: labels.passwordPlaceholder,
577
- required: true,
578
- minLength: MIN_PASSWORD_LENGTH,
579
- value: confirmPassword,
580
- disabled: submitting,
581
- onChange: (event) => setConfirmPassword(event.target.value)
582
- })]
657
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PasswordField, {
658
+ id: "polyx-signin-confirm-password",
659
+ label: labels.confirmPasswordLabel,
660
+ name: "confirmPassword",
661
+ autoComplete: "new-password",
662
+ placeholder: labels.passwordPlaceholder,
663
+ minLength: MIN_PASSWORD_LENGTH,
664
+ value: confirmPassword,
665
+ disabled: submitting,
666
+ onChange: setConfirmPassword,
667
+ showLabel: labels.showPassword,
668
+ hideLabel: labels.hidePassword
583
669
  }),
584
670
  errorNode,
585
671
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -615,23 +701,26 @@ function SignIn(props) {
615
701
  onChange: (event) => setEmail(event.target.value)
616
702
  })]
617
703
  }),
618
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
619
- className: "polyx-signin__field",
620
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
621
- className: "polyx-signin__label",
622
- children: labels.passwordLabel
623
- }), /* @__PURE__ */ (0, react_jsx_runtime.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
- })]
704
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PasswordField, {
705
+ id: "polyx-signin-password",
706
+ label: labels.passwordLabel,
707
+ name: "password",
708
+ autoComplete: "current-password",
709
+ placeholder: labels.passwordPlaceholder,
710
+ value: password,
711
+ disabled: submitting,
712
+ onChange: setPassword,
713
+ showLabel: labels.showPassword,
714
+ hideLabel: labels.hidePassword
634
715
  }),
716
+ props.forgotPasswordUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
717
+ className: "polyx-signin__forgot",
718
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
719
+ className: "polyx-signin__link",
720
+ href: props.forgotPasswordUrl,
721
+ children: labels.forgotPassword
722
+ })
723
+ }) : null,
635
724
  errorNode,
636
725
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
637
726
  type: "submit",
@@ -647,11 +736,12 @@ function SignIn(props) {
647
736
  if (variant === "page") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
648
737
  className: rootClass,
649
738
  "data-polyx-signin": step,
739
+ style: brandingStyle,
650
740
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
651
741
  className: "polyx-signin__panel",
652
- children: props.panel ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [props.logo ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
742
+ children: props.panel ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [brandingLogo ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
653
743
  className: "polyx-signin__panel-logo",
654
- children: props.logo
744
+ children: brandingLogo
655
745
  }) : null, labels.tagline ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
656
746
  className: "polyx-signin__tagline",
657
747
  children: labels.tagline
@@ -664,6 +754,7 @@ function SignIn(props) {
664
754
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
665
755
  className: rootClass,
666
756
  "data-polyx-signin": step,
757
+ style: brandingStyle,
667
758
  children: card
668
759
  });
669
760
  }
package/dist/index.d.cts CHANGED
@@ -201,8 +201,12 @@ interface SignInLabels {
201
201
  emailPlaceholder: string;
202
202
  passwordLabel: string;
203
203
  passwordPlaceholder: string;
204
+ /** Reveal-toggle a11y labels (aria-label / tooltip on the show-hide button). */
205
+ showPassword: string;
206
+ hidePassword: string;
204
207
  submit: string;
205
208
  submitting: string;
209
+ forgotPassword: string;
206
210
  chooseWorkspace: string;
207
211
  chooseWorkspaceSubheading: string;
208
212
  securedBy: string;
@@ -235,6 +239,11 @@ interface SignInProps {
235
239
  panel?: ReactNode;
236
240
  /** BFF endpoint backed by `createAuthHandlers().authenticate`. Default `/api/polyx/authenticate`. */
237
241
  action?: string;
242
+ /**
243
+ * URL of your reset flow (where `<ForgotPassword/>` lives). When set, a
244
+ * "Forgot password?" link appears under the password field. Omit to hide it.
245
+ */
246
+ forgotPasswordUrl?: string;
238
247
  /** Where to send the browser after success. Overrides the server's default redirect. */
239
248
  afterSignInPath?: string;
240
249
  /** Pre-scope the login to a single organization (skips org discovery). */
package/dist/index.d.mts CHANGED
@@ -201,8 +201,12 @@ interface SignInLabels {
201
201
  emailPlaceholder: string;
202
202
  passwordLabel: string;
203
203
  passwordPlaceholder: string;
204
+ /** Reveal-toggle a11y labels (aria-label / tooltip on the show-hide button). */
205
+ showPassword: string;
206
+ hidePassword: string;
204
207
  submit: string;
205
208
  submitting: string;
209
+ forgotPassword: string;
206
210
  chooseWorkspace: string;
207
211
  chooseWorkspaceSubheading: string;
208
212
  securedBy: string;
@@ -235,6 +239,11 @@ interface SignInProps {
235
239
  panel?: ReactNode;
236
240
  /** BFF endpoint backed by `createAuthHandlers().authenticate`. Default `/api/polyx/authenticate`. */
237
241
  action?: string;
242
+ /**
243
+ * URL of your reset flow (where `<ForgotPassword/>` lives). When set, a
244
+ * "Forgot password?" link appears under the password field. Omit to hide it.
245
+ */
246
+ forgotPasswordUrl?: string;
238
247
  /** Where to send the browser after success. Overrides the server's default redirect. */
239
248
  afterSignInPath?: string;
240
249
  /** Pre-scope the login to a single organization (skips org discovery). */
package/dist/index.mjs CHANGED
@@ -359,8 +359,11 @@ const DEFAULT_LABELS$2 = {
359
359
  emailPlaceholder: "you@company.com",
360
360
  passwordLabel: "Password",
361
361
  passwordPlaceholder: "••••••••",
362
+ showPassword: "Show password",
363
+ hidePassword: "Hide password",
362
364
  submit: "Sign in",
363
365
  submitting: "Signing in…",
366
+ forgotPassword: "Forgot password?",
364
367
  chooseWorkspace: "Choose a workspace",
365
368
  chooseWorkspaceSubheading: "You have access to more than one workspace.",
366
369
  securedBy: "Secured by PolyX",
@@ -378,6 +381,77 @@ const DEFAULT_LABELS$2 = {
378
381
  };
379
382
  /** Mirrors poly-auth's own floor (Joi: min 6). */
380
383
  const MIN_PASSWORD_LENGTH = 6;
384
+ function EyeIcon() {
385
+ return /* @__PURE__ */ jsxs("svg", {
386
+ viewBox: "0 0 24 24",
387
+ fill: "none",
388
+ stroke: "currentColor",
389
+ strokeWidth: "2",
390
+ strokeLinecap: "round",
391
+ strokeLinejoin: "round",
392
+ "aria-hidden": "true",
393
+ focusable: "false",
394
+ children: [/* @__PURE__ */ jsx("path", { d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" }), /* @__PURE__ */ jsx("circle", {
395
+ cx: "12",
396
+ cy: "12",
397
+ r: "3"
398
+ })]
399
+ });
400
+ }
401
+ function EyeOffIcon() {
402
+ return /* @__PURE__ */ jsxs("svg", {
403
+ viewBox: "0 0 24 24",
404
+ fill: "none",
405
+ stroke: "currentColor",
406
+ strokeWidth: "2",
407
+ strokeLinecap: "round",
408
+ strokeLinejoin: "round",
409
+ "aria-hidden": "true",
410
+ focusable: "false",
411
+ children: [/* @__PURE__ */ jsx("path", { d: "M9.88 4.24A9.1 9.1 0 0 1 12 4c6.5 0 10 7 10 7a13.2 13.2 0 0 1-1.67 2.68M6.61 6.61A13.5 13.5 0 0 0 2 12s3.5 7 10 7a9.7 9.7 0 0 0 5.39-1.61" }), /* @__PURE__ */ jsx("line", {
412
+ x1: "2",
413
+ y1: "2",
414
+ x2: "22",
415
+ y2: "22"
416
+ })]
417
+ });
418
+ }
419
+ /** A password input with an accessible show/hide reveal toggle (own visibility state). */
420
+ function PasswordField(props) {
421
+ const [visible, setVisible] = useState(false);
422
+ return /* @__PURE__ */ jsxs("div", {
423
+ className: "polyx-signin__field",
424
+ children: [/* @__PURE__ */ jsx("label", {
425
+ className: "polyx-signin__label",
426
+ htmlFor: props.id,
427
+ children: props.label
428
+ }), /* @__PURE__ */ jsxs("div", {
429
+ className: "polyx-signin__input-wrap",
430
+ children: [/* @__PURE__ */ jsx("input", {
431
+ id: props.id,
432
+ className: "polyx-signin__input polyx-signin__input--has-reveal",
433
+ type: visible ? "text" : "password",
434
+ name: props.name,
435
+ autoComplete: props.autoComplete,
436
+ placeholder: props.placeholder,
437
+ required: true,
438
+ minLength: props.minLength,
439
+ value: props.value,
440
+ disabled: props.disabled,
441
+ onChange: (event) => props.onChange(event.target.value)
442
+ }), /* @__PURE__ */ jsx("button", {
443
+ type: "button",
444
+ className: "polyx-signin__reveal",
445
+ "aria-pressed": visible,
446
+ "aria-label": visible ? props.hideLabel : props.showLabel,
447
+ title: visible ? props.hideLabel : props.showLabel,
448
+ disabled: props.disabled,
449
+ onClick: () => setVisible((value) => !value),
450
+ children: visible ? /* @__PURE__ */ jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsx(EyeIcon, {})
451
+ })]
452
+ })]
453
+ });
454
+ }
381
455
  /**
382
456
  * The embedded, Clerk-style sign-in form (D2b). Renders email/password inside the
383
457
  * consuming app and POSTs to the SDK's BFF (`@poly-x/next` `authenticate`), which
@@ -403,6 +477,29 @@ function SignIn(props) {
403
477
  const [tenants, setTenants] = useState(null);
404
478
  const [submitting, setSubmitting] = useState(false);
405
479
  const [error, setError] = useState(null);
480
+ const polyx = useContext(PolyXContext);
481
+ const [branding, setBranding] = useState({});
482
+ useEffect(() => {
483
+ if (!polyx) return;
484
+ let active = true;
485
+ polyx.authClient.fetchBranding(props.organizationCode ? { organizationCode: props.organizationCode } : {}).then((tokens) => {
486
+ if (active) setBranding(tokens);
487
+ });
488
+ return () => {
489
+ active = false;
490
+ };
491
+ }, [polyx, props.organizationCode]);
492
+ const brandingStyle = {};
493
+ if (branding.primaryColor) brandingStyle["--polyx-brand"] = branding.primaryColor;
494
+ if (branding.backgroundColor) {
495
+ brandingStyle["--polyx-bg"] = branding.backgroundColor;
496
+ brandingStyle["--polyx-page-bg"] = branding.backgroundColor;
497
+ }
498
+ const brandingLogo = props.logo ?? (branding.logoUrl ? /* @__PURE__ */ jsx("img", {
499
+ className: "polyx-signin__brand-logo",
500
+ src: branding.logoUrl,
501
+ alt: branding.displayName ?? ""
502
+ }) : void 0);
406
503
  /**
407
504
  * One round-trip to the BFF. `tenantId` picks a workspace; `passwords` completes
408
505
  * the temp-password flow (the BFF re-verifies the temporary password, sets the
@@ -472,8 +569,7 @@ function SignIn(props) {
472
569
  } });
473
570
  }
474
571
  const picking = tenants !== null;
475
- const hasPanel = variant === "page";
476
- const showHeaderLogo = Boolean(props.logo) && !hasPanel;
572
+ const showHeaderLogo = Boolean(brandingLogo) && !(variant === "page");
477
573
  const rootClass = [
478
574
  "polyx-signin",
479
575
  `polyx-signin--${variant}`,
@@ -499,7 +595,7 @@ function SignIn(props) {
499
595
  children: [
500
596
  showHeaderLogo ? /* @__PURE__ */ jsx("div", {
501
597
  className: "polyx-signin__logo",
502
- children: props.logo
598
+ children: brandingLogo
503
599
  }) : null,
504
600
  /* @__PURE__ */ jsx("h1", {
505
601
  className: "polyx-signin__heading",
@@ -544,41 +640,31 @@ function SignIn(props) {
544
640
  className: "polyx-signin__form",
545
641
  onSubmit: onSetPasswordSubmit,
546
642
  children: [
547
- /* @__PURE__ */ jsxs("label", {
548
- className: "polyx-signin__field",
549
- children: [/* @__PURE__ */ jsx("span", {
550
- className: "polyx-signin__label",
551
- children: labels.newPasswordLabel
552
- }), /* @__PURE__ */ jsx("input", {
553
- className: "polyx-signin__input",
554
- type: "password",
555
- name: "newPassword",
556
- autoComplete: "new-password",
557
- placeholder: labels.passwordPlaceholder,
558
- required: true,
559
- minLength: MIN_PASSWORD_LENGTH,
560
- value: newPassword,
561
- disabled: submitting,
562
- onChange: (event) => setNewPassword(event.target.value)
563
- })]
643
+ /* @__PURE__ */ jsx(PasswordField, {
644
+ id: "polyx-signin-new-password",
645
+ label: labels.newPasswordLabel,
646
+ name: "newPassword",
647
+ autoComplete: "new-password",
648
+ placeholder: labels.passwordPlaceholder,
649
+ minLength: MIN_PASSWORD_LENGTH,
650
+ value: newPassword,
651
+ disabled: submitting,
652
+ onChange: setNewPassword,
653
+ showLabel: labels.showPassword,
654
+ hideLabel: labels.hidePassword
564
655
  }),
565
- /* @__PURE__ */ jsxs("label", {
566
- className: "polyx-signin__field",
567
- children: [/* @__PURE__ */ jsx("span", {
568
- className: "polyx-signin__label",
569
- children: labels.confirmPasswordLabel
570
- }), /* @__PURE__ */ jsx("input", {
571
- className: "polyx-signin__input",
572
- type: "password",
573
- name: "confirmPassword",
574
- autoComplete: "new-password",
575
- placeholder: labels.passwordPlaceholder,
576
- required: true,
577
- minLength: MIN_PASSWORD_LENGTH,
578
- value: confirmPassword,
579
- disabled: submitting,
580
- onChange: (event) => setConfirmPassword(event.target.value)
581
- })]
656
+ /* @__PURE__ */ jsx(PasswordField, {
657
+ id: "polyx-signin-confirm-password",
658
+ label: labels.confirmPasswordLabel,
659
+ name: "confirmPassword",
660
+ autoComplete: "new-password",
661
+ placeholder: labels.passwordPlaceholder,
662
+ minLength: MIN_PASSWORD_LENGTH,
663
+ value: confirmPassword,
664
+ disabled: submitting,
665
+ onChange: setConfirmPassword,
666
+ showLabel: labels.showPassword,
667
+ hideLabel: labels.hidePassword
582
668
  }),
583
669
  errorNode,
584
670
  /* @__PURE__ */ jsx("button", {
@@ -614,23 +700,26 @@ function SignIn(props) {
614
700
  onChange: (event) => setEmail(event.target.value)
615
701
  })]
616
702
  }),
617
- /* @__PURE__ */ jsxs("label", {
618
- className: "polyx-signin__field",
619
- children: [/* @__PURE__ */ jsx("span", {
620
- className: "polyx-signin__label",
621
- children: labels.passwordLabel
622
- }), /* @__PURE__ */ jsx("input", {
623
- className: "polyx-signin__input",
624
- type: "password",
625
- name: "password",
626
- autoComplete: "current-password",
627
- placeholder: labels.passwordPlaceholder,
628
- required: true,
629
- value: password,
630
- disabled: submitting,
631
- onChange: (event) => setPassword(event.target.value)
632
- })]
703
+ /* @__PURE__ */ jsx(PasswordField, {
704
+ id: "polyx-signin-password",
705
+ label: labels.passwordLabel,
706
+ name: "password",
707
+ autoComplete: "current-password",
708
+ placeholder: labels.passwordPlaceholder,
709
+ value: password,
710
+ disabled: submitting,
711
+ onChange: setPassword,
712
+ showLabel: labels.showPassword,
713
+ hideLabel: labels.hidePassword
633
714
  }),
715
+ props.forgotPasswordUrl ? /* @__PURE__ */ jsx("div", {
716
+ className: "polyx-signin__forgot",
717
+ children: /* @__PURE__ */ jsx("a", {
718
+ className: "polyx-signin__link",
719
+ href: props.forgotPasswordUrl,
720
+ children: labels.forgotPassword
721
+ })
722
+ }) : null,
634
723
  errorNode,
635
724
  /* @__PURE__ */ jsx("button", {
636
725
  type: "submit",
@@ -646,11 +735,12 @@ function SignIn(props) {
646
735
  if (variant === "page") return /* @__PURE__ */ jsxs("div", {
647
736
  className: rootClass,
648
737
  "data-polyx-signin": step,
738
+ style: brandingStyle,
649
739
  children: [/* @__PURE__ */ jsx("aside", {
650
740
  className: "polyx-signin__panel",
651
- children: props.panel ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [props.logo ? /* @__PURE__ */ jsx("div", {
741
+ children: props.panel ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [brandingLogo ? /* @__PURE__ */ jsx("div", {
652
742
  className: "polyx-signin__panel-logo",
653
- children: props.logo
743
+ children: brandingLogo
654
744
  }) : null, labels.tagline ? /* @__PURE__ */ jsx("p", {
655
745
  className: "polyx-signin__tagline",
656
746
  children: labels.tagline
@@ -663,6 +753,7 @@ function SignIn(props) {
663
753
  return /* @__PURE__ */ jsx("div", {
664
754
  className: rootClass,
665
755
  "data-polyx-signin": step,
756
+ style: brandingStyle,
666
757
  children: card
667
758
  });
668
759
  }
package/dist/styles.css CHANGED
@@ -208,6 +208,13 @@
208
208
  margin-bottom: 0.75rem;
209
209
  }
210
210
 
211
+ /* Branding logo fetched from the app's tokens (FR-BRND). */
212
+ .polyx-signin__brand-logo {
213
+ max-height: 2.25rem;
214
+ width: auto;
215
+ object-fit: contain;
216
+ }
217
+
211
218
  .polyx-signin__heading {
212
219
  margin: 0;
213
220
  font-size: 1.375rem;
@@ -270,6 +277,56 @@
270
277
  cursor: not-allowed;
271
278
  }
272
279
 
280
+ /* --- Password reveal toggle --------------------------------------------- */
281
+
282
+ .polyx-signin__input-wrap {
283
+ position: relative;
284
+ display: flex;
285
+ align-items: center;
286
+ }
287
+
288
+ /* Leave room for the reveal button so masked text never slides under it. */
289
+ .polyx-signin__input--has-reveal {
290
+ padding-right: 2.75rem;
291
+ }
292
+
293
+ .polyx-signin__reveal {
294
+ position: absolute;
295
+ right: 0.25rem;
296
+ display: inline-flex;
297
+ align-items: center;
298
+ justify-content: center;
299
+ width: 2rem;
300
+ height: 2rem;
301
+ padding: 0;
302
+ color: var(--polyx-muted-fg);
303
+ background: transparent;
304
+ border: none;
305
+ border-radius: var(--polyx-radius-sm);
306
+ cursor: pointer;
307
+ transition: color 0.15s ease, box-shadow 0.15s ease;
308
+ }
309
+
310
+ .polyx-signin__reveal:hover:not(:disabled) {
311
+ color: var(--polyx-fg);
312
+ }
313
+
314
+ .polyx-signin__reveal:focus-visible {
315
+ outline: none;
316
+ color: var(--polyx-fg);
317
+ box-shadow: 0 0 0 3px var(--polyx-ring);
318
+ }
319
+
320
+ .polyx-signin__reveal:disabled {
321
+ cursor: not-allowed;
322
+ opacity: 0.6;
323
+ }
324
+
325
+ .polyx-signin__reveal svg {
326
+ width: 1.125rem;
327
+ height: 1.125rem;
328
+ }
329
+
273
330
  /* --- Buttons ------------------------------------------------------------- */
274
331
 
275
332
  .polyx-signin__submit,
@@ -339,6 +396,13 @@
339
396
  opacity: 0.85;
340
397
  }
341
398
 
399
+ /* "Forgot password?" link under the sign-in password field. */
400
+ .polyx-signin__forgot {
401
+ display: flex;
402
+ justify-content: flex-end;
403
+ margin-top: -0.5rem;
404
+ }
405
+
342
406
  /* --- Recovery confirmation states (ForgotPassword / ResetPassword) ------- */
343
407
 
344
408
  .polyx-signin__header--center {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poly-x/react",
3
- "version": "0.1.0-alpha.7",
3
+ "version": "0.1.0-alpha.9",
4
4
  "description": "PolyX SDK for React SPAs - provider, hooks, drop-in auth UI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,7 +27,7 @@
27
27
  "module": "./dist/index.mjs",
28
28
  "types": "./dist/index.d.cts",
29
29
  "dependencies": {
30
- "@poly-x/core": "0.1.0-alpha.7"
30
+ "@poly-x/core": "0.1.0-alpha.9"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "react": ">=18",