@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 +118 -66
- package/dist/authhero-widget/authhero-widget.esm.js +1 -1
- package/dist/authhero-widget/p-6e32b31d.entry.js +1 -0
- package/dist/cjs/authhero-widget.cjs.entry.js +311 -32
- package/dist/cjs/authhero-widget.cjs.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/authhero-widget/authhero-widget.js +470 -34
- package/dist/components/authhero-widget.js +1 -1
- package/dist/esm/authhero-widget.entry.js +311 -32
- package/dist/esm/authhero-widget.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/types/components/authhero-widget/authhero-widget.d.ts +96 -4
- package/dist/types/components.d.ts +66 -4
- package/hydrate/index.js +319 -32
- package/hydrate/index.mjs +319 -32
- package/package.json +1 -1
- package/dist/authhero-widget/p-ea2660b2.entry.js +0 -1
package/hydrate/index.js
CHANGED
|
@@ -5616,8 +5616,43 @@ class AuthheroWidget {
|
|
|
5616
5616
|
/**
|
|
5617
5617
|
* API endpoint to fetch the initial screen from.
|
|
5618
5618
|
* If provided, the widget will fetch the screen on load.
|
|
5619
|
+
* Can include {screenId} placeholder which will be replaced with the current screen.
|
|
5620
|
+
* Example: "/u2/screen/{screenId}" or "https://auth.example.com/u2/screen/{screenId}"
|
|
5619
5621
|
*/
|
|
5620
5622
|
apiUrl;
|
|
5623
|
+
/**
|
|
5624
|
+
* Base URL for all API calls. Used when widget is embedded on a different domain.
|
|
5625
|
+
* If not provided, relative URLs are used.
|
|
5626
|
+
* Example: "https://auth.example.com"
|
|
5627
|
+
*/
|
|
5628
|
+
baseUrl;
|
|
5629
|
+
/**
|
|
5630
|
+
* Login session state token. Required for social login and maintaining session.
|
|
5631
|
+
*/
|
|
5632
|
+
state;
|
|
5633
|
+
/**
|
|
5634
|
+
* Current screen ID. Used with apiUrl to fetch screen configuration.
|
|
5635
|
+
* When statePersistence is 'url', this is synced with the URL.
|
|
5636
|
+
*/
|
|
5637
|
+
screenId;
|
|
5638
|
+
/**
|
|
5639
|
+
* OAuth/OIDC parameters for social login redirects.
|
|
5640
|
+
* Can be passed as a JSON string or object.
|
|
5641
|
+
*/
|
|
5642
|
+
authParams;
|
|
5643
|
+
/**
|
|
5644
|
+
* Where to persist state and screen ID.
|
|
5645
|
+
* - 'url': Updates URL path/query (default for standalone pages)
|
|
5646
|
+
* - 'session': Uses sessionStorage (for embedded widgets)
|
|
5647
|
+
* - 'memory': No persistence, state only in memory
|
|
5648
|
+
* @default 'memory'
|
|
5649
|
+
*/
|
|
5650
|
+
statePersistence = "memory";
|
|
5651
|
+
/**
|
|
5652
|
+
* Storage key prefix for session/local storage persistence.
|
|
5653
|
+
* @default 'authhero_widget'
|
|
5654
|
+
*/
|
|
5655
|
+
storageKey = "authhero_widget";
|
|
5621
5656
|
/**
|
|
5622
5657
|
* Branding configuration from AuthHero API.
|
|
5623
5658
|
* Controls logo, primary color, and page background.
|
|
@@ -5642,10 +5677,21 @@ class AuthheroWidget {
|
|
|
5642
5677
|
* @default false
|
|
5643
5678
|
*/
|
|
5644
5679
|
autoSubmit = false;
|
|
5680
|
+
/**
|
|
5681
|
+
* Whether the widget should handle navigation automatically.
|
|
5682
|
+
* When true, social login buttons redirect, links navigate, etc.
|
|
5683
|
+
* When false, only events are emitted.
|
|
5684
|
+
* @default false (same as autoSubmit when not specified)
|
|
5685
|
+
*/
|
|
5686
|
+
autoNavigate;
|
|
5645
5687
|
/**
|
|
5646
5688
|
* Internal parsed screen state.
|
|
5647
5689
|
*/
|
|
5648
5690
|
_screen;
|
|
5691
|
+
/**
|
|
5692
|
+
* Internal parsed auth params state.
|
|
5693
|
+
*/
|
|
5694
|
+
_authParams;
|
|
5649
5695
|
/**
|
|
5650
5696
|
* Internal parsed branding state.
|
|
5651
5697
|
*/
|
|
@@ -5692,12 +5738,12 @@ class AuthheroWidget {
|
|
|
5692
5738
|
*/
|
|
5693
5739
|
screenChange;
|
|
5694
5740
|
watchScreen(newValue) {
|
|
5695
|
-
if (typeof newValue ===
|
|
5741
|
+
if (typeof newValue === "string") {
|
|
5696
5742
|
try {
|
|
5697
5743
|
this._screen = JSON.parse(newValue);
|
|
5698
5744
|
}
|
|
5699
5745
|
catch {
|
|
5700
|
-
console.error(
|
|
5746
|
+
console.error("Failed to parse screen JSON");
|
|
5701
5747
|
}
|
|
5702
5748
|
}
|
|
5703
5749
|
else {
|
|
@@ -5708,12 +5754,12 @@ class AuthheroWidget {
|
|
|
5708
5754
|
}
|
|
5709
5755
|
}
|
|
5710
5756
|
watchBranding(newValue) {
|
|
5711
|
-
if (typeof newValue ===
|
|
5757
|
+
if (typeof newValue === "string") {
|
|
5712
5758
|
try {
|
|
5713
5759
|
this._branding = JSON.parse(newValue);
|
|
5714
5760
|
}
|
|
5715
5761
|
catch {
|
|
5716
|
-
console.error(
|
|
5762
|
+
console.error("Failed to parse branding JSON");
|
|
5717
5763
|
}
|
|
5718
5764
|
}
|
|
5719
5765
|
else {
|
|
@@ -5722,12 +5768,12 @@ class AuthheroWidget {
|
|
|
5722
5768
|
this.applyThemeStyles();
|
|
5723
5769
|
}
|
|
5724
5770
|
watchTheme(newValue) {
|
|
5725
|
-
if (typeof newValue ===
|
|
5771
|
+
if (typeof newValue === "string") {
|
|
5726
5772
|
try {
|
|
5727
5773
|
this._theme = JSON.parse(newValue);
|
|
5728
5774
|
}
|
|
5729
5775
|
catch {
|
|
5730
|
-
console.error(
|
|
5776
|
+
console.error("Failed to parse theme JSON");
|
|
5731
5777
|
}
|
|
5732
5778
|
}
|
|
5733
5779
|
else {
|
|
@@ -5735,6 +5781,19 @@ class AuthheroWidget {
|
|
|
5735
5781
|
}
|
|
5736
5782
|
this.applyThemeStyles();
|
|
5737
5783
|
}
|
|
5784
|
+
watchAuthParams(newValue) {
|
|
5785
|
+
if (typeof newValue === "string") {
|
|
5786
|
+
try {
|
|
5787
|
+
this._authParams = JSON.parse(newValue);
|
|
5788
|
+
}
|
|
5789
|
+
catch {
|
|
5790
|
+
console.error("Failed to parse authParams JSON");
|
|
5791
|
+
}
|
|
5792
|
+
}
|
|
5793
|
+
else {
|
|
5794
|
+
this._authParams = newValue;
|
|
5795
|
+
}
|
|
5796
|
+
}
|
|
5738
5797
|
/**
|
|
5739
5798
|
* Apply branding and theme as CSS custom properties
|
|
5740
5799
|
*/
|
|
@@ -5742,36 +5801,164 @@ class AuthheroWidget {
|
|
|
5742
5801
|
const vars = mergeThemeVars(this._branding, this._theme);
|
|
5743
5802
|
applyCssVars(this.el, vars);
|
|
5744
5803
|
}
|
|
5804
|
+
/**
|
|
5805
|
+
* Get the effective autoNavigate value (defaults to autoSubmit if not set)
|
|
5806
|
+
*/
|
|
5807
|
+
get shouldAutoNavigate() {
|
|
5808
|
+
return this.autoNavigate ?? this.autoSubmit;
|
|
5809
|
+
}
|
|
5810
|
+
/**
|
|
5811
|
+
* Build the full URL for API calls
|
|
5812
|
+
*/
|
|
5813
|
+
buildUrl(path) {
|
|
5814
|
+
if (this.baseUrl) {
|
|
5815
|
+
return new URL(path, this.baseUrl).toString();
|
|
5816
|
+
}
|
|
5817
|
+
return path;
|
|
5818
|
+
}
|
|
5819
|
+
/**
|
|
5820
|
+
* Load state from URL or storage based on statePersistence setting
|
|
5821
|
+
*/
|
|
5822
|
+
loadPersistedState() {
|
|
5823
|
+
if (this.statePersistence === "url") {
|
|
5824
|
+
const url = new URL(window.location.href);
|
|
5825
|
+
const stateParam = url.searchParams.get("state");
|
|
5826
|
+
if (stateParam && !this.state) {
|
|
5827
|
+
this.state = stateParam;
|
|
5828
|
+
}
|
|
5829
|
+
}
|
|
5830
|
+
else if (this.statePersistence === "session") {
|
|
5831
|
+
try {
|
|
5832
|
+
const stored = sessionStorage.getItem(`${this.storageKey}_state`);
|
|
5833
|
+
if (stored && !this.state) {
|
|
5834
|
+
this.state = stored;
|
|
5835
|
+
}
|
|
5836
|
+
const storedScreenId = sessionStorage.getItem(`${this.storageKey}_screenId`);
|
|
5837
|
+
if (storedScreenId && !this.screenId) {
|
|
5838
|
+
this.screenId = storedScreenId;
|
|
5839
|
+
}
|
|
5840
|
+
}
|
|
5841
|
+
catch {
|
|
5842
|
+
// sessionStorage not available
|
|
5843
|
+
}
|
|
5844
|
+
}
|
|
5845
|
+
}
|
|
5846
|
+
/**
|
|
5847
|
+
* Save state to URL or storage based on statePersistence setting
|
|
5848
|
+
*/
|
|
5849
|
+
persistState() {
|
|
5850
|
+
if (this.statePersistence === "url") {
|
|
5851
|
+
const url = new URL(window.location.href);
|
|
5852
|
+
if (this.state) {
|
|
5853
|
+
url.searchParams.set("state", this.state);
|
|
5854
|
+
}
|
|
5855
|
+
if (this.screenId) {
|
|
5856
|
+
url.searchParams.set("screen", this.screenId);
|
|
5857
|
+
}
|
|
5858
|
+
window.history.replaceState({}, "", url.toString());
|
|
5859
|
+
}
|
|
5860
|
+
else if (this.statePersistence === "session") {
|
|
5861
|
+
try {
|
|
5862
|
+
if (this.state) {
|
|
5863
|
+
sessionStorage.setItem(`${this.storageKey}_state`, this.state);
|
|
5864
|
+
}
|
|
5865
|
+
if (this.screenId) {
|
|
5866
|
+
sessionStorage.setItem(`${this.storageKey}_screenId`, this.screenId);
|
|
5867
|
+
}
|
|
5868
|
+
}
|
|
5869
|
+
catch {
|
|
5870
|
+
// sessionStorage not available
|
|
5871
|
+
}
|
|
5872
|
+
}
|
|
5873
|
+
}
|
|
5745
5874
|
async componentWillLoad() {
|
|
5746
5875
|
// Parse initial props
|
|
5747
5876
|
this.watchScreen(this.screen);
|
|
5748
5877
|
this.watchBranding(this.branding);
|
|
5749
5878
|
this.watchTheme(this.theme);
|
|
5879
|
+
this.watchAuthParams(this.authParams);
|
|
5880
|
+
// Load persisted state if available
|
|
5881
|
+
this.loadPersistedState();
|
|
5750
5882
|
// Fetch screen from API if URL provided and no screen prop
|
|
5751
5883
|
if (this.apiUrl && !this._screen) {
|
|
5752
|
-
await this.fetchScreen();
|
|
5884
|
+
await this.fetchScreen(this.screenId);
|
|
5753
5885
|
}
|
|
5754
5886
|
}
|
|
5755
|
-
|
|
5887
|
+
/**
|
|
5888
|
+
* Fetch screen configuration from the API
|
|
5889
|
+
* @param screenIdOverride Optional screen ID to fetch (overrides this.screenId)
|
|
5890
|
+
* @param nodeId Optional node ID for flow navigation
|
|
5891
|
+
*/
|
|
5892
|
+
async fetchScreen(screenIdOverride, nodeId) {
|
|
5756
5893
|
if (!this.apiUrl)
|
|
5757
5894
|
return;
|
|
5895
|
+
const currentScreenId = screenIdOverride || this.screenId;
|
|
5896
|
+
// Build the API URL, replacing {screenId} placeholder if present
|
|
5897
|
+
let url = this.apiUrl;
|
|
5898
|
+
if (currentScreenId && url.includes("{screenId}")) {
|
|
5899
|
+
url = url.replace("{screenId}", encodeURIComponent(currentScreenId));
|
|
5900
|
+
}
|
|
5901
|
+
// Add state and nodeId as query params
|
|
5902
|
+
const urlObj = new URL(url, this.baseUrl || window.location.origin);
|
|
5903
|
+
if (this.state) {
|
|
5904
|
+
urlObj.searchParams.set("state", this.state);
|
|
5905
|
+
}
|
|
5906
|
+
if (nodeId) {
|
|
5907
|
+
urlObj.searchParams.set("nodeId", nodeId);
|
|
5908
|
+
}
|
|
5758
5909
|
this.loading = true;
|
|
5759
5910
|
try {
|
|
5760
|
-
const response = await fetch(this.
|
|
5761
|
-
credentials:
|
|
5911
|
+
const response = await fetch(this.buildUrl(urlObj.pathname + urlObj.search), {
|
|
5912
|
+
credentials: "include",
|
|
5762
5913
|
headers: {
|
|
5763
|
-
Accept:
|
|
5914
|
+
Accept: "application/json",
|
|
5764
5915
|
},
|
|
5765
5916
|
});
|
|
5766
5917
|
if (response.ok) {
|
|
5767
|
-
|
|
5918
|
+
const data = await response.json();
|
|
5919
|
+
// Handle different response formats
|
|
5920
|
+
if (data.screen) {
|
|
5921
|
+
this._screen = data.screen;
|
|
5922
|
+
if (data.branding) {
|
|
5923
|
+
this._branding = data.branding;
|
|
5924
|
+
this.applyThemeStyles();
|
|
5925
|
+
}
|
|
5926
|
+
// Update state if returned
|
|
5927
|
+
if (data.state) {
|
|
5928
|
+
this.state = data.state;
|
|
5929
|
+
}
|
|
5930
|
+
// Update screenId if returned in response
|
|
5931
|
+
if (data.screenId) {
|
|
5932
|
+
this.screenId = data.screenId;
|
|
5933
|
+
}
|
|
5934
|
+
}
|
|
5935
|
+
else {
|
|
5936
|
+
// Response is the screen itself
|
|
5937
|
+
this._screen = data;
|
|
5938
|
+
}
|
|
5768
5939
|
if (this._screen) {
|
|
5940
|
+
// If we fetched with a screenId override, update our stored screenId
|
|
5941
|
+
if (currentScreenId && currentScreenId !== this.screenId) {
|
|
5942
|
+
this.screenId = currentScreenId;
|
|
5943
|
+
}
|
|
5769
5944
|
this.screenChange.emit(this._screen);
|
|
5945
|
+
this.persistState();
|
|
5770
5946
|
}
|
|
5771
5947
|
}
|
|
5948
|
+
else {
|
|
5949
|
+
const error = await response
|
|
5950
|
+
.json()
|
|
5951
|
+
.catch(() => ({ message: "Failed to load screen" }));
|
|
5952
|
+
this.flowError.emit({
|
|
5953
|
+
message: error.message || "Failed to load screen",
|
|
5954
|
+
});
|
|
5955
|
+
}
|
|
5772
5956
|
}
|
|
5773
5957
|
catch (error) {
|
|
5774
|
-
console.error(
|
|
5958
|
+
console.error("Failed to fetch screen:", error);
|
|
5959
|
+
this.flowError.emit({
|
|
5960
|
+
message: error instanceof Error ? error.message : "Failed to fetch screen",
|
|
5961
|
+
});
|
|
5775
5962
|
}
|
|
5776
5963
|
finally {
|
|
5777
5964
|
this.loading = false;
|
|
@@ -5799,17 +5986,17 @@ class AuthheroWidget {
|
|
|
5799
5986
|
// Submit to the server
|
|
5800
5987
|
this.loading = true;
|
|
5801
5988
|
try {
|
|
5802
|
-
const response = await fetch(this._screen.action, {
|
|
5989
|
+
const response = await fetch(this.buildUrl(this._screen.action), {
|
|
5803
5990
|
method: this._screen.method,
|
|
5804
|
-
credentials:
|
|
5991
|
+
credentials: "include",
|
|
5805
5992
|
headers: {
|
|
5806
|
-
|
|
5807
|
-
Accept:
|
|
5993
|
+
"Content-Type": "application/json",
|
|
5994
|
+
Accept: "application/json",
|
|
5808
5995
|
},
|
|
5809
5996
|
body: JSON.stringify({ data: this.formData }),
|
|
5810
5997
|
});
|
|
5811
|
-
const contentType = response.headers.get(
|
|
5812
|
-
if (contentType?.includes(
|
|
5998
|
+
const contentType = response.headers.get("content-type");
|
|
5999
|
+
if (contentType?.includes("application/json")) {
|
|
5813
6000
|
const result = await response.json();
|
|
5814
6001
|
// Handle different response types
|
|
5815
6002
|
if (result.redirect) {
|
|
@@ -5817,17 +6004,31 @@ class AuthheroWidget {
|
|
|
5817
6004
|
this.flowComplete.emit({ redirectUrl: result.redirect });
|
|
5818
6005
|
// Also emit navigate for backwards compatibility
|
|
5819
6006
|
this.navigate.emit({ url: result.redirect });
|
|
6007
|
+
// Auto-navigate if enabled
|
|
6008
|
+
if (this.shouldAutoNavigate) {
|
|
6009
|
+
window.location.href = result.redirect;
|
|
6010
|
+
}
|
|
5820
6011
|
}
|
|
5821
6012
|
else if (result.screen) {
|
|
5822
6013
|
// Next screen
|
|
5823
6014
|
this._screen = result.screen;
|
|
5824
6015
|
this.formData = {};
|
|
5825
6016
|
this.screenChange.emit(result.screen);
|
|
6017
|
+
// Update screenId if returned in response
|
|
6018
|
+
if (result.screenId) {
|
|
6019
|
+
this.screenId = result.screenId;
|
|
6020
|
+
}
|
|
6021
|
+
this.persistState();
|
|
5826
6022
|
// Apply branding if included
|
|
5827
6023
|
if (result.branding) {
|
|
5828
6024
|
this._branding = result.branding;
|
|
5829
6025
|
this.applyThemeStyles();
|
|
5830
6026
|
}
|
|
6027
|
+
// Update state if returned
|
|
6028
|
+
if (result.state) {
|
|
6029
|
+
this.state = result.state;
|
|
6030
|
+
this.persistState();
|
|
6031
|
+
}
|
|
5831
6032
|
}
|
|
5832
6033
|
else if (result.complete) {
|
|
5833
6034
|
// Flow complete without redirect
|
|
@@ -5841,9 +6042,9 @@ class AuthheroWidget {
|
|
|
5841
6042
|
}
|
|
5842
6043
|
}
|
|
5843
6044
|
catch (err) {
|
|
5844
|
-
console.error(
|
|
6045
|
+
console.error("Form submission failed:", err);
|
|
5845
6046
|
this.flowError.emit({
|
|
5846
|
-
message: err instanceof Error ? err.message :
|
|
6047
|
+
message: err instanceof Error ? err.message : "Form submission failed",
|
|
5847
6048
|
});
|
|
5848
6049
|
}
|
|
5849
6050
|
finally {
|
|
@@ -5852,14 +6053,79 @@ class AuthheroWidget {
|
|
|
5852
6053
|
};
|
|
5853
6054
|
handleButtonClick = (detail) => {
|
|
5854
6055
|
// If this is a submit button click, trigger form submission
|
|
5855
|
-
if (detail.type ===
|
|
6056
|
+
if (detail.type === "submit") {
|
|
5856
6057
|
// Create a synthetic submit event and call handleSubmit
|
|
5857
6058
|
const syntheticEvent = { preventDefault: () => { } };
|
|
5858
6059
|
this.handleSubmit(syntheticEvent);
|
|
5859
6060
|
return;
|
|
5860
6061
|
}
|
|
6062
|
+
// Always emit the event
|
|
5861
6063
|
this.buttonClick.emit(detail);
|
|
6064
|
+
// Handle social login if autoNavigate is enabled
|
|
6065
|
+
if (detail.type === "SOCIAL" && detail.value && this.shouldAutoNavigate) {
|
|
6066
|
+
this.handleSocialLogin(detail.value);
|
|
6067
|
+
return;
|
|
6068
|
+
}
|
|
6069
|
+
// Handle resend button
|
|
6070
|
+
if (detail.type === "RESEND_BUTTON" && this.shouldAutoNavigate) {
|
|
6071
|
+
this.handleResend();
|
|
6072
|
+
return;
|
|
6073
|
+
}
|
|
5862
6074
|
};
|
|
6075
|
+
/**
|
|
6076
|
+
* Handle social login redirect
|
|
6077
|
+
*/
|
|
6078
|
+
handleSocialLogin(connection) {
|
|
6079
|
+
const params = this._authParams || {};
|
|
6080
|
+
const queryParams = {
|
|
6081
|
+
connection,
|
|
6082
|
+
};
|
|
6083
|
+
// Add state
|
|
6084
|
+
if (this.state) {
|
|
6085
|
+
queryParams.state = this.state;
|
|
6086
|
+
}
|
|
6087
|
+
else if (params.state) {
|
|
6088
|
+
queryParams.state = params.state;
|
|
6089
|
+
}
|
|
6090
|
+
// Add client_id
|
|
6091
|
+
if (params.client_id) {
|
|
6092
|
+
queryParams.client_id = params.client_id;
|
|
6093
|
+
}
|
|
6094
|
+
// Add optional params
|
|
6095
|
+
if (params.redirect_uri)
|
|
6096
|
+
queryParams.redirect_uri = params.redirect_uri;
|
|
6097
|
+
if (params.scope)
|
|
6098
|
+
queryParams.scope = params.scope;
|
|
6099
|
+
if (params.audience)
|
|
6100
|
+
queryParams.audience = params.audience;
|
|
6101
|
+
if (params.nonce)
|
|
6102
|
+
queryParams.nonce = params.nonce;
|
|
6103
|
+
if (params.response_type)
|
|
6104
|
+
queryParams.response_type = params.response_type;
|
|
6105
|
+
const socialUrl = this.buildUrl("/authorize?" + new URLSearchParams(queryParams).toString());
|
|
6106
|
+
// Emit navigate event and redirect
|
|
6107
|
+
this.navigate.emit({ url: socialUrl });
|
|
6108
|
+
window.location.href = socialUrl;
|
|
6109
|
+
}
|
|
6110
|
+
/**
|
|
6111
|
+
* Handle resend button click (e.g., resend OTP code)
|
|
6112
|
+
*/
|
|
6113
|
+
async handleResend() {
|
|
6114
|
+
if (!this._screen?.action)
|
|
6115
|
+
return;
|
|
6116
|
+
try {
|
|
6117
|
+
const url = this._screen.action +
|
|
6118
|
+
(this._screen.action.includes("?") ? "&" : "?") +
|
|
6119
|
+
"action=resend";
|
|
6120
|
+
await fetch(this.buildUrl(url), {
|
|
6121
|
+
method: "POST",
|
|
6122
|
+
credentials: "include",
|
|
6123
|
+
});
|
|
6124
|
+
}
|
|
6125
|
+
catch (error) {
|
|
6126
|
+
console.error("Resend failed:", error);
|
|
6127
|
+
}
|
|
6128
|
+
}
|
|
5863
6129
|
handleLinkClick = (e, link) => {
|
|
5864
6130
|
// Emit the event so the consuming app can handle it
|
|
5865
6131
|
this.linkClick.emit({
|
|
@@ -5867,9 +6133,9 @@ class AuthheroWidget {
|
|
|
5867
6133
|
href: link.href,
|
|
5868
6134
|
text: link.text,
|
|
5869
6135
|
});
|
|
5870
|
-
// If
|
|
6136
|
+
// If autoNavigate is enabled, let the browser handle the navigation
|
|
5871
6137
|
// Otherwise, prevent default and let the app decide
|
|
5872
|
-
if (!this.
|
|
6138
|
+
if (!this.shouldAutoNavigate) {
|
|
5873
6139
|
e.preventDefault();
|
|
5874
6140
|
}
|
|
5875
6141
|
};
|
|
@@ -5877,13 +6143,13 @@ class AuthheroWidget {
|
|
|
5877
6143
|
* Get error messages from the screen-level messages array.
|
|
5878
6144
|
*/
|
|
5879
6145
|
getScreenErrors() {
|
|
5880
|
-
return this._screen?.messages?.filter((m) => m.type ===
|
|
6146
|
+
return this._screen?.messages?.filter((m) => m.type === "error") || [];
|
|
5881
6147
|
}
|
|
5882
6148
|
/**
|
|
5883
6149
|
* Get success messages from the screen-level messages array.
|
|
5884
6150
|
*/
|
|
5885
6151
|
getScreenSuccesses() {
|
|
5886
|
-
return this._screen?.messages?.filter((m) => m.type ===
|
|
6152
|
+
return this._screen?.messages?.filter((m) => m.type === "success") || [];
|
|
5887
6153
|
}
|
|
5888
6154
|
/**
|
|
5889
6155
|
* Sort components by order.
|
|
@@ -5901,13 +6167,13 @@ class AuthheroWidget {
|
|
|
5901
6167
|
isSocialComponent(component) {
|
|
5902
6168
|
// Check the type property directly - FormComponent has a 'type' field
|
|
5903
6169
|
// SocialField has type 'SOCIAL'
|
|
5904
|
-
return component.type ===
|
|
6170
|
+
return component.type === "SOCIAL";
|
|
5905
6171
|
}
|
|
5906
6172
|
/**
|
|
5907
6173
|
* Check if a component is a divider.
|
|
5908
6174
|
*/
|
|
5909
6175
|
isDividerComponent(component) {
|
|
5910
|
-
return component.type ===
|
|
6176
|
+
return component.type === "DIVIDER";
|
|
5911
6177
|
}
|
|
5912
6178
|
render() {
|
|
5913
6179
|
if (this.loading && !this._screen) {
|
|
@@ -5920,12 +6186,22 @@ class AuthheroWidget {
|
|
|
5920
6186
|
const screenSuccesses = this.getScreenSuccesses();
|
|
5921
6187
|
const components = this.getOrderedComponents();
|
|
5922
6188
|
// Separate social, divider, and field components for layout ordering
|
|
5923
|
-
const socialComponents = components.filter(c => this.isSocialComponent(c));
|
|
5924
|
-
const fieldComponents = components.filter(c => !this.isSocialComponent(c) && !this.isDividerComponent(c));
|
|
5925
|
-
const hasDivider = components.some(c => this.isDividerComponent(c));
|
|
6189
|
+
const socialComponents = components.filter((c) => this.isSocialComponent(c));
|
|
6190
|
+
const fieldComponents = components.filter((c) => !this.isSocialComponent(c) && !this.isDividerComponent(c));
|
|
6191
|
+
const hasDivider = components.some((c) => this.isDividerComponent(c));
|
|
5926
6192
|
// Get logo URL from theme.widget (takes precedence) or branding
|
|
5927
6193
|
const logoUrl = this._theme?.widget?.logo_url || this._branding?.logo_url;
|
|
5928
|
-
return (hAsync("div", { class: "widget-container", part: "container" }, hAsync("header", { class: "widget-header", part: "header" }, logoUrl && (hAsync("div", { class: "logo-wrapper", part: "logo-wrapper" }, hAsync("img", { class: "logo", part: "logo", src: logoUrl, alt: "Logo" }))), this._screen.title && (hAsync("h1", { class: "title", part: "title" }, this._screen.title)), this._screen.description && (hAsync("p", { class: "description", part: "description" }, this._screen.description))), hAsync("div", { class: "widget-body", part: "body" }, screenErrors.map((err) => (hAsync("div", { class: "message message-error", part: "message message-error", key: err.id ?? err.text }, err.text))), screenSuccesses.map((msg) => (hAsync("div", { class: "message message-success", part: "message message-success", key: msg.id ?? msg.text }, msg.text))), hAsync("form", { onSubmit: this.handleSubmit, part: "form" }, hAsync("div", { class: "form-content" }, socialComponents.length > 0 && (hAsync("div", { class: "social-section", part: "social-section" }, socialComponents.map((component) => (hAsync("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading }))))), socialComponents.length > 0 &&
|
|
6194
|
+
return (hAsync("div", { class: "widget-container", part: "container" }, hAsync("header", { class: "widget-header", part: "header" }, logoUrl && (hAsync("div", { class: "logo-wrapper", part: "logo-wrapper" }, hAsync("img", { class: "logo", part: "logo", src: logoUrl, alt: "Logo" }))), this._screen.title && (hAsync("h1", { class: "title", part: "title" }, this._screen.title)), this._screen.description && (hAsync("p", { class: "description", part: "description" }, this._screen.description))), hAsync("div", { class: "widget-body", part: "body" }, screenErrors.map((err) => (hAsync("div", { class: "message message-error", part: "message message-error", key: err.id ?? err.text }, err.text))), screenSuccesses.map((msg) => (hAsync("div", { class: "message message-success", part: "message message-success", key: msg.id ?? msg.text }, msg.text))), hAsync("form", { onSubmit: this.handleSubmit, part: "form" }, hAsync("div", { class: "form-content" }, socialComponents.length > 0 && (hAsync("div", { class: "social-section", part: "social-section" }, socialComponents.map((component) => (hAsync("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading }))))), socialComponents.length > 0 &&
|
|
6195
|
+
fieldComponents.length > 0 &&
|
|
6196
|
+
hasDivider && (hAsync("div", { class: "divider", part: "divider" }, hAsync("span", { class: "divider-text" }, "Or"))), hAsync("div", { class: "fields-section", part: "fields-section" }, fieldComponents.map((component) => (hAsync("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading })))))), this._screen.links && this._screen.links.length > 0 && (hAsync("div", { class: "links", part: "links" }, this._screen.links.map((link) => (hAsync("span", { class: "link-wrapper", part: "link-wrapper", key: link.id ?? link.href }, link.linkText ? (hAsync("span", null, link.text, " ", hAsync("a", { href: link.href, class: "link", part: "link", onClick: (e) => this.handleLinkClick(e, {
|
|
6197
|
+
id: link.id,
|
|
6198
|
+
href: link.href,
|
|
6199
|
+
text: link.linkText || link.text,
|
|
6200
|
+
}) }, link.linkText))) : (hAsync("a", { href: link.href, class: "link", part: "link", onClick: (e) => this.handleLinkClick(e, {
|
|
6201
|
+
id: link.id,
|
|
6202
|
+
href: link.href,
|
|
6203
|
+
text: link.text,
|
|
6204
|
+
}) }, link.text))))))))));
|
|
5929
6205
|
}
|
|
5930
6206
|
static get watchers() { return {
|
|
5931
6207
|
"screen": [{
|
|
@@ -5936,6 +6212,9 @@ class AuthheroWidget {
|
|
|
5936
6212
|
}],
|
|
5937
6213
|
"theme": [{
|
|
5938
6214
|
"watchTheme": 0
|
|
6215
|
+
}],
|
|
6216
|
+
"authParams": [{
|
|
6217
|
+
"watchAuthParams": 0
|
|
5939
6218
|
}]
|
|
5940
6219
|
}; }
|
|
5941
6220
|
static get style() { return authheroWidgetCss(); }
|
|
@@ -5945,11 +6224,19 @@ class AuthheroWidget {
|
|
|
5945
6224
|
"$members$": {
|
|
5946
6225
|
"screen": [1],
|
|
5947
6226
|
"apiUrl": [1, "api-url"],
|
|
6227
|
+
"baseUrl": [1, "base-url"],
|
|
6228
|
+
"state": [1025],
|
|
6229
|
+
"screenId": [1025, "screen-id"],
|
|
6230
|
+
"authParams": [1, "auth-params"],
|
|
6231
|
+
"statePersistence": [1, "state-persistence"],
|
|
6232
|
+
"storageKey": [1, "storage-key"],
|
|
5948
6233
|
"branding": [1],
|
|
5949
6234
|
"theme": [1],
|
|
5950
6235
|
"loading": [1028],
|
|
5951
6236
|
"autoSubmit": [4, "auto-submit"],
|
|
6237
|
+
"autoNavigate": [4, "auto-navigate"],
|
|
5952
6238
|
"_screen": [32],
|
|
6239
|
+
"_authParams": [32],
|
|
5953
6240
|
"_branding": [32],
|
|
5954
6241
|
"_theme": [32],
|
|
5955
6242
|
"formData": [32]
|