@authhero/widget 0.14.0 → 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 +39 -14
- 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 +19 -6
- package/dist/collection/components/authhero-node/authhero-node.js +39 -14
- 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 +39 -14
- 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 +5 -0
- package/dist/types/components/authhero-widget/authhero-widget.d.ts +6 -0
- package/hydrate/index.js +75 -23
- package/hydrate/index.mjs +75 -23
- package/package.json +2 -2
- package/dist/authhero-widget/p-1107d60e.entry.js +0 -1
- package/dist/authhero-widget/p-5e699de4.entry.js +0 -1
- package/dist/components/p-CHIjqNof.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,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 .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.
|
|
@@ -5112,40 +5129,46 @@ class AuthheroNode {
|
|
|
5112
5129
|
const inputId = `input-${component.id}`;
|
|
5113
5130
|
const errors = this.getErrors();
|
|
5114
5131
|
const { multiline, max_length } = component.config ?? {};
|
|
5115
|
-
const
|
|
5132
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5133
|
+
const hasValue = !!(effectiveValue && effectiveValue.length > 0);
|
|
5116
5134
|
if (multiline) {
|
|
5117
|
-
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 },
|
|
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)));
|
|
5118
5136
|
}
|
|
5119
|
-
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:
|
|
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)));
|
|
5120
5138
|
}
|
|
5121
5139
|
renderEmailField(component) {
|
|
5122
5140
|
const inputId = `input-${component.id}`;
|
|
5123
5141
|
const errors = this.getErrors();
|
|
5124
|
-
const
|
|
5125
|
-
|
|
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)));
|
|
5126
5145
|
}
|
|
5127
5146
|
renderPasswordField(component) {
|
|
5128
5147
|
const inputId = `input-${component.id}`;
|
|
5129
5148
|
const errors = this.getErrors();
|
|
5130
|
-
const
|
|
5149
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5150
|
+
const hasValue = !!(effectiveValue && effectiveValue.length > 0);
|
|
5131
5151
|
const forgotPasswordLink = component.config?.forgot_password_link;
|
|
5132
|
-
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:
|
|
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?")))));
|
|
5133
5153
|
}
|
|
5134
5154
|
renderNumberField(component) {
|
|
5135
5155
|
const inputId = `input-${component.id}`;
|
|
5136
5156
|
const errors = this.getErrors();
|
|
5137
5157
|
const { placeholder, min, max, step } = component.config ?? {};
|
|
5138
|
-
|
|
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)));
|
|
5139
5160
|
}
|
|
5140
5161
|
renderTelField(component) {
|
|
5141
5162
|
const inputId = `input-${component.id}`;
|
|
5142
5163
|
const errors = this.getErrors();
|
|
5143
|
-
|
|
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)));
|
|
5144
5166
|
}
|
|
5145
5167
|
renderUrlField(component) {
|
|
5146
5168
|
const inputId = `input-${component.id}`;
|
|
5147
5169
|
const errors = this.getErrors();
|
|
5148
|
-
|
|
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)));
|
|
5149
5172
|
}
|
|
5150
5173
|
renderDateField(component) {
|
|
5151
5174
|
const inputId = `input-${component.id}`;
|
|
@@ -5153,7 +5176,8 @@ class AuthheroNode {
|
|
|
5153
5176
|
const { min, max } = component.config ?? {};
|
|
5154
5177
|
// Date fields always have a value (even if placeholder format), so always float the label
|
|
5155
5178
|
const hasValue = true;
|
|
5156
|
-
|
|
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)));
|
|
5157
5181
|
}
|
|
5158
5182
|
renderBooleanField(component) {
|
|
5159
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)));
|
|
@@ -5169,14 +5193,15 @@ class AuthheroNode {
|
|
|
5169
5193
|
const { options, placeholder } = component.config ?? {};
|
|
5170
5194
|
// Dropdown always has visual content (selected option), so always float the label
|
|
5171
5195
|
const hasValue = true;
|
|
5172
|
-
|
|
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)));
|
|
5173
5198
|
}
|
|
5174
5199
|
renderChoiceField(component) {
|
|
5175
5200
|
const errors = this.getErrors();
|
|
5176
5201
|
const { options, display } = component.config ?? {};
|
|
5177
5202
|
const isCheckbox = display === "checkbox";
|
|
5178
5203
|
const inputType = isCheckbox ? "checkbox" : "radio";
|
|
5179
|
-
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)));
|
|
5180
5205
|
}
|
|
5181
5206
|
renderSocialField(component) {
|
|
5182
5207
|
const providers = component.config?.providers ?? [];
|
|
@@ -5726,7 +5751,7 @@ function escapeAttr(value) {
|
|
|
5726
5751
|
.replace(/>/g, ">");
|
|
5727
5752
|
}
|
|
5728
5753
|
|
|
5729
|
-
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}}`;
|
|
5730
5755
|
|
|
5731
5756
|
class AuthheroWidget {
|
|
5732
5757
|
constructor(hostRef) {
|
|
@@ -5886,10 +5911,34 @@ class AuthheroWidget {
|
|
|
5886
5911
|
this._screen = newValue;
|
|
5887
5912
|
}
|
|
5888
5913
|
if (this._screen) {
|
|
5914
|
+
this.formData = {};
|
|
5915
|
+
this.initFormDataFromDefaults(this._screen);
|
|
5889
5916
|
this.screenChange.emit(this._screen);
|
|
5890
5917
|
this.updateDataScreenAttribute();
|
|
5891
5918
|
}
|
|
5892
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
|
+
}
|
|
5893
5942
|
/**
|
|
5894
5943
|
* Updates the data-screen attribute on the host element.
|
|
5895
5944
|
* This allows external CSS to target different screens using attribute selectors.
|
|
@@ -6126,6 +6175,7 @@ class AuthheroWidget {
|
|
|
6126
6175
|
if (currentScreenId && currentScreenId !== this.screenId) {
|
|
6127
6176
|
this.screenId = currentScreenId;
|
|
6128
6177
|
}
|
|
6178
|
+
this.initFormDataFromDefaults(this._screen);
|
|
6129
6179
|
this.screenChange.emit(this._screen);
|
|
6130
6180
|
this.updateDataScreenAttribute();
|
|
6131
6181
|
this.persistState();
|
|
@@ -6196,10 +6246,19 @@ class AuthheroWidget {
|
|
|
6196
6246
|
window.location.href = result.redirect;
|
|
6197
6247
|
}
|
|
6198
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
|
+
}
|
|
6199
6257
|
else if (result.screen) {
|
|
6200
|
-
// Next screen
|
|
6258
|
+
// Next screen (success)
|
|
6201
6259
|
this._screen = result.screen;
|
|
6202
6260
|
this.formData = {};
|
|
6261
|
+
this.initFormDataFromDefaults(result.screen);
|
|
6203
6262
|
this.screenChange.emit(result.screen);
|
|
6204
6263
|
this.updateDataScreenAttribute();
|
|
6205
6264
|
// Update screenId if returned in response
|
|
@@ -6229,13 +6288,6 @@ class AuthheroWidget {
|
|
|
6229
6288
|
// Flow complete without redirect
|
|
6230
6289
|
this.flowComplete.emit({});
|
|
6231
6290
|
}
|
|
6232
|
-
// Handle validation errors (400 response)
|
|
6233
|
-
if (!response.ok && result.screen) {
|
|
6234
|
-
this._screen = result.screen;
|
|
6235
|
-
this.screenChange.emit(result.screen);
|
|
6236
|
-
this.updateDataScreenAttribute();
|
|
6237
|
-
this.focusFirstInput();
|
|
6238
|
-
}
|
|
6239
6291
|
}
|
|
6240
6292
|
}
|
|
6241
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,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 .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.
|
|
@@ -5110,40 +5127,46 @@ class AuthheroNode {
|
|
|
5110
5127
|
const inputId = `input-${component.id}`;
|
|
5111
5128
|
const errors = this.getErrors();
|
|
5112
5129
|
const { multiline, max_length } = component.config ?? {};
|
|
5113
|
-
const
|
|
5130
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5131
|
+
const hasValue = !!(effectiveValue && effectiveValue.length > 0);
|
|
5114
5132
|
if (multiline) {
|
|
5115
|
-
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 },
|
|
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)));
|
|
5116
5134
|
}
|
|
5117
|
-
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:
|
|
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)));
|
|
5118
5136
|
}
|
|
5119
5137
|
renderEmailField(component) {
|
|
5120
5138
|
const inputId = `input-${component.id}`;
|
|
5121
5139
|
const errors = this.getErrors();
|
|
5122
|
-
const
|
|
5123
|
-
|
|
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)));
|
|
5124
5143
|
}
|
|
5125
5144
|
renderPasswordField(component) {
|
|
5126
5145
|
const inputId = `input-${component.id}`;
|
|
5127
5146
|
const errors = this.getErrors();
|
|
5128
|
-
const
|
|
5147
|
+
const effectiveValue = this.getEffectiveValue();
|
|
5148
|
+
const hasValue = !!(effectiveValue && effectiveValue.length > 0);
|
|
5129
5149
|
const forgotPasswordLink = component.config?.forgot_password_link;
|
|
5130
|
-
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:
|
|
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?")))));
|
|
5131
5151
|
}
|
|
5132
5152
|
renderNumberField(component) {
|
|
5133
5153
|
const inputId = `input-${component.id}`;
|
|
5134
5154
|
const errors = this.getErrors();
|
|
5135
5155
|
const { placeholder, min, max, step } = component.config ?? {};
|
|
5136
|
-
|
|
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)));
|
|
5137
5158
|
}
|
|
5138
5159
|
renderTelField(component) {
|
|
5139
5160
|
const inputId = `input-${component.id}`;
|
|
5140
5161
|
const errors = this.getErrors();
|
|
5141
|
-
|
|
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)));
|
|
5142
5164
|
}
|
|
5143
5165
|
renderUrlField(component) {
|
|
5144
5166
|
const inputId = `input-${component.id}`;
|
|
5145
5167
|
const errors = this.getErrors();
|
|
5146
|
-
|
|
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)));
|
|
5147
5170
|
}
|
|
5148
5171
|
renderDateField(component) {
|
|
5149
5172
|
const inputId = `input-${component.id}`;
|
|
@@ -5151,7 +5174,8 @@ class AuthheroNode {
|
|
|
5151
5174
|
const { min, max } = component.config ?? {};
|
|
5152
5175
|
// Date fields always have a value (even if placeholder format), so always float the label
|
|
5153
5176
|
const hasValue = true;
|
|
5154
|
-
|
|
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)));
|
|
5155
5179
|
}
|
|
5156
5180
|
renderBooleanField(component) {
|
|
5157
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)));
|
|
@@ -5167,14 +5191,15 @@ class AuthheroNode {
|
|
|
5167
5191
|
const { options, placeholder } = component.config ?? {};
|
|
5168
5192
|
// Dropdown always has visual content (selected option), so always float the label
|
|
5169
5193
|
const hasValue = true;
|
|
5170
|
-
|
|
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)));
|
|
5171
5196
|
}
|
|
5172
5197
|
renderChoiceField(component) {
|
|
5173
5198
|
const errors = this.getErrors();
|
|
5174
5199
|
const { options, display } = component.config ?? {};
|
|
5175
5200
|
const isCheckbox = display === "checkbox";
|
|
5176
5201
|
const inputType = isCheckbox ? "checkbox" : "radio";
|
|
5177
|
-
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)));
|
|
5178
5203
|
}
|
|
5179
5204
|
renderSocialField(component) {
|
|
5180
5205
|
const providers = component.config?.providers ?? [];
|
|
@@ -5724,7 +5749,7 @@ function escapeAttr(value) {
|
|
|
5724
5749
|
.replace(/>/g, ">");
|
|
5725
5750
|
}
|
|
5726
5751
|
|
|
5727
|
-
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}}`;
|
|
5728
5753
|
|
|
5729
5754
|
class AuthheroWidget {
|
|
5730
5755
|
constructor(hostRef) {
|
|
@@ -5884,10 +5909,34 @@ class AuthheroWidget {
|
|
|
5884
5909
|
this._screen = newValue;
|
|
5885
5910
|
}
|
|
5886
5911
|
if (this._screen) {
|
|
5912
|
+
this.formData = {};
|
|
5913
|
+
this.initFormDataFromDefaults(this._screen);
|
|
5887
5914
|
this.screenChange.emit(this._screen);
|
|
5888
5915
|
this.updateDataScreenAttribute();
|
|
5889
5916
|
}
|
|
5890
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
|
+
}
|
|
5891
5940
|
/**
|
|
5892
5941
|
* Updates the data-screen attribute on the host element.
|
|
5893
5942
|
* This allows external CSS to target different screens using attribute selectors.
|
|
@@ -6124,6 +6173,7 @@ class AuthheroWidget {
|
|
|
6124
6173
|
if (currentScreenId && currentScreenId !== this.screenId) {
|
|
6125
6174
|
this.screenId = currentScreenId;
|
|
6126
6175
|
}
|
|
6176
|
+
this.initFormDataFromDefaults(this._screen);
|
|
6127
6177
|
this.screenChange.emit(this._screen);
|
|
6128
6178
|
this.updateDataScreenAttribute();
|
|
6129
6179
|
this.persistState();
|
|
@@ -6194,10 +6244,19 @@ class AuthheroWidget {
|
|
|
6194
6244
|
window.location.href = result.redirect;
|
|
6195
6245
|
}
|
|
6196
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
|
+
}
|
|
6197
6255
|
else if (result.screen) {
|
|
6198
|
-
// Next screen
|
|
6256
|
+
// Next screen (success)
|
|
6199
6257
|
this._screen = result.screen;
|
|
6200
6258
|
this.formData = {};
|
|
6259
|
+
this.initFormDataFromDefaults(result.screen);
|
|
6201
6260
|
this.screenChange.emit(result.screen);
|
|
6202
6261
|
this.updateDataScreenAttribute();
|
|
6203
6262
|
// Update screenId if returned in response
|
|
@@ -6227,13 +6286,6 @@ class AuthheroWidget {
|
|
|
6227
6286
|
// Flow complete without redirect
|
|
6228
6287
|
this.flowComplete.emit({});
|
|
6229
6288
|
}
|
|
6230
|
-
// Handle validation errors (400 response)
|
|
6231
|
-
if (!response.ok && result.screen) {
|
|
6232
|
-
this._screen = result.screen;
|
|
6233
|
-
this.screenChange.emit(result.screen);
|
|
6234
|
-
this.updateDataScreenAttribute();
|
|
6235
|
-
this.focusFirstInput();
|
|
6236
|
-
}
|
|
6237
6289
|
}
|
|
6238
6290
|
}
|
|
6239
6291
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authhero/widget",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Server-Driven UI widget for AuthHero authentication flows",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@authhero/adapter-interfaces": "0.
|
|
40
|
+
"@authhero/adapter-interfaces": "0.135.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@hono/node-server": "^1.14.1",
|