@monygroupcorp/micro-web3 1.2.3 → 1.3.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.
@@ -1,238 +1,206 @@
1
1
  // src/components/SyncProgressBar/SyncProgressBar.js
2
2
 
3
+ import { Component, h, render } from '@monygroupcorp/microact';
4
+
3
5
  /**
4
6
  * SyncProgressBar - UI component showing indexer sync status.
5
7
  *
6
8
  * @example
7
9
  * import { SyncProgressBar } from '@monygroupcorp/micro-web3/components';
8
- * const progressBar = new SyncProgressBar({ indexer, eventBus });
9
- * progressBar.mount(document.getElementById('sync-status'));
10
+ * render(h(SyncProgressBar, { indexer, eventBus }), document.getElementById('sync-status'));
10
11
  */
11
- class SyncProgressBar {
12
- constructor({ indexer, eventBus }) {
13
- if (!indexer) {
14
- throw new Error('SyncProgressBar requires indexer instance');
15
- }
16
- if (!eventBus) {
17
- throw new Error('SyncProgressBar requires eventBus instance');
12
+ class SyncProgressBar extends Component {
13
+ constructor(props) {
14
+ super(props);
15
+
16
+ if (!props.indexer) {
17
+ throw new Error('SyncProgressBar requires indexer instance');
18
+ }
19
+ if (!props.eventBus) {
20
+ throw new Error('SyncProgressBar requires eventBus instance');
21
+ }
22
+
23
+ this.indexer = props.indexer;
24
+ this.eventBus = props.eventBus;
25
+
26
+ this.state = {
27
+ status: 'initializing',
28
+ progress: 0,
29
+ currentBlock: 0,
30
+ latestBlock: 0,
31
+ error: null
32
+ };
18
33
  }
19
34
 
20
- this.indexer = indexer;
21
- this.eventBus = eventBus;
22
- this.element = null;
23
- this.unsubscribers = [];
24
-
25
- this.state = {
26
- status: 'initializing',
27
- progress: 0,
28
- currentBlock: 0,
29
- latestBlock: 0,
30
- error: null
31
- };
32
- }
33
-
34
- mount(container) {
35
- if (typeof container === 'string') {
36
- container = document.querySelector(container);
37
- }
38
- if (!container) {
39
- throw new Error('SyncProgressBar: Invalid container');
40
- }
35
+ didMount() {
36
+ this.subscribe('indexer:syncProgress', (data) => {
37
+ this.setState({
38
+ status: 'syncing',
39
+ progress: data.progress,
40
+ currentBlock: data.currentBlock,
41
+ latestBlock: data.latestBlock
42
+ });
43
+ });
44
+
45
+ this.subscribe('indexer:syncComplete', () => {
46
+ this.setState({ status: 'synced', progress: 1 });
47
+ });
41
48
 
42
- // Create element
43
- this.element = document.createElement('div');
44
- this.element.className = 'mw3-sync-progress';
45
- container.appendChild(this.element);
49
+ this.subscribe('indexer:error', (data) => {
50
+ this.setState({ status: 'error', error: data.message });
51
+ });
46
52
 
47
- // Subscribe to events
48
- this._setupListeners();
53
+ this.subscribe('indexer:paused', () => {
54
+ this.setState({ status: 'paused' });
55
+ });
49
56
 
50
- // Initial render
51
- this._updateState(this.indexer.sync.getStatus());
52
- this._render();
57
+ this.subscribe('indexer:resumed', () => {
58
+ this.setState({ status: 'syncing' });
59
+ });
53
60
 
54
- return this;
55
- }
61
+ // Initial status
62
+ const initialStatus = this.indexer.sync?.getStatus?.();
63
+ if (initialStatus) {
64
+ this.setState(initialStatus);
65
+ }
66
+ }
56
67
 
57
- unmount() {
58
- // Cleanup subscriptions
59
- for (const unsub of this.unsubscribers) {
60
- unsub();
68
+ handleRetry() {
69
+ this.indexer.sync?.resync?.();
61
70
  }
62
- this.unsubscribers = [];
63
71
 
64
- // Remove element
65
- if (this.element && this.element.parentNode) {
66
- this.element.parentNode.removeChild(this.element);
72
+ handleResume() {
73
+ this.indexer.sync?.resume?.();
67
74
  }
68
- this.element = null;
69
- }
70
-
71
- _setupListeners() {
72
- const listeners = [
73
- ['indexer:syncProgress', (data) => {
74
- this._updateState({
75
- status: 'syncing',
76
- progress: data.progress,
77
- currentBlock: data.currentBlock,
78
- latestBlock: data.latestBlock
79
- });
80
- }],
81
- ['indexer:syncComplete', () => {
82
- this._updateState({ status: 'synced', progress: 1 });
83
- }],
84
- ['indexer:error', (data) => {
85
- this._updateState({ status: 'error', error: data.message });
86
- }],
87
- ['indexer:paused', () => {
88
- this._updateState({ status: 'paused' });
89
- }],
90
- ['indexer:resumed', () => {
91
- this._updateState({ status: 'syncing' });
92
- }]
93
- ];
94
-
95
- for (const [event, handler] of listeners) {
96
- const unsub = this.eventBus.on(event, handler);
97
- this.unsubscribers.push(unsub);
75
+
76
+ render() {
77
+ const { status, progress, currentBlock, latestBlock, error } = this.state;
78
+ const percent = Math.round(progress * 100);
79
+
80
+ return h('div', { className: `mw3-sync-progress mw3-sync-progress--${status}` },
81
+ status === 'syncing' && [
82
+ h('div', {
83
+ key: 'bar',
84
+ className: 'mw3-sync-progress__bar',
85
+ style: { width: `${percent}%` }
86
+ }),
87
+ h('span', { key: 'text', className: 'mw3-sync-progress__text' }, `Syncing... ${percent}%`)
88
+ ],
89
+
90
+ status === 'synced' &&
91
+ h('span', { className: 'mw3-sync-progress__text' },
92
+ `Synced to block ${latestBlock.toLocaleString()}`
93
+ ),
94
+
95
+ status === 'error' && [
96
+ h('span', { key: 'text', className: 'mw3-sync-progress__text' },
97
+ `Sync failed: ${error || 'Unknown error'}`
98
+ ),
99
+ h('button', {
100
+ key: 'retry',
101
+ className: 'mw3-sync-progress__retry',
102
+ onClick: this.bind(this.handleRetry)
103
+ }, 'Retry')
104
+ ],
105
+
106
+ status === 'paused' && [
107
+ h('span', { key: 'text', className: 'mw3-sync-progress__text' }, 'Sync paused'),
108
+ h('button', {
109
+ key: 'resume',
110
+ className: 'mw3-sync-progress__resume',
111
+ onClick: this.bind(this.handleResume)
112
+ }, 'Resume')
113
+ ],
114
+
115
+ status === 'initializing' &&
116
+ h('span', { className: 'mw3-sync-progress__text' }, 'Initializing...')
117
+ );
98
118
  }
99
- }
100
-
101
- _updateState(updates) {
102
- this.state = { ...this.state, ...updates };
103
- this._render();
104
- }
105
-
106
- _render() {
107
- if (!this.element) return;
108
-
109
- const { status, progress, currentBlock, latestBlock, error } = this.state;
110
-
111
- // Update class for state
112
- this.element.className = `mw3-sync-progress mw3-sync-progress--${status}`;
113
-
114
- if (status === 'syncing') {
115
- const percent = Math.round(progress * 100);
116
- this.element.innerHTML = `
117
- <div class="mw3-sync-progress__bar" style="width: ${percent}%"></div>
118
- <span class="mw3-sync-progress__text">Syncing... ${percent}%</span>
119
- `;
120
- } else if (status === 'synced') {
121
- this.element.innerHTML = `
122
- <span class="mw3-sync-progress__text">Synced to block ${latestBlock.toLocaleString()}</span>
123
- `;
124
- } else if (status === 'error') {
125
- this.element.innerHTML = `
126
- <span class="mw3-sync-progress__text">Sync failed: ${error || 'Unknown error'}</span>
127
- <button class="mw3-sync-progress__retry">Retry</button>
128
- `;
129
-
130
- // Add retry handler
131
- const retryBtn = this.element.querySelector('.mw3-sync-progress__retry');
132
- if (retryBtn) {
133
- retryBtn.onclick = () => this.indexer.sync.resync();
134
- }
135
- } else if (status === 'paused') {
136
- this.element.innerHTML = `
137
- <span class="mw3-sync-progress__text">Sync paused</span>
138
- <button class="mw3-sync-progress__resume">Resume</button>
139
- `;
140
-
141
- const resumeBtn = this.element.querySelector('.mw3-sync-progress__resume');
142
- if (resumeBtn) {
143
- resumeBtn.onclick = () => this.indexer.sync.resume();
144
- }
145
- } else {
146
- this.element.innerHTML = `
147
- <span class="mw3-sync-progress__text">Initializing...</span>
148
- `;
119
+
120
+ /**
121
+ * Inject default styles into document.
122
+ * Call once at app startup if not using custom CSS.
123
+ */
124
+ static injectStyles() {
125
+ if (document.getElementById('mw3-sync-progress-styles')) return;
126
+
127
+ const style = document.createElement('style');
128
+ style.id = 'mw3-sync-progress-styles';
129
+ style.textContent = `
130
+ .mw3-sync-progress {
131
+ position: relative;
132
+ height: 24px;
133
+ background: var(--mw3-sync-bg, #f0f0f0);
134
+ border-radius: 4px;
135
+ overflow: hidden;
136
+ font-family: system-ui, sans-serif;
137
+ font-size: 12px;
138
+ }
139
+
140
+ .mw3-sync-progress__bar {
141
+ position: absolute;
142
+ top: 0;
143
+ left: 0;
144
+ height: 100%;
145
+ background: var(--mw3-sync-bar, #4caf50);
146
+ transition: width 0.3s ease;
147
+ }
148
+
149
+ .mw3-sync-progress__text {
150
+ position: relative;
151
+ z-index: 1;
152
+ display: flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ height: 100%;
156
+ color: var(--mw3-sync-text, #333);
157
+ padding: 0 8px;
158
+ }
159
+
160
+ .mw3-sync-progress--synced {
161
+ background: var(--mw3-sync-bar, #4caf50);
162
+ }
163
+
164
+ .mw3-sync-progress--synced .mw3-sync-progress__text {
165
+ color: white;
166
+ }
167
+
168
+ .mw3-sync-progress--error {
169
+ background: var(--mw3-sync-error, #f44336);
170
+ }
171
+
172
+ .mw3-sync-progress--error .mw3-sync-progress__text {
173
+ color: white;
174
+ justify-content: space-between;
175
+ }
176
+
177
+ .mw3-sync-progress__retry,
178
+ .mw3-sync-progress__resume {
179
+ background: rgba(255,255,255,0.2);
180
+ border: 1px solid rgba(255,255,255,0.4);
181
+ border-radius: 3px;
182
+ color: white;
183
+ padding: 2px 8px;
184
+ cursor: pointer;
185
+ font-size: 11px;
186
+ }
187
+
188
+ .mw3-sync-progress__retry:hover,
189
+ .mw3-sync-progress__resume:hover {
190
+ background: rgba(255,255,255,0.3);
191
+ }
192
+
193
+ .mw3-sync-progress--paused {
194
+ background: var(--mw3-sync-paused, #ff9800);
195
+ }
196
+
197
+ .mw3-sync-progress--paused .mw3-sync-progress__text {
198
+ color: white;
199
+ justify-content: space-between;
200
+ }
201
+ `;
202
+ document.head.appendChild(style);
149
203
  }
150
- }
151
-
152
- /**
153
- * Inject default styles into document.
154
- * Call once at app startup if not using custom CSS.
155
- */
156
- static injectStyles() {
157
- if (document.getElementById('mw3-sync-progress-styles')) return;
158
-
159
- const style = document.createElement('style');
160
- style.id = 'mw3-sync-progress-styles';
161
- style.textContent = `
162
- .mw3-sync-progress {
163
- position: relative;
164
- height: 24px;
165
- background: var(--mw3-sync-bg, #f0f0f0);
166
- border-radius: 4px;
167
- overflow: hidden;
168
- font-family: system-ui, sans-serif;
169
- font-size: 12px;
170
- }
171
-
172
- .mw3-sync-progress__bar {
173
- position: absolute;
174
- top: 0;
175
- left: 0;
176
- height: 100%;
177
- background: var(--mw3-sync-bar, #4caf50);
178
- transition: width 0.3s ease;
179
- }
180
-
181
- .mw3-sync-progress__text {
182
- position: relative;
183
- z-index: 1;
184
- display: flex;
185
- align-items: center;
186
- justify-content: center;
187
- height: 100%;
188
- color: var(--mw3-sync-text, #333);
189
- padding: 0 8px;
190
- }
191
-
192
- .mw3-sync-progress--synced {
193
- background: var(--mw3-sync-bar, #4caf50);
194
- }
195
-
196
- .mw3-sync-progress--synced .mw3-sync-progress__text {
197
- color: white;
198
- }
199
-
200
- .mw3-sync-progress--error {
201
- background: var(--mw3-sync-error, #f44336);
202
- }
203
-
204
- .mw3-sync-progress--error .mw3-sync-progress__text {
205
- color: white;
206
- justify-content: space-between;
207
- }
208
-
209
- .mw3-sync-progress__retry,
210
- .mw3-sync-progress__resume {
211
- background: rgba(255,255,255,0.2);
212
- border: 1px solid rgba(255,255,255,0.4);
213
- border-radius: 3px;
214
- color: white;
215
- padding: 2px 8px;
216
- cursor: pointer;
217
- font-size: 11px;
218
- }
219
-
220
- .mw3-sync-progress__retry:hover,
221
- .mw3-sync-progress__resume:hover {
222
- background: rgba(255,255,255,0.3);
223
- }
224
-
225
- .mw3-sync-progress--paused {
226
- background: var(--mw3-sync-paused, #ff9800);
227
- }
228
-
229
- .mw3-sync-progress--paused .mw3-sync-progress__text {
230
- color: white;
231
- justify-content: space-between;
232
- }
233
- `;
234
- document.head.appendChild(style);
235
- }
236
204
  }
237
205
 
238
206
  export { SyncProgressBar };
@@ -1,4 +1,4 @@
1
- import { Component } from '@monygroupcorp/microact';
1
+ import { Component, h } from '@monygroupcorp/microact';
2
2
 
3
3
  export class MessagePopup extends Component {
4
4
  constructor(props) {
@@ -11,7 +11,7 @@ export class MessagePopup extends Component {
11
11
  show(options) {
12
12
  const { title = '', message = '', type = 'info', duration = 5000 } = options;
13
13
  const id = Date.now() + Math.random();
14
-
14
+
15
15
  const newMessage = { id, title, message, type };
16
16
  this.setState({ messages: [...this.state.messages, newMessage] });
17
17
 
@@ -41,29 +41,31 @@ export class MessagePopup extends Component {
41
41
  this.show({ title, message, type: 'info' });
42
42
  }
43
43
 
44
- render() {
45
- return `
46
- <div id="message-popup-container">
47
- ${this.state.messages.map(msg => `
48
- <div class="message-popup ${msg.type}" data-id="${msg.id}">
49
- <div class="message-content">
50
- ${msg.title ? `<div class="message-title">${msg.title}</div>` : ''}
51
- <div class="message-text">${msg.message}</div>
52
- </div>
53
- <button class="close-button" data-id="${msg.id}">&times;</button>
54
- </div>
55
- `).join('')}
56
- </div>
57
- `;
44
+ handleClose(id) {
45
+ this.close(id);
58
46
  }
59
47
 
60
- events() {
61
- return {
62
- 'click .close-button': (e) => {
63
- const id = parseFloat(e.target.dataset.id);
64
- this.close(id);
65
- }
66
- };
48
+ render() {
49
+ const { messages } = this.state;
50
+
51
+ return h('div', { id: 'message-popup-container' },
52
+ messages.map(msg =>
53
+ h('div', {
54
+ key: msg.id,
55
+ className: `message-popup ${msg.type}`,
56
+ 'data-id': msg.id
57
+ },
58
+ h('div', { className: 'message-content' },
59
+ msg.title && h('div', { className: 'message-title' }, msg.title),
60
+ h('div', { className: 'message-text' }, msg.message)
61
+ ),
62
+ h('button', {
63
+ className: 'close-button',
64
+ onClick: () => this.handleClose(msg.id)
65
+ }, '\u00D7')
66
+ )
67
+ )
68
+ );
67
69
  }
68
70
 
69
71
  static get styles() {
@@ -156,4 +158,4 @@ export class MessagePopup extends Component {
156
158
  }
157
159
  `;
158
160
  }
159
- }
161
+ }
@@ -1,33 +1,13 @@
1
- import { Component } from '@monygroupcorp/microact';
1
+ import { Component, h } from '@monygroupcorp/microact';
2
2
 
3
3
  export class WalletModal extends Component {
4
4
  constructor(props) {
5
5
  super(props);
6
- this.providerMap = props.providerMap;
7
- this.walletIcons = props.walletIcons;
8
- this.onWalletSelected = props.onWalletSelected;
9
-
10
6
  this.state = {
11
7
  isVisible: false,
12
8
  };
13
9
  }
14
10
 
15
- events() {
16
- return {
17
- 'click .wallet-modal-close': this.hide,
18
- 'click .wallet-modal-overlay': (e) => {
19
- if (e.target === e.currentTarget) this.hide();
20
- },
21
- 'click .wallet-option': async (e) => {
22
- const walletType = e.currentTarget.dataset.wallet;
23
- if (this.onWalletSelected) {
24
- await this.onWalletSelected(walletType);
25
- }
26
- this.hide();
27
- }
28
- };
29
- }
30
-
31
11
  show() {
32
12
  this.setState({ isVisible: true });
33
13
  }
@@ -36,34 +16,55 @@ export class WalletModal extends Component {
36
16
  this.setState({ isVisible: false });
37
17
  }
38
18
 
19
+ handleWalletClick(walletType) {
20
+ if (this.props.onWalletSelected) {
21
+ this.props.onWalletSelected(walletType);
22
+ }
23
+ this.hide();
24
+ }
25
+
39
26
  render() {
40
27
  if (!this.state.isVisible) {
41
- return '<div class="wallet-modal-container" style="display: none;"></div>';
28
+ return h('div', { className: 'wallet-modal-container', style: { display: 'none' } });
42
29
  }
43
30
 
44
- const walletOptions = Object.keys(this.providerMap).map(walletType => `
45
- <button class="wallet-option" data-wallet="${walletType}">
46
- <img src="${this.walletIcons[walletType]}" alt="${walletType}">
47
- <span>${walletType.charAt(0).toUpperCase() + walletType.slice(1)}</span>
48
- </button>
49
- `).join('');
31
+ const walletOptions = Object.keys(this.props.providerMap);
50
32
 
51
- return `
52
- <div class="wallet-modal-container">
53
- <div class="wallet-modal-overlay">
54
- <div class="wallet-modal">
55
- <div class="wallet-modal-header">
56
- <h3>Select Your Wallet</h3>
57
- <button class="wallet-modal-close">&times;</button>
58
- </div>
59
- <div class="wallet-options">
60
- ${walletOptions}
61
- </div>
62
- </div>
63
- </div>
64
- </div>
65
- `;
33
+ return h('div', { className: 'wallet-modal-container' },
34
+ h('div', {
35
+ className: 'wallet-modal-overlay',
36
+ onClick: (e) => {
37
+ if (e.target === e.currentTarget) this.hide();
38
+ }
39
+ },
40
+ h('div', { className: 'wallet-modal' },
41
+ h('div', { className: 'wallet-modal-header' },
42
+ h('h3', null, 'Select Your Wallet'),
43
+ h('button', {
44
+ className: 'wallet-modal-close',
45
+ onClick: this.bind(this.hide)
46
+ }, '\u00D7')
47
+ ),
48
+ h('div', { className: 'wallet-options' },
49
+ walletOptions.map(walletType =>
50
+ h('button', {
51
+ key: walletType,
52
+ className: 'wallet-option',
53
+ 'data-wallet': walletType,
54
+ onClick: () => this.handleWalletClick(walletType)
55
+ },
56
+ h('img', {
57
+ src: this.props.walletIcons[walletType],
58
+ alt: walletType
59
+ }),
60
+ h('span', null, walletType.charAt(0).toUpperCase() + walletType.slice(1))
61
+ )
62
+ )
63
+ )
64
+ )
65
+ )
66
+ );
66
67
  }
67
68
  }
68
69
 
69
- export default WalletModal;
70
+ export default WalletModal;