Skip to content

Commit 3977b34

Browse files
committed
feat(ui): implement multiple buttons support
1 parent 43034b4 commit 3977b34

File tree

6 files changed

+59
-16
lines changed

6 files changed

+59
-16
lines changed

packages/ui-react/src/components/TonConnectButton.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { CSSProperties, FunctionComponent, memo, useEffect } from 'react';
1+
import { CSSProperties, FunctionComponent, memo, useEffect, useRef } from 'react';
22
import { useTonConnectUI } from '../hooks/useTonConnectUI';
3-
4-
const buttonRootId = 'ton-connect-button';
3+
import { addButtonId, removeButtonId, getButtonIds, DEFAULT_BUTTON_ID } from '../utils/tonconnect-button-ids';
54

65
export interface TonConnectButtonProps {
76
className?: string;
8-
97
style?: CSSProperties;
8+
9+
id?: string;
1010
}
1111

1212
/**
@@ -15,18 +15,28 @@ export interface TonConnectButtonProps {
1515
* @param [className] css class to add to the button container.
1616
* @param [style] style to add to the button container.
1717
* @constructor
18-
*/
19-
const TonConnectButton: FunctionComponent<TonConnectButtonProps> = ({ className, style }) => {
18+
*/const TonConnectButton: FunctionComponent<TonConnectButtonProps> = ({ className, style, id }) => {
2019
const [_, setOptions] = useTonConnectUI();
20+
const buttonId = useRef<string>(
21+
id ??
22+
(getButtonIds().length === 0
23+
? DEFAULT_BUTTON_ID
24+
: `ton-connect-button-${Math.random().toString(36).slice(2, 10)}`)
25+
);
2126

2227
useEffect(() => {
23-
setOptions({ buttonRootId });
24-
return () => setOptions({ buttonRootId: null });
28+
const newIds = addButtonId(buttonId.current);
29+
setOptions({ buttonRootId: newIds });
30+
31+
return () => {
32+
const newIds = removeButtonId(buttonId.current);
33+
setOptions({ buttonRootId: newIds.length > 0 ? newIds : null });
34+
};
2535
}, [setOptions]);
2636

2737
return (
2838
<div
29-
id={buttonRootId}
39+
id={buttonId.current}
3040
className={className}
3141
style={{ width: 'fit-content', ...style }}
3242
></div>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export const DEFAULT_BUTTON_ID = 'ton-connect-button';
2+
3+
let ids: string[] = [];
4+
5+
export function getButtonIds() {
6+
return [...ids];
7+
}
8+
9+
export function addButtonId(id: string) {
10+
if (!ids.includes(id)) {
11+
ids.push(id);
12+
}
13+
return getButtonIds();
14+
}
15+
16+
export function removeButtonId(id: string) {
17+
ids = ids.filter(existingId => existingId !== id);
18+
return getButtonIds();
19+
}

packages/ui/src/app/App.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@ const App: Component<AppProps> = props => {
3333
<ConnectorContext.Provider value={appState.connector}>
3434
<GlobalStyles />
3535
<ThemeProvider theme={themeState}>
36-
<Show when={appState.buttonRootId}>
37-
<Portal mount={document.getElementById(appState.buttonRootId!)!}>
38-
<AccountButton />
39-
</Portal>
36+
<Show when={Array.isArray(appState.buttonRootId) && appState.buttonRootId.length > 0}>
37+
<>
38+
{appState.buttonRootId && (appState.buttonRootId as string[]).map((id) => (
39+
<Portal mount={document.getElementById(id)!}>
40+
<AccountButton />
41+
</Portal>
42+
))}
43+
</>
4044
</Show>
4145
<Dynamic component={globalStylesTag}>
4246
<WalletsModal />

packages/ui/src/app/state/app.state.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Loadable } from 'src/models/loadable';
77

88
export type AppState = {
99
connector: ITonConnect;
10-
buttonRootId: string | null;
10+
buttonRootId: string | null | string[];
1111
language: Locales;
1212
walletsListConfiguration: WalletsListConfiguration | {};
1313
connectRequestParameters?: Loadable<ConnectAdditionalRequest> | null;

packages/ui/src/models/ton-connect-ui-options.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface TonConnectUiOptions {
1414
* HTML element id to attach the wallet connect button. If not passed button won't appear.
1515
* @default null.
1616
*/
17-
buttonRootId?: string | null;
17+
buttonRootId?: string | null | string[];
1818

1919
/**
2020
* Language for the phrases it the UI elements.

packages/ui/src/ton-connect-ui.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -993,11 +993,21 @@ export class TonConnectUI {
993993
return rootId;
994994
}
995995

996-
private checkButtonRootExist(buttonRootId: string | null | undefined): void | never {
996+
private checkButtonRootExist(buttonRootId: string | string[] | null | undefined): void | never {
997997
if (buttonRootId == null) {
998998
return;
999999
}
10001000

1001+
if (Array.isArray(buttonRootId)) {
1002+
for (const buttonId of buttonRootId) {
1003+
if (document.getElementById(buttonId)) {
1004+
return;
1005+
}
1006+
}
1007+
1008+
throw new TonConnectUIError(`${buttonRootId} element not found in the document.`);
1009+
}
1010+
10011011
if (!document.getElementById(buttonRootId)) {
10021012
throw new TonConnectUIError(`${buttonRootId} element not found in the document.`);
10031013
}

0 commit comments

Comments
 (0)