@authhero/widget 0.13.3 → 0.15.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/dist/authhero-widget/authhero-widget.esm.js +1 -1
- package/dist/authhero-widget/index.esm.js +1 -1
- package/dist/authhero-widget/p-a4fd5f18.entry.js +1 -0
- package/dist/authhero-widget/p-df99a4ad.entry.js +1 -0
- package/dist/cjs/authhero-node.cjs.entry.js +53 -15
- package/dist/cjs/authhero-widget.cjs.entry.js +36 -9
- package/dist/cjs/index.cjs.js +40 -22
- package/dist/collection/components/authhero-node/authhero-node.css +20 -4
- package/dist/collection/components/authhero-node/authhero-node.js +53 -15
- package/dist/collection/components/authhero-widget/authhero-widget.css +3 -2
- package/dist/collection/components/authhero-widget/authhero-widget.js +37 -10
- package/dist/components/authhero-node.js +1 -1
- package/dist/components/authhero-widget.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/p-CA--5Mag.js +1 -0
- package/dist/esm/authhero-node.entry.js +53 -15
- package/dist/esm/authhero-widget.entry.js +36 -9
- package/dist/esm/index.js +40 -22
- package/dist/types/components/authhero-node/authhero-node.d.ts +10 -0
- package/dist/types/components/authhero-widget/authhero-widget.d.ts +6 -0
- package/hydrate/index.js +89 -24
- package/hydrate/index.mjs +89 -24
- package/package.json +2 -2
- package/dist/authhero-widget/p-1107d60e.entry.js +0 -1
- package/dist/authhero-widget/p-b2d3d319.entry.js +0 -1
- package/dist/components/p-CWVARG2s.js +0 -1
package/hydrate/index.js
CHANGED
|
@@ -4947,7 +4947,7 @@ var setScopedSSR = (opts) => {
|
|
|
4947
4947
|
var needsScopedSSR = () => scopedSSR;
|
|
4948
4948
|
var scopedSSR = false;
|
|
4949
4949
|
|
|
4950
|
-
const authheroNodeCss = () => `:host{display:block}.input-wrapper{display:flex;flex-direction:column;position:relative;margin-bottom:16px}.input-container{position:relative;width:100%}.input-label{position:absolute;left:16px;top:50%;transform:translateY(-50%);font-size:16px;font-weight:var(--ah-font-weight-label, 400);color:var(--ah-color-text-muted, #65676e);pointer-events:none;transition:all 0.15s ease-out;background-color:transparent;padding:0;z-index:1}.input-label.floating,.input-field:focus+.input-label,.input-field:not(:placeholder-shown)+.input-label{top:-8px;transform:translateY(0);font-size:12px;background-color:var(--ah-color-bg, #ffffff);padding:0 4px;left:12px;color:var(--ah-color-text-muted, #65676e)}.input-field:focus+.input-label{color:var(--ah-color-primary, #635dff)}.required{color:var(--ah-color-error, #d03c38);margin-left:2px}.input-field{width:100%;padding:16px;font-size:16px;font-family:inherit;color:var(--ah-color-text, #1e212a);background-color:var(--ah-color-input-bg, #ffffff);border:1px solid var(--ah-color-border, #c9cace);border-radius:var(--ah-input-radius, 3px);outline:none;transition:border-color 0.15s ease-out,\\n box-shadow 0.15s ease-out;box-sizing:border-box}.input-field::placeholder{color:transparent}.input-field:hover{border-color:var(--ah-color-border-hover, #65676e)}.input-field:focus{border-color:var(--ah-color-primary, #635dff);box-shadow:inset 0 0 0 1px var(--ah-color-primary, #635dff)}.input-field.has-error{border-color:var(--ah-color-error, #d03c38)}.input-field.has-error:focus{box-shadow:inset 0 0 0 1px var(--ah-color-error, #d03c38)}.input-field:disabled{background-color:var(--ah-color-bg-disabled, #f5f5f5);border-color:var(--ah-color-border-disabled, #e0e1e3);cursor:not-allowed;opacity:0.7}.password-container{position:relative;display:flex;align-items:center}.password-container .input-field{padding-right:48px}.password-toggle{position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--ah-color-text-muted, #65676e);transition:color 0.15s ease}.password-toggle:hover{color:var(--ah-color-text, #1e212a)}.password-toggle svg{width:20px;height:20px}.error-text{font-size:12px;color:var(--ah-color-error, #d03c38);margin-top:4px;line-height:1.4}.helper-text{font-size:12px;color:var(--ah-color-text-muted, #65676e);margin-top:4px;line-height:1.4}.field-link{display:block;text-align:left;margin-top:8px;margin-bottom:16px}.field-link a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400)}.field-link a:hover{text-decoration:underline}.checkbox-wrapper{display:flex;align-items:flex-start;gap:10px;cursor:pointer;margin-bottom:16px}.checkbox-wrapper input[type="checkbox"]{width:18px;height:18px;margin:0;accent-color:var(--ah-color-primary, #635dff);cursor:pointer;flex-shrink:0}.checkbox-label{font-size:14px;color:var(--ah-color-text, #1e212a);line-height:1.5}.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:14px 20px;font-size:16px;font-weight:var(--ah-font-weight-btn, 400);font-family:inherit;line-height:1.25;text-align:center;text-decoration:none;border:none;border-radius:var(--ah-btn-radius, 3px);cursor:pointer;transition:background-color 0.15s ease,\\n border-color 0.15s ease,\\n transform 0.1s ease;box-sizing:border-box}.btn:disabled{opacity:0.6;cursor:not-allowed}.btn:not(:disabled):active{transform:scale(0.98)}.btn:focus-visible{outline:2px solid var(--ah-color-primary, #635dff);outline-offset:2px}.btn-primary{background-color:var(--ah-color-primary, #635dff);color:var(--ah-color-text-on-primary, #ffffff);margin-top:12px}.btn-primary:not(:disabled):hover{filter:brightness(0.85)}.btn-secondary{background-color:var(--ah-color-bg, #ffffff);color:var(--ah-color-text, #1e212a);border:1px solid var(--ah-color-border, #c9cace)}.btn-secondary:not(:disabled):hover{background-color:var(--ah-color-bg-hover, #f5f5f5);border-color:var(--ah-color-border-hover, #65676e)}.btn-link{background:none;border:none;color:var(--ah-color-link, #635dff);padding:8px 0;font-weight:var(--ah-font-weight-link, 400);text-decoration:none}.btn-link:hover{text-decoration:underline}.social-buttons{display:flex;flex-direction:column;gap:12px}.btn-social{display:flex;align-items:center;justify-content:center;gap:12px}.btn-social-content{display:flex;flex-direction:column;align-items:center;text-align:center}.btn-social-subtitle{font-size:12px;font-style:italic;opacity:0.8;margin-top:2px}.btn-social-subtitle:empty{display:none}.social-icon{width:20px;height:20px;flex-shrink:0}@media (max-width: 480px){.social-buttons:has(.btn-social:nth-child(3)){flex-direction:row;flex-wrap:nowrap;justify-content:stretch;gap:8px}.social-buttons:has(.btn-social:nth-child(3)) .btn-social{width:auto;min-width:0;padding:12px;flex:1 1 0}.social-buttons:has(.btn-social:nth-child(3)) .btn-social span{display:none}.social-buttons:has(.btn-social:nth-child(3)) .social-icon{width:24px;height:24px}}.btn-icon{width:20px;height:20px;flex-shrink:0}.btn-icon img{width:100%;height:100%;object-fit:contain}.text-title{font-size:20px;font-weight:400;color:var(--ah-color-text, #1e212a);margin:8px 0;line-height:1.3}.text-title.text-success{color:var(--ah-color-success, #13a769)}.text-description{font-size:14px;color:var(--ah-color-text-muted, #65676e);margin:4px 0;line-height:1.5}.image{display:block;max-width:100%;height:auto;border-radius:4px}.image-centered{margin:0 auto 16px;width:52px;height:52px;object-fit:contain}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;transition:color 0.15s ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.node-error{padding:12px 16px;background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38);border-radius:0;font-size:14px;margin-bottom:16px}.node-success{padding:12px 16px;background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769);border-radius:0;font-size:14px;margin-bottom:16px}.divider{display:flex;align-items:center;text-align:center;margin:16px 0}.divider::before,.divider::after{content:"";flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0.5px}.rich-text{font-size:14px;line-height:1.5;color:var(--ah-color-text, #1e212a)}.rich-text a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);transition:color 0.15s ease}.rich-text a:hover{text-decoration:underline}.rich-text .terms-text{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.rich-text .terms-text a{font-size:12px}.rich-text .signup-link{margin-top:16px;text-align:center;font-size:14px;color:var(--ah-color-text, #1e212a)}`;
|
|
4950
|
+
const authheroNodeCss = () => `:host{display:block}.input-wrapper{display:flex;flex-direction:column;position:relative;margin-bottom:16px}.input-container{position:relative;width:100%}.input-label{position:absolute;left:16px;top:50%;transform:translateY(-50%);font-size:16px;font-weight:var(--ah-font-weight-label, 400);color:var(--ah-color-text-muted, #65676e);pointer-events:none;transition:all 0.15s ease-out;background-color:transparent;padding:0;z-index:1}.input-label.floating,.input-field:focus+.input-label,.input-field:not(:placeholder-shown)+.input-label,select.input-field+.input-label,input[type="date"].input-field+.input-label{top:-8px;transform:translateY(0);font-size:12px;background-color:var(--ah-color-bg, #ffffff);padding:0 4px;left:12px;color:var(--ah-color-text-muted, #65676e)}.input-field:focus+.input-label{color:var(--ah-color-primary, #635dff)}.required{color:var(--ah-color-error, #d03c38);margin-left:2px}.input-field{width:100%;padding:16px;font-size:16px;font-family:inherit;color:var(--ah-color-text, #1e212a);background-color:var(--ah-color-input-bg, #ffffff);border:1px solid var(--ah-color-border, #c9cace);border-radius:var(--ah-input-radius, 3px);outline:none;transition:border-color 0.15s ease-out,\\n box-shadow 0.15s ease-out;box-sizing:border-box}.input-field::placeholder{color:transparent}.input-field:hover{border-color:var(--ah-color-border-hover, #65676e)}.input-field:focus{border-color:var(--ah-color-primary, #635dff);box-shadow:inset 0 0 0 1px var(--ah-color-primary, #635dff)}.input-field.has-error{border-color:var(--ah-color-error, #d03c38)}.input-field.has-error:focus{box-shadow:inset 0 0 0 1px var(--ah-color-error, #d03c38)}.input-field:disabled{background-color:var(--ah-color-bg-disabled, #f5f5f5);border-color:var(--ah-color-border-disabled, #e0e1e3);cursor:not-allowed;opacity:0.7}.password-container{position:relative;display:flex;align-items:center}.password-container .input-field{padding-right:48px}.password-toggle{position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--ah-color-text-muted, #65676e);transition:color 0.15s ease}.password-toggle:hover{color:var(--ah-color-text, #1e212a)}.password-toggle svg{width:20px;height:20px}.error-text{font-size:12px;color:var(--ah-color-error, #d03c38);margin-top:4px;line-height:1.4}.helper-text{font-size:12px;color:var(--ah-color-text-muted, #65676e);margin-top:4px;line-height:1.4}.field-link{display:block;text-align:left;margin-top:8px;margin-bottom:16px}.field-link a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400)}.field-link a:hover{text-decoration:underline}.checkbox-wrapper{display:flex;align-items:flex-start;gap:10px;cursor:pointer;margin-bottom:16px}.checkbox-wrapper input[type="checkbox"]{width:18px;height:18px;margin:0;accent-color:var(--ah-color-primary, #635dff);cursor:pointer;flex-shrink:0}.checkbox-label{font-size:14px;color:var(--ah-color-text, #1e212a);line-height:1.5}.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:14px 20px;font-size:16px;font-weight:var(--ah-font-weight-btn, 400);font-family:inherit;line-height:1.25;text-align:center;text-decoration:none;border:none;border-radius:var(--ah-btn-radius, 3px);cursor:pointer;transition:background-color 0.15s ease,\\n border-color 0.15s ease,\\n transform 0.1s ease;box-sizing:border-box}.btn:disabled{opacity:0.6;cursor:not-allowed}.btn:not(:disabled):active{transform:scale(0.98)}.btn:focus-visible{outline:2px solid var(--ah-color-primary, #635dff);outline-offset:2px}.btn-primary{background-color:var(--ah-color-primary, #635dff);color:var(--ah-color-text-on-primary, #ffffff);margin-top:12px}.btn-primary:not(:disabled):hover{filter:brightness(0.85)}.btn-secondary{background-color:var(--ah-color-bg, #ffffff);color:var(--ah-color-text, #1e212a);border:1px solid var(--ah-color-border, #c9cace)}.btn-secondary:not(:disabled):hover{background-color:var(--ah-color-bg-hover, #f5f5f5);border-color:var(--ah-color-border-hover, #65676e)}.btn-link{background:none;border:none;color:var(--ah-color-link, #635dff);padding:8px 0;font-weight:var(--ah-font-weight-link, 400);text-decoration:none}.btn-link:hover{text-decoration:underline}.social-buttons{display:flex;flex-direction:column;gap:12px}.btn-social{display:flex;align-items:center;justify-content:center;gap:12px}.btn-social-content{display:flex;flex-direction:column;align-items:center;text-align:center}.btn-social-subtitle{font-size:12px;font-style:italic;opacity:0.8;margin-top:2px}.btn-social-subtitle:empty{display:none}.social-icon{width:20px;height:20px;flex-shrink:0}@media (max-width: 480px){.social-buttons:has(.btn-social:nth-child(3)){flex-direction:row;flex-wrap:nowrap;justify-content:stretch;gap:8px}.social-buttons:has(.btn-social:nth-child(3)) .btn-social{width:auto;min-width:0;padding:12px;flex:1 1 0}.social-buttons:has(.btn-social:nth-child(3)) .btn-social span{display:none}.social-buttons:has(.btn-social:nth-child(3)) .social-icon{width:24px;height:24px}}.btn-icon{width:20px;height:20px;flex-shrink:0}.btn-icon img{width:100%;height:100%;object-fit:contain}.text-title{font-size:20px;font-weight:400;color:var(--ah-color-text, #1e212a);margin:8px 0;line-height:1.3}.text-title.text-success{color:var(--ah-color-success, #13a769)}.text-description{font-size:14px;color:var(--ah-color-text-muted, #65676e);margin:4px 0;line-height:1.5}.image{display:block;max-width:100%;height:auto;border-radius:4px}.image-centered{margin:0 auto 16px;width:52px;height:52px;object-fit:contain}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;transition:color 0.15s ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.node-error{padding:12px 16px;background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38);border-radius:0;font-size:14px;margin-bottom:16px}.node-success{padding:12px 16px;background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769);border-radius:0;font-size:14px;margin-bottom:16px}.divider{display:flex;align-items:center;text-align:center;margin:16px 0}.divider::before,.divider::after{content:"";flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0.5px}.rich-text{font-size:14px;line-height:1.5;color:var(--ah-color-text, #1e212a)}.rich-text a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);transition:color 0.15s ease}.rich-text a:hover{text-decoration:underline}.rich-text .terms-text{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.rich-text .terms-text a{font-size:12px}.rich-text .forgot-password-link{text-align:right;font-size:13px;margin-top:4px}.rich-text .forgot-password-link a{font-size:13px;color:var(--ah-color-link, #635dff)}.rich-text .signup-link{margin-top:16px;text-align:center;font-size:14px;color:var(--ah-color-text, #1e212a)}`;
|
|
4951
4951
|
|
|
4952
4952
|
class AuthheroNode {
|
|
4953
4953
|
constructor(hostRef) {
|
|
@@ -4997,6 +4997,23 @@ class AuthheroNode {
|
|
|
4997
4997
|
value: target.checked ? "true" : "false",
|
|
4998
4998
|
});
|
|
4999
4999
|
};
|
|
5000
|
+
/**
|
|
5001
|
+
* Returns the effective value for the field: uses `this.value` if set,
|
|
5002
|
+
* otherwise falls back to `config.default_value` (resolved by the server).
|
|
5003
|
+
*/
|
|
5004
|
+
getEffectiveValue() {
|
|
5005
|
+
if (this.value !== undefined && this.value !== null) {
|
|
5006
|
+
return this.value;
|
|
5007
|
+
}
|
|
5008
|
+
const comp = this.component;
|
|
5009
|
+
if (comp.config && "default_value" in comp.config) {
|
|
5010
|
+
const dv = comp.config.default_value;
|
|
5011
|
+
if (typeof dv === "string" && dv !== "") {
|
|
5012
|
+
return dv;
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
5015
|
+
return undefined;
|
|
5016
|
+
}
|
|
5000
5017
|
/**
|
|
5001
5018
|
* Sanitize a string for use in CSS class names and part tokens.
|
|
5002
5019
|
* Replaces spaces and special characters with hyphens, converts to lowercase.
|
|
@@ -5030,7 +5047,9 @@ class AuthheroNode {
|
|
|
5030
5047
|
renderFloatingLabel(text, inputId, required, hasValue) {
|
|
5031
5048
|
if (!text)
|
|
5032
5049
|
return null;
|
|
5033
|
-
|
|
5050
|
+
// Use string class instead of object to avoid hydration mismatch
|
|
5051
|
+
const labelClass = hasValue ? "input-label floating" : "input-label";
|
|
5052
|
+
return (hAsync("label", { class: labelClass, part: "label", htmlFor: inputId }, text, required && hAsync("span", { class: "required" }, "*")));
|
|
5034
5053
|
}
|
|
5035
5054
|
/**
|
|
5036
5055
|
* Render a label for a field (non-floating version for checkboxes etc).
|
|
@@ -5040,6 +5059,13 @@ class AuthheroNode {
|
|
|
5040
5059
|
return null;
|
|
5041
5060
|
return (hAsync("label", { class: "input-label", part: "label", htmlFor: inputId }, text, required && hAsync("span", { class: "required" }, "*")));
|
|
5042
5061
|
}
|
|
5062
|
+
/**
|
|
5063
|
+
* Get the input field class string.
|
|
5064
|
+
* Uses string instead of object to avoid hydration mismatch.
|
|
5065
|
+
*/
|
|
5066
|
+
getInputFieldClass(hasError) {
|
|
5067
|
+
return hasError ? "input-field has-error" : "input-field";
|
|
5068
|
+
}
|
|
5043
5069
|
/**
|
|
5044
5070
|
* Render the eye icon for password visibility toggle.
|
|
5045
5071
|
*/
|
|
@@ -5103,46 +5129,55 @@ class AuthheroNode {
|
|
|
5103
5129
|
const inputId = `input-${component.id}`;
|
|
5104
5130
|
const errors = this.getErrors();
|
|
5105
5131
|
const { multiline, max_length } = component.config ?? {};
|
|
5106
|
-
const
|
|
5132
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5133
|
+
const hasValue = !!(effectiveValue && effectiveValue.length > 0);
|
|
5107
5134
|
if (multiline) {
|
|
5108
|
-
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), hAsync("textarea", { id: inputId, class:
|
|
5135
|
+
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)));
|
|
5109
5136
|
}
|
|
5110
|
-
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container" }, hAsync("input", { id: inputId, class:
|
|
5137
|
+
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)));
|
|
5111
5138
|
}
|
|
5112
5139
|
renderEmailField(component) {
|
|
5113
5140
|
const inputId = `input-${component.id}`;
|
|
5114
5141
|
const errors = this.getErrors();
|
|
5115
|
-
const
|
|
5116
|
-
|
|
5142
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5143
|
+
const hasValue = !!(effectiveValue && effectiveValue.length > 0);
|
|
5144
|
+
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)));
|
|
5117
5145
|
}
|
|
5118
5146
|
renderPasswordField(component) {
|
|
5119
5147
|
const inputId = `input-${component.id}`;
|
|
5120
5148
|
const errors = this.getErrors();
|
|
5121
|
-
const
|
|
5149
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5150
|
+
const hasValue = !!(effectiveValue && effectiveValue.length > 0);
|
|
5122
5151
|
const forgotPasswordLink = component.config?.forgot_password_link;
|
|
5123
|
-
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container password-container" }, hAsync("input", { id: inputId, class:
|
|
5152
|
+
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container password-container" }, hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: this.passwordVisible ? "text" : "password", name: component.id, "data-input-name": component.id, value: effectiveValue ?? "", placeholder: " ", required: component.required, disabled: this.disabled, minLength: component.config?.min_length, autocomplete: "current-password", onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue), hAsync("button", { type: "button", class: "password-toggle", part: "password-toggle", onClick: this.togglePasswordVisibility, "aria-label": "Toggle password visibility", "aria-pressed": this.passwordVisible ? "true" : "false" }, this.renderPasswordToggle())), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint), forgotPasswordLink && (hAsync("div", { class: "field-link", part: "field-link" }, hAsync("a", { href: forgotPasswordLink, class: "link", part: "link" }, "Forgot password?")))));
|
|
5124
5153
|
}
|
|
5125
5154
|
renderNumberField(component) {
|
|
5126
5155
|
const inputId = `input-${component.id}`;
|
|
5127
5156
|
const errors = this.getErrors();
|
|
5128
5157
|
const { placeholder, min, max, step } = component.config ?? {};
|
|
5129
|
-
|
|
5158
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5159
|
+
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: "number", name: component.id, value: effectiveValue ?? "", placeholder: placeholder, required: component.required, disabled: this.disabled, min: min, max: max, step: step, onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5130
5160
|
}
|
|
5131
5161
|
renderTelField(component) {
|
|
5132
5162
|
const inputId = `input-${component.id}`;
|
|
5133
5163
|
const errors = this.getErrors();
|
|
5134
|
-
|
|
5164
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5165
|
+
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: "tel", name: component.id, value: effectiveValue ?? "", placeholder: component.config?.placeholder, required: component.required, disabled: this.disabled, autocomplete: "tel", onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5135
5166
|
}
|
|
5136
5167
|
renderUrlField(component) {
|
|
5137
5168
|
const inputId = `input-${component.id}`;
|
|
5138
5169
|
const errors = this.getErrors();
|
|
5139
|
-
|
|
5170
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5171
|
+
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: "url", name: component.id, value: effectiveValue ?? "", placeholder: component.config?.placeholder, required: component.required, disabled: this.disabled, onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5140
5172
|
}
|
|
5141
5173
|
renderDateField(component) {
|
|
5142
5174
|
const inputId = `input-${component.id}`;
|
|
5143
5175
|
const errors = this.getErrors();
|
|
5144
5176
|
const { min, max } = component.config ?? {};
|
|
5145
|
-
|
|
5177
|
+
// Date fields always have a value (even if placeholder format), so always float the label
|
|
5178
|
+
const hasValue = true;
|
|
5179
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5180
|
+
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: "date", name: component.id, "data-input-name": component.id, value: effectiveValue ?? "", placeholder: " ", required: component.required, disabled: this.disabled, min: min, max: max, onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5146
5181
|
}
|
|
5147
5182
|
renderBooleanField(component) {
|
|
5148
5183
|
return (hAsync("label", { class: "checkbox-wrapper", part: "checkbox-wrapper" }, hAsync("input", { type: "checkbox", part: "checkbox", name: component.id, checked: this.value === "true" || component.config?.default_value === true, required: component.required, disabled: this.disabled, onChange: this.handleCheckbox }), hAsync("span", { class: "checkbox-label", part: "checkbox-label" }, component.label)));
|
|
@@ -5156,14 +5191,17 @@ class AuthheroNode {
|
|
|
5156
5191
|
const inputId = `input-${component.id}`;
|
|
5157
5192
|
const errors = this.getErrors();
|
|
5158
5193
|
const { options, placeholder } = component.config ?? {};
|
|
5159
|
-
|
|
5194
|
+
// Dropdown always has visual content (selected option), so always float the label
|
|
5195
|
+
const hasValue = true;
|
|
5196
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5197
|
+
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container" }, hAsync("select", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input select", name: component.id, required: component.required, disabled: this.disabled, onChange: this.handleInput }, placeholder && (hAsync("option", { value: "", disabled: true, selected: !effectiveValue }, placeholder)), options?.map((opt) => (hAsync("option", { value: opt.value, selected: effectiveValue === opt.value, key: opt.value }, opt.label)))), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5160
5198
|
}
|
|
5161
5199
|
renderChoiceField(component) {
|
|
5162
5200
|
const errors = this.getErrors();
|
|
5163
5201
|
const { options, display } = component.config ?? {};
|
|
5164
5202
|
const isCheckbox = display === "checkbox";
|
|
5165
5203
|
const inputType = isCheckbox ? "checkbox" : "radio";
|
|
5166
|
-
return (hAsync("div", { class: "choice-wrapper", part: "choice-wrapper" }, component.label && (hAsync("span", { class: "choice-label", part: "choice-label" }, component.label, component.required && hAsync("span", { class: "required" }, "*"))), hAsync("div", { class: "choice-options", part: "choice-options" }, options?.map((opt) => (hAsync("label", { class: "choice-option", part: "choice-option", key: opt.value }, hAsync("input", { type: inputType, part: inputType, name: component.id, value: opt.value, checked: this.
|
|
5204
|
+
return (hAsync("div", { class: "choice-wrapper", part: "choice-wrapper" }, component.label && (hAsync("span", { class: "choice-label", part: "choice-label" }, component.label, component.required && hAsync("span", { class: "required" }, "*"))), hAsync("div", { class: "choice-options", part: "choice-options" }, options?.map((opt) => (hAsync("label", { class: "choice-option", part: "choice-option", key: opt.value }, hAsync("input", { type: inputType, part: inputType, name: component.id, value: opt.value, checked: this.getEffectiveValue() === opt.value, required: component.required && !isCheckbox, disabled: this.disabled, onChange: this.handleInput }), hAsync("span", null, opt.label))))), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5167
5205
|
}
|
|
5168
5206
|
renderSocialField(component) {
|
|
5169
5207
|
const providers = component.config?.providers ?? [];
|
|
@@ -5713,7 +5751,7 @@ function escapeAttr(value) {
|
|
|
5713
5751
|
.replace(/>/g, ">");
|
|
5714
5752
|
}
|
|
5715
5753
|
|
|
5716
|
-
const authheroWidgetCss = () => `:host{display:block;font-family:var(--ah-font-family, 'ulp-font', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, sans-serif);font-size:var(--ah-font-size-base, 14px);line-height:var(--ah-line-height-base, 1.5);color:var(--ah-color-text, #1e212a);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.widget-container{max-width:var(--ah-widget-max-width, 400px);width:100%;margin:0 auto;background-color:var(--ah-color-bg, #ffffff);border-radius:var(--ah-widget-radius, 5px);box-shadow:var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));box-sizing:border-box}.widget-header{padding:var(--ah-header-padding, 40px 48px 24px)}.widget-body{padding:var(--ah-body-padding, 0 48px 40px)}.logo-wrapper{display:var(--ah-logo-display, flex);justify-content:var(--ah-logo-align, center);margin-bottom:8px}.logo{display:block;height:var(--ah-logo-height, 52px);max-width:100%;width:auto;object-fit:contain}.title{font-size:var(--ah-font-size-title, 24px);font-weight:var(--ah-font-weight-title, 700);text-align:var(--ah-title-align, center);margin:var(--ah-title-margin, 24px 0
|
|
5754
|
+
const authheroWidgetCss = () => `:host{display:block;font-family:var(--ah-font-family, 'ulp-font', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, sans-serif);font-size:var(--ah-font-size-base, 14px);line-height:var(--ah-line-height-base, 1.5);color:var(--ah-color-text, #1e212a);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.widget-container{max-width:var(--ah-widget-max-width, 400px);width:100%;margin:0 auto;background-color:var(--ah-color-bg, #ffffff);border-radius:var(--ah-widget-radius, 5px);box-shadow:var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));box-sizing:border-box}.widget-header{padding:var(--ah-header-padding, 40px 48px 24px)}.widget-body{padding:var(--ah-body-padding, 0 48px 40px)}.logo-wrapper{display:var(--ah-logo-display, flex);justify-content:var(--ah-logo-align, center);margin-bottom:8px}.logo{display:block;height:var(--ah-logo-height, 52px);max-width:100%;width:auto;object-fit:contain}.title{font-size:var(--ah-font-size-title, 24px);font-weight:var(--ah-font-weight-title, 700);text-align:var(--ah-title-align, center);margin:var(--ah-title-margin, 24px 0 24px);color:var(--ah-color-header, #1e212a);line-height:1.2}.description{font-size:var(--ah-font-size-description, 14px);text-align:var(--ah-title-align, center);margin:var(--ah-description-margin, 0 0 8px);color:var(--ah-color-text, #1e212a);line-height:1.5}.message{padding:12px 16px;border-radius:4px;margin-bottom:16px;font-size:14px;line-height:1.5}.message-error{background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38)}.message-success{background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769)}form{display:flex;flex-direction:column}.form-content{display:flex;flex-direction:column}.social-section{display:flex;flex-direction:column;gap:8px;order:var(--ah-social-order, 2)}.fields-section{display:flex;flex-direction:column;order:var(--ah-fields-order, 0)}.divider{display:flex;align-items:center;text-align:center;margin:16px 0;order:var(--ah-divider-order, 1)}.divider::before,.divider::after{content:'';flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0}.links{display:flex;flex-direction:column;align-items:center;gap:8px;margin-top:16px}.link-wrapper{font-size:14px;color:var(--ah-color-text, #1e212a)}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400);transition:color 150ms ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.widget-footer{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.widget-footer a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:12px;transition:color 150ms ease}.widget-footer a:hover{text-decoration:underline}.widget-footer a:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.loading-spinner{width:32px;height:32px;margin:24px auto;border:3px solid var(--ah-color-border-muted, #e0e1e3);border-top-color:var(--ah-color-primary, #635dff);border-radius:50%;animation:spin 0.8s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.error-message{text-align:center;color:var(--ah-color-error, #d03c38);padding:16px;font-size:14px}@media (max-width: 480px){:host{display:block;width:100%;min-height:100vh;background-color:var(--ah-color-bg, #ffffff)}.widget-container{box-shadow:none;border-radius:0;max-width:none;width:100%;margin:0}.widget-header{padding:24px 16px 16px}.widget-body{padding:0 16px 24px}}`;
|
|
5717
5755
|
|
|
5718
5756
|
class AuthheroWidget {
|
|
5719
5757
|
constructor(hostRef) {
|
|
@@ -5873,10 +5911,34 @@ class AuthheroWidget {
|
|
|
5873
5911
|
this._screen = newValue;
|
|
5874
5912
|
}
|
|
5875
5913
|
if (this._screen) {
|
|
5914
|
+
this.formData = {};
|
|
5915
|
+
this.initFormDataFromDefaults(this._screen);
|
|
5876
5916
|
this.screenChange.emit(this._screen);
|
|
5877
5917
|
this.updateDataScreenAttribute();
|
|
5878
5918
|
}
|
|
5879
5919
|
}
|
|
5920
|
+
/**
|
|
5921
|
+
* Initialize formData from component default_value configs.
|
|
5922
|
+
* This pre-fills form fields with values resolved on the server
|
|
5923
|
+
* (e.g. from user profile context).
|
|
5924
|
+
*/
|
|
5925
|
+
initFormDataFromDefaults(screen) {
|
|
5926
|
+
const defaults = {};
|
|
5927
|
+
for (const comp of screen.components || []) {
|
|
5928
|
+
if ("config" in comp &&
|
|
5929
|
+
comp.config &&
|
|
5930
|
+
"default_value" in comp.config &&
|
|
5931
|
+
comp.config.default_value) {
|
|
5932
|
+
const val = comp.config.default_value;
|
|
5933
|
+
if (typeof val === "string" && val !== "") {
|
|
5934
|
+
defaults[comp.id] = val;
|
|
5935
|
+
}
|
|
5936
|
+
}
|
|
5937
|
+
}
|
|
5938
|
+
if (Object.keys(defaults).length > 0) {
|
|
5939
|
+
this.formData = { ...defaults, ...this.formData };
|
|
5940
|
+
}
|
|
5941
|
+
}
|
|
5880
5942
|
/**
|
|
5881
5943
|
* Updates the data-screen attribute on the host element.
|
|
5882
5944
|
* This allows external CSS to target different screens using attribute selectors.
|
|
@@ -6113,6 +6175,7 @@ class AuthheroWidget {
|
|
|
6113
6175
|
if (currentScreenId && currentScreenId !== this.screenId) {
|
|
6114
6176
|
this.screenId = currentScreenId;
|
|
6115
6177
|
}
|
|
6178
|
+
this.initFormDataFromDefaults(this._screen);
|
|
6116
6179
|
this.screenChange.emit(this._screen);
|
|
6117
6180
|
this.updateDataScreenAttribute();
|
|
6118
6181
|
this.persistState();
|
|
@@ -6183,10 +6246,19 @@ class AuthheroWidget {
|
|
|
6183
6246
|
window.location.href = result.redirect;
|
|
6184
6247
|
}
|
|
6185
6248
|
}
|
|
6249
|
+
else if (!response.ok && result.screen) {
|
|
6250
|
+
// Handle validation errors (400 response) — preserve user input
|
|
6251
|
+
this._screen = result.screen;
|
|
6252
|
+
this.initFormDataFromDefaults(result.screen);
|
|
6253
|
+
this.screenChange.emit(result.screen);
|
|
6254
|
+
this.updateDataScreenAttribute();
|
|
6255
|
+
this.focusFirstInput();
|
|
6256
|
+
}
|
|
6186
6257
|
else if (result.screen) {
|
|
6187
|
-
// Next screen
|
|
6258
|
+
// Next screen (success)
|
|
6188
6259
|
this._screen = result.screen;
|
|
6189
6260
|
this.formData = {};
|
|
6261
|
+
this.initFormDataFromDefaults(result.screen);
|
|
6190
6262
|
this.screenChange.emit(result.screen);
|
|
6191
6263
|
this.updateDataScreenAttribute();
|
|
6192
6264
|
// Update screenId if returned in response
|
|
@@ -6216,13 +6288,6 @@ class AuthheroWidget {
|
|
|
6216
6288
|
// Flow complete without redirect
|
|
6217
6289
|
this.flowComplete.emit({});
|
|
6218
6290
|
}
|
|
6219
|
-
// Handle validation errors (400 response)
|
|
6220
|
-
if (!response.ok && result.screen) {
|
|
6221
|
-
this._screen = result.screen;
|
|
6222
|
-
this.screenChange.emit(result.screen);
|
|
6223
|
-
this.updateDataScreenAttribute();
|
|
6224
|
-
this.focusFirstInput();
|
|
6225
|
-
}
|
|
6226
6291
|
}
|
|
6227
6292
|
}
|
|
6228
6293
|
catch (err) {
|
package/hydrate/index.mjs
CHANGED
|
@@ -4945,7 +4945,7 @@ var setScopedSSR = (opts) => {
|
|
|
4945
4945
|
var needsScopedSSR = () => scopedSSR;
|
|
4946
4946
|
var scopedSSR = false;
|
|
4947
4947
|
|
|
4948
|
-
const authheroNodeCss = () => `:host{display:block}.input-wrapper{display:flex;flex-direction:column;position:relative;margin-bottom:16px}.input-container{position:relative;width:100%}.input-label{position:absolute;left:16px;top:50%;transform:translateY(-50%);font-size:16px;font-weight:var(--ah-font-weight-label, 400);color:var(--ah-color-text-muted, #65676e);pointer-events:none;transition:all 0.15s ease-out;background-color:transparent;padding:0;z-index:1}.input-label.floating,.input-field:focus+.input-label,.input-field:not(:placeholder-shown)+.input-label{top:-8px;transform:translateY(0);font-size:12px;background-color:var(--ah-color-bg, #ffffff);padding:0 4px;left:12px;color:var(--ah-color-text-muted, #65676e)}.input-field:focus+.input-label{color:var(--ah-color-primary, #635dff)}.required{color:var(--ah-color-error, #d03c38);margin-left:2px}.input-field{width:100%;padding:16px;font-size:16px;font-family:inherit;color:var(--ah-color-text, #1e212a);background-color:var(--ah-color-input-bg, #ffffff);border:1px solid var(--ah-color-border, #c9cace);border-radius:var(--ah-input-radius, 3px);outline:none;transition:border-color 0.15s ease-out,\\n box-shadow 0.15s ease-out;box-sizing:border-box}.input-field::placeholder{color:transparent}.input-field:hover{border-color:var(--ah-color-border-hover, #65676e)}.input-field:focus{border-color:var(--ah-color-primary, #635dff);box-shadow:inset 0 0 0 1px var(--ah-color-primary, #635dff)}.input-field.has-error{border-color:var(--ah-color-error, #d03c38)}.input-field.has-error:focus{box-shadow:inset 0 0 0 1px var(--ah-color-error, #d03c38)}.input-field:disabled{background-color:var(--ah-color-bg-disabled, #f5f5f5);border-color:var(--ah-color-border-disabled, #e0e1e3);cursor:not-allowed;opacity:0.7}.password-container{position:relative;display:flex;align-items:center}.password-container .input-field{padding-right:48px}.password-toggle{position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--ah-color-text-muted, #65676e);transition:color 0.15s ease}.password-toggle:hover{color:var(--ah-color-text, #1e212a)}.password-toggle svg{width:20px;height:20px}.error-text{font-size:12px;color:var(--ah-color-error, #d03c38);margin-top:4px;line-height:1.4}.helper-text{font-size:12px;color:var(--ah-color-text-muted, #65676e);margin-top:4px;line-height:1.4}.field-link{display:block;text-align:left;margin-top:8px;margin-bottom:16px}.field-link a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400)}.field-link a:hover{text-decoration:underline}.checkbox-wrapper{display:flex;align-items:flex-start;gap:10px;cursor:pointer;margin-bottom:16px}.checkbox-wrapper input[type="checkbox"]{width:18px;height:18px;margin:0;accent-color:var(--ah-color-primary, #635dff);cursor:pointer;flex-shrink:0}.checkbox-label{font-size:14px;color:var(--ah-color-text, #1e212a);line-height:1.5}.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:14px 20px;font-size:16px;font-weight:var(--ah-font-weight-btn, 400);font-family:inherit;line-height:1.25;text-align:center;text-decoration:none;border:none;border-radius:var(--ah-btn-radius, 3px);cursor:pointer;transition:background-color 0.15s ease,\\n border-color 0.15s ease,\\n transform 0.1s ease;box-sizing:border-box}.btn:disabled{opacity:0.6;cursor:not-allowed}.btn:not(:disabled):active{transform:scale(0.98)}.btn:focus-visible{outline:2px solid var(--ah-color-primary, #635dff);outline-offset:2px}.btn-primary{background-color:var(--ah-color-primary, #635dff);color:var(--ah-color-text-on-primary, #ffffff);margin-top:12px}.btn-primary:not(:disabled):hover{filter:brightness(0.85)}.btn-secondary{background-color:var(--ah-color-bg, #ffffff);color:var(--ah-color-text, #1e212a);border:1px solid var(--ah-color-border, #c9cace)}.btn-secondary:not(:disabled):hover{background-color:var(--ah-color-bg-hover, #f5f5f5);border-color:var(--ah-color-border-hover, #65676e)}.btn-link{background:none;border:none;color:var(--ah-color-link, #635dff);padding:8px 0;font-weight:var(--ah-font-weight-link, 400);text-decoration:none}.btn-link:hover{text-decoration:underline}.social-buttons{display:flex;flex-direction:column;gap:12px}.btn-social{display:flex;align-items:center;justify-content:center;gap:12px}.btn-social-content{display:flex;flex-direction:column;align-items:center;text-align:center}.btn-social-subtitle{font-size:12px;font-style:italic;opacity:0.8;margin-top:2px}.btn-social-subtitle:empty{display:none}.social-icon{width:20px;height:20px;flex-shrink:0}@media (max-width: 480px){.social-buttons:has(.btn-social:nth-child(3)){flex-direction:row;flex-wrap:nowrap;justify-content:stretch;gap:8px}.social-buttons:has(.btn-social:nth-child(3)) .btn-social{width:auto;min-width:0;padding:12px;flex:1 1 0}.social-buttons:has(.btn-social:nth-child(3)) .btn-social span{display:none}.social-buttons:has(.btn-social:nth-child(3)) .social-icon{width:24px;height:24px}}.btn-icon{width:20px;height:20px;flex-shrink:0}.btn-icon img{width:100%;height:100%;object-fit:contain}.text-title{font-size:20px;font-weight:400;color:var(--ah-color-text, #1e212a);margin:8px 0;line-height:1.3}.text-title.text-success{color:var(--ah-color-success, #13a769)}.text-description{font-size:14px;color:var(--ah-color-text-muted, #65676e);margin:4px 0;line-height:1.5}.image{display:block;max-width:100%;height:auto;border-radius:4px}.image-centered{margin:0 auto 16px;width:52px;height:52px;object-fit:contain}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;transition:color 0.15s ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.node-error{padding:12px 16px;background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38);border-radius:0;font-size:14px;margin-bottom:16px}.node-success{padding:12px 16px;background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769);border-radius:0;font-size:14px;margin-bottom:16px}.divider{display:flex;align-items:center;text-align:center;margin:16px 0}.divider::before,.divider::after{content:"";flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0.5px}.rich-text{font-size:14px;line-height:1.5;color:var(--ah-color-text, #1e212a)}.rich-text a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);transition:color 0.15s ease}.rich-text a:hover{text-decoration:underline}.rich-text .terms-text{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.rich-text .terms-text a{font-size:12px}.rich-text .signup-link{margin-top:16px;text-align:center;font-size:14px;color:var(--ah-color-text, #1e212a)}`;
|
|
4948
|
+
const authheroNodeCss = () => `:host{display:block}.input-wrapper{display:flex;flex-direction:column;position:relative;margin-bottom:16px}.input-container{position:relative;width:100%}.input-label{position:absolute;left:16px;top:50%;transform:translateY(-50%);font-size:16px;font-weight:var(--ah-font-weight-label, 400);color:var(--ah-color-text-muted, #65676e);pointer-events:none;transition:all 0.15s ease-out;background-color:transparent;padding:0;z-index:1}.input-label.floating,.input-field:focus+.input-label,.input-field:not(:placeholder-shown)+.input-label,select.input-field+.input-label,input[type="date"].input-field+.input-label{top:-8px;transform:translateY(0);font-size:12px;background-color:var(--ah-color-bg, #ffffff);padding:0 4px;left:12px;color:var(--ah-color-text-muted, #65676e)}.input-field:focus+.input-label{color:var(--ah-color-primary, #635dff)}.required{color:var(--ah-color-error, #d03c38);margin-left:2px}.input-field{width:100%;padding:16px;font-size:16px;font-family:inherit;color:var(--ah-color-text, #1e212a);background-color:var(--ah-color-input-bg, #ffffff);border:1px solid var(--ah-color-border, #c9cace);border-radius:var(--ah-input-radius, 3px);outline:none;transition:border-color 0.15s ease-out,\\n box-shadow 0.15s ease-out;box-sizing:border-box}.input-field::placeholder{color:transparent}.input-field:hover{border-color:var(--ah-color-border-hover, #65676e)}.input-field:focus{border-color:var(--ah-color-primary, #635dff);box-shadow:inset 0 0 0 1px var(--ah-color-primary, #635dff)}.input-field.has-error{border-color:var(--ah-color-error, #d03c38)}.input-field.has-error:focus{box-shadow:inset 0 0 0 1px var(--ah-color-error, #d03c38)}.input-field:disabled{background-color:var(--ah-color-bg-disabled, #f5f5f5);border-color:var(--ah-color-border-disabled, #e0e1e3);cursor:not-allowed;opacity:0.7}.password-container{position:relative;display:flex;align-items:center}.password-container .input-field{padding-right:48px}.password-toggle{position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--ah-color-text-muted, #65676e);transition:color 0.15s ease}.password-toggle:hover{color:var(--ah-color-text, #1e212a)}.password-toggle svg{width:20px;height:20px}.error-text{font-size:12px;color:var(--ah-color-error, #d03c38);margin-top:4px;line-height:1.4}.helper-text{font-size:12px;color:var(--ah-color-text-muted, #65676e);margin-top:4px;line-height:1.4}.field-link{display:block;text-align:left;margin-top:8px;margin-bottom:16px}.field-link a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400)}.field-link a:hover{text-decoration:underline}.checkbox-wrapper{display:flex;align-items:flex-start;gap:10px;cursor:pointer;margin-bottom:16px}.checkbox-wrapper input[type="checkbox"]{width:18px;height:18px;margin:0;accent-color:var(--ah-color-primary, #635dff);cursor:pointer;flex-shrink:0}.checkbox-label{font-size:14px;color:var(--ah-color-text, #1e212a);line-height:1.5}.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:14px 20px;font-size:16px;font-weight:var(--ah-font-weight-btn, 400);font-family:inherit;line-height:1.25;text-align:center;text-decoration:none;border:none;border-radius:var(--ah-btn-radius, 3px);cursor:pointer;transition:background-color 0.15s ease,\\n border-color 0.15s ease,\\n transform 0.1s ease;box-sizing:border-box}.btn:disabled{opacity:0.6;cursor:not-allowed}.btn:not(:disabled):active{transform:scale(0.98)}.btn:focus-visible{outline:2px solid var(--ah-color-primary, #635dff);outline-offset:2px}.btn-primary{background-color:var(--ah-color-primary, #635dff);color:var(--ah-color-text-on-primary, #ffffff);margin-top:12px}.btn-primary:not(:disabled):hover{filter:brightness(0.85)}.btn-secondary{background-color:var(--ah-color-bg, #ffffff);color:var(--ah-color-text, #1e212a);border:1px solid var(--ah-color-border, #c9cace)}.btn-secondary:not(:disabled):hover{background-color:var(--ah-color-bg-hover, #f5f5f5);border-color:var(--ah-color-border-hover, #65676e)}.btn-link{background:none;border:none;color:var(--ah-color-link, #635dff);padding:8px 0;font-weight:var(--ah-font-weight-link, 400);text-decoration:none}.btn-link:hover{text-decoration:underline}.social-buttons{display:flex;flex-direction:column;gap:12px}.btn-social{display:flex;align-items:center;justify-content:center;gap:12px}.btn-social-content{display:flex;flex-direction:column;align-items:center;text-align:center}.btn-social-subtitle{font-size:12px;font-style:italic;opacity:0.8;margin-top:2px}.btn-social-subtitle:empty{display:none}.social-icon{width:20px;height:20px;flex-shrink:0}@media (max-width: 480px){.social-buttons:has(.btn-social:nth-child(3)){flex-direction:row;flex-wrap:nowrap;justify-content:stretch;gap:8px}.social-buttons:has(.btn-social:nth-child(3)) .btn-social{width:auto;min-width:0;padding:12px;flex:1 1 0}.social-buttons:has(.btn-social:nth-child(3)) .btn-social span{display:none}.social-buttons:has(.btn-social:nth-child(3)) .social-icon{width:24px;height:24px}}.btn-icon{width:20px;height:20px;flex-shrink:0}.btn-icon img{width:100%;height:100%;object-fit:contain}.text-title{font-size:20px;font-weight:400;color:var(--ah-color-text, #1e212a);margin:8px 0;line-height:1.3}.text-title.text-success{color:var(--ah-color-success, #13a769)}.text-description{font-size:14px;color:var(--ah-color-text-muted, #65676e);margin:4px 0;line-height:1.5}.image{display:block;max-width:100%;height:auto;border-radius:4px}.image-centered{margin:0 auto 16px;width:52px;height:52px;object-fit:contain}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;transition:color 0.15s ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.node-error{padding:12px 16px;background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38);border-radius:0;font-size:14px;margin-bottom:16px}.node-success{padding:12px 16px;background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769);border-radius:0;font-size:14px;margin-bottom:16px}.divider{display:flex;align-items:center;text-align:center;margin:16px 0}.divider::before,.divider::after{content:"";flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0.5px}.rich-text{font-size:14px;line-height:1.5;color:var(--ah-color-text, #1e212a)}.rich-text a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);transition:color 0.15s ease}.rich-text a:hover{text-decoration:underline}.rich-text .terms-text{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.rich-text .terms-text a{font-size:12px}.rich-text .forgot-password-link{text-align:right;font-size:13px;margin-top:4px}.rich-text .forgot-password-link a{font-size:13px;color:var(--ah-color-link, #635dff)}.rich-text .signup-link{margin-top:16px;text-align:center;font-size:14px;color:var(--ah-color-text, #1e212a)}`;
|
|
4949
4949
|
|
|
4950
4950
|
class AuthheroNode {
|
|
4951
4951
|
constructor(hostRef) {
|
|
@@ -4995,6 +4995,23 @@ class AuthheroNode {
|
|
|
4995
4995
|
value: target.checked ? "true" : "false",
|
|
4996
4996
|
});
|
|
4997
4997
|
};
|
|
4998
|
+
/**
|
|
4999
|
+
* Returns the effective value for the field: uses `this.value` if set,
|
|
5000
|
+
* otherwise falls back to `config.default_value` (resolved by the server).
|
|
5001
|
+
*/
|
|
5002
|
+
getEffectiveValue() {
|
|
5003
|
+
if (this.value !== undefined && this.value !== null) {
|
|
5004
|
+
return this.value;
|
|
5005
|
+
}
|
|
5006
|
+
const comp = this.component;
|
|
5007
|
+
if (comp.config && "default_value" in comp.config) {
|
|
5008
|
+
const dv = comp.config.default_value;
|
|
5009
|
+
if (typeof dv === "string" && dv !== "") {
|
|
5010
|
+
return dv;
|
|
5011
|
+
}
|
|
5012
|
+
}
|
|
5013
|
+
return undefined;
|
|
5014
|
+
}
|
|
4998
5015
|
/**
|
|
4999
5016
|
* Sanitize a string for use in CSS class names and part tokens.
|
|
5000
5017
|
* Replaces spaces and special characters with hyphens, converts to lowercase.
|
|
@@ -5028,7 +5045,9 @@ class AuthheroNode {
|
|
|
5028
5045
|
renderFloatingLabel(text, inputId, required, hasValue) {
|
|
5029
5046
|
if (!text)
|
|
5030
5047
|
return null;
|
|
5031
|
-
|
|
5048
|
+
// Use string class instead of object to avoid hydration mismatch
|
|
5049
|
+
const labelClass = hasValue ? "input-label floating" : "input-label";
|
|
5050
|
+
return (hAsync("label", { class: labelClass, part: "label", htmlFor: inputId }, text, required && hAsync("span", { class: "required" }, "*")));
|
|
5032
5051
|
}
|
|
5033
5052
|
/**
|
|
5034
5053
|
* Render a label for a field (non-floating version for checkboxes etc).
|
|
@@ -5038,6 +5057,13 @@ class AuthheroNode {
|
|
|
5038
5057
|
return null;
|
|
5039
5058
|
return (hAsync("label", { class: "input-label", part: "label", htmlFor: inputId }, text, required && hAsync("span", { class: "required" }, "*")));
|
|
5040
5059
|
}
|
|
5060
|
+
/**
|
|
5061
|
+
* Get the input field class string.
|
|
5062
|
+
* Uses string instead of object to avoid hydration mismatch.
|
|
5063
|
+
*/
|
|
5064
|
+
getInputFieldClass(hasError) {
|
|
5065
|
+
return hasError ? "input-field has-error" : "input-field";
|
|
5066
|
+
}
|
|
5041
5067
|
/**
|
|
5042
5068
|
* Render the eye icon for password visibility toggle.
|
|
5043
5069
|
*/
|
|
@@ -5101,46 +5127,55 @@ class AuthheroNode {
|
|
|
5101
5127
|
const inputId = `input-${component.id}`;
|
|
5102
5128
|
const errors = this.getErrors();
|
|
5103
5129
|
const { multiline, max_length } = component.config ?? {};
|
|
5104
|
-
const
|
|
5130
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5131
|
+
const hasValue = !!(effectiveValue && effectiveValue.length > 0);
|
|
5105
5132
|
if (multiline) {
|
|
5106
|
-
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), hAsync("textarea", { id: inputId, class:
|
|
5133
|
+
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)));
|
|
5107
5134
|
}
|
|
5108
|
-
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container" }, hAsync("input", { id: inputId, class:
|
|
5135
|
+
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)));
|
|
5109
5136
|
}
|
|
5110
5137
|
renderEmailField(component) {
|
|
5111
5138
|
const inputId = `input-${component.id}`;
|
|
5112
5139
|
const errors = this.getErrors();
|
|
5113
|
-
const
|
|
5114
|
-
|
|
5140
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5141
|
+
const hasValue = !!(effectiveValue && effectiveValue.length > 0);
|
|
5142
|
+
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)));
|
|
5115
5143
|
}
|
|
5116
5144
|
renderPasswordField(component) {
|
|
5117
5145
|
const inputId = `input-${component.id}`;
|
|
5118
5146
|
const errors = this.getErrors();
|
|
5119
|
-
const
|
|
5147
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5148
|
+
const hasValue = !!(effectiveValue && effectiveValue.length > 0);
|
|
5120
5149
|
const forgotPasswordLink = component.config?.forgot_password_link;
|
|
5121
|
-
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container password-container" }, hAsync("input", { id: inputId, class:
|
|
5150
|
+
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container password-container" }, hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: this.passwordVisible ? "text" : "password", name: component.id, "data-input-name": component.id, value: effectiveValue ?? "", placeholder: " ", required: component.required, disabled: this.disabled, minLength: component.config?.min_length, autocomplete: "current-password", onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue), hAsync("button", { type: "button", class: "password-toggle", part: "password-toggle", onClick: this.togglePasswordVisibility, "aria-label": "Toggle password visibility", "aria-pressed": this.passwordVisible ? "true" : "false" }, this.renderPasswordToggle())), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint), forgotPasswordLink && (hAsync("div", { class: "field-link", part: "field-link" }, hAsync("a", { href: forgotPasswordLink, class: "link", part: "link" }, "Forgot password?")))));
|
|
5122
5151
|
}
|
|
5123
5152
|
renderNumberField(component) {
|
|
5124
5153
|
const inputId = `input-${component.id}`;
|
|
5125
5154
|
const errors = this.getErrors();
|
|
5126
5155
|
const { placeholder, min, max, step } = component.config ?? {};
|
|
5127
|
-
|
|
5156
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5157
|
+
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: "number", name: component.id, value: effectiveValue ?? "", placeholder: placeholder, required: component.required, disabled: this.disabled, min: min, max: max, step: step, onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5128
5158
|
}
|
|
5129
5159
|
renderTelField(component) {
|
|
5130
5160
|
const inputId = `input-${component.id}`;
|
|
5131
5161
|
const errors = this.getErrors();
|
|
5132
|
-
|
|
5162
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5163
|
+
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: "tel", name: component.id, value: effectiveValue ?? "", placeholder: component.config?.placeholder, required: component.required, disabled: this.disabled, autocomplete: "tel", onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5133
5164
|
}
|
|
5134
5165
|
renderUrlField(component) {
|
|
5135
5166
|
const inputId = `input-${component.id}`;
|
|
5136
5167
|
const errors = this.getErrors();
|
|
5137
|
-
|
|
5168
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5169
|
+
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), hAsync("input", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input", type: "url", name: component.id, value: effectiveValue ?? "", placeholder: component.config?.placeholder, required: component.required, disabled: this.disabled, onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5138
5170
|
}
|
|
5139
5171
|
renderDateField(component) {
|
|
5140
5172
|
const inputId = `input-${component.id}`;
|
|
5141
5173
|
const errors = this.getErrors();
|
|
5142
5174
|
const { min, max } = component.config ?? {};
|
|
5143
|
-
|
|
5175
|
+
// Date fields always have a value (even if placeholder format), so always float the label
|
|
5176
|
+
const hasValue = true;
|
|
5177
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5178
|
+
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: "date", name: component.id, "data-input-name": component.id, value: effectiveValue ?? "", placeholder: " ", required: component.required, disabled: this.disabled, min: min, max: max, onInput: this.handleInput, onKeyDown: this.handleKeyDown }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5144
5179
|
}
|
|
5145
5180
|
renderBooleanField(component) {
|
|
5146
5181
|
return (hAsync("label", { class: "checkbox-wrapper", part: "checkbox-wrapper" }, hAsync("input", { type: "checkbox", part: "checkbox", name: component.id, checked: this.value === "true" || component.config?.default_value === true, required: component.required, disabled: this.disabled, onChange: this.handleCheckbox }), hAsync("span", { class: "checkbox-label", part: "checkbox-label" }, component.label)));
|
|
@@ -5154,14 +5189,17 @@ class AuthheroNode {
|
|
|
5154
5189
|
const inputId = `input-${component.id}`;
|
|
5155
5190
|
const errors = this.getErrors();
|
|
5156
5191
|
const { options, placeholder } = component.config ?? {};
|
|
5157
|
-
|
|
5192
|
+
// Dropdown always has visual content (selected option), so always float the label
|
|
5193
|
+
const hasValue = true;
|
|
5194
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5195
|
+
return (hAsync("div", { class: "input-wrapper", part: "input-wrapper" }, hAsync("div", { class: "input-container" }, hAsync("select", { id: inputId, class: this.getInputFieldClass(errors.length > 0), part: "input select", name: component.id, required: component.required, disabled: this.disabled, onChange: this.handleInput }, placeholder && (hAsync("option", { value: "", disabled: true, selected: !effectiveValue }, placeholder)), options?.map((opt) => (hAsync("option", { value: opt.value, selected: effectiveValue === opt.value, key: opt.value }, opt.label)))), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5158
5196
|
}
|
|
5159
5197
|
renderChoiceField(component) {
|
|
5160
5198
|
const errors = this.getErrors();
|
|
5161
5199
|
const { options, display } = component.config ?? {};
|
|
5162
5200
|
const isCheckbox = display === "checkbox";
|
|
5163
5201
|
const inputType = isCheckbox ? "checkbox" : "radio";
|
|
5164
|
-
return (hAsync("div", { class: "choice-wrapper", part: "choice-wrapper" }, component.label && (hAsync("span", { class: "choice-label", part: "choice-label" }, component.label, component.required && hAsync("span", { class: "required" }, "*"))), hAsync("div", { class: "choice-options", part: "choice-options" }, options?.map((opt) => (hAsync("label", { class: "choice-option", part: "choice-option", key: opt.value }, hAsync("input", { type: inputType, part: inputType, name: component.id, value: opt.value, checked: this.
|
|
5202
|
+
return (hAsync("div", { class: "choice-wrapper", part: "choice-wrapper" }, component.label && (hAsync("span", { class: "choice-label", part: "choice-label" }, component.label, component.required && hAsync("span", { class: "required" }, "*"))), hAsync("div", { class: "choice-options", part: "choice-options" }, options?.map((opt) => (hAsync("label", { class: "choice-option", part: "choice-option", key: opt.value }, hAsync("input", { type: inputType, part: inputType, name: component.id, value: opt.value, checked: this.getEffectiveValue() === opt.value, required: component.required && !isCheckbox, disabled: this.disabled, onChange: this.handleInput }), hAsync("span", null, opt.label))))), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
|
|
5165
5203
|
}
|
|
5166
5204
|
renderSocialField(component) {
|
|
5167
5205
|
const providers = component.config?.providers ?? [];
|
|
@@ -5711,7 +5749,7 @@ function escapeAttr(value) {
|
|
|
5711
5749
|
.replace(/>/g, ">");
|
|
5712
5750
|
}
|
|
5713
5751
|
|
|
5714
|
-
const authheroWidgetCss = () => `:host{display:block;font-family:var(--ah-font-family, 'ulp-font', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, sans-serif);font-size:var(--ah-font-size-base, 14px);line-height:var(--ah-line-height-base, 1.5);color:var(--ah-color-text, #1e212a);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.widget-container{max-width:var(--ah-widget-max-width, 400px);width:100%;margin:0 auto;background-color:var(--ah-color-bg, #ffffff);border-radius:var(--ah-widget-radius, 5px);box-shadow:var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));box-sizing:border-box}.widget-header{padding:var(--ah-header-padding, 40px 48px 24px)}.widget-body{padding:var(--ah-body-padding, 0 48px 40px)}.logo-wrapper{display:var(--ah-logo-display, flex);justify-content:var(--ah-logo-align, center);margin-bottom:8px}.logo{display:block;height:var(--ah-logo-height, 52px);max-width:100%;width:auto;object-fit:contain}.title{font-size:var(--ah-font-size-title, 24px);font-weight:var(--ah-font-weight-title, 700);text-align:var(--ah-title-align, center);margin:var(--ah-title-margin, 24px 0
|
|
5752
|
+
const authheroWidgetCss = () => `:host{display:block;font-family:var(--ah-font-family, 'ulp-font', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, sans-serif);font-size:var(--ah-font-size-base, 14px);line-height:var(--ah-line-height-base, 1.5);color:var(--ah-color-text, #1e212a);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.widget-container{max-width:var(--ah-widget-max-width, 400px);width:100%;margin:0 auto;background-color:var(--ah-color-bg, #ffffff);border-radius:var(--ah-widget-radius, 5px);box-shadow:var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));box-sizing:border-box}.widget-header{padding:var(--ah-header-padding, 40px 48px 24px)}.widget-body{padding:var(--ah-body-padding, 0 48px 40px)}.logo-wrapper{display:var(--ah-logo-display, flex);justify-content:var(--ah-logo-align, center);margin-bottom:8px}.logo{display:block;height:var(--ah-logo-height, 52px);max-width:100%;width:auto;object-fit:contain}.title{font-size:var(--ah-font-size-title, 24px);font-weight:var(--ah-font-weight-title, 700);text-align:var(--ah-title-align, center);margin:var(--ah-title-margin, 24px 0 24px);color:var(--ah-color-header, #1e212a);line-height:1.2}.description{font-size:var(--ah-font-size-description, 14px);text-align:var(--ah-title-align, center);margin:var(--ah-description-margin, 0 0 8px);color:var(--ah-color-text, #1e212a);line-height:1.5}.message{padding:12px 16px;border-radius:4px;margin-bottom:16px;font-size:14px;line-height:1.5}.message-error{background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38)}.message-success{background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769)}form{display:flex;flex-direction:column}.form-content{display:flex;flex-direction:column}.social-section{display:flex;flex-direction:column;gap:8px;order:var(--ah-social-order, 2)}.fields-section{display:flex;flex-direction:column;order:var(--ah-fields-order, 0)}.divider{display:flex;align-items:center;text-align:center;margin:16px 0;order:var(--ah-divider-order, 1)}.divider::before,.divider::after{content:'';flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0}.links{display:flex;flex-direction:column;align-items:center;gap:8px;margin-top:16px}.link-wrapper{font-size:14px;color:var(--ah-color-text, #1e212a)}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400);transition:color 150ms ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.widget-footer{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.widget-footer a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:12px;transition:color 150ms ease}.widget-footer a:hover{text-decoration:underline}.widget-footer a:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.loading-spinner{width:32px;height:32px;margin:24px auto;border:3px solid var(--ah-color-border-muted, #e0e1e3);border-top-color:var(--ah-color-primary, #635dff);border-radius:50%;animation:spin 0.8s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.error-message{text-align:center;color:var(--ah-color-error, #d03c38);padding:16px;font-size:14px}@media (max-width: 480px){:host{display:block;width:100%;min-height:100vh;background-color:var(--ah-color-bg, #ffffff)}.widget-container{box-shadow:none;border-radius:0;max-width:none;width:100%;margin:0}.widget-header{padding:24px 16px 16px}.widget-body{padding:0 16px 24px}}`;
|
|
5715
5753
|
|
|
5716
5754
|
class AuthheroWidget {
|
|
5717
5755
|
constructor(hostRef) {
|
|
@@ -5871,10 +5909,34 @@ class AuthheroWidget {
|
|
|
5871
5909
|
this._screen = newValue;
|
|
5872
5910
|
}
|
|
5873
5911
|
if (this._screen) {
|
|
5912
|
+
this.formData = {};
|
|
5913
|
+
this.initFormDataFromDefaults(this._screen);
|
|
5874
5914
|
this.screenChange.emit(this._screen);
|
|
5875
5915
|
this.updateDataScreenAttribute();
|
|
5876
5916
|
}
|
|
5877
5917
|
}
|
|
5918
|
+
/**
|
|
5919
|
+
* Initialize formData from component default_value configs.
|
|
5920
|
+
* This pre-fills form fields with values resolved on the server
|
|
5921
|
+
* (e.g. from user profile context).
|
|
5922
|
+
*/
|
|
5923
|
+
initFormDataFromDefaults(screen) {
|
|
5924
|
+
const defaults = {};
|
|
5925
|
+
for (const comp of screen.components || []) {
|
|
5926
|
+
if ("config" in comp &&
|
|
5927
|
+
comp.config &&
|
|
5928
|
+
"default_value" in comp.config &&
|
|
5929
|
+
comp.config.default_value) {
|
|
5930
|
+
const val = comp.config.default_value;
|
|
5931
|
+
if (typeof val === "string" && val !== "") {
|
|
5932
|
+
defaults[comp.id] = val;
|
|
5933
|
+
}
|
|
5934
|
+
}
|
|
5935
|
+
}
|
|
5936
|
+
if (Object.keys(defaults).length > 0) {
|
|
5937
|
+
this.formData = { ...defaults, ...this.formData };
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5878
5940
|
/**
|
|
5879
5941
|
* Updates the data-screen attribute on the host element.
|
|
5880
5942
|
* This allows external CSS to target different screens using attribute selectors.
|
|
@@ -6111,6 +6173,7 @@ class AuthheroWidget {
|
|
|
6111
6173
|
if (currentScreenId && currentScreenId !== this.screenId) {
|
|
6112
6174
|
this.screenId = currentScreenId;
|
|
6113
6175
|
}
|
|
6176
|
+
this.initFormDataFromDefaults(this._screen);
|
|
6114
6177
|
this.screenChange.emit(this._screen);
|
|
6115
6178
|
this.updateDataScreenAttribute();
|
|
6116
6179
|
this.persistState();
|
|
@@ -6181,10 +6244,19 @@ class AuthheroWidget {
|
|
|
6181
6244
|
window.location.href = result.redirect;
|
|
6182
6245
|
}
|
|
6183
6246
|
}
|
|
6247
|
+
else if (!response.ok && result.screen) {
|
|
6248
|
+
// Handle validation errors (400 response) — preserve user input
|
|
6249
|
+
this._screen = result.screen;
|
|
6250
|
+
this.initFormDataFromDefaults(result.screen);
|
|
6251
|
+
this.screenChange.emit(result.screen);
|
|
6252
|
+
this.updateDataScreenAttribute();
|
|
6253
|
+
this.focusFirstInput();
|
|
6254
|
+
}
|
|
6184
6255
|
else if (result.screen) {
|
|
6185
|
-
// Next screen
|
|
6256
|
+
// Next screen (success)
|
|
6186
6257
|
this._screen = result.screen;
|
|
6187
6258
|
this.formData = {};
|
|
6259
|
+
this.initFormDataFromDefaults(result.screen);
|
|
6188
6260
|
this.screenChange.emit(result.screen);
|
|
6189
6261
|
this.updateDataScreenAttribute();
|
|
6190
6262
|
// Update screenId if returned in response
|
|
@@ -6214,13 +6286,6 @@ class AuthheroWidget {
|
|
|
6214
6286
|
// Flow complete without redirect
|
|
6215
6287
|
this.flowComplete.emit({});
|
|
6216
6288
|
}
|
|
6217
|
-
// Handle validation errors (400 response)
|
|
6218
|
-
if (!response.ok && result.screen) {
|
|
6219
|
-
this._screen = result.screen;
|
|
6220
|
-
this.screenChange.emit(result.screen);
|
|
6221
|
-
this.updateDataScreenAttribute();
|
|
6222
|
-
this.focusFirstInput();
|
|
6223
|
-
}
|
|
6224
6289
|
}
|
|
6225
6290
|
}
|
|
6226
6291
|
catch (err) {
|