@miden-sdk/miden-wallet-adapter-reactui 0.13.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.
Files changed (76) hide show
  1. package/dist/Button.d.ts +15 -0
  2. package/dist/Button.js +5 -0
  3. package/dist/Button.js.map +1 -0
  4. package/dist/Collapse.d.ts +6 -0
  5. package/dist/Collapse.js +61 -0
  6. package/dist/Collapse.js.map +1 -0
  7. package/dist/DiscoverMidenMessage.d.ts +1 -0
  8. package/dist/DiscoverMidenMessage.js +6 -0
  9. package/dist/DiscoverMidenMessage.js.map +1 -0
  10. package/dist/MidenSVG.d.ts +2 -0
  11. package/dist/MidenSVG.js +5 -0
  12. package/dist/MidenSVG.js.map +1 -0
  13. package/dist/WalletConnectButton.d.ts +3 -0
  14. package/dist/WalletConnectButton.js +29 -0
  15. package/dist/WalletConnectButton.js.map +1 -0
  16. package/dist/WalletDisconnectButton.d.ts +3 -0
  17. package/dist/WalletDisconnectButton.js +26 -0
  18. package/dist/WalletDisconnectButton.js.map +1 -0
  19. package/dist/WalletIcon.d.ts +6 -0
  20. package/dist/WalletIcon.js +5 -0
  21. package/dist/WalletIcon.js.map +1 -0
  22. package/dist/WalletListItem.d.ts +8 -0
  23. package/dist/WalletListItem.js +8 -0
  24. package/dist/WalletListItem.js.map +1 -0
  25. package/dist/WalletModal.d.ts +10 -0
  26. package/dist/WalletModal.js +115 -0
  27. package/dist/WalletModal.js.map +1 -0
  28. package/dist/WalletModalButton.d.ts +3 -0
  29. package/dist/WalletModalButton.js +15 -0
  30. package/dist/WalletModalButton.js.map +1 -0
  31. package/dist/WalletModalProvider.d.ts +6 -0
  32. package/dist/WalletModalProvider.js +12 -0
  33. package/dist/WalletModalProvider.js.map +1 -0
  34. package/dist/WalletMultiButton.d.ts +3 -0
  35. package/dist/WalletMultiButton.js +70 -0
  36. package/dist/WalletMultiButton.js.map +1 -0
  37. package/dist/index.d.ts +8 -0
  38. package/dist/index.js +9 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/styles.css +359 -0
  41. package/dist/useWalletModal.d.ts +6 -0
  42. package/dist/useWalletModal.js +26 -0
  43. package/dist/useWalletModal.js.map +1 -0
  44. package/docs/README.md +27 -0
  45. package/docs/functions/useWalletModal.md +13 -0
  46. package/docs/interfaces/WalletIconProps.md +3222 -0
  47. package/docs/interfaces/WalletModalContextState.md +29 -0
  48. package/docs/interfaces/WalletModalProps.md +41 -0
  49. package/docs/interfaces/WalletModalProviderProps.md +67 -0
  50. package/docs/variables/WalletConnectButton.md +9 -0
  51. package/docs/variables/WalletDisconnectButton.md +9 -0
  52. package/docs/variables/WalletIcon.md +9 -0
  53. package/docs/variables/WalletModal.md +9 -0
  54. package/docs/variables/WalletModalButton.md +9 -0
  55. package/docs/variables/WalletModalContext.md +9 -0
  56. package/docs/variables/WalletModalProvider.md +9 -0
  57. package/docs/variables/WalletMultiButton.md +9 -0
  58. package/package.json +35 -0
  59. package/src/Button.tsx +46 -0
  60. package/src/Collapse.tsx +83 -0
  61. package/src/DiscoverMidenMessage.tsx +17 -0
  62. package/src/MidenSVG.tsx +21 -0
  63. package/src/WalletConnectButton.tsx +53 -0
  64. package/src/WalletDisconnectButton.tsx +43 -0
  65. package/src/WalletIcon.tsx +22 -0
  66. package/src/WalletListItem.tsx +46 -0
  67. package/src/WalletModal.tsx +281 -0
  68. package/src/WalletModalButton.tsx +23 -0
  69. package/src/WalletModalProvider.tsx +25 -0
  70. package/src/WalletMultiButton.tsx +130 -0
  71. package/src/aleo.svg +250 -0
  72. package/src/index.ts +8 -0
  73. package/src/useWalletModal.tsx +36 -0
  74. package/styles.css +359 -0
  75. package/tsconfig.json +32 -0
  76. package/yarn-error.log +525 -0
@@ -0,0 +1,281 @@
1
+ import type { FC, MouseEvent } from 'react';
2
+ import {
3
+ useCallback,
4
+ useLayoutEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
9
+ import { createPortal } from 'react-dom';
10
+ import {
11
+ AllowedPrivateData,
12
+ PrivateDataPermission,
13
+ WalletAdapterNetwork,
14
+ WalletName,
15
+ WalletReadyState,
16
+ } from '@miden-sdk/miden-wallet-adapter-base';
17
+ import { useWallet, Wallet } from '@miden-sdk/miden-wallet-adapter-react';
18
+ import { useWalletModal } from './useWalletModal';
19
+ import { WalletListItem } from './WalletListItem';
20
+ import { DiscoverMidenMessage } from './DiscoverMidenMessage';
21
+
22
+ export interface WalletModalProps {
23
+ className?: string;
24
+ container?: string;
25
+ privateDataPermission?: PrivateDataPermission;
26
+ network?: WalletAdapterNetwork;
27
+ allowedPrivateData?: AllowedPrivateData;
28
+ }
29
+
30
+ export const WalletModal: FC<WalletModalProps> = ({
31
+ className = '',
32
+ container = 'body',
33
+ privateDataPermission,
34
+ network,
35
+ allowedPrivateData,
36
+ }) => {
37
+ const ref = useRef<HTMLDivElement>(null);
38
+ const { wallets, select, connect, wallet } = useWallet();
39
+ const { setVisible } = useWalletModal();
40
+ const [fadeIn, setFadeIn] = useState(false);
41
+ const [portal, setPortal] = useState<Element | null>(null);
42
+
43
+ const [installedWallets, otherWallets] = useMemo(() => {
44
+ const installed: Wallet[] = [];
45
+ const notDetected: Wallet[] = [];
46
+ const loadable: Wallet[] = [];
47
+
48
+ for (const wallet of wallets) {
49
+ if (wallet.readyState === WalletReadyState.NotDetected) {
50
+ notDetected.push(wallet);
51
+ } else if (wallet.readyState === WalletReadyState.Loadable) {
52
+ loadable.push(wallet);
53
+ } else if (wallet.readyState === WalletReadyState.Installed) {
54
+ installed.push(wallet);
55
+ }
56
+ }
57
+
58
+ return [installed, [...loadable, ...notDetected]];
59
+ }, [wallets]);
60
+
61
+ const getStartedWallet = useMemo(() => {
62
+ return installedWallets.length
63
+ ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
64
+ installedWallets[0]!
65
+ : wallets.find(
66
+ (wallet: { adapter: { name: WalletName } }) =>
67
+ wallet.adapter.name === 'Miden Wallet'
68
+ ) ||
69
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
70
+ otherWallets[0]!;
71
+ }, [installedWallets, wallets, otherWallets]);
72
+
73
+ const otherInstalledWallets = useMemo(() => {
74
+ return installedWallets.filter(
75
+ (wallet) => wallet.adapter.name !== getStartedWallet.adapter.name
76
+ );
77
+ }, [installedWallets, getStartedWallet]);
78
+
79
+ const hideModal = useCallback(() => {
80
+ setFadeIn(false);
81
+ setTimeout(() => setVisible(false), 150);
82
+ }, [setVisible]);
83
+
84
+ const handleClose = useCallback(
85
+ (event: MouseEvent) => {
86
+ event.preventDefault();
87
+ hideModal();
88
+ },
89
+ [hideModal]
90
+ );
91
+
92
+ const handleWalletClick = useCallback(
93
+ (event: MouseEvent, walletName: WalletName) => {
94
+ select(walletName);
95
+ handleClose(event);
96
+ },
97
+ [select, handleClose]
98
+ );
99
+
100
+ const handleTabKey = useCallback(
101
+ (event: KeyboardEvent) => {
102
+ const node = ref.current;
103
+ if (!node) return;
104
+
105
+ // here we query all focusable elements
106
+ const focusableElements = node.querySelectorAll('button');
107
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
108
+ const firstElement = focusableElements[0]!;
109
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
110
+ const lastElement = focusableElements[focusableElements.length - 1]!;
111
+
112
+ if (event.shiftKey) {
113
+ // if going backward by pressing tab and firstElement is active, shift focus to last focusable element
114
+ if (document.activeElement === firstElement) {
115
+ lastElement.focus();
116
+ event.preventDefault();
117
+ }
118
+ } else {
119
+ // if going forward by pressing tab and lastElement is active, shift focus to first focusable element
120
+ if (document.activeElement === lastElement) {
121
+ firstElement.focus();
122
+ event.preventDefault();
123
+ }
124
+ }
125
+ },
126
+ [ref]
127
+ );
128
+
129
+ useLayoutEffect(() => {
130
+ const handleKeyDown = (event: KeyboardEvent) => {
131
+ if (event.key === 'Escape') {
132
+ hideModal();
133
+ } else if (event.key === 'Tab') {
134
+ handleTabKey(event);
135
+ }
136
+ };
137
+
138
+ // Get original overflow
139
+ const { overflow } = window.getComputedStyle(document.body);
140
+ // Hack to enable fade in animation after mount
141
+ setTimeout(() => setFadeIn(true), 0);
142
+ // Prevent scrolling on mount
143
+ document.body.style.overflow = 'hidden';
144
+ // Listen for keydown events
145
+ window.addEventListener('keydown', handleKeyDown, false);
146
+
147
+ return () => {
148
+ // Re-enable scrolling when component unmounts
149
+ document.body.style.overflow = overflow;
150
+ window.removeEventListener('keydown', handleKeyDown, false);
151
+ };
152
+ }, [hideModal, handleTabKey]);
153
+
154
+ useLayoutEffect(
155
+ () => setPortal(document.querySelector(container)),
156
+ [container]
157
+ );
158
+
159
+ useLayoutEffect(() => {
160
+ if (wallet) {
161
+ connect(
162
+ privateDataPermission || PrivateDataPermission.UponRequest,
163
+ network || WalletAdapterNetwork.Testnet,
164
+ allowedPrivateData ?? AllowedPrivateData.None
165
+ ).catch((e) => {
166
+ console.log({ e });
167
+ });
168
+ }
169
+ }, [wallet, privateDataPermission, network, allowedPrivateData, connect]);
170
+
171
+ return (
172
+ portal &&
173
+ createPortal(
174
+ <div
175
+ aria-labelledby="wallet-adapter-modal-title"
176
+ aria-modal="true"
177
+ className={`wallet-adapter-modal ${
178
+ fadeIn && 'wallet-adapter-modal-fade-in'
179
+ } ${className}`}
180
+ ref={ref}
181
+ role="dialog"
182
+ >
183
+ <div className="wallet-adapter-modal-container">
184
+ <div className="wallet-adapter-modal-wrapper">
185
+ <div className="wallet-adapter-modal-title">
186
+ Connect a Wallet
187
+ <button
188
+ onClick={handleClose}
189
+ className="wallet-adapter-modal-button-close"
190
+ >
191
+ <svg width="14" height="14">
192
+ <path d="M14 12.461 8.3 6.772l5.234-5.233L12.006 0 6.772 5.234 1.54 0 0 1.539l5.234 5.233L0 12.006l1.539 1.528L6.772 8.3l5.69 5.7L14 12.461z" />
193
+ </svg>
194
+ </button>
195
+ </div>
196
+ {installedWallets.length ? (
197
+ <>
198
+ <div className="wallet-adapter-modal-content">
199
+ Connect your Miden Wallet and start exploring its powerful
200
+ features now!
201
+ <hr />
202
+ </div>
203
+ <ul className="wallet-adapter-modal-list">
204
+ <span className="wallet-adapter-modal-list-section-title">
205
+ Recommended
206
+ </span>
207
+ <WalletListItem
208
+ key={getStartedWallet.adapter.name}
209
+ handleClick={(event) =>
210
+ handleWalletClick(event, getStartedWallet.adapter.name)
211
+ }
212
+ wallet={getStartedWallet}
213
+ />
214
+ {(otherInstalledWallets.length > 0 ||
215
+ otherWallets.length > 0) && (
216
+ <>
217
+ <hr />
218
+ <span className="wallet-adapter-modal-list-section-title">
219
+ Other wallets
220
+ </span>
221
+ {otherInstalledWallets.map((wallet) => (
222
+ <WalletListItem
223
+ key={wallet.adapter.name}
224
+ handleClick={(event) =>
225
+ handleWalletClick(event, wallet.adapter.name)
226
+ }
227
+ wallet={wallet}
228
+ />
229
+ ))}
230
+ {otherWallets.map((wallet) => (
231
+ <WalletListItem
232
+ key={wallet.adapter.name}
233
+ handleClick={(event) =>
234
+ handleWalletClick(event, wallet.adapter.name)
235
+ }
236
+ wallet={wallet}
237
+ />
238
+ ))}
239
+ </>
240
+ )}
241
+ </ul>
242
+ </>
243
+ ) : (
244
+ <>
245
+ <div className="wallet-adapter-modal-middle">
246
+ <DiscoverMidenMessage />
247
+ <button
248
+ type="button"
249
+ className="wallet-adapter-modal-middle-button"
250
+ onClick={(event) =>
251
+ handleWalletClick(event, getStartedWallet.adapter.name)
252
+ }
253
+ >
254
+ Install Miden Wallet
255
+ <svg
256
+ xmlns="http://www.w3.org/2000/svg"
257
+ width="25"
258
+ height="24"
259
+ viewBox="0 0 25 24"
260
+ fill="none"
261
+ >
262
+ <path
263
+ d="M10.1003 5L8.5 6.5745L13.6981 11.7L8.5 16.8255L10.1003 18.4L16.91 11.7L10.1003 5Z"
264
+ fill="white"
265
+ />
266
+ </svg>
267
+ </button>
268
+ </div>
269
+ </>
270
+ )}
271
+ </div>
272
+ </div>
273
+ <div
274
+ className="wallet-adapter-modal-overlay"
275
+ onMouseDown={handleClose}
276
+ />
277
+ </div>,
278
+ portal
279
+ )
280
+ );
281
+ };
@@ -0,0 +1,23 @@
1
+ import type { FC, MouseEvent } from 'react';
2
+ import { useCallback } from 'react';
3
+ import type { ButtonProps } from './Button';
4
+ import { Button } from './Button';
5
+ import { useWalletModal } from './useWalletModal';
6
+
7
+ export const WalletModalButton: FC<ButtonProps> = ({ children = 'Select Wallet', onClick, ...props }) => {
8
+ const { visible, setVisible } = useWalletModal();
9
+
10
+ const handleClick = useCallback(
11
+ (event: MouseEvent<HTMLButtonElement>) => {
12
+ if (onClick) onClick(event);
13
+ if (!event.defaultPrevented) setVisible(!visible);
14
+ },
15
+ [onClick, setVisible, visible]
16
+ );
17
+
18
+ return (
19
+ <Button className="wallet-adapter-button-trigger" onClick={handleClick} {...props}>
20
+ {children}
21
+ </Button>
22
+ );
23
+ };
@@ -0,0 +1,25 @@
1
+ import type { FC, ReactNode } from 'react';
2
+ import React, { useState } from 'react';
3
+ import { WalletModalContext } from './useWalletModal';
4
+ import type { WalletModalProps } from './WalletModal';
5
+ import { WalletModal } from './WalletModal';
6
+
7
+ export interface WalletModalProviderProps extends WalletModalProps {
8
+ children: ReactNode;
9
+ }
10
+
11
+ export const WalletModalProvider: FC<WalletModalProviderProps> = ({ children, ...props }) => {
12
+ const [visible, setVisible] = useState(false);
13
+
14
+ return (
15
+ <WalletModalContext.Provider
16
+ value={{
17
+ visible,
18
+ setVisible,
19
+ }}
20
+ >
21
+ {children}
22
+ {visible && <WalletModal {...props} />}
23
+ </WalletModalContext.Provider>
24
+ );
25
+ };
@@ -0,0 +1,130 @@
1
+ import type { FC } from 'react';
2
+ import {
3
+ useCallback,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
9
+ import { useWallet } from '@miden-sdk/miden-wallet-adapter-react';
10
+ import type { ButtonProps } from './Button';
11
+ import { Button } from './Button';
12
+ import { useWalletModal } from './useWalletModal';
13
+ import { WalletConnectButton } from './WalletConnectButton';
14
+ import { WalletIcon } from './WalletIcon';
15
+ import { WalletModalButton } from './WalletModalButton';
16
+
17
+ export const WalletMultiButton: FC<ButtonProps> = ({ children, ...props }) => {
18
+ const { address, wallet, disconnect } = useWallet();
19
+ const { setVisible } = useWalletModal();
20
+ const [copied, setCopied] = useState(false);
21
+ const [active, setActive] = useState(false);
22
+ const ref = useRef<HTMLUListElement>(null);
23
+
24
+ const content = useMemo(() => {
25
+ if (children) return children;
26
+ if (!wallet || !address) return null;
27
+
28
+ const underscoreIndex = address.indexOf('_');
29
+ const frontPart = address.slice(0, 6);
30
+ const middlePart = address.slice(underscoreIndex - 4, underscoreIndex);
31
+ return `${frontPart}...${middlePart}`;
32
+ }, [children, wallet, address]);
33
+
34
+ const copyAddress = useCallback(async () => {
35
+ if (address) {
36
+ await navigator.clipboard.writeText(address);
37
+ setCopied(true);
38
+ setTimeout(() => setCopied(false), 400);
39
+ }
40
+ }, [address]);
41
+
42
+ const openDropdown = useCallback(() => {
43
+ setActive(true);
44
+ }, []);
45
+
46
+ const closeDropdown = useCallback(() => {
47
+ setActive(false);
48
+ }, []);
49
+
50
+ const openModal = useCallback(() => {
51
+ setVisible(true);
52
+ closeDropdown();
53
+ }, [setVisible, closeDropdown]);
54
+
55
+ useEffect(() => {
56
+ const listener = (event: MouseEvent | TouchEvent) => {
57
+ const node = ref.current;
58
+
59
+ // Do nothing if clicking dropdown or its descendants
60
+ if (!node || node.contains(event.target as Node)) return;
61
+
62
+ closeDropdown();
63
+ };
64
+
65
+ document.addEventListener('mousedown', listener);
66
+ document.addEventListener('touchstart', listener);
67
+
68
+ return () => {
69
+ document.removeEventListener('mousedown', listener);
70
+ document.removeEventListener('touchstart', listener);
71
+ };
72
+ }, [ref, closeDropdown]);
73
+
74
+ if (!wallet)
75
+ return <WalletModalButton {...props}>{children}</WalletModalButton>;
76
+ if (!address)
77
+ return <WalletConnectButton {...props}>{children}</WalletConnectButton>;
78
+
79
+ return (
80
+ <div className="wallet-adapter-dropdown">
81
+ <Button
82
+ aria-expanded={active}
83
+ className="wallet-adapter-button-trigger"
84
+ style={{
85
+ pointerEvents: active ? 'none' : 'auto',
86
+ width: '100%',
87
+ backgroundColor: '#FFFFFF',
88
+ border: '1px solid #D7D7D7',
89
+ color: 'black',
90
+ ...props.style
91
+ }}
92
+ onClick={openDropdown}
93
+ startIcon={<WalletIcon wallet={wallet} />}
94
+ {...props}
95
+ >
96
+ {content}
97
+ </Button>
98
+ <ul
99
+ aria-label="dropdown-list"
100
+ className={`wallet-adapter-dropdown-list ${
101
+ active && 'wallet-adapter-dropdown-list-active'
102
+ }`}
103
+ ref={ref}
104
+ role="menu"
105
+ >
106
+ <li
107
+ onClick={copyAddress}
108
+ className="wallet-adapter-dropdown-list-item"
109
+ role="menuitem"
110
+ >
111
+ {copied ? 'Copied' : 'Copy address'}
112
+ </li>
113
+ <li
114
+ onClick={openModal}
115
+ className="wallet-adapter-dropdown-list-item"
116
+ role="menuitem"
117
+ >
118
+ Change wallet
119
+ </li>
120
+ <li
121
+ onClick={disconnect}
122
+ className="wallet-adapter-dropdown-list-item"
123
+ role="menuitem"
124
+ >
125
+ Disconnect
126
+ </li>
127
+ </ul>
128
+ </div>
129
+ );
130
+ };