UI Cleanup (#4191)

## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the connect modal styles and adds support for wallet authentication in the authentication flow.

### Detailed summary
- Updated connect modal styles
- Added support for wallet authentication
- Modified styles for various UI components
- Added a new component for copying addresses
- Improved wallet image rendering based on wallet type

> The following files were skipped due to too many changes: `packages/thirdweb/src/wallets/custom/smart/image.ts`, `packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
This commit is contained in:
gregfromstl
2024-08-20 17:52:34 +00:00
parent 4abf7a7f25
commit 42cdc3a2bc
16 changed files with 272 additions and 207 deletions
@@ -170,6 +170,7 @@ export const fontSize = {
};
export const spacing = {
"4xs": "2px",
"3xs": "4px",
xxs: "6px",
xs: "8px",
@@ -19,6 +19,7 @@ import type { SmartWalletOptions } from "../../../../wallets/smart/types.js";
import type { AppMetadata } from "../../../../wallets/types.js";
import {
CustomThemeProvider,
parseTheme,
useCustomTheme,
} from "../../../core/design-system/CustomThemeProvider.js";
import {
@@ -51,15 +52,20 @@ import type {
SupportedTokens,
} from "../../../core/utils/defaultTokens.js";
import { hasSmartAccount } from "../../../core/utils/isSmartWallet.js";
import { useConnectedWalletDetails } from "../../../core/utils/wallet.js";
import {
useConnectedWalletDetails,
useWalletInfo,
} from "../../../core/utils/wallet.js";
import { WalletUIStatesProvider } from "../../providers/wallet-ui-states-provider.js";
import { ChainIcon } from "../components/ChainIcon.js";
import { CopyIcon } from "../components/CopyIcon.js";
import { Img } from "../components/Img.js";
import { IconContainer } from "../components/IconContainer.js";
import { Modal } from "../components/Modal.js";
import { Skeleton } from "../components/Skeleton.js";
import { Spacer } from "../components/Spacer.js";
import { Spinner } from "../components/Spinner.js";
import { ToolTip } from "../components/Tooltip.js";
import { WalletImage } from "../components/WalletImage.js";
import { Container, Line } from "../components/basic.js";
import { Button, IconButton } from "../components/buttons.js";
import { Link, Text } from "../components/text.js";
@@ -78,8 +84,6 @@ import { onModalUnmount } from "./constants.js";
import { CoinsIcon } from "./icons/CoinsIcon.js";
import { FundsIcon } from "./icons/FundsIcon.js";
import { OutlineWalletIcon } from "./icons/OutlineWalletIcon.js";
import { ShuffleIconLucide } from "./icons/ShuffleIconLucide.js";
import { SmartWalletBadgeIcon } from "./icons/SmartAccountBadgeIcon.js";
import { getConnectLocale } from "./locale/getConnectLocale.js";
import type { ConnectLocale } from "./locale/types.js";
import { LazyBuyScreen } from "./screens/Buy/LazyBuyScreen.js";
@@ -125,7 +129,6 @@ export const ConnectedWalletDetails: React.FC<{
const setRootEl = useContext(SetRootElementContext);
const activeAccount = useActiveAccount();
const walletChain = useActiveWalletChain();
const theme = useCustomTheme();
const { ensAvatarQuery, addressOrENS, balanceQuery } =
useConnectedWalletDetails(
@@ -191,36 +194,37 @@ export const ConnectedWalletDetails: React.FC<{
data-test="connected-wallet-details"
onClick={openModal}
>
{ensAvatarQuery.data ? (
<Img
src={ensAvatarQuery.data}
width="50px"
height="50px"
style={{
width: 50,
height: 50,
}}
client={client}
/>
) : (
activeAccount && <Blobbie address={activeAccount.address} size={50} />
)}
<Container
style={{
borderRadius: "100%",
overflow: "hidden",
width: "35px",
height: "35px",
}}
>
{ensAvatarQuery.data ? (
<img
alt=""
src={ensAvatarQuery.data}
style={{
width: "100%",
height: "100%",
}}
/>
) : (
activeAccount && <Blobbie address={activeAccount.address} size={35} />
)}
</Container>
<Container
flex="column"
gap="3xs"
px="sm"
py="xs"
gap="4xs"
style={{
border: `1px solid ${theme.colors.borderColor}`,
textOverflow: "ellipsis",
minWidth: "115px",
height: "50px",
whiteSpace: "nowrap",
borderRadius: `0 ${radius.md} ${radius.md} 0`,
}}
>
{/* Address */}
{addressOrENS ? (
<Text
size="xs"
@@ -231,7 +235,7 @@ export const ConnectedWalletDetails: React.FC<{
{addressOrENS}
</Text>
) : (
<Skeleton height={fontSize.sm} width="80px" />
<Skeleton height={fontSize.xs} width="80px" />
)}
{/* Balance */}
@@ -239,9 +243,12 @@ export const ConnectedWalletDetails: React.FC<{
<Text
className={`${TW_CONNECTED_WALLET}__balance`}
size="xs"
weight={500}
color="secondaryText"
weight={400}
>
{Number(balanceQuery.data.displayValue).toFixed(3)}{" "}
{Number.parseFloat(
Number(balanceQuery.data.displayValue).toFixed(3),
)}{" "}
{balanceQuery.data.symbol}
</Text>
) : (
@@ -282,6 +289,7 @@ function DetailsModal(props: {
activeAccount,
props.displayBalanceToken,
);
const theme = parseTheme(props.theme);
const activeWallet = useActiveWallet();
const chainIconQuery = useChainIconUrl(walletChain);
@@ -340,8 +348,18 @@ function DetailsModal(props: {
{chainNameQuery.isLoading ? (
<Skeleton height={"16px"} width={"150px"} />
) : (
<Text color="primaryText" multiline>
<Text color="primaryText" size="md" multiline>
{chainNameQuery.name || `Unknown chain #${walletChain?.id}`}
<Text color="secondaryText" size="xs">
{balanceQuery.data ? (
Number.parseFloat(
Number(balanceQuery.data.displayValue).toFixed(3),
)
) : (
<Skeleton height="1em" width="100px" />
)}{" "}
{balanceQuery.data?.symbol}
</Text>
</Text>
)}
@@ -358,114 +376,103 @@ function DetailsModal(props: {
let content = (
<div>
<Spacer y="xl" />
<IconButton
style={{
position: "absolute",
top: spacing.lg,
left: spacing.lg,
transform: "translateX(-6px)",
}}
onClick={() => {
setScreen("wallet-manager");
}}
>
<div
style={{
width: `${iconSize.md}px`,
height: `${iconSize.md}px`,
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<ShuffleIconLucide size="20" />
</div>
</IconButton>
<Container px="lg" flex="column" center="x">
<Container
style={{
position: "relative",
height: `${iconSize.xxl}px`,
width: `${iconSize.xxl}px`,
overflow: "visible",
}}
>
{ensAvatarQuery.data ? (
<Img
src={ensAvatarQuery.data}
width={iconSize.xxl}
height={iconSize.xxl}
style={{
borderRadius: radius.md,
}}
client={client}
/>
) : (
activeAccount && (
<Container
style={{ borderRadius: radius.md, overflow: "hidden" }}
>
<Blobbie
address={activeAccount.address}
size={Number(iconSize.xxl)}
/>
</Container>
)
)}
</Container>
<Spacer y="md" />
<ConnectedToSmartWallet client={props.client} connectLocale={locale} />
{(activeWallet?.id === "embedded" || activeWallet?.id === "inApp") && (
<InAppWalletUserInfo client={props.client} />
)}
{/* Address */}
<div
style={{
display: "flex",
gap: spacing.xxs,
alignItems: "center",
transform: "translateX(10px)",
}}
data-test="connected-wallet-address"
data-address={activeAccount?.address}
>
<Text color="primaryText" weight={500} size="md">
{addressOrENS}
</Text>
<IconButton
<Spacer y="xs" />
<Container p="lg" gap="sm" flex="row" center="y">
<ToolTip tip="Switch wallet">
<div
style={{
padding: "3px",
cursor: "pointer",
}}
onKeyDown={(e) => {
if (e.key === "w") {
setScreen("wallet-manager");
}
}}
onClick={() => {
setScreen("wallet-manager");
}}
data-test="copy-address"
>
<CopyIcon
text={activeAccount?.address || ""}
tip={locale.copyAddress}
side="top"
/>
</IconButton>
</div>
<Container
style={{
position: "relative",
height: `${iconSize.xl}px`,
width: `${iconSize.xl}px`,
}}
>
<Container
style={{
width: "100%",
height: "100%",
borderRadius: "100%",
overflow: "hidden",
}}
>
{ensAvatarQuery.data ? (
<img
src={ensAvatarQuery.data}
style={{
width: iconSize.xxl,
height: iconSize.xxl,
}}
alt=""
/>
) : (
activeAccount && (
<Blobbie
address={activeAccount.address}
size={Number(iconSize.xxl)}
/>
)
)}
</Container>
<Container
style={{
position: "absolute",
bottom: -2,
right: -2,
}}
>
<IconContainer
style={{
background: theme.colors.modalBg,
}}
padding="4px"
>
{activeWallet && (
<WalletImage
style={{ borderRadius: 0 }}
id={activeWallet.id}
client={client}
size="12"
/>
)}
</IconContainer>
</Container>
</Container>
</div>
</ToolTip>
<Spacer y="xxs" />
{/* Balance */}
<Text weight={500} size="sm">
{balanceQuery.data ? (
Number(balanceQuery.data.displayValue).toFixed(3)
) : (
<Skeleton height="1em" width="100px" />
)}{" "}
{balanceQuery.data?.symbol}{" "}
</Text>
<Container flex="column" gap="3xs">
<div
style={{
display: "flex",
gap: spacing.xxs,
alignItems: "center",
}}
>
<Text color="primaryText" weight={500} size="md">
{addressOrENS}
</Text>
<IconButton>
<CopyIcon
text={activeAccount?.address || ""}
tip={locale.copyAddress}
/>
</IconButton>
</div>
<InAppWalletUserInfo client={client} locale={locale} />
</Container>
</Container>
<Spacer y="lg" />
<Container px="lg">
{/* Send, Receive, Swap */}
<Container
@@ -911,10 +918,13 @@ const WalletInfoButton = /* @__PURE__ */ StyledButton((_) => {
borderRadius: radius.md,
cursor: "pointer",
display: "inline-flex",
gap: spacing.xs,
padding: spacing.xs,
alignItems: "center",
minWidth: "165px",
height: "50px",
boxSizing: "border-box",
border: `1px solid ${theme.colors.borderColor}`,
WebkitTapHighlightColor: "transparent",
lineHeight: "normal",
animation: `${fadeInAnimation} 300ms ease`,
@@ -963,20 +973,8 @@ function ConnectedToSmartWallet(props: {
}, [activeAccount, chain, client, isSmartWallet]);
const content = (
<Container
flex="row"
bg="secondaryButtonBg"
gap="xxs"
style={{
borderRadius: radius.md,
padding: `${spacing.xxs} ${spacing.sm} ${spacing.xxs} ${spacing.xs}`,
}}
center="y"
>
<Container flex="row" color="accentText" center="both">
<SmartWalletBadgeIcon size={iconSize.xs} />
</Container>
<Text size="xs" color="secondaryButtonText">
<Container flex="row" gap="3xs" center="y">
<Text size="xs" weight={400} color="secondaryText">
{locale.connectedToSmartWallet}
</Text>
</Container>
@@ -998,8 +996,6 @@ function ConnectedToSmartWallet(props: {
) : (
<Text size="sm"> {content}</Text>
)}
<Spacer y="xs" />
</>
);
}
@@ -1007,9 +1003,15 @@ function ConnectedToSmartWallet(props: {
return null;
}
function InAppWalletUserInfo(props: { client: ThirdwebClient }) {
const { client } = props;
function InAppWalletUserInfo(props: {
client: ThirdwebClient;
locale: ConnectLocale;
}) {
const { client, locale } = props;
const account = useActiveAccount();
const activeWallet = useActiveWallet();
const { data: walletInfo } = useWalletInfo(activeWallet?.id);
const isSmartWallet = hasSmartAccount(activeWallet);
const userInfoQuery = useQuery({
queryKey: ["in-app-wallet-user", client, account?.address],
@@ -1031,21 +1033,19 @@ function InAppWalletUserInfo(props: { client: ThirdwebClient }) {
},
});
if (userInfoQuery.data) {
if (isSmartWallet) {
return <ConnectedToSmartWallet client={client} connectLocale={locale} />;
}
if (userInfoQuery.data || walletInfo) {
return (
<Container
flex="row"
center="x"
style={{
paddingBottom: spacing.xs,
}}
>
<Text size="sm">{userInfoQuery.data}</Text>
</Container>
<Text size="xs" weight={400}>
{userInfoQuery.data || walletInfo?.name}
</Text>
);
}
return null;
return <Skeleton width="50px" height="10px" />;
}
/**
@@ -28,7 +28,6 @@ export const MenuButton = /* @__PURE__ */ StyledButton((_) => {
transition: "background-color 200ms ease, transform 200ms ease",
"&:hover": {
backgroundColor: theme.colors.tertiaryBg,
transform: "scale(1.01)",
svg: {
color: theme.colors.accentText,
},
@@ -15,7 +15,6 @@ import { WalletImage } from "../../../components/WalletImage.js";
import { Container } from "../../../components/basic.js";
import { Button } from "../../../components/buttons.js";
import { Text } from "../../../components/text.js";
import { SmartWalletBadgeIcon } from "../../icons/SmartAccountBadgeIcon.js";
export function WalletSelectorButton(props: {
address: string;
@@ -107,11 +106,6 @@ export function WalletRow(props: {
<Text size="sm" color="primaryText">
{addressOrENS || shortenAddress(props.address)}
</Text>
{walletId === "smart" && (
<Container color="accentText" center="both">
<SmartWalletBadgeIcon size={iconSize.sm} />
</Container>
)}
</Container>
);
}
@@ -148,20 +148,19 @@ function WalletManangerButton(props: {
justifyContent: "space-between",
}}
>
<Container flex="row" gap="sm" center="y">
<WalletImage client={props.client} id={walletId} size={iconSize.xl} />
<Container flex="row" gap="md" center="y">
<WalletImage client={props.client} id={walletId} size={iconSize.lg} />
<div>
<Container flex="column" gap="4xs">
<Text color="primaryText">{shortenAddress(address || "")}</Text>
<Spacer y="xxs" />
{balanceQuery.data ? (
<Text size="sm" color="secondaryText">
<Text size="xs" color="secondaryText" weight={400}>
{formatTokenBalance(balanceQuery.data)}
</Text>
) : (
<Skeleton width="100px" height={fontSize.sm} />
)}
</div>
</Container>
</Container>
</WalletButtonEl>
@@ -10,6 +10,7 @@ import { Img } from "../../components/Img.js";
import { Spacer } from "../../components/Spacer.js";
import { Container, Line, ModalHeader } from "../../components/basic.js";
import { Text } from "../../components/text.js";
import { Blobbie } from "../Blobbie.js";
import { MenuButton } from "../MenuButton.js";
import { AddUserIcon } from "../icons/AddUserIcon.js";
import type { ConnectLocale } from "../locale/types.js";
@@ -98,13 +99,26 @@ export function LinkedProfilesScreen(props: {
}}
disabled // disabled until we have more data to show on a dedicated profile screen
>
<Img
src={getWalletIcon(profile.type)}
width={iconSize.md}
height={iconSize.md}
loading="eager"
client={props.client}
/>
{profile.type === "wallet" && profile.details.address ? (
<Container
style={{
width: "24px",
height: "24px",
borderRadius: "100%",
overflow: "hidden",
}}
>
<Blobbie address={profile.details.address} size={24} />
</Container>
) : (
<Img
src={getWalletIcon(profile.type)}
width={iconSize.md}
height={iconSize.md}
loading="eager"
client={props.client}
/>
)}
<Text color="primaryText">
{getProfileDisplayName(profile)}
</Text>
@@ -2,6 +2,7 @@ import type { ThirdwebClient } from "../../../../client/client.js";
import { resolveScheme } from "../../../../utils/ipfs.js";
import { StyledDiv } from "../design-system/elements.js";
import { Img } from "./Img.js";
import { Container } from "./basic.js";
export const fallbackChainIcon =
"data:image/svg+xml;charset=UTF-8,%3csvg width='15' height='14' viewBox='0 0 15 14' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M7 8.04238e-07C5.1435 8.04238e-07 3.36301 0.737501 2.05025 2.05025C0.7375 3.36301 0 5.1435 0 7C0 7.225 -1.52737e-07 7.445 0.0349998 7.665C0.16385 9.0151 0.68213 10.2988 1.52686 11.3598C2.37158 12.4209 3.50637 13.2137 4.79326 13.642C6.0801 14.0702 7.4637 14.1153 8.7758 13.7719C10.0879 13.4285 11.2719 12.7113 12.184 11.7075C13.0961 10.7038 13.6969 9.4567 13.9135 8.1178C14.1301 6.7789 13.9531 5.406 13.4039 4.16587C12.8548 2.92574 11.9573 1.87184 10.8204 1.13228C9.6835 0.392721 8.3563 -0.000649196 7 8.04238e-07ZM7 1C8.581 1.00137 10.0975 1.62668 11.22 2.74V3.24C9.2438 2.55991 7.0956 2.56872 5.125 3.265C4.96758 3.1116 4.76997 3.00586 4.555 2.96H4.43C4.37 2.75 4.315 2.54 4.27 2.325C4.225 2.11 4.2 1.92 4.175 1.715C5.043 1.24658 6.0137 1.00091 7 1ZM5.5 3.935C7.3158 3.32693 9.2838 3.34984 11.085 4C10.8414 5.2703 10.3094 6.4677 9.53 7.5C9.312 7.4077 9.0707 7.3855 8.8395 7.4366C8.6083 7.4877 8.3988 7.6094 8.24 7.785C8.065 7.685 7.89 7.585 7.74 7.47C6.7307 6.7966 5.8877 5.9023 5.275 4.855C5.374 4.73221 5.4461 4.58996 5.4866 4.43749C5.5271 4.28502 5.5351 4.12575 5.51 3.97L5.5 3.935ZM3.5 2.135C3.5 2.24 3.53 2.35 3.55 2.455C3.595 2.675 3.655 2.89 3.715 3.105C3.52353 3.21838 3.36943 3.38531 3.2717 3.58522C3.17397 3.78513 3.13688 4.00927 3.165 4.23C2.37575 4.7454 1.67078 5.3795 1.075 6.11C1.19455 5.3189 1.47112 4.55966 1.88843 3.87701C2.30575 3.19437 2.85539 2.60208 3.505 2.135H3.5ZM3.5 9.99C3.30481 10.0555 3.13037 10.1714 2.9943 10.3259C2.85822 10.4804 2.76533 10.6681 2.725 10.87H2.405C1.59754 9.9069 1.1146 8.7136 1.025 7.46L1.08 7.365C1.70611 6.3942 2.52463 5.562 3.485 4.92C3.62899 5.0704 3.81094 5.179 4.01162 5.2345C4.2123 5.2899 4.42423 5.2901 4.625 5.235C5.2938 6.3652 6.208 7.3306 7.3 8.06C7.505 8.195 7.715 8.32 7.925 8.44C7.9082 8.6312 7.9391 8.8237 8.015 9C7.1 9.7266 6.0445 10.256 4.915 10.555C4.78401 10.3103 4.57028 10.1201 4.31199 10.0184C4.05369 9.9167 3.76766 9.9102 3.505 10L3.5 9.99ZM7 12.99C5.9831 12.9903 4.98307 12.7304 4.095 12.235L4.235 12.205C4.43397 12.1397 4.61176 12.0222 4.74984 11.8648C4.88792 11.7074 4.98122 11.5158 5.02 11.31C6.2985 10.984 7.4921 10.3872 8.52 9.56C8.7642 9.7027 9.0525 9.75 9.3295 9.6927C9.6064 9.6355 9.8524 9.4778 10.02 9.25C10.7254 9.4334 11.4511 9.5275 12.18 9.53H12.445C11.9626 10.5673 11.1938 11.4451 10.2291 12.0599C9.2643 12.6747 8.144 13.0009 7 13V12.99ZM10.255 8.54C10.2545 8.3304 10.1975 8.1249 10.09 7.945C10.9221 6.8581 11.5012 5.5991 11.785 4.26C12.035 4.37667 12.2817 4.50667 12.525 4.65C13.0749 5.9495 13.1493 7.4012 12.735 8.75C11.9049 8.8142 11.0698 8.7484 10.26 8.555L10.255 8.54Z' fill='%23646D7A'/%3e%3c/svg%3e";
@@ -33,7 +34,7 @@ export const ChainIcon: React.FC<{
};
return (
<div
<Container
style={{
position: "relative",
display: "flex",
@@ -49,7 +50,7 @@ export const ChainIcon: React.FC<{
client={props.client}
/>
{props.active && <ActiveDot />}
</div>
</Container>
);
};
@@ -58,7 +59,7 @@ const ActiveDot = /* @__PURE__ */ StyledDiv({
height: "28%",
borderRadius: "50%",
position: "absolute",
top: "60%",
bottom: 0,
right: 0,
backgroundColor: "#00d395",
boxShadow: "0 0 0 2px var(--bg)",
@@ -0,0 +1,34 @@
"use client";
import { spacing } from "../../../core/design-system/index.js";
import { useClipboard } from "../hooks/useCopyClipboard.js";
import { IconButton } from "./buttons.js";
import { Text } from "./text.js";
/**
* @internal
*/
export const CopyAddress: React.FC<{
addressOrEns: string;
}> = (props) => {
const { hasCopied, onCopy } = useClipboard(props.addressOrEns);
return (
<IconButton
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
onCopy();
}
}}
onClick={onCopy}
style={{ padding: `${spacing["4xs"]} ${spacing.xxs}` }}
>
<Text
color={hasCopied ? "primaryText" : "secondaryText"}
weight={400}
size="sm"
>
{hasCopied ? "Copied address!" : props.addressOrEns}
</Text>
</IconButton>
);
};
@@ -0,0 +1,31 @@
import type { ReactNode } from "react";
import { useCustomTheme } from "../../../core/design-system/CustomThemeProvider.js";
/**
* @internal
*/
export const IconContainer: React.FC<{
children: ReactNode | ReactNode[];
padding?: string;
style?: React.CSSProperties;
}> = (props) => {
const theme = useCustomTheme();
return (
<div
style={{
position: "relative",
overflow: "hidden",
display: "flex",
justifyItems: "center",
flexShrink: 0,
alignItems: "center",
padding: props.padding ?? "6px",
borderRadius: "100%",
border: `1px solid ${theme.colors.borderColor}`,
...props.style,
}}
>
{props.children}
</div>
);
};
@@ -114,7 +114,7 @@ export const Modal: React.FC<{
{!props.hideCloseIcon && (
<CrossContainer>
<Dialog.Close asChild>
<IconButton type="button" aria-label="Close">
<IconButton autoFocus type="button" aria-label="Close">
<Cross2Icon
width={iconSize.md}
height={iconSize.md}
@@ -4,7 +4,6 @@ import type { ThirdwebClient } from "../../../../client/client.js";
import { webLocalStorage } from "../../../../utils/storage/webStorage.js";
import { getWalletInfo } from "../../../../wallets/__generated__/getWalletInfo.js";
import { getInstalledWalletProviders } from "../../../../wallets/injected/mipdStore.js";
import { getStoredActiveWalletId } from "../../../../wallets/manager/index.js";
import type { WalletId } from "../../../../wallets/wallet-types.js";
import { radius } from "../../../core/design-system/index.js";
import { useActiveWallet } from "../../../core/hooks/wallets/useActiveWallet.js";
@@ -33,13 +32,7 @@ export function WalletImage(props: {
// show auth provider icon for in-app wallets
// show the admin EOA icon for smart
const storage = webLocalStorage;
let activeEOAId = props.id;
if (props.id === "smart") {
const storedId = await getStoredActiveWalletId(storage);
if (storedId) {
activeEOAId = storedId;
}
}
const activeEOAId = props.id;
let image: string | undefined;
if (
@@ -110,16 +110,16 @@ export const InputContainer = /* @__PURE__ */ StyledDiv(() => {
return {
display: "flex",
borderRadius: radius.md,
boxShadow: `0 0 0px 1.5px ${theme.colors.borderColor}`,
boxShadow: `0 0 0px 1px ${theme.colors.borderColor}`,
"&:focus-within": {
boxShadow: `0 0 0px 2px ${theme.colors.accentText}`,
boxShadow: `0 0 0px 1px ${theme.colors.accentText}`,
},
"input:focus": {
boxShadow: "none",
},
// show error ring on container instead of input
"&[data-error='true']": {
boxShadow: `0 0 0px 2px ${theme.colors.danger}`,
boxShadow: `0 0 0px 1px ${theme.colors.danger}`,
},
};
});
@@ -2,11 +2,7 @@
import { useQuery } from "@tanstack/react-query";
import { useRef } from "react";
import { useCustomTheme } from "../../../core/design-system/CustomThemeProvider.js";
import {
fontSize,
radius,
spacing,
} from "../../../core/design-system/index.js";
import { radius, spacing } from "../../../core/design-system/index.js";
import { StyledOption, StyledSelect } from "../../ui/design-system/elements.js";
export function CountrySelector({
@@ -47,8 +43,7 @@ export function CountrySelector({
setCountryCode(e.target.value);
}}
style={{
paddingLeft: spacing.md,
paddingRight: "0",
padding: `${spacing.sm} ${spacing.md}`,
}}
>
<Option
@@ -89,7 +84,6 @@ const Option = /* @__PURE__ */ StyledOption(() => {
const Select = /* @__PURE__ */ StyledSelect((_) => {
const theme = useCustomTheme();
return {
fontSize: fontSize.sm,
display: "block",
padding: spacing.sm,
boxSizing: "border-box",
@@ -1,4 +1,4 @@
const image =
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiByeD0iMTIiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8xXzkxKSIvPgo8cGF0aCBkPSJNMzkuOTk2OSAxOEw0MC4yMzMzIDE4LjAxNTZMNDAuMzUxMyAxOC4wMzMzTDQwLjQ3MzYgMTguMDYyMkw0MC42OTU4IDE4LjEzNzhDNDAuODQ5NCAxOC4yMDA2IDQwLjk5NTQgMTguMjg0MiA0MS4xMzA1IDE4LjM4NjdMNDEuMzM4NyAxOC41Njg5TDQxLjg0OTMgMTkuMDUzM0M0NS44ODkxIDIyLjc3NjggNTAuOTk0NyAyNC43NzYyIDU2LjI0NTggMjQuNjkxMUw1Ni45MzA3IDI0LjY2ODlDNTcuMzc4MyAyNC42NDYyIDU3LjgyIDI0Ljc5MDkgNTguMTg0OSAyNS4wNzk4QzU4LjU0OTggMjUuMzY4NyA1OC44MTY3IDI1Ljc4NSA1OC45NDMxIDI2LjI2MjJDNTkuOTI3MSAyOS45NzY5IDYwLjIyODMgMzMuODczMSA1OS44Mjg3IDM3LjcxOTRDNTkuNDI4OSA0MS41NjU4IDU4LjMzNjcgNDUuMjgzNiA1Ni42MTY1IDQ4LjY1MjNDNTQuODk2NSA1Mi4wMjA3IDUyLjU4MzYgNTQuOTcxNCA0OS44MTU2IDU3LjMyODVDNDcuMDQ3NiA1OS42ODU2IDQzLjg4MDkgNjEuNDAxMiA0MC41MDM2IDYyLjM3MzRDNDAuMTc0IDYyLjQ2ODMgMzkuODI4IDYyLjQ2ODMgMzkuNDk4MiA2Mi4zNzM0QzM2LjEyMDkgNjEuNDAxNCAzMi45NTM4IDU5LjY4NiAzMC4xODU2IDU3LjMyODlDMjcuNDE3NiA1NC45NzE4IDI1LjEwNDUgNTIuMDIxNCAyMy4zODQyIDQ4LjY1MjlDMjEuNjYzOCA0NS4yODQzIDIwLjU3MTMgNDEuNTY2MiAyMC4xNzE1IDM3LjcxOThDMTkuNzcxNyAzMy44NzM0IDIwLjA3MjcgMjkuOTc2OSAyMS4wNTY3IDI2LjI2MjJDMjEuMTgzMSAyNS43ODUgMjEuNDUwMSAyNS4zNjg3IDIxLjgxNSAyNS4wNzk4QzIyLjE3OTkgMjQuNzkwOSAyMi42MjE1IDI0LjY0NjIgMjMuMDY5MyAyNC42Njg5QzI4LjU1MTMgMjQuOTQ3IDMzLjkyOTMgMjIuOTQ0NCAzOC4xNTA3IDE5LjA1MzNMMzguNjc3MyAxOC41NTMzTDM4Ljg2OTYgMTguMzg2N0MzOS4wMDQ3IDE4LjI4NDIgMzkuMTUwNSAxOC4yMDA2IDM5LjMwNCAxOC4xMzc4TDM5LjUyODIgMTguMDYyMkMzOS42MDY5IDE4LjA0MTIgMzkuNjg2NSAxOC4wMjU2IDM5Ljc2NjcgMTguMDE1NkwzOS45OTY5IDE4Wk00MC4wMDA5IDMzLjU1NTZDMzguOTkwNSAzMy41NTUxIDM4LjAxNzMgMzMuOTc4NyAzNy4yNzY1IDM0Ljc0MTFDMzYuNTM1NiAzNS41MDM2IDM2LjA4MTYgMzYuNTQ4NSAzNi4wMDU4IDM3LjY2NjdMMzUuOTk1OCAzOEwzNi4wMDU4IDM4LjMzMzRDMzYuMDU1MSAzOS4wNTQ3IDM2LjI2MjUgMzkuNzUxOCAzNi42MDk4IDQwLjM2NEMzNi45NTY5IDQwLjk3NjIgMzcuNDMzNiA0MS40ODUxIDM3Ljk5ODUgNDEuODQ2N1Y0NS43Nzc4TDM4LjAxMjUgNDYuMDM3OEMzOC4wNzI3IDQ2LjYwMDMgMzguMzI0MiA0Ny4xMTU4IDM4LjcxNTYgNDcuNDc5NEMzOS4xMDcxIDQ3Ljg0MjkgMzkuNjA4NyA0OC4wMjY5IDQwLjExODIgNDcuOTkzOEM0MC42Mjc4IDQ3Ljk2MDUgNDEuMTA2NyA0Ny43MTI3IDQxLjQ1NzEgNDcuMzAwOUM0MS44MDc2IDQ2Ljg4ODkgNDIuMDAyOSA0Ni4zNDQzIDQyLjAwMzYgNDUuNzc3OEw0Mi4wMDU2IDQxLjg0ODlDNDIuNzY5MSA0MS4zNTk2IDQzLjM2NiA0MC42MDQyIDQzLjcwMzQgMzkuNzAwMkM0NC4wNDA3IDM4Ljc5NiA0NC4wOTk2IDM3Ljc5MzggNDMuODcxMSAzNi44NDg3QzQzLjY0MjcgMzUuOTAzNiA0My4xMzk2IDM1LjA2ODUgNDIuNDM5OCAzNC40NzMxQzQxLjc0IDMzLjg3NzYgNDAuODgyNyAzMy41NTUxIDQwLjAwMDkgMzMuNTU1NloiIGZpbGw9InVybCgjcGFpbnQxX2xpbmVhcl8xXzkxKSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzFfOTEiIHgxPSI0MCIgeTE9IjAiIHgyPSI0MCIgeTI9IjgwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiM4MzU2QkQiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjN0MyMEY0Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQxX2xpbmVhcl8xXzkxIiB4MT0iNDAiIHkxPSIxOCIgeDI9IjQwIiB5Mj0iNjIuNDQ0NSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSJ3aGl0ZSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNFMUQ4RkIiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K";
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIwIDEzLjAwMDRDMjAgMTguMDAwNCAxNi41IDIwLjUwMDUgMTIuMzQgMjEuOTUwNUMxMi4xMjIyIDIyLjAyNDMgMTEuODg1NSAyMi4wMjA3IDExLjY3IDIxLjk0MDVDNy41IDIwLjUwMDUgNCAxOC4wMDA0IDQgMTMuMDAwNFY2LjAwMDQ1QzQgNS43MzUyMyA0LjEwNTM2IDUuNDgwODggNC4yOTI4OSA1LjI5MzM0QzQuNDgwNDMgNS4xMDU4MSA0LjczNDc4IDUuMDAwNDUgNSA1LjAwMDQ1QzcgNS4wMDA0NSA5LjUgMy44MDA0NSAxMS4yNCAyLjI4MDQ1QzExLjQ1MTkgMi4wOTk0NSAxMS43MjE0IDIgMTIgMkMxMi4yNzg2IDIgMTIuNTQ4MSAyLjA5OTQ1IDEyLjc2IDIuMjgwNDVDMTQuNTEgMy44MTA0NSAxNyA1LjAwMDQ1IDE5IDUuMDAwNDVDMTkuMjY1MiA1LjAwMDQ1IDE5LjUxOTYgNS4xMDU4MSAxOS43MDcxIDUuMjkzMzRDMTkuODk0NiA1LjQ4MDg4IDIwIDUuNzM1MjMgMjAgNi4wMDA0NVYxMy4wMDA0WiIgc3Ryb2tlPSIjMzM4NUZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNOSAxMkwxMSAxNEwxNSAxMCIgc3Ryb2tlPSIjMzM4NUZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L3N2Zz4K";
export default image;
@@ -93,7 +93,7 @@ export type OauthOption = {
};
export type Profile = {
type: AuthOption;
type: AuthOption | "wallet";
details: {
id?: string;
email?: string;