diff --git a/.changeset/little-cougars-smile.md b/.changeset/little-cougars-smile.md
new file mode 100644
index 000000000..27e701a7e
--- /dev/null
+++ b/.changeset/little-cougars-smile.md
@@ -0,0 +1,6 @@
+---
+'@ant-design/web3-common': minor
+'@ant-design/web3': minor
+---
+
+feat: optimize connect-modal component
diff --git a/packages/common/src/locale/en_US.ts b/packages/common/src/locale/en_US.ts
index 938f0222f..643ca9c30 100644
--- a/packages/common/src/locale/en_US.ts
+++ b/packages/common/src/locale/en_US.ts
@@ -35,6 +35,7 @@ const localeValues: RequiredLocale = {
walletCardAppTitle: '{selectedWalletName} for Mobile',
walletCardAppDesc: 'Use the mobile wallet to explore the world of Ethereum.',
walletCardExtensionTitle: '{selectedWalletName} for {selectedExtensionBrowserName}',
+ walletPanelPlugin: 'PLUGIN',
},
NFTCard: {
actionText: 'Buy Now',
diff --git a/packages/common/src/locale/zh_CN.ts b/packages/common/src/locale/zh_CN.ts
index e5d8d341e..1cef64d7e 100644
--- a/packages/common/src/locale/zh_CN.ts
+++ b/packages/common/src/locale/zh_CN.ts
@@ -33,6 +33,7 @@ const localeValues: RequiredLocale = {
walletCardAppTitle: '在手机使用 {selectedWalletName}',
walletCardAppDesc: '使用移动钱包探索以太坊世界。',
walletCardExtensionTitle: '在 {selectedExtensionBrowserName} 浏览器中使用 {selectedWalletName}',
+ walletPanelPlugin: '插件',
},
NFTCard: {
actionText: '立即购买',
diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts
index 1820cf87e..e044a84a1 100644
--- a/packages/common/src/types.ts
+++ b/packages/common/src/types.ts
@@ -211,6 +211,7 @@ export interface RequiredLocale {
walletCardAppTitle: string;
walletCardAppDesc: string;
walletCardExtensionTitle: string;
+ walletPanelPlugin: string;
};
NFTCard: {
actionText: string;
diff --git a/packages/web3/src/connect-modal/__tests__/__snapshots__/basic.test.tsx.snap b/packages/web3/src/connect-modal/__tests__/__snapshots__/basic.test.tsx.snap
index 67f55b87f..9d47665a1 100644
--- a/packages/web3/src/connect-modal/__tests__/__snapshots__/basic.test.tsx.snap
+++ b/packages/web3/src/connect-modal/__tests__/__snapshots__/basic.test.tsx.snap
@@ -18,7 +18,7 @@ exports[`ConnectModal with guide > panel route change 1`] = `
aria-modal="true"
class="ant-modal css-dev-only-do-not-override-1qhpsh8 ant-web3-connect-modal css-dev-only-do-not-override-1qhpsh8 ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
- style="width: 737px;"
+ style="width: 797px;"
>
panel route change 1`] = `
Test Wallet
+
panel route change 1`] = `
Test Wallet3
+
@@ -187,6 +215,20 @@ exports[`ConnectModal with guide > panel route change 1`] = `
Test Wallet2
+
panel route change 1`] = `
Test Wallet4
+
panel route change 1`] = `
Test Wallet5
+
panel route change 1`] = `
Test Wallet6
+
panel route change 1`] = `
Test Wallet6
+
@@ -418,7 +516,7 @@ exports[`ConnectModal with guide > panel route change 2`] = `
aria-modal="true"
class="ant-modal css-dev-only-do-not-override-1qhpsh8 ant-web3-connect-modal css-dev-only-do-not-override-1qhpsh8 ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
- style="width: 737px;"
+ style="width: 797px;"
>
panel route change 2`] = `
Test Wallet
+
panel route change 2`] = `
Test Wallet3
+
@@ -587,6 +713,20 @@ exports[`ConnectModal with guide > panel route change 2`] = `
Test Wallet2
+
panel route change 2`] = `
Test Wallet4
+
panel route change 2`] = `
Test Wallet5
+
panel route change 2`] = `
Test Wallet6
+
panel route change 2`] = `
Test Wallet6
+
@@ -818,7 +1014,7 @@ exports[`ConnectModal with guide > should display default guide 1`] = `
aria-modal="true"
class="ant-modal css-dev-only-do-not-override-1qhpsh8 ant-web3-connect-modal css-dev-only-do-not-override-1qhpsh8 ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
- style="width: 737px; transform-origin: 0px 0px;"
+ style="width: 797px; transform-origin: 0px 0px;"
>
should display default guide 1`] = `
Test Wallet
+
should display default guide 1`] = `
Test Wallet3
+
@@ -987,6 +1211,20 @@ exports[`ConnectModal with guide > should display default guide 1`] = `
Test Wallet2
+
should display default guide 1`] = `
Test Wallet4
+
should display default guide 1`] = `
Test Wallet5
+
should display default guide 1`] = `
Test Wallet6
+
should display default guide 1`] = `
Test Wallet6
+
@@ -1213,7 +1507,7 @@ exports[`ConnectModal with guide > should render in dark mode 1`] = `
aria-modal="true"
class="ant-modal css-dev-only-do-not-override-1hnpuv1 ant-web3-connect-modal css-dev-only-do-not-override-1hnpuv1 ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
- style="width: 737px;"
+ style="width: 797px;"
>
should render in dark mode 1`] = `
Test Wallet
+
should render in dark mode 1`] = `
Test Wallet3
+
@@ -1382,6 +1704,20 @@ exports[`ConnectModal with guide > should render in dark mode 1`] = `
Test Wallet2
+
should render in dark mode 1`] = `
Test Wallet4
+
should render in dark mode 1`] = `
Test Wallet5
+
should render in dark mode 1`] = `
Test Wallet6
+
should render in dark mode 1`] = `
Test Wallet6
+
@@ -1633,7 +2025,7 @@ exports[`ConnectModal with guide > should render in light mode 1`] = `
aria-modal="true"
class="ant-modal css-dev-only-do-not-override-1qhpsh8 ant-web3-connect-modal css-dev-only-do-not-override-1qhpsh8 ant-zoom-appear ant-zoom-appear-prepare ant-zoom"
role="dialog"
- style="width: 737px;"
+ style="width: 797px;"
>
should render in light mode 1`] = `
Test Wallet
+
should render in light mode 1`] = `
Test Wallet3
+
@@ -1802,6 +2222,20 @@ exports[`ConnectModal with guide > should render in light mode 1`] = `
Test Wallet2
+
should render in light mode 1`] = `
Test Wallet4
+
should render in light mode 1`] = `
Test Wallet5
+
should render in light mode 1`] = `
Test Wallet6
+
should render in light mode 1`] = `
Test Wallet6
+
diff --git a/packages/web3/src/connect-modal/__tests__/__snapshots__/simple.test.tsx.snap b/packages/web3/src/connect-modal/__tests__/__snapshots__/simple.test.tsx.snap
index 293d8f924..236f97b0f 100644
--- a/packages/web3/src/connect-modal/__tests__/__snapshots__/simple.test.tsx.snap
+++ b/packages/web3/src/connect-modal/__tests__/__snapshots__/simple.test.tsx.snap
@@ -122,6 +122,20 @@ exports[`ConnectModal without guide > should render in dark mode 1`] = `
Test Wallet
+
should render in dark mode 1`] = `
Test Wallet3
+
@@ -187,6 +215,20 @@ exports[`ConnectModal without guide > should render in dark mode 1`] = `
Test Wallet2
+
should render in dark mode 1`] = `
Test Wallet4
+
should render in dark mode 1`] = `
Test Wallet5
+
should render in dark mode 1`] = `
Test Wallet6
+
should render in dark mode 1`] = `
Test Wallet6
+
@@ -407,6 +505,20 @@ exports[`ConnectModal without guide > should render in light mode 1`] = `
Test Wallet
+
should render in light mode 1`] = `
Test Wallet3
+
@@ -472,6 +598,20 @@ exports[`ConnectModal without guide > should render in light mode 1`] = `
Test Wallet2
+
should render in light mode 1`] = `
Test Wallet4
+
should render in light mode 1`] = `
Test Wallet5
+
should render in light mode 1`] = `
Test Wallet6
+
should render in light mode 1`] = `
Test Wallet6
+
diff --git a/packages/web3/src/connect-modal/__tests__/pluginTag.test.tsx b/packages/web3/src/connect-modal/__tests__/pluginTag.test.tsx
new file mode 100644
index 000000000..aaf82384b
--- /dev/null
+++ b/packages/web3/src/connect-modal/__tests__/pluginTag.test.tsx
@@ -0,0 +1,138 @@
+import { ConnectModal } from '@ant-design/web3';
+import { metadata_MetaMask } from '@ant-design/web3-assets';
+import { Wallet } from '@ant-design/web3-common';
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+
+describe('ConnectModal with qrCode & plugin tag', () => {
+ it('show qrCode', async () => {
+ const wallet: Wallet = {
+ ...metadata_MetaMask,
+ getQrCode: async () => {
+ return {
+ uri: '',
+ };
+ },
+ };
+
+ const App = () => (
+
+ );
+ const { baseElement } = render( );
+ expect(baseElement.querySelector('.anticon-qrcode')).toBeTruthy();
+ });
+
+ it('do not show qrCode', async () => {
+ const wallet: Wallet = {
+ ...metadata_MetaMask,
+ };
+
+ const App = () => (
+
+ );
+ const { baseElement } = render( );
+ expect(baseElement.querySelector('.anticon-qrcode')).not.toBeTruthy();
+ });
+
+ it('not show plugin tag', async () => {
+ const wallet: Wallet = {
+ ...metadata_MetaMask,
+ };
+
+ const App = () => (
+
+ );
+ render( );
+ const span = screen.queryByText('PLUGIN');
+ expect(!!span?.parentElement === false).toBeTruthy();
+ });
+
+ it('show plugin tag when installed', async () => {
+ const wallet: Wallet = {
+ ...metadata_MetaMask,
+ hasExtensionInstalled: async () => {
+ return true;
+ },
+ };
+
+ const App = () => (
+
+ );
+ render( );
+ const span = screen.queryByText('PLUGIN');
+ expect(!!span?.parentElement?.getAttribute('disabled') === false).toBeTruthy();
+ });
+
+ it('show plugin tag when not installed', async () => {
+ const wallet: Wallet = {
+ ...metadata_MetaMask,
+ hasExtensionInstalled: async () => {
+ return false;
+ },
+ };
+
+ const App = () => (
+
+ );
+ render( );
+ const span = screen.queryByText('PLUGIN');
+ expect(span?.parentElement?.getAttribute('disabled') === '').toBeTruthy();
+ });
+
+ it('click event trigger', async () => {
+ const wallet: Wallet = {
+ ...metadata_MetaMask,
+ getQrCode: async () => {
+ return {
+ uri: '',
+ };
+ },
+ };
+
+ const App = () => (
+
+ );
+ const { baseElement } = render( );
+ const icon = baseElement.querySelector('.anticon-qrcode');
+ fireEvent.click(icon!);
+ await waitFor(() => {
+ expect(baseElement.querySelector('.anticon-qrcode')).toBeTruthy();
+ });
+ });
+});
diff --git a/packages/web3/src/connect-modal/components/PluginTag.tsx b/packages/web3/src/connect-modal/components/PluginTag.tsx
new file mode 100644
index 000000000..efed9ca2e
--- /dev/null
+++ b/packages/web3/src/connect-modal/components/PluginTag.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import { Badge, Button } from 'antd';
+
+import { connectModalContext } from '../context';
+import type { Wallet } from '../interface';
+
+const PluginTag: React.FC<{ wallet: Wallet }> = ({ wallet }) => {
+ const [extensionInstalled, setExtensionInstalled] = React.useState(false);
+ const { getMessage, localeMessage } = React.useContext(connectModalContext);
+
+ const judgeExtensionInstalled = async () => {
+ const hasWalletReady = await wallet.hasWalletReady?.();
+ if (hasWalletReady) {
+ const hasInstalled = await wallet.hasExtensionInstalled?.();
+ setExtensionInstalled(!!hasInstalled);
+ }
+ };
+
+ React.useEffect(() => {
+ judgeExtensionInstalled();
+ }, [wallet]);
+
+ return wallet.hasExtensionInstalled ? (
+
+
+ {getMessage(localeMessage.walletPanelPlugin)}
+
+
+ ) : null;
+};
+
+export default PluginTag;
diff --git a/packages/web3/src/connect-modal/components/WalletList.tsx b/packages/web3/src/connect-modal/components/WalletList.tsx
index c41e07f11..0be0a2fdd 100644
--- a/packages/web3/src/connect-modal/components/WalletList.tsx
+++ b/packages/web3/src/connect-modal/components/WalletList.tsx
@@ -1,10 +1,12 @@
import React, { useContext, useMemo } from 'react';
-import { List } from 'antd';
+import { QrcodeOutlined } from '@ant-design/icons';
+import { Button, List, Space } from 'antd';
import classNames from 'classnames';
import { connectModalContext } from '../context';
import type { ConnectModalProps, Wallet } from '../interface';
import { defaultGroupOrder } from '../utils';
+import PluginTag from './PluginTag';
export type WalletListProps = Pick;
@@ -75,6 +77,22 @@ const WalletList: React.FC = (props) => {
)}
{item.name}
+
+
+ {item.getQrCode ? (
+
+ {
+ e.stopPropagation();
+ updateSelectedWallet(item, false);
+ updatePanelRoute('qrCode', true);
+ }}
+ />
+
+ ) : (
+
+ )}
+
)}
/>
diff --git a/packages/web3/src/connect-modal/demos/panel.tsx b/packages/web3/src/connect-modal/demos/panel.tsx
index 427aea8f2..7ae43a4a2 100644
--- a/packages/web3/src/connect-modal/demos/panel.tsx
+++ b/packages/web3/src/connect-modal/demos/panel.tsx
@@ -55,7 +55,7 @@ const App: React.FC = () => {
return (
& {
return wrapSSR(
{
[`${componentCls}-list-panel`]: {
paddingInline: 18,
paddingBlock: 24,
- width: 268,
+ width: 328,
flexShrink: 0,
borderRight: `1px solid ${token.splitColor}`,
display: 'flex',
@@ -99,6 +99,12 @@ const getThemeStyle = (token: ConnectModalToken): CSSInterpolation => {
marginBlock: token.marginSM,
overflow: 'auto',
[`${componentCls}-wallet-list`]: {
+ maxHeight: 390,
+ overflow: 'scroll',
+ '&::-webkit-scrollbar': {
+ display: 'none',
+ },
+ scrollbarWidth: 'none',
[`${componentCls}-group`]: {
marginBlockEnd: token.marginSM,
[`${componentCls}-group-title`]: {
@@ -115,10 +121,6 @@ const getThemeStyle = (token: ConnectModalToken): CSSInterpolation => {
transition: 'background .3s, color .3s',
marginBlockEnd: 5,
border: 'none',
- [`${componentCls}-extra`]: {
- fontSize: token.fontSizeSM,
- color: token.colorTextDescription,
- },
[`${componentCls}-content`]: {
display: 'flex',
alignItems: 'center',
@@ -141,8 +143,12 @@ const getThemeStyle = (token: ConnectModalToken): CSSInterpolation => {
justifySelf: 'flex-start',
marginInlineStart: token.marginSM,
color: token.colorText,
+ wordBreak: 'break-word',
},
},
+ [`${componentCls}-qr-icon-empty`]: {
+ width: 30,
+ },
'&:last-child': {
marginBlockEnd: 0,
},
@@ -151,9 +157,6 @@ const getThemeStyle = (token: ConnectModalToken): CSSInterpolation => {
},
'&.selected': {
background: token.selectedBg,
- [`${componentCls}-name`]: {
- color: token.selectedColor,
- },
},
},
},
@@ -427,14 +430,16 @@ const genModalStyle: GenerateStyle = (token) => {
export function useStyle(prefixCls: string): UseStyleResult {
return useAntdStyle('ConnectModal', (token) => {
const isDark = isDarkTheme(token);
+ const hoverBg = new TinyColor(isDark ? token.colorWhite : '#000')
+ .setAlpha(0.08)
+ .onBackground(token.colorBgContainer)
+ .toRgbString();
+
const connectModalToken: ConnectModalToken = {
...token,
- selectedBg: isDark ? token.colorWhite : token.colorPrimary,
selectedColor: token.colorBgContainer,
- hoverBg: new TinyColor(isDark ? token.colorWhite : token.colorPrimary)
- .setAlpha(0.1)
- .onBackground(token.colorBgContainer)
- .toRgbString(),
+ hoverBg,
+ selectedBg: hoverBg,
splitColor: new TinyColor(token.colorText).setAlpha(0.06).toRgbString(),
modalTitleStartColor: isDark ? token.colorWhite : token.colorPrimary,
modalTitleEndColor: new TinyColor('#000')