@abstraxn/signer-react 3.3.9 → 4.0.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/CHANGELOG.md +44 -0
- package/dist/src/ConnectButton.css.d.ts +2 -0
- package/dist/src/ConnectButton.css.js +1 -0
- package/dist/src/ConnectButton.d.ts +0 -1
- package/dist/src/ConnectButton.js +7 -6
- package/dist/src/ExternalWalletButtons.css.d.ts +2 -0
- package/dist/src/ExternalWalletButtons.css.js +1 -0
- package/dist/src/ExternalWalletButtons.d.ts +4 -1
- package/dist/src/ExternalWalletButtons.js +6 -1
- package/dist/src/WalletModal.css +2 -0
- package/dist/src/WalletModal.css.d.ts +2 -0
- package/dist/src/WalletModal.css.js +1 -0
- package/dist/src/WalletModal.d.ts +2 -2
- package/dist/src/WalletModal.js +113 -59
- package/dist/src/components/AbstraxnLogo.d.ts +17 -0
- package/dist/src/components/AbstraxnLogo.js +29 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js +406 -338
- package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.d.ts +7 -0
- package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.js +9 -0
- package/dist/src/components/AbstraxnProvider/useWalletInitialization.js +41 -6
- package/dist/src/components/OnboardingUI/OnboardingUI.css +99 -4
- package/dist/src/components/OnboardingUI/OnboardingUI.css.d.ts +2 -0
- package/dist/src/components/OnboardingUI/OnboardingUI.css.js +1 -0
- package/dist/src/components/OnboardingUI/OnboardingUIReact.d.ts +0 -1
- package/dist/src/components/OnboardingUI/OnboardingUIReact.js +46 -19
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.d.ts +24 -1
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.js +348 -160
- package/dist/src/components/OnboardingUI/components/Divider.d.ts +7 -0
- package/dist/src/components/OnboardingUI/components/Divider.js +9 -0
- package/dist/src/components/OnboardingUI/components/EmailForm.d.ts +0 -1
- package/dist/src/components/OnboardingUI/components/EmailForm.js +1 -2
- package/dist/src/components/OnboardingUI/components/MfaForm.d.ts +0 -1
- package/dist/src/components/OnboardingUI/components/MfaForm.js +2 -3
- package/dist/src/components/OnboardingUI/components/Modal.d.ts +0 -1
- package/dist/src/components/OnboardingUI/components/Modal.js +12 -4
- package/dist/src/components/OnboardingUI/components/OtpForm.d.ts +0 -1
- package/dist/src/components/OnboardingUI/components/OtpForm.js +2 -3
- package/dist/src/components/OnboardingUI/components/PasskeyButton.d.ts +0 -1
- package/dist/src/components/OnboardingUI/components/PasskeyButton.js +2 -2
- package/dist/src/components/OnboardingUI/components/SocialButtons.d.ts +0 -1
- package/dist/src/components/OnboardingUI/components/SocialButtons.js +3 -3
- package/dist/src/components/OnboardingUI/onboardingShadowContainer.d.ts +9 -0
- package/dist/src/components/OnboardingUI/onboardingShadowContainer.js +36 -0
- package/dist/src/components/ShadowHost/ShadowHost.d.ts +18 -0
- package/dist/src/components/ShadowHost/ShadowHost.js +123 -0
- package/dist/src/components/ShadowHost/ShadowHostContext.d.ts +10 -0
- package/dist/src/components/ShadowHost/ShadowHostContext.js +14 -0
- package/dist/src/components/ShadowHost/index.d.ts +2 -0
- package/dist/src/components/ShadowHost/index.js +3 -0
- package/dist/src/components/WalletModal/components/ChainSelector.css +9 -0
- package/dist/src/components/WalletModal/components/ChainSelector.css.d.ts +2 -0
- package/dist/src/components/WalletModal/components/ChainSelector.css.js +1 -0
- package/dist/src/components/WalletModal/components/ChainSelector.d.ts +0 -1
- package/dist/src/components/WalletModal/components/ChainSelector.js +78 -23
- package/dist/src/components/WalletModal/components/CodeInput.d.ts +12 -0
- package/dist/src/components/WalletModal/components/CodeInput.js +39 -0
- package/dist/src/components/WalletModal/components/ExportKeyModal.css.d.ts +2 -0
- package/dist/src/components/WalletModal/components/ExportKeyModal.css.js +1 -0
- package/dist/src/components/WalletModal/components/ExportKeyModal.d.ts +0 -2
- package/dist/src/components/WalletModal/components/ExportKeyModal.js +2 -4
- package/dist/src/components/WalletModal/components/ExportWarningModal.css.d.ts +2 -0
- package/dist/src/components/WalletModal/components/ExportWarningModal.css.js +1 -0
- package/dist/src/components/WalletModal/components/ExportWarningModal.d.ts +0 -2
- package/dist/src/components/WalletModal/components/ExportWarningModal.js +2 -4
- package/dist/src/components/WalletModal/components/ManageWalletModal.css +27 -1
- package/dist/src/components/WalletModal/components/ManageWalletModal.css.d.ts +2 -0
- package/dist/src/components/WalletModal/components/ManageWalletModal.css.js +1 -0
- package/dist/src/components/WalletModal/components/ManageWalletModal.d.ts +0 -1
- package/dist/src/components/WalletModal/components/ManageWalletModal.js +66 -148
- package/dist/src/components/WalletModal/components/MfaVerifyModal.d.ts +15 -0
- package/dist/src/components/WalletModal/components/MfaVerifyModal.js +98 -0
- package/dist/src/components/WalletModal/components/PreviewTransactionModal.css +2 -12
- package/dist/src/components/WalletModal/components/PreviewTransactionModal.css.d.ts +2 -0
- package/dist/src/components/WalletModal/components/PreviewTransactionModal.css.js +1 -0
- package/dist/src/components/WalletModal/components/PreviewTransactionModal.d.ts +0 -1
- package/dist/src/components/WalletModal/components/PreviewTransactionModal.js +0 -1
- package/dist/src/components/WalletModal/components/ReceiveModal.css.d.ts +2 -0
- package/dist/src/components/WalletModal/components/ReceiveModal.css.js +1 -0
- package/dist/src/components/WalletModal/components/ReceiveModal.d.ts +0 -1
- package/dist/src/components/WalletModal/components/ReceiveModal.js +2 -2
- package/dist/src/components/WalletModal/components/SendModal.css.d.ts +2 -0
- package/dist/src/components/WalletModal/components/SendModal.css.js +1 -0
- package/dist/src/components/WalletModal/components/SendModal.d.ts +5 -2
- package/dist/src/components/WalletModal/components/SendModal.js +25 -82
- package/dist/src/components/WalletModal/components/SignMfaModalHost.d.ts +1 -0
- package/dist/src/components/WalletModal/components/SignMfaModalHost.js +50 -0
- package/dist/src/components/WalletModal/components/SuccessModal.css.d.ts +2 -0
- package/dist/src/components/WalletModal/components/SuccessModal.css.js +1 -0
- package/dist/src/components/WalletModal/components/SuccessModal.d.ts +0 -1
- package/dist/src/components/WalletModal/components/SuccessModal.js +0 -1
- package/dist/src/components/WalletModal/components/TokenSelectorModal.css.d.ts +2 -0
- package/dist/src/components/WalletModal/components/TokenSelectorModal.css.js +1 -0
- package/dist/src/components/WalletModal/components/TokenSelectorModal.d.ts +0 -1
- package/dist/src/components/WalletModal/components/TokenSelectorModal.js +0 -1
- package/dist/src/components/WalletModal/components/UserAvatar.js +2 -1
- package/dist/src/components/WalletModal/components/index.d.ts +3 -0
- package/dist/src/components/WalletModal/components/index.js +2 -0
- package/dist/src/components/WalletModal/hooks/useSendTransaction.js +18 -6
- package/dist/src/components/icons.d.ts +2 -0
- package/dist/src/components/icons.js +4 -0
- package/dist/src/hooks.d.ts +81 -20
- package/dist/src/hooks.js +194 -36
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +2 -2
- package/dist/src/signMfaBridge.d.ts +7 -0
- package/dist/src/signMfaBridge.js +20 -0
- package/dist/src/types.d.ts +4 -0
- package/dist/src/utils/shadowFocus.d.ts +8 -0
- package/dist/src/utils/shadowFocus.js +15 -0
- package/dist/src/utils/shadowRegistry.d.ts +23 -0
- package/dist/src/utils/shadowRegistry.js +36 -0
- package/dist/src/walletConnectMobile.d.ts +9 -5
- package/dist/src/walletConnectMobile.js +152 -51
- package/package.json +9 -4
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Divider Component
|
|
4
|
+
* A horizontal "or" divider used to separate auth method sections
|
|
5
|
+
*/
|
|
6
|
+
export function Divider({ text = 'or' }) {
|
|
7
|
+
return (_jsx("div", { className: "onboarding-divider", children: _jsx("span", { className: "onboarding-divider-text", children: text }) }));
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=Divider.js.map
|
|
@@ -6,7 +6,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
6
6
|
import { useMemo } from "react";
|
|
7
7
|
import { TfiEmail } from "../../icons";
|
|
8
8
|
import { isValidEmail } from "@abstraxn/signer-core";
|
|
9
|
-
import "../OnboardingUI.css";
|
|
10
9
|
export const EmailForm = ({ email, onChange, onSubmit, loading = false, error = null, config, readOnly = false, }) => {
|
|
11
10
|
const theme = config?.theme || "light";
|
|
12
11
|
const labels = config?.labels || {};
|
|
@@ -32,6 +31,6 @@ export const EmailForm = ({ email, onChange, onSubmit, loading = false, error =
|
|
|
32
31
|
onSubmit();
|
|
33
32
|
}
|
|
34
33
|
};
|
|
35
|
-
return (_jsx("form", { className: "onboarding-form", onSubmit: handleSubmit, noValidate: true, children: _jsxs("div", { className: "onboarding-input-group", children: [_jsx("label", { htmlFor: "onboarding-email", className: "onboarding-input-label", children: labels.emailLabel || "Email Address" }), _jsxs("div", { className: "onboarding-input-wrapper", children: [_jsx(TfiEmail, { className: "onboarding-input-icon", size: 18 }), _jsx("input", { id: "onboarding-email", type: "email", className: `onboarding-input${readOnly ? " onboarding-email-readonly" : ""}`, placeholder: labels.emailPlaceholder || "Enter your email address", value: email, onChange: readOnly ? undefined : (e) => onChange(e.target.value), onKeyPress: handleKeyPress, disabled: loading, readOnly: readOnly, required: true, autoComplete: "email", autoFocus: true }), _jsx("button", { type: "submit", className: `onboarding-input-arrow-button${hasEmailValue ? " onboarding-input-arrow-button-filled" : ""}`, disabled: loading || !isEmailValid, "aria-label": "Continue", children: loading ? (_jsxs("svg", { className: "onboarding-input-arrow-icon", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round",
|
|
34
|
+
return (_jsx("form", { className: "onboarding-form", onSubmit: handleSubmit, noValidate: true, children: _jsxs("div", { className: "onboarding-input-group", children: [_jsx("label", { htmlFor: "onboarding-email", className: "onboarding-input-label", children: labels.emailLabel || "Email Address" }), _jsxs("div", { className: "onboarding-input-wrapper", children: [_jsx(TfiEmail, { className: "onboarding-input-icon", size: 18 }), _jsx("input", { id: "onboarding-email", type: "email", className: `onboarding-input${readOnly ? " onboarding-email-readonly" : ""}`, part: "input", placeholder: labels.emailPlaceholder || "Enter your email address", value: email, onChange: readOnly ? undefined : (e) => onChange(e.target.value), onKeyPress: handleKeyPress, disabled: loading, readOnly: readOnly, required: true, autoComplete: "email", autoFocus: true }), _jsx("button", { type: "submit", className: `onboarding-input-arrow-button${hasEmailValue ? " onboarding-input-arrow-button-filled" : ""}`, disabled: loading || !isEmailValid, "aria-label": "Continue", children: loading ? (_jsxs("svg", { className: "onboarding-input-arrow-icon onboarding-button-spinner", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("line", { x1: "12", y1: "2", x2: "12", y2: "6" }), _jsx("line", { x1: "12", y1: "18", x2: "12", y2: "22" }), _jsx("line", { x1: "4.93", y1: "4.93", x2: "7.76", y2: "7.76" }), _jsx("line", { x1: "16.24", y1: "16.24", x2: "19.07", y2: "19.07" }), _jsx("line", { x1: "2", y1: "12", x2: "6", y2: "12" }), _jsx("line", { x1: "18", y1: "12", x2: "22", y2: "12" }), _jsx("line", { x1: "4.93", y1: "19.07", x2: "7.76", y2: "16.24" }), _jsx("line", { x1: "16.24", y1: "7.76", x2: "19.07", y2: "4.93" })] })) : (_jsxs("svg", { className: "onboarding-input-arrow-icon", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" }), _jsx("polyline", { points: "12 5 19 12 12 19" })] })) })] }), displayError && (_jsx("div", { className: "onboarding-error", role: "alert", children: displayError }))] }) }));
|
|
36
35
|
};
|
|
37
36
|
//# sourceMappingURL=EmailForm.js.map
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* MfaForm Component
|
|
4
4
|
* MFA (auth code / TOTP) input form for login verification
|
|
5
5
|
*/
|
|
6
6
|
import React, { useRef, useEffect } from "react";
|
|
7
|
-
import "../OnboardingUI.css";
|
|
8
7
|
export const MfaForm = ({ code, onChange, onSubmit, onBack, loading = false, error = null, config, }) => {
|
|
9
8
|
const labels = config?.labels || {};
|
|
10
9
|
const mfaInputsRef = useRef([]);
|
|
@@ -83,6 +82,6 @@ export const MfaForm = ({ code, onChange, onSubmit, onBack, loading = false, err
|
|
|
83
82
|
}, type: "text", inputMode: "numeric", pattern: "[0-9]*", maxLength: 1, className: "onboarding-otp-input", value: digit, onChange: (e) => handleDigitChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), disabled: loading, autoFocus: index === 0, "aria-label": `Authentication code digit ${index + 1}` }, `mfa-input-${index}`))) })), !useBackupCode && (_jsxs("p", { className: "onboarding-mfa-trouble-text", children: ["Having trouble?", " ", _jsx("button", { type: "button", className: "onboarding-mfa-backup-link", onClick: () => {
|
|
84
83
|
setUseBackupCode(true);
|
|
85
84
|
onChange("");
|
|
86
|
-
}, disabled: loading, children: "Use a backup code" })] })), _jsx("button", { type: "submit", className: "onboarding-button onboarding-button-primary", disabled: loading || code.length !== codeLength, children: loading ? (_jsxs(
|
|
85
|
+
}, disabled: loading, children: "Use a backup code" })] })), _jsx("button", { type: "submit", className: "onboarding-button onboarding-button-primary", part: "button-primary", disabled: loading || code.length !== codeLength, children: loading ? (_jsxs("span", { className: "onboarding-button-loading", children: [_jsxs("svg", { className: "onboarding-button-spinner", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("line", { x1: "12", y1: "2", x2: "12", y2: "6" }), _jsx("line", { x1: "12", y1: "18", x2: "12", y2: "22" }), _jsx("line", { x1: "4.93", y1: "4.93", x2: "7.76", y2: "7.76" }), _jsx("line", { x1: "16.24", y1: "16.24", x2: "19.07", y2: "19.07" }), _jsx("line", { x1: "2", y1: "12", x2: "6", y2: "12" }), _jsx("line", { x1: "18", y1: "12", x2: "22", y2: "12" }), _jsx("line", { x1: "4.93", y1: "19.07", x2: "7.76", y2: "16.24" }), _jsx("line", { x1: "16.24", y1: "7.76", x2: "19.07", y2: "4.93" })] }), _jsx("span", { children: "Verifying..." })] })) : ("Verify") })] })] }));
|
|
87
86
|
};
|
|
88
87
|
//# sourceMappingURL=MfaForm.js.map
|
|
@@ -5,10 +5,17 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
*/
|
|
6
6
|
import { useEffect, useRef } from 'react';
|
|
7
7
|
import { createPortal } from 'react-dom';
|
|
8
|
-
import '
|
|
8
|
+
import { useShadowHost } from '../../ShadowHost';
|
|
9
|
+
import { getDeepActiveElement } from '../../../utils/shadowFocus';
|
|
10
|
+
import { CloseIcon } from '../../icons';
|
|
9
11
|
export const Modal = ({ isOpen, onClose, closeOnBackdropClick = true, showCloseButton = false, children, theme = 'light', }) => {
|
|
10
12
|
const modalRef = useRef(null);
|
|
11
13
|
const contentRef = useRef(null);
|
|
14
|
+
// Portal target: the enclosing ShadowHost's container, so this modal stays
|
|
15
|
+
// inside the isolation boundary instead of escaping to document.body.
|
|
16
|
+
// document.body fallback is purely defensive.
|
|
17
|
+
const shadowHost = useShadowHost();
|
|
18
|
+
const portalTarget = shadowHost?.container ?? document.body;
|
|
12
19
|
useEffect(() => {
|
|
13
20
|
if (isOpen) {
|
|
14
21
|
// Prevent body scroll
|
|
@@ -32,11 +39,12 @@ export const Modal = ({ isOpen, onClose, closeOnBackdropClick = true, showCloseB
|
|
|
32
39
|
const firstElement = focusableElements[0];
|
|
33
40
|
const lastElement = focusableElements[focusableElements.length - 1];
|
|
34
41
|
if (e.key === 'Tab') {
|
|
35
|
-
|
|
42
|
+
const active = getDeepActiveElement();
|
|
43
|
+
if (e.shiftKey && active === firstElement) {
|
|
36
44
|
e.preventDefault();
|
|
37
45
|
lastElement.focus();
|
|
38
46
|
}
|
|
39
|
-
else if (!e.shiftKey &&
|
|
47
|
+
else if (!e.shiftKey && active === lastElement) {
|
|
40
48
|
e.preventDefault();
|
|
41
49
|
firstElement.focus();
|
|
42
50
|
}
|
|
@@ -59,6 +67,6 @@ export const Modal = ({ isOpen, onClose, closeOnBackdropClick = true, showCloseB
|
|
|
59
67
|
return createPortal(_jsx("div", { ref: modalRef, className: `onboarding-modal-overlay onboarding-modal-open onboarding-theme-${theme}`, onClick: handleBackdropClick, role: "dialog", "aria-modal": "true", children: _jsxs("div", { ref: contentRef, className: `onboarding-modal-content onboarding-theme-${theme}`, onClick: (e) => e.stopPropagation(), children: [showCloseButton && (_jsx("button", { type: "button", className: `onboarding-modal-close onboarding-theme-${theme}`, "aria-label": "Close", onClick: (e) => {
|
|
60
68
|
e.stopPropagation();
|
|
61
69
|
onClose();
|
|
62
|
-
}, children:
|
|
70
|
+
}, children: _jsx(CloseIcon, { size: 20 }) })), children] }) }), portalTarget);
|
|
63
71
|
};
|
|
64
72
|
//# sourceMappingURL=Modal.js.map
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* OtpForm Component
|
|
4
4
|
* OTP input form for verification
|
|
5
5
|
*/
|
|
6
6
|
import React, { useRef, useEffect } from "react";
|
|
7
7
|
import { TfiEmail } from "../../icons";
|
|
8
|
-
import "../OnboardingUI.css";
|
|
9
8
|
export const OtpForm = ({ email, otp, onChange, onSubmit, onResend, onBack, loading = false, resending = false, requireManualVerify = false, error = null, resendCooldown = 0, config, }) => {
|
|
10
9
|
const theme = config?.theme || "light";
|
|
11
10
|
const labels = config?.labels || {};
|
|
@@ -69,7 +68,7 @@ export const OtpForm = ({ email, otp, onChange, onSubmit, onResend, onBack, load
|
|
|
69
68
|
}, [otp, loading, error, requireManualVerify, onSubmit]);
|
|
70
69
|
return (_jsxs("div", { className: "onboarding-otp-verification", children: [onBack && (_jsx("button", { type: "button", className: "onboarding-otp-back-button", onClick: onBack, "aria-label": "Go back to email input", disabled: loading || resending, children: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M19 12H5M12 19l-7-7 7-7" }) }) })), _jsx("div", { className: "onboarding-otp-icon-container", children: _jsx("div", { className: "onboarding-otp-icon-inner", children: _jsx(TfiEmail, { className: "onboarding-otp-icon", size: 28 }) }) }), _jsx("h1", { className: "onboarding-otp-title", children: labels.otpLabel || "Enter verification code" }), _jsx("p", { className: "onboarding-otp-instruction", children: "We sent a 6-digit code to" }), _jsx("p", { className: "onboarding-otp-email", children: email }), displayError && (_jsx("div", { className: "onboarding-error", role: "alert", children: displayError })), _jsxs("form", { onSubmit: handleSubmit, className: "onboarding-form", children: [_jsx("div", { className: "onboarding-otp-inputs-container", children: otpDigits.map((digit, index) => (_jsx("input", { ref: (el) => {
|
|
71
70
|
otpInputsRef.current[index] = el;
|
|
72
|
-
}, type: "text", inputMode: "numeric", pattern: "[0-9]*", maxLength: 1, className: "onboarding-otp-input", value: digit, onChange: (e) => handleOtpChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), disabled: loading || resending, autoFocus: index === 0, "aria-label": `OTP digit ${index + 1}` }, `otp-input-${index}`))) }), _jsx("button", { type: "submit", className: "onboarding-button onboarding-button-primary", disabled: loading || resending || otp.length !== 6, children: loading ? (_jsxs(
|
|
71
|
+
}, type: "text", inputMode: "numeric", pattern: "[0-9]*", maxLength: 1, className: "onboarding-otp-input", value: digit, onChange: (e) => handleOtpChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), disabled: loading || resending, autoFocus: index === 0, "aria-label": `OTP digit ${index + 1}` }, `otp-input-${index}`))) }), _jsx("button", { type: "submit", className: "onboarding-button onboarding-button-primary", part: "button-primary", disabled: loading || resending || otp.length !== 6, children: loading ? (_jsxs("span", { className: "onboarding-button-loading", children: [_jsxs("svg", { className: "onboarding-button-spinner", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("line", { x1: "12", y1: "2", x2: "12", y2: "6" }), _jsx("line", { x1: "12", y1: "18", x2: "12", y2: "22" }), _jsx("line", { x1: "4.93", y1: "4.93", x2: "7.76", y2: "7.76" }), _jsx("line", { x1: "16.24", y1: "16.24", x2: "19.07", y2: "19.07" }), _jsx("line", { x1: "2", y1: "12", x2: "6", y2: "12" }), _jsx("line", { x1: "18", y1: "12", x2: "22", y2: "12" }), _jsx("line", { x1: "4.93", y1: "19.07", x2: "7.76", y2: "16.24" }), _jsx("line", { x1: "16.24", y1: "7.76", x2: "19.07", y2: "4.93" })] }), _jsx("span", { children: "Verifying..." })] })) : (labels.otpButton || "Verify") })] }), _jsx("div", { className: "onboarding-otp-resend", children: _jsxs("p", { className: "onboarding-otp-resend-text", children: ["Didn't receive the code?", " ", resendCooldown > 0 ? (_jsxs("span", { className: "onboarding-otp-resend-cooldown", children: ["Resend in ", resendCooldown, "s"] })) : (_jsx("a", { href: "#", className: "onboarding-otp-resend-link", onClick: (e) => {
|
|
73
72
|
e.preventDefault();
|
|
74
73
|
if (!loading && !resending) {
|
|
75
74
|
onResend();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import '
|
|
2
|
+
import { Divider } from './Divider';
|
|
3
3
|
export const PasskeyButton = ({ onPasskeyLogin, onPasskeySignup, loading = false, config, }) => {
|
|
4
4
|
const theme = config?.theme || 'light';
|
|
5
5
|
const labels = config?.labels || {};
|
|
@@ -14,7 +14,7 @@ export const PasskeyButton = ({ onPasskeyLogin, onPasskeySignup, loading = false
|
|
|
14
14
|
if (!isWebAuthnAvailable) {
|
|
15
15
|
return null;
|
|
16
16
|
}
|
|
17
|
-
return (_jsxs(_Fragment, { children: [_jsx(
|
|
17
|
+
return (_jsxs(_Fragment, { children: [_jsx(Divider, {}), _jsxs("button", { type: "button", className: "onboarding-button onboarding-button-passkey", onClick: onPasskeyLogin, disabled: loading, children: [_jsx("svg", { className: "onboarding-passkey-icon", width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M3.682 19.338v-2.102c0-.476.127-.915.382-1.315.255-.4.603-.708 1.042-.926a14.89 14.89 0 0 1 2.939-1.067c.982-.24 1.966-.36 2.953-.36.352 0 .708.018 1.066.054.358.035.712.084 1.062.147-.015.831.164 1.613.537 2.347a4.882 4.882 0 0 0 1.595 1.822v1.4H3.682Zm15.003 2.713-1.303-1.303v-4.029a2.976 2.976 0 0 1-1.564-1.08 2.944 2.944 0 0 1-.608-1.832c0-.841.297-1.557.891-2.147a2.944 2.944 0 0 1 2.154-.886c.837 0 1.552.295 2.145.886.594.59.89 1.306.89 2.148 0 .656-.182 1.238-.546 1.743a3.018 3.018 0 0 1-1.404 1.084l1.073 1.072-1.294 1.31 1.294 1.301-1.728 1.733ZM11 12.006c-.94 0-1.742-.333-2.405-.999a3.28 3.28 0 0 1-.994-2.4c0-.94.331-1.743.994-2.405A3.276 3.276 0 0 1 11 5.208c.94 0 1.742.331 2.405.994.663.662.994 1.463.994 2.4 0 .939-.331 1.74-.994 2.406a3.27 3.27 0 0 1-2.405.998Zm7.252 2.235a.833.833 0 0 0 .612-.255.842.842 0 0 0 .255-.617.834.834 0 0 0-.253-.614.835.835 0 0 0-.612-.252.85.85 0 0 0-.616.251.823.823 0 0 0-.257.612c0 .24.085.447.257.618a.838.838 0 0 0 .614.257Z" }) }), labels.passkeyLoginButton || 'Continue with Passkey'] }), _jsx("a", { href: "#", className: "onboarding-passkey-signup-link", onClick: (e) => {
|
|
18
18
|
e.preventDefault();
|
|
19
19
|
onPasskeySignup();
|
|
20
20
|
}, children: labels.passkeySignupButton || 'Sign up with passkey' })] }));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import '
|
|
2
|
+
import { GoogleIcon } from '../../icons';
|
|
3
3
|
export const SocialButtons = ({ onGoogleLogin, onTwitterLogin, onDiscordLogin, loading = false, config, }) => {
|
|
4
4
|
const theme = config?.theme || 'light';
|
|
5
5
|
const labels = config?.labels || {};
|
|
@@ -12,9 +12,9 @@ export const SocialButtons = ({ onGoogleLogin, onTwitterLogin, onDiscordLogin, l
|
|
|
12
12
|
}
|
|
13
13
|
// If only Google is shown, use full button with text
|
|
14
14
|
if (showGoogle && !showTwitter && !showDiscord) {
|
|
15
|
-
return (_jsxs("button", { type: "button", className: "onboarding-button onboarding-button-google",
|
|
15
|
+
return (_jsxs("button", { type: "button", className: "onboarding-button onboarding-button-google", part: "button-google", onClick: onGoogleLogin, disabled: loading, children: [_jsx(GoogleIcon, { size: 20, className: "onboarding-google-icon" }), labels.googleButton || 'Continue with Google'] }));
|
|
16
16
|
}
|
|
17
17
|
// Multiple social buttons - show as grid with icons only
|
|
18
|
-
return (_jsxs("div", { className: "onboarding-social-grid", children: [showGoogle && (_jsx("button", { type: "button", className: "onboarding-button-social-icon", onClick: onGoogleLogin, disabled: loading, "aria-label": "Continue with Google", children:
|
|
18
|
+
return (_jsxs("div", { className: "onboarding-social-grid", children: [showGoogle && (_jsx("button", { type: "button", className: "onboarding-button-social-icon", onClick: onGoogleLogin, disabled: loading, "aria-label": "Continue with Google", children: _jsx(GoogleIcon, { size: 20, className: "onboarding-social-icon" }) })), showTwitter && (_jsx("button", { type: "button", className: "onboarding-button-social-icon", onClick: onTwitterLogin, disabled: loading, "aria-label": "Continue with X (Twitter)", children: _jsx("svg", { className: "onboarding-social-icon", width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" }) }) })), showDiscord && (_jsx("button", { type: "button", className: "onboarding-button-social-icon", onClick: onDiscordLogin, disabled: loading, "aria-label": "Continue with Discord", children: _jsx("svg", { className: "onboarding-social-icon", width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" }) }) }))] }));
|
|
19
19
|
};
|
|
20
20
|
//# sourceMappingURL=SocialButtons.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface OnboardingShadowContainer {
|
|
2
|
+
shadowRoot: ShadowRoot;
|
|
3
|
+
/** Stable element inside the shadow root — append the onboarding DOM tree here instead of document.body. */
|
|
4
|
+
container: HTMLElement;
|
|
5
|
+
hostElement: HTMLElement;
|
|
6
|
+
/** Call from destroy() — removes hostElement from document.body and unregisters from shadowRegistry. */
|
|
7
|
+
teardown: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function createOnboardingShadowContainer(): OnboardingShadowContainer;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-`OnboardingUIWeb`-instance shadow container factory — NOT a page-wide
|
|
3
|
+
* singleton. Each instance calls this once from init() and owns the result
|
|
4
|
+
* exclusively. A shared singleton would mean tearing down one
|
|
5
|
+
* AbstraxnProvider instance's onboarding modal rips the shadow host out from
|
|
6
|
+
* under a sibling instance's still-mounted modal. See
|
|
7
|
+
* docs/shadow-dom-migration-plan.md for the full rationale.
|
|
8
|
+
*/
|
|
9
|
+
import externalWalletButtonsCss from '../../ExternalWalletButtons.css.js';
|
|
10
|
+
import { registerShadowRoot } from '../../utils/shadowRegistry';
|
|
11
|
+
const STACKING_DEFENSE_STYLE = 'position: static; z-index: auto; transform: none; opacity: 1; isolation: auto;';
|
|
12
|
+
export function createOnboardingShadowContainer() {
|
|
13
|
+
const hostElement = document.createElement('div');
|
|
14
|
+
hostElement.setAttribute('data-abstraxn-shadow-host', 'onboarding');
|
|
15
|
+
hostElement.setAttribute('style', STACKING_DEFENSE_STYLE);
|
|
16
|
+
document.body.appendChild(hostElement);
|
|
17
|
+
const shadowRoot = hostElement.attachShadow({ mode: 'open' });
|
|
18
|
+
const container = document.createElement('div');
|
|
19
|
+
container.setAttribute('data-abstraxn-shadow-container', '');
|
|
20
|
+
shadowRoot.appendChild(container);
|
|
21
|
+
// Static, no config dependency — injected once at creation, since
|
|
22
|
+
// ExternalWalletButtons is mounted (via a separate createRoot call in
|
|
23
|
+
// AbstraxnProviderInner.tsx) inside this same shadow root and isn't
|
|
24
|
+
// wrapped by a React ShadowHost of its own.
|
|
25
|
+
const externalWalletStyle = document.createElement('style');
|
|
26
|
+
externalWalletStyle.setAttribute('data-abstraxn-css-key', 'external-wallet-buttons');
|
|
27
|
+
externalWalletStyle.textContent = externalWalletButtonsCss;
|
|
28
|
+
shadowRoot.appendChild(externalWalletStyle);
|
|
29
|
+
const unregister = registerShadowRoot(shadowRoot);
|
|
30
|
+
const teardown = () => {
|
|
31
|
+
unregister();
|
|
32
|
+
hostElement.remove();
|
|
33
|
+
};
|
|
34
|
+
return { shadowRoot, container, hostElement, teardown };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=onboardingShadowContainer.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShadowHost - wraps children in a Shadow DOM boundary so host-page CSS can
|
|
3
|
+
* never bleed in, and this SDK's CSS can never bleed out.
|
|
4
|
+
*
|
|
5
|
+
* See docs/shadow-dom-migration-plan.md for the full design rationale
|
|
6
|
+
* (two-pass render lifecycle, Strict Mode idempotency, nested-boundary
|
|
7
|
+
* invariant, customCSS precedence, failure modes).
|
|
8
|
+
*/
|
|
9
|
+
import { type ReactNode } from 'react';
|
|
10
|
+
export interface ShadowHostProps {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
/** SDK's own static component styles — fixed at build time, injected once. */
|
|
13
|
+
cssTexts: string[];
|
|
14
|
+
/** Consumer-provided uiConfig.customCSS — runtime-mutable, always injected after cssTexts. */
|
|
15
|
+
customCSS?: string;
|
|
16
|
+
hostTagName?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function ShadowHost({ children, cssTexts, customCSS, hostTagName }: ShadowHostProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ShadowHost - wraps children in a Shadow DOM boundary so host-page CSS can
|
|
4
|
+
* never bleed in, and this SDK's CSS can never bleed out.
|
|
5
|
+
*
|
|
6
|
+
* See docs/shadow-dom-migration-plan.md for the full design rationale
|
|
7
|
+
* (two-pass render lifecycle, Strict Mode idempotency, nested-boundary
|
|
8
|
+
* invariant, customCSS precedence, failure modes).
|
|
9
|
+
*/
|
|
10
|
+
import React, { useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
11
|
+
import { createPortal } from 'react-dom';
|
|
12
|
+
import { ShadowHostContext, useShadowHost } from './ShadowHostContext';
|
|
13
|
+
import { registerShadowRoot } from '../../utils/shadowRegistry';
|
|
14
|
+
/**
|
|
15
|
+
* Prevents ordinary host-page global CSS (e.g. a broad `div { transform: ... }`
|
|
16
|
+
* reset) from accidentally promoting the shadow host into its own stacking
|
|
17
|
+
* context and trapping the modal's internal z-index inside it.
|
|
18
|
+
*/
|
|
19
|
+
const STACKING_DEFENSE_STYLE = {
|
|
20
|
+
position: 'static',
|
|
21
|
+
zIndex: 'auto',
|
|
22
|
+
transform: 'none',
|
|
23
|
+
opacity: 1,
|
|
24
|
+
isolation: 'auto',
|
|
25
|
+
};
|
|
26
|
+
function ensureStyleTagOnce(root, key, cssText) {
|
|
27
|
+
if (root.querySelector(`style[data-abstraxn-css-key="${key}"]`))
|
|
28
|
+
return;
|
|
29
|
+
const style = document.createElement('style');
|
|
30
|
+
style.setAttribute('data-abstraxn-css-key', key);
|
|
31
|
+
style.textContent = cssText;
|
|
32
|
+
root.appendChild(style);
|
|
33
|
+
}
|
|
34
|
+
function upsertStyleTag(root, key, cssText) {
|
|
35
|
+
let style = root.querySelector(`style[data-abstraxn-css-key="${key}"]`);
|
|
36
|
+
if (!style) {
|
|
37
|
+
style = document.createElement('style');
|
|
38
|
+
style.setAttribute('data-abstraxn-css-key', key);
|
|
39
|
+
root.appendChild(style);
|
|
40
|
+
}
|
|
41
|
+
if (style.textContent !== cssText) {
|
|
42
|
+
style.textContent = cssText;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function ShadowHost({ children, cssTexts, customCSS, hostTagName = 'div' }) {
|
|
46
|
+
const ancestor = useShadowHost();
|
|
47
|
+
const hostRef = useRef(null);
|
|
48
|
+
const containerRef = useRef(null);
|
|
49
|
+
const unregisterRef = useRef(null);
|
|
50
|
+
const [status, setStatus] = useState('pending');
|
|
51
|
+
// Nested case: extend the ancestor's boundary — keep its stylesheet in sync
|
|
52
|
+
// with whatever this nested instance additionally needs. No new shadow
|
|
53
|
+
// root, no new host element: the nested-boundary invariant is enforced
|
|
54
|
+
// here, not left to callers to remember.
|
|
55
|
+
useLayoutEffect(() => {
|
|
56
|
+
if (!ancestor)
|
|
57
|
+
return;
|
|
58
|
+
cssTexts.forEach((text, i) => ensureStyleTagOnce(ancestor.shadowRoot, `nested-css-${i}`, text));
|
|
59
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
|
+
}, [ancestor]);
|
|
61
|
+
useLayoutEffect(() => {
|
|
62
|
+
if (ancestor)
|
|
63
|
+
return;
|
|
64
|
+
const root = hostRef.current?.shadowRoot;
|
|
65
|
+
if (!root)
|
|
66
|
+
return;
|
|
67
|
+
if (customCSS !== undefined) {
|
|
68
|
+
upsertStyleTag(root, 'custom', customCSS);
|
|
69
|
+
}
|
|
70
|
+
}, [ancestor, customCSS]);
|
|
71
|
+
// Mount: attach shadow root, inject static cssTexts once, register.
|
|
72
|
+
// Idempotent under React 18 Strict Mode's mount->cleanup->mount dev
|
|
73
|
+
// double-invocation — attachShadow is guarded, the container/style lookups
|
|
74
|
+
// are DOM-existence checks (not trusted booleans), and register/unregister
|
|
75
|
+
// is naturally idempotent since attachShadow can only ever produce one
|
|
76
|
+
// ShadowRoot object per host element.
|
|
77
|
+
useLayoutEffect(() => {
|
|
78
|
+
if (ancestor)
|
|
79
|
+
return;
|
|
80
|
+
const host = hostRef.current;
|
|
81
|
+
if (!host)
|
|
82
|
+
return;
|
|
83
|
+
try {
|
|
84
|
+
const root = host.shadowRoot ?? host.attachShadow({ mode: 'open' });
|
|
85
|
+
let container = root.querySelector('[data-abstraxn-shadow-container]');
|
|
86
|
+
if (!container) {
|
|
87
|
+
container = document.createElement('div');
|
|
88
|
+
container.setAttribute('data-abstraxn-shadow-container', '');
|
|
89
|
+
root.appendChild(container);
|
|
90
|
+
}
|
|
91
|
+
containerRef.current = container;
|
|
92
|
+
cssTexts.forEach((text, i) => ensureStyleTagOnce(root, `css-${i}`, text));
|
|
93
|
+
if (!unregisterRef.current) {
|
|
94
|
+
unregisterRef.current = registerShadowRoot(root);
|
|
95
|
+
}
|
|
96
|
+
setStatus('ready');
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
console.error('[ShadowHost] attachShadow failed — falling back to un-isolated rendering for this boundary:', err);
|
|
100
|
+
setStatus('fallback');
|
|
101
|
+
}
|
|
102
|
+
return () => {
|
|
103
|
+
unregisterRef.current?.();
|
|
104
|
+
unregisterRef.current = null;
|
|
105
|
+
};
|
|
106
|
+
// cssTexts intentionally omitted from deps: static per component,
|
|
107
|
+
// injected once via the idempotent guard above.
|
|
108
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
109
|
+
}, [ancestor]);
|
|
110
|
+
if (ancestor) {
|
|
111
|
+
return _jsx(_Fragment, { children: children });
|
|
112
|
+
}
|
|
113
|
+
const contextValue = useMemo(() => {
|
|
114
|
+
const root = hostRef.current?.shadowRoot;
|
|
115
|
+
const container = containerRef.current;
|
|
116
|
+
if (status !== 'ready' || !root || !container)
|
|
117
|
+
return null;
|
|
118
|
+
return { shadowRoot: root, container };
|
|
119
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
120
|
+
}, [status]);
|
|
121
|
+
return (_jsxs(_Fragment, { children: [React.createElement(hostTagName, { ref: hostRef, 'data-abstraxn-shadow-host': true, style: STACKING_DEFENSE_STYLE }, status === 'fallback' ? children : undefined), status === 'ready' && contextValue && (_jsx(ShadowHostContext.Provider, { value: contextValue, children: createPortal(children, contextValue.container) }))] }));
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=ShadowHost.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ShadowHostContextValue {
|
|
2
|
+
shadowRoot: ShadowRoot;
|
|
3
|
+
/** Stable element inside the shadow root — the actual createPortal/createRoot target. */
|
|
4
|
+
container: HTMLElement;
|
|
5
|
+
}
|
|
6
|
+
export declare const ShadowHostContext: import("react").Context<ShadowHostContextValue | null>;
|
|
7
|
+
/** Nullable — returns the enclosing ShadowHost's value, or null if not inside one. */
|
|
8
|
+
export declare function useShadowHost(): ShadowHostContextValue | null;
|
|
9
|
+
/** True if already nested inside another SDK-owned shadow boundary. */
|
|
10
|
+
export declare function useIsInsideShadowHost(): boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstraxn Shadow Host context
|
|
3
|
+
*/
|
|
4
|
+
import { createContext, useContext } from 'react';
|
|
5
|
+
export const ShadowHostContext = createContext(null);
|
|
6
|
+
/** Nullable — returns the enclosing ShadowHost's value, or null if not inside one. */
|
|
7
|
+
export function useShadowHost() {
|
|
8
|
+
return useContext(ShadowHostContext);
|
|
9
|
+
}
|
|
10
|
+
/** True if already nested inside another SDK-owned shadow boundary. */
|
|
11
|
+
export function useIsInsideShadowHost() {
|
|
12
|
+
return useShadowHost() !== null;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=ShadowHostContext.js.map
|
|
@@ -226,6 +226,15 @@
|
|
|
226
226
|
pointer-events: none;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
/* Visual-only equivalent of :disabled - deliberately keeps pointer-events enabled so a
|
|
230
|
+
stuck switching state can never silently swallow clicks the way a native disabled
|
|
231
|
+
attribute (or pointer-events: none) would. The JS click guard + safety timeout in
|
|
232
|
+
ChainSelector.tsx handle actually preventing redundant switches. */
|
|
233
|
+
.wallet-modal-chain-item[aria-disabled="true"] {
|
|
234
|
+
opacity: 0.5;
|
|
235
|
+
cursor: not-allowed;
|
|
236
|
+
}
|
|
237
|
+
|
|
229
238
|
.wallet-modal-chain-item.switching {
|
|
230
239
|
opacity: 0.7;
|
|
231
240
|
pointer-events: none;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default "/* Select Network View */\n.wallet-modal-chain-selector-view {\n position: relative;\n width: 100%;\n height: 100%;\n min-height: 450px;\n background: #ffffff;\n display: flex;\n flex-direction: column;\n animation: slideInRight 0.3s ease-out;\n}\n\n@keyframes slideInRight {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n}\n\n.wallet-modal-chain-selector-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0;\n border-bottom: 1px solid #f1f5f9;\n padding-bottom: 16px;\n margin-bottom: 16px;\n}\n\n.wallet-modal-chain-selector-back,\n.wallet-modal-chain-selector-close {\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 8px;\n border-radius: 8px;\n color: #64748b;\n transition: all 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: auto;\n user-select: none;\n -webkit-user-select: none;\n width: 36px;\n height: 36px;\n flex-shrink: 0;\n}\n\n.wallet-modal-chain-selector-back:hover,\n.wallet-modal-chain-selector-close:hover {\n background: #f1f5f9;\n color: #111827;\n}\n\n.wallet-modal-chain-selector-back:active,\n.wallet-modal-chain-selector-close:active {\n transform: scale(0.95);\n}\n\n.wallet-modal-content .wallet-modal-chain-selector-title {\n font-size: 18px;\n font-weight: 500;\n margin: 0;\n color: #111827;\n flex: 1;\n text-align: center;\n}\n\n.wallet-modal-chain-selector-content {\n flex: 1;\n overflow-y: auto;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n/* Custom Scrollbar */\n.wallet-modal-chain-selector-content::-webkit-scrollbar {\n width: 8px;\n}\n\n.wallet-modal-chain-selector-content::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.wallet-modal-chain-selector-content::-webkit-scrollbar-thumb {\n background: #cbd5e1;\n border-radius: 10px;\n border: 2px solid transparent;\n background-clip: content-box;\n}\n\n.wallet-modal-chain-selector-content::-webkit-scrollbar-thumb:hover {\n background: #94a3b8;\n background-clip: content-box;\n}\n\n.wallet-modal-theme-dark .wallet-modal-chain-selector-content::-webkit-scrollbar-thumb {\n background: #475569;\n}\n\n/* Tabs */\n.wallet-modal-chain-tabs {\n display: flex;\n gap: 12px;\n}\n\n.wallet-modal-chain-tab {\n padding: 8px 16px;\n border-radius: 10px;\n border: none;\n background: transparent;\n cursor: pointer;\n font-size: 14px;\n font-weight: 600;\n color: #64748b;\n transition: all 0.2s;\n pointer-events: auto;\n user-select: none;\n -webkit-user-select: none;\n}\n\n.wallet-modal-chain-tab:hover {\n color: #111827;\n}\n\n.wallet-modal-chain-tab.active {\n background: #f1f5f9;\n color: #111827;\n}\n\n/* Search */\n.wallet-modal-chain-search-container {\n position: relative;\n width: 100%;\n}\n\n.wallet-modal-chain-search-icon {\n position: absolute;\n left: 16px;\n top: 50%;\n transform: translateY(-50%);\n color: #94a3b8;\n pointer-events: none;\n display: flex;\n}\n\n.wallet-modal-chain-search-input {\n width: 100%;\n padding: 12px 16px 12px 48px;\n border-radius: 12px;\n border: 1px solid #e2e8f0;\n background: #ffffff;\n font-size: 15px;\n outline: none;\n transition: all 0.2s;\n color: #111827;\n box-sizing: border-box;\n pointer-events: auto;\n user-select: text;\n -webkit-user-select: text;\n}\n\n.wallet-modal-chain-search-input:focus {\n border-color: #9333ea;\n}\n\n.wallet-modal-chain-search-input::placeholder {\n color: #94a3b8;\n}\n\n/* List */\n.wallet-modal-chain-list-container {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.wallet-modal-chain-list-header {\n font-size: 13px;\n font-weight: 600;\n color: #94a3b8;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n padding-left: 0;\n}\n\n.wallet-modal-chain-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.wallet-modal-chain-item {\n width: 100%;\n padding: 10px 12px;\n border-radius: 12px;\n border: none;\n background: transparent;\n cursor: pointer;\n transition: all 0.2s;\n display: flex;\n align-items: center;\n gap: 16px;\n text-align: left;\n pointer-events: auto;\n user-select: none;\n -webkit-user-select: none;\n}\n\n.wallet-modal-chain-item:hover {\n background: #f8fafc;\n}\n\n.wallet-modal-chain-item.active {\n background: rgba(147, 51, 234, 0.05);\n}\n\n.wallet-modal-chain-item:active {\n transform: scale(0.98);\n}\n\n.wallet-modal-chain-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n/* Visual-only equivalent of :disabled - deliberately keeps pointer-events enabled so a\n stuck switching state can never silently swallow clicks the way a native disabled\n attribute (or pointer-events: none) would. The JS click guard + safety timeout in\n ChainSelector.tsx handle actually preventing redundant switches. */\n.wallet-modal-chain-item[aria-disabled=\"true\"] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.wallet-modal-chain-item.switching {\n opacity: 0.7;\n pointer-events: none;\n}\n\n.wallet-modal-chain-item-icon-wrapper {\n position: relative;\n width: 36px;\n height: 36px;\n flex-shrink: 0;\n}\n\n.wallet-modal-chain-item-icon-img {\n width: 100%;\n height: 100%;\n border-radius: 50%;\n object-fit: cover;\n}\n\n.wallet-modal-chain-item-icon-placeholder {\n width: 100%;\n height: 100%;\n border-radius: 50%;\n background: #f1f5f9;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n color: #64748b;\n font-size: 14px;\n}\n\n.wallet-modal-chain-item-status {\n position: absolute;\n bottom: -2px;\n right: -2px;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: #10b981;\n border: 2px solid #ffffff;\n}\n\n.wallet-modal-chain-item-name {\n flex: 1;\n font-size: 16px;\n font-weight: 600;\n color: #111827;\n}\n\n.wallet-modal-chain-item-checkmark {\n color: #10b981;\n display: flex;\n}\n\n.wallet-modal-chain-no-results {\n padding: 40px 20px;\n text-align: center;\n color: #64748b;\n font-size: 15px;\n}\n\n/* Dark Theme Support */\n.wallet-modal-theme-dark.wallet-modal-chain-selector-view {\n background: #1a1a1a;\n}\n\n.wallet-modal-theme-dark .wallet-modal-chain-selector-header {\n border-color: #2a2a2a;\n}\n\n.wallet-modal-content.wallet-modal-theme-dark .wallet-modal-chain-selector-title {\n color: #ffffff;\n}\n\n.wallet-modal-theme-dark .wallet-modal-chain-tab.active {\n background: #2a2a2a;\n color: #ffffff;\n}\n\n.wallet-modal-theme-dark .wallet-modal-chain-search-input {\n background: #2a2a2a;\n border-color: #3a3a3a;\n color: #ffffff;\n}\n\n.wallet-modal-theme-dark .wallet-modal-chain-item:hover {\n background: #2a2a2a;\n}\n\n.wallet-modal-theme-dark .wallet-modal-chain-item-name {\n color: #ffffff;\n}\n\n.wallet-modal-theme-dark .wallet-modal-chain-item-status {\n border-color: #1a1a1a;\n}\n\n\n";
|