@authhero/widget 0.10.0 → 0.12.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.
@@ -1,6 +1,6 @@
1
1
  import { r as registerInstance, c as createEvent, h } from './index-ARKBiJrR.js';
2
2
 
3
- 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, 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, border-color 0.15s ease, 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}.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}`;
3
+ 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, 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, border-color 0.15s ease, 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}`;
4
4
 
5
5
  const AuthheroNode = class {
6
6
  constructor(hostRef) {
@@ -132,7 +132,7 @@ const AuthheroNode = class {
132
132
  return (h("div", { class: "rich-text", part: "rich-text", innerHTML: component.config?.content ?? "" }));
133
133
  }
134
134
  renderNextButton(component) {
135
- return (h("button", { type: "submit", class: "btn btn-primary", part: "button button-primary", disabled: this.disabled, onClick: (e) => this.handleButtonClick(e, "submit", "next") }, component.config.text ?? "Continue"));
135
+ return (h("button", { type: "submit", class: "btn btn-primary", part: "button button-primary", "data-primary-action-button": true, disabled: this.disabled, onClick: (e) => this.handleButtonClick(e, "submit", "next") }, component.config.text ?? "Continue"));
136
136
  }
137
137
  renderPreviousButton(component) {
138
138
  return (h("button", { type: "button", class: "btn btn-secondary", part: "button button-secondary", disabled: this.disabled, onClick: (e) => this.handleButtonClick(e, "previous", "back") }, component.config.text ?? "Back"));
@@ -154,20 +154,20 @@ const AuthheroNode = class {
154
154
  if (multiline) {
155
155
  return (h("div", { class: "input-wrapper", part: "input-wrapper" }, this.renderLabel(component.label, inputId, component.required), h("textarea", { id: inputId, class: { "input-field": true, "has-error": errors.length > 0 }, part: "input textarea", name: component.id, placeholder: " ", required: component.required, disabled: this.disabled, maxLength: max_length, onInput: this.handleInput }, this.value ?? ""), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
156
156
  }
157
- return (h("div", { class: "input-wrapper", part: "input-wrapper" }, h("div", { class: "input-container" }, h("input", { id: inputId, class: { "input-field": true, "has-error": errors.length > 0 }, part: "input", type: component.sensitive ? "password" : "text", name: component.id, value: this.value ?? "", placeholder: " ", required: component.required, disabled: this.disabled, maxLength: max_length, onInput: this.handleInput }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
157
+ return (h("div", { class: "input-wrapper", part: "input-wrapper" }, h("div", { class: "input-container" }, h("input", { id: inputId, class: { "input-field": true, "has-error": errors.length > 0 }, part: "input", type: component.sensitive ? "password" : "text", name: component.id, "data-input-name": component.id, value: this.value ?? "", placeholder: " ", required: component.required, disabled: this.disabled, maxLength: max_length, onInput: this.handleInput }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
158
158
  }
159
159
  renderEmailField(component) {
160
160
  const inputId = `input-${component.id}`;
161
161
  const errors = this.getErrors();
162
162
  const hasValue = !!(this.value && this.value.length > 0);
163
- return (h("div", { class: "input-wrapper", part: "input-wrapper" }, h("div", { class: "input-container" }, h("input", { id: inputId, class: { "input-field": true, "has-error": errors.length > 0 }, part: "input", type: "email", name: component.id, value: this.value ?? "", placeholder: " ", required: component.required, disabled: this.disabled, autocomplete: "email", onInput: this.handleInput }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
163
+ return (h("div", { class: "input-wrapper", part: "input-wrapper" }, h("div", { class: "input-container" }, h("input", { id: inputId, class: { "input-field": true, "has-error": errors.length > 0 }, part: "input", type: "email", name: component.id, "data-input-name": component.id, value: this.value ?? "", placeholder: " ", required: component.required, disabled: this.disabled, autocomplete: "email", onInput: this.handleInput }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue)), this.renderErrors(), errors.length === 0 && this.renderHint(component.hint)));
164
164
  }
165
165
  renderPasswordField(component) {
166
166
  const inputId = `input-${component.id}`;
167
167
  const errors = this.getErrors();
168
168
  const hasValue = !!(this.value && this.value.length > 0);
169
169
  const forgotPasswordLink = component.config?.forgot_password_link;
170
- return (h("div", { class: "input-wrapper", part: "input-wrapper" }, h("div", { class: "input-container password-container" }, h("input", { id: inputId, class: { "input-field": true, "has-error": errors.length > 0 }, part: "input", type: this.passwordVisible ? "text" : "password", name: component.id, value: this.value ?? "", placeholder: " ", required: component.required, disabled: this.disabled, minLength: component.config?.min_length, autocomplete: "current-password", onInput: this.handleInput }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue), h("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 && (h("div", { class: "field-link", part: "field-link" }, h("a", { href: forgotPasswordLink, class: "link", part: "link" }, "Forgot password?")))));
170
+ return (h("div", { class: "input-wrapper", part: "input-wrapper" }, h("div", { class: "input-container password-container" }, h("input", { id: inputId, class: { "input-field": true, "has-error": errors.length > 0 }, part: "input", type: this.passwordVisible ? "text" : "password", name: component.id, "data-input-name": component.id, value: this.value ?? "", placeholder: " ", required: component.required, disabled: this.disabled, minLength: component.config?.min_length, autocomplete: "current-password", onInput: this.handleInput }), this.renderFloatingLabel(component.label, inputId, component.required, hasValue), h("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 && (h("div", { class: "field-link", part: "field-link" }, h("a", { href: forgotPasswordLink, class: "link", part: "link" }, "Forgot password?")))));
171
171
  }
172
172
  renderNumberField(component) {
173
173
  const inputId = `input-${component.id}`;
@@ -217,115 +217,38 @@ const AuthheroNode = class {
217
217
  const providerDetails = component.config?.provider_details;
218
218
  // Create a map of provider details for quick lookup
219
219
  const detailsMap = new Map(providerDetails?.map((d) => [d.name, d]) ?? []);
220
- // Known provider identifiers for icon matching
221
- const knownProviders = [
222
- "google-oauth2",
223
- "google",
224
- "facebook",
225
- "apple",
226
- "github",
227
- "microsoft",
228
- "windowslive",
229
- "linkedin",
230
- "vipps",
231
- ];
232
- // Find matching known provider from name or strategy
233
- const findKnownProvider = (name, strategy) => {
234
- const nameLower = name.toLowerCase();
235
- const strategyLower = strategy?.toLowerCase();
236
- // First check exact match on strategy
237
- if (strategyLower && knownProviders.includes(strategyLower)) {
238
- return strategyLower;
239
- }
240
- // Then check exact match on name
241
- if (knownProviders.includes(nameLower)) {
242
- return nameLower;
243
- }
244
- // Check if name contains a known provider (e.g., "Vipps Login" contains "vipps")
245
- for (const known of knownProviders) {
246
- if (nameLower.includes(known)) {
247
- return known;
248
- }
249
- }
250
- return null;
251
- };
252
- // Map provider IDs to display names
253
- const getProviderDisplayName = (provider) => {
254
- // First check provider_details
220
+ // Get button text from provider_details (already contains the full button text from server)
221
+ const getButtonText = (provider) => {
255
222
  const details = detailsMap.get(provider);
256
223
  if (details?.display_name) {
257
224
  return details.display_name;
258
225
  }
259
- const displayNames = {
260
- "google-oauth2": "Google",
261
- facebook: "Facebook",
262
- twitter: "Twitter",
263
- github: "GitHub",
264
- linkedin: "LinkedIn",
265
- apple: "Apple",
266
- microsoft: "Microsoft",
267
- windowslive: "Microsoft",
268
- amazon: "Amazon",
269
- dropbox: "Dropbox",
270
- bitbucket: "Bitbucket",
271
- spotify: "Spotify",
272
- slack: "Slack",
273
- discord: "Discord",
274
- twitch: "Twitch",
275
- line: "LINE",
276
- shopify: "Shopify",
277
- paypal: "PayPal",
278
- "paypal-sandbox": "PayPal",
279
- box: "Box",
280
- salesforce: "Salesforce",
281
- "salesforce-sandbox": "Salesforce",
282
- yahoo: "Yahoo",
283
- auth0: "Auth0",
284
- vipps: "Vipps",
285
- };
286
- return (displayNames[provider.toLowerCase()] ||
287
- provider
288
- .split("-")
289
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
290
- .join(" "));
226
+ // Fallback: use provider name with basic formatting
227
+ return provider
228
+ .split("-")
229
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
230
+ .join(" ");
291
231
  };
292
- // Get provider icon - either from provider_details or built-in SVG
232
+ // Get provider icon from provider_details icon_url
293
233
  const getProviderIcon = (provider) => {
294
- // First check if we have a custom icon URL from provider_details
295
234
  const details = detailsMap.get(provider);
235
+ const safeProvider = this.sanitizeForCssToken(provider);
296
236
  if (details?.icon_url) {
297
- return (h("img", { class: "social-icon", src: details.icon_url, alt: details.display_name || provider }));
298
- }
299
- // Try to find a known provider from name or strategy
300
- const knownProvider = findKnownProvider(provider, details?.strategy);
301
- const p = knownProvider || provider.toLowerCase();
302
- if (p === "google-oauth2" || p === "google") {
303
- return (h("svg", { class: "social-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, h("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z", fill: "#4285F4" }), h("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: "#34A853" }), h("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: "#FBBC05" }), h("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: "#EA4335" })));
304
- }
305
- if (p === "facebook") {
306
- return (h("svg", { class: "social-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, h("path", { d: "M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z", fill: "#1877F2" })));
237
+ return (h("img", { class: "social-icon", part: `social-icon social-icon-${safeProvider}`, src: details.icon_url, alt: details.display_name || provider }));
307
238
  }
308
- if (p === "apple") {
309
- return (h("svg", { class: "social-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, h("path", { d: "M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z", fill: "#000000" })));
310
- }
311
- if (p === "github") {
312
- return (h("svg", { class: "social-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, h("path", { d: "M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z", fill: "#181717" })));
313
- }
314
- if (p === "microsoft" || p === "windowslive") {
315
- return (h("svg", { class: "social-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, h("path", { d: "M0 0h11.377v11.372H0V0z", fill: "#f25022" }), h("path", { d: "M12.623 0H24v11.372H12.623V0z", fill: "#7fba00" }), h("path", { d: "M0 12.623h11.377V24H0v-11.377z", fill: "#00a4ef" }), h("path", { d: "M12.623 12.623H24V24H12.623v-11.377z", fill: "#ffb900" })));
316
- }
317
- if (p === "linkedin") {
318
- return (h("svg", { class: "social-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, h("path", { d: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z", fill: "#0A66C2" })));
319
- }
320
- if (p === "vipps") {
321
- return (h("svg", { class: "social-icon", viewBox: "0 0 48 48", xmlns: "http://www.w3.org/2000/svg" }, h("path", { fill: "#FF5B24", d: "M3.5,8h41c1.9,0,3.5,1.6,3.5,3.5v25c0,1.9-1.6,3.5-3.5,3.5h-41C1.6,40,0,38.4,0,36.5v-25C0,9.6,1.6,8,3.5,8z" }), h("path", { fill: "#FFFFFF", d: "M27.9,20.3c1.4,0,2.6-1,2.6-2.5c0-1.5-1.2-2.5-2.6-2.5c-1.4,0-2.6,1-2.6,2.5C25.3,19.2,26.5,20.3,27.9,20.3z" }), h("path", { fill: "#FFFFFF", d: "M31.2,24.4c-1.7,2.2-3.5,3.8-6.7,3.8c-3.2,0-5.8-2-7.7-4.8c-0.8-1.2-2-1.4-2.9-0.8c-0.8,0.6-1,1.8-0.3,2.9c2.7,4.1,6.5,6.6,10.9,6.6c4,0,7.2-2,9.6-5.2c0.9-1.2,0.9-2.5,0-3.1C33.3,22.9,32.1,23.2,31.2,24.4z" })));
322
- }
323
- // Default: generic globe icon for unknown providers
324
- return (h("svg", { class: "social-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, h("circle", { cx: "12", cy: "12", r: "10", fill: "none", stroke: "#666", "stroke-width": "2" }), h("path", { d: "M2 12h20M12 2c-2.5 2.5-4 5.5-4 10s1.5 7.5 4 10c2.5-2.5 4-5.5 4-10s-1.5-7.5-4-10z", fill: "none", stroke: "#666", "stroke-width": "2" })));
239
+ // No icon provided - return null (button will just show text)
240
+ return null;
241
+ };
242
+ // Get strategy from provider details
243
+ const getProviderStrategy = (provider) => {
244
+ const details = detailsMap.get(provider);
245
+ return details?.strategy ?? provider;
325
246
  };
326
247
  return (h("div", { class: "social-buttons", part: "social-buttons" }, providers.map((provider) => {
327
248
  const safeProvider = this.sanitizeForCssToken(provider);
328
- return (h("button", { type: "button", class: `btn btn-secondary btn-social btn-social-${safeProvider}`, part: `button button-secondary button-social button-social-${safeProvider}`, "data-provider": provider, disabled: this.disabled, onClick: (e) => this.handleButtonClick(e, "SOCIAL", provider), key: provider }, getProviderIcon(provider), h("span", { part: "button-social-text" }, `Continue with ${getProviderDisplayName(provider)}`)));
249
+ const strategy = getProviderStrategy(provider);
250
+ const icon = getProviderIcon(provider);
251
+ return (h("button", { type: "button", class: `btn btn-secondary btn-social btn-social-${safeProvider}${icon ? "" : " no-icon"}`, part: `button button-secondary button-social button-social-${safeProvider}`, "data-connection-name": provider, "data-strategy": strategy, disabled: this.disabled, onClick: (e) => this.handleButtonClick(e, "SOCIAL", provider), key: provider }, icon, h("span", { class: "btn-social-content", part: `button-social-content button-social-content-${safeProvider}` }, h("span", { part: `button-social-text button-social-text-${safeProvider}` }, getButtonText(provider)), h("span", { class: "btn-social-subtitle", part: `button-social-subtitle button-social-subtitle-${safeProvider}` }))));
329
252
  })));
330
253
  }
331
254
  // ===========================================================================
@@ -463,6 +463,9 @@ const AuthheroWidget = class {
463
463
  * When statePersistence is 'url', this is synced with the URL.
464
464
  */
465
465
  screenId;
466
+ watchScreenId() {
467
+ this.updateDataScreenAttribute();
468
+ }
466
469
  /**
467
470
  * OAuth/OIDC parameters for social login redirects.
468
471
  * Can be passed as a JSON string or object.
@@ -579,6 +582,19 @@ const AuthheroWidget = class {
579
582
  }
580
583
  if (this._screen) {
581
584
  this.screenChange.emit(this._screen);
585
+ this.updateDataScreenAttribute();
586
+ }
587
+ }
588
+ /**
589
+ * Updates the data-screen attribute on the host element.
590
+ * This allows external CSS to target different screens using attribute selectors.
591
+ */
592
+ updateDataScreenAttribute() {
593
+ if (this.screenId) {
594
+ this.el.setAttribute("data-screen", this.screenId);
595
+ }
596
+ else {
597
+ this.el.removeAttribute("data-screen");
582
598
  }
583
599
  }
584
600
  watchBranding(newValue) {
@@ -770,6 +786,7 @@ const AuthheroWidget = class {
770
786
  this.screenId = currentScreenId;
771
787
  }
772
788
  this.screenChange.emit(this._screen);
789
+ this.updateDataScreenAttribute();
773
790
  this.persistState();
774
791
  }
775
792
  }
@@ -842,11 +859,17 @@ const AuthheroWidget = class {
842
859
  this._screen = result.screen;
843
860
  this.formData = {};
844
861
  this.screenChange.emit(result.screen);
862
+ this.updateDataScreenAttribute();
845
863
  // Update screenId if returned in response
846
864
  if (result.screenId) {
847
865
  this.screenId = result.screenId;
848
866
  }
867
+ // Persist state (especially for session storage mode)
849
868
  this.persistState();
869
+ // Update URL path if navigateUrl is provided (client-side navigation)
870
+ if (result.navigateUrl && this.shouldAutoNavigate) {
871
+ window.history.pushState({ screen: result.screenId, state: this.state }, "", result.navigateUrl);
872
+ }
850
873
  // Apply branding if included
851
874
  if (result.branding) {
852
875
  this._branding = result.branding;
@@ -866,6 +889,7 @@ const AuthheroWidget = class {
866
889
  if (!response.ok && result.screen) {
867
890
  this._screen = result.screen;
868
891
  this.screenChange.emit(result.screen);
892
+ this.updateDataScreenAttribute();
869
893
  }
870
894
  }
871
895
  }
@@ -1017,9 +1041,35 @@ const AuthheroWidget = class {
1017
1041
  const socialComponents = components.filter((c) => this.isSocialComponent(c));
1018
1042
  const fieldComponents = components.filter((c) => !this.isSocialComponent(c) && !this.isDividerComponent(c));
1019
1043
  const hasDivider = components.some((c) => this.isDividerComponent(c));
1044
+ // Build dynamic exportparts for social buttons including provider-specific parts
1045
+ const getExportParts = (component) => {
1046
+ const baseParts = [
1047
+ "social-buttons",
1048
+ "button",
1049
+ "button-secondary",
1050
+ "button-social",
1051
+ "button-social-content",
1052
+ "button-social-text",
1053
+ "button-social-subtitle",
1054
+ "social-icon",
1055
+ ];
1056
+ const config = component.config;
1057
+ const providers = config?.providers ?? [];
1058
+ const providerParts = providers.flatMap((p) => {
1059
+ const safe = p.replace(/[^a-zA-Z0-9-]/g, "-");
1060
+ return [
1061
+ `button-social-${safe}`,
1062
+ `button-social-content-${safe}`,
1063
+ `button-social-text-${safe}`,
1064
+ `button-social-subtitle-${safe}`,
1065
+ `social-icon-${safe}`,
1066
+ ];
1067
+ });
1068
+ return [...baseParts, ...providerParts].join(", ");
1069
+ };
1020
1070
  // Get logo URL from theme.widget (takes precedence) or branding
1021
1071
  const logoUrl = this._theme?.widget?.logo_url || this._branding?.logo_url;
1022
- return (h("div", { class: "widget-container", part: "container" }, h("header", { class: "widget-header", part: "header" }, logoUrl && (h("div", { class: "logo-wrapper", part: "logo-wrapper" }, h("img", { class: "logo", part: "logo", src: logoUrl, alt: "Logo" }))), this._screen.title && (h("h1", { class: "title", part: "title", innerHTML: sanitizeHtml(this._screen.title) })), this._screen.description && (h("p", { class: "description", part: "description", innerHTML: sanitizeHtml(this._screen.description) }))), h("div", { class: "widget-body", part: "body" }, screenErrors.map((err) => (h("div", { class: "message message-error", part: "message message-error", key: err.id ?? err.text }, err.text))), screenSuccesses.map((msg) => (h("div", { class: "message message-success", part: "message message-success", key: msg.id ?? msg.text }, msg.text))), h("form", { onSubmit: this.handleSubmit, part: "form" }, h("div", { class: "form-content" }, socialComponents.length > 0 && (h("div", { class: "social-section", part: "social-section" }, socialComponents.map((component) => (h("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading }))))), socialComponents.length > 0 &&
1072
+ return (h("div", { class: "widget-container", part: "container", "data-authstack-container": true }, h("header", { class: "widget-header", part: "header" }, logoUrl && (h("div", { class: "logo-wrapper", part: "logo-wrapper" }, h("img", { class: "logo", part: "logo", src: logoUrl, alt: "Logo" }))), this._screen.title && (h("h1", { class: "title", part: "title", innerHTML: sanitizeHtml(this._screen.title) })), this._screen.description && (h("p", { class: "description", part: "description", innerHTML: sanitizeHtml(this._screen.description) }))), h("div", { class: "widget-body", part: "body" }, screenErrors.map((err) => (h("div", { class: "message message-error", part: "message message-error", key: err.id ?? err.text }, err.text))), screenSuccesses.map((msg) => (h("div", { class: "message message-success", part: "message message-success", key: msg.id ?? msg.text }, msg.text))), h("form", { onSubmit: this.handleSubmit, part: "form" }, h("div", { class: "form-content" }, socialComponents.length > 0 && (h("div", { class: "social-section", part: "social-section" }, socialComponents.map((component) => (h("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading, exportparts: getExportParts(component) }))))), socialComponents.length > 0 &&
1023
1073
  fieldComponents.length > 0 &&
1024
1074
  hasDivider && (h("div", { class: "divider", part: "divider" }, h("span", { class: "divider-text" }, "Or"))), h("div", { class: "fields-section", part: "fields-section" }, fieldComponents.map((component) => (h("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading })))))), this._screen.links && this._screen.links.length > 0 && (h("div", { class: "links", part: "links" }, this._screen.links.map((link) => (h("span", { class: "link-wrapper", part: "link-wrapper", key: link.id ?? link.href }, link.linkText ? (h("span", null, link.text, " ", h("a", { href: link.href, class: "link", part: "link", onClick: (e) => this.handleLinkClick(e, {
1025
1075
  id: link.id,
@@ -1032,6 +1082,9 @@ const AuthheroWidget = class {
1032
1082
  }) }, link.text))))))))));
1033
1083
  }
1034
1084
  static get watchers() { return {
1085
+ "screenId": [{
1086
+ "watchScreenId": 0
1087
+ }],
1035
1088
  "screen": [{
1036
1089
  "watchScreen": 0
1037
1090
  }],
@@ -17,5 +17,5 @@ var patchBrowser = () => {
17
17
 
18
18
  patchBrowser().then(async (options) => {
19
19
  await globalScripts();
20
- return bootstrapLazy([["authhero-node",[[513,"authhero-node",{"component":[16],"value":[1],"disabled":[4],"passwordVisible":[32]}]]],["authhero-widget",[[513,"authhero-widget",{"screen":[1],"apiUrl":[1,"api-url"],"baseUrl":[1,"base-url"],"state":[1025],"screenId":[1025,"screen-id"],"authParams":[1,"auth-params"],"statePersistence":[1,"state-persistence"],"storageKey":[1,"storage-key"],"branding":[1],"theme":[1],"loading":[1028],"autoSubmit":[4,"auto-submit"],"autoNavigate":[4,"auto-navigate"],"_screen":[32],"_authParams":[32],"_branding":[32],"_theme":[32],"formData":[32]},null,{"screen":[{"watchScreen":0}],"branding":[{"watchBranding":0}],"theme":[{"watchTheme":0}],"authParams":[{"watchAuthParams":0}]}]]]], options);
20
+ return bootstrapLazy([["authhero-node",[[513,"authhero-node",{"component":[16],"value":[1],"disabled":[4],"passwordVisible":[32]}]]],["authhero-widget",[[513,"authhero-widget",{"screen":[1],"apiUrl":[1,"api-url"],"baseUrl":[1,"base-url"],"state":[1025],"screenId":[1025,"screen-id"],"authParams":[1,"auth-params"],"statePersistence":[1,"state-persistence"],"storageKey":[1,"storage-key"],"branding":[1],"theme":[1],"loading":[1028],"autoSubmit":[4,"auto-submit"],"autoNavigate":[4,"auto-navigate"],"_screen":[32],"_authParams":[32],"_branding":[32],"_theme":[32],"formData":[32]},null,{"screenId":[{"watchScreenId":0}],"screen":[{"watchScreen":0}],"branding":[{"watchBranding":0}],"theme":[{"watchTheme":0}],"authParams":[{"watchAuthParams":0}]}]]]], options);
21
21
  });
@@ -5,7 +5,7 @@ import { g as globalScripts } from './app-globals-DQuL1Twl.js';
5
5
  const defineCustomElements = async (win, options) => {
6
6
  if (typeof window === 'undefined') return undefined;
7
7
  await globalScripts();
8
- return bootstrapLazy([["authhero-node",[[513,"authhero-node",{"component":[16],"value":[1],"disabled":[4],"passwordVisible":[32]}]]],["authhero-widget",[[513,"authhero-widget",{"screen":[1],"apiUrl":[1,"api-url"],"baseUrl":[1,"base-url"],"state":[1025],"screenId":[1025,"screen-id"],"authParams":[1,"auth-params"],"statePersistence":[1,"state-persistence"],"storageKey":[1,"storage-key"],"branding":[1],"theme":[1],"loading":[1028],"autoSubmit":[4,"auto-submit"],"autoNavigate":[4,"auto-navigate"],"_screen":[32],"_authParams":[32],"_branding":[32],"_theme":[32],"formData":[32]},null,{"screen":[{"watchScreen":0}],"branding":[{"watchBranding":0}],"theme":[{"watchTheme":0}],"authParams":[{"watchAuthParams":0}]}]]]], options);
8
+ return bootstrapLazy([["authhero-node",[[513,"authhero-node",{"component":[16],"value":[1],"disabled":[4],"passwordVisible":[32]}]]],["authhero-widget",[[513,"authhero-widget",{"screen":[1],"apiUrl":[1,"api-url"],"baseUrl":[1,"base-url"],"state":[1025],"screenId":[1025,"screen-id"],"authParams":[1,"auth-params"],"statePersistence":[1,"state-persistence"],"storageKey":[1,"storage-key"],"branding":[1],"theme":[1],"loading":[1028],"autoSubmit":[4,"auto-submit"],"autoNavigate":[4,"auto-navigate"],"_screen":[32],"_authParams":[32],"_branding":[32],"_theme":[32],"formData":[32]},null,{"screenId":[{"watchScreenId":0}],"screen":[{"watchScreen":0}],"branding":[{"watchBranding":0}],"theme":[{"watchTheme":0}],"authParams":[{"watchAuthParams":0}]}]]]], options);
9
9
  };
10
10
 
11
11
  export { defineCustomElements };
@@ -103,6 +103,7 @@ export declare class AuthheroWidget {
103
103
  * When statePersistence is 'url', this is synced with the URL.
104
104
  */
105
105
  screenId?: string;
106
+ watchScreenId(): void;
106
107
  /**
107
108
  * OAuth/OIDC parameters for social login redirects.
108
109
  * Can be passed as a JSON string or object.
@@ -206,6 +207,11 @@ export declare class AuthheroWidget {
206
207
  */
207
208
  screenChange: EventEmitter<UiScreen>;
208
209
  watchScreen(newValue: UiScreen | string | undefined): void;
210
+ /**
211
+ * Updates the data-screen attribute on the host element.
212
+ * This allows external CSS to target different screens using attribute selectors.
213
+ */
214
+ private updateDataScreenAttribute;
209
215
  watchBranding(newValue: WidgetBranding | string | undefined): void;
210
216
  watchTheme(newValue: WidgetTheme | string | undefined): void;
211
217
  watchAuthParams(newValue: AuthParams | string | undefined): void;