@authhero/widget 0.7.2 → 0.8.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/README.md CHANGED
@@ -23,35 +23,34 @@ pnpm add @authhero/widget
23
23
  ### Browser (CDN)
24
24
 
25
25
  ```html
26
- <script type="module" src="https://unpkg.com/@authhero/widget/dist/authhero-widget/authhero-widget.esm.js"></script>
26
+ <script
27
+ type="module"
28
+ src="https://unpkg.com/@authhero/widget/dist/authhero-widget/authhero-widget.esm.js"
29
+ ></script>
27
30
 
28
- <authhero-widget
29
- api-url="/u/flow/screen"
30
- auto-submit="false">
31
+ <authhero-widget api-url="/u/flow/screen" auto-submit="false">
31
32
  </authhero-widget>
32
33
  ```
33
34
 
34
35
  ### With a JavaScript Framework
35
36
 
36
37
  ```javascript
37
- import '@authhero/widget';
38
+ import "@authhero/widget";
38
39
 
39
40
  // Or with the loader for lazy-loading
40
- import { defineCustomElements } from '@authhero/widget/loader';
41
+ import { defineCustomElements } from "@authhero/widget/loader";
41
42
  defineCustomElements();
42
43
  ```
43
44
 
44
45
  ```html
45
- <authhero-widget
46
- api-url="/u/flow/screen"
47
- auto-submit="false">
46
+ <authhero-widget api-url="/u/flow/screen" auto-submit="false">
48
47
  </authhero-widget>
49
48
  ```
50
49
 
51
50
  ### Server-Side Rendering (Hono)
52
51
 
53
52
  ```typescript
54
- import { renderToString } from '@authhero/widget/hydrate';
53
+ import { renderToString } from "@authhero/widget/hydrate";
55
54
 
56
55
  const html = await renderToString(`
57
56
  <authhero-widget screen='${JSON.stringify(screenConfig)}'></authhero-widget>
@@ -66,42 +65,42 @@ The widget renders UI based on Auth0's Forms API schema for universal login flow
66
65
 
67
66
  The widget supports [27+ Auth0 component types](https://auth0.com/docs/authenticate/login/auth0-universal-login/new-experience/universal-login-page-templates):
68
67
 
69
- | Component | Description |
70
- |-----------|-------------|
71
- | `heading` | Page headings and titles |
72
- | `description` | Descriptive text |
73
- | `text-input` | Text, email, phone inputs |
74
- | `password-input` | Password field with show/hide toggle |
75
- | `checkbox-input` | Checkboxes |
76
- | `select-input` | Dropdown selects |
77
- | `submit-button` | Primary action buttons |
78
- | `button` | Secondary action buttons |
79
- | `social-button-group` | Social login buttons |
80
- | `anchor` | Links and navigation |
81
- | `separator` | Visual dividers |
82
- | `image` | Logos and images |
83
- | And more... | Phone input, captcha, MFA, etc. |
68
+ | Component | Description |
69
+ | --------------------- | ------------------------------------ |
70
+ | `heading` | Page headings and titles |
71
+ | `description` | Descriptive text |
72
+ | `text-input` | Text, email, phone inputs |
73
+ | `password-input` | Password field with show/hide toggle |
74
+ | `checkbox-input` | Checkboxes |
75
+ | `select-input` | Dropdown selects |
76
+ | `submit-button` | Primary action buttons |
77
+ | `button` | Secondary action buttons |
78
+ | `social-button-group` | Social login buttons |
79
+ | `anchor` | Links and navigation |
80
+ | `separator` | Visual dividers |
81
+ | `image` | Logos and images |
82
+ | And more... | Phone input, captcha, MFA, etc. |
84
83
 
85
84
  ### Screen Configuration
86
85
 
87
86
  ```typescript
88
87
  interface UIScreen {
89
- title?: string; // Screen title
90
- description?: string; // Screen description
91
- components: UIComponent[]; // UI components to render
88
+ title?: string; // Screen title
89
+ description?: string; // Screen description
90
+ components: UIComponent[]; // UI components to render
92
91
  branding?: {
93
92
  logoUrl?: string;
94
93
  primaryColor?: string;
95
94
  backgroundColor?: string;
96
95
  };
97
- theme?: string; // Theme configuration JSON
96
+ theme?: string; // Theme configuration JSON
98
97
  }
99
98
 
100
99
  interface UIComponent {
101
- component: string; // Component type (e.g., 'text-input', 'submit-button')
102
- id: string; // Component identifier
103
- label?: string; // Display label
104
- [key: string]: any; // Component-specific props
100
+ component: string; // Component type (e.g., 'text-input', 'submit-button')
101
+ id: string; // Component identifier
102
+ label?: string; // Display label
103
+ [key: string]: any; // Component-specific props
105
104
  }
106
105
  ```
107
106
 
@@ -172,54 +171,106 @@ Social buttons are configured using the `social-button-group` component:
172
171
 
173
172
  ## Props
174
173
 
175
- | Prop | Type | Default | Description |
176
- |------|------|---------|-------------|
177
- | `screen` | `string \| UIScreen` | - | Screen configuration (JSON string or object) |
178
- | `api-url` | `string` | - | API endpoint for screen fetching |
179
- | `branding` | `string \| Branding` | - | Branding configuration |
180
- | `theme` | `string` | - | Theme configuration JSON |
181
- | `loading` | `boolean` | `false` | Loading state |
182
- | `auto-submit` | `boolean` | `false` | Auto-submit forms (not recommended, use events instead) |
174
+ | Prop | Type | Default | Description |
175
+ | ------------------- | -------------------------------- | ------------------- | ------------------------------------------------------------------ |
176
+ | `screen` | `string \| UIScreen` | - | Screen configuration (JSON string or object) |
177
+ | `api-url` | `string` | - | API endpoint for screen fetching |
178
+ | `branding` | `string \| Branding` | - | Branding configuration |
179
+ | `theme` | `string` | - | Theme configuration JSON |
180
+ | `loading` | `boolean` | `false` | Loading state |
181
+ | `auto-submit` | `boolean` | `false` | Auto-submit forms to the action URL |
182
+ | `auto-navigate` | `boolean` | `false` | Auto-navigate on social login, links, and redirects |
183
+ | `state` | `string` | - | Login session state token |
184
+ | `screen-id` | `string` | - | Current screen ID for API fetching |
185
+ | `auth-params` | `string` | - | OAuth params JSON for social login (client_id, redirect_uri, etc.) |
186
+ | `base-url` | `string` | - | Base URL for API calls (for cross-domain embedding) |
187
+ | `state-persistence` | `'url' \| 'session' \| 'memory'` | `'memory'` | Where to persist state and screen ID |
188
+ | `storage-key` | `string` | `'authhero_widget'` | Storage key prefix for session persistence |
189
+
190
+ ## Usage Modes
191
+
192
+ ### 1. Event-Based (Default)
193
+
194
+ The widget emits events for your application to handle:
195
+
196
+ ```html
197
+ <authhero-widget api-url="/u2/screen/identifier" state="your-state-token">
198
+ </authhero-widget>
199
+ ```
200
+
201
+ ### 2. Self-Contained (Universal Login Pages)
202
+
203
+ The widget handles everything internally - ideal for hosted login pages:
204
+
205
+ ```html
206
+ <authhero-widget
207
+ api-url="/u2/screen/{screenId}"
208
+ screen-id="identifier"
209
+ state="your-state-token"
210
+ auth-params='{"client_id":"abc123","redirect_uri":"https://app.example.com/callback"}'
211
+ auto-submit="true"
212
+ auto-navigate="true"
213
+ >
214
+ </authhero-widget>
215
+ ```
216
+
217
+ ### 3. Embedded on External Pages
218
+
219
+ Use `base-url` when embedding the widget on a different domain:
220
+
221
+ ```html
222
+ <authhero-widget
223
+ base-url="https://auth.example.com"
224
+ api-url="/u2/screen/{screenId}"
225
+ screen-id="identifier"
226
+ state="your-state-token"
227
+ auth-params='{"client_id":"abc123"}'
228
+ auto-submit="true"
229
+ auto-navigate="true"
230
+ state-persistence="session"
231
+ >
232
+ </authhero-widget>
233
+ ```
183
234
 
184
235
  ## Events
185
236
 
186
237
  The widget is a **pure UI component** that emits events for your auth library to handle. It does not manage tokens, sessions, or HTTP requests.
187
238
 
188
- | Event | Detail | Description |
189
- |-------|--------|-------------|
190
- | `formSubmit` | `{ data: FormData, screen: UIScreen }` | Form submitted by user |
191
- | `buttonClick` | `{ id: string, action: string }` | Button clicked |
192
- | `linkClick` | `{ href: string }` | Link clicked |
193
- | `navigate` | `{ to: string }` | Navigation requested |
194
- | `flowComplete` | `{ result: any }` | Auth flow completed successfully |
195
- | `flowError` | `{ error: Error }` | Auth flow error occurred |
196
- | `screenChange` | `UIScreen` | Screen changed |
239
+ | Event | Detail | Description |
240
+ | -------------- | -------------------------------------- | -------------------------------- |
241
+ | `formSubmit` | `{ data: FormData, screen: UIScreen }` | Form submitted by user |
242
+ | `buttonClick` | `{ id: string, action: string }` | Button clicked |
243
+ | `linkClick` | `{ href: string }` | Link clicked |
244
+ | `navigate` | `{ to: string }` | Navigation requested |
245
+ | `flowComplete` | `{ result: any }` | Auth flow completed successfully |
246
+ | `flowError` | `{ error: Error }` | Auth flow error occurred |
247
+ | `screenChange` | `UIScreen` | Screen changed |
197
248
 
198
249
  ### Event-Based Integration (Recommended)
199
250
 
200
251
  The recommended approach is to handle events and let your auth library manage the flow:
201
252
 
202
253
  ```javascript
203
- const widget = document.querySelector('authhero-widget');
254
+ const widget = document.querySelector("authhero-widget");
204
255
 
205
- widget.addEventListener('formSubmit', async (e) => {
256
+ widget.addEventListener("formSubmit", async (e) => {
206
257
  const { data, screen } = e.detail;
207
-
258
+
208
259
  try {
209
260
  // Your auth library handles the HTTP request
210
- const response = await fetch('/u/flow/screen', {
211
- method: 'POST',
261
+ const response = await fetch("/u/flow/screen", {
262
+ method: "POST",
212
263
  body: data,
213
264
  });
214
-
265
+
215
266
  const nextScreen = await response.json();
216
267
  widget.screen = JSON.stringify(nextScreen);
217
268
  } catch (error) {
218
- widget.dispatchEvent(new CustomEvent('flowError', { detail: { error } }));
269
+ widget.dispatchEvent(new CustomEvent("flowError", { detail: { error } }));
219
270
  }
220
271
  });
221
272
 
222
- widget.addEventListener('linkClick', (e) => {
273
+ widget.addEventListener("linkClick", (e) => {
223
274
  // Handle navigation
224
275
  window.location.href = e.detail.href;
225
276
  });
@@ -230,14 +281,12 @@ widget.addEventListener('linkClick', (e) => {
230
281
  For simple use cases, the widget can handle HTTP requests automatically:
231
282
 
232
283
  ```html
233
- <authhero-widget
234
- api-url="/u/flow/screen"
235
- auto-submit="true">
236
- </authhero-widget>
284
+ <authhero-widget api-url="/u/flow/screen" auto-submit="true"> </authhero-widget>
237
285
  ```
238
286
 
239
287
  ⚠️ **Note**: Auto-submit mode is provided for convenience but is not recommended for production. Use the event-based approach for proper error handling, token management, and integration with auth libraries like Auth0 SPA SDK.
240
- ```
288
+
289
+ ````
241
290
 
242
291
  ## Customization
243
292
 
@@ -250,7 +299,7 @@ authhero-widget {
250
299
  --text-color: #333333;
251
300
  --border-radius: 8px;
252
301
  }
253
- ```
302
+ ````
254
303
 
255
304
  ### Server-Side Branding
256
305
 
@@ -276,7 +325,8 @@ See [`packages/authhero/FLOWS.md`](../authhero/FLOWS.md) for detailed integratio
276
325
  3. **Auth0 SPA SDK Integration** - Using `loginWithRedirect()` and callback handling
277
326
  4. **Custom Token Management** - Custom refresh token and session handling
278
327
  5. **Generic Forms** - Non-auth form use cases
279
- ```
328
+
329
+ ````
280
330
 
281
331
  ## Development
282
332
 
@@ -295,7 +345,7 @@ pnpm build
295
345
 
296
346
  # Run tests
297
347
  pnpm test
298
- ```
348
+ ````
299
349
 
300
350
  ### Demo Server
301
351
 
@@ -306,10 +356,12 @@ The widget includes a demo server at `demo-server/server.ts` that provides:
306
356
  - **Server-Driven UI**: Demonstrates how the widget integrates with a backend
307
357
 
308
358
  When you run `pnpm dev`, the demo is available at:
359
+
309
360
  - Path-based: http://localhost:3456/u2/login/identifier
310
361
  - Query-based: http://localhost:3456/u2/login?screen=identifier
311
362
 
312
363
  The demo server provides:
364
+
313
365
  - `GET /u2/screen/:screenId` - Returns screen configuration
314
366
  - `POST /u2/screen/:screenId` - Processes form submissions and returns next screen
315
367
  - Settings panel to customize theme, branding, and widget options
@@ -1 +1 @@
1
- import{p as e,b as a}from"./p-BzFenraS.js";export{s as setNonce}from"./p-BzFenraS.js";import{g as r}from"./p-DQuL1Twl.js";(()=>{const a=import.meta.url,r={};return""!==a&&(r.resourcesUrl=new URL(".",a).href),e(r)})().then((async e=>(await r(),a([["p-188ffffd",[[513,"authhero-node",{component:[16],value:[1],disabled:[4],passwordVisible:[32]}]]],["p-ea2660b2",[[513,"authhero-widget",{screen:[1],apiUrl:[1,"api-url"],branding:[1],theme:[1],loading:[1028],autoSubmit:[4,"auto-submit"],_screen:[32],_branding:[32],_theme:[32],formData:[32]},null,{screen:[{watchScreen:0}],branding:[{watchBranding:0}],theme:[{watchTheme:0}]}]]]],e))));
1
+ import{p as a,b as e}from"./p-BzFenraS.js";export{s as setNonce}from"./p-BzFenraS.js";import{g as t}from"./p-DQuL1Twl.js";(()=>{const e=import.meta.url,t={};return""!==e&&(t.resourcesUrl=new URL(".",e).href),a(t)})().then((async a=>(await t(),e([["p-188ffffd",[[513,"authhero-node",{component:[16],value:[1],disabled:[4],passwordVisible:[32]}]]],["p-6e32b31d",[[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}]}]]]],a))));
@@ -0,0 +1 @@
1
+ import{r as t,c as i,g as e,h as s}from"./p-BzFenraS.js";function a(t,i){if(void 0!==i)return`${i}px`;switch(t){case"pill":return"9999px";case"rounded":return"8px";case"sharp":return"0";default:return}}function r(t){if(!t)return{};const i={};if(t.colors?.primary&&(i["--ah-color-primary"]=t.colors.primary,i["--ah-color-primary-hover"]=t.colors.primary),t.colors?.page_background){const e=t.colors.page_background;"solid"===e.type&&e.start?i["--ah-page-bg"]=e.start:"gradient"===e.type&&e.start&&e.end&&(i["--ah-page-bg"]=`linear-gradient(${e.angle_deg??180}deg, ${e.start}, ${e.end})`)}return t.logo_url&&(i["--ah-logo-url"]=`url(${t.logo_url})`),t.font?.url&&(i["--ah-font-url"]=t.font.url),i}function o(t){if(!t)return{};const i={};if(t.borders){const e=t.borders;void 0!==e.widget_corner_radius&&(i["--ah-widget-radius"]=`${e.widget_corner_radius}px`),void 0!==e.widget_border_weight&&(i["--ah-widget-border-width"]=`${e.widget_border_weight}px`),!1===e.show_widget_shadow&&(i["--ah-widget-shadow"]="none");const s=a(e.buttons_style,e.button_border_radius);s&&(i["--ah-btn-radius"]=s),void 0!==e.button_border_weight&&(i["--ah-btn-border-width"]=`${e.button_border_weight}px`);const r=a(e.inputs_style,e.input_border_radius);r&&(i["--ah-input-radius"]=r),void 0!==e.input_border_weight&&(i["--ah-input-border-width"]=`${e.input_border_weight}px`)}if(t.colors){const e=t.colors;e.primary_button&&(i["--ah-color-primary"]=e.primary_button,i["--ah-color-primary-hover"]=e.primary_button),e.primary_button_label&&(i["--ah-btn-primary-text"]=e.primary_button_label),e.secondary_button_border&&(i["--ah-btn-secondary-border"]=e.secondary_button_border),e.secondary_button_label&&(i["--ah-btn-secondary-text"]=e.secondary_button_label),e.body_text&&(i["--ah-color-text"]=e.body_text),e.header&&(i["--ah-color-text-header"]=e.header),e.input_labels_placeholders&&(i["--ah-color-text-label"]=e.input_labels_placeholders,i["--ah-color-text-muted"]=e.input_labels_placeholders),e.input_filled_text&&(i["--ah-color-input-text"]=e.input_filled_text),e.widget_background&&(i["--ah-color-bg"]=e.widget_background),e.input_background&&(i["--ah-color-input-bg"]=e.input_background),e.widget_border&&(i["--ah-widget-border-color"]=e.widget_border),e.input_border&&(i["--ah-color-border"]=e.input_border),e.links_focused_components&&(i["--ah-color-link"]=e.links_focused_components),e.base_focus_color&&(i["--ah-color-focus-ring"]=e.base_focus_color),e.base_hover_color&&(i["--ah-color-primary-hover"]=e.base_hover_color),e.error&&(i["--ah-color-error"]=e.error),e.success&&(i["--ah-color-success"]=e.success),e.icons&&(i["--ah-color-icon"]=e.icons)}if(t.fonts){const e=t.fonts,s=e.reference_text_size||16;if(e.font_url&&(i["--ah-font-url"]=e.font_url),e.reference_text_size&&(i["--ah-font-size-base"]=`${e.reference_text_size}px`),e.title?.size){const t=Math.round(e.title.size/100*s);i["--ah-font-size-title"]=`${t}px`}if(e.subtitle?.size){const t=Math.round(e.subtitle.size/100*s);i["--ah-font-size-subtitle"]=`${t}px`}if(e.body_text?.size){const t=Math.round(e.body_text.size/100*s);i["--ah-font-size-body"]=`${t}px`}if(e.input_labels?.size){const t=Math.round(e.input_labels.size/100*s);i["--ah-font-size-label"]=`${t}px`}if(e.buttons_text?.size){const t=Math.round(e.buttons_text.size/100*s);i["--ah-font-size-btn"]=`${t}px`}if(e.links?.size){const t=Math.round(e.links.size/100*s);i["--ah-font-size-link"]=`${t}px`}"underlined"===e.links_style&&(i["--ah-link-decoration"]="underline"),void 0!==e.title?.bold&&(i["--ah-font-weight-title"]=e.title.bold?"700":"400"),void 0!==e.subtitle?.bold&&(i["--ah-font-weight-subtitle"]=e.subtitle.bold?"700":"400"),void 0!==e.body_text?.bold&&(i["--ah-font-weight-body"]=e.body_text.bold?"700":"400"),void 0!==e.input_labels?.bold&&(i["--ah-font-weight-label"]=e.input_labels.bold?"700":"400"),void 0!==e.buttons_text?.bold&&(i["--ah-font-weight-btn"]=e.buttons_text.bold?"600":"400"),void 0!==e.links?.bold&&(i["--ah-font-weight-link"]=e.links.bold?"700":"400")}if(t.widget){const e=t.widget;if(e.header_text_alignment&&(i["--ah-title-align"]=e.header_text_alignment),e.logo_height&&(i["--ah-logo-height"]=`${e.logo_height}px`),e.logo_position){const t={center:"center",left:"flex-start",right:"flex-end"};"none"===e.logo_position?i["--ah-logo-display"]="none":i["--ah-logo-align"]=t[e.logo_position]??"center"}e.social_buttons_layout&&("top"===e.social_buttons_layout?(i["--ah-social-order"]="0",i["--ah-divider-order"]="1",i["--ah-fields-order"]="2"):(i["--ah-social-order"]="2",i["--ah-divider-order"]="1",i["--ah-fields-order"]="0"))}if(t.page_background){const e=t.page_background;e.background_color&&(i["--ah-page-bg"]=e.background_color),e.background_image_url&&(i["--ah-page-bg-image"]=`url(${e.background_image_url})`)}return i}const n=class{constructor(e){t(this,e),this.formSubmit=i(this,"formSubmit"),this.buttonClick=i(this,"buttonClick"),this.linkClick=i(this,"linkClick"),this.navigate=i(this,"navigate"),this.flowComplete=i(this,"flowComplete"),this.flowError=i(this,"flowError"),this.screenChange=i(this,"screenChange")}get el(){return e(this)}screen;apiUrl;baseUrl;state;screenId;authParams;statePersistence="memory";storageKey="authhero_widget";branding;theme;loading=!1;autoSubmit=!1;autoNavigate;_screen;_authParams;_branding;_theme;formData={};formSubmit;buttonClick;linkClick;navigate;flowComplete;flowError;screenChange;watchScreen(t){if("string"==typeof t)try{this._screen=JSON.parse(t)}catch{console.error("Failed to parse screen JSON")}else this._screen=t;this._screen&&this.screenChange.emit(this._screen)}watchBranding(t){if("string"==typeof t)try{this._branding=JSON.parse(t)}catch{console.error("Failed to parse branding JSON")}else this._branding=t;this.applyThemeStyles()}watchTheme(t){if("string"==typeof t)try{this._theme=JSON.parse(t)}catch{console.error("Failed to parse theme JSON")}else this._theme=t;this.applyThemeStyles()}watchAuthParams(t){if("string"==typeof t)try{this._authParams=JSON.parse(t)}catch{console.error("Failed to parse authParams JSON")}else this._authParams=t}applyThemeStyles(){const t=(i=this._theme,{...r(this._branding),...o(i)});var i;!function(t,i){Object.entries(i).forEach((([i,e])=>{t.style.setProperty(i,e)}))}(this.el,t)}get shouldAutoNavigate(){return this.autoNavigate??this.autoSubmit}buildUrl(t){return this.baseUrl?new URL(t,this.baseUrl).toString():t}loadPersistedState(){if("url"===this.statePersistence){const t=new URL(window.location.href).searchParams.get("state");t&&!this.state&&(this.state=t)}else if("session"===this.statePersistence)try{const t=sessionStorage.getItem(`${this.storageKey}_state`);t&&!this.state&&(this.state=t);const i=sessionStorage.getItem(`${this.storageKey}_screenId`);i&&!this.screenId&&(this.screenId=i)}catch{}}persistState(){if("url"===this.statePersistence){const t=new URL(window.location.href);this.state&&t.searchParams.set("state",this.state),this.screenId&&t.searchParams.set("screen",this.screenId),window.history.replaceState({},"",t.toString())}else if("session"===this.statePersistence)try{this.state&&sessionStorage.setItem(`${this.storageKey}_state`,this.state),this.screenId&&sessionStorage.setItem(`${this.storageKey}_screenId`,this.screenId)}catch{}}async componentWillLoad(){this.watchScreen(this.screen),this.watchBranding(this.branding),this.watchTheme(this.theme),this.watchAuthParams(this.authParams),this.loadPersistedState(),this.apiUrl&&!this._screen&&await this.fetchScreen(this.screenId)}async fetchScreen(t,i){if(!this.apiUrl)return;const e=t||this.screenId;let s=this.apiUrl;e&&s.includes("{screenId}")&&(s=s.replace("{screenId}",encodeURIComponent(e)));const a=new URL(s,this.baseUrl||window.location.origin);this.state&&a.searchParams.set("state",this.state),i&&a.searchParams.set("nodeId",i),this.loading=!0;try{const t=await fetch(this.buildUrl(a.pathname+a.search),{credentials:"include",headers:{Accept:"application/json"}});if(t.ok){const i=await t.json();i.screen?(this._screen=i.screen,i.branding&&(this._branding=i.branding,this.applyThemeStyles()),i.state&&(this.state=i.state),i.screenId&&(this.screenId=i.screenId)):this._screen=i,this._screen&&(e&&e!==this.screenId&&(this.screenId=e),this.screenChange.emit(this._screen),this.persistState())}else{const i=await t.json().catch((()=>({message:"Failed to load screen"})));this.flowError.emit({message:i.message||"Failed to load screen"})}}catch(t){console.error("Failed to fetch screen:",t),this.flowError.emit({message:t instanceof Error?t.message:"Failed to fetch screen"})}finally{this.loading=!1}}handleInputChange=(t,i)=>{this.formData={...this.formData,[t]:i}};handleSubmit=async t=>{if(t.preventDefault(),this._screen&&(this.formSubmit.emit({screen:this._screen,data:this.formData}),this.autoSubmit)){this.loading=!0;try{const t=await fetch(this.buildUrl(this._screen.action),{method:this._screen.method,credentials:"include",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({data:this.formData})}),i=t.headers.get("content-type");if(i?.includes("application/json")){const i=await t.json();i.redirect?(this.flowComplete.emit({redirectUrl:i.redirect}),this.navigate.emit({url:i.redirect}),this.shouldAutoNavigate&&(window.location.href=i.redirect)):i.screen?(this._screen=i.screen,this.formData={},this.screenChange.emit(i.screen),i.screenId&&(this.screenId=i.screenId),this.persistState(),i.branding&&(this._branding=i.branding,this.applyThemeStyles()),i.state&&(this.state=i.state,this.persistState())):i.complete&&this.flowComplete.emit({}),!t.ok&&i.screen&&(this._screen=i.screen,this.screenChange.emit(i.screen))}}catch(t){console.error("Form submission failed:",t),this.flowError.emit({message:t instanceof Error?t.message:"Form submission failed"})}finally{this.loading=!1}}};handleButtonClick=t=>{"submit"!==t.type?(this.buttonClick.emit(t),"SOCIAL"===t.type&&t.value&&this.shouldAutoNavigate?this.handleSocialLogin(t.value):"RESEND_BUTTON"===t.type&&this.shouldAutoNavigate&&this.handleResend()):this.handleSubmit({preventDefault:()=>{}})};handleSocialLogin(t){const i=this._authParams||{},e={connection:t};this.state?e.state=this.state:i.state&&(e.state=i.state),i.client_id&&(e.client_id=i.client_id),i.redirect_uri&&(e.redirect_uri=i.redirect_uri),i.scope&&(e.scope=i.scope),i.audience&&(e.audience=i.audience),i.nonce&&(e.nonce=i.nonce),i.response_type&&(e.response_type=i.response_type);const s=this.buildUrl("/authorize?"+new URLSearchParams(e).toString());this.navigate.emit({url:s}),window.location.href=s}async handleResend(){if(this._screen?.action)try{const t=this._screen.action+(this._screen.action.includes("?")?"&":"?")+"action=resend";await fetch(this.buildUrl(t),{method:"POST",credentials:"include"})}catch(t){console.error("Resend failed:",t)}}handleLinkClick=(t,i)=>{this.linkClick.emit({id:i.id,href:i.href,text:i.text}),this.shouldAutoNavigate||t.preventDefault()};getScreenErrors(){return this._screen?.messages?.filter((t=>"error"===t.type))||[]}getScreenSuccesses(){return this._screen?.messages?.filter((t=>"success"===t.type))||[]}getOrderedComponents(){return this._screen?[...this._screen.components].filter((t=>!1!==t.visible)).sort(((t,i)=>(t.order??0)-(i.order??0))):[]}isSocialComponent(t){return"SOCIAL"===t.type}isDividerComponent(t){return"DIVIDER"===t.type}render(){if(this.loading&&!this._screen)return s("div",{class:"widget-container"},s("div",{class:"loading-spinner"}));if(!this._screen)return s("div",{class:"widget-container"},s("div",{class:"error-message"},"No screen configuration provided"));const t=this.getScreenErrors(),i=this.getScreenSuccesses(),e=this.getOrderedComponents(),a=e.filter((t=>this.isSocialComponent(t))),r=e.filter((t=>!this.isSocialComponent(t)&&!this.isDividerComponent(t))),o=e.some((t=>this.isDividerComponent(t))),n=this._theme?.widget?.logo_url||this._branding?.logo_url;return s("div",{class:"widget-container",part:"container"},s("header",{class:"widget-header",part:"header"},n&&s("div",{class:"logo-wrapper",part:"logo-wrapper"},s("img",{class:"logo",part:"logo",src:n,alt:"Logo"})),this._screen.title&&s("h1",{class:"title",part:"title"},this._screen.title),this._screen.description&&s("p",{class:"description",part:"description"},this._screen.description)),s("div",{class:"widget-body",part:"body"},t.map((t=>s("div",{class:"message message-error",part:"message message-error",key:t.id??t.text},t.text))),i.map((t=>s("div",{class:"message message-success",part:"message message-success",key:t.id??t.text},t.text))),s("form",{onSubmit:this.handleSubmit,part:"form"},s("div",{class:"form-content"},a.length>0&&s("div",{class:"social-section",part:"social-section"},a.map((t=>s("authhero-node",{key:t.id,component:t,value:this.formData[t.id],onFieldChange:t=>this.handleInputChange(t.detail.id,t.detail.value),onButtonClick:t=>this.handleButtonClick(t.detail),disabled:this.loading})))),a.length>0&&r.length>0&&o&&s("div",{class:"divider",part:"divider"},s("span",{class:"divider-text"},"Or")),s("div",{class:"fields-section",part:"fields-section"},r.map((t=>s("authhero-node",{key:t.id,component:t,value:this.formData[t.id],onFieldChange:t=>this.handleInputChange(t.detail.id,t.detail.value),onButtonClick:t=>this.handleButtonClick(t.detail),disabled:this.loading})))))),this._screen.links&&this._screen.links.length>0&&s("div",{class:"links",part:"links"},this._screen.links.map((t=>s("span",{class:"link-wrapper",part:"link-wrapper",key:t.id??t.href},t.linkText?s("span",null,t.text," ",s("a",{href:t.href,class:"link",part:"link",onClick:i=>this.handleLinkClick(i,{id:t.id,href:t.href,text:t.linkText||t.text})},t.linkText)):s("a",{href:t.href,class:"link",part:"link",onClick:i=>this.handleLinkClick(i,{id:t.id,href:t.href,text:t.text})},t.text)))))))}static get watchers(){return{screen:[{watchScreen:0}],branding:[{watchBranding:0}],theme:[{watchTheme:0}],authParams:[{watchAuthParams:0}]}}};n.style=":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 8px);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}.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}}";export{n as authhero_widget}