@yakuzaa/jade 0.1.12 → 0.1.13
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/commands/html.js +155 -76
- package/package.json +2 -2
package/commands/html.js
CHANGED
|
@@ -95,47 +95,74 @@ function gerarCSS(tema = {}) {
|
|
|
95
95
|
font-family: var(--jade-fonte);
|
|
96
96
|
background: var(--jade-cor-fundo);
|
|
97
97
|
color: var(--jade-cor-texto);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/* Layout principal — ocupa toda a tela, sem rolar o body */
|
|
101
|
-
#jade-app {
|
|
102
|
-
display: flex;
|
|
103
|
-
height: 100dvh;
|
|
104
98
|
overflow: hidden;
|
|
105
99
|
}
|
|
106
100
|
|
|
107
|
-
/*
|
|
108
|
-
#jade-
|
|
109
|
-
|
|
110
|
-
|
|
101
|
+
/* ── Header fixo ─────────────────────────────── */
|
|
102
|
+
#jade-header {
|
|
103
|
+
position: fixed;
|
|
104
|
+
top: 0; left: 0; right: 0;
|
|
105
|
+
height: 52px;
|
|
111
106
|
background: var(--jade-cor-fundo-nav);
|
|
112
107
|
display: flex;
|
|
113
|
-
|
|
108
|
+
align-items: center;
|
|
109
|
+
gap: 10px;
|
|
110
|
+
padding: 0 16px 0 8px;
|
|
111
|
+
z-index: 300;
|
|
112
|
+
box-shadow: 0 1px 0 rgba(255,255,255,0.06), 0 2px 8px rgba(0,0,0,0.2);
|
|
114
113
|
flex-shrink: 0;
|
|
115
|
-
overflow-y: auto;
|
|
116
|
-
z-index: 10;
|
|
117
114
|
}
|
|
118
115
|
|
|
119
|
-
#jade-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
#jade-hamburger {
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
justify-content: center;
|
|
120
|
+
width: 40px;
|
|
121
|
+
height: 40px;
|
|
122
122
|
flex-shrink: 0;
|
|
123
|
+
border: none;
|
|
124
|
+
border-radius: var(--jade-raio);
|
|
125
|
+
background: transparent;
|
|
126
|
+
color: rgba(255,255,255,0.85);
|
|
127
|
+
cursor: pointer;
|
|
128
|
+
transition: background 0.15s;
|
|
123
129
|
}
|
|
130
|
+
#jade-hamburger:hover { background: rgba(255,255,255,0.1); }
|
|
124
131
|
|
|
125
|
-
#jade-
|
|
132
|
+
#jade-header-titulo {
|
|
126
133
|
font-size: 0.875rem;
|
|
127
134
|
font-weight: 700;
|
|
128
135
|
color: #fff;
|
|
129
|
-
letter-spacing: 0.
|
|
136
|
+
letter-spacing: 0.05em;
|
|
130
137
|
text-transform: uppercase;
|
|
138
|
+
white-space: nowrap;
|
|
139
|
+
overflow: hidden;
|
|
140
|
+
text-overflow: ellipsis;
|
|
131
141
|
}
|
|
132
142
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
143
|
+
/* ── Layout principal (abaixo do header) ─────── */
|
|
144
|
+
#jade-app {
|
|
145
|
+
display: flex;
|
|
146
|
+
height: calc(100dvh - 52px);
|
|
147
|
+
margin-top: 52px;
|
|
148
|
+
overflow: hidden;
|
|
137
149
|
}
|
|
138
150
|
|
|
151
|
+
/* ── Nav lateral ─────────────────────────────── */
|
|
152
|
+
#jade-nav {
|
|
153
|
+
width: 240px;
|
|
154
|
+
height: 100%;
|
|
155
|
+
background: var(--jade-cor-fundo-nav);
|
|
156
|
+
display: flex;
|
|
157
|
+
flex-direction: column;
|
|
158
|
+
flex-shrink: 0;
|
|
159
|
+
overflow-y: auto;
|
|
160
|
+
transition: width 0.2s ease;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* O nav-header foi movido para o #jade-header global */
|
|
164
|
+
#jade-nav-header { display: none; }
|
|
165
|
+
|
|
139
166
|
#jade-nav-lista {
|
|
140
167
|
flex: 1;
|
|
141
168
|
padding: 8px;
|
|
@@ -162,20 +189,17 @@ function gerarCSS(tema = {}) {
|
|
|
162
189
|
transition: background 0.15s, color 0.15s;
|
|
163
190
|
flex-shrink: 0;
|
|
164
191
|
}
|
|
165
|
-
|
|
166
192
|
.jade-nav-item:hover {
|
|
167
193
|
background: rgba(255,255,255,0.07);
|
|
168
194
|
color: rgba(255,255,255,0.9);
|
|
169
195
|
}
|
|
170
|
-
|
|
171
196
|
.jade-nav-ativo {
|
|
172
197
|
background: var(--jade-cor-primaria) !important;
|
|
173
198
|
color: #fff !important;
|
|
174
199
|
}
|
|
175
|
-
|
|
176
200
|
.jade-nav-icone { display: flex; align-items: center; }
|
|
177
201
|
|
|
178
|
-
/* Área de conteúdo
|
|
202
|
+
/* ── Área de conteúdo ────────────────────────── */
|
|
179
203
|
#jade-conteudo {
|
|
180
204
|
flex: 1;
|
|
181
205
|
min-width: 0;
|
|
@@ -185,11 +209,12 @@ function gerarCSS(tema = {}) {
|
|
|
185
209
|
overflow-x: hidden;
|
|
186
210
|
}
|
|
187
211
|
|
|
188
|
-
/* Overlay
|
|
212
|
+
/* ── Overlay drawer (mobile) ─────────────────── */
|
|
189
213
|
#jade-overlay {
|
|
190
214
|
display: none;
|
|
191
215
|
position: fixed;
|
|
192
216
|
inset: 0;
|
|
217
|
+
top: 52px;
|
|
193
218
|
background: rgba(0,0,0,0.45);
|
|
194
219
|
z-index: 199;
|
|
195
220
|
opacity: 0;
|
|
@@ -200,26 +225,6 @@ function gerarCSS(tema = {}) {
|
|
|
200
225
|
opacity: 1;
|
|
201
226
|
}
|
|
202
227
|
|
|
203
|
-
/* Botão hambúrguer — só aparece no mobile */
|
|
204
|
-
#jade-hamburger {
|
|
205
|
-
display: none;
|
|
206
|
-
position: fixed;
|
|
207
|
-
top: 12px;
|
|
208
|
-
left: 12px;
|
|
209
|
-
z-index: 198;
|
|
210
|
-
width: 40px;
|
|
211
|
-
height: 40px;
|
|
212
|
-
border: none;
|
|
213
|
-
border-radius: var(--jade-raio);
|
|
214
|
-
background: var(--jade-cor-fundo-nav);
|
|
215
|
-
color: #fff;
|
|
216
|
-
cursor: pointer;
|
|
217
|
-
align-items: center;
|
|
218
|
-
justify-content: center;
|
|
219
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
|
220
|
-
flex-shrink: 0;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
228
|
/* Toolbar */
|
|
224
229
|
.jade-toolbar {
|
|
225
230
|
display: flex;
|
|
@@ -302,28 +307,29 @@ function gerarCSS(tema = {}) {
|
|
|
302
307
|
@keyframes jade-giro { to { transform: rotate(360deg); } }
|
|
303
308
|
|
|
304
309
|
/* Mobile: hamburger + drawer overlay */
|
|
305
|
-
|
|
306
|
-
|
|
310
|
+
/* ── Desktop: sidebar pode ser colapsada ─────── */
|
|
311
|
+
#jade-nav.jade-nav-colapsada {
|
|
312
|
+
width: 0;
|
|
313
|
+
overflow: hidden;
|
|
314
|
+
}
|
|
307
315
|
|
|
316
|
+
/* ── Mobile: drawer slide-in (abaixo do header) ─ */
|
|
317
|
+
@media (max-width: 768px) {
|
|
308
318
|
#jade-nav {
|
|
309
319
|
position: fixed;
|
|
310
|
-
top:
|
|
320
|
+
top: 52px;
|
|
311
321
|
left: 0;
|
|
312
|
-
height: 100dvh;
|
|
322
|
+
height: calc(100dvh - 52px);
|
|
313
323
|
z-index: 200;
|
|
314
324
|
transform: translateX(-100%);
|
|
315
325
|
transition: transform 0.25s ease;
|
|
316
326
|
box-shadow: 4px 0 16px rgba(0,0,0,0.3);
|
|
327
|
+
width: 240px !important;
|
|
317
328
|
}
|
|
318
|
-
|
|
319
329
|
#jade-nav.jade-nav-aberto {
|
|
320
330
|
transform: translateX(0);
|
|
321
331
|
}
|
|
322
|
-
|
|
323
|
-
#jade-conteudo {
|
|
324
|
-
padding: 16px;
|
|
325
|
-
padding-top: 64px;
|
|
326
|
-
}
|
|
332
|
+
#jade-conteudo { padding: 16px; }
|
|
327
333
|
}
|
|
328
334
|
`.trim();
|
|
329
335
|
}
|
|
@@ -374,31 +380,98 @@ function coletarEntidades(telas) {
|
|
|
374
380
|
for (const el of tela.elementos || []) {
|
|
375
381
|
for (const prop of el.propriedades || []) {
|
|
376
382
|
if (prop.chave === 'entidade' && prop.valor) nomes.add(String(prop.valor));
|
|
383
|
+
// Agrega referências em marcadores @funcao:Entidade:campo
|
|
384
|
+
if (typeof prop.valor === 'string' && prop.valor.startsWith('@')) {
|
|
385
|
+
const partes = prop.valor.slice(1).split(':');
|
|
386
|
+
if (partes[1]) nomes.add(partes[1]);
|
|
387
|
+
}
|
|
377
388
|
}
|
|
378
389
|
}
|
|
379
390
|
}
|
|
380
391
|
return [...nomes];
|
|
381
392
|
}
|
|
382
393
|
|
|
394
|
+
// Formata valor numérico de acordo com o campo (moeda vs número simples)
|
|
395
|
+
function formatarValor(v, campo) {
|
|
396
|
+
if (typeof v !== 'number' || isNaN(v)) return String(v ?? '');
|
|
397
|
+
const campoLower = (campo || '').toLowerCase();
|
|
398
|
+
// Campos monetários
|
|
399
|
+
if (/preco|total|valor|custo|receita|despesa|salario|pagamento|desconto|subtotal|moeda/.test(campoLower)) {
|
|
400
|
+
return v.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
|
|
401
|
+
}
|
|
402
|
+
// Inteiro
|
|
403
|
+
if (Number.isInteger(v)) return v.toLocaleString('pt-BR');
|
|
404
|
+
return v.toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Resolve marcadores @funcao:Entidade:campo nos descritores de tela
|
|
408
|
+
function resolverAgregacoes(tela, dadosMap) {
|
|
409
|
+
for (const el of tela.elementos || []) {
|
|
410
|
+
for (const prop of el.propriedades) {
|
|
411
|
+
if (typeof prop.valor !== 'string' || !prop.valor.startsWith('@')) continue;
|
|
412
|
+
const [funcao, entidade, campo] = prop.valor.slice(1).split(':');
|
|
413
|
+
const registros = dadosMap[entidade] ?? [];
|
|
414
|
+
let resultado;
|
|
415
|
+
switch (funcao) {
|
|
416
|
+
case 'soma':
|
|
417
|
+
resultado = registros.reduce((s, r) => s + (Number(r[campo]) || 0), 0);
|
|
418
|
+
prop.valor = formatarValor(resultado, campo);
|
|
419
|
+
break;
|
|
420
|
+
case 'contagem':
|
|
421
|
+
resultado = registros.length;
|
|
422
|
+
prop.valor = resultado.toLocaleString('pt-BR');
|
|
423
|
+
break;
|
|
424
|
+
case 'media':
|
|
425
|
+
resultado = registros.length
|
|
426
|
+
? registros.reduce((s, r) => s + (Number(r[campo]) || 0), 0) / registros.length
|
|
427
|
+
: 0;
|
|
428
|
+
prop.valor = formatarValor(resultado, campo);
|
|
429
|
+
break;
|
|
430
|
+
case 'maximo':
|
|
431
|
+
resultado = registros.length ? Math.max(...registros.map(r => Number(r[campo]) || 0)) : 0;
|
|
432
|
+
prop.valor = formatarValor(resultado, campo);
|
|
433
|
+
break;
|
|
434
|
+
case 'minimo':
|
|
435
|
+
resultado = registros.length ? Math.min(...registros.map(r => Number(r[campo]) || 0)) : 0;
|
|
436
|
+
prop.valor = formatarValor(resultado, campo);
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
383
443
|
async function mudarTela(nome, telas, db, ui, navItems) {
|
|
384
444
|
const idx = telas.findIndex(t => t.nome === nome);
|
|
385
445
|
if (idx < 0) return;
|
|
386
446
|
|
|
387
|
-
navItems
|
|
447
|
+
// navItems mapeia para telasNav (filtradas), mas idx é em telas (completa)
|
|
448
|
+
// Encontra o índice correto no navItems pelo nome
|
|
449
|
+
const navIdx = navItems.findIndex(b => b.dataset.tela === nome);
|
|
450
|
+
navItems.forEach((btn, i) => btn.classList.toggle('jade-nav-ativo', i === navIdx));
|
|
388
451
|
|
|
389
452
|
const tela = telas[idx];
|
|
390
453
|
const container = document.getElementById('jade-conteudo');
|
|
391
454
|
container.innerHTML = '';
|
|
392
455
|
|
|
456
|
+
// Carrega todas as entidades referenciadas (inclusive em marcadores @)
|
|
393
457
|
const dadosMap = {};
|
|
394
458
|
for (const el of tela.elementos || []) {
|
|
395
459
|
for (const prop of el.propriedades || []) {
|
|
396
|
-
|
|
397
|
-
|
|
460
|
+
const refs = [];
|
|
461
|
+
if (prop.chave === 'entidade' && prop.valor) refs.push(String(prop.valor));
|
|
462
|
+
if (typeof prop.valor === 'string' && prop.valor.startsWith('@')) {
|
|
463
|
+
const partes = prop.valor.slice(1).split(':');
|
|
464
|
+
if (partes[1]) refs.push(partes[1]);
|
|
465
|
+
}
|
|
466
|
+
for (const ref of refs) {
|
|
467
|
+
if (!dadosMap[ref]) dadosMap[ref] = await db.find(ref).catch(() => []);
|
|
398
468
|
}
|
|
399
469
|
}
|
|
400
470
|
}
|
|
401
471
|
|
|
472
|
+
// Resolve @soma, @contagem, @media antes de renderizar
|
|
473
|
+
resolverAgregacoes(tela, dadosMap);
|
|
474
|
+
|
|
402
475
|
ui.renderizarTela(tela, container, dadosMap);
|
|
403
476
|
}
|
|
404
477
|
|
|
@@ -429,14 +502,13 @@ async function iniciar() {
|
|
|
429
502
|
|
|
430
503
|
document.getElementById('jade-carregando')?.remove();
|
|
431
504
|
document.getElementById('jade-app').style.display = '';
|
|
505
|
+
document.getElementById('jade-header').style.display = '';
|
|
432
506
|
|
|
433
|
-
// ──
|
|
507
|
+
// ── Header + hambúrguer ──────────────────────────────────────────────────────
|
|
434
508
|
const hamburger = document.getElementById('jade-hamburger');
|
|
435
509
|
const overlay = document.getElementById('jade-overlay');
|
|
436
510
|
const navEl = document.getElementById('jade-nav');
|
|
437
|
-
|
|
438
|
-
// Mostra o botão (estava display:none para não piscar antes do app carregar)
|
|
439
|
-
hamburger.style.display = '';
|
|
511
|
+
const isMobile = () => window.innerWidth <= 768;
|
|
440
512
|
|
|
441
513
|
const iconeMenu = criarElementoIcone('menu', 22);
|
|
442
514
|
const iconeFechar = criarElementoIcone('fechar', 22);
|
|
@@ -446,23 +518,29 @@ async function iniciar() {
|
|
|
446
518
|
navEl.classList.add('jade-nav-aberto');
|
|
447
519
|
overlay.classList.add('visivel');
|
|
448
520
|
hamburger.setAttribute('aria-expanded', 'true');
|
|
449
|
-
if (iconeMenu && iconeFechar)
|
|
521
|
+
if (iconeMenu && iconeFechar && hamburger.firstChild)
|
|
450
522
|
hamburger.replaceChild(iconeFechar, hamburger.firstChild);
|
|
451
|
-
}
|
|
452
523
|
}
|
|
453
524
|
function fecharDrawer() {
|
|
454
525
|
navEl.classList.remove('jade-nav-aberto');
|
|
455
526
|
overlay.classList.remove('visivel');
|
|
456
527
|
hamburger.setAttribute('aria-expanded', 'false');
|
|
457
|
-
if (iconeMenu && hamburger.firstChild !== iconeMenu)
|
|
528
|
+
if (iconeMenu && hamburger.firstChild !== iconeMenu)
|
|
458
529
|
hamburger.replaceChild(iconeMenu, hamburger.firstChild);
|
|
459
|
-
|
|
530
|
+
}
|
|
531
|
+
function toggleSidebar() {
|
|
532
|
+
navEl.classList.toggle('jade-nav-colapsada');
|
|
460
533
|
}
|
|
461
534
|
|
|
462
|
-
hamburger.addEventListener('click', () =>
|
|
463
|
-
|
|
464
|
-
|
|
535
|
+
hamburger.addEventListener('click', () => {
|
|
536
|
+
if (isMobile()) {
|
|
537
|
+
navEl.classList.contains('jade-nav-aberto') ? fecharDrawer() : abrirDrawer();
|
|
538
|
+
} else {
|
|
539
|
+
toggleSidebar();
|
|
540
|
+
}
|
|
541
|
+
});
|
|
465
542
|
overlay.addEventListener('click', fecharDrawer);
|
|
543
|
+
window.addEventListener('resize', () => { if (!isMobile()) fecharDrawer(); });
|
|
466
544
|
|
|
467
545
|
if (telas.length === 0) {
|
|
468
546
|
document.getElementById('jade-conteudo').innerHTML =
|
|
@@ -565,15 +643,16 @@ ${gerarCSS(tema)}
|
|
|
565
643
|
Carregando...
|
|
566
644
|
</div>
|
|
567
645
|
|
|
568
|
-
<
|
|
646
|
+
<header id="jade-header" style="display:none">
|
|
647
|
+
<button id="jade-hamburger" aria-label="Abrir menu" aria-expanded="false"></button>
|
|
648
|
+
<span id="jade-header-titulo">${nome}</span>
|
|
649
|
+
</header>
|
|
650
|
+
|
|
569
651
|
<div id="jade-overlay" role="presentation"></div>
|
|
570
652
|
|
|
571
653
|
<div id="jade-app" style="display:none">
|
|
572
654
|
<nav id="jade-nav" aria-label="Menu de navegação">
|
|
573
|
-
<div id="jade-nav-header">
|
|
574
|
-
<div id="jade-nav-titulo">${nome}</div>
|
|
575
|
-
<div id="jade-nav-versao">feito com Jade DSL</div>
|
|
576
|
-
</div>
|
|
655
|
+
<div id="jade-nav-header"></div>
|
|
577
656
|
<div id="jade-nav-lista" role="list"></div>
|
|
578
657
|
</nav>
|
|
579
658
|
<main id="jade-conteudo"></main>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yakuzaa/jade",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Jade DSL — linguagem empresarial em português compilada para WebAssembly. Instala compilador + runtime + CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"postinstall": "node postinstall.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@yakuzaa/jade-compiler": "^0.1.
|
|
20
|
+
"@yakuzaa/jade-compiler": "^0.1.15",
|
|
21
21
|
"@yakuzaa/jade-runtime": "^0.1.8"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|