@wrrdev/fenix-ui 1.1.10 → 1.1.11
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 +20 -2
- package/dist/assets/{font-CF0TXsAP.js → font-DPeWTz-n.js} +58 -13
- package/dist/assets/font-DPeWTz-n.js.map +1 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts +4 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js +12 -2
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/table/table.d.ts +16 -0
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +28 -1
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/toast/toast.d.ts +8 -0
- package/dist/components/toast/toast.d.ts.map +1 -1
- package/dist/components/toast/toast.js +13 -0
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/components/tooltip/directive.d.ts +8 -0
- package/dist/components/tooltip/directive.d.ts.map +1 -1
- package/dist/components/tooltip/directive.js +59 -18
- package/dist/components/tooltip/directive.js.map +1 -1
- package/dist/docs/app.d.ts.map +1 -1
- package/dist/fenix-ui.umd.min.js +142 -142
- package/dist/fenix-ui3.esm.js +8 -13
- package/dist/fenix-ui3.esm.js.map +1 -1
- package/dist/fenix-ui4.esm.js +184 -93
- package/dist/fenix-ui4.esm.js.map +1 -1
- package/dist/icons/font.d.ts +23 -0
- package/dist/icons/font.d.ts.map +1 -1
- package/dist/icons/font.js +54 -8
- package/dist/icons/font.js.map +1 -1
- package/dist/icons/index.d.ts +7 -2
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/icons/index.js +6 -11
- package/dist/icons/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/font-CF0TXsAP.js.map +0 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ import { FenixUI } from '@wrrdev/fenix-ui'; // API de tema/tokens (índice)
|
|
|
18
18
|
FenixUI.theme('dark');
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
> 📦 npm: [`@wrrdev/fenix-ui`](https://www.npmjs.com/package/@wrrdev/fenix-ui) — **v1.1.
|
|
21
|
+
> 📦 npm: [`@wrrdev/fenix-ui`](https://www.npmjs.com/package/@wrrdev/fenix-ui) — **v1.1.11**, acesso público.
|
|
22
22
|
> Subpaths disponíveis: `./theme`, `./tokens`, `./jsx`, `./vue`, `./react`, `./button`, `./badge`, `./spinner`, `./select`, `./input`, `./switch`, `./textarea`, `./checkbox`, `./radio`, `./calendar`, `./datepicker`, `./multiselect`, `./table`, `./floatlabel`, `./dialog`, `./toast`, `./tooltip`, `./tabs`, `./progress`, `./skeleton`, `./alert`, `./drawer`, `./dropdown`, `./icon`, `./icons`, `./pagination`, `./autocomplete`, `./knob`, `./orderlist`, `./picklist`, `./auto-import` e o curinga `./components/*`.
|
|
23
23
|
|
|
24
24
|
|
|
@@ -65,7 +65,7 @@ Carregue o bundle **UMD** único: ele registra todos os componentes e expõe os
|
|
|
65
65
|
<fx-toast></fx-toast>
|
|
66
66
|
|
|
67
67
|
<!--
|
|
68
|
-
🔗 Links diretos para CDN (versão 1.1.
|
|
68
|
+
🔗 Links diretos para CDN (versão 1.1.11):
|
|
69
69
|
• https://cdn.jsdelivr.net/npm/@wrrdev/fenix-ui@latest/dist/fenix-ui.umd.min.js
|
|
70
70
|
• https://unpkg.com/@wrrdev/fenix-ui@latest/dist/fenix-ui.umd.min.js
|
|
71
71
|
-->
|
|
@@ -349,6 +349,24 @@ Requisitos:
|
|
|
349
349
|
- Volar / vue-tsc (já incluído no `@vue/language-tools` usado pelo VS Code + Volar);
|
|
350
350
|
- no `tsconfig`, `"moduleResolution": "bundler"`.
|
|
351
351
|
|
|
352
|
+
#### Reduzindo o peso da fonte de ícones (opcional)
|
|
353
|
+
|
|
354
|
+
A fonte embarca **todos** os ~4.300 glifos, o que dá ~5 MB. Ela já usa
|
|
355
|
+
`font-display: swap` (o ícone aparece na hora com a fonte de fallback — nunca
|
|
356
|
+
mais a tela em branco esperando o download). Se o seu app usa apenas algumas
|
|
357
|
+
dezenas de ícones, gere um *subset* e aponte a biblioteca para ele:
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
import { setFenixIconsFontUrl } from '@wrrdev/fenix-ui/icons';
|
|
361
|
+
|
|
362
|
+
setFenixIconsFontUrl('/fonts/fenix-icons-subset.woff2');
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Isso derruba a fonte para algumas dezenas de KB. **A tipagem não muda**:
|
|
366
|
+
`FenixIconName` continua sugerindo todos os nomes no editor — garanta apenas
|
|
367
|
+
que o subset contenha os glifos que o app usa. Chame a função antes do primeiro
|
|
368
|
+
`<fx-icon>` ser montado; use `setFenixIconsFontUrl('')` para voltar ao padrão.
|
|
369
|
+
|
|
352
370
|
Com isso o editor mostra as propriedades de cada componente (`variant`, `size`, `loading`, …)
|
|
353
371
|
nos templates. Para além do autocomplete, **validar** valores inválidos (ex.: `variant="foo"`),
|
|
354
372
|
ative o modo estrito do Volar no `tsconfig.json`:
|
|
@@ -69,26 +69,69 @@ function resolveFontUrl() {
|
|
|
69
69
|
return new URL("" + new URL("fenix-icons-DmgotBzD.woff2", import.meta.url).href, import.meta.url).toString();
|
|
70
70
|
}
|
|
71
71
|
const FENIX_ICONS_FONT_URL = resolveFontUrl();
|
|
72
|
+
function setFenixIconsFontUrl(url) {
|
|
73
|
+
const next = url.trim() || void 0;
|
|
74
|
+
if (fontUrlOverride === next) return;
|
|
75
|
+
fontUrlOverride = next;
|
|
76
|
+
const target = typeof document !== "undefined" ? document : void 0;
|
|
77
|
+
if (!target) return;
|
|
78
|
+
const hadFull = !!target.getElementById(FENIX_ICONS_STYLE_ID);
|
|
79
|
+
const hadFont = !!target.getElementById(FENIX_ICONS_FONT_STYLE_ID);
|
|
80
|
+
resetInjectedStyles(target);
|
|
81
|
+
if (hadFull) reinjectFull(target, fullNamesRef);
|
|
82
|
+
if (hadFont) injectFontStyle(target);
|
|
83
|
+
}
|
|
84
|
+
let fullNamesRef = [];
|
|
85
|
+
function __setFenixIconNames(names) {
|
|
86
|
+
fullNamesRef = names;
|
|
87
|
+
}
|
|
88
|
+
let fontUrlOverride;
|
|
89
|
+
function currentFontUrl() {
|
|
90
|
+
return fontUrlOverride ?? FENIX_ICONS_FONT_URL;
|
|
91
|
+
}
|
|
72
92
|
const FENIX_ICONS_FONT_STYLE_ID = "fenix-icons-font";
|
|
73
93
|
const FENIX_ICONS_STYLE_ID = "fenix-icons";
|
|
74
94
|
function buildFenixIconsFontFaceCss() {
|
|
75
|
-
return `@font-face{font-family:'${FENIX_ICON_FONT_FAMILY}';font-style:normal;font-weight:100 700;font-display:
|
|
95
|
+
return `@font-face{font-family:'${FENIX_ICON_FONT_FAMILY}';font-style:normal;font-weight:100 700;font-display:swap;src:url('${currentFontUrl()}') format('woff2');}`;
|
|
76
96
|
}
|
|
77
97
|
function buildFenixIconsFontCss() {
|
|
78
98
|
return buildFenixIconsFontFaceCss() + FENIX_ICON_BASE_CSS;
|
|
79
99
|
}
|
|
100
|
+
function buildFullCss(names) {
|
|
101
|
+
let rules = "";
|
|
102
|
+
for (const name of names) {
|
|
103
|
+
rules += `.fx-icon-${name}::before{content:"${name}"}`;
|
|
104
|
+
}
|
|
105
|
+
return buildFenixIconsFontFaceCss() + FENIX_ICON_BASE_CSS + rules;
|
|
106
|
+
}
|
|
80
107
|
let fontInjected = false;
|
|
108
|
+
function resetInjectedStyles(target = typeof document !== "undefined" ? document : void 0) {
|
|
109
|
+
fontInjected = false;
|
|
110
|
+
if (!target) return;
|
|
111
|
+
target.getElementById(FENIX_ICONS_FONT_STYLE_ID)?.remove();
|
|
112
|
+
target.getElementById(FENIX_ICONS_STYLE_ID)?.remove();
|
|
113
|
+
}
|
|
114
|
+
function injectFontStyle(target) {
|
|
115
|
+
const style = target.createElement("style");
|
|
116
|
+
style.id = FENIX_ICONS_FONT_STYLE_ID;
|
|
117
|
+
style.textContent = buildFenixIconsFontCss();
|
|
118
|
+
target.head.appendChild(style);
|
|
119
|
+
fontInjected = true;
|
|
120
|
+
}
|
|
121
|
+
function reinjectFull(target, names) {
|
|
122
|
+
if (target.getElementById(FENIX_ICONS_STYLE_ID)) return;
|
|
123
|
+
const style = target.createElement("style");
|
|
124
|
+
style.id = FENIX_ICONS_STYLE_ID;
|
|
125
|
+
style.textContent = buildFullCss(names);
|
|
126
|
+
target.head.appendChild(style);
|
|
127
|
+
}
|
|
81
128
|
function loadFenixIconsFont(target = typeof document !== "undefined" ? document : void 0) {
|
|
82
129
|
if (fontInjected || !target) return;
|
|
83
130
|
if (target.getElementById(FENIX_ICONS_STYLE_ID)) {
|
|
84
131
|
fontInjected = true;
|
|
85
132
|
return;
|
|
86
133
|
}
|
|
87
|
-
|
|
88
|
-
style.id = FENIX_ICONS_FONT_STYLE_ID;
|
|
89
|
-
style.textContent = buildFenixIconsFontCss();
|
|
90
|
-
target.head.appendChild(style);
|
|
91
|
-
fontInjected = true;
|
|
134
|
+
injectFontStyle(target);
|
|
92
135
|
}
|
|
93
136
|
function __resetFenixIconsFontInjection() {
|
|
94
137
|
fontInjected = false;
|
|
@@ -97,14 +140,16 @@ export {
|
|
|
97
140
|
FENIX_ICONS_STYLE_ID as F,
|
|
98
141
|
__resetFenixIconsFontInjection as _,
|
|
99
142
|
FENIX_ICON_BASE_CSS as a,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
143
|
+
buildFullCss as b,
|
|
144
|
+
__setFenixIconNames as c,
|
|
145
|
+
FENIX_ICON_FONT_FAMILY as d,
|
|
146
|
+
buildFenixIconsFontCss as e,
|
|
104
147
|
fenixIconHtml as f,
|
|
105
|
-
|
|
148
|
+
esc as g,
|
|
149
|
+
sanitizeData as h,
|
|
106
150
|
isFenixIconName as i,
|
|
151
|
+
stripTags as j,
|
|
107
152
|
loadFenixIconsFont as l,
|
|
108
|
-
|
|
153
|
+
setFenixIconsFontUrl as s
|
|
109
154
|
};
|
|
110
|
-
//# sourceMappingURL=font-
|
|
155
|
+
//# sourceMappingURL=font-DPeWTz-n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"font-DPeWTz-n.js","sources":["../../src/core/sanitize.ts","../../src/icons/base-css.ts","../../src/icons/font.ts"],"sourcesContent":["/**\n * Sanitização de strings para injeção segura em HTML.\n *\n * Previne XSS ao escapar caracteres especiais antes de usar innerHTML.\n * Use SEMPRE que dados externos (API, usuário) forem injetados no DOM.\n */\n\n/** Caracteres que devem ser escapados para prevenir XSS. */\nconst HTML_ENTITIES: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n '/': '/',\n '`': '`',\n '=': '=',\n};\n\n/** Regex que captura todos os caracteres perigosos. */\nconst SANITIZE_REGEX = /[&<>\"'`=/]/g;\n\n/**\n * Escapa caracteres especiais de HTML para prevenir XSS.\n *\n * @param value - Valor a ser escapado (convertido para string)\n * @returns String segura para innerHTML\n *\n * @example\n * // Seguro contra XSS\n * const userInput = '<img src=x onerror=alert(1)>';\n * element.innerHTML = `<div>${esc(userInput)}</div>`;\n * // Resultado: <img src=x onerror=alert(1)>\n */\nexport function esc(value: unknown): string {\n if (value == null) return '';\n return String(value).replace(SANITIZE_REGEX, (char) => HTML_ENTITIES[char] || char);\n}\n\n/**\n * Remove todas as tags HTML da string (strip tags).\n * Use quando quiser apenas texto puro, sem nenhuma marcação.\n *\n * @param html - String potencialmente contendo HTML\n * @returns String sem tags HTML\n */\nexport function stripTags(html: string): string {\n return html.replace(/<[^>]*>/g, '');\n}\n\n/**\n * Sanitiza um objeto de dados recursivamente.\n * Útil para sanitizar arrays de registros antes de renderizar.\n *\n * @param data - Objeto ou array a ser sanitizado\n * @returns Cópia sanitizada do objeto\n */\nexport function sanitizeData<T>(data: T): T {\n if (data == null) return data;\n if (typeof data === 'string') return esc(data) as unknown as T;\n if (Array.isArray(data)) return data.map(sanitizeData) as unknown as T;\n if (typeof data === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n result[key] = sanitizeData(value);\n }\n return result as T;\n }\n return data;\n}\n","/**\n * CSS base da biblioteca de ícones — módulo LEVE (não importa a lista de nomes\n * nem dispara o carregamento da fonte). Usado dentro dos Shadow DOMs dos\n * componentes que suportam o atributo `icon`.\n *\n * O @font-face em si é injetado no <head> por `@wrrdev/fenix-ui/icons`\n * (ver ./index.ts) — @font-face registrado no documento também se aplica\n * dentro dos Shadow DOMs.\n */\n\nimport { esc } from '../core/sanitize';\n\n/** Nome da família de fontes dos ícones (self-hosted, sem dependência externa). */\nexport const FENIX_ICON_FONT_FAMILY = 'Fenix Icons';\n\n/** Família configurável via CSS custom property (fallback para o nome padrão). */\nexport const FENIX_ICON_FONT_VAR = `var(--fx-icon-font, ${FENIX_ICON_FONT_FAMILY})`;\n\n/**\n * Regras da classe `.fx-icon` para uso DENTRO de Shadow DOM\n * (concatene às `static styles` do componente).\n */\nexport const FENIX_ICON_BASE_CSS = `\n.fx-icon {\n font-family: ${FENIX_ICON_FONT_VAR};\n font-weight: normal;\n font-style: normal;\n line-height: 1;\n letter-spacing: normal;\n text-transform: none;\n display: inline-block;\n white-space: nowrap;\n word-wrap: normal;\n direction: ltr;\n font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;\n -webkit-font-smoothing: antialiased;\n text-rendering: optimizeLegibility;\n user-select: none;\n}\n.fx-icon.fx-icon-fill { font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24; }\n.fx-icon.fx-icon-bold { font-variation-settings: 'FILL' 0, 'wght' 600, 'GRAD' 0, 'opsz' 24; }\n`;\n\n/** Verifica se o valor parece um nome de ícone da Fenix Icons (e não emoji/texto livre). */\nexport function isFenixIconName(value: string): boolean {\n return /^[a-z][a-z0-9_]*$/.test(value.trim());\n}\n\n/**\n * HTML do ícone para uso DENTRO de Shadow DOM (concatenar ao template).\n *\n * - Nome válido da Fenix Icons (ex.: `home`) → glifo via LIGADURA: o nome\n * vira o conteúdo de texto do elemento com a font-family de ícones.\n * Isso é obrigatório dentro de Shadow DOM: as regras\n * `.fx-icon-<nome>::before` são injetadas no <head> do documento e NÃO\n * atravessam a fronteira do shadow root (apenas o @font-face vale\n * dentro dele) — a classe vazia renderizaria um elemento sem glifo.\n * - Qualquer outro valor (emoji, texto) → texto puro escapado.\n *\n * A classe base `.fx-icon` é fornecida por `FENIX_ICON_BASE_CSS`\n * (concatenada às `static styles` do componente); o @font-face vem do\n * `@wrrdev/fenix-ui/icons` (injetado no <head>).\n */\nexport function fenixIconHtml(icon: string): string {\n const value = (icon ?? '').trim();\n if (!value) return '';\n if (isFenixIconName(value)) {\n return `<i class=\"fx-icon\" aria-hidden=\"true\">${value}</i>`;\n }\n return `<span aria-hidden=\"true\">${esc(value)}</span>`;\n}\n","/**\n * Carregamento LEVE da fonte de ícones (@font-face + classe base).\n *\n * Separado de ./index de propósito: importar este módulo NÃO injeta o CSS\n * completo com as ~4.300 regras `fx-icon-<nome>`. É o que o <fx-icon> usa —\n * dentro do Shadow DOM o glifo vem da ligadura (o nome como conteúdo), então\n * as classes por ícone não atravessam a fronteira do shadow root.\n */\n\nimport { FENIX_ICON_BASE_CSS, FENIX_ICON_FONT_FAMILY } from './base-css';\n\n/** URL da fonte woff2: no build ESM a fonte vive na mesma pasta do módulo (dist/icons). */\ndeclare const __FENIX_ICONS_FONT_URL__: string | undefined;\n\nfunction resolveFontUrl(): string {\n if (typeof __FENIX_ICONS_FONT_URL__ !== 'undefined' && __FENIX_ICONS_FONT_URL__) {\n return __FENIX_ICONS_FONT_URL__;\n }\n // IMPORTANTE: o `import.meta.url` precisa ser usado DIRETAMENTE como 2º\n // argumento de `new URL` (sem variável intermediária). É assim que Vite,\n // webpack e Rollup reconhecem o padrão de asset e copiam/reescrevem a URL\n // da fonte no build do PROJETO CONSUMIDOR — com variável, a URL permanece\n // dinâmica e quebra em produção (a fonte 404 e os ícones viram texto).\n return new URL('./fenix-icons.woff2', import.meta.url).toString();\n}\n\n/** URL resolvida da fonte (reutilizada por ./index para o CSS completo). */\nexport const FENIX_ICONS_FONT_URL = resolveFontUrl();\n\n/**\n * Sobrescreve a URL da fonte em tempo de execução.\n *\n * Use para apontar para um SUBSET próprio (só os glifos que o app usa), o que\n * derruba a fonte de ~5 MB para algumas dezenas de KB:\n *\n * import { setFenixIconsFontUrl } from '@wrrdev/fenix-ui/icons';\n * setFenixIconsFontUrl('/fonts/fenix-icons-subset.woff2');\n *\n * Deve ser chamado ANTES do primeiro `loadFenixIcons()`/`loadFenixIconsFont()`\n * (ou de `<fx-icon>` ser montado). A tipagem dos ícones não muda: continua\n * valendo a lista completa de `FenixIconName` — o subset precisa conter os\n * glifos que você realmente usa. Passe `''` para voltar à URL do build.\n */\nexport function setFenixIconsFontUrl(url: string): void {\n const next = url.trim() || undefined;\n if (fontUrlOverride === next) return;\n fontUrlOverride = next;\n // O CSS já injetado embute a URL antiga: remove e reinjeta com a nova,\n // senão a troca não teria efeito em apps que já carregaram a fonte.\n const target = typeof document !== 'undefined' ? document : undefined;\n if (!target) return;\n const hadFull = !!target.getElementById(FENIX_ICONS_STYLE_ID);\n const hadFont = !!target.getElementById(FENIX_ICONS_FONT_STYLE_ID);\n resetInjectedStyles(target);\n if (hadFull) reinjectFull(target, fullNamesRef);\n if (hadFont) injectFontStyle(target);\n}\n\n/**\n * Nomes registrados por ./index, usados só se for preciso recriar o CSS\n * completo. Evita que ./font dependa da lista de ~4.300 nomes.\n */\nlet fullNamesRef: readonly string[] = [];\n\n/** Informa os nomes disponíveis para a reinjeção do CSS completo. */\nexport function __setFenixIconNames(names: readonly string[]): void {\n fullNamesRef = names;\n}\n\nlet fontUrlOverride: string | undefined;\n\n/** URL efetiva: a sobrescrita em runtime tem prioridade sobre a do build. */\nfunction currentFontUrl(): string {\n return fontUrlOverride ?? FENIX_ICONS_FONT_URL;\n}\n\n/** Id do <style> com @font-face + classe base. */\nexport const FENIX_ICONS_FONT_STYLE_ID = 'fenix-icons-font';\n\n/** Id do <style> com o CSS completo (@font-face + base + classes por ícone). */\nexport const FENIX_ICONS_STYLE_ID = 'fenix-icons';\n\n/** CSS do @font-face. */\nexport function buildFenixIconsFontFaceCss(): string {\n return (\n `@font-face{font-family:'${FENIX_ICON_FONT_FAMILY}';font-style:normal;font-weight:100 700;` +\n // `swap` (e não `block`): com `block` o texto fica INVISÍVEL até a fonte\n // baixar — em 3G/4G isso vira tela branca por vários segundos. `swap`\n // mostra o glifo de fallback imediatamente e troca quando a fonte chega.\n `font-display:swap;src:url('${currentFontUrl()}') format('woff2');}`\n );\n}\n\n/** CSS leve: @font-face + classe base (sem as classes por ícone). */\nexport function buildFenixIconsFontCss(): string {\n return buildFenixIconsFontFaceCss() + FENIX_ICON_BASE_CSS;\n}\n\n/**\n * CSS completo (@font-face + base + uma regra por ícone), com a URL de fonte\n * vigente. Vive aqui (e não só em ./index) para que `setFenixIconsFontUrl`\n * consiga recriar o estilo completo sem importar outro módulo.\n */\nexport function buildFullCss(names: readonly string[]): string {\n let rules = '';\n for (const name of names) {\n rules += `.fx-icon-${name}::before{content:\"${name}\"}`;\n }\n return buildFenixIconsFontFaceCss() + FENIX_ICON_BASE_CSS + rules;\n}\n\nlet fontInjected = false;\n\n/**\n * Remove os <style> de ícones já injetados, para reinjetar com outra URL.\n * Usado por `setFenixIconsFontUrl`.\n */\nfunction resetInjectedStyles(\n target: Document | undefined = typeof document !== 'undefined' ? document : undefined,\n): void {\n fontInjected = false;\n if (!target) return;\n target.getElementById(FENIX_ICONS_FONT_STYLE_ID)?.remove();\n target.getElementById(FENIX_ICONS_STYLE_ID)?.remove();\n}\n\n/** Cria e anexa o <style> leve (@font-face + classe base) no <head>. */\nfunction injectFontStyle(target: Document): void {\n const style = target.createElement('style');\n style.id = FENIX_ICONS_FONT_STYLE_ID;\n style.textContent = buildFenixIconsFontCss();\n target.head.appendChild(style);\n fontInjected = true;\n}\n\n/**\n * Reinjeta o CSS completo (o de `./index`).\n *\n * Fica aqui para que a troca de URL da fonte recrie o estilo. Não usa o flag\n * `injected` de ./index: o <style> antigo já foi removido do DOM, então\n * recriá-lo aqui é sempre seguro e evita depender do estado de outro módulo.\n */\nfunction reinjectFull(target: Document, names: readonly string[]): void {\n if (target.getElementById(FENIX_ICONS_STYLE_ID)) return;\n const style = target.createElement('style');\n style.id = FENIX_ICONS_STYLE_ID;\n style.textContent = buildFullCss(names);\n target.head.appendChild(style);\n}\n\n/**\n * Injeta @font-face + classe base no <head> (idempotente). Não duplica quando\n * o CSS completo (`fenix-icons`) já foi carregado por `import '.../icons'`.\n */\nexport function loadFenixIconsFont(\n target: Document | undefined = typeof document !== 'undefined' ? document : undefined,\n): void {\n if (fontInjected || !target) return;\n if (target.getElementById(FENIX_ICONS_STYLE_ID)) {\n fontInjected = true;\n return;\n }\n injectFontStyle(target);\n}\n\n/** Reseta o estado de injeção da fonte (uso interno/testes). */\nexport function __resetFenixIconsFontInjection(): void {\n fontInjected = false;\n}"],"names":[],"mappings":"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"}
|
|
@@ -28,7 +28,11 @@ export declare class FxBreadcrumb extends FxElement {
|
|
|
28
28
|
*/
|
|
29
29
|
private _decorateItems;
|
|
30
30
|
private _items;
|
|
31
|
+
/** Observer de upgrade dos filhos; precisa ser solto no disconnect. */
|
|
32
|
+
private _childObserver?;
|
|
31
33
|
protected connectedCallback(): void;
|
|
34
|
+
/** Desconecta o observer de filhos (evita trabalho eterno após o connect). */
|
|
35
|
+
private _stopObservingChildren;
|
|
32
36
|
protected disconnectedCallback(): void;
|
|
33
37
|
}
|
|
34
38
|
export declare function defineFxBreadcrumb(): typeof FxBreadcrumb;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumb.d.ts","sourceRoot":"","sources":["../../../src/components/breadcrumb/breadcrumb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,YAAa,SAAQ,SAAS;IACzC,OAAgB,MAAM,SAuCpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;cAEkB,MAAM,IAAI,IAAI;IAUjC;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IA4BtB,OAAO,CAAC,MAAM,CAAiB;
|
|
1
|
+
{"version":3,"file":"breadcrumb.d.ts","sourceRoot":"","sources":["../../../src/components/breadcrumb/breadcrumb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,YAAa,SAAQ,SAAS;IACzC,OAAgB,MAAM,SAuCpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;cAEkB,MAAM,IAAI,IAAI;IAUjC;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IA4BtB,OAAO,CAAC,MAAM,CAAiB;IAC/B,uEAAuE;IACvE,OAAO,CAAC,cAAc,CAAC,CAAmB;cAEvB,iBAAiB,IAAI,IAAI;IAkB5C,8EAA8E;IAC9E,OAAO,CAAC,sBAAsB;cAKX,oBAAoB,IAAI,IAAI;CAKhD;AAED,wBAAgB,kBAAkB,IAAI,OAAO,YAAY,CAExD"}
|
|
@@ -50,14 +50,24 @@ const _FxBreadcrumb = class _FxBreadcrumb extends FxElement {
|
|
|
50
50
|
super.connectedCallback();
|
|
51
51
|
const obs = new MutationObserver(() => {
|
|
52
52
|
if (this.children.length > 0) {
|
|
53
|
-
|
|
53
|
+
this._stopObservingChildren();
|
|
54
54
|
this._decorateItems();
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
|
+
this._childObserver = obs;
|
|
57
58
|
obs.observe(this, { childList: true });
|
|
58
|
-
if (this.children.length > 0)
|
|
59
|
+
if (this.children.length > 0) {
|
|
60
|
+
this._stopObservingChildren();
|
|
61
|
+
this._decorateItems();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Desconecta o observer de filhos (evita trabalho eterno após o connect). */
|
|
65
|
+
_stopObservingChildren() {
|
|
66
|
+
this._childObserver?.disconnect();
|
|
67
|
+
this._childObserver = void 0;
|
|
59
68
|
}
|
|
60
69
|
disconnectedCallback() {
|
|
70
|
+
this._stopObservingChildren();
|
|
61
71
|
super.disconnectedCallback();
|
|
62
72
|
this._items = [];
|
|
63
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumb.js","sources":["../../../src/components/breadcrumb/breadcrumb.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-breadcrumb> — Trilha de navegação hierárquica.\n *\n * Atributos: size (sm|md|lg), separator (glifo entre itens, padrão '/'),\n * label (acessibilidade, padrão 'Breadcrumb').\n * Slots: `item` (repetível — cada <fx-breadcrumb-item> ou elemento com\n * [slot=\"item\"] vira um passo) e `separator` (customiza o separador).\n *\n * Marcação declarativa recomendada:\n * <fx-breadcrumb>\n * <fx-breadcrumb-item href=\"/\">Home</fx-breadcrumb-item>\n * <fx-breadcrumb-item href=\"/produtos\">Produtos</fx-breadcrumb-item>\n * <fx-breadcrumb-item>Detalhe</fx-breadcrumb-item>\n * </fx-breadcrumb>\n *\n * Eventos (composed): `select` (detail: { item, index }) ao clicar num item.\n */\nexport class FxBreadcrumb extends FxElement {\n static override styles = css`\n :host {\n display: block;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n }\n nav {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--fx-space-xs);\n }\n :host([size='sm']) nav { font-size: calc(var(--fx-font-size) - 2px); }\n :host([size='lg']) nav { font-size: calc(var(--fx-font-size) + 2px); }\n\n /* itens (movidos para o nav no shadow DOM) */\n nav [slot='item'] {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-xs);\n color: var(--fx-text-muted);\n text-decoration: none;\n cursor: pointer;\n transition: color var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n nav [slot='item']:hover { color: var(--fx-color-primary); }\n nav [slot='item'][aria-current='page'] {\n color: var(--fx-text-default);\n font-weight: 600;\n cursor: default;\n }\n nav [slot='item'][aria-current='page']:hover { color: var(--fx-text-default); }\n\n .sep {\n color: var(--fx-text-muted);\n user-select: none;\n display: inline-flex;\n align-items: center;\n }\n `;\n\n static override get observedAttributes(): string[] {\n return ['size', 'separator', 'label'];\n }\n\n protected override render(): void {\n const label = this.getAttr('label', 'Breadcrumb');\n\n this.setTemplate(`\n <nav part=\"nav\" aria-label=\"${esc(label)}\"></nav>\n `);\n\n this._decorateItems();\n }\n\n /**\n * Move os itens do light DOM para o nav e intercala separadores.\n * Como o playground atribui innerHTML (filhos chegam assincronamente) e\n * re-renders posteriores já terão os itens no nav, a lógica abaixo é\n * idempotente: coleta filhos do light DOM apenas na 1ª chamada.\n */\n private _decorateItems(): void {\n const nav = this.shadowRoot?.querySelector('nav');\n if (!nav) return;\n const sep = this.getAttr('separator', '/');\n\n // 1ª vez: captura itens do light DOM (idempotente — só coleta uma vez)\n if (this._items.length === 0) {\n this._items = Array.from(this.children).filter((el) =>\n el.matches('[slot=\"item\"]'),\n );\n }\n\n // limpa nav e reconstrói com os itens armazenados\n nav.innerHTML = '';\n this._items.forEach((item, i) => {\n const isLast = i === this._items.length - 1;\n item.setAttribute('aria-current', isLast ? 'page' : 'false');\n nav.appendChild(item);\n if (isLast) return;\n const s = document.createElement('span');\n s.className = 'sep';\n s.setAttribute('part', 'separator');\n s.setAttribute('aria-hidden', 'true');\n s.textContent = sep;\n nav.appendChild(s);\n });\n }\n\n private _items: Element[] = [];\n\n protected override connectedCallback(): void {\n super.connectedCallback();\n // filhos podem não estar disponíveis ainda (innerHTML do playground)\n const obs = new MutationObserver(() => {\n if (this.children.length > 0) {\n
|
|
1
|
+
{"version":3,"file":"breadcrumb.js","sources":["../../../src/components/breadcrumb/breadcrumb.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-breadcrumb> — Trilha de navegação hierárquica.\n *\n * Atributos: size (sm|md|lg), separator (glifo entre itens, padrão '/'),\n * label (acessibilidade, padrão 'Breadcrumb').\n * Slots: `item` (repetível — cada <fx-breadcrumb-item> ou elemento com\n * [slot=\"item\"] vira um passo) e `separator` (customiza o separador).\n *\n * Marcação declarativa recomendada:\n * <fx-breadcrumb>\n * <fx-breadcrumb-item href=\"/\">Home</fx-breadcrumb-item>\n * <fx-breadcrumb-item href=\"/produtos\">Produtos</fx-breadcrumb-item>\n * <fx-breadcrumb-item>Detalhe</fx-breadcrumb-item>\n * </fx-breadcrumb>\n *\n * Eventos (composed): `select` (detail: { item, index }) ao clicar num item.\n */\nexport class FxBreadcrumb extends FxElement {\n static override styles = css`\n :host {\n display: block;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n }\n nav {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--fx-space-xs);\n }\n :host([size='sm']) nav { font-size: calc(var(--fx-font-size) - 2px); }\n :host([size='lg']) nav { font-size: calc(var(--fx-font-size) + 2px); }\n\n /* itens (movidos para o nav no shadow DOM) */\n nav [slot='item'] {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-xs);\n color: var(--fx-text-muted);\n text-decoration: none;\n cursor: pointer;\n transition: color var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n nav [slot='item']:hover { color: var(--fx-color-primary); }\n nav [slot='item'][aria-current='page'] {\n color: var(--fx-text-default);\n font-weight: 600;\n cursor: default;\n }\n nav [slot='item'][aria-current='page']:hover { color: var(--fx-text-default); }\n\n .sep {\n color: var(--fx-text-muted);\n user-select: none;\n display: inline-flex;\n align-items: center;\n }\n `;\n\n static override get observedAttributes(): string[] {\n return ['size', 'separator', 'label'];\n }\n\n protected override render(): void {\n const label = this.getAttr('label', 'Breadcrumb');\n\n this.setTemplate(`\n <nav part=\"nav\" aria-label=\"${esc(label)}\"></nav>\n `);\n\n this._decorateItems();\n }\n\n /**\n * Move os itens do light DOM para o nav e intercala separadores.\n * Como o playground atribui innerHTML (filhos chegam assincronamente) e\n * re-renders posteriores já terão os itens no nav, a lógica abaixo é\n * idempotente: coleta filhos do light DOM apenas na 1ª chamada.\n */\n private _decorateItems(): void {\n const nav = this.shadowRoot?.querySelector('nav');\n if (!nav) return;\n const sep = this.getAttr('separator', '/');\n\n // 1ª vez: captura itens do light DOM (idempotente — só coleta uma vez)\n if (this._items.length === 0) {\n this._items = Array.from(this.children).filter((el) =>\n el.matches('[slot=\"item\"]'),\n );\n }\n\n // limpa nav e reconstrói com os itens armazenados\n nav.innerHTML = '';\n this._items.forEach((item, i) => {\n const isLast = i === this._items.length - 1;\n item.setAttribute('aria-current', isLast ? 'page' : 'false');\n nav.appendChild(item);\n if (isLast) return;\n const s = document.createElement('span');\n s.className = 'sep';\n s.setAttribute('part', 'separator');\n s.setAttribute('aria-hidden', 'true');\n s.textContent = sep;\n nav.appendChild(s);\n });\n }\n\n private _items: Element[] = [];\n /** Observer de upgrade dos filhos; precisa ser solto no disconnect. */\n private _childObserver?: MutationObserver;\n\n protected override connectedCallback(): void {\n super.connectedCallback();\n // filhos podem não estar disponíveis ainda (innerHTML do playground)\n const obs = new MutationObserver(() => {\n if (this.children.length > 0) {\n this._stopObservingChildren();\n this._decorateItems();\n }\n });\n this._childObserver = obs;\n obs.observe(this, { childList: true });\n // se já tem filhos, decora na hora\n if (this.children.length > 0) {\n this._stopObservingChildren();\n this._decorateItems();\n }\n }\n\n /** Desconecta o observer de filhos (evita trabalho eterno após o connect). */\n private _stopObservingChildren(): void {\n this._childObserver?.disconnect();\n this._childObserver = undefined;\n }\n\n protected override disconnectedCallback(): void {\n this._stopObservingChildren();\n super.disconnectedCallback();\n this._items = [];\n }\n}\n\nexport function defineFxBreadcrumb(): typeof FxBreadcrumb {\n return defineElement('fx-breadcrumb', FxBreadcrumb);\n}\n"],"names":[],"mappings":";;;;AAsBO,MAAM,gBAAN,MAAM,sBAAqB,UAAU;AAAA,EAArC,cAAA;AAAA,UAAA,GAAA,SAAA;AA0FL,SAAQ,SAAoB,CAAA;AAAA,EAAC;AAAA,EAhD7B,WAAoB,qBAA+B;AACjD,WAAO,CAAC,QAAQ,aAAa,OAAO;AAAA,EACtC;AAAA,EAEmB,SAAe;AAChC,UAAM,QAAQ,KAAK,QAAQ,SAAS,YAAY;AAEhD,SAAK,YAAY;AAAA,oCACe,IAAI,KAAK,CAAC;AAAA,KACzC;AAED,SAAK,eAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,iBAAuB;AAC7B,UAAM,MAAM,KAAK,YAAY,cAAc,KAAK;AAChD,QAAI,CAAC,IAAK;AACV,UAAM,MAAM,KAAK,QAAQ,aAAa,GAAG;AAGzC,QAAI,KAAK,OAAO,WAAW,GAAG;AAC5B,WAAK,SAAS,MAAM,KAAK,KAAK,QAAQ,EAAE;AAAA,QAAO,CAAC,OAC9C,GAAG,QAAQ,eAAe;AAAA,MAAA;AAAA,IAE9B;AAGA,QAAI,YAAY;AAChB,SAAK,OAAO,QAAQ,CAAC,MAAM,MAAM;AAC/B,YAAM,SAAS,MAAM,KAAK,OAAO,SAAS;AAC1C,WAAK,aAAa,gBAAgB,SAAS,SAAS,OAAO;AAC3D,UAAI,YAAY,IAAI;AACpB,UAAI,OAAQ;AACZ,YAAM,IAAI,SAAS,cAAc,MAAM;AACvC,QAAE,YAAY;AACd,QAAE,aAAa,QAAQ,WAAW;AAClC,QAAE,aAAa,eAAe,MAAM;AACpC,QAAE,cAAc;AAChB,UAAI,YAAY,CAAC;AAAA,IACnB,CAAC;AAAA,EACH;AAAA,EAMmB,oBAA0B;AAC3C,UAAM,kBAAA;AAEN,UAAM,MAAM,IAAI,iBAAiB,MAAM;AACrC,UAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,aAAK,uBAAA;AACL,aAAK,eAAA;AAAA,MACP;AAAA,IACF,CAAC;AACD,SAAK,iBAAiB;AACtB,QAAI,QAAQ,MAAM,EAAE,WAAW,MAAM;AAErC,QAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,WAAK,uBAAA;AACL,WAAK,eAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA,EAGQ,yBAA+B;AACrC,SAAK,gBAAgB,WAAA;AACrB,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAEmB,uBAA6B;AAC9C,SAAK,uBAAA;AACL,UAAM,qBAAA;AACN,SAAK,SAAS,CAAA;AAAA,EAChB;AACF;AA1HE,cAAgB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AADpB,IAAM,eAAN;AA6HA,SAAS,qBAA0C;AACxD,SAAO,cAAc,iBAAiB,YAAY;AACpD;"}
|
|
@@ -105,7 +105,23 @@ export declare class FxTable extends FxElement {
|
|
|
105
105
|
private computeRows;
|
|
106
106
|
private _getSearchFields;
|
|
107
107
|
private cellHtml;
|
|
108
|
+
/**
|
|
109
|
+
* Assinatura da "casca" da tabela: tudo que NÃO depende das linhas
|
|
110
|
+
* (colunas, toolbar, filtros, paginação, loading).
|
|
111
|
+
*
|
|
112
|
+
* Serve para o caminho rápido: quando só `data` muda, a casca é idêntica e
|
|
113
|
+
* podemos trocar apenas o `<tbody>`, sem recriar `<style>`, `<thead>`,
|
|
114
|
+
* toolbar e paginação — nem religar todos os listeners.
|
|
115
|
+
*/
|
|
116
|
+
private _shellKey;
|
|
108
117
|
protected render(): void;
|
|
118
|
+
/**
|
|
119
|
+
* Liga o clique das linhas ao evento `row-click`.
|
|
120
|
+
*
|
|
121
|
+
* Extraído do render para ser reaproveitado no caminho rápido (troca só do
|
|
122
|
+
* <tbody>): os <tr> são novos, então precisam ser rebound.
|
|
123
|
+
*/
|
|
124
|
+
private _bindRowClicks;
|
|
109
125
|
private _restoreToolbarSearch;
|
|
110
126
|
}
|
|
111
127
|
export declare function defineFxTable(): typeof FxTable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/components/table/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAO5C,OAAO,WAAW,CAAC;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACrC,OAAgB,MAAM,SAiNpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAiBjD;IAED,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,eAAe,CAAC,CAAmB;IAE3C,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAEpC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAGxC;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;IAED,IAAI,WAAW,IAAI,MAAM,CAGxB;IACD,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAE5B;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,OAAO,KAAK,OAAO,GAuBlB;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,oBAAoB;IAW5B,OAAO,KAAK,eAAe,GAU1B;IAED;;;;OAIG;IACH,OAAO,CAAC,WAAW;cAeA,iBAAiB,IAAI,IAAI;cAWzB,oBAAoB,IAAI,IAAI;IAK/C,+EAA+E;IAC/E,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,WAAW;IAiDnB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,QAAQ;
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/components/table/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAO5C,OAAO,WAAW,CAAC;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACrC,OAAgB,MAAM,SAiNpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAiBjD;IAED,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,eAAe,CAAC,CAAmB;IAE3C,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAEpC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAGxC;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;IAED,IAAI,WAAW,IAAI,MAAM,CAGxB;IACD,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAE5B;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,OAAO,KAAK,OAAO,GAuBlB;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,oBAAoB;IAW5B,OAAO,KAAK,eAAe,GAU1B;IAED;;;;OAIG;IACH,OAAO,CAAC,WAAW;cAeA,iBAAiB,IAAI,IAAI;cAWzB,oBAAoB,IAAI,IAAI;IAK/C,+EAA+E;IAC/E,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,WAAW;IAiDnB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,QAAQ;IAahB;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS,CAAM;cAEJ,MAAM,IAAI,IAAI;IAgOjC;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,qBAAqB;CAM7B;AAED,wBAAgB,aAAa,IAAI,OAAO,OAAO,CAE9C"}
|
|
@@ -15,6 +15,7 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
15
15
|
this.sortDir = 1;
|
|
16
16
|
this.filters = {};
|
|
17
17
|
this.globalSearch = "";
|
|
18
|
+
this._shellKey = "";
|
|
18
19
|
}
|
|
19
20
|
static get observedAttributes() {
|
|
20
21
|
return [
|
|
@@ -251,6 +252,23 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
251
252
|
</div>`;
|
|
252
253
|
const loading = this.hasAttr("loading");
|
|
253
254
|
const loadingHtml = loading ? `<div class="loading-overlay" part="loading-overlay"><div class="tbl-spinner" part="spinner"></div><span class="loading-text">${esc(this.getAttr("loading-message", "Carregando…"))}</span></div>` : "";
|
|
255
|
+
const shellKey = [
|
|
256
|
+
toolbarHtml,
|
|
257
|
+
headHtml,
|
|
258
|
+
filterRow,
|
|
259
|
+
pagerHtml,
|
|
260
|
+
loadingHtml,
|
|
261
|
+
cols.length
|
|
262
|
+
].join("|");
|
|
263
|
+
if (shellKey === this._shellKey) {
|
|
264
|
+
const tbody = this.root.querySelector("tbody");
|
|
265
|
+
if (tbody) {
|
|
266
|
+
tbody.innerHTML = bodyHtml;
|
|
267
|
+
this._bindRowClicks(rows);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
this._shellKey = shellKey;
|
|
254
272
|
this.setTemplate(`
|
|
255
273
|
${toolbarHtml}
|
|
256
274
|
<div class="table-wrap">
|
|
@@ -354,6 +372,16 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
354
372
|
this.page = 0;
|
|
355
373
|
this.render();
|
|
356
374
|
});
|
|
375
|
+
this._bindRowClicks(rows);
|
|
376
|
+
this._restoreToolbarSearch();
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Liga o clique das linhas ao evento `row-click`.
|
|
380
|
+
*
|
|
381
|
+
* Extraído do render para ser reaproveitado no caminho rápido (troca só do
|
|
382
|
+
* <tbody>): os <tr> são novos, então precisam ser rebound.
|
|
383
|
+
*/
|
|
384
|
+
_bindRowClicks(rows) {
|
|
357
385
|
this.root.querySelectorAll("tbody tr[data-index]").forEach((tr) => {
|
|
358
386
|
tr.addEventListener("click", () => {
|
|
359
387
|
this.dispatchEvent(
|
|
@@ -368,7 +396,6 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
368
396
|
);
|
|
369
397
|
});
|
|
370
398
|
});
|
|
371
|
-
this._restoreToolbarSearch();
|
|
372
399
|
}
|
|
373
400
|
_restoreToolbarSearch() {
|
|
374
401
|
const input = this.root.querySelector(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.js","sources":["../../../src/components/table/table.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from \"../../core/sanitize\";\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\nimport { loadFenixIconsFont } from '../../icons/font';\nimport { renderCell } from \"./expr\";\nimport \"../select\";\n\n/**\n * <fx-table> — Tabela de dados estilo DataTable (dados + colunas configuráveis,\n * ordenação, filtros por coluna, paginação personalizável, busca global na toolbar\n * e modo lazy para busca no servidor).\n *\n * Colunas (light DOM):\n * <fx-column field=\"nome\" header=\"Nome\" sortable filterable align=\"left\">\n * opcional: <fx-cell>R$ {{value}}</fx-cell> para render customizado\n * ou conteúdo direto (HTML + {{ }}) como template alternativo\n * </fx-column>\n *\n * IMPORTANTE (frameworks): use <fx-cell>, NÃO <template>. A tag nativa\n * <template> é reservada — o Vue 3 a transforma em bloco do compilador e o\n * React gerencia os filhos, então o innerHTML chega vazio. <fx-cell> é uma\n * tag própria, sem significado especial para nenhum framework. A forma\n * antiga com <template> continua funcionando (compatibilidade) e há ainda\n * a opção `template=\"#id\"` apontando para um <template> externo.\n *\n * ⚠️ REGRA DO v-pre (Vue) — o conteúdo do <fx-cell> é TEXTO CRU:\n * {{ }} é avaliado pelo motor de expressões do <fx-table> (./expr.ts) e\n * NÃO pelo Vue. Sem `v-pre`, o compilador do Vue consome a interpolação\n * antes: emite `_toDisplayString(_ctx.value | _ctx.number)`, o conteúdo\n * chega sem as chaves no innerHTML e o vue-tsc acusa `value`/`number`\n * inexistentes no escopo. → SEMPRE use <fx-cell v-pre> no Vue.\n *\n * Cada framework tem a sua neutralização (todas verificadas por compilador):\n * - Vue 3 / Nuxt .... v-pre <fx-cell v-pre>{{ value }}</fx-cell>\n * - React / JSX ..... string JS <fx-cell>{'{{ value }}'}</fx-cell>\n * (o `{{ }}` solto é ERRO de sintaxe no parser JSX)\n * - Angular ......... ngNonBindable <fx-cell ngNonBindable>…</fx-cell>\n * - Svelte .......... string JS ou entidades HTML\n * {'{{ value }}'} ou {{ value }}\n * - HTML/CDN/JSF/JSP nada <fx-cell>{{ value }}</fx-cell>\n *\n * Toolbar (opcional):\n * <fx-toolbar>\n * <input data-search-fields=\"nome,cargo\" placeholder=\"Buscar…\">\n * <button>Nova ação</button>\n * </fx-toolbar>\n * (legado ainda aceito: <template slot=\"toolbar\">…</template>)\n *\n * Dados: propriedade `data` (array de objetos) ou atributo `data` (JSON).\n *\n * Atributos: pagination, rows, rows-options, pagination-position (left|center|right),\n * striped, hover, empty-message, lazy, total, loading, loading-message,\n * sort-field, sort-order.\n * Eventos: page-change, sort-change, row-click, filter-change (todos composed).\n */\nexport class FxTable extends FxElement {\n\tstatic override styles = css`\n\t\t${FENIX_ICON_BASE_CSS}\n\t\t:host {\n\t\t\tdisplay: block;\n\t\t\tfont-family: var(--fx-font-family);\n\t\t\tfont-size: var(--fx-font-size);\n\t\t\tcolor: var(--fx-text-default);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t}\n\t\t.table-wrap {\n\t\t\tposition: relative;\n\t\t}\n\t\t.scroll {\n\t\t\toverflow-x: auto;\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-radius-md);\n\t\t}\n\t\ttable {\n\t\t\twidth: 100%;\n\t\t\tborder-collapse: collapse;\n\t\t}\n\t\tth {\n\t\t\ttext-align: left;\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tbackground: var(--fx-surface-surface);\n\t\t\tborder-bottom: 2px solid var(--fx-border-default);\n\t\t\twhite-space: nowrap;\n\t\t\tuser-select: none;\n\t\t}\n\t\tth.sortable {\n\t\t\tcursor: pointer;\n\t\t}\n\t\tth.sortable:hover {\n\t\t\tcolor: var(--fx-color-primary);\n\t\t}\n\t\t.sort-ind {\n\t\t\tfont-size: calc(var(--fx-font-size) - 4px);\n\t\t\tmargin-left: var(--fx-space-3xs, 2px);\n\t\t\topacity: 0.45;\n\t\t\ttransition: opacity var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\tth.sortable:hover .sort-ind {\n\t\t\topacity: 0.85;\n\t\t}\n\t\t.sort-ind.active {\n\t\t\topacity: 1;\n\t\t\tcolor: var(--fx-color-primary);\n\t\t}\n\t\t.filter-row th {\n\t\t\tpadding: var(--fx-space-3xs, 4px) var(--fx-space-md) var(--fx-space-sm);\n\t\t\tborder-bottom-width: 1px;\n\t\t}\n\t\t.filter {\n\t\t\twidth: 100%;\n\t\t\tbox-sizing: border-box;\n\t\t\tfont: inherit;\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t\tcolor: var(--fx-text-default);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-radius-sm);\n\t\t\tpadding: var(--fx-space-3xs, 4px) var(--fx-space-xs);\n\t\t\toutline: none;\n\t\t}\n\t\t.filter:focus {\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tbox-shadow: var(--fx-effect-focus-ring, none);\n\t\t}\n\t\ttd {\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tborder-bottom: 1px solid var(--fx-border-default);\n\t\t}\n\t\ttbody tr {\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\t/* Listrado: alterna a superfície (cinza). Antes usava\n\t\t --fx-surface-background, que é a MESMA cor do fundo do :host e\n\t\t portanto pintava branco sobre branco (listrado invisível). */\n\t\t:host([striped]) tbody tr:nth-child(even) {\n\t\t\tbackground: var(--fx-surface-surface, #f8fafc);\n\t\t}\n\t\t/* O hover é opt-in via atributo (o clique na linha continua sempre ativo). */\n\t\t:host([hover]) tbody tr:hover {\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--fx-color-primary) 8%,\n\t\t\t\ttransparent\n\t\t\t);\n\t\t}\n\t\t.empty {\n\t\t\ttext-align: center;\n\t\t\tpadding: var(--fx-space-xl);\n\t\t\tcolor: var(--fx-text-muted);\n\t\t}\n\t\t.pager {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tflex-wrap: wrap;\n\t\t\tpadding: var(--fx-space-sm) 0;\n\t\t}\n\t\t:host([pagination-position=\"center\"]) .pager {\n\t\t\tjustify-content: center;\n\t\t}\n\t\t:host([pagination-position=\"right\"]) .pager {\n\t\t\tjustify-content: flex-end;\n\t\t}\n\t\t.pager .info {\n\t\t\tcolor: var(--fx-text-muted);\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t}\n\t\t.pg-btn {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tmin-width: var(--fx-size-sm);\n\t\t\theight: var(--fx-size-sm);\n\t\t\tfont: inherit;\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t\tcolor: var(--fx-text-default);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-radius-sm);\n\t\t\tpadding: var(--fx-space-3xs, 4px) var(--fx-space-sm);\n\t\t\tcursor: pointer;\n\t\t\ttransition: border-color var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\t/* O glifo precisa de 1px a mais que o dígito: a altura de traço do ícone é\n\t\t maior que a do número, então igualar os dois font-size deixa a seta\n\t\t visivelmente maior que o texto. */\n\t\t.pg-btn .fx-icon {\n\t\t\tfont-size: calc(var(--fx-font-size) - 1px);\n\t\t}\n\t\t.pg-btn:hover:not(:disabled):not([aria-current=\"true\"]) {\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tcolor: var(--fx-color-primary);\n\t\t}\n\t\t.pg-btn:disabled {\n\t\t\topacity: 0.5;\n\t\t\tcursor: not-allowed;\n\t\t}\n\t\t.pg-btn[aria-current=\"true\"] {\n\t\t\tbackground: var(--fx-color-primary);\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tcolor: #fff;\n\t\t\tfont-weight: var(--fx-font-weight);\n\t\t}\n\t\tfx-select {\n\t\t\tvertical-align: middle;\n\t\t}\n\t\tfx-select::part(trigger) {\n\t\t\tmin-width: 64px !important;\n\t\t\tmin-height: var(--fx-size-sm) !important;\n\t\t\tpadding: 0 var(--fx-space-sm) !important;\n\t\t\tborder-radius: var(--fx-radius-sm) !important;\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px) !important;\n\t\t}\n\t\t.toolbar {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tflex-wrap: wrap;\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-bottom: none;\n\t\t\tborder-radius: var(--fx-radius-md) var(--fx-radius-md) 0 0;\n\t\t\tbackground: var(--fx-surface-background);\n\t\t}\n\t\t.toolbar:empty {\n\t\t\tdisplay: none;\n\t\t}\n\t\t.loading-overlay {\n\t\t\tposition: absolute;\n\t\t\tinset: 0;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--fx-surface-background) 85%,\n\t\t\t\ttransparent\n\t\t\t);\n\t\t\tbackdrop-filter: blur(2px);\n\t\t\tz-index: 10;\n\t\t\tborder-radius: var(--fx-radius-md);\n\t\t}\n\t\t.tbl-spinner {\n\t\t\twidth: 32px;\n\t\t\theight: 32px;\n\t\t\tborder: 3px solid var(--fx-border-default);\n\t\t\tborder-top-color: var(--fx-color-primary);\n\t\t\tborder-radius: 50%;\n\t\t\tanimation: tbl-spin 1s linear infinite;\n\t\t}\n\t\t@keyframes tbl-spin {\n\t\t\tto {\n\t\t\t\ttransform: rotate(360deg);\n\t\t\t}\n\t\t}\n\t\t.loading-text {\n\t\t\tcolor: var(--fx-text-muted);\n\t\t\tfont-size: calc(var(--fx-font-size) - 1px);\n\t\t}\n\t`;\n\n\tstatic override get observedAttributes(): string[] {\n\t\treturn [\n\t\t\t\"pagination\",\n\t\t\t\"rows\",\n\t\t\t\"rows-options\",\n\t\t\t\"striped\",\n\t\t\t\"hover\",\n\t\t\t\"toolbar\",\n\t\t\t\"empty-message\",\n\t\t\t\"pagination-position\",\n\t\t\t\"lazy\",\n\t\t\t\"total\",\n\t\t\t\"loading\",\n\t\t\t\"loading-message\",\n\t\t\t\"sort-field\",\n\t\t\t\"sort-order\",\n\t\t];\n\t}\n\n\tprivate _data: Record<string, unknown>[] = [];\n\tprivate _total?: number;\n\tprivate page = 0;\n\tprivate sortField = \"\";\n\tprivate sortDir: 1 | -1 = 1;\n\tprivate filters: Record<string, string> = {};\n\tprivate globalSearch = \"\";\n\tprivate _columnObserver?: MutationObserver;\n\n\tget data(): Record<string, unknown>[] {\n\t\treturn this._data;\n\t}\n\tset data(value: Record<string, unknown>[]) {\n\t\tthis._data = Array.isArray(value) ? value : [];\n\t\tthis.render();\n\t}\n\n\tget total(): number {\n\t\treturn this._total ?? this._data.length;\n\t}\n\tset total(value: number) {\n\t\tthis._total = value;\n\t\tthis.setAttribute(\"total\", String(value));\n\t}\n\n\tget rowsPerPage(): number {\n\t\tconst r = Number(this.getAttr(\"rows\", \"10\"));\n\t\treturn r > 0 ? r : 10;\n\t}\n\tset rowsPerPage(value: number) {\n\t\tthis.setAttribute(\"rows\", String(value));\n\t}\n\n\tget lazy(): boolean {\n\t\treturn this.hasAttr(\"lazy\");\n\t}\n\n\tprivate get columns(): {\n\t\tfield: string;\n\t\theader: string;\n\t\tsortable: boolean;\n\t\tfilterable: boolean;\n\t\talign: string;\n\t\ttemplate?: HTMLElement;\n\t\tdirectTemplate?: string;\n\t}[] {\n\t\treturn [...this.querySelectorAll(\"fx-column\")].map((c) => {\n\t\t\tconst tpl = this._resolveCellTemplate(c);\n\t\t\tconst direct = tpl ? undefined : c.innerHTML.trim();\n\t\t\tconst align = c.getAttribute(\"align\") ?? \"left\";\n\t\t\treturn {\n\t\t\t\tfield: c.getAttribute(\"field\") ?? \"\",\n\t\t\t\theader: c.getAttribute(\"header\") ?? c.getAttribute(\"field\") ?? \"\",\n\t\t\t\tsortable: c.hasAttribute(\"sortable\"),\n\t\t\t\tfilterable: c.hasAttribute(\"filterable\"),\n\t\t\t\talign: [\"left\", \"center\", \"right\", \"justify\", \"start\", \"end\"].includes(align) ? align : \"left\",\n\t\t\t\ttemplate: tpl ?? undefined,\n\t\t\t\tdirectTemplate: direct || undefined,\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Resolve o template de célula de uma `<fx-column>`, em ordem de prioridade:\n\t *\n\t * 1. `<fx-cell>` — **recomendado**. Tag própria, sem significado especial\n\t * para Vue/React/Angular/Svelte: nenhum framework a intercepta, então o\n\t * `innerHTML` sempre chega intacto ao componente. O light DOM do\n\t * `<fx-table>` não é exibido (a tabela não usa `<slot>`), então o\n\t * conteúdo-fonte nunca aparece na tela.\n\t *\n\t * ⚠️ O conteúdo é TEXTO CRU para o motor de expressões: o `{{ }}` NÃO é\n\t * interpretado pelo framework hospedeiro. Cada um precisa neutralizar a\n\t * própria sintaxe antes (ver o cabeçalho do arquivo): `v-pre` no Vue,\n\t * `{'{{ }}'}` no JSX, `ngNonBindable` no Angular, string/entidades no\n\t * Svelte. Sem isso o template não chega ao `renderCell`.\n\t * 2. `template=\"#id\"` (ou `template=\"id\"`) — aponta para um `<template>`\n\t * declarado **fora** da tabela, útil em React/Angular, onde é mais\n\t * simples manter o markup num único lugar.\n\t * 3. `<template>` dentro da coluna — forma **legada**, mantida por\n\t * compatibilidade. Em Vue 3 ela vira um bloco do compilador e em React os\n\t * filhos são gerenciados pelo framework; nos dois casos o `innerHTML`\n\t * chega vazio e a célula não renderiza. Prefira `<fx-cell>`.\n\t */\n\tprivate _resolveCellTemplate(c: Element): HTMLElement | null {\n\t\tconst cell = c.querySelector<HTMLElement>(\":scope > fx-cell\");\n\t\tif (cell) return cell;\n\t\tconst ref = c.getAttribute(\"template\");\n\t\tif (ref) {\n\t\t\tconst alvo = this._resolveRef(ref);\n\t\t\tif (alvo) return alvo;\n\t\t}\n\t\treturn c.querySelector<HTMLElement>(\"template\");\n\t}\n\n\tprivate get toolbarTemplate(): HTMLElement | null {\n\t\t// Mesma lógica da célula: `<fx-toolbar>` > `toolbar=\"#id\"` > legado.\n\t\tconst bar = this.querySelector<HTMLElement>(\":scope > fx-toolbar\");\n\t\tif (bar) return bar;\n\t\tconst ref = this.getAttr(\"toolbar\");\n\t\tif (ref) {\n\t\t\tconst alvo = this._resolveRef(ref);\n\t\t\tif (alvo) return alvo;\n\t\t}\n\t\treturn this.querySelector<HTMLElement>('template[slot=\"toolbar\"]');\n\t}\n\n\t/**\n\t * Resolve `ref` para um elemento: `#id`/`id` ⇒ `getElementById` no\n\t * documento; qualquer outro valor é tratado como seletor CSS relativo à\n\t * própria tabela. Valores inválidos devolvem `null` (sem exceção).\n\t */\n\tprivate _resolveRef(ref: string): HTMLElement | null {\n\t\tconst valor = ref.trim();\n\t\tif (!valor) return null;\n\t\tconst doc = this.ownerDocument;\n\t\tif (valor.startsWith('#')) return doc?.getElementById(valor.slice(1)) ?? null;\n\t\tif (/^[.#[>]/.test(valor)) {\n\t\t\ttry {\n\t\t\t\treturn this.querySelector<HTMLElement>(valor);\n\t\t\t} catch {\n\t\t\t\treturn null; // seletor inválido — degrada para o próximo fallback\n\t\t\t}\n\t\t}\n\t\treturn doc?.getElementById(valor) ?? null;\n\t}\n\n\tprotected override connectedCallback(): void {\n\t\tthis._parseDataAttribute();\n\t\tthis._parseSortAttributes();\n\t\t// O pager usa glifos da Fenix Icons: garante o @font-face mesmo quando o\n\t\t// app não importou `@wrrdev/fenix-ui/icons` (idempotente).\n\t\tloadFenixIconsFont();\n\t\tsuper.connectedCallback();\n\t\tthis._columnObserver = new MutationObserver(() => this.render());\n\t\tthis._columnObserver.observe(this, { childList: true, subtree: true });\n\t}\n\n\tprotected override disconnectedCallback(): void {\n\t\tthis._columnObserver?.disconnect();\n\t\tsuper.disconnectedCallback();\n\t}\n\n\t/** Ordenação inicial via `sort-field` / `sort-order` (documentados na API). */\n\tprivate _parseSortAttributes(): void {\n\t\tconst field = this.getAttr(\"sort-field\");\n\t\tif (field) this.sortField = field;\n\t\tconst order = this.getAttr(\"sort-order\");\n\t\tif (order === \"asc\" || order === \"desc\") this.sortDir = order === \"asc\" ? 1 : -1;\n\t}\n\n\tprivate _parseDataAttribute(): void {\n\t\tif (!this._data.length) {\n\t\t\ttry {\n\t\t\t\tconst json = this.getAttr(\"data\");\n\t\t\t\tif (json) this._data = JSON.parse(json);\n\t\t\t} catch {\n\t\t\t\t/* atributo invalido - ignora */\n\t\t\t}\n\t\t}\n\t\tconst totalAttr = this.getAttr(\"total\");\n\t\tif (totalAttr) {\n\t\t\tconst n = Number(totalAttr);\n\t\t\tif (!isNaN(n)) this._total = n;\n\t\t}\n\t}\n\n\tprivate computeRows(): { rows: Record<string, unknown>[]; total: number } {\n\t\tlet out = [...this._data];\n\n\t\tfor (const [field, term] of Object.entries(this.filters)) {\n\t\t\tif (!term) continue;\n\t\t\tconst t = term.toLowerCase();\n\t\t\tout = out.filter((r) =>\n\t\t\t\tString(r[field] ?? \"\")\n\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t.includes(t),\n\t\t\t);\n\t\t}\n\n\t\tif (this.globalSearch) {\n\t\t\tconst t = this.globalSearch.toLowerCase();\n\t\t\tconst searchFields = this._getSearchFields();\n\t\t\tout = out.filter((r) => {\n\t\t\t\tconst fields = searchFields.length ? searchFields : Object.keys(r);\n\t\t\t\treturn fields.some((f) =>\n\t\t\t\t\tString(r[f] ?? \"\")\n\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t.includes(t),\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\n\t\tconst total = this.lazy ? this.total : out.length;\n\n\t\tif (this.sortField) {\n\t\t\tout.sort((a, b) => {\n\t\t\t\tconst av = a[this.sortField],\n\t\t\t\t\tbv = b[this.sortField];\n\t\t\t\tif (typeof av === \"number\" && typeof bv === \"number\")\n\t\t\t\t\treturn (av - bv) * this.sortDir;\n\t\t\t\treturn (\n\t\t\t\t\tString(av ?? \"\").localeCompare(String(bv ?? \"\"), \"pt-BR\") *\n\t\t\t\t\tthis.sortDir\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\n\t\tif (this.hasAttr(\"pagination\") && !this.lazy) {\n\t\t\tconst start = this.page * this.rowsPerPage;\n\t\t\tout = out.slice(start, start + this.rowsPerPage);\n\t\t}\n\n\t\treturn { rows: out, total };\n\t}\n\n\tprivate _getSearchFields(): string[] {\n\t\tconst input = this.root.querySelector<HTMLInputElement>(\n\t\t\t\"[data-search-fields]\",\n\t\t);\n\t\tif (!input) return [];\n\t\tconst attr = input.getAttribute(\"data-search-fields\");\n\t\tif (attr === null) return [];\n\t\treturn attr\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean);\n\t}\n\n\tprivate cellHtml(\n\t\tcol: {\n\t\t\tfield: string;\n\t\t\ttemplate?: HTMLElement;\n\t\t\tdirectTemplate?: string;\n\t\t},\n\t\trow: Record<string, unknown>,\n\t): string {\n\t\tconst template = col.template?.innerHTML ?? col.directTemplate;\n\t\tif (template) return renderCell(template, row, col.field);\n\t\treturn esc(String(row[col.field] ?? \"\"));\n\t}\n\n\tprotected override render(): void {\n\t\tconst cols = this.columns.filter((c) => c.field);\n\t\tconst { rows, total } = this.computeRows();\n\t\tconst paginated = this.hasAttr(\"pagination\");\n\t\tconst pages = Math.max(1, Math.ceil(total / this.rowsPerPage));\n\t\tif (this.page >= pages) this.page = pages - 1;\n\n\t\tconst toolbar = this.toolbarTemplate;\n\t\tconst toolbarHtml = toolbar\n\t\t\t? `<div class=\"toolbar\" part=\"toolbar\">${toolbar.innerHTML}</div>`\n\t\t\t: \"\";\n\n\t\tconst headHtml = cols\n\t\t\t.map((c) => {\n\t\t\t\tconst isSorted = this.sortField === c.field;\n\t\t\t\tconst ind = isSorted\n\t\t\t\t\t? this.sortDir === 1\n\t\t\t\t\t\t? \"▲\"\n\t\t\t\t\t\t: \"▼\"\n\t\t\t\t\t: c.sortable\n\t\t\t\t\t\t? \"⇅\"\n\t\t\t\t\t\t: \"\";\n\t\t\t\tconst activeClass = isSorted ? \"active\" : \"\";\n\t\t\t\treturn `<th part=\"header\" class=\"${c.sortable ? \"sortable\" : \"\"}\" style=\"text-align:${c.align}\" data-field=\"${esc(c.field)}\">${esc(c.header)}${ind ? `<span class=\"sort-ind ${activeClass}\">${ind}</span>` : \"\"}</th>`;\n\t\t\t})\n\t\t\t.join(\"\");\n\n\t\tconst filterRow = cols.some((c) => c.filterable)\n\t\t\t? `<tr class=\"filter-row\">${cols.map((c) => `<th style=\"text-align:${c.align}\">${c.filterable ? `<input class=\"filter\" data-field=\"${esc(c.field)}\" placeholder=\"${esc(\"Filtrar…\")}\" value=\"${esc(this.filters[c.field] ?? \"\")}\">` : \"\"}</th>`).join(\"\")}</tr>`\n\t\t\t: \"\";\n\n\t\tconst bodyHtml = rows.length\n\t\t\t? rows\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(row, i) =>\n\t\t\t\t\t\t\t`<tr part=\"row\" data-index=\"${i}\">${cols.map((c) => `<td part=\"cell\" style=\"text-align:${c.align}\">${this.cellHtml(c, row)}</td>`).join(\"\")}</tr>`,\n\t\t\t\t\t)\n\t\t\t\t\t.join(\"\")\n\t\t\t: `<tr><td class=\"empty\" colspan=\"${cols.length || 1}\">${esc(this.getAttr(\"empty-message\", \"Nenhum registro encontrado\"))}</td></tr>`;\n\n\t\tconst pagerHtml = !paginated\n\t\t\t? \"\"\n\t\t\t: `\n <div class=\"pager\" part=\"pager\">\n <span class=\"info\">Página ${this.page + 1} de ${pages} · ${total} registros</span>\n <button type=\"button\" class=\"pg-btn\" part=\"first\" aria-label=\"Primeira página\" data-pg=\"first\" ${this.page === 0 ? \"disabled\" : \"\"}>${fenixIconHtml(\"first_page\")}</button>\n <button type=\"button\" class=\"pg-btn\" part=\"prev\" aria-label=\"Página anterior\" data-pg=\"prev\" ${this.page === 0 ? \"disabled\" : \"\"}>${fenixIconHtml(\"navigate_before\")}</button>\n ${Array.from({ length: pages }, (_, p) => `<button type=\"button\" class=\"pg-btn\" data-pg=\"${p}\" aria-current=\"${p === this.page}\">${p + 1}</button>`).join(\"\")}\n <button type=\"button\" class=\"pg-btn\" part=\"next\" aria-label=\"Próxima página\" data-pg=\"next\" ${this.page >= pages - 1 ? \"disabled\" : \"\"}>${fenixIconHtml(\"navigate_next\")}</button>\n <button type=\"button\" class=\"pg-btn\" part=\"last\" aria-label=\"Última página\" data-pg=\"last\" ${this.page >= pages - 1 ? \"disabled\" : \"\"}>${fenixIconHtml(\"last_page\")}</button>\n <label class=\"info\">${esc(\"Por página:\")}\n <fx-select class=\"rows-sel\" size=\"sm\" value=\"${this.rowsPerPage}\" aria-label=\"Itens por página\">${this.getAttr(\n\t\t\t\t\t\"rows-options\",\n\t\t\t\t\t\"5,10,20,50\",\n\t\t\t\t)\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((n) => n.trim())\n\t\t\t\t\t.map((n) => `<option value=\"${esc(n)}\">${esc(n)}</option>`)\n\t\t\t\t\t.join(\"\")}</fx-select>\n </label>\n </div>`;\n\n\t\tconst loading = this.hasAttr(\"loading\");\n\t\tconst loadingHtml = loading\n\t\t\t? `<div class=\"loading-overlay\" part=\"loading-overlay\"><div class=\"tbl-spinner\" part=\"spinner\"></div><span class=\"loading-text\">${esc(this.getAttr(\"loading-message\", \"Carregando…\"))}</span></div>`\n\t\t\t: \"\";\n\n\t\tthis.setTemplate(`\n ${toolbarHtml}\n <div class=\"table-wrap\">\n <div class=\"scroll\">\n <table part=\"table\">\n <thead><tr>${headHtml}</tr>${filterRow}</thead>\n <tbody>${bodyHtml}</tbody>\n </table>\n </div>\n ${loadingHtml}\n </div>\n ${pagerHtml}\n `);\n\n\t\t// Ordenação.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLTableCellElement>(\"th.sortable\")\n\t\t\t.forEach((th) => {\n\t\t\t\tth.addEventListener(\"click\", () => {\n\t\t\t\t\tconst f = th.dataset.field!;\n\t\t\t\t\tif (this.sortField === f)\n\t\t\t\t\t\tthis.sortDir = this.sortDir === 1 ? -1 : 1;\n\t\t\t\t\telse {\n\t\t\t\t\t\tthis.sortField = f;\n\t\t\t\t\t\tthis.sortDir = 1;\n\t\t\t\t\t}\n\t\t\t\t\tthis.render();\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"sort-change\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\tfield: this.sortField,\n\t\t\t\t\t\t\t\tdirection: this.sortDir === 1 ? \"asc\" : \"desc\",\n\t\t\t\t\t\t\t\tlazy: this.lazy,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\n\t\t// Filtros (preserva foco/caret ao digitar).\n\t\tthis.root.querySelectorAll<HTMLInputElement>(\".filter\").forEach((inp) => {\n\t\t\tinp.addEventListener(\"input\", () => {\n\t\t\t\tconst field = inp.dataset.field!;\n\t\t\t\tconst value = inp.value;\n\t\t\t\tconst pos = inp.selectionStart;\n\t\t\t\tthis.filters[field] = value;\n\t\t\t\tthis.page = 0;\n\t\t\t\tthis.render();\n\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\tnew CustomEvent(\"filter-change\", {\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\tdetail: { field, value, lazy: this.lazy },\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tconst again = this.root.querySelector<HTMLInputElement>(\n\t\t\t\t\t`.filter[data-field=\"${esc(field)}\"]`,\n\t\t\t\t);\n\t\t\t\tif (again) {\n\t\t\t\t\tagain.focus();\n\t\t\t\t\tagain.setSelectionRange(pos, pos);\n\t\t\t\t}\n\t\t\t});\n\t\t\tinp.addEventListener(\"click\", (e) => e.stopPropagation());\n\t\t});\n\n\t\t// Busca global da toolbar.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLInputElement>(\"[data-search-fields]\")\n\t\t\t.forEach((inp) => {\n\t\t\t\tinp.addEventListener(\"input\", () => {\n\t\t\t\t\tthis.globalSearch = inp.value;\n\t\t\t\t\tthis.page = 0;\n\t\t\t\t\tconst pos = inp.selectionStart;\n\t\t\t\t\tthis.render();\n\t\t\t\t\tconst again = this.root.querySelector<HTMLInputElement>(\n\t\t\t\t\t\t\"[data-search-fields]\",\n\t\t\t\t\t);\n\t\t\t\t\tif (again) {\n\t\t\t\t\t\tagain.focus();\n\t\t\t\t\t\tagain.setSelectionRange(pos, pos);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\n\t\t// Paginação.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLButtonElement>(\".pg-btn\")\n\t\t\t.forEach((btn) => {\n\t\t\t\tbtn.addEventListener(\"click\", () => {\n\t\t\t\t\tconst pg = btn.dataset.pg!;\n\t\t\t\t\tthis.page =\n\t\t\t\t\t\tpg === \"first\"\n\t\t\t\t\t\t\t? 0\n\t\t\t\t\t\t\t: pg === \"prev\"\n\t\t\t\t\t\t\t\t? Math.max(0, this.page - 1)\n\t\t\t\t\t\t\t\t: pg === \"next\"\n\t\t\t\t\t\t\t\t\t? Math.min(pages - 1, this.page + 1)\n\t\t\t\t\t\t\t\t\t: pg === \"last\"\n\t\t\t\t\t\t\t\t\t\t? pages - 1\n\t\t\t\t\t\t\t\t\t\t: Number(pg);\n\t\t\t\t\tthis.render();\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"page-change\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\tpage: this.page + 1,\n\t\t\t\t\t\t\t\tpages,\n\t\t\t\t\t\t\t\trowsPerPage: this.rowsPerPage,\n\t\t\t\t\t\t\t\ttotal,\n\t\t\t\t\t\t\t\tlazy: this.lazy,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\t\tthis.root\n\t\t\t.querySelector<HTMLElement>(\"fx-select.rows-sel\")\n\t\t\t?.addEventListener(\"change\", (e) => {\n\t\t\t\tconst value = Number((e as CustomEvent).detail?.value);\n\t\t\t\tif (!value || value === this.rowsPerPage) return;\n\t\t\t\tthis.rowsPerPage = value;\n\t\t\t\tthis.page = 0;\n\t\t\t\tthis.render();\n\t\t\t});\n\n\t\t// Clique na linha.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLTableRowElement>(\"tbody tr[data-index]\")\n\t\t\t.forEach((tr) => {\n\t\t\t\ttr.addEventListener(\"click\", () => {\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"row-click\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\trow: rows[Number(tr.dataset.index)],\n\t\t\t\t\t\t\t\tindex: Number(tr.dataset.index),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\n\t\tthis._restoreToolbarSearch();\n\t}\n\n\tprivate _restoreToolbarSearch(): void {\n\t\tconst input = this.root.querySelector<HTMLInputElement>(\n\t\t\t\"[data-search-fields]\",\n\t\t);\n\t\tif (input && this.globalSearch) input.value = this.globalSearch;\n\t}\n}\n\nexport function defineFxTable(): typeof FxTable {\n return defineElement('fx-table', FxTable);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"table.js","sources":["../../../src/components/table/table.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from \"../../core/sanitize\";\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\nimport { loadFenixIconsFont } from '../../icons/font';\nimport { renderCell } from \"./expr\";\nimport \"../select\";\n\n/**\n * <fx-table> — Tabela de dados estilo DataTable (dados + colunas configuráveis,\n * ordenação, filtros por coluna, paginação personalizável, busca global na toolbar\n * e modo lazy para busca no servidor).\n *\n * Colunas (light DOM):\n * <fx-column field=\"nome\" header=\"Nome\" sortable filterable align=\"left\">\n * opcional: <fx-cell>R$ {{value}}</fx-cell> para render customizado\n * ou conteúdo direto (HTML + {{ }}) como template alternativo\n * </fx-column>\n *\n * IMPORTANTE (frameworks): use <fx-cell>, NÃO <template>. A tag nativa\n * <template> é reservada — o Vue 3 a transforma em bloco do compilador e o\n * React gerencia os filhos, então o innerHTML chega vazio. <fx-cell> é uma\n * tag própria, sem significado especial para nenhum framework. A forma\n * antiga com <template> continua funcionando (compatibilidade) e há ainda\n * a opção `template=\"#id\"` apontando para um <template> externo.\n *\n * ⚠️ REGRA DO v-pre (Vue) — o conteúdo do <fx-cell> é TEXTO CRU:\n * {{ }} é avaliado pelo motor de expressões do <fx-table> (./expr.ts) e\n * NÃO pelo Vue. Sem `v-pre`, o compilador do Vue consome a interpolação\n * antes: emite `_toDisplayString(_ctx.value | _ctx.number)`, o conteúdo\n * chega sem as chaves no innerHTML e o vue-tsc acusa `value`/`number`\n * inexistentes no escopo. → SEMPRE use <fx-cell v-pre> no Vue.\n *\n * Cada framework tem a sua neutralização (todas verificadas por compilador):\n * - Vue 3 / Nuxt .... v-pre <fx-cell v-pre>{{ value }}</fx-cell>\n * - React / JSX ..... string JS <fx-cell>{'{{ value }}'}</fx-cell>\n * (o `{{ }}` solto é ERRO de sintaxe no parser JSX)\n * - Angular ......... ngNonBindable <fx-cell ngNonBindable>…</fx-cell>\n * - Svelte .......... string JS ou entidades HTML\n * {'{{ value }}'} ou {{ value }}\n * - HTML/CDN/JSF/JSP nada <fx-cell>{{ value }}</fx-cell>\n *\n * Toolbar (opcional):\n * <fx-toolbar>\n * <input data-search-fields=\"nome,cargo\" placeholder=\"Buscar…\">\n * <button>Nova ação</button>\n * </fx-toolbar>\n * (legado ainda aceito: <template slot=\"toolbar\">…</template>)\n *\n * Dados: propriedade `data` (array de objetos) ou atributo `data` (JSON).\n *\n * Atributos: pagination, rows, rows-options, pagination-position (left|center|right),\n * striped, hover, empty-message, lazy, total, loading, loading-message,\n * sort-field, sort-order.\n * Eventos: page-change, sort-change, row-click, filter-change (todos composed).\n */\nexport class FxTable extends FxElement {\n\tstatic override styles = css`\n\t\t${FENIX_ICON_BASE_CSS}\n\t\t:host {\n\t\t\tdisplay: block;\n\t\t\tfont-family: var(--fx-font-family);\n\t\t\tfont-size: var(--fx-font-size);\n\t\t\tcolor: var(--fx-text-default);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t}\n\t\t.table-wrap {\n\t\t\tposition: relative;\n\t\t}\n\t\t.scroll {\n\t\t\toverflow-x: auto;\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-radius-md);\n\t\t}\n\t\ttable {\n\t\t\twidth: 100%;\n\t\t\tborder-collapse: collapse;\n\t\t}\n\t\tth {\n\t\t\ttext-align: left;\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tbackground: var(--fx-surface-surface);\n\t\t\tborder-bottom: 2px solid var(--fx-border-default);\n\t\t\twhite-space: nowrap;\n\t\t\tuser-select: none;\n\t\t}\n\t\tth.sortable {\n\t\t\tcursor: pointer;\n\t\t}\n\t\tth.sortable:hover {\n\t\t\tcolor: var(--fx-color-primary);\n\t\t}\n\t\t.sort-ind {\n\t\t\tfont-size: calc(var(--fx-font-size) - 4px);\n\t\t\tmargin-left: var(--fx-space-3xs, 2px);\n\t\t\topacity: 0.45;\n\t\t\ttransition: opacity var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\tth.sortable:hover .sort-ind {\n\t\t\topacity: 0.85;\n\t\t}\n\t\t.sort-ind.active {\n\t\t\topacity: 1;\n\t\t\tcolor: var(--fx-color-primary);\n\t\t}\n\t\t.filter-row th {\n\t\t\tpadding: var(--fx-space-3xs, 4px) var(--fx-space-md) var(--fx-space-sm);\n\t\t\tborder-bottom-width: 1px;\n\t\t}\n\t\t.filter {\n\t\t\twidth: 100%;\n\t\t\tbox-sizing: border-box;\n\t\t\tfont: inherit;\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t\tcolor: var(--fx-text-default);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-radius-sm);\n\t\t\tpadding: var(--fx-space-3xs, 4px) var(--fx-space-xs);\n\t\t\toutline: none;\n\t\t}\n\t\t.filter:focus {\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tbox-shadow: var(--fx-effect-focus-ring, none);\n\t\t}\n\t\ttd {\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tborder-bottom: 1px solid var(--fx-border-default);\n\t\t}\n\t\ttbody tr {\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\t/* Listrado: alterna a superfície (cinza). Antes usava\n\t\t --fx-surface-background, que é a MESMA cor do fundo do :host e\n\t\t portanto pintava branco sobre branco (listrado invisível). */\n\t\t:host([striped]) tbody tr:nth-child(even) {\n\t\t\tbackground: var(--fx-surface-surface, #f8fafc);\n\t\t}\n\t\t/* O hover é opt-in via atributo (o clique na linha continua sempre ativo). */\n\t\t:host([hover]) tbody tr:hover {\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--fx-color-primary) 8%,\n\t\t\t\ttransparent\n\t\t\t);\n\t\t}\n\t\t.empty {\n\t\t\ttext-align: center;\n\t\t\tpadding: var(--fx-space-xl);\n\t\t\tcolor: var(--fx-text-muted);\n\t\t}\n\t\t.pager {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tflex-wrap: wrap;\n\t\t\tpadding: var(--fx-space-sm) 0;\n\t\t}\n\t\t:host([pagination-position=\"center\"]) .pager {\n\t\t\tjustify-content: center;\n\t\t}\n\t\t:host([pagination-position=\"right\"]) .pager {\n\t\t\tjustify-content: flex-end;\n\t\t}\n\t\t.pager .info {\n\t\t\tcolor: var(--fx-text-muted);\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t}\n\t\t.pg-btn {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tmin-width: var(--fx-size-sm);\n\t\t\theight: var(--fx-size-sm);\n\t\t\tfont: inherit;\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t\tcolor: var(--fx-text-default);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-radius-sm);\n\t\t\tpadding: var(--fx-space-3xs, 4px) var(--fx-space-sm);\n\t\t\tcursor: pointer;\n\t\t\ttransition: border-color var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\t/* O glifo precisa de 1px a mais que o dígito: a altura de traço do ícone é\n\t\t maior que a do número, então igualar os dois font-size deixa a seta\n\t\t visivelmente maior que o texto. */\n\t\t.pg-btn .fx-icon {\n\t\t\tfont-size: calc(var(--fx-font-size) - 1px);\n\t\t}\n\t\t.pg-btn:hover:not(:disabled):not([aria-current=\"true\"]) {\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tcolor: var(--fx-color-primary);\n\t\t}\n\t\t.pg-btn:disabled {\n\t\t\topacity: 0.5;\n\t\t\tcursor: not-allowed;\n\t\t}\n\t\t.pg-btn[aria-current=\"true\"] {\n\t\t\tbackground: var(--fx-color-primary);\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tcolor: #fff;\n\t\t\tfont-weight: var(--fx-font-weight);\n\t\t}\n\t\tfx-select {\n\t\t\tvertical-align: middle;\n\t\t}\n\t\tfx-select::part(trigger) {\n\t\t\tmin-width: 64px !important;\n\t\t\tmin-height: var(--fx-size-sm) !important;\n\t\t\tpadding: 0 var(--fx-space-sm) !important;\n\t\t\tborder-radius: var(--fx-radius-sm) !important;\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px) !important;\n\t\t}\n\t\t.toolbar {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tflex-wrap: wrap;\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-bottom: none;\n\t\t\tborder-radius: var(--fx-radius-md) var(--fx-radius-md) 0 0;\n\t\t\tbackground: var(--fx-surface-background);\n\t\t}\n\t\t.toolbar:empty {\n\t\t\tdisplay: none;\n\t\t}\n\t\t.loading-overlay {\n\t\t\tposition: absolute;\n\t\t\tinset: 0;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--fx-surface-background) 85%,\n\t\t\t\ttransparent\n\t\t\t);\n\t\t\tbackdrop-filter: blur(2px);\n\t\t\tz-index: 10;\n\t\t\tborder-radius: var(--fx-radius-md);\n\t\t}\n\t\t.tbl-spinner {\n\t\t\twidth: 32px;\n\t\t\theight: 32px;\n\t\t\tborder: 3px solid var(--fx-border-default);\n\t\t\tborder-top-color: var(--fx-color-primary);\n\t\t\tborder-radius: 50%;\n\t\t\tanimation: tbl-spin 1s linear infinite;\n\t\t}\n\t\t@keyframes tbl-spin {\n\t\t\tto {\n\t\t\t\ttransform: rotate(360deg);\n\t\t\t}\n\t\t}\n\t\t.loading-text {\n\t\t\tcolor: var(--fx-text-muted);\n\t\t\tfont-size: calc(var(--fx-font-size) - 1px);\n\t\t}\n\t`;\n\n\tstatic override get observedAttributes(): string[] {\n\t\treturn [\n\t\t\t\"pagination\",\n\t\t\t\"rows\",\n\t\t\t\"rows-options\",\n\t\t\t\"striped\",\n\t\t\t\"hover\",\n\t\t\t\"toolbar\",\n\t\t\t\"empty-message\",\n\t\t\t\"pagination-position\",\n\t\t\t\"lazy\",\n\t\t\t\"total\",\n\t\t\t\"loading\",\n\t\t\t\"loading-message\",\n\t\t\t\"sort-field\",\n\t\t\t\"sort-order\",\n\t\t];\n\t}\n\n\tprivate _data: Record<string, unknown>[] = [];\n\tprivate _total?: number;\n\tprivate page = 0;\n\tprivate sortField = \"\";\n\tprivate sortDir: 1 | -1 = 1;\n\tprivate filters: Record<string, string> = {};\n\tprivate globalSearch = \"\";\n\tprivate _columnObserver?: MutationObserver;\n\n\tget data(): Record<string, unknown>[] {\n\t\treturn this._data;\n\t}\n\tset data(value: Record<string, unknown>[]) {\n\t\tthis._data = Array.isArray(value) ? value : [];\n\t\tthis.render();\n\t}\n\n\tget total(): number {\n\t\treturn this._total ?? this._data.length;\n\t}\n\tset total(value: number) {\n\t\tthis._total = value;\n\t\tthis.setAttribute(\"total\", String(value));\n\t}\n\n\tget rowsPerPage(): number {\n\t\tconst r = Number(this.getAttr(\"rows\", \"10\"));\n\t\treturn r > 0 ? r : 10;\n\t}\n\tset rowsPerPage(value: number) {\n\t\tthis.setAttribute(\"rows\", String(value));\n\t}\n\n\tget lazy(): boolean {\n\t\treturn this.hasAttr(\"lazy\");\n\t}\n\n\tprivate get columns(): {\n\t\tfield: string;\n\t\theader: string;\n\t\tsortable: boolean;\n\t\tfilterable: boolean;\n\t\talign: string;\n\t\ttemplate?: HTMLElement;\n\t\tdirectTemplate?: string;\n\t}[] {\n\t\treturn [...this.querySelectorAll(\"fx-column\")].map((c) => {\n\t\t\tconst tpl = this._resolveCellTemplate(c);\n\t\t\tconst direct = tpl ? undefined : c.innerHTML.trim();\n\t\t\tconst align = c.getAttribute(\"align\") ?? \"left\";\n\t\t\treturn {\n\t\t\t\tfield: c.getAttribute(\"field\") ?? \"\",\n\t\t\t\theader: c.getAttribute(\"header\") ?? c.getAttribute(\"field\") ?? \"\",\n\t\t\t\tsortable: c.hasAttribute(\"sortable\"),\n\t\t\t\tfilterable: c.hasAttribute(\"filterable\"),\n\t\t\t\talign: [\"left\", \"center\", \"right\", \"justify\", \"start\", \"end\"].includes(align) ? align : \"left\",\n\t\t\t\ttemplate: tpl ?? undefined,\n\t\t\t\tdirectTemplate: direct || undefined,\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Resolve o template de célula de uma `<fx-column>`, em ordem de prioridade:\n\t *\n\t * 1. `<fx-cell>` — **recomendado**. Tag própria, sem significado especial\n\t * para Vue/React/Angular/Svelte: nenhum framework a intercepta, então o\n\t * `innerHTML` sempre chega intacto ao componente. O light DOM do\n\t * `<fx-table>` não é exibido (a tabela não usa `<slot>`), então o\n\t * conteúdo-fonte nunca aparece na tela.\n\t *\n\t * ⚠️ O conteúdo é TEXTO CRU para o motor de expressões: o `{{ }}` NÃO é\n\t * interpretado pelo framework hospedeiro. Cada um precisa neutralizar a\n\t * própria sintaxe antes (ver o cabeçalho do arquivo): `v-pre` no Vue,\n\t * `{'{{ }}'}` no JSX, `ngNonBindable` no Angular, string/entidades no\n\t * Svelte. Sem isso o template não chega ao `renderCell`.\n\t * 2. `template=\"#id\"` (ou `template=\"id\"`) — aponta para um `<template>`\n\t * declarado **fora** da tabela, útil em React/Angular, onde é mais\n\t * simples manter o markup num único lugar.\n\t * 3. `<template>` dentro da coluna — forma **legada**, mantida por\n\t * compatibilidade. Em Vue 3 ela vira um bloco do compilador e em React os\n\t * filhos são gerenciados pelo framework; nos dois casos o `innerHTML`\n\t * chega vazio e a célula não renderiza. Prefira `<fx-cell>`.\n\t */\n\tprivate _resolveCellTemplate(c: Element): HTMLElement | null {\n\t\tconst cell = c.querySelector<HTMLElement>(\":scope > fx-cell\");\n\t\tif (cell) return cell;\n\t\tconst ref = c.getAttribute(\"template\");\n\t\tif (ref) {\n\t\t\tconst alvo = this._resolveRef(ref);\n\t\t\tif (alvo) return alvo;\n\t\t}\n\t\treturn c.querySelector<HTMLElement>(\"template\");\n\t}\n\n\tprivate get toolbarTemplate(): HTMLElement | null {\n\t\t// Mesma lógica da célula: `<fx-toolbar>` > `toolbar=\"#id\"` > legado.\n\t\tconst bar = this.querySelector<HTMLElement>(\":scope > fx-toolbar\");\n\t\tif (bar) return bar;\n\t\tconst ref = this.getAttr(\"toolbar\");\n\t\tif (ref) {\n\t\t\tconst alvo = this._resolveRef(ref);\n\t\t\tif (alvo) return alvo;\n\t\t}\n\t\treturn this.querySelector<HTMLElement>('template[slot=\"toolbar\"]');\n\t}\n\n\t/**\n\t * Resolve `ref` para um elemento: `#id`/`id` ⇒ `getElementById` no\n\t * documento; qualquer outro valor é tratado como seletor CSS relativo à\n\t * própria tabela. Valores inválidos devolvem `null` (sem exceção).\n\t */\n\tprivate _resolveRef(ref: string): HTMLElement | null {\n\t\tconst valor = ref.trim();\n\t\tif (!valor) return null;\n\t\tconst doc = this.ownerDocument;\n\t\tif (valor.startsWith('#')) return doc?.getElementById(valor.slice(1)) ?? null;\n\t\tif (/^[.#[>]/.test(valor)) {\n\t\t\ttry {\n\t\t\t\treturn this.querySelector<HTMLElement>(valor);\n\t\t\t} catch {\n\t\t\t\treturn null; // seletor inválido — degrada para o próximo fallback\n\t\t\t}\n\t\t}\n\t\treturn doc?.getElementById(valor) ?? null;\n\t}\n\n\tprotected override connectedCallback(): void {\n\t\tthis._parseDataAttribute();\n\t\tthis._parseSortAttributes();\n\t\t// O pager usa glifos da Fenix Icons: garante o @font-face mesmo quando o\n\t\t// app não importou `@wrrdev/fenix-ui/icons` (idempotente).\n\t\tloadFenixIconsFont();\n\t\tsuper.connectedCallback();\n\t\tthis._columnObserver = new MutationObserver(() => this.render());\n\t\tthis._columnObserver.observe(this, { childList: true, subtree: true });\n\t}\n\n\tprotected override disconnectedCallback(): void {\n\t\tthis._columnObserver?.disconnect();\n\t\tsuper.disconnectedCallback();\n\t}\n\n\t/** Ordenação inicial via `sort-field` / `sort-order` (documentados na API). */\n\tprivate _parseSortAttributes(): void {\n\t\tconst field = this.getAttr(\"sort-field\");\n\t\tif (field) this.sortField = field;\n\t\tconst order = this.getAttr(\"sort-order\");\n\t\tif (order === \"asc\" || order === \"desc\") this.sortDir = order === \"asc\" ? 1 : -1;\n\t}\n\n\tprivate _parseDataAttribute(): void {\n\t\tif (!this._data.length) {\n\t\t\ttry {\n\t\t\t\tconst json = this.getAttr(\"data\");\n\t\t\t\tif (json) this._data = JSON.parse(json);\n\t\t\t} catch {\n\t\t\t\t/* atributo invalido - ignora */\n\t\t\t}\n\t\t}\n\t\tconst totalAttr = this.getAttr(\"total\");\n\t\tif (totalAttr) {\n\t\t\tconst n = Number(totalAttr);\n\t\t\tif (!isNaN(n)) this._total = n;\n\t\t}\n\t}\n\n\tprivate computeRows(): { rows: Record<string, unknown>[]; total: number } {\n\t\tlet out = [...this._data];\n\n\t\tfor (const [field, term] of Object.entries(this.filters)) {\n\t\t\tif (!term) continue;\n\t\t\tconst t = term.toLowerCase();\n\t\t\tout = out.filter((r) =>\n\t\t\t\tString(r[field] ?? \"\")\n\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t.includes(t),\n\t\t\t);\n\t\t}\n\n\t\tif (this.globalSearch) {\n\t\t\tconst t = this.globalSearch.toLowerCase();\n\t\t\tconst searchFields = this._getSearchFields();\n\t\t\tout = out.filter((r) => {\n\t\t\t\tconst fields = searchFields.length ? searchFields : Object.keys(r);\n\t\t\t\treturn fields.some((f) =>\n\t\t\t\t\tString(r[f] ?? \"\")\n\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t.includes(t),\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\n\t\tconst total = this.lazy ? this.total : out.length;\n\n\t\tif (this.sortField) {\n\t\t\tout.sort((a, b) => {\n\t\t\t\tconst av = a[this.sortField],\n\t\t\t\t\tbv = b[this.sortField];\n\t\t\t\tif (typeof av === \"number\" && typeof bv === \"number\")\n\t\t\t\t\treturn (av - bv) * this.sortDir;\n\t\t\t\treturn (\n\t\t\t\t\tString(av ?? \"\").localeCompare(String(bv ?? \"\"), \"pt-BR\") *\n\t\t\t\t\tthis.sortDir\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\n\t\tif (this.hasAttr(\"pagination\") && !this.lazy) {\n\t\t\tconst start = this.page * this.rowsPerPage;\n\t\t\tout = out.slice(start, start + this.rowsPerPage);\n\t\t}\n\n\t\treturn { rows: out, total };\n\t}\n\n\tprivate _getSearchFields(): string[] {\n\t\tconst input = this.root.querySelector<HTMLInputElement>(\n\t\t\t\"[data-search-fields]\",\n\t\t);\n\t\tif (!input) return [];\n\t\tconst attr = input.getAttribute(\"data-search-fields\");\n\t\tif (attr === null) return [];\n\t\treturn attr\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean);\n\t}\n\n\tprivate cellHtml(\n\t\tcol: {\n\t\t\tfield: string;\n\t\t\ttemplate?: HTMLElement;\n\t\t\tdirectTemplate?: string;\n\t\t},\n\t\trow: Record<string, unknown>,\n\t): string {\n\t\tconst template = col.template?.innerHTML ?? col.directTemplate;\n\t\tif (template) return renderCell(template, row, col.field);\n\t\treturn esc(String(row[col.field] ?? \"\"));\n\t}\n\n\t/**\n\t * Assinatura da \"casca\" da tabela: tudo que NÃO depende das linhas\n\t * (colunas, toolbar, filtros, paginação, loading).\n\t *\n\t * Serve para o caminho rápido: quando só `data` muda, a casca é idêntica e\n\t * podemos trocar apenas o `<tbody>`, sem recriar `<style>`, `<thead>`,\n\t * toolbar e paginação — nem religar todos os listeners.\n\t */\n\tprivate _shellKey = '';\n\n\tprotected override render(): void {\n\t\tconst cols = this.columns.filter((c) => c.field);\n\t\tconst { rows, total } = this.computeRows();\n\t\tconst paginated = this.hasAttr(\"pagination\");\n\t\tconst pages = Math.max(1, Math.ceil(total / this.rowsPerPage));\n\t\tif (this.page >= pages) this.page = pages - 1;\n\n\t\tconst toolbar = this.toolbarTemplate;\n\t\tconst toolbarHtml = toolbar\n\t\t\t? `<div class=\"toolbar\" part=\"toolbar\">${toolbar.innerHTML}</div>`\n\t\t\t: \"\";\n\n\t\tconst headHtml = cols\n\t\t\t.map((c) => {\n\t\t\t\tconst isSorted = this.sortField === c.field;\n\t\t\t\tconst ind = isSorted\n\t\t\t\t\t? this.sortDir === 1\n\t\t\t\t\t\t? \"▲\"\n\t\t\t\t\t\t: \"▼\"\n\t\t\t\t\t: c.sortable\n\t\t\t\t\t\t? \"⇅\"\n\t\t\t\t\t\t: \"\";\n\t\t\t\tconst activeClass = isSorted ? \"active\" : \"\";\n\t\t\t\treturn `<th part=\"header\" class=\"${c.sortable ? \"sortable\" : \"\"}\" style=\"text-align:${c.align}\" data-field=\"${esc(c.field)}\">${esc(c.header)}${ind ? `<span class=\"sort-ind ${activeClass}\">${ind}</span>` : \"\"}</th>`;\n\t\t\t})\n\t\t\t.join(\"\");\n\n\t\tconst filterRow = cols.some((c) => c.filterable)\n\t\t\t? `<tr class=\"filter-row\">${cols.map((c) => `<th style=\"text-align:${c.align}\">${c.filterable ? `<input class=\"filter\" data-field=\"${esc(c.field)}\" placeholder=\"${esc(\"Filtrar…\")}\" value=\"${esc(this.filters[c.field] ?? \"\")}\">` : \"\"}</th>`).join(\"\")}</tr>`\n\t\t\t: \"\";\n\n\t\tconst bodyHtml = rows.length\n\t\t\t? rows\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(row, i) =>\n\t\t\t\t\t\t\t`<tr part=\"row\" data-index=\"${i}\">${cols.map((c) => `<td part=\"cell\" style=\"text-align:${c.align}\">${this.cellHtml(c, row)}</td>`).join(\"\")}</tr>`,\n\t\t\t\t\t)\n\t\t\t\t\t.join(\"\")\n\t\t\t: `<tr><td class=\"empty\" colspan=\"${cols.length || 1}\">${esc(this.getAttr(\"empty-message\", \"Nenhum registro encontrado\"))}</td></tr>`;\n\n\t\tconst pagerHtml = !paginated\n\t\t\t? \"\"\n\t\t\t: `\n <div class=\"pager\" part=\"pager\">\n <span class=\"info\">Página ${this.page + 1} de ${pages} · ${total} registros</span>\n <button type=\"button\" class=\"pg-btn\" part=\"first\" aria-label=\"Primeira página\" data-pg=\"first\" ${this.page === 0 ? \"disabled\" : \"\"}>${fenixIconHtml(\"first_page\")}</button>\n <button type=\"button\" class=\"pg-btn\" part=\"prev\" aria-label=\"Página anterior\" data-pg=\"prev\" ${this.page === 0 ? \"disabled\" : \"\"}>${fenixIconHtml(\"navigate_before\")}</button>\n ${Array.from({ length: pages }, (_, p) => `<button type=\"button\" class=\"pg-btn\" data-pg=\"${p}\" aria-current=\"${p === this.page}\">${p + 1}</button>`).join(\"\")}\n <button type=\"button\" class=\"pg-btn\" part=\"next\" aria-label=\"Próxima página\" data-pg=\"next\" ${this.page >= pages - 1 ? \"disabled\" : \"\"}>${fenixIconHtml(\"navigate_next\")}</button>\n <button type=\"button\" class=\"pg-btn\" part=\"last\" aria-label=\"Última página\" data-pg=\"last\" ${this.page >= pages - 1 ? \"disabled\" : \"\"}>${fenixIconHtml(\"last_page\")}</button>\n <label class=\"info\">${esc(\"Por página:\")}\n <fx-select class=\"rows-sel\" size=\"sm\" value=\"${this.rowsPerPage}\" aria-label=\"Itens por página\">${this.getAttr(\n\t\t\t\t\t\"rows-options\",\n\t\t\t\t\t\"5,10,20,50\",\n\t\t\t\t)\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((n) => n.trim())\n\t\t\t\t\t.map((n) => `<option value=\"${esc(n)}\">${esc(n)}</option>`)\n\t\t\t\t\t.join(\"\")}</fx-select>\n </label>\n </div>`;\n\n\t\tconst loading = this.hasAttr(\"loading\");\n\t\tconst loadingHtml = loading\n\t\t\t? `<div class=\"loading-overlay\" part=\"loading-overlay\"><div class=\"tbl-spinner\" part=\"spinner\"></div><span class=\"loading-text\">${esc(this.getAttr(\"loading-message\", \"Carregando…\"))}</span></div>`\n\t\t\t: \"\";\n\n\t\t// Caminho rápido: se a casca não mudou (só `data` foi substituído),\n\t\t// troca APENAS o <tbody>. Evita recriar <style>/<thead>/toolbar/pager e\n\t\t// religar todos os listeners a cada atribuição de `data` — que é o\n\t\t// gargalo em tabelas grandes (busca por tecla, paginação, reload).\n\t\tconst shellKey = [\n\t\t\ttoolbarHtml,\n\t\t\theadHtml,\n\t\t\tfilterRow,\n\t\t\tpagerHtml,\n\t\t\tloadingHtml,\n\t\t\tcols.length,\n\t\t].join(\"|\");\n\t\tif (shellKey === this._shellKey) {\n\t\t\tconst tbody = this.root.querySelector(\"tbody\");\n\t\t\tif (tbody) {\n\t\t\t\ttbody.innerHTML = bodyHtml;\n\t\t\t\tthis._bindRowClicks(rows);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthis._shellKey = shellKey;\n\n\t\tthis.setTemplate(`\n ${toolbarHtml}\n <div class=\"table-wrap\">\n <div class=\"scroll\">\n <table part=\"table\">\n <thead><tr>${headHtml}</tr>${filterRow}</thead>\n <tbody>${bodyHtml}</tbody>\n </table>\n </div>\n ${loadingHtml}\n </div>\n ${pagerHtml}\n `);\n\n\t\t// Ordenação.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLTableCellElement>(\"th.sortable\")\n\t\t\t.forEach((th) => {\n\t\t\t\tth.addEventListener(\"click\", () => {\n\t\t\t\t\tconst f = th.dataset.field!;\n\t\t\t\t\tif (this.sortField === f)\n\t\t\t\t\t\tthis.sortDir = this.sortDir === 1 ? -1 : 1;\n\t\t\t\t\telse {\n\t\t\t\t\t\tthis.sortField = f;\n\t\t\t\t\t\tthis.sortDir = 1;\n\t\t\t\t\t}\n\t\t\t\t\tthis.render();\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"sort-change\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\tfield: this.sortField,\n\t\t\t\t\t\t\t\tdirection: this.sortDir === 1 ? \"asc\" : \"desc\",\n\t\t\t\t\t\t\t\tlazy: this.lazy,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\n\t\t// Filtros (preserva foco/caret ao digitar).\n\t\tthis.root.querySelectorAll<HTMLInputElement>(\".filter\").forEach((inp) => {\n\t\t\tinp.addEventListener(\"input\", () => {\n\t\t\t\tconst field = inp.dataset.field!;\n\t\t\t\tconst value = inp.value;\n\t\t\t\tconst pos = inp.selectionStart;\n\t\t\t\tthis.filters[field] = value;\n\t\t\t\tthis.page = 0;\n\t\t\t\tthis.render();\n\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\tnew CustomEvent(\"filter-change\", {\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\tdetail: { field, value, lazy: this.lazy },\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tconst again = this.root.querySelector<HTMLInputElement>(\n\t\t\t\t\t`.filter[data-field=\"${esc(field)}\"]`,\n\t\t\t\t);\n\t\t\t\tif (again) {\n\t\t\t\t\tagain.focus();\n\t\t\t\t\tagain.setSelectionRange(pos, pos);\n\t\t\t\t}\n\t\t\t});\n\t\t\tinp.addEventListener(\"click\", (e) => e.stopPropagation());\n\t\t});\n\n\t\t// Busca global da toolbar.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLInputElement>(\"[data-search-fields]\")\n\t\t\t.forEach((inp) => {\n\t\t\t\tinp.addEventListener(\"input\", () => {\n\t\t\t\t\tthis.globalSearch = inp.value;\n\t\t\t\t\tthis.page = 0;\n\t\t\t\t\tconst pos = inp.selectionStart;\n\t\t\t\t\tthis.render();\n\t\t\t\t\tconst again = this.root.querySelector<HTMLInputElement>(\n\t\t\t\t\t\t\"[data-search-fields]\",\n\t\t\t\t\t);\n\t\t\t\t\tif (again) {\n\t\t\t\t\t\tagain.focus();\n\t\t\t\t\t\tagain.setSelectionRange(pos, pos);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\n\t\t// Paginação.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLButtonElement>(\".pg-btn\")\n\t\t\t.forEach((btn) => {\n\t\t\t\tbtn.addEventListener(\"click\", () => {\n\t\t\t\t\tconst pg = btn.dataset.pg!;\n\t\t\t\t\tthis.page =\n\t\t\t\t\t\tpg === \"first\"\n\t\t\t\t\t\t\t? 0\n\t\t\t\t\t\t\t: pg === \"prev\"\n\t\t\t\t\t\t\t\t? Math.max(0, this.page - 1)\n\t\t\t\t\t\t\t\t: pg === \"next\"\n\t\t\t\t\t\t\t\t\t? Math.min(pages - 1, this.page + 1)\n\t\t\t\t\t\t\t\t\t: pg === \"last\"\n\t\t\t\t\t\t\t\t\t\t? pages - 1\n\t\t\t\t\t\t\t\t\t\t: Number(pg);\n\t\t\t\t\tthis.render();\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"page-change\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\tpage: this.page + 1,\n\t\t\t\t\t\t\t\tpages,\n\t\t\t\t\t\t\t\trowsPerPage: this.rowsPerPage,\n\t\t\t\t\t\t\t\ttotal,\n\t\t\t\t\t\t\t\tlazy: this.lazy,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\t\tthis.root\n\t\t\t.querySelector<HTMLElement>(\"fx-select.rows-sel\")\n\t\t\t?.addEventListener(\"change\", (e) => {\n\t\t\t\tconst value = Number((e as CustomEvent).detail?.value);\n\t\t\t\tif (!value || value === this.rowsPerPage) return;\n\t\t\t\tthis.rowsPerPage = value;\n\t\t\t\tthis.page = 0;\n\t\t\t\tthis.render();\n\t\t\t});\n\n\t\t// Clique na linha.\n\t\tthis._bindRowClicks(rows);\n\n\t\tthis._restoreToolbarSearch();\n\t}\n\n\t/**\n\t * Liga o clique das linhas ao evento `row-click`.\n\t *\n\t * Extraído do render para ser reaproveitado no caminho rápido (troca só do\n\t * <tbody>): os <tr> são novos, então precisam ser rebound.\n\t */\n\tprivate _bindRowClicks(rows: Record<string, unknown>[]): void {\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLTableRowElement>(\"tbody tr[data-index]\")\n\t\t\t.forEach((tr) => {\n\t\t\t\ttr.addEventListener(\"click\", () => {\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"row-click\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\trow: rows[Number(tr.dataset.index)],\n\t\t\t\t\t\t\t\tindex: Number(tr.dataset.index),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\t}\n\n\tprivate _restoreToolbarSearch(): void {\n\t\tconst input = this.root.querySelector<HTMLInputElement>(\n\t\t\t\"[data-search-fields]\",\n\t\t);\n\t\tif (input && this.globalSearch) input.value = this.globalSearch;\n\t}\n}\n\nexport function defineFxTable(): typeof FxTable {\n return defineElement('fx-table', FxTable);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -49,6 +49,14 @@ export declare class FxToast extends HTMLElement {
|
|
|
49
49
|
private timer;
|
|
50
50
|
constructor();
|
|
51
51
|
connectedCallback(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Limpa recursos ao sair do DOM e avisa a API imperativa (`FenixToast`).
|
|
54
|
+
*
|
|
55
|
+
* O aviso é essencial: `dismiss()` (timer ou botão de fechar) tira o
|
|
56
|
+
* elemento do DOM mas, sem isto, `ToastApi.map` continuaria segurando uma
|
|
57
|
+
* referência forte para cada toast já dispensado — crescimento de memória
|
|
58
|
+
* sem limite em uso prolongado.
|
|
59
|
+
*/
|
|
52
60
|
disconnectedCallback(): void;
|
|
53
61
|
attributeChangedCallback(name: string): void;
|
|
54
62
|
dismiss(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../../src/components/toast/toast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,QAAA,MAAM,SAAS,kGAGL,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AACvD,KAAK,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../../src/components/toast/toast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,QAAA,MAAM,SAAS,kGAGL,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AACvD,KAAK,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAoI1D,qBAAa,OAAQ,SAAQ,WAAW;;IACtC,MAAM,KAAK,kBAAkB,aAE5B;IAED;;;;;;;;;;;OAWG;IACH,IAAI,QAAQ,IAAI,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAIxD;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,EAG5D;IAED;;;;;;;;;;OAUG;IACH,IAAI,cAAc,IAAI,MAAM,GAAG,OAAO,CAErC;IACD,IAAI,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAEzC;IAED,oEAAoE;IACpE,OAAO,KAAK,YAAY,GAIvB;IAED,qFAAqF;IACrF,IAAI,IAAI,IAAI,OAAO,GAAG,MAAM,GAAG,SAAS,CAGvC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,EAG3C;IAED,OAAO,CAAC,KAAK,CAA8C;;IAO3D,iBAAiB;IAWjB;;;;;;;OAOG;IACH,oBAAoB;IAMpB,wBAAwB,CAAC,IAAI,EAAE,MAAM;IA6CrC,OAAO;CAqCR;AA8BD,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;IACzC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,0EAA0E;IAC1E,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACzB;AAED,cAAM,QAAQ;IACZ,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,GAAG,CAA8B;IAEzC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,MAAM;IA0BxG,KAAK,CAAC,EAAE,EAAE,MAAM;IAKhB,OAAO,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAmC;IACrF,KAAK,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAiC;IACjF,IAAI,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAgC;IAC/E,OAAO,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAmC;CACtF;AAED,eAAO,MAAM,UAAU,UAAiB,CAAC;AAIzC,wBAAgB,aAAa,SAE5B"}
|
|
@@ -6,6 +6,7 @@ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot
|
|
|
6
6
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
7
7
|
var _FxToast_instances, syncProgress_fn, render_fn;
|
|
8
8
|
import { __fenixPreload } from "../../_virtual/preload-helper.js";
|
|
9
|
+
const REMOVED_EVENT = "fx-toast:removed";
|
|
9
10
|
const KIND_COLOR = {
|
|
10
11
|
success: "var(--fx-color-success, #10b981)",
|
|
11
12
|
error: "var(--fx-color-danger, #f43f5e)",
|
|
@@ -196,9 +197,18 @@ class FxToast extends HTMLElement {
|
|
|
196
197
|
}
|
|
197
198
|
__privateMethod(this, _FxToast_instances, syncProgress_fn).call(this);
|
|
198
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Limpa recursos ao sair do DOM e avisa a API imperativa (`FenixToast`).
|
|
202
|
+
*
|
|
203
|
+
* O aviso é essencial: `dismiss()` (timer ou botão de fechar) tira o
|
|
204
|
+
* elemento do DOM mas, sem isto, `ToastApi.map` continuaria segurando uma
|
|
205
|
+
* referência forte para cada toast já dispensado — crescimento de memória
|
|
206
|
+
* sem limite em uso prolongado.
|
|
207
|
+
*/
|
|
199
208
|
disconnectedCallback() {
|
|
200
209
|
if (this.timer) clearTimeout(this.timer);
|
|
201
210
|
this.timer = null;
|
|
211
|
+
this.dispatchEvent(new CustomEvent(REMOVED_EVENT, { bubbles: false, composed: false }));
|
|
202
212
|
}
|
|
203
213
|
attributeChangedCallback(name) {
|
|
204
214
|
if (name === "duration" || name === "progress" || name === "progress-origin" || name === "kind") {
|
|
@@ -320,6 +330,9 @@ class ToastApi {
|
|
|
320
330
|
if (o.progress) el.setAttribute("progress", o.progress);
|
|
321
331
|
if (o.progressOrigin) el.setAttribute("progress-origin", o.progressOrigin);
|
|
322
332
|
regionFor(o.position ?? "top-right").appendChild(el);
|
|
333
|
+
el.addEventListener(REMOVED_EVENT, () => {
|
|
334
|
+
if (this.map.get(id) === el) this.map.delete(id);
|
|
335
|
+
});
|
|
323
336
|
this.map.set(id, el);
|
|
324
337
|
return id;
|
|
325
338
|
}
|