ui-optago 1.0.1 → 1.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-optago",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Design System Óptago — componentes UI em CSS puro + JS vanilla, com tema claro/escuro, verde-lima (#C0FF6B) e app shell com sidebar colapsável.",
5
5
  "author": "Óptago <cowork@optago.com.br> (https://optago.com.br)",
6
6
  "license": "MIT",
package/ui-optago.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /* ============================================================================
2
- UI ÓPTAGO — Design System v1.0.1
2
+ UI ÓPTAGO — Design System v1.0.2
3
3
  CSS puro, sem dependências. Tema escuro por padrão.
4
4
  Para tema claro: adicione a classe "light" ao <html> (o ui-optago.js
5
5
  gerencia isso automaticamente via localStorage).
@@ -74,12 +74,12 @@
74
74
 
75
75
  /* Superfícies — TEMA CLARO */
76
76
  html.light {
77
- --op-bg: #EEF2F7;
78
- --op-panel: #FFFFFF;
79
- --op-panel-2: #F6F8FB;
80
- --op-panel-3: #EDF1F6;
81
- --op-border: #DFE6EE;
82
- --op-border-2: #C6D2DF;
77
+ --op-bg: #E3E9F0;
78
+ --op-panel: #F8FAFC;
79
+ --op-panel-2: #EEF2F7;
80
+ --op-panel-3: #E6EBF2;
81
+ --op-border: #D6DEE7;
82
+ --op-border-2: #BDC9D8;
83
83
  --op-text: #334155;
84
84
  --op-strong: #0F172A;
85
85
  --op-muted: #64748B;
@@ -100,9 +100,9 @@ html.light {
100
100
  /* Fundo do tema claro: gradiente suave com um toque de verde-lima no topo */
101
101
  html.light body {
102
102
  background:
103
- radial-gradient(900px 420px at 75% -10%, rgba(192,255,107,.30) 0%, transparent 60%),
104
- radial-gradient(700px 380px at 10% 110%, rgba(192,255,107,.14) 0%, transparent 60%),
105
- linear-gradient(180deg, #F2F5F9 0%, #E9EEF5 100%);
103
+ radial-gradient(900px 420px at 75% -10%, rgba(192,255,107,.20) 0%, transparent 60%),
104
+ radial-gradient(700px 380px at 10% 110%, rgba(192,255,107,.10) 0%, transparent 60%),
105
+ linear-gradient(180deg, #E7ECF2 0%, #DEE5EC 100%);
106
106
  }
107
107
 
108
108
  /* ----------------------------------------------------------------------------
@@ -140,7 +140,9 @@ h4 { font-size: 1rem; }
140
140
 
141
141
  p { margin: 0 0 1em; }
142
142
  a { color: var(--op-accent); }
143
- html.light a { color: #3F6212; }
143
+ html.light a:not(.op-btn) {
144
+ color: #3F6212;
145
+ }
144
146
  code, kbd { font-family: var(--op-font-mono); font-size: .85em; }
145
147
 
146
148
  kbd {
@@ -279,12 +281,12 @@ html.light .op-brand-mark::after { background: var(--op-accent); }
279
281
  .op-main {
280
282
  flex: 1;
281
283
  overflow-y: auto;
282
- padding: 2rem 1.5rem;
284
+ padding: 1.5rem;
283
285
  scroll-behavior: smooth;
284
286
  position: relative;
285
287
  z-index: 1; /* mantém o conteúdo acima dos .op-glow do fundo */
286
288
  }
287
- @media (min-width: 1024px) { .op-main { padding: 2.5rem; } }
289
+ @media (min-width: 1024px) { .op-main { padding: 1.75rem 2rem; } }
288
290
 
289
291
  .op-container { max-width: 1100px; margin: 0 auto; }
290
292
 
@@ -358,25 +360,28 @@ html.light .op-brand-mark::after { background: var(--op-accent); }
358
360
  align-items: center;
359
361
  gap: .75rem;
360
362
  padding: .625rem .75rem;
363
+ border: 1px solid transparent;
361
364
  border-radius: var(--op-radius);
362
365
  color: var(--op-text);
363
366
  text-decoration: none;
364
367
  font-weight: 600;
365
368
  font-size: .875rem;
366
369
  white-space: nowrap;
367
- transition: background .2s ease, color .2s ease;
370
+ transition: background .2s ease, color .2s ease, border-color .2s ease;
368
371
  }
369
372
  .op-nav-item i { font-size: 1.25rem; flex-shrink: 0; }
370
373
  .op-nav-item:hover {
371
374
  background: color-mix(in srgb, var(--op-strong) 6%, transparent);
375
+ border-color: var(--op-border-2);
372
376
  color: var(--op-strong);
373
377
  }
374
378
  .op-nav-item.is-active {
375
- background: color-mix(in srgb, var(--op-accent) 12%, transparent);
379
+ background: color-mix(in srgb, var(--op-accent) 15%, transparent);
380
+ border-color: color-mix(in srgb, var(--op-accent) 45%, transparent);
376
381
  color: var(--op-strong);
377
382
  box-shadow: inset -3px 0 0 var(--op-accent);
378
383
  }
379
- html.light .op-nav-item.is-active { box-shadow: inset -3px 0 0 #84CC16; }
384
+ html.light .op-nav-item.is-active { box-shadow: inset -3px 0 0 #84CC16; border-color: color-mix(in srgb, #84CC16 45%, transparent); }
380
385
  .op-nav-item.is-active i { color: var(--op-accent); }
381
386
  html.light .op-nav-item.is-active i { color: #3F6212; }
382
387
 
@@ -385,7 +390,7 @@ html.light .op-nav-item.is-active i { color: #3F6212; }
385
390
  .op-sidebar.is-collapsed .op-nav-text,
386
391
  .op-sidebar.is-collapsed .op-sidebar-label,
387
392
  .op-sidebar.is-collapsed .op-sidebar-header-text { display: none; }
388
- .op-sidebar.is-collapsed .op-nav-item { justify-content: center; padding-left: 0; padding-right: 0; }
393
+ .op-sidebar.is-collapsed .op-nav-item { justify-content: center; padding: .625rem; }
389
394
 
390
395
  /* Tooltip do item (aparece à esquerda quando colapsada).
391
396
  position:fixed — o JS calcula top/right no hover, pois o overflow-y:auto
@@ -1489,17 +1494,53 @@ html.light .op-modal { background: rgba(15,23,42,.5); }
1489
1494
  .op-toast-danger .op-toast-icon { background: color-mix(in srgb, var(--op-danger) 15%, transparent); color: var(--op-danger); border-color: color-mix(in srgb, var(--op-danger) 30%, transparent); }
1490
1495
 
1491
1496
  /* ----------------------------------------------------------------------------
1492
- 20. BLOCO DE DEMONSTRAÇÃO (showcase: preview + copiar + ver código)
1497
+ 20. TELA DE CARREGAMENTO (overlay em tela cheia)
1498
+ Uso: OptagoUI.showLoading('Mensagem...') / OptagoUI.hideLoading()
1499
+ ou data-op-loading-form no <form> para acionar sozinho no submit.
1500
+ ---------------------------------------------------------------------------- */
1501
+ .op-loading-overlay {
1502
+ position: fixed;
1503
+ inset: 0;
1504
+ z-index: 150;
1505
+ display: none;
1506
+ flex-direction: column;
1507
+ align-items: center;
1508
+ justify-content: center;
1509
+ gap: 1.25rem;
1510
+ background: color-mix(in srgb, var(--op-bg) 85%, transparent);
1511
+ backdrop-filter: blur(8px);
1512
+ -webkit-backdrop-filter: blur(8px);
1513
+ opacity: 0;
1514
+ transition: opacity .25s var(--op-ease);
1515
+ }
1516
+ .op-loading-overlay.is-open { display: flex; }
1517
+ .op-loading-overlay.is-visible { opacity: 1; }
1518
+ .op-loading-overlay .op-spinner { width: 46px; height: 46px; border-width: 3px; }
1519
+ .op-loading-message {
1520
+ max-width: 320px;
1521
+ padding: 0 1.5rem;
1522
+ font-family: var(--op-font-mono);
1523
+ font-size: .875rem;
1524
+ font-weight: 600;
1525
+ letter-spacing: .01em;
1526
+ text-align: center;
1527
+ color: var(--op-strong);
1528
+ }
1529
+
1530
+ /* ----------------------------------------------------------------------------
1531
+ 21. BLOCO DE DEMONSTRAÇÃO (showcase: preview + copiar + ver código)
1493
1532
  ---------------------------------------------------------------------------- */
1494
1533
  .op-demo {
1495
1534
  background: var(--op-panel);
1496
1535
  border: 1px solid var(--op-border);
1497
1536
  border-radius: var(--op-radius-lg);
1498
- overflow: hidden;
1499
1537
  box-shadow: var(--op-shadow);
1500
1538
  }
1539
+ /* Sem overflow:hidden no container: dropdowns/tooltips exibidos dentro do
1540
+ preview não podem ser cortados. O arredondamento é feito nos filhos. */
1501
1541
  .op-demo-preview {
1502
- padding: 2rem;
1542
+ padding: 1.5rem;
1543
+ border-radius: var(--op-radius-lg) var(--op-radius-lg) 0 0;
1503
1544
  background:
1504
1545
  radial-gradient(color-mix(in srgb, var(--op-border) 55%, transparent) 1px, transparent 1px);
1505
1546
  background-size: 22px 22px;
@@ -1511,7 +1552,11 @@ html.light .op-modal { background: rgba(15,23,42,.5); }
1511
1552
  padding: .625rem .875rem;
1512
1553
  border-top: 1px solid var(--op-border);
1513
1554
  background: var(--op-panel-2);
1555
+ border-radius: 0 0 var(--op-radius-lg) var(--op-radius-lg);
1514
1556
  }
1557
+ /* Quando o código-fonte está visível, ele passa a ser o último bloco:
1558
+ a barra perde o arredondamento inferior e o código assume. */
1559
+ .op-demo-bar:has(+ .op-demo-code.is-visible) { border-radius: 0; }
1515
1560
  .op-demo-code {
1516
1561
  display: none;
1517
1562
  margin: 0;
@@ -1525,6 +1570,7 @@ html.light .op-modal { background: rgba(15,23,42,.5); }
1525
1570
  overflow-x: auto;
1526
1571
  tab-size: 2;
1527
1572
  white-space: pre;
1573
+ border-radius: 0 0 var(--op-radius-lg) var(--op-radius-lg);
1528
1574
  }
1529
1575
  .op-demo-code.is-visible { display: block; }
1530
1576
 
@@ -1533,14 +1579,14 @@ html.light .op-modal { background: rgba(15,23,42,.5); }
1533
1579
  display: flex;
1534
1580
  align-items: center;
1535
1581
  gap: .75rem;
1536
- padding-bottom: 1rem;
1537
- margin-bottom: 1.5rem;
1582
+ padding-bottom: .75rem;
1583
+ margin-bottom: 1.125rem;
1538
1584
  border-bottom: 1px solid var(--op-border);
1539
1585
  }
1540
1586
  .op-section-head i {
1541
- font-size: 1.375rem;
1587
+ font-size: 1.25rem;
1542
1588
  color: var(--op-accent);
1543
- width: 44px; height: 44px;
1589
+ width: 38px; height: 38px;
1544
1590
  display: flex;
1545
1591
  align-items: center;
1546
1592
  justify-content: center;
@@ -1867,7 +1913,7 @@ html.light .op-tabulator.tabulator .tabulator-footer .tabulator-page.active {
1867
1913
  .op-tabulator .op-progress { min-width: 90px; }
1868
1914
 
1869
1915
  /* ----------------------------------------------------------------------------
1870
- 21. UTILITÁRIOS
1916
+ 22. UTILITÁRIOS
1871
1917
  ---------------------------------------------------------------------------- */
1872
1918
  .op-row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; }
1873
1919
  .op-stack { display: flex; flex-direction: column; gap: 1rem; }
@@ -1886,11 +1932,11 @@ html.light .op-text-accent { color: #3F6212; }
1886
1932
  .op-text-sm { font-size: .8125rem; }
1887
1933
  .op-text-xs { font-size: .6875rem; }
1888
1934
 
1889
- .op-mt-1 { margin-top: .5rem; } .op-mt-2 { margin-top: 1rem; } .op-mt-3 { margin-top: 1.5rem; } .op-mt-4 { margin-top: 2.5rem; }
1890
- .op-mb-1 { margin-bottom: .5rem; } .op-mb-2 { margin-bottom: 1rem; } .op-mb-3 { margin-bottom: 1.5rem; } .op-mb-4 { margin-bottom: 2.5rem; }
1935
+ .op-mt-1 { margin-top: .5rem; } .op-mt-2 { margin-top: 1rem; } .op-mt-3 { margin-top: 1.5rem; } .op-mt-4 { margin-top: 1.75rem; }
1936
+ .op-mb-1 { margin-bottom: .5rem; } .op-mb-2 { margin-bottom: 1rem; } .op-mb-3 { margin-bottom: 1.5rem; } .op-mb-4 { margin-bottom: 1.75rem; }
1891
1937
 
1892
1938
  /* ----------------------------------------------------------------------------
1893
- 22. ANIMAÇÕES
1939
+ 23. ANIMAÇÕES
1894
1940
  ---------------------------------------------------------------------------- */
1895
1941
  @keyframes op-spin { to { transform: rotate(360deg); } }
1896
1942
  @keyframes op-bounce {
package/ui-optago.html CHANGED
@@ -26,8 +26,8 @@
26
26
  <!-- Favicon: octógono verde-lima da marca, embutido em SVG (sem arquivo externo) -->
27
27
  <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><polygon points=%2230,4 70,4 96,30 96,70 70,96 30,96 4,70 4,30%22 fill=%22%23C0FF6B%22/></svg>">
28
28
 
29
- <link rel="stylesheet" href="ui-optago.css">
30
- <script src="ui-optago.js"></script>
29
+ <link rel="stylesheet" href="./ui-optago.css">
30
+ <script src="./ui-optago.js"></script>
31
31
  </head>
32
32
 
33
33
  <body class="op-app">
@@ -50,7 +50,7 @@
50
50
  <div class="op-header-divider"></div>
51
51
  <div class="op-header-group">
52
52
  <strong style="font-family:var(--op-font-display); font-size:.875rem;">UI</strong>
53
- <span class="op-badge op-badge-brand op-badge-pill"><span class="op-dot op-dot-pulse"></span>v1.0.1</span>
53
+ <span class="op-badge op-badge-brand op-badge-pill"><span class="op-dot op-dot-pulse"></span>v1.0.2</span>
54
54
  </div>
55
55
  </div>
56
56
  <div class="op-header-group">
@@ -154,7 +154,7 @@
154
154
  <p>Parágrafo padrão em <strong>Inter</strong>. Criamos e mantemos produtos próprios e sob medida,
155
155
  sempre com a mesma régua: proteger dados, reduzir atrito e manter tudo no ar.</p>
156
156
  <p class="op-text-muted">Texto secundário / muted para descrições e apoio.</p>
157
- <p class="op-text-mono op-text-sm">JetBrains Mono dados técnicos, códigos e rótulos. <span class="op-text-accent">optago.com.br · 0 cookies</span></p>
157
+ <p class="op-text-mono op-text-sm">JetBrains Mono dados técnicos, códigos e rótulos. <span class="op-text-accent">optago.com.br</span></p>
158
158
  <p>Atalho de teclado: <kbd>Ctrl</kbd> + <kbd>K</kbd> · código inline: <code>--op-accent: #C0FF6B</code></p>
159
159
  </div>
160
160
  <div class="op-demo-bar">
@@ -739,7 +739,7 @@ OptagoUI.tabulator('#minhaTabela', {
739
739
  </details>
740
740
  <details>
741
741
  <summary>O site usa cookies?</summary>
742
- <div class="op-accordion-body">Não. Coleta mínima de dados: o site institucional não usa cookies nem rastreadores.</div>
742
+ <div class="op-accordion-body">Usamos apenas 1 cookie, para lembrar se você prefere o tema claro ou escuro. Nenhum rastreador ou coleta de dados além disso.</div>
743
743
  </details>
744
744
  <details>
745
745
  <summary>Como funciona o suporte?</summary>
@@ -937,7 +937,34 @@ OptagoUI.tabulator('#minhaTabela', {
937
937
  </div>
938
938
  </section>
939
939
 
940
- <!-- ==================== 17. MODAIS ==================== -->
940
+ <!-- ==================== 17. CARREGAMENTO ==================== -->
941
+ <section id="carregamento" class="op-mb-4" style="scroll-margin-top:1rem;">
942
+ <div class="op-section-head">
943
+ <i class="ph ph-circle-notch"></i>
944
+ <h2>Tela de carregamento</h2>
945
+ </div>
946
+ <div class="op-demo">
947
+ <div class="op-demo-preview" id="d-carregamento">
948
+ <p class="op-text-muted op-text-sm" style="margin-top:0;">Overlay em tela cheia para usar ao submeter formulários ou aguardar uma ação. API: <code>OptagoUI.showLoading(mensagem)</code> / <code>OptagoUI.hideLoading()</code>.</p>
949
+ <div class="op-row">
950
+ <button class="op-btn op-btn-primary" onclick="OptagoUI.showLoading('Salvando alterações...'); setTimeout(OptagoUI.hideLoading, 2200)">
951
+ <i class="ph ph-floppy-disk"></i> Disparar via JS
952
+ </button>
953
+ <form data-op-loading-form data-op-loading-message="Enviando formulário..." onsubmit="event.preventDefault(); setTimeout(OptagoUI.hideLoading, 2200)">
954
+ <button class="op-btn op-btn-secondary" type="submit">
955
+ <i class="ph ph-paper-plane-tilt"></i> Disparar via submit do form
956
+ </button>
957
+ </form>
958
+ </div>
959
+ </div>
960
+ <div class="op-demo-bar">
961
+ <button class="op-btn op-btn-ghost op-btn-sm" data-op-code="#d-carregamento"><i class="ph ph-code"></i> Ver código</button>
962
+ <button class="op-btn op-btn-secondary op-btn-sm" data-op-copy="#d-carregamento"><i class="ph ph-copy"></i> Copiar</button>
963
+ </div>
964
+ </div>
965
+ </section>
966
+
967
+ <!-- ==================== 18. MODAIS ==================== -->
941
968
  <section id="modais" class="op-mb-4" style="scroll-margin-top:1rem;">
942
969
  <div class="op-section-head">
943
970
  <i class="ph ph-app-window"></i>
@@ -970,7 +997,7 @@ OptagoUI.tabulator('#minhaTabela', {
970
997
  </div>
971
998
  </section>
972
999
 
973
- <!-- ==================== 18. LAYOUT ==================== -->
1000
+ <!-- ==================== 19. LAYOUT ==================== -->
974
1001
  <section id="layout" class="op-mb-4" style="scroll-margin-top:1rem;">
975
1002
  <div class="op-section-head">
976
1003
  <i class="ph ph-layout"></i>
@@ -1010,8 +1037,8 @@ OptagoUI.tabulator('#minhaTabela', {
1010
1037
 
1011
1038
  <!-- FOOTER (fixo) -->
1012
1039
  <footer class="op-footer">
1013
- <span>ÓPTAGO © 2026 Design System v1.0.1</span>
1014
- <span><a href="https://optago.com.br" target="_blank" rel="noopener">optago.com.br</a> · 0 cookies</span>
1040
+ <span>ÓPTAGO © 2026 Design System v1.0.2</span>
1041
+ <span><a href="https://optago.com.br" target="_blank" rel="noopener">optago.com.br</a> · 1 cookie (só p/ tema)</span>
1015
1042
  </footer>
1016
1043
  </div>
1017
1044
 
@@ -1045,6 +1072,7 @@ OptagoUI.tabulator('#minhaTabela', {
1045
1072
  <a class="op-nav-item" href="#efeitos"><i class="ph ph-sparkle"></i><span class="op-nav-text">Efeitos / Glow</span><span class="op-nav-tooltip">Efeitos / Glow</span></a>
1046
1073
  <div class="op-sidebar-label">Interativos</div>
1047
1074
  <a class="op-nav-item" href="#toasts"><i class="ph ph-bell-ringing"></i><span class="op-nav-text">Toasts</span><span class="op-nav-tooltip">Toasts</span></a>
1075
+ <a class="op-nav-item" href="#carregamento"><i class="ph ph-circle-notch"></i><span class="op-nav-text">Carregamento</span><span class="op-nav-tooltip">Carregamento</span></a>
1048
1076
  <a class="op-nav-item" href="#modais"><i class="ph ph-app-window"></i><span class="op-nav-text">Modais</span><span class="op-nav-tooltip">Modais</span></a>
1049
1077
  <a class="op-nav-item" href="#layout"><i class="ph ph-layout"></i><span class="op-nav-text">Layout</span><span class="op-nav-tooltip">Layout</span></a>
1050
1078
  </nav>
package/ui-optago.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* ============================================================================
2
- UI ÓPTAGO — Design System v1.0.1 (JavaScript)
2
+ UI ÓPTAGO — Design System v1.0.2 (JavaScript)
3
3
  Sem dependências de build. Carregue no <head> SEM defer para evitar
4
4
  flash de tema errado (FOUC):
5
5
 
@@ -21,9 +21,13 @@
21
21
  data-op-dismiss → remove o elemento pai .op-alert
22
22
  data-op-copy="#seletor" → copia o HTML do elemento alvo
23
23
  data-op-code="#seletor" → mostra/esconde o código-fonte do alvo
24
+ data-op-loading-form → no <form>: mostra a tela de carregamento
25
+ sozinho ao submeter (mensagem opcional em
26
+ data-op-loading-message="Enviando...")
24
27
 
25
28
  API global: OptagoUI.toast(msg, tipo, ms) · OptagoUI.openModal(sel)
26
29
  OptagoUI.closeModal(sel) · OptagoUI.setTheme('dark'|'light')
30
+ OptagoUI.showLoading(msg) · OptagoUI.hideLoading()
27
31
  ============================================================================ */
28
32
  (function () {
29
33
  'use strict';
@@ -241,6 +245,45 @@
241
245
  });
242
246
  };
243
247
 
248
+ /* --------------------------------------------------------------------------
249
+ 5c. TELA DE CARREGAMENTO — overlay em tela cheia
250
+ Uso: OptagoUI.showLoading('Salvando...') ... OptagoUI.hideLoading()
251
+ ou data-op-loading-form no <form> para acionar sozinho no submit.
252
+ -------------------------------------------------------------------------- */
253
+ var LOADING_ID = 'opLoadingOverlay';
254
+
255
+ function getLoadingOverlay() {
256
+ var ov = document.getElementById(LOADING_ID);
257
+ if (!ov) {
258
+ ov = document.createElement('div');
259
+ ov.id = LOADING_ID;
260
+ ov.className = 'op-loading-overlay';
261
+ ov.setAttribute('role', 'alert');
262
+ ov.setAttribute('aria-live', 'assertive');
263
+ ov.innerHTML =
264
+ '<span class="op-spinner"></span>' +
265
+ '<span class="op-loading-message"></span>';
266
+ document.body.appendChild(ov);
267
+ }
268
+ return ov;
269
+ }
270
+
271
+ OptagoUI.showLoading = function (message) {
272
+ var ov = getLoadingOverlay();
273
+ ov.querySelector('.op-loading-message').textContent = message || 'Carregando...';
274
+ ov.classList.add('is-open');
275
+ requestAnimationFrame(function () {
276
+ requestAnimationFrame(function () { ov.classList.add('is-visible'); });
277
+ });
278
+ };
279
+
280
+ OptagoUI.hideLoading = function () {
281
+ var ov = document.getElementById(LOADING_ID);
282
+ if (!ov) return;
283
+ ov.classList.remove('is-visible');
284
+ setTimeout(function () { ov.classList.remove('is-open'); }, 250);
285
+ };
286
+
244
287
  /* --------------------------------------------------------------------------
245
288
  6. HELPERS DO SHOWCASE — copiar HTML / ver código
246
289
  -------------------------------------------------------------------------- */
@@ -395,6 +438,14 @@
395
438
  tip.style.left = 'auto';
396
439
  });
397
440
 
441
+ // Formulário com data-op-loading-form: mostra a tela de carregamento
442
+ // sozinho ao submeter (mensagem em data-op-loading-message, se houver).
443
+ document.addEventListener('submit', function (e) {
444
+ var form = e.target.closest('[data-op-loading-form]');
445
+ if (!form) return;
446
+ OptagoUI.showLoading(form.getAttribute('data-op-loading-message'));
447
+ });
448
+
398
449
  // ESC fecha modais e dropdowns
399
450
  document.addEventListener('keydown', function (e) {
400
451
  if (e.key !== 'Escape') return;