@monygroupcorp/micro-web3 0.1.3 → 1.2.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.
- package/CLAUDE.md +6 -0
- package/dist/{micro-web3.cjs.js → micro-web3.cjs} +3 -3
- package/dist/micro-web3.cjs.map +1 -0
- package/dist/micro-web3.esm.js +2 -2
- package/dist/micro-web3.esm.js.map +1 -1
- package/dist/micro-web3.umd.js +2 -2
- package/dist/micro-web3.umd.js.map +1 -1
- package/docs/plans/2026-01-22-event-indexer.md +3642 -0
- package/package.json +2 -2
- package/rollup.config.cjs +1 -1
- package/src/components/FloatingWalletButton/FloatingWalletButton.js +53 -21
- package/src/components/SettingsModal/SettingsModal.js +371 -0
- package/src/components/SyncProgressBar/SyncProgressBar.js +238 -0
- package/src/index.js +15 -1
- package/src/indexer/EntityResolver.js +218 -0
- package/src/indexer/Patterns.js +277 -0
- package/src/indexer/QueryEngine.js +149 -0
- package/src/indexer/SyncEngine.js +494 -0
- package/src/indexer/index.js +13 -0
- package/src/services/BlockchainService.js +30 -0
- package/src/services/ContractCache.js +20 -2
- package/src/services/EventIndexer.js +399 -0
- package/src/storage/IndexedDBAdapter.js +423 -0
- package/src/storage/IndexerSettings.js +88 -0
- package/src/storage/MemoryAdapter.js +194 -0
- package/src/storage/StorageAdapter.js +129 -0
- package/src/storage/index.js +41 -0
- package/dist/micro-web3.cjs.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monygroupcorp/micro-web3",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "A lean, reusable Web3 toolkit with components for wallet connection, IPFS, and common Web3 UI patterns.",
|
|
5
|
-
"main": "dist/micro-web3.cjs
|
|
5
|
+
"main": "dist/micro-web3.cjs",
|
|
6
6
|
"module": "dist/micro-web3.esm.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"scripts": {
|
package/rollup.config.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Component, eventBus } from '@monygroupcorp/microact';
|
|
2
2
|
import WalletModal from '../Wallet/WalletModal.js';
|
|
3
|
+
import { SettingsModal } from '../SettingsModal/SettingsModal.js';
|
|
3
4
|
import { ethers } from 'ethers';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -12,6 +13,7 @@ export class FloatingWalletButton extends Component {
|
|
|
12
13
|
super();
|
|
13
14
|
this.walletService = props.walletService;
|
|
14
15
|
this.walletModal = null;
|
|
16
|
+
this.settingsModal = null;
|
|
15
17
|
this.state = {
|
|
16
18
|
walletConnected: false,
|
|
17
19
|
address: null,
|
|
@@ -47,30 +49,31 @@ export class FloatingWalletButton extends Component {
|
|
|
47
49
|
let isConnected = this.walletService.isConnected();
|
|
48
50
|
let address = this.walletService.getAddress();
|
|
49
51
|
|
|
50
|
-
// If not connected,
|
|
52
|
+
// If not connected, check if window.ethereum already has connected accounts
|
|
51
53
|
if (!isConnected && typeof window.ethereum !== 'undefined') {
|
|
52
54
|
try {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
console.log('[FloatingWalletButton] Auto-reconnect not possible');
|
|
55
|
+
// eth_accounts returns already-authorized accounts without prompting
|
|
56
|
+
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
|
|
57
|
+
const hasAccounts = accounts && accounts.length > 0;
|
|
58
|
+
|
|
59
|
+
if (hasAccounts) {
|
|
60
|
+
// Detect which wallet is active
|
|
61
|
+
const walletType = this._detectWalletType();
|
|
62
|
+
console.log('[FloatingWalletButton] Detected existing connection:', walletType, accounts[0]);
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
await this.walletService.selectWallet(walletType);
|
|
66
|
+
await this.walletService.connect();
|
|
67
|
+
isConnected = this.walletService.isConnected();
|
|
68
|
+
address = this.walletService.getAddress();
|
|
69
|
+
|
|
70
|
+
if (isConnected) {
|
|
71
|
+
console.log('[FloatingWalletButton] Auto-connected to existing session:', walletType);
|
|
72
|
+
// Update lastWallet for future sessions
|
|
73
|
+
localStorage.setItem('ms2fun_lastWallet', walletType);
|
|
73
74
|
}
|
|
75
|
+
} catch (connectError) {
|
|
76
|
+
console.log('[FloatingWalletButton] Could not auto-connect:', connectError.message);
|
|
74
77
|
}
|
|
75
78
|
}
|
|
76
79
|
} catch (error) {
|
|
@@ -89,6 +92,21 @@ export class FloatingWalletButton extends Component {
|
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Detect which wallet type is currently active based on provider flags
|
|
97
|
+
*/
|
|
98
|
+
_detectWalletType() {
|
|
99
|
+
if (typeof window.ethereum === 'undefined') return 'metamask';
|
|
100
|
+
|
|
101
|
+
// Check specific wallet flags (order matters - more specific first)
|
|
102
|
+
if (window.ethereum.isRabby) return 'rabby';
|
|
103
|
+
if (window.ethereum.isRainbow) return 'rainbow';
|
|
104
|
+
if (window.phantom?.ethereum) return 'phantom';
|
|
105
|
+
|
|
106
|
+
// Default to metamask for any EIP-1193 provider
|
|
107
|
+
return 'metamask';
|
|
108
|
+
}
|
|
109
|
+
|
|
92
110
|
/**
|
|
93
111
|
* Load wallet info (balance, EXEC tokens, vault benefactor status)
|
|
94
112
|
*/
|
|
@@ -643,6 +661,7 @@ export class FloatingWalletButton extends Component {
|
|
|
643
661
|
this.handleMenuItemClick(route);
|
|
644
662
|
} else if (action === 'settings') {
|
|
645
663
|
this.setState({ menuOpen: false });
|
|
664
|
+
this.openSettings();
|
|
646
665
|
}
|
|
647
666
|
});
|
|
648
667
|
});
|
|
@@ -663,12 +682,25 @@ export class FloatingWalletButton extends Component {
|
|
|
663
682
|
}
|
|
664
683
|
}
|
|
665
684
|
|
|
685
|
+
openSettings() {
|
|
686
|
+
if (!this.settingsModal) {
|
|
687
|
+
this.settingsModal = new SettingsModal({ eventBus: eventBus });
|
|
688
|
+
}
|
|
689
|
+
this.settingsModal.show();
|
|
690
|
+
}
|
|
691
|
+
|
|
666
692
|
onUnmount() {
|
|
667
693
|
// Clean up wallet modal
|
|
668
694
|
if (this.walletModal) {
|
|
669
695
|
this.walletModal.hide();
|
|
670
696
|
this.walletModal = null;
|
|
671
697
|
}
|
|
698
|
+
|
|
699
|
+
// Clean up settings modal
|
|
700
|
+
if (this.settingsModal) {
|
|
701
|
+
this.settingsModal.hide();
|
|
702
|
+
this.settingsModal = null;
|
|
703
|
+
}
|
|
672
704
|
}
|
|
673
705
|
|
|
674
706
|
escapeHtml(text) {
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
// src/components/SettingsModal/SettingsModal.js
|
|
2
|
+
|
|
3
|
+
import { IndexerSettings } from '../../storage/index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SettingsModal - Modal for user settings including indexer storage controls.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const modal = new SettingsModal({ eventBus });
|
|
10
|
+
* modal.show();
|
|
11
|
+
*/
|
|
12
|
+
class SettingsModal {
|
|
13
|
+
constructor({ eventBus }) {
|
|
14
|
+
this.eventBus = eventBus;
|
|
15
|
+
this.element = null;
|
|
16
|
+
this.overlay = null;
|
|
17
|
+
this.isVisible = false;
|
|
18
|
+
this.state = {
|
|
19
|
+
storageEnabled: true,
|
|
20
|
+
storageUsed: 0,
|
|
21
|
+
storageQuota: 0,
|
|
22
|
+
clearing: false
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async show() {
|
|
27
|
+
if (this.isVisible) return;
|
|
28
|
+
|
|
29
|
+
// Load current settings
|
|
30
|
+
const settings = IndexerSettings.get();
|
|
31
|
+
const estimate = await IndexerSettings.getStorageEstimate();
|
|
32
|
+
|
|
33
|
+
this.state = {
|
|
34
|
+
storageEnabled: settings.storageEnabled,
|
|
35
|
+
storageUsed: estimate.used || 0,
|
|
36
|
+
storageQuota: estimate.quota || 0,
|
|
37
|
+
clearing: false
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
this._createModal();
|
|
41
|
+
this.isVisible = true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
hide() {
|
|
45
|
+
if (!this.isVisible) return;
|
|
46
|
+
|
|
47
|
+
if (this.overlay && this.overlay.parentNode) {
|
|
48
|
+
this.overlay.parentNode.removeChild(this.overlay);
|
|
49
|
+
}
|
|
50
|
+
this.overlay = null;
|
|
51
|
+
this.element = null;
|
|
52
|
+
this.isVisible = false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_createModal() {
|
|
56
|
+
// Inject styles if needed
|
|
57
|
+
SettingsModal.injectStyles();
|
|
58
|
+
|
|
59
|
+
// Create overlay
|
|
60
|
+
this.overlay = document.createElement('div');
|
|
61
|
+
this.overlay.className = 'mw3-settings-overlay';
|
|
62
|
+
this.overlay.addEventListener('click', (e) => {
|
|
63
|
+
if (e.target === this.overlay) this.hide();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Create modal
|
|
67
|
+
this.element = document.createElement('div');
|
|
68
|
+
this.element.className = 'mw3-settings-modal';
|
|
69
|
+
this.element.innerHTML = this._render();
|
|
70
|
+
|
|
71
|
+
this.overlay.appendChild(this.element);
|
|
72
|
+
document.body.appendChild(this.overlay);
|
|
73
|
+
|
|
74
|
+
this._setupEventListeners();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_render() {
|
|
78
|
+
const { storageEnabled, storageUsed, storageQuota, clearing } = this.state;
|
|
79
|
+
const usedMB = (storageUsed / (1024 * 1024)).toFixed(1);
|
|
80
|
+
const quotaMB = (storageQuota / (1024 * 1024)).toFixed(0);
|
|
81
|
+
|
|
82
|
+
return `
|
|
83
|
+
<div class="mw3-settings-header">
|
|
84
|
+
<h2>Settings</h2>
|
|
85
|
+
<button class="mw3-settings-close" data-action="close">×</button>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div class="mw3-settings-content">
|
|
89
|
+
<div class="mw3-settings-section">
|
|
90
|
+
<h3>Data Storage</h3>
|
|
91
|
+
<p class="mw3-settings-description">
|
|
92
|
+
Event data is cached locally to improve performance. You can disable this to save space.
|
|
93
|
+
</p>
|
|
94
|
+
|
|
95
|
+
<div class="mw3-settings-row">
|
|
96
|
+
<label class="mw3-settings-toggle">
|
|
97
|
+
<input type="checkbox" ${storageEnabled ? 'checked' : ''} data-setting="storageEnabled">
|
|
98
|
+
<span class="mw3-toggle-slider"></span>
|
|
99
|
+
<span class="mw3-toggle-label">Enable local event storage</span>
|
|
100
|
+
</label>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div class="mw3-settings-info">
|
|
104
|
+
<span>Storage used: ${usedMB} MB${quotaMB > 0 ? ` / ${quotaMB} MB` : ''}</span>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div class="mw3-settings-row">
|
|
108
|
+
<button class="mw3-settings-btn mw3-settings-btn--danger" data-action="clearData" ${clearing ? 'disabled' : ''}>
|
|
109
|
+
${clearing ? 'Clearing...' : 'Clear All Cached Data'}
|
|
110
|
+
</button>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<p class="mw3-settings-note">
|
|
114
|
+
Disabling storage means event history will need to be re-fetched each session.
|
|
115
|
+
</p>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div class="mw3-settings-footer">
|
|
120
|
+
<button class="mw3-settings-btn mw3-settings-btn--primary" data-action="close">Done</button>
|
|
121
|
+
</div>
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
_setupEventListeners() {
|
|
126
|
+
// Close button
|
|
127
|
+
this.element.querySelectorAll('[data-action="close"]').forEach(btn => {
|
|
128
|
+
btn.addEventListener('click', () => this.hide());
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Storage toggle
|
|
132
|
+
const toggle = this.element.querySelector('[data-setting="storageEnabled"]');
|
|
133
|
+
if (toggle) {
|
|
134
|
+
toggle.addEventListener('change', (e) => {
|
|
135
|
+
const enabled = e.target.checked;
|
|
136
|
+
IndexerSettings.set({ storageEnabled: enabled });
|
|
137
|
+
this.state.storageEnabled = enabled;
|
|
138
|
+
|
|
139
|
+
// Emit event so indexer can react
|
|
140
|
+
this.eventBus.emit('settings:storageChanged', { enabled });
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Clear data button
|
|
145
|
+
const clearBtn = this.element.querySelector('[data-action="clearData"]');
|
|
146
|
+
if (clearBtn) {
|
|
147
|
+
clearBtn.addEventListener('click', async () => {
|
|
148
|
+
if (this.state.clearing) return;
|
|
149
|
+
|
|
150
|
+
this.state.clearing = true;
|
|
151
|
+
this._updateContent();
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
await IndexerSettings.clearAllData();
|
|
155
|
+
|
|
156
|
+
// Update storage estimate
|
|
157
|
+
const estimate = await IndexerSettings.getStorageEstimate();
|
|
158
|
+
this.state.storageUsed = estimate.used || 0;
|
|
159
|
+
this.state.storageQuota = estimate.quota || 0;
|
|
160
|
+
|
|
161
|
+
// Emit event so indexer knows to resync
|
|
162
|
+
this.eventBus.emit('settings:dataCleared', {});
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error('[SettingsModal] Failed to clear data:', error);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this.state.clearing = false;
|
|
168
|
+
this._updateContent();
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ESC key to close
|
|
173
|
+
this._escHandler = (e) => {
|
|
174
|
+
if (e.key === 'Escape') this.hide();
|
|
175
|
+
};
|
|
176
|
+
document.addEventListener('keydown', this._escHandler);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
_updateContent() {
|
|
180
|
+
if (this.element) {
|
|
181
|
+
this.element.innerHTML = this._render();
|
|
182
|
+
this._setupEventListeners();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
static injectStyles() {
|
|
187
|
+
if (document.getElementById('mw3-settings-styles')) return;
|
|
188
|
+
|
|
189
|
+
const style = document.createElement('style');
|
|
190
|
+
style.id = 'mw3-settings-styles';
|
|
191
|
+
style.textContent = `
|
|
192
|
+
.mw3-settings-overlay {
|
|
193
|
+
position: fixed;
|
|
194
|
+
top: 0;
|
|
195
|
+
left: 0;
|
|
196
|
+
right: 0;
|
|
197
|
+
bottom: 0;
|
|
198
|
+
background: rgba(0, 0, 0, 0.7);
|
|
199
|
+
display: flex;
|
|
200
|
+
align-items: center;
|
|
201
|
+
justify-content: center;
|
|
202
|
+
z-index: 10000;
|
|
203
|
+
backdrop-filter: blur(4px);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.mw3-settings-modal {
|
|
207
|
+
background: linear-gradient(135deg, #1a1a2e, #16213e);
|
|
208
|
+
border: 1px solid rgba(218, 165, 32, 0.3);
|
|
209
|
+
border-radius: 12px;
|
|
210
|
+
width: 90%;
|
|
211
|
+
max-width: 400px;
|
|
212
|
+
color: #fff;
|
|
213
|
+
font-family: 'Lato', system-ui, sans-serif;
|
|
214
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.mw3-settings-header {
|
|
218
|
+
display: flex;
|
|
219
|
+
align-items: center;
|
|
220
|
+
justify-content: space-between;
|
|
221
|
+
padding: 1rem 1.25rem;
|
|
222
|
+
border-bottom: 1px solid rgba(218, 165, 32, 0.2);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.mw3-settings-header h2 {
|
|
226
|
+
margin: 0;
|
|
227
|
+
font-size: 1.125rem;
|
|
228
|
+
font-weight: 600;
|
|
229
|
+
color: rgba(218, 165, 32, 0.95);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.mw3-settings-close {
|
|
233
|
+
background: none;
|
|
234
|
+
border: none;
|
|
235
|
+
color: rgba(255, 255, 255, 0.6);
|
|
236
|
+
font-size: 1.5rem;
|
|
237
|
+
cursor: pointer;
|
|
238
|
+
padding: 0;
|
|
239
|
+
line-height: 1;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.mw3-settings-close:hover {
|
|
243
|
+
color: #fff;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.mw3-settings-content {
|
|
247
|
+
padding: 1.25rem;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.mw3-settings-section h3 {
|
|
251
|
+
margin: 0 0 0.5rem 0;
|
|
252
|
+
font-size: 0.9rem;
|
|
253
|
+
font-weight: 600;
|
|
254
|
+
color: rgba(255, 255, 255, 0.9);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.mw3-settings-description {
|
|
258
|
+
margin: 0 0 1rem 0;
|
|
259
|
+
font-size: 0.8rem;
|
|
260
|
+
color: rgba(255, 255, 255, 0.6);
|
|
261
|
+
line-height: 1.4;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.mw3-settings-row {
|
|
265
|
+
margin-bottom: 1rem;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.mw3-settings-toggle {
|
|
269
|
+
display: flex;
|
|
270
|
+
align-items: center;
|
|
271
|
+
gap: 0.75rem;
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.mw3-settings-toggle input {
|
|
276
|
+
display: none;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.mw3-toggle-slider {
|
|
280
|
+
width: 44px;
|
|
281
|
+
height: 24px;
|
|
282
|
+
background: rgba(255, 255, 255, 0.2);
|
|
283
|
+
border-radius: 12px;
|
|
284
|
+
position: relative;
|
|
285
|
+
transition: background 0.2s;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.mw3-toggle-slider::after {
|
|
289
|
+
content: '';
|
|
290
|
+
position: absolute;
|
|
291
|
+
top: 2px;
|
|
292
|
+
left: 2px;
|
|
293
|
+
width: 20px;
|
|
294
|
+
height: 20px;
|
|
295
|
+
background: #fff;
|
|
296
|
+
border-radius: 50%;
|
|
297
|
+
transition: transform 0.2s;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.mw3-settings-toggle input:checked + .mw3-toggle-slider {
|
|
301
|
+
background: rgba(218, 165, 32, 0.8);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.mw3-settings-toggle input:checked + .mw3-toggle-slider::after {
|
|
305
|
+
transform: translateX(20px);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.mw3-toggle-label {
|
|
309
|
+
font-size: 0.875rem;
|
|
310
|
+
color: rgba(255, 255, 255, 0.9);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.mw3-settings-info {
|
|
314
|
+
font-size: 0.75rem;
|
|
315
|
+
color: rgba(255, 255, 255, 0.5);
|
|
316
|
+
margin-bottom: 1rem;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.mw3-settings-note {
|
|
320
|
+
font-size: 0.75rem;
|
|
321
|
+
color: rgba(255, 255, 255, 0.4);
|
|
322
|
+
margin: 0;
|
|
323
|
+
font-style: italic;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.mw3-settings-btn {
|
|
327
|
+
padding: 0.625rem 1rem;
|
|
328
|
+
border-radius: 6px;
|
|
329
|
+
font-size: 0.875rem;
|
|
330
|
+
font-weight: 500;
|
|
331
|
+
cursor: pointer;
|
|
332
|
+
border: none;
|
|
333
|
+
transition: all 0.2s;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.mw3-settings-btn--primary {
|
|
337
|
+
background: rgba(218, 165, 32, 0.9);
|
|
338
|
+
color: #1a1a2e;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.mw3-settings-btn--primary:hover {
|
|
342
|
+
background: rgba(218, 165, 32, 1);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.mw3-settings-btn--danger {
|
|
346
|
+
background: rgba(255, 99, 71, 0.2);
|
|
347
|
+
color: rgba(255, 99, 71, 0.9);
|
|
348
|
+
border: 1px solid rgba(255, 99, 71, 0.3);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.mw3-settings-btn--danger:hover {
|
|
352
|
+
background: rgba(255, 99, 71, 0.3);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.mw3-settings-btn:disabled {
|
|
356
|
+
opacity: 0.5;
|
|
357
|
+
cursor: not-allowed;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.mw3-settings-footer {
|
|
361
|
+
padding: 1rem 1.25rem;
|
|
362
|
+
border-top: 1px solid rgba(218, 165, 32, 0.2);
|
|
363
|
+
display: flex;
|
|
364
|
+
justify-content: flex-end;
|
|
365
|
+
}
|
|
366
|
+
`;
|
|
367
|
+
document.head.appendChild(style);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export { SettingsModal };
|