@authhero/widget 0.29.1 → 0.30.0

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/hydrate/index.js CHANGED
@@ -5343,6 +5343,38 @@ class AuthheroNode {
5343
5343
  }
5344
5344
  return null;
5345
5345
  }
5346
+ /**
5347
+ * Shared phone-input cleaning logic. Detects dial codes, strips non-phone
5348
+ * characters, updates the input value, and emits the full number.
5349
+ * @param allowPlus When true, uses a two-pass clean that first keeps '+'
5350
+ * then strips it (for combined tel+email fields). When false, strips '+'
5351
+ * in a single pass (phone-only fields where the picker provides the prefix).
5352
+ */
5353
+ processPhoneInput(target, value, allowPlus) {
5354
+ const dialLocal = this.detectDialCodeFromInput(value);
5355
+ if (dialLocal !== null) {
5356
+ const cleanedLocal = allowPlus
5357
+ ? dialLocal.replace(/[^+\d\s\-()]/g, "").replace(/\+/g, "")
5358
+ : dialLocal.replace(/[^\d\s\-()]/g, "");
5359
+ target.value = cleanedLocal;
5360
+ this.localPhoneNumber = cleanedLocal;
5361
+ const fullNumber = `${this.selectedCountry.dialCode}${cleanedLocal}`;
5362
+ this.fieldChange.emit({ id: this.component.id, value: fullNumber });
5363
+ }
5364
+ else {
5365
+ const cleaned = allowPlus
5366
+ ? value.replace(/[^+\d\s\-()]/g, "").replace(/\+/g, "")
5367
+ : value.replace(/[^\d\s\-()]/g, "");
5368
+ if (cleaned !== value) {
5369
+ target.value = cleaned;
5370
+ }
5371
+ this.localPhoneNumber = cleaned;
5372
+ const fullNumber = cleaned
5373
+ ? `${this.selectedCountry.dialCode}${cleaned}`
5374
+ : "";
5375
+ this.fieldChange.emit({ id: this.component.id, value: fullNumber });
5376
+ }
5377
+ }
5346
5378
  handlePhoneInput = (e) => {
5347
5379
  const target = e.target;
5348
5380
  const value = target.value;
@@ -5355,31 +5387,7 @@ class AuthheroNode {
5355
5387
  const looksLikePhone = value.length > 0 && /^[+\d]/.test(value) && !value.includes("@");
5356
5388
  this.telEmailMode = !looksLikePhone;
5357
5389
  if (!this.telEmailMode) {
5358
- // Phone mode — first try dial code detection before stripping '+'
5359
- // so that typing +46 or 0046 can match a country
5360
- const dialLocal = this.detectDialCodeFromInput(value);
5361
- if (dialLocal !== null) {
5362
- // Dial code matched — strip it from the input and show only local part
5363
- const cleanedLocal = dialLocal
5364
- .replace(/[^+\d\s\-()]/g, "")
5365
- .replace(/\+/g, "");
5366
- target.value = cleanedLocal;
5367
- this.localPhoneNumber = cleanedLocal;
5368
- const fullNumber = `${this.selectedCountry.dialCode}${cleanedLocal}`;
5369
- this.fieldChange.emit({ id: this.component.id, value: fullNumber });
5370
- }
5371
- else {
5372
- // No dial code — strip non-phone chars and '+' (picker provides the prefix)
5373
- const cleaned = value.replace(/[^+\d\s\-()]/g, "").replace(/\+/g, "");
5374
- if (cleaned !== value) {
5375
- target.value = cleaned;
5376
- }
5377
- this.localPhoneNumber = cleaned;
5378
- const fullNumber = cleaned
5379
- ? `${this.selectedCountry.dialCode}${cleaned}`
5380
- : "";
5381
- this.fieldChange.emit({ id: this.component.id, value: fullNumber });
5382
- }
5390
+ this.processPhoneInput(target, value, true);
5383
5391
  }
5384
5392
  else {
5385
5393
  // Email or text — emit as-is
@@ -5388,16 +5396,8 @@ class AuthheroNode {
5388
5396
  }
5389
5397
  return;
5390
5398
  }
5391
- // Standard phone-only mode — strip '+' since the picker provides the prefix
5392
- const cleaned = value.replace(/[^\d\s\-()]/g, "");
5393
- if (cleaned !== value) {
5394
- target.value = cleaned;
5395
- }
5396
- this.localPhoneNumber = cleaned;
5397
- const fullNumber = cleaned
5398
- ? `${this.selectedCountry.dialCode}${cleaned}`
5399
- : "";
5400
- this.fieldChange.emit({ id: this.component.id, value: fullNumber });
5399
+ // Standard phone-only mode
5400
+ this.processPhoneInput(target, value, false);
5401
5401
  };
5402
5402
  handleInput = (e) => {
5403
5403
  const target = e.target;
@@ -5557,20 +5557,21 @@ class AuthheroNode {
5557
5557
  renderTextField(component) {
5558
5558
  const inputId = `input-${component.id}`;
5559
5559
  const errors = this.getErrors();
5560
- const { multiline, max_length } = component.config ?? {};
5560
+ const { multiline, max_length, autocomplete } = component.config ?? {};
5561
5561
  const effectiveValue = this.getEffectiveValue();
5562
5562
  const hasValue = !!(effectiveValue && effectiveValue.length > 0);
5563
5563
  if (multiline) {
5564
5564
  return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), hAsync("textarea", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input textarea", name: component.id, placeholder: " ", required: component.required, disabled: this.disabled, maxLength: max_length, onInput: this.handleInput }, effectiveValue ?? ""), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
5565
5565
  }
5566
- return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container" }, hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: component.sensitive ? "password" : "text", name: component.id, "data-input-name": component.id, value: effectiveValue ?? "", placeholder: " ", required: component.required, disabled: this.disabled, maxLength: max_length, onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
5566
+ return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container" }, hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: component.sensitive ? "password" : "text", name: component.id, "data-input-name": component.id, value: effectiveValue ?? "", placeholder: " ", required: component.required, disabled: this.disabled, maxLength: max_length, autoComplete: autocomplete, onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
5567
5567
  }
5568
5568
  renderEmailField(component) {
5569
5569
  const inputId = `input-${component.id}`;
5570
5570
  const errors = this.getErrors();
5571
5571
  const effectiveValue = this.getEffectiveValue();
5572
5572
  const hasValue = !!(effectiveValue && effectiveValue.length > 0);
5573
- return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container" }, hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: "email", name: component.id, "data-input-name": component.id, value: effectiveValue ?? "", placeholder: " ", required: component.required, disabled: this.disabled, autocomplete: "email", onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
5573
+ const { autocomplete } = (component.config ?? {});
5574
+ return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container" }, hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: "email", name: component.id, "data-input-name": component.id, value: effectiveValue ?? "", placeholder: " ", required: component.required, disabled: this.disabled, autocomplete: autocomplete || "email", onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
5574
5575
  }
5575
5576
  renderPasswordField(component) {
5576
5577
  const inputId = `input-${component.id}`;
@@ -6435,6 +6436,11 @@ class AuthheroWidget {
6435
6436
  * Form data collected from inputs.
6436
6437
  */
6437
6438
  formData = {};
6439
+ /**
6440
+ * AbortController for an in-flight conditional mediation request.
6441
+ * Aborted on screen change or component disconnect.
6442
+ */
6443
+ conditionalMediationAbort;
6438
6444
  /**
6439
6445
  * Emitted when the form is submitted.
6440
6446
  * The consuming application should handle the submission unless autoSubmit is true.
@@ -6469,6 +6475,9 @@ class AuthheroWidget {
6469
6475
  */
6470
6476
  screenChange;
6471
6477
  watchScreen(newValue) {
6478
+ // Abort any in-flight conditional mediation when screen changes
6479
+ this.conditionalMediationAbort?.abort();
6480
+ this.conditionalMediationAbort = undefined;
6472
6481
  if (typeof newValue === "string") {
6473
6482
  try {
6474
6483
  this._screen = JSON.parse(newValue);
@@ -6702,6 +6711,8 @@ class AuthheroWidget {
6702
6711
  }
6703
6712
  disconnectedCallback() {
6704
6713
  window.removeEventListener("popstate", this.handlePopState);
6714
+ this.conditionalMediationAbort?.abort();
6715
+ this.conditionalMediationAbort = undefined;
6705
6716
  }
6706
6717
  async componentWillLoad() {
6707
6718
  // Parse initial props - this prevents unnecessary state changes during hydration that cause flashes
@@ -6790,6 +6801,10 @@ class AuthheroWidget {
6790
6801
  this.updateDataScreenAttribute();
6791
6802
  this.persistState();
6792
6803
  this.focusFirstInput();
6804
+ // Start WebAuthn ceremony if returned with the screen (e.g. conditional mediation)
6805
+ if (data.ceremony) {
6806
+ this.performWebAuthnCeremony(data.ceremony);
6807
+ }
6793
6808
  return true;
6794
6809
  }
6795
6810
  }
@@ -6981,9 +6996,19 @@ class AuthheroWidget {
6981
6996
  console.error("Invalid WebAuthn ceremony payload", ceremony);
6982
6997
  return;
6983
6998
  }
6999
+ if (ceremony.type === "webauthn-authentication-conditional") {
7000
+ // Conditional mediation runs in the background, no requestAnimationFrame needed
7001
+ this.executeWebAuthnConditionalMediation(ceremony);
7002
+ return;
7003
+ }
6984
7004
  requestAnimationFrame(() => {
6985
7005
  this.overrideFormSubmit();
6986
- this.executeWebAuthnRegistration(ceremony);
7006
+ if (ceremony.type === "webauthn-authentication") {
7007
+ this.executeWebAuthnAuthentication(ceremony);
7008
+ }
7009
+ else {
7010
+ this.executeWebAuthnRegistration(ceremony);
7011
+ }
6987
7012
  });
6988
7013
  }
6989
7014
  /**
@@ -6994,8 +7019,6 @@ class AuthheroWidget {
6994
7019
  if (typeof data !== "object" || data === null)
6995
7020
  return false;
6996
7021
  const obj = data;
6997
- if (obj.type !== "webauthn-registration")
6998
- return false;
6999
7022
  if (typeof obj.successAction !== "string")
7000
7023
  return false;
7001
7024
  const opts = obj.options;
@@ -7004,23 +7027,30 @@ class AuthheroWidget {
7004
7027
  const o = opts;
7005
7028
  if (typeof o.challenge !== "string")
7006
7029
  return false;
7007
- const rp = o.rp;
7008
- if (typeof rp !== "object" || rp === null)
7009
- return false;
7010
- if (typeof rp.id !== "string" ||
7011
- typeof rp.name !== "string")
7012
- return false;
7013
- const user = o.user;
7014
- if (typeof user !== "object" || user === null)
7015
- return false;
7016
- const u = user;
7017
- if (typeof u.id !== "string" ||
7018
- typeof u.name !== "string" ||
7019
- typeof u.displayName !== "string")
7020
- return false;
7021
- if (!Array.isArray(o.pubKeyCredParams))
7022
- return false;
7023
- return true;
7030
+ if (obj.type === "webauthn-registration") {
7031
+ const rp = o.rp;
7032
+ if (typeof rp !== "object" || rp === null)
7033
+ return false;
7034
+ if (typeof rp.id !== "string" ||
7035
+ typeof rp.name !== "string")
7036
+ return false;
7037
+ const user = o.user;
7038
+ if (typeof user !== "object" || user === null)
7039
+ return false;
7040
+ const u = user;
7041
+ if (typeof u.id !== "string" ||
7042
+ typeof u.name !== "string" ||
7043
+ typeof u.displayName !== "string")
7044
+ return false;
7045
+ if (!Array.isArray(o.pubKeyCredParams))
7046
+ return false;
7047
+ return true;
7048
+ }
7049
+ if (obj.type === "webauthn-authentication" ||
7050
+ obj.type === "webauthn-authentication-conditional") {
7051
+ return true;
7052
+ }
7053
+ return false;
7024
7054
  }
7025
7055
  /**
7026
7056
  * Perform the WebAuthn navigator.credentials.create() ceremony and submit
@@ -7133,6 +7163,193 @@ class AuthheroWidget {
7133
7163
  }
7134
7164
  }
7135
7165
  }
7166
+ /**
7167
+ * Perform the WebAuthn navigator.credentials.get() ceremony (explicit modal)
7168
+ * and submit the credential result via the form.
7169
+ */
7170
+ async executeWebAuthnAuthentication(ceremony) {
7171
+ const opts = ceremony.options;
7172
+ const b64uToBuf = (s) => {
7173
+ s = s.replace(/-/g, "+").replace(/_/g, "/");
7174
+ while (s.length % 4)
7175
+ s += "=";
7176
+ const b = atob(s);
7177
+ const a = new Uint8Array(b.length);
7178
+ for (let i = 0; i < b.length; i++)
7179
+ a[i] = b.charCodeAt(i);
7180
+ return a.buffer;
7181
+ };
7182
+ const bufToB64u = (b) => {
7183
+ const a = new Uint8Array(b);
7184
+ let s = "";
7185
+ for (let i = 0; i < a.length; i++)
7186
+ s += String.fromCharCode(a[i]);
7187
+ return btoa(s)
7188
+ .replace(/\+/g, "-")
7189
+ .replace(/\//g, "_")
7190
+ .replace(/=+$/, "");
7191
+ };
7192
+ const findForm = () => {
7193
+ const shadowRoot = this.el?.shadowRoot;
7194
+ if (shadowRoot) {
7195
+ const f = shadowRoot.querySelector("form");
7196
+ if (f)
7197
+ return f;
7198
+ }
7199
+ return document.querySelector("form");
7200
+ };
7201
+ try {
7202
+ const publicKey = {
7203
+ challenge: b64uToBuf(opts.challenge),
7204
+ rpId: opts.rpId,
7205
+ timeout: opts.timeout,
7206
+ userVerification: opts.userVerification || "preferred",
7207
+ };
7208
+ if (opts.allowCredentials?.length) {
7209
+ publicKey.allowCredentials = opts.allowCredentials.map((c) => ({
7210
+ id: b64uToBuf(c.id),
7211
+ type: c.type,
7212
+ transports: (c.transports || []),
7213
+ }));
7214
+ }
7215
+ const cred = (await navigator.credentials.get({
7216
+ publicKey,
7217
+ }));
7218
+ const response = cred.response;
7219
+ const resp = {
7220
+ id: cred.id,
7221
+ rawId: bufToB64u(cred.rawId),
7222
+ type: cred.type,
7223
+ response: {
7224
+ authenticatorData: bufToB64u(response.authenticatorData),
7225
+ clientDataJSON: bufToB64u(response.clientDataJSON),
7226
+ signature: bufToB64u(response.signature),
7227
+ },
7228
+ clientExtensionResults: cred.getClientExtensionResults(),
7229
+ authenticatorAttachment: cred.authenticatorAttachment || undefined,
7230
+ };
7231
+ if (response.userHandle) {
7232
+ resp.response.userHandle = bufToB64u(response.userHandle);
7233
+ }
7234
+ const form = findForm();
7235
+ if (form) {
7236
+ const cf = form.querySelector('[name="credential-field"]') ||
7237
+ form.querySelector("#credential-field");
7238
+ const af = form.querySelector('[name="action-field"]') ||
7239
+ form.querySelector("#action-field");
7240
+ if (cf)
7241
+ cf.value = JSON.stringify(resp);
7242
+ if (af)
7243
+ af.value = ceremony.successAction;
7244
+ form.submit();
7245
+ }
7246
+ }
7247
+ catch (e) {
7248
+ console.error("WebAuthn authentication error:", e);
7249
+ const form = findForm();
7250
+ if (form) {
7251
+ const af = form.querySelector('[name="action-field"]') ||
7252
+ form.querySelector("#action-field");
7253
+ if (af)
7254
+ af.value = "error";
7255
+ form.submit();
7256
+ }
7257
+ }
7258
+ }
7259
+ /**
7260
+ * Execute WebAuthn conditional mediation (autofill-assisted passkeys).
7261
+ * Runs in the background — the browser shows passkey suggestions in the
7262
+ * username field's autofill dropdown. Silently ignored if unsupported.
7263
+ */
7264
+ async executeWebAuthnConditionalMediation(ceremony) {
7265
+ // Feature detection
7266
+ if (!window.PublicKeyCredential ||
7267
+ !PublicKeyCredential.isConditionalMediationAvailable) {
7268
+ return;
7269
+ }
7270
+ const available = await PublicKeyCredential.isConditionalMediationAvailable();
7271
+ if (!available)
7272
+ return;
7273
+ // Abort any previous conditional mediation request
7274
+ this.conditionalMediationAbort?.abort();
7275
+ const abortController = new AbortController();
7276
+ this.conditionalMediationAbort = abortController;
7277
+ const opts = ceremony.options;
7278
+ const b64uToBuf = (s) => {
7279
+ s = s.replace(/-/g, "+").replace(/_/g, "/");
7280
+ while (s.length % 4)
7281
+ s += "=";
7282
+ const b = atob(s);
7283
+ const a = new Uint8Array(b.length);
7284
+ for (let i = 0; i < b.length; i++)
7285
+ a[i] = b.charCodeAt(i);
7286
+ return a.buffer;
7287
+ };
7288
+ const bufToB64u = (b) => {
7289
+ const a = new Uint8Array(b);
7290
+ let s = "";
7291
+ for (let i = 0; i < a.length; i++)
7292
+ s += String.fromCharCode(a[i]);
7293
+ return btoa(s)
7294
+ .replace(/\+/g, "-")
7295
+ .replace(/\//g, "_")
7296
+ .replace(/=+$/, "");
7297
+ };
7298
+ try {
7299
+ const cred = (await navigator.credentials.get({
7300
+ mediation: "conditional",
7301
+ signal: abortController.signal,
7302
+ publicKey: {
7303
+ challenge: b64uToBuf(opts.challenge),
7304
+ rpId: opts.rpId,
7305
+ timeout: opts.timeout,
7306
+ userVerification: opts.userVerification ||
7307
+ "preferred",
7308
+ },
7309
+ }));
7310
+ const response = cred.response;
7311
+ const resp = {
7312
+ id: cred.id,
7313
+ rawId: bufToB64u(cred.rawId),
7314
+ type: cred.type,
7315
+ response: {
7316
+ authenticatorData: bufToB64u(response.authenticatorData),
7317
+ clientDataJSON: bufToB64u(response.clientDataJSON),
7318
+ signature: bufToB64u(response.signature),
7319
+ },
7320
+ clientExtensionResults: cred.getClientExtensionResults(),
7321
+ authenticatorAttachment: cred.authenticatorAttachment || undefined,
7322
+ };
7323
+ if (response.userHandle) {
7324
+ resp.response.userHandle = bufToB64u(response.userHandle);
7325
+ }
7326
+ // Submit via the widget's form handling
7327
+ this.formData["credential-field"] = JSON.stringify(resp);
7328
+ this.formData["action-field"] = ceremony.successAction;
7329
+ // Ensure form submit override is set up, then submit
7330
+ this.overrideFormSubmit();
7331
+ const shadowRoot = this.el?.shadowRoot;
7332
+ const form = shadowRoot?.querySelector("form");
7333
+ if (form) {
7334
+ // Set the hidden input values directly
7335
+ const cf = form.querySelector('[name="credential-field"]') ||
7336
+ form.querySelector("#credential-field");
7337
+ const af = form.querySelector('[name="action-field"]') ||
7338
+ form.querySelector("#action-field");
7339
+ if (cf)
7340
+ cf.value = JSON.stringify(resp);
7341
+ if (af)
7342
+ af.value = ceremony.successAction;
7343
+ form.submit();
7344
+ }
7345
+ }
7346
+ catch (e) {
7347
+ // Silently ignore AbortError and NotAllowedError
7348
+ if (e?.name === "AbortError" || e?.name === "NotAllowedError")
7349
+ return;
7350
+ console.error("Conditional mediation error:", e);
7351
+ }
7352
+ }
7136
7353
  handleButtonClick = (detail) => {
7137
7354
  // If this is a submit button click, trigger form submission
7138
7355
  if (detail.type === "submit") {
@@ -7374,7 +7591,7 @@ class AuthheroWidget {
7374
7591
  };
7375
7592
  // Get logo URL from theme.widget (takes precedence) or branding
7376
7593
  const logoUrl = this._theme?.widget?.logo_url || this._branding?.logo_url;
7377
- return (hAsync("div", { class: "widget-container", part: "container", "data-authstack-container": true }, hAsync("header", { class: "widget-header", part: "header" }, logoUrl && (hAsync("div", { class: "logo-wrapper", part: "logo-wrapper" }, hAsync("img", { class: "logo", part: "logo", src: logoUrl, alt: "Logo" }))), screen.title && (hAsync("h1", { class: "title", part: "title", innerHTML: sanitizeHtml(screen.title) })), screen.description && (hAsync("p", { class: "description", part: "description", innerHTML: sanitizeHtml(screen.description) }))), hAsync("div", { class: "widget-body", part: "body" }, screenErrors.map((err) => (hAsync("div", { class: "message message-error", part: "message message-error", key: err.id ?? err.text }, err.text))), screenSuccesses.map((msg) => (hAsync("div", { class: "message message-success", part: "message message-success", key: msg.id ?? msg.text }, msg.text))), hAsync("form", { onSubmit: this.handleSubmit, part: "form" }, hiddenComponents.map((c) => (hAsync("input", { type: "hidden", name: c.id, id: c.id, key: c.id, value: this.formData[c.id] || "" }))), hAsync("div", { class: "form-content" }, socialComponents.length > 0 && (hAsync("div", { class: "social-section", part: "social-section" }, socialComponents.map((component) => (hAsync("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading, exportparts: getExportParts(component) }))))), socialComponents.length > 0 &&
7594
+ return (hAsync("div", { class: "widget-container", part: "container", "data-authstack-container": true }, hAsync("header", { class: "widget-header", part: "header" }, logoUrl && (hAsync("div", { class: "logo-wrapper", part: "logo-wrapper" }, hAsync("img", { class: "logo", part: "logo", src: logoUrl, alt: "Logo" }))), screen.title && (hAsync("h1", { class: "title", part: "title", innerHTML: sanitizeHtml(screen.title) })), screen.description && (hAsync("p", { class: "description", part: "description", innerHTML: sanitizeHtml(screen.description) }))), hAsync("div", { class: "widget-body", part: "body" }, screenErrors.map((err) => (hAsync("div", { class: "message message-error", part: "message message-error", key: err.id ?? err.text }, err.text))), screenSuccesses.map((msg) => (hAsync("div", { class: "message message-success", part: "message message-success", key: msg.id ?? msg.text }, msg.text))), hAsync("form", { onSubmit: this.handleSubmit, action: screen.action, method: screen.method || "POST", part: "form" }, hiddenComponents.map((c) => (hAsync("input", { type: "hidden", name: c.id, id: c.id, key: c.id, value: this.formData[c.id] || "" }))), hAsync("div", { class: "form-content" }, socialComponents.length > 0 && (hAsync("div", { class: "social-section", part: "social-section" }, socialComponents.map((component) => (hAsync("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading, exportparts: getExportParts(component) }))))), socialComponents.length > 0 &&
7378
7595
  fieldComponents.length > 0 &&
7379
7596
  hasDivider && (hAsync("div", { class: "divider", part: "divider" }, hAsync("span", { class: "divider-text" }, dividerText))), hAsync("div", { class: "fields-section", part: "fields-section" }, fieldComponents.map((component) => (hAsync("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading })))))), screen.links && screen.links.length > 0 && (hAsync("div", { class: "links", part: "links" }, screen.links.map((link) => (hAsync("span", { class: "link-wrapper", part: "link-wrapper", key: link.id ?? link.href }, link.linkText ? (hAsync("span", null, link.text, " ", hAsync("a", { href: link.href, class: "link", part: "link", onClick: (e) => this.handleLinkClick(e, {
7380
7597
  id: link.id,