@monygroupcorp/micro-web3 1.2.4 → 1.3.1

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.
@@ -1,213 +1,221 @@
1
+ import { Component, h, render } from '@monygroupcorp/microact';
1
2
  import { FloatingWalletButton } from '../FloatingWalletButton/FloatingWalletButton.js';
2
3
  import WalletModal from '../Wallet/WalletModal.js';
3
4
 
4
5
  class WalletButtonModal extends WalletModal {
5
- events() {
6
- const baseEvents = typeof super.events === 'function' ? super.events() : {};
7
- return {
8
- ...baseEvents,
9
- 'click .wallet-option': async (e) => {
10
- const button = e?.target?.closest?.('.wallet-option');
11
- const walletType = button?.dataset?.wallet;
12
- if (!walletType) {
13
- return;
14
- }
15
- if (this.onWalletSelected) {
16
- await this.onWalletSelected(walletType);
6
+ handleWalletOptionClick(walletType) {
7
+ if (this.props.onWalletSelected) {
8
+ this.props.onWalletSelected(walletType);
17
9
  }
18
10
  this.hide();
19
- },
20
- };
21
- }
22
- }
11
+ }
23
12
 
24
- export class WalletButton extends FloatingWalletButton {
25
- constructor(props) {
26
- super(props);
27
- this.modalRoot = null;
28
- }
29
-
30
- async showWalletModal() {
31
- const providerMap =
32
- this.walletService?.providerMap ||
33
- {
34
- rabby: () => (window.ethereum?.isRabby ? window.ethereum : null),
35
- rainbow: () => (window.ethereum?.isRainbow ? window.ethereum : null),
36
- phantom: () => window.phantom?.ethereum || null,
37
- metamask: () => window.ethereum || null,
38
- };
39
-
40
- const walletIcons =
41
- this.walletService?.walletIcons ||
42
- {
43
- rabby: '/public/wallets/rabby.webp',
44
- rainbow: '/public/wallets/rainbow.webp',
45
- phantom: '/public/wallets/phantom.webp',
46
- metamask: '/public/wallets/MetaMask.webp',
47
- };
48
-
49
- if (!this.walletModal) {
50
- this.walletModal = new WalletButtonModal({
51
- providerMap,
52
- walletIcons,
53
- onWalletSelected: async (walletType) => {
54
- await this.handleWalletSelection(walletType);
55
- },
56
- });
13
+ render() {
14
+ if (!this.state.isVisible) {
15
+ return h('div', { className: 'wallet-modal-container', style: { display: 'none' } });
16
+ }
57
17
 
58
- if (!this.modalRoot) {
59
- this.modalRoot = document.createElement('div');
60
- document.body.appendChild(this.modalRoot);
61
- }
18
+ const walletOptions = Object.keys(this.props.providerMap);
19
+
20
+ return h('div', { className: 'wallet-modal-container' },
21
+ h('div', {
22
+ className: 'wallet-modal-overlay',
23
+ onClick: (e) => {
24
+ if (e.target === e.currentTarget) this.hide();
25
+ }
26
+ },
27
+ h('div', { className: 'wallet-modal' },
28
+ h('div', { className: 'wallet-modal-header' },
29
+ h('h3', null, 'Select Your Wallet'),
30
+ h('button', {
31
+ className: 'wallet-modal-close',
32
+ onClick: this.bind(this.hide)
33
+ }, '\u00D7')
34
+ ),
35
+ h('div', { className: 'wallet-options' },
36
+ walletOptions.map(walletType =>
37
+ h('button', {
38
+ key: walletType,
39
+ className: 'wallet-option',
40
+ onClick: () => this.handleWalletOptionClick(walletType)
41
+ },
42
+ h('img', {
43
+ src: this.props.walletIcons[walletType],
44
+ alt: walletType
45
+ }),
46
+ h('span', null, walletType.charAt(0).toUpperCase() + walletType.slice(1))
47
+ )
48
+ )
49
+ )
50
+ )
51
+ )
52
+ );
53
+ }
54
+ }
62
55
 
63
- this.walletModal.mount(this.modalRoot);
56
+ export class WalletButton extends FloatingWalletButton {
57
+ constructor(props) {
58
+ super(props);
59
+ this.modalRoot = null;
64
60
  }
65
61
 
66
- this.walletModal.show();
67
- }
62
+ showWalletModal() {
63
+ const providerMap =
64
+ this.walletService?.providerMap ||
65
+ {
66
+ rabby: () => (window.ethereum?.isRabby ? window.ethereum : null),
67
+ rainbow: () => (window.ethereum?.isRainbow ? window.ethereum : null),
68
+ phantom: () => window.phantom?.ethereum || null,
69
+ metamask: () => window.ethereum || null,
70
+ };
71
+
72
+ const walletIcons =
73
+ this.walletService?.walletIcons ||
74
+ {
75
+ rabby: '/public/wallets/rabby.webp',
76
+ rainbow: '/public/wallets/rainbow.webp',
77
+ phantom: '/public/wallets/phantom.webp',
78
+ metamask: '/public/wallets/MetaMask.webp',
79
+ };
80
+
81
+ if (!this.walletModal) {
82
+ if (!this.modalRoot) {
83
+ this.modalRoot = document.createElement('div');
84
+ document.body.appendChild(this.modalRoot);
85
+ }
86
+
87
+ render(h(WalletButtonModal, {
88
+ providerMap,
89
+ walletIcons,
90
+ onWalletSelected: async (walletType) => {
91
+ await this.handleWalletSelection(walletType);
92
+ },
93
+ ref: instance => this.walletModal = instance
94
+ }), this.modalRoot);
95
+ }
68
96
 
69
- onUnmount() {
70
- if (this.modalRoot?.parentNode) {
71
- this.modalRoot.parentNode.removeChild(this.modalRoot);
72
- this.modalRoot = null;
73
- }
74
- super.onUnmount();
75
- }
76
-
77
- render() {
78
- if (this.state.loading) {
79
- return `
80
- <div class="floating-wallet-button loading">
81
- <div class="wallet-spinner"></div>
82
- </div>
83
- `;
97
+ this.walletModal?.show();
84
98
  }
85
99
 
86
- const { walletConnected, address, balance, menuOpen } = this.state;
87
-
88
- if (!walletConnected) {
89
- return `
90
- <div class="floating-wallet-button disconnected" data-ref="wallet-button">
91
- <button class="wallet-btn" data-ref="connect-btn">
92
- <span class="wallet-mark"></span>
93
- <span class="wallet-text">Connect Wallet</span>
94
- </button>
95
- </div>
96
- `;
100
+ willUnmount() {
101
+ if (this.modalRoot?.parentNode) {
102
+ this.modalRoot.parentNode.removeChild(this.modalRoot);
103
+ this.modalRoot = null;
104
+ }
105
+ super.willUnmount();
97
106
  }
98
107
 
99
- const truncatedAddress = `${address.slice(0, 6)}...${address.slice(-4)}`;
100
- const classNames = ['floating-wallet-button', 'connected'];
101
- if (menuOpen) {
102
- classNames.push('menu-open');
103
- }
108
+ render() {
109
+ if (this.state.loading) {
110
+ return h('div', { className: 'floating-wallet-button loading' },
111
+ h('div', { className: 'wallet-spinner' })
112
+ );
113
+ }
104
114
 
105
- const markup = `
106
- <div class="${classNames.join(' ')}" data-ref="wallet-button">
107
- <button class="wallet-btn" data-ref="wallet-btn" title="${this.escapeHtml(address)}\nBalance: ${balance} ETH">
108
- <span class="wallet-mark wallet-mark--connected"></span>
109
- <span class="wallet-address">${this.escapeHtml(truncatedAddress)}</span>
110
- <span class="wallet-disconnect" data-ref="inline-disconnect" title="Disconnect">⏏︎</span>
111
- </button>
112
- ${this.renderWalletPanel(address, balance, menuOpen)}
113
- </div>
114
- `;
115
-
116
- return this.minifyHtml(markup);
117
- }
118
-
119
- events() {
120
- const baseEvents = super.events?.() || {};
121
- return {
122
- ...baseEvents,
123
- 'click [data-ref="inline-disconnect"]': (e) => this.handleInlineDisconnect(e),
124
- 'click [data-ref="wallet-btn"]': (e) => this.handleWalletToggle(e),
125
- 'click [data-ref="connect-btn"]': (e) => this.handleWalletToggle(e),
126
- };
127
- }
128
-
129
- renderWalletPanel(address, balance, menuOpen) {
130
- const truncatedAddress = `${address.slice(0, 6)}...${address.slice(-4)}`;
131
-
132
- const markup = `
133
- <div class="wallet-info-panel" data-ref="wallet-panel" aria-hidden="${menuOpen ? 'false' : 'true'}">
134
- <div class="wallet-info-row">
135
- <span class="wallet-info-label">Address</span>
136
- <span class="wallet-info-value">${this.escapeHtml(truncatedAddress)}</span>
137
- </div>
138
- <div class="wallet-info-row">
139
- <span class="wallet-info-label">ETH Balance</span>
140
- <span class="wallet-info-value">${balance} ETH</span>
141
- </div>
142
- </div>
143
- `;
144
-
145
- return this.minifyHtml(markup);
146
- }
147
-
148
- onStateUpdate(oldState, newState) {
149
- if (typeof super.onStateUpdate === 'function') {
150
- super.onStateUpdate(oldState, newState);
115
+ const { walletConnected, address, balance, menuOpen } = this.state;
116
+
117
+ if (!walletConnected) {
118
+ return h('div', { className: 'floating-wallet-button disconnected', ref: el => this.element = el },
119
+ h('button', {
120
+ className: 'wallet-btn',
121
+ onClick: this.bind(this.handleWalletToggle)
122
+ },
123
+ h('span', { className: 'wallet-mark' }),
124
+ h('span', { className: 'wallet-text' }, 'Connect Wallet')
125
+ )
126
+ );
127
+ }
128
+
129
+ const truncatedAddress = `${address.slice(0, 6)}...${address.slice(-4)}`;
130
+ const classNames = ['floating-wallet-button', 'connected'];
131
+ if (menuOpen) {
132
+ classNames.push('menu-open');
133
+ }
134
+
135
+ return h('div', { className: classNames.join(' '), ref: el => this.element = el },
136
+ h('button', {
137
+ className: 'wallet-btn',
138
+ title: `${this.escapeHtml(address)}\nBalance: ${balance} ETH`,
139
+ onClick: this.bind(this.handleWalletToggle)
140
+ },
141
+ h('span', { className: 'wallet-mark wallet-mark--connected' }),
142
+ h('span', { className: 'wallet-address' }, this.escapeHtml(truncatedAddress)),
143
+ h('span', {
144
+ className: 'wallet-disconnect',
145
+ title: 'Disconnect',
146
+ onClick: this.bind(this.handleInlineDisconnect)
147
+ }, '\u23CF\uFE0E')
148
+ ),
149
+ this.renderWalletPanel(address, balance, menuOpen)
150
+ );
151
151
  }
152
- if (!oldState || oldState.menuOpen !== newState.menuOpen) {
153
- if (newState.menuOpen) {
154
- this.attachOutsideClickListener();
155
- } else {
156
- this.detachOutsideClickListener();
157
- }
152
+
153
+ renderWalletPanel(address, balance, menuOpen) {
154
+ const truncatedAddress = `${address.slice(0, 6)}...${address.slice(-4)}`;
155
+
156
+ return h('div', {
157
+ className: 'wallet-info-panel',
158
+ 'aria-hidden': menuOpen ? 'false' : 'true'
159
+ },
160
+ h('div', { className: 'wallet-info-row' },
161
+ h('span', { className: 'wallet-info-label' }, 'Address'),
162
+ h('span', { className: 'wallet-info-value' }, this.escapeHtml(truncatedAddress))
163
+ ),
164
+ h('div', { className: 'wallet-info-row' },
165
+ h('span', { className: 'wallet-info-label' }, 'ETH Balance'),
166
+ h('span', { className: 'wallet-info-value' }, `${balance} ETH`)
167
+ )
168
+ );
158
169
  }
159
- }
160
-
161
- attachOutsideClickListener() {
162
- if (this.outsideClickHandler || this.outsideClickTimeout) return;
163
- this.outsideClickHandler = (event) => {
164
- if (!this.element) return;
165
- const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
166
- const clickedInside =
167
- (path.length && path.includes(this.element)) ||
168
- (event.target && this.element.contains(event.target));
169
- if (!clickedInside) {
170
- this.setState({ menuOpen: false });
171
- }
172
- };
173
- this.outsideClickTimeout = window.setTimeout(() => {
174
- document.addEventListener('click', this.outsideClickHandler);
175
- this.outsideClickTimeout = null;
176
- }, 0);
177
- }
178
-
179
- detachOutsideClickListener() {
180
- if (this.outsideClickTimeout) {
181
- window.clearTimeout(this.outsideClickTimeout);
182
- this.outsideClickTimeout = null;
170
+
171
+ didUpdate() {
172
+ if (this.state.menuOpen) {
173
+ this.attachOutsideClickListener();
174
+ } else {
175
+ this.detachOutsideClickListener();
176
+ }
183
177
  }
184
- if (this.outsideClickHandler) {
185
- document.removeEventListener('click', this.outsideClickHandler);
186
- this.outsideClickHandler = null;
178
+
179
+ attachOutsideClickListener() {
180
+ if (this.outsideClickHandler || this.outsideClickTimeout) return;
181
+ this.outsideClickHandler = (event) => {
182
+ if (!this.element) return;
183
+ const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
184
+ const clickedInside =
185
+ (path.length && path.includes(this.element)) ||
186
+ (event.target && this.element.contains(event.target));
187
+ if (!clickedInside) {
188
+ this.setState({ menuOpen: false });
189
+ }
190
+ };
191
+ this.outsideClickTimeout = window.setTimeout(() => {
192
+ document.addEventListener('click', this.outsideClickHandler);
193
+ this.outsideClickTimeout = null;
194
+ }, 0);
187
195
  }
188
- }
189
196
 
190
- setupDOMEventListeners() {
191
- // Delegated events handle wiring in this component.
192
- }
197
+ detachOutsideClickListener() {
198
+ if (this.outsideClickTimeout) {
199
+ window.clearTimeout(this.outsideClickTimeout);
200
+ this.outsideClickTimeout = null;
201
+ }
202
+ if (this.outsideClickHandler) {
203
+ document.removeEventListener('click', this.outsideClickHandler);
204
+ this.outsideClickHandler = null;
205
+ }
206
+ }
193
207
 
194
- handleWalletToggle(e) {
195
- this.handleButtonClick(e);
196
- }
208
+ handleWalletToggle(e) {
209
+ this.handleButtonClick(e);
210
+ }
197
211
 
198
- handleInlineDisconnect(e) {
199
- if (e) {
200
- e.preventDefault();
201
- e.stopPropagation();
212
+ handleInlineDisconnect(e) {
213
+ if (e) {
214
+ e.preventDefault();
215
+ e.stopPropagation();
216
+ }
217
+ this.handleDisconnect(e);
202
218
  }
203
- super.handleDisconnect(e);
204
- }
205
-
206
- minifyHtml(markup) {
207
- return typeof markup === 'string'
208
- ? markup.replace(/>\s+</g, '><').trim()
209
- : markup;
210
- }
211
219
  }
212
220
 
213
221
  export default WalletButton;