ui-optago 1.0.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.
- package/README.md +136 -0
- package/package.json +26 -0
- package/ui-optago.css +1917 -0
- package/ui-optago.html +1209 -0
- package/ui-optago.js +446 -0
package/ui-optago.js
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
UI ÓPTAGO — Design System v1.0 (JavaScript)
|
|
3
|
+
Sem dependências de build. Carregue no <head> SEM defer para evitar
|
|
4
|
+
flash de tema errado (FOUC):
|
|
5
|
+
|
|
6
|
+
<link rel="stylesheet" href="ui-optago.css">
|
|
7
|
+
<script src="ui-optago.js"></script>
|
|
8
|
+
|
|
9
|
+
O script injeta automaticamente as fontes (Google Fonts) e os ícones
|
|
10
|
+
Phosphor, aplica o tema salvo e liga todos os comportamentos via
|
|
11
|
+
atributos data-op-*:
|
|
12
|
+
|
|
13
|
+
data-op-theme-toggle → alterna tema claro/escuro
|
|
14
|
+
data-op-sidebar-toggle → colapsa/expande a sidebar (desktop)
|
|
15
|
+
data-op-sidebar-mobile → abre/fecha a sidebar (mobile drawer)
|
|
16
|
+
data-op-modal-open="#id" → abre o modal
|
|
17
|
+
data-op-modal-close → fecha o modal (dentro dele)
|
|
18
|
+
data-op-tab="nome" → botão de aba (com data-op-tab-group no pai)
|
|
19
|
+
data-op-tab-panel="nome" → painel correspondente
|
|
20
|
+
data-op-dropdown-toggle → abre/fecha o .op-dropdown pai
|
|
21
|
+
data-op-dismiss → remove o elemento pai .op-alert
|
|
22
|
+
data-op-copy="#seletor" → copia o HTML do elemento alvo
|
|
23
|
+
data-op-code="#seletor" → mostra/esconde o código-fonte do alvo
|
|
24
|
+
|
|
25
|
+
API global: OptagoUI.toast(msg, tipo, ms) · OptagoUI.openModal(sel)
|
|
26
|
+
OptagoUI.closeModal(sel) · OptagoUI.setTheme('dark'|'light')
|
|
27
|
+
============================================================================ */
|
|
28
|
+
(function () {
|
|
29
|
+
'use strict';
|
|
30
|
+
|
|
31
|
+
var OptagoUI = {};
|
|
32
|
+
|
|
33
|
+
/* --------------------------------------------------------------------------
|
|
34
|
+
1. TEMA — aplicado imediatamente (antes do primeiro paint)
|
|
35
|
+
-------------------------------------------------------------------------- */
|
|
36
|
+
var THEME_KEY = 'op-theme';
|
|
37
|
+
|
|
38
|
+
function currentTheme() {
|
|
39
|
+
var saved = localStorage.getItem(THEME_KEY);
|
|
40
|
+
if (saved === 'light' || saved === 'dark') return saved;
|
|
41
|
+
return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
OptagoUI.setTheme = function (theme) {
|
|
45
|
+
document.documentElement.classList.toggle('light', theme === 'light');
|
|
46
|
+
localStorage.setItem(THEME_KEY, theme);
|
|
47
|
+
document.querySelectorAll('[data-op-theme-toggle]').forEach(function (btn) {
|
|
48
|
+
var moon = btn.querySelector('.op-icon-moon');
|
|
49
|
+
var sun = btn.querySelector('.op-icon-sun');
|
|
50
|
+
if (moon) moon.style.display = theme === 'dark' ? 'none' : '';
|
|
51
|
+
if (sun) sun.style.display = theme === 'dark' ? '' : 'none';
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
OptagoUI.toggleTheme = function () {
|
|
56
|
+
OptagoUI.setTheme(document.documentElement.classList.contains('light') ? 'dark' : 'light');
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Aplica já, sem esperar o DOM (evita FOUC)
|
|
60
|
+
document.documentElement.classList.toggle('light', currentTheme() === 'light');
|
|
61
|
+
|
|
62
|
+
/* --------------------------------------------------------------------------
|
|
63
|
+
2. INJEÇÃO DE DEPENDÊNCIAS (Google Fonts + Phosphor Icons)
|
|
64
|
+
-------------------------------------------------------------------------- */
|
|
65
|
+
function ensureAssets() {
|
|
66
|
+
if (!document.getElementById('op-fonts')) {
|
|
67
|
+
var pre1 = document.createElement('link');
|
|
68
|
+
pre1.rel = 'preconnect'; pre1.href = 'https://fonts.googleapis.com';
|
|
69
|
+
var pre2 = document.createElement('link');
|
|
70
|
+
pre2.rel = 'preconnect'; pre2.href = 'https://fonts.gstatic.com'; pre2.crossOrigin = '';
|
|
71
|
+
var fonts = document.createElement('link');
|
|
72
|
+
fonts.id = 'op-fonts';
|
|
73
|
+
fonts.rel = 'stylesheet';
|
|
74
|
+
fonts.href = 'https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@500;600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap';
|
|
75
|
+
document.head.append(pre1, pre2, fonts);
|
|
76
|
+
}
|
|
77
|
+
if (!document.getElementById('op-icons') && !window.PhosphorIcons) {
|
|
78
|
+
var icons = document.createElement('script');
|
|
79
|
+
icons.id = 'op-icons';
|
|
80
|
+
icons.src = 'https://unpkg.com/@phosphor-icons/web';
|
|
81
|
+
document.head.appendChild(icons);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
ensureAssets();
|
|
85
|
+
|
|
86
|
+
/* --------------------------------------------------------------------------
|
|
87
|
+
3. SIDEBAR (colapsar no desktop / drawer no mobile)
|
|
88
|
+
-------------------------------------------------------------------------- */
|
|
89
|
+
var SIDEBAR_KEY = 'op-sidebar-collapsed';
|
|
90
|
+
|
|
91
|
+
function getSidebar() { return document.querySelector('.op-sidebar'); }
|
|
92
|
+
function getOverlay() {
|
|
93
|
+
var ov = document.querySelector('.op-overlay');
|
|
94
|
+
if (!ov) {
|
|
95
|
+
ov = document.createElement('div');
|
|
96
|
+
ov.className = 'op-overlay';
|
|
97
|
+
ov.addEventListener('click', OptagoUI.closeSidebarMobile);
|
|
98
|
+
document.body.appendChild(ov);
|
|
99
|
+
}
|
|
100
|
+
return ov;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
OptagoUI.toggleSidebar = function () {
|
|
104
|
+
var sb = getSidebar();
|
|
105
|
+
if (!sb) return;
|
|
106
|
+
if (window.innerWidth < 1024) { OptagoUI.toggleSidebarMobile(); return; }
|
|
107
|
+
sb.classList.toggle('is-collapsed');
|
|
108
|
+
localStorage.setItem(SIDEBAR_KEY, sb.classList.contains('is-collapsed') ? '1' : '0');
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
OptagoUI.toggleSidebarMobile = function () {
|
|
112
|
+
var sb = getSidebar();
|
|
113
|
+
if (!sb) return;
|
|
114
|
+
var open = sb.classList.toggle('is-open');
|
|
115
|
+
getOverlay().classList.toggle('is-open', open);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
OptagoUI.closeSidebarMobile = function () {
|
|
119
|
+
var sb = getSidebar();
|
|
120
|
+
if (sb) sb.classList.remove('is-open');
|
|
121
|
+
var ov = document.querySelector('.op-overlay');
|
|
122
|
+
if (ov) ov.classList.remove('is-open');
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/* --------------------------------------------------------------------------
|
|
126
|
+
4. TOASTS — OptagoUI.toast('Mensagem', 'success'|'error'|'warning'|'info')
|
|
127
|
+
-------------------------------------------------------------------------- */
|
|
128
|
+
var TOAST_ICONS = {
|
|
129
|
+
success: 'ph-check-circle',
|
|
130
|
+
error: 'ph-x-circle',
|
|
131
|
+
danger: 'ph-x-circle',
|
|
132
|
+
warning: 'ph-warning',
|
|
133
|
+
info: 'ph-info'
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
OptagoUI.toast = function (message, type, duration) {
|
|
137
|
+
type = type || 'info';
|
|
138
|
+
if (type === 'error') type = 'danger';
|
|
139
|
+
duration = duration || 4000;
|
|
140
|
+
|
|
141
|
+
var container = document.querySelector('.op-toast-container');
|
|
142
|
+
if (!container) {
|
|
143
|
+
container = document.createElement('div');
|
|
144
|
+
container.className = 'op-toast-container';
|
|
145
|
+
document.body.appendChild(container);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
var toast = document.createElement('div');
|
|
149
|
+
toast.className = 'op-toast op-toast-' + type;
|
|
150
|
+
toast.setAttribute('role', 'status');
|
|
151
|
+
toast.innerHTML =
|
|
152
|
+
'<div class="op-toast-icon"><i class="ph ' + (TOAST_ICONS[type] || TOAST_ICONS.info) + '"></i></div>' +
|
|
153
|
+
'<span></span>' +
|
|
154
|
+
'<button class="op-toast-close" aria-label="Fechar"><i class="ph ph-x"></i></button>';
|
|
155
|
+
toast.querySelector('span').textContent = message;
|
|
156
|
+
container.appendChild(toast);
|
|
157
|
+
|
|
158
|
+
var removed = false;
|
|
159
|
+
var dismiss = function () {
|
|
160
|
+
if (removed) return;
|
|
161
|
+
removed = true;
|
|
162
|
+
toast.classList.remove('is-visible');
|
|
163
|
+
setTimeout(function () { toast.remove(); }, 320);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
toast.querySelector('.op-toast-close').addEventListener('click', dismiss);
|
|
167
|
+
|
|
168
|
+
requestAnimationFrame(function () {
|
|
169
|
+
requestAnimationFrame(function () { toast.classList.add('is-visible'); });
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
setTimeout(dismiss, duration);
|
|
173
|
+
|
|
174
|
+
return toast;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/* --------------------------------------------------------------------------
|
|
178
|
+
5. MODAIS
|
|
179
|
+
-------------------------------------------------------------------------- */
|
|
180
|
+
OptagoUI.openModal = function (selector) {
|
|
181
|
+
var modal = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
182
|
+
if (!modal) return;
|
|
183
|
+
modal.classList.remove('is-closing');
|
|
184
|
+
modal.classList.add('is-open');
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
OptagoUI.closeModal = function (selector) {
|
|
188
|
+
var modal = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
189
|
+
if (!modal || !modal.classList.contains('is-open')) return;
|
|
190
|
+
modal.classList.add('is-closing');
|
|
191
|
+
setTimeout(function () {
|
|
192
|
+
modal.classList.remove('is-open', 'is-closing');
|
|
193
|
+
}, 200);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/* --------------------------------------------------------------------------
|
|
197
|
+
5b. TABULATOR — tabela avançada com tema Óptago
|
|
198
|
+
Uso: OptagoUI.tabulator('#minhaTabela', { data: [...], columns: [...] })
|
|
199
|
+
Carrega a biblioteca (CDN) sob demanda; o CSS base do Tabulator é
|
|
200
|
+
inserido ANTES do ui-optago.css para que o tema personalizado
|
|
201
|
+
(.op-tabulator) vença na cascata.
|
|
202
|
+
-------------------------------------------------------------------------- */
|
|
203
|
+
var TABULATOR_JS = 'https://unpkg.com/tabulator-tables@6.3/dist/js/tabulator.min.js';
|
|
204
|
+
var TABULATOR_CSS = 'https://unpkg.com/tabulator-tables@6.3/dist/css/tabulator.min.css';
|
|
205
|
+
|
|
206
|
+
OptagoUI.tabulator = function (selector, options) {
|
|
207
|
+
return new Promise(function (resolve, reject) {
|
|
208
|
+
function build() {
|
|
209
|
+
var el = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
210
|
+
if (!el) { reject(new Error('Tabulator: elemento não encontrado: ' + selector)); return; }
|
|
211
|
+
el.classList.add('op-tabulator');
|
|
212
|
+
resolve(new window.Tabulator(el, options || {}));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (window.Tabulator) { build(); return; }
|
|
216
|
+
|
|
217
|
+
if (!document.getElementById('op-tabulator-css')) {
|
|
218
|
+
var css = document.createElement('link');
|
|
219
|
+
css.id = 'op-tabulator-css';
|
|
220
|
+
css.rel = 'stylesheet';
|
|
221
|
+
css.href = TABULATOR_CSS;
|
|
222
|
+
var own = document.querySelector('link[href*="ui-optago.css"]');
|
|
223
|
+
if (own && own.parentNode) {
|
|
224
|
+
own.parentNode.insertBefore(css, own);
|
|
225
|
+
} else {
|
|
226
|
+
document.head.prepend(css);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
var script = document.getElementById('op-tabulator-js');
|
|
231
|
+
if (!script) {
|
|
232
|
+
script = document.createElement('script');
|
|
233
|
+
script.id = 'op-tabulator-js';
|
|
234
|
+
script.src = TABULATOR_JS;
|
|
235
|
+
document.head.appendChild(script);
|
|
236
|
+
}
|
|
237
|
+
script.addEventListener('load', build);
|
|
238
|
+
script.addEventListener('error', function () {
|
|
239
|
+
reject(new Error('Tabulator: falha ao carregar a biblioteca via CDN.'));
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/* --------------------------------------------------------------------------
|
|
245
|
+
6. HELPERS DO SHOWCASE — copiar HTML / ver código
|
|
246
|
+
-------------------------------------------------------------------------- */
|
|
247
|
+
function dedentHTML(el) {
|
|
248
|
+
var html = el.innerHTML.replace(/^\n+|\s+$/g, '');
|
|
249
|
+
var lines = html.split('\n');
|
|
250
|
+
var indent = Infinity;
|
|
251
|
+
lines.forEach(function (line) {
|
|
252
|
+
if (!line.trim()) return;
|
|
253
|
+
var m = line.match(/^\s*/);
|
|
254
|
+
indent = Math.min(indent, m[0].length);
|
|
255
|
+
});
|
|
256
|
+
if (!isFinite(indent)) indent = 0;
|
|
257
|
+
return lines.map(function (line) { return line.slice(indent); }).join('\n');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
OptagoUI.copyHTML = function (targetSelector, feedbackBtn) {
|
|
261
|
+
var target = document.querySelector(targetSelector);
|
|
262
|
+
if (!target) return;
|
|
263
|
+
var html = dedentHTML(target);
|
|
264
|
+
|
|
265
|
+
var done = function () {
|
|
266
|
+
if (feedbackBtn) {
|
|
267
|
+
var original = feedbackBtn.innerHTML;
|
|
268
|
+
feedbackBtn.innerHTML = '<i class="ph ph-check"></i> Copiado!';
|
|
269
|
+
setTimeout(function () { feedbackBtn.innerHTML = original; }, 2000);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
274
|
+
navigator.clipboard.writeText(html).then(done);
|
|
275
|
+
} else {
|
|
276
|
+
var ta = document.createElement('textarea');
|
|
277
|
+
ta.value = html;
|
|
278
|
+
ta.style.position = 'fixed';
|
|
279
|
+
ta.style.left = '-9999px';
|
|
280
|
+
document.body.appendChild(ta);
|
|
281
|
+
ta.select();
|
|
282
|
+
document.execCommand('copy');
|
|
283
|
+
ta.remove();
|
|
284
|
+
done();
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
OptagoUI.toggleCode = function (targetSelector, btn) {
|
|
289
|
+
var target = document.querySelector(targetSelector);
|
|
290
|
+
if (!target) return;
|
|
291
|
+
var demo = btn.closest('.op-demo') || target.parentElement;
|
|
292
|
+
var pre = demo.querySelector('.op-demo-code');
|
|
293
|
+
if (!pre) {
|
|
294
|
+
pre = document.createElement('pre');
|
|
295
|
+
pre.className = 'op-demo-code';
|
|
296
|
+
demo.appendChild(pre);
|
|
297
|
+
}
|
|
298
|
+
if (!pre.dataset.filled) {
|
|
299
|
+
pre.textContent = dedentHTML(target);
|
|
300
|
+
pre.dataset.filled = '1';
|
|
301
|
+
}
|
|
302
|
+
pre.classList.toggle('is-visible');
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
/* --------------------------------------------------------------------------
|
|
306
|
+
7. BINDING GLOBAL via data-attributes (event delegation)
|
|
307
|
+
-------------------------------------------------------------------------- */
|
|
308
|
+
document.addEventListener('click', function (e) {
|
|
309
|
+
var t;
|
|
310
|
+
|
|
311
|
+
// Tema
|
|
312
|
+
if (e.target.closest('[data-op-theme-toggle]')) { OptagoUI.toggleTheme(); return; }
|
|
313
|
+
|
|
314
|
+
// Sidebar
|
|
315
|
+
if (e.target.closest('[data-op-sidebar-toggle]')) { OptagoUI.toggleSidebar(); return; }
|
|
316
|
+
if (e.target.closest('[data-op-sidebar-mobile]')) { OptagoUI.toggleSidebarMobile(); return; }
|
|
317
|
+
|
|
318
|
+
// Fecha drawer mobile ao clicar em link da sidebar
|
|
319
|
+
if (e.target.closest('.op-sidebar .op-nav-item') && window.innerWidth < 1024) {
|
|
320
|
+
OptagoUI.closeSidebarMobile();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Modal
|
|
324
|
+
t = e.target.closest('[data-op-modal-open]');
|
|
325
|
+
if (t) { OptagoUI.openModal(t.getAttribute('data-op-modal-open')); return; }
|
|
326
|
+
if (e.target.closest('[data-op-modal-close]')) {
|
|
327
|
+
OptagoUI.closeModal(e.target.closest('.op-modal'));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (e.target.classList && e.target.classList.contains('op-modal')) {
|
|
331
|
+
OptagoUI.closeModal(e.target); // clique no backdrop
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Tabs
|
|
336
|
+
t = e.target.closest('[data-op-tab]');
|
|
337
|
+
if (t) {
|
|
338
|
+
var name = t.getAttribute('data-op-tab');
|
|
339
|
+
var group = t.closest('[data-op-tab-group]');
|
|
340
|
+
var scope = group ? '[data-op-tab-group="' + group.getAttribute('data-op-tab-group') + '"]' : '';
|
|
341
|
+
document.querySelectorAll(scope + ' [data-op-tab]').forEach(function (tab) {
|
|
342
|
+
tab.classList.toggle('is-active', tab === t);
|
|
343
|
+
});
|
|
344
|
+
document.querySelectorAll(scope + ' [data-op-tab-panel]').forEach(function (panel) {
|
|
345
|
+
panel.classList.toggle('is-active', panel.getAttribute('data-op-tab-panel') === name);
|
|
346
|
+
});
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Dropdown
|
|
351
|
+
t = e.target.closest('[data-op-dropdown-toggle]');
|
|
352
|
+
if (t) {
|
|
353
|
+
var dd = t.closest('.op-dropdown');
|
|
354
|
+
var wasOpen = dd.classList.contains('is-open');
|
|
355
|
+
document.querySelectorAll('.op-dropdown.is-open').forEach(function (d) { d.classList.remove('is-open'); });
|
|
356
|
+
if (!wasOpen) dd.classList.add('is-open');
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
// Fecha dropdowns em clique fora
|
|
360
|
+
if (!e.target.closest('.op-dropdown')) {
|
|
361
|
+
document.querySelectorAll('.op-dropdown.is-open').forEach(function (d) { d.classList.remove('is-open'); });
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Alert dismiss
|
|
365
|
+
t = e.target.closest('[data-op-dismiss]');
|
|
366
|
+
if (t) {
|
|
367
|
+
var box = t.closest('.op-alert') || t.parentElement;
|
|
368
|
+
box.style.transition = 'opacity .25s, transform .25s';
|
|
369
|
+
box.style.opacity = '0';
|
|
370
|
+
box.style.transform = 'translateX(8px)';
|
|
371
|
+
setTimeout(function () { box.remove(); }, 250);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Copiar HTML
|
|
376
|
+
t = e.target.closest('[data-op-copy]');
|
|
377
|
+
if (t) { OptagoUI.copyHTML(t.getAttribute('data-op-copy'), t); return; }
|
|
378
|
+
|
|
379
|
+
// Ver código
|
|
380
|
+
t = e.target.closest('[data-op-code]');
|
|
381
|
+
if (t) { OptagoUI.toggleCode(t.getAttribute('data-op-code'), t); return; }
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Tooltip da sidebar colapsada: o overflow-y da nav cortaria um tooltip
|
|
385
|
+
// com position:absolute, então ele usa position:fixed e o top/right é
|
|
386
|
+
// calculado aqui a cada hover.
|
|
387
|
+
document.addEventListener('mouseover', function (e) {
|
|
388
|
+
var item = e.target.closest('.op-sidebar.is-collapsed .op-nav-item');
|
|
389
|
+
if (!item) return;
|
|
390
|
+
var tip = item.querySelector('.op-nav-tooltip');
|
|
391
|
+
if (!tip) return;
|
|
392
|
+
var r = item.getBoundingClientRect();
|
|
393
|
+
tip.style.top = (r.top + r.height / 2) + 'px';
|
|
394
|
+
tip.style.right = (window.innerWidth - r.left + 14) + 'px';
|
|
395
|
+
tip.style.left = 'auto';
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// ESC fecha modais e dropdowns
|
|
399
|
+
document.addEventListener('keydown', function (e) {
|
|
400
|
+
if (e.key !== 'Escape') return;
|
|
401
|
+
document.querySelectorAll('.op-modal.is-open').forEach(function (m) { OptagoUI.closeModal(m); });
|
|
402
|
+
document.querySelectorAll('.op-dropdown.is-open').forEach(function (d) { d.classList.remove('is-open'); });
|
|
403
|
+
OptagoUI.closeSidebarMobile();
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
/* --------------------------------------------------------------------------
|
|
407
|
+
8. INICIALIZAÇÃO no DOM pronto
|
|
408
|
+
-------------------------------------------------------------------------- */
|
|
409
|
+
function init() {
|
|
410
|
+
// Restaura estado da sidebar (desktop)
|
|
411
|
+
var sb = getSidebar();
|
|
412
|
+
if (sb && window.innerWidth >= 1024 && localStorage.getItem(SIDEBAR_KEY) === '1') {
|
|
413
|
+
sb.classList.add('is-collapsed');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Ajusta ícones do botão de tema
|
|
417
|
+
OptagoUI.setTheme(document.documentElement.classList.contains('light') ? 'light' : 'dark');
|
|
418
|
+
|
|
419
|
+
// Scrollspy simples: marca .op-nav-item ativo conforme a seção visível
|
|
420
|
+
var main = document.querySelector('.op-main');
|
|
421
|
+
var links = Array.prototype.slice.call(document.querySelectorAll('.op-sidebar .op-nav-item[href^="#"]'));
|
|
422
|
+
if (main && links.length) {
|
|
423
|
+
var sections = links
|
|
424
|
+
.map(function (l) { return document.querySelector(l.getAttribute('href')); })
|
|
425
|
+
.filter(Boolean);
|
|
426
|
+
var onScroll = function () {
|
|
427
|
+
var top = main.scrollTop + 120;
|
|
428
|
+
var active = sections[0];
|
|
429
|
+
sections.forEach(function (s) { if (s.offsetTop <= top) active = s; });
|
|
430
|
+
links.forEach(function (l) {
|
|
431
|
+
l.classList.toggle('is-active', active && l.getAttribute('href') === '#' + active.id);
|
|
432
|
+
});
|
|
433
|
+
};
|
|
434
|
+
main.addEventListener('scroll', onScroll, { passive: true });
|
|
435
|
+
onScroll();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (document.readyState === 'loading') {
|
|
440
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
441
|
+
} else {
|
|
442
|
+
init();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
window.OptagoUI = OptagoUI;
|
|
446
|
+
})();
|