Skip to content

Commit c68d2dd

Browse files
committed
widget type and style added
2 parents 855d5c0 + e466f27 commit c68d2dd

File tree

7 files changed

+156
-26
lines changed

7 files changed

+156
-26
lines changed

demo/vue-app-new/src/MainView.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,5 +259,6 @@ const configs = computed(() => {
259259
<AppSettings />
260260
<AppDashboard />
261261
</main>
262+
<div id="w3a-parent-test-container" class="flex flex-col items-center justify-center"></div>
262263
</Web3AuthProvider>
263264
</template>

packages/modal-ui/src/components/Body/Login.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const Login = (props: LoginProps) => {
6363

6464
const handleExpand = () => {
6565
setExpand((prev) => !prev);
66+
setIsPasswordlessCtaClicked(false);
6667
};
6768

6869
createEffect(() => {
@@ -102,7 +103,7 @@ const Login = (props: LoginProps) => {
102103
method,
103104
isDark: props.isDark,
104105
isPrimaryBtn,
105-
name,
106+
name: name === "Twitter" ? "X" : name,
106107
adapter: props.socialLoginsConfig.adapter,
107108
loginParams: { loginProvider: method, name, login_hint: "" },
108109
order,

packages/modal-ui/src/components/LoginModal/LoginModal.tsx

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { LOGIN_PROVIDER, type SafeEventEmitter } from "@web3auth/auth";
22
import { ADAPTER_NAMES, ChainNamespaceType, cloneDeep, log, WALLET_ADAPTERS, WalletRegistry } from "@web3auth/base/src";
33
import deepmerge from "deepmerge";
4-
import { createEffect, createMemo, createSignal, on } from "solid-js";
4+
import { createEffect, createMemo, createSignal, Match, on, Suspense, Switch } from "solid-js";
55

66
import { PAGES } from "../../constants";
77
import { ExternalWalletEventType, MODAL_STATUS, ModalState, SocialLoginEventType, StateEmitterEvents } from "../../interfaces";
88
import { Body } from "../Body";
99
import { Modal } from "../Modal";
10+
import { Widget } from "../Widget";
1011

1112
export interface LoginModalProps {
1213
stateListener: SafeEventEmitter<StateEmitterEvents>;
@@ -18,6 +19,7 @@ export interface LoginModalProps {
1819
handleExternalWalletClick: (params: ExternalWalletEventType) => void;
1920
handleShowExternalWallets: (externalWalletsInitialized: boolean) => void;
2021
closeModal: () => void;
22+
widget?: "modal" | "embed";
2123
}
2224

2325
const LoginModal = (props: LoginModalProps) => {
@@ -176,27 +178,56 @@ const LoginModal = (props: LoginModalProps) => {
176178
);
177179

178180
return (
179-
<Modal open={modalState().modalVisibility} placement="center" padding={false} showCloseIcon={showCloseIcon()} onClose={closeModal}>
180-
<Body
181-
{...props}
182-
showPasswordLessInput={showPasswordLessInput()}
183-
showExternalWalletButton={showExternalWalletButton()}
184-
handleSocialLoginClick={(params: SocialLoginEventType) => preHandleSocialWalletClick(params)}
185-
socialLoginsConfig={modalState().socialLoginsConfig}
186-
areSocialLoginsVisible={areSocialLoginsVisible()}
187-
isEmailPrimary={isEmailPrimary()}
188-
isExternalPrimary={isExternalPrimary()}
189-
showExternalWalletPage={showExternalWalletPage()}
190-
handleExternalWalletBtnClick={handleExternalWalletBtnClick}
191-
modalState={modalState()}
192-
preHandleExternalWalletClick={preHandleExternalWalletClick}
193-
setModalState={setModalState}
194-
onCloseLoader={onCloseLoader}
195-
isEmailPasswordLessLoginVisible={isEmailPasswordLessLoginVisible()}
196-
isSmsPasswordLessLoginVisible={isSmsPasswordLessLoginVisible()}
197-
handleBackClick={handleBackClick}
198-
/>
199-
</Modal>
181+
<Suspense>
182+
<Switch>
183+
<Match when={props.widget === "modal"}>
184+
<Modal open={modalState().modalVisibility} placement="center" padding={false} showCloseIcon={showCloseIcon()} onClose={closeModal}>
185+
<Body
186+
{...props}
187+
showPasswordLessInput={showPasswordLessInput()}
188+
showExternalWalletButton={showExternalWalletButton()}
189+
handleSocialLoginClick={(params: SocialLoginEventType) => preHandleSocialWalletClick(params)}
190+
socialLoginsConfig={modalState().socialLoginsConfig}
191+
areSocialLoginsVisible={areSocialLoginsVisible()}
192+
isEmailPrimary={isEmailPrimary()}
193+
isExternalPrimary={isExternalPrimary()}
194+
showExternalWalletPage={showExternalWalletPage()}
195+
handleExternalWalletBtnClick={handleExternalWalletBtnClick}
196+
modalState={modalState()}
197+
preHandleExternalWalletClick={preHandleExternalWalletClick}
198+
setModalState={setModalState}
199+
onCloseLoader={onCloseLoader}
200+
isEmailPasswordLessLoginVisible={isEmailPasswordLessLoginVisible()}
201+
isSmsPasswordLessLoginVisible={isSmsPasswordLessLoginVisible()}
202+
handleBackClick={handleBackClick}
203+
/>
204+
</Modal>
205+
</Match>
206+
<Match when={props.widget === "embed"}>
207+
<Widget padding={false}>
208+
<Body
209+
{...props}
210+
showPasswordLessInput={showPasswordLessInput()}
211+
showExternalWalletButton={showExternalWalletButton()}
212+
handleSocialLoginClick={(params: SocialLoginEventType) => preHandleSocialWalletClick(params)}
213+
socialLoginsConfig={modalState().socialLoginsConfig}
214+
areSocialLoginsVisible={areSocialLoginsVisible()}
215+
isEmailPrimary={isEmailPrimary()}
216+
isExternalPrimary={isExternalPrimary()}
217+
showExternalWalletPage={showExternalWalletPage()}
218+
handleExternalWalletBtnClick={handleExternalWalletBtnClick}
219+
modalState={modalState()}
220+
preHandleExternalWalletClick={preHandleExternalWalletClick}
221+
setModalState={setModalState}
222+
onCloseLoader={onCloseLoader}
223+
isEmailPasswordLessLoginVisible={isEmailPasswordLessLoginVisible()}
224+
isSmsPasswordLessLoginVisible={isSmsPasswordLessLoginVisible()}
225+
handleBackClick={handleBackClick}
226+
/>
227+
</Widget>
228+
</Match>
229+
</Switch>
230+
</Suspense>
200231
);
201232
};
202233

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Component, createEffect, createSignal, JSX, mergeProps } from "solid-js";
2+
3+
import { cn } from "../../utils/common";
4+
5+
export interface WidgetProps {
6+
children: JSX.Element[] | JSX.Element;
7+
padding?: boolean;
8+
shadow?: boolean;
9+
border?: boolean;
10+
showCloseIcon?: boolean;
11+
onCloseHandler?: () => void;
12+
}
13+
14+
const Widget: Component<WidgetProps> = (props: WidgetProps) => {
15+
const mergedProps = mergeProps({ padding: true, shadow: true, border: false, showCloseIcon: false }, props as WidgetProps);
16+
17+
const [isOpen, setIsOpen] = createSignal<boolean>(false);
18+
19+
createEffect(() => {
20+
// Give a very small delay for the animation to start from the correct position
21+
setTimeout(() => {
22+
setIsOpen(true);
23+
}, 50);
24+
}, false);
25+
26+
return (
27+
<div
28+
class={cn(
29+
"w3a--bg-app-light-surface1 dark:w3a--bg-app-dark-surface-main w3a--rounded-3xl w3a--w-[96%] sm:w3a--w-[393px] w3a--h-[642px] w3a--flex w3a--flex-col w3a--duration-500",
30+
{
31+
"w3a--opacity-100 w3a--delay-100": isOpen(),
32+
"w3a--opacity-0": !isOpen(),
33+
"w3a--p-4": mergedProps.padding,
34+
"w3a--shadow-xl sm:w3a--shadow-lg": mergedProps.shadow,
35+
"w3a--border w3a--border-app-gray-100 dark:w3a--border-app-gray-800": mergedProps.border,
36+
}
37+
)}
38+
>
39+
{mergedProps.showCloseIcon && (
40+
<div class="w3a--absolute w3a--right-6 w3a--top-[30px] w3a--cursor-pointer z-10">
41+
<svg
42+
width="13"
43+
height="13"
44+
viewBox="0 0 13 13"
45+
fill="none"
46+
xmlns="http://www.w3.org/2000/svg"
47+
onClick={mergedProps.onCloseHandler}
48+
class="w3a--text-app-gray-900 dark:w3a--text-app-white"
49+
>
50+
<path
51+
fill-rule="evenodd"
52+
clip-rule="evenodd"
53+
d="M0.292787 1.29299C0.480314 1.10552 0.734622 1.0002 0.999786 1.0002C1.26495 1.0002 1.51926 1.10552 1.70679 1.29299L5.99979 5.58599L10.2928 1.29299C10.385 1.19748 10.4954 1.1213 10.6174 1.06889C10.7394 1.01648 10.8706 0.988893 11.0034 0.987739C11.1362 0.986585 11.2678 1.01189 11.3907 1.06217C11.5136 1.11245 11.6253 1.1867 11.7192 1.28059C11.8131 1.37449 11.8873 1.48614 11.9376 1.60904C11.9879 1.73193 12.0132 1.86361 12.012 1.99639C12.0109 2.12917 11.9833 2.26039 11.9309 2.38239C11.8785 2.5044 11.8023 2.61474 11.7068 2.70699L7.41379 6.99999L11.7068 11.293C11.8889 11.4816 11.9897 11.7342 11.9875 11.9964C11.9852 12.2586 11.88 12.5094 11.6946 12.6948C11.5092 12.8802 11.2584 12.9854 10.9962 12.9877C10.734 12.9899 10.4814 12.8891 10.2928 12.707L5.99979 8.41399L1.70679 12.707C1.51818 12.8891 1.26558 12.9899 1.00339 12.9877C0.741188 12.9854 0.490376 12.8802 0.304968 12.6948C0.11956 12.5094 0.0143906 12.2586 0.0121121 11.9964C0.00983372 11.7342 0.110629 11.4816 0.292787 11.293L4.58579 6.99999L0.292787 2.70699C0.105316 2.51946 0 2.26515 0 1.99999C0 1.73483 0.105316 1.48052 0.292787 1.29299V1.29299Z"
54+
fill="currentColor"
55+
/>
56+
</svg>
57+
</div>
58+
)}
59+
{mergedProps.children}
60+
</div>
61+
);
62+
};
63+
64+
export default Widget;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as Widget } from "./Widget";

packages/modal-ui/src/interfaces.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ import {
1111

1212
// capture whitelabel only once
1313
export interface UIConfig extends WhiteLabelData {
14+
/**
15+
* Whether to use the modal or embed widget
16+
*
17+
* @defaultValue `modal`
18+
*/
19+
widget?: "modal" | "embed";
20+
/**
21+
* ID of the element to embed the widget into
22+
*/
23+
targetId?: string;
24+
1425
/**
1526
* order of how login methods are shown
1627
*

packages/modal-ui/src/loginModal.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import {
3535
import { changeLocale } from "./localeImport";
3636
import { getUserLanguage } from "./utils/modal";
3737

38-
function createWrapper(parentZIndex: string) {
38+
function createWrapperForModal(parentZIndex: string) {
3939
const existingWrapper = document.getElementById("w3a-parent-container");
4040
if (existingWrapper) existingWrapper.remove();
4141
const parent = document.createElement("section");
@@ -46,6 +46,15 @@ function createWrapper(parentZIndex: string) {
4646
document.body.appendChild(parent);
4747
}
4848

49+
function createWrapperForEmbed(targetId: string) {
50+
const targetElement = document.getElementById(targetId);
51+
if (!targetElement) {
52+
log.error(`Element with ID ${targetId} not found`);
53+
return;
54+
}
55+
targetElement.innerHTML = `<div id="w3a-parent-container" class="w3a-parent-container"></div>`;
56+
}
57+
4958
export class LoginModal extends SafeEventEmitter {
5059
private uiConfig: UIConfig;
5160

@@ -55,7 +64,7 @@ export class LoginModal extends SafeEventEmitter {
5564

5665
private walletRegistry: WalletRegistry;
5766

58-
constructor(uiConfig: LoginModalProps) {
67+
constructor(uiConfig: LoginModalProps & { widget?: "modal" | "embed"; targetId?: string }) {
5968
super();
6069
this.uiConfig = uiConfig;
6170

@@ -68,6 +77,12 @@ export class LoginModal extends SafeEventEmitter {
6877
if (!uiConfig.loginGridCol) this.uiConfig.loginGridCol = 3;
6978
if (!uiConfig.primaryButton) this.uiConfig.primaryButton = "socialLogin";
7079
if (!uiConfig.defaultLanguage) this.uiConfig.defaultLanguage = getUserLanguage(uiConfig.defaultLanguage);
80+
if (!uiConfig.widget) this.uiConfig.widget = "embed";
81+
if (!uiConfig.targetId) this.uiConfig.targetId = "w3a-parent-test-container";
82+
83+
if (uiConfig.widget === "embed" && !uiConfig.targetId) {
84+
throw new Error("targetId is required for embed widget");
85+
}
7186

7287
this.stateEmitter = new SafeEventEmitter<StateEmitterEvents>();
7388
this.chainNamespace = uiConfig.chainNamespace;
@@ -94,9 +109,14 @@ export class LoginModal extends SafeEventEmitter {
94109
return resolve();
95110
});
96111

97-
createWrapper(this.uiConfig.modalZIndex);
112+
if (this.uiConfig.widget === "modal") {
113+
createWrapperForModal(this.uiConfig.modalZIndex);
114+
} else {
115+
createWrapperForEmbed(this.uiConfig.targetId);
116+
}
98117

99118
const root = document.getElementById("w3a-parent-container");
119+
100120
if (darkState.isDark) {
101121
root?.classList.add("w3a--dark");
102122
} else {
@@ -120,6 +140,7 @@ export class LoginModal extends SafeEventEmitter {
120140
appName={this.uiConfig.appName}
121141
chainNamespace={this.chainNamespace}
122142
walletRegistry={this.walletRegistry}
143+
widget={this.uiConfig.widget}
123144
/>
124145
</ThemedContext.Provider>
125146
),

0 commit comments

Comments
 (0)