@wrrdev/fenix-ui 1.1.17 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 { pageRange } from '../../core/page-range';\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 &lbrace;&lbrace; value &rbrace;&rbrace;\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, rounded (none|sm|md|lg, padrão md; `radius` é alias),\n * no-borders-x (remove apenas as bordas laterais, mantendo topo e base).\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\t/* Raio da moldura (tabela + paginação). O atributo \"rounded\"\n\t\t\t sobrescreve este valor e o consumidor também pode sobrescrever\n\t\t\t por CSS: fx-table { --fx-table-radius: 20px; }. */\n\t\t\t--fx-table-radius: var(--fx-radius-md);\n\t\t}\n\t\t/* Arredondamento das bordas — \"none\" deixa tudo reto, como nos demais\n\t\t tokens de radius do design system. \"rounded\" é o nome atual; \"radius\"\n\t\t segue aceito como alias para ficar consistente com o <fx-card>. */\n\t\t:host([rounded=\"none\"]),\n\t\t:host([radius=\"none\"]) {\n\t\t\t--fx-table-radius: var(--fx-radius-none, 0);\n\t\t}\n\t\t:host([rounded=\"sm\"]),\n\t\t:host([radius=\"sm\"]) {\n\t\t\t--fx-table-radius: var(--fx-radius-sm);\n\t\t}\n\t\t:host([rounded=\"md\"]),\n\t\t:host([radius=\"md\"]) {\n\t\t\t--fx-table-radius: var(--fx-radius-md);\n\t\t}\n\t\t:host([rounded=\"lg\"]),\n\t\t:host([radius=\"lg\"]) {\n\t\t\t--fx-table-radius: var(--fx-radius-lg);\n\t\t}\n\t\t/* Moldura: uma única borda envolvendo a tabela E a paginação, para que\n\t\t o pager não fique \"solto\" fora do contorno da tabela. */\n\t\t.frame {\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-table-radius);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t}\n\t\t/* Com toolbar no topo, quem desenha a borda superior é a própria\n\t\t toolbar — a moldura desenha apenas o contorno restante. */\n\t\t.frame.has-toolbar {\n\t\t\tborder-top: none;\n\t\t}\n\t\t/* \"no-borders-x\": mantém somente as bordas de topo e de base\n\t\t (tabela encaixada em um layout que já desenha as laterais). */\n\t\t:host([no-borders-x]) .frame,\n\t\t:host([no-borders-x]) .toolbar {\n\t\t\tborder-left: none;\n\t\t\tborder-right: none;\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\t/* O topo acompanha o raio da moldura; a base fica reta porque o\n\t\t\t rodapé (.pager) é quem a desenha. */\n\t\t\tborder-radius: var(--fx-table-radius) var(--fx-table-radius) 0 0;\n\t\t}\n\t\t/* Sem paginação não existe rodapé: a moldura fecha embaixo. */\n\t\t:host(:not([pagination])) .scroll {\n\t\t\tborder-radius: var(--fx-table-radius);\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\t/* O rodapé é parte da moldura: borda só no topo (separando da\n\t\t\t tabela) e o mesmo padding horizontal das células. Antes ele\n\t\t\t ficava fora do contorno, sem borda e sem respiro lateral. */\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tborder-top: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: 0 0 var(--fx-table-radius) var(--fx-table-radius);\n\t\t\tbackground: var(--fx-surface-background);\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=\"page\"]) {\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=\"page\"] {\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\t/* Elipse da janela de páginas (1 … 499 [500] 501 … 1000): ocupa o\n\t\t mesmo espaço de um botão para o rodapé não \"pular\" ao navegar,\n\t\t mas não é clicável. */\n\t\t.pager .ellipsis {\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\tcolor: var(--fx-text-muted);\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t\tuser-select: none;\n\t\t}\n\t\t/* O seletor \"Por página\" só mostra números, então não pode herdar a\n\t\t largura padrão do <fx-select> (200px / 180px em size=\"sm\"), que\n\t\t deixava o rodapé da paginação desproporcional. O limite tem que ser\n\t\t no HOST — apertar só ::part(trigger) não funciona, porque o\n\t\t trigger é width:100% e continua herdando o min-width do host.\n\t\t O painel (dropdown) continua largo (220px) em select.ts. */\n\t\tfx-select.rows-sel {\n\t\t\tvertical-align: middle;\n\t\t\t/* Não deixa o flex do .pager esticar nem esmagar o controle. */\n\t\t\tflex: none;\n\t\t\twidth: auto;\n\t\t\t--fx-select-min-width: 56px;\n\t\t\t--fx-select-min-width-sm: 56px;\n\t\t}\n\t\tfx-select.rows-sel::part(trigger) {\n\t\t\tmin-width: 0;\n\t\t\tmin-height: var(--fx-size-sm);\n\t\t\t/* Padding curto: sobra espaço para o número + caret. */\n\t\t\tpadding: 0 var(--fx-space-xs);\n\t\t\tborder-radius: var(--fx-radius-sm);\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\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-table-radius) var(--fx-table-radius) 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/* <fx-toolbar> é apenas um invólucro no light DOM: o slot projeta\n\t\t seus filhos (com listeners do Vue preservados) direto no flex. */\n\t\t::slotted(fx-toolbar) {\n\t\t\tdisplay: contents;\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-table-radius) var(--fx-table-radius) 0 0;\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\t\"rounded\",\n\t\t\t\"radius\",\n\t\t\t\"no-borders-x\",\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\t/** Inputs da toolbar slotted já receberam listener (não recria a cada render). */\n\tprivate _boundToolbarInputs = new WeakSet<HTMLInputElement>();\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\tset lazy(value: boolean) {\n\t\tthis.toggleAttr(\"lazy\", Boolean(value));\n\t}\n\n\tget pagination(): boolean {\n\t\treturn this.hasAttr(\"pagination\");\n\t}\n\tset pagination(value: boolean) {\n\t\tthis.toggleAttr(\"pagination\", Boolean(value));\n\t}\n\n\tget striped(): boolean {\n\t\treturn this.hasAttr(\"striped\");\n\t}\n\tset striped(value: boolean) {\n\t\tthis.toggleAttr(\"striped\", Boolean(value));\n\t}\n\n\tget hover(): boolean {\n\t\treturn this.hasAttr(\"hover\");\n\t}\n\tset hover(value: boolean) {\n\t\tthis.toggleAttr(\"hover\", Boolean(value));\n\t}\n\n\tget loading(): boolean {\n\t\treturn this.hasAttr(\"loading\");\n\t}\n\tset loading(value: boolean) {\n\t\tthis.toggleAttr(\"loading\", Boolean(value));\n\t}\n\n\t/**\n\t * Vue não cameliza atributos de elementos customizados:\n\t * `:no-borders-x=\"false\"` procura a propriedade LITERAL\n\t * `'no-borders-x'` — o accessor literal evita o contorno `.attr`.\n\t */\n\tget 'no-borders-x'(): boolean {\n\t\treturn this.hasAttr(\"no-borders-x\");\n\t}\n\tset 'no-borders-x'(value: boolean) {\n\t\tthis.toggleAttr(\"no-borders-x\", Boolean(value));\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, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\t// Colunas com header/field/align/sortable/filterable alterados\n\t\t\t// em runtime também precisam re-renderizar.\n\t\t\tattributes: true,\n\t\t\tattributeFilter: [\"header\", \"field\", \"align\", \"sortable\", \"filterable\", \"template\"],\n\t\t});\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\t// <fx-toolbar> usa <slot>: os nós continuam no light DOM e os\n\t\t// listeners do framework (@click, v-model) sobrevivem. As formas\n\t\t// legadas (template/toolbar=\"#id\") seguem por clone de innerHTML.\n\t\tconst toolbarSlotted = toolbar?.localName === \"fx-toolbar\";\n\t\tconst toolbarHasContent = Boolean(\n\t\t\ttoolbar &&\n\t\t\t\t(toolbarSlotted\n\t\t\t\t\t? toolbar.children.length > 0 || (toolbar.textContent ?? \"\").trim() !== \"\"\n\t\t\t\t\t: toolbar.innerHTML.trim() !== \"\"),\n\t\t);\n\t\tif (toolbarSlotted && toolbarHasContent) {\n\t\t\ttoolbar.setAttribute(\"slot\", \"fx-toolbar\");\n\t\t}\n\t\tconst toolbarHtml = toolbarHasContent\n\t\t\t? `<div class=\"toolbar\" part=\"toolbar\">${\n\t\t\t\t\ttoolbarSlotted ? '<slot name=\"fx-toolbar\"></slot>' : toolbar!.innerHTML\n\t\t\t\t}</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// Janela de páginas: primeira + última + vizinhas da atual, com elipse no\n\t// miolo. Sem isso, com 1.000 páginas o rodapé renderizava 1.000 botões.\n\tconst pageButtons = pageRange(this.page + 1, pages)\n\t\t.map((item) =>\n\t\t\titem === \"ellipsis\"\n\t\t\t\t? `<span class=\"ellipsis\" part=\"ellipsis\" aria-hidden=\"true\">…</span>`\n\t\t\t\t: `<button type=\"button\" class=\"pg-btn\" part=\"page\" data-pg=\"${item - 1}\"${item === this.page + 1 ? ' aria-current=\"page\"' : \"\"}>${item}</button>`,\n\t\t)\n\t\t.join(\"\");\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 ${pageButtons}\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\" placement=\"top\" 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\t// A toolbar slotted vive no light DOM: mesmo no caminho rápido\n\t\t\t\t// pode ter recebido um input novo do framework.\n\t\t\t\tthis._bindSlottedToolbarSearch(toolbar);\n\t\t\t\tthis._restoreToolbarSearch();\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=\"frame${toolbarHasContent ? \" has-toolbar\" : \"\"}\" part=\"frame\">\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 </div>\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 (clone no shadow + inputs slotted persistentes).\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLInputElement>(\"[data-search-fields]\")\n\t\t\t.forEach((inp) => this._bindSearchInput(inp, false));\n\t\tif (toolbarSlotted && toolbar) {\n\t\t\tthis._bindSlottedToolbarSearch(toolbar);\n\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\t/** Liga (uma vez) os inputs de busca de um <fx-toolbar> slotted. */\n\tprivate _bindSlottedToolbarSearch(toolbar: HTMLElement | null): void {\n\t\tif (!toolbar || toolbar.localName !== \"fx-toolbar\") return;\n\t\ttoolbar\n\t\t\t.querySelectorAll<HTMLInputElement>(\"[data-search-fields]\")\n\t\t\t.forEach((inp) => {\n\t\t\t\tif (this._boundToolbarInputs.has(inp)) return;\n\t\t\t\tthis._boundToolbarInputs.add(inp);\n\t\t\t\tthis._bindSearchInput(inp, true);\n\t\t\t});\n\t}\n\n\t/**\n\t * Liga um input da toolbar à busca global.\n\t * `persistent` = input slotted (não é recriado pelo render): o foco volta\n\t * no próprio nó. Inputs clonados são substituídos, então o foco é\n\t * restaurado no clone novo do shadow.\n\t */\n\tprivate _bindSearchInput(inp: HTMLInputElement, persistent: boolean): void {\n\t\tinp.addEventListener(\"input\", () => {\n\t\t\tthis.globalSearch = inp.value;\n\t\t\tthis.page = 0;\n\t\t\tconst pos = inp.selectionStart;\n\t\t\tthis.render();\n\t\t\tconst again = persistent && inp.isConnected\n\t\t\t\t? inp\n\t\t\t\t: this.root.querySelector<HTMLInputElement>(\"[data-search-fields]\");\n\t\t\tif (again) {\n\t\t\t\tagain.focus();\n\t\t\t\tagain.setSelectionRange(pos, pos);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate _restoreToolbarSearch(): void {\n\t\tconst shadow = this.root.querySelector<HTMLInputElement>(\n\t\t\t\"[data-search-fields]\",\n\t\t);\n\t\tif (shadow && this.globalSearch) shadow.value = this.globalSearch;\n\n\t\tconst toolbar = this.toolbarTemplate;\n\t\tif (toolbar?.localName === \"fx-toolbar\") {\n\t\t\ttoolbar\n\t\t\t\t.querySelectorAll<HTMLInputElement>(\"[data-search-fields]\")\n\t\t\t\t.forEach((input) => {\n\t\t\t\t\tif (this.globalSearch && input.value !== this.globalSearch) {\n\t\t\t\t\t\tinput.value = this.globalSearch;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}\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 { pageRange } from '../../core/page-range';\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 &lbrace;&lbrace; value &rbrace;&rbrace;\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, rounded (none|sm|md|lg, padrão md; `radius` é alias),\n * no-borders-x (remove apenas as bordas laterais, mantendo topo e base).\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\t/* Raio da moldura (tabela + paginação). O atributo \"rounded\"\n\t\t\t sobrescreve este valor e o consumidor também pode sobrescrever\n\t\t\t por CSS: fx-table { --fx-table-radius: 20px; }. */\n\t\t\t--fx-table-radius: var(--fx-radius-md);\n\t\t}\n\t\t/* Arredondamento das bordas — \"none\" deixa tudo reto, como nos demais\n\t\t tokens de radius do design system. \"rounded\" é o nome atual; \"radius\"\n\t\t segue aceito como alias para ficar consistente com o <fx-card>. */\n\t\t:host([rounded=\"none\"]),\n\t\t:host([radius=\"none\"]) {\n\t\t\t--fx-table-radius: var(--fx-radius-none, 0);\n\t\t}\n\t\t:host([rounded=\"sm\"]),\n\t\t:host([radius=\"sm\"]) {\n\t\t\t--fx-table-radius: var(--fx-radius-sm);\n\t\t}\n\t\t:host([rounded=\"md\"]),\n\t\t:host([radius=\"md\"]) {\n\t\t\t--fx-table-radius: var(--fx-radius-md);\n\t\t}\n\t\t:host([rounded=\"lg\"]),\n\t\t:host([radius=\"lg\"]) {\n\t\t\t--fx-table-radius: var(--fx-radius-lg);\n\t\t}\n\t\t/* Moldura: uma única borda envolvendo a tabela E a paginação, para que\n\t\t o pager não fique \"solto\" fora do contorno da tabela. */\n\t\t.frame {\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-table-radius);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t}\n\t\t/* Com toolbar no topo, quem desenha a borda superior é a própria\n\t\t toolbar — a moldura desenha apenas o contorno restante. */\n\t\t.frame.has-toolbar {\n\t\t\tborder-top: none;\n\t\t}\n\t\t/* \"no-borders-x\": mantém somente as bordas de topo e de base\n\t\t (tabela encaixada em um layout que já desenha as laterais). */\n\t\t:host([no-borders-x]) .frame,\n\t\t:host([no-borders-x]) .toolbar {\n\t\t\tborder-left: none;\n\t\t\tborder-right: none;\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\t/* O topo acompanha o raio da moldura; a base fica reta porque o\n\t\t\t rodapé (.pager) é quem a desenha. */\n\t\t\tborder-radius: var(--fx-table-radius) var(--fx-table-radius) 0 0;\n\t\t}\n\t\t/* Sem paginação não existe rodapé: a moldura fecha embaixo. */\n\t\t:host(:not([pagination])) .scroll {\n\t\t\tborder-radius: var(--fx-table-radius);\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\t/* Cor/peso do cabeçalho por token de TABELA. Sem isso o th herda\n\t\t\t --fx-text-default do host e o peso vem do UA (bold). */\n\t\t\tcolor: var(\n\t\t\t\t--fx-table-header-text,\n\t\t\t\tvar(--fx-table-text, var(--fx-text-default))\n\t\t\t);\n\t\t\tfont-weight: var(--fx-table-header-weight, bold);\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\t/* Texto do corpo: token próprio para não depender de\n\t\t\t --fx-text-default (que afeta TODOS os textos do app) e para\n\t\t\t não herdar o font-weight do body da página — era isso que\n\t\t\t fazia a célula parecer mais clara que o cabeçalho. */\n\t\t\tcolor: var(--fx-table-text, var(--fx-text-default));\n\t\t\tfont-weight: var(--fx-table-text-weight, inherit);\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\t/* O rodapé é parte da moldura: borda só no topo (separando da\n\t\t\t tabela) e o mesmo padding horizontal das células. Antes ele\n\t\t\t ficava fora do contorno, sem borda e sem respiro lateral. */\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tborder-top: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: 0 0 var(--fx-table-radius) var(--fx-table-radius);\n\t\t\tbackground: var(--fx-surface-background);\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=\"page\"]) {\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=\"page\"] {\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\t/* Elipse da janela de páginas (1 … 499 [500] 501 … 1000): ocupa o\n\t\t mesmo espaço de um botão para o rodapé não \"pular\" ao navegar,\n\t\t mas não é clicável. */\n\t\t.pager .ellipsis {\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\tcolor: var(--fx-text-muted);\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t\tuser-select: none;\n\t\t}\n\t\t/* O seletor \"Por página\" só mostra números, então não pode herdar a\n\t\t largura padrão do <fx-select> (200px / 180px em size=\"sm\"), que\n\t\t deixava o rodapé da paginação desproporcional. O limite tem que ser\n\t\t no HOST — apertar só ::part(trigger) não funciona, porque o\n\t\t trigger é width:100% e continua herdando o min-width do host.\n\t\t O painel (dropdown) continua largo (220px) em select.ts. */\n\t\tfx-select.rows-sel {\n\t\t\tvertical-align: middle;\n\t\t\t/* Não deixa o flex do .pager esticar nem esmagar o controle. */\n\t\t\tflex: none;\n\t\t\twidth: auto;\n\t\t\t--fx-select-min-width: 56px;\n\t\t\t--fx-select-min-width-sm: 56px;\n\t\t}\n\t\tfx-select.rows-sel::part(trigger) {\n\t\t\tmin-width: 0;\n\t\t\tmin-height: var(--fx-size-sm);\n\t\t\t/* Padding curto: sobra espaço para o número + caret. */\n\t\t\tpadding: 0 var(--fx-space-xs);\n\t\t\tborder-radius: var(--fx-radius-sm);\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\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-table-radius) var(--fx-table-radius) 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/* <fx-toolbar> é apenas um invólucro no light DOM: o slot projeta\n\t\t seus filhos (com listeners do Vue preservados) direto no flex. */\n\t\t::slotted(fx-toolbar) {\n\t\t\tdisplay: contents;\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-table-radius) var(--fx-table-radius) 0 0;\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\t\"rounded\",\n\t\t\t\"radius\",\n\t\t\t\"no-borders-x\",\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\t/** Inputs da toolbar slotted já receberam listener (não recria a cada render). */\n\tprivate _boundToolbarInputs = new WeakSet<HTMLInputElement>();\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\tset lazy(value: boolean) {\n\t\tthis.toggleAttr(\"lazy\", Boolean(value));\n\t}\n\n\tget pagination(): boolean {\n\t\treturn this.hasAttr(\"pagination\");\n\t}\n\tset pagination(value: boolean) {\n\t\tthis.toggleAttr(\"pagination\", Boolean(value));\n\t}\n\n\tget striped(): boolean {\n\t\treturn this.hasAttr(\"striped\");\n\t}\n\tset striped(value: boolean) {\n\t\tthis.toggleAttr(\"striped\", Boolean(value));\n\t}\n\n\tget hover(): boolean {\n\t\treturn this.hasAttr(\"hover\");\n\t}\n\tset hover(value: boolean) {\n\t\tthis.toggleAttr(\"hover\", Boolean(value));\n\t}\n\n\tget loading(): boolean {\n\t\treturn this.hasAttr(\"loading\");\n\t}\n\tset loading(value: boolean) {\n\t\tthis.toggleAttr(\"loading\", Boolean(value));\n\t}\n\n\t/**\n\t * Vue não cameliza atributos de elementos customizados:\n\t * `:no-borders-x=\"false\"` procura a propriedade LITERAL\n\t * `'no-borders-x'` — o accessor literal evita o contorno `.attr`.\n\t */\n\tget 'no-borders-x'(): boolean {\n\t\treturn this.hasAttr(\"no-borders-x\");\n\t}\n\tset 'no-borders-x'(value: boolean) {\n\t\tthis.toggleAttr(\"no-borders-x\", Boolean(value));\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, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\t// Colunas com header/field/align/sortable/filterable alterados\n\t\t\t// em runtime também precisam re-renderizar.\n\t\t\tattributes: true,\n\t\t\tattributeFilter: [\"header\", \"field\", \"align\", \"sortable\", \"filterable\", \"template\"],\n\t\t});\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\t// <fx-toolbar> usa <slot>: os nós continuam no light DOM e os\n\t\t// listeners do framework (@click, v-model) sobrevivem. As formas\n\t\t// legadas (template/toolbar=\"#id\") seguem por clone de innerHTML.\n\t\tconst toolbarSlotted = toolbar?.localName === \"fx-toolbar\";\n\t\tconst toolbarHasContent = Boolean(\n\t\t\ttoolbar &&\n\t\t\t\t(toolbarSlotted\n\t\t\t\t\t? toolbar.children.length > 0 || (toolbar.textContent ?? \"\").trim() !== \"\"\n\t\t\t\t\t: toolbar.innerHTML.trim() !== \"\"),\n\t\t);\n\t\tif (toolbarSlotted && toolbarHasContent) {\n\t\t\ttoolbar.setAttribute(\"slot\", \"fx-toolbar\");\n\t\t}\n\t\tconst toolbarHtml = toolbarHasContent\n\t\t\t? `<div class=\"toolbar\" part=\"toolbar\">${\n\t\t\t\t\ttoolbarSlotted ? '<slot name=\"fx-toolbar\"></slot>' : toolbar!.innerHTML\n\t\t\t\t}</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// Janela de páginas: primeira + última + vizinhas da atual, com elipse no\n\t// miolo. Sem isso, com 1.000 páginas o rodapé renderizava 1.000 botões.\n\tconst pageButtons = pageRange(this.page + 1, pages)\n\t\t.map((item) =>\n\t\t\titem === \"ellipsis\"\n\t\t\t\t? `<span class=\"ellipsis\" part=\"ellipsis\" aria-hidden=\"true\">…</span>`\n\t\t\t\t: `<button type=\"button\" class=\"pg-btn\" part=\"page\" data-pg=\"${item - 1}\"${item === this.page + 1 ? ' aria-current=\"page\"' : \"\"}>${item}</button>`,\n\t\t)\n\t\t.join(\"\");\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 ${pageButtons}\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\" placement=\"top\" 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\t// A toolbar slotted vive no light DOM: mesmo no caminho rápido\n\t\t\t\t// pode ter recebido um input novo do framework.\n\t\t\t\tthis._bindSlottedToolbarSearch(toolbar);\n\t\t\t\tthis._restoreToolbarSearch();\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=\"frame${toolbarHasContent ? \" has-toolbar\" : \"\"}\" part=\"frame\">\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 </div>\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 (clone no shadow + inputs slotted persistentes).\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLInputElement>(\"[data-search-fields]\")\n\t\t\t.forEach((inp) => this._bindSearchInput(inp, false));\n\t\tif (toolbarSlotted && toolbar) {\n\t\t\tthis._bindSlottedToolbarSearch(toolbar);\n\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\t/** Liga (uma vez) os inputs de busca de um <fx-toolbar> slotted. */\n\tprivate _bindSlottedToolbarSearch(toolbar: HTMLElement | null): void {\n\t\tif (!toolbar || toolbar.localName !== \"fx-toolbar\") return;\n\t\ttoolbar\n\t\t\t.querySelectorAll<HTMLInputElement>(\"[data-search-fields]\")\n\t\t\t.forEach((inp) => {\n\t\t\t\tif (this._boundToolbarInputs.has(inp)) return;\n\t\t\t\tthis._boundToolbarInputs.add(inp);\n\t\t\t\tthis._bindSearchInput(inp, true);\n\t\t\t});\n\t}\n\n\t/**\n\t * Liga um input da toolbar à busca global.\n\t * `persistent` = input slotted (não é recriado pelo render): o foco volta\n\t * no próprio nó. Inputs clonados são substituídos, então o foco é\n\t * restaurado no clone novo do shadow.\n\t */\n\tprivate _bindSearchInput(inp: HTMLInputElement, persistent: boolean): void {\n\t\tinp.addEventListener(\"input\", () => {\n\t\t\tthis.globalSearch = inp.value;\n\t\t\tthis.page = 0;\n\t\t\tconst pos = inp.selectionStart;\n\t\t\tthis.render();\n\t\t\tconst again = persistent && inp.isConnected\n\t\t\t\t? inp\n\t\t\t\t: this.root.querySelector<HTMLInputElement>(\"[data-search-fields]\");\n\t\t\tif (again) {\n\t\t\t\tagain.focus();\n\t\t\t\tagain.setSelectionRange(pos, pos);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate _restoreToolbarSearch(): void {\n\t\tconst shadow = this.root.querySelector<HTMLInputElement>(\n\t\t\t\"[data-search-fields]\",\n\t\t);\n\t\tif (shadow && this.globalSearch) shadow.value = this.globalSearch;\n\n\t\tconst toolbar = this.toolbarTemplate;\n\t\tif (toolbar?.localName === \"fx-toolbar\") {\n\t\t\ttoolbar\n\t\t\t\t.querySelectorAll<HTMLInputElement>(\"[data-search-fields]\")\n\t\t\t\t.forEach((input) => {\n\t\t\t\t\tif (this.globalSearch && input.value !== this.globalSearch) {\n\t\t\t\t\t\tinput.value = this.globalSearch;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}\n\t}\n}\n\nexport function defineFxTable(): typeof FxTable {\n return defineElement('fx-table', FxTable);\n}\n"],"names":[],"mappings":"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"}
@@ -26,6 +26,8 @@ export interface FenixTokens {
26
26
  /** Brilho/anel do estado de sucesso — acompanha `focus-ring` (ver applyTokens). */
27
27
  'success-ring': string;
28
28
  };
29
+ /** Texto das tabelas (`fx-table`): cor do corpo/cabeçalho e peso do cabeçalho. */
30
+ table: Record<'text' | 'header-text' | 'header-weight', string>;
29
31
  /** Z-index */ z: Record<'base' | 'dropdown' | 'modal' | 'toast', string>;
30
32
  }
31
33
  /** Tema claro (padrão). */
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/core/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,WAAW;IAC1B,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC;IACnH,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,GAAG,SAAS,GAAG,eAAe,EAAE,MAAM,CAAC,CAAC;IACvF,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,GAAG,OAAO,GAAG,UAAU,EAAE,MAAM,CAAC,CAAC;IACpE,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1D,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,CAAC;IACrF,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3E,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3E,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IACzC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1D,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,GAAG,iBAAiB,GAAG,QAAQ,EAAE,MAAM,CAAC,CAAC;IACrF,uEAAuE;IACvE,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,uFAAuF;QACvF,YAAY,EAAE,MAAM,CAAC;QACrB,mFAAmF;QACnF,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,cAAc,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;CAC3E;AAED,2BAA2B;AAC3B,eAAO,MAAM,WAAW,EAAE,WA4DzB,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,UAAU,EAAE,WAyBxB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAAyB,CAAC;AAEtD,qFAAqF;AACrF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC"}
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/core/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,WAAW;IAC1B,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC;IACnH,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,GAAG,SAAS,GAAG,eAAe,EAAE,MAAM,CAAC,CAAC;IACvF,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,GAAG,OAAO,GAAG,UAAU,EAAE,MAAM,CAAC,CAAC;IACpE,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1D,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,CAAC;IACrF,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3E,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3E,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IACzC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1D,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,GAAG,iBAAiB,GAAG,QAAQ,EAAE,MAAM,CAAC,CAAC;IACrF,uEAAuE;IACvE,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,uFAAuF;QACvF,YAAY,EAAE,MAAM,CAAC;QACrB,mFAAmF;QACnF,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG,aAAa,GAAG,eAAe,EAAE,MAAM,CAAC,CAAC;IAChE,cAAc,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;CAC3E;AAED,2BAA2B;AAC3B,eAAO,MAAM,WAAW,EAAE,WAoEzB,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,UAAU,EAAE,WA0BxB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAAyB,CAAC;AAEtD,qFAAqF;AACrF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC"}
@@ -54,6 +54,14 @@ const lightTokens = {
54
54
  /** Brilho dos estados `success`/`valid` — acompanha `focus-ring` igualmente. */
55
55
  "success-ring": "0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent)"
56
56
  },
57
+ table: {
58
+ /** Cor do corpo do fx-table; `var()` mantém claro/escuro automáticos. */
59
+ text: "var(--fx-text-default)",
60
+ /** Cabeçalho acompanha o corpo por padrão. */
61
+ "header-text": "var(--fx-table-text)",
62
+ /** O UA aplica bold no <th>; aqui o valor fica explícito e tokenizado. */
63
+ "header-weight": "bold"
64
+ },
57
65
  z: { base: "auto", dropdown: "1000", modal: "1100", toast: "1200" }
58
66
  };
59
67
  const darkTokens = {
@@ -80,6 +88,7 @@ const darkTokens = {
80
88
  },
81
89
  motion: { ...lightTokens.motion },
82
90
  effect: { ...lightTokens.effect },
91
+ table: { ...lightTokens.table },
83
92
  z: { ...lightTokens.z }
84
93
  };
85
94
  const defaultTokens = lightTokens;
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.js","sources":["../../src/core/tokens.ts"],"sourcesContent":["/**\n * Design Tokens — camada de personalização do FenixUI.\n *\n * Os componentes consomem apenas `var(--fx-*)`, nunca valores fixos.\n * Isso atravessa o Shadow DOM via CSS Custom Properties e permite\n * trocar o tema em runtime sem recompilar a biblioteca.\n */\n\nexport interface FenixTokens {\n /** Cores semânticas */ color: Record<'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info', string>;\n /** Superfícies */ surface: Record<'background' | 'surface' | 'surface-hover', string>;\n /** Texto */ text: Record<'default' | 'muted' | 'disabled', string>;\n /** Bordas */ border: Record<'default' | 'hover', string>;\n /** Tipografia */ font: Record<'family' | 'size' | 'weight' | 'line-height', string>;\n /** Espaçamento */ space: Record<'xs' | 'sm' | 'md' | 'lg' | 'xl', string>;\n /** Radius */ radius: Record<'none' | 'sm' | 'md' | 'lg' | 'full', string>;\n /** Altura dos controles (button, input, select, switch…). Personalizável por preset. */\n size: Record<'sm' | 'md' | 'lg', string>;\n /** Sombras */ shadow: Record<'sm' | 'md' | 'lg', string>;\n /** Motion */ motion: Record<'duration-fast' | 'duration-normal' | 'easing', string>;\n /** Efeitos visuais configuráveis (ripple, anel de foco dos campos). */\n effect: {\n ripple: string;\n 'focus-ring': string;\n /** Brilho/anel dos estados de validação — acompanha `focus-ring` (ver applyTokens). */\n 'error-ring': string;\n /** Brilho/anel do estado de sucesso — acompanha `focus-ring` (ver applyTokens). */\n 'success-ring': string;\n };\n /** Z-index */ z: Record<'base' | 'dropdown' | 'modal' | 'toast', string>;\n}\n\n/** Tema claro (padrão). */\nexport const lightTokens: FenixTokens = {\n\tcolor: {\n\t\tprimary: \"#c2410c\",\n\t\tsecondary: \"#991B1B\",\n\t\tsuccess: \"#10b972\",\n\t\twarning: \"#f59e0b\",\n\t\tdanger: \"#f43f5e\",\n\t\tinfo: \"#0ea5e9\",\n\t},\n\tsurface: {\n\t\tbackground: \"#ffffff\",\n\t\tsurface: \"#f8fafc\",\n\t\t\"surface-hover\": \"#eef2f7\",\n\t},\n\ttext: {\n\t\tdefault: \"#10182c\",\n\t\tmuted: \"#64748b\",\n\t\tdisabled: \"#94a3b8\",\n\t},\n\tborder: {\n\t\tdefault: \"#e2e8f0\",\n\t\thover: \"#cbd5e1\",\n\t},\n\tfont: {\n\t\tfamily: \"'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif\",\n\t\tsize: \"14px\",\n\t\tweight: \"500\",\n\t\t\"line-height\": \"1.5\",\n\t},\n\tspace: { xs: \"4px\", sm: \"8px\", md: \"12px\", lg: \"16px\", xl: \"24px\" },\n\tradius: { none: \"0\", sm: \"4px\", md: \"8px\", lg: \"12px\", full: \"9999px\" },\n\tsize: { sm: \"32px\", md: \"40px\", lg: \"48px\" },\n\tshadow: {\n\t\tsm: \"0 1px 2px rgba(15, 23, 42, 0.06)\",\n\t\tmd: \"0 4px 12px rgba(15, 23, 42, 0.10)\",\n\t\tlg: \"0 12px 32px rgba(15, 23, 42, 0.18)\",\n\t},\n\tmotion: {\n\t\t\"duration-fast\": \"120ms\",\n\t\t\"duration-normal\": \"240ms\",\n\t\teasing: \"cubic-bezier(0.25, 0.1, 0.25, 1)\",\n\t},\n\teffect: {\n\t\t/** '0' desativa o efeito ripple do botão. */\n\t\tripple: \"1\",\n\t\t/** Anel de foco dos campos de formulário. Use 'none' para campos sem sobra. */\n\t\t\"focus-ring\":\n\t\t\t\"0 0 0 3px color-mix(in srgb, var(--fx-color-primary) 22%, transparent)\",\n\t\t/**\n\t\t * Brilho dos estados `error`/`invalid`. Acompanha `focus-ring`: quando o\n\t\t * anel de foco está desligado, `applyTokens` força 'none' — o erro segue\n\t\t * visível pela borda (border-color) e pela mensagem, mas sem o anel.\n\t\t */\n\t\t\"error-ring\":\n\t\t\t\"0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent)\",\n\t\t/** Brilho dos estados `success`/`valid` — acompanha `focus-ring` igualmente. */\n\t\t\"success-ring\":\n\t\t\t\"0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent)\",\n\t},\n\tz: { base: \"auto\", dropdown: \"1000\", modal: \"1100\", toast: \"1200\" },\n};\n\n/** Tema escuro — os componentes reagem automaticamente às variáveis CSS. */\nexport const darkTokens: FenixTokens = {\n color: { ...lightTokens.color },\n surface: {\n background: '#0f172a',\n surface: '#1e293b',\n 'surface-hover': '#334155',\n },\n text: {\n default: '#f8fafc',\n muted: '#94a3b8',\n disabled: '#64748b',\n },\n border: { default: '#334155', hover: '#475569' },\n font: { ...lightTokens.font },\n space: { ...lightTokens.space },\n radius: { ...lightTokens.radius },\n size: { ...lightTokens.size },\n shadow: {\n sm: '0 1px 2px rgba(0, 0, 0, 0.4)',\n md: '0 6px 16px rgba(0, 0, 0, 0.5)',\n lg: '0 16px 40px rgba(0, 0, 0, 0.6)',\n },\n motion: { ...lightTokens.motion },\n effect: { ...lightTokens.effect },\n z: { ...lightTokens.z },\n};\n\nexport const defaultTokens: FenixTokens = lightTokens;\n\n/** Override parcial e profundo — permite customizar apenas alguns tokens por vez. */\nexport type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] };"],"names":[],"mappings":"AAiCO,MAAM,cAA2B;AAAA,EACvC,OAAO;AAAA,IACN,SAAS;AAAA,IACT,WAAW;AAAA,IACX,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,EAAA;AAAA,EAEP,SAAS;AAAA,IACR,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,iBAAiB;AAAA,EAAA;AAAA,EAElB,MAAM;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA,IACP,UAAU;AAAA,EAAA;AAAA,EAEX,QAAQ;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,EAAA;AAAA,EAER,MAAM;AAAA,IACL,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,eAAe;AAAA,EAAA;AAAA,EAEhB,OAAO,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,IAAI,OAAA;AAAA,EAC3D,QAAQ,EAAE,MAAM,KAAK,IAAI,OAAO,IAAI,OAAO,IAAI,QAAQ,MAAM,SAAA;AAAA,EAC7D,MAAM,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,OAAA;AAAA,EACpC,QAAQ;AAAA,IACP,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,EAAA;AAAA,EAEL,QAAQ;AAAA,IACP,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,QAAQ;AAAA,EAAA;AAAA,EAET,QAAQ;AAAA;AAAA,IAEP,QAAQ;AAAA;AAAA,IAER,cACC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMD,cACC;AAAA;AAAA,IAED,gBACC;AAAA,EAAA;AAAA,EAEF,GAAG,EAAE,MAAM,QAAQ,UAAU,QAAQ,OAAO,QAAQ,OAAO,OAAA;AAC5D;AAGO,MAAM,aAA0B;AAAA,EACrC,OAAO,EAAE,GAAG,YAAY,MAAA;AAAA,EACxB,SAAS;AAAA,IACP,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,iBAAiB;AAAA,EAAA;AAAA,EAEnB,MAAM;AAAA,IACJ,SAAS;AAAA,IACT,OAAO;AAAA,IACP,UAAU;AAAA,EAAA;AAAA,EAEZ,QAAQ,EAAE,SAAS,WAAW,OAAO,UAAA;AAAA,EACrC,MAAM,EAAE,GAAG,YAAY,KAAA;AAAA,EACvB,OAAO,EAAE,GAAG,YAAY,MAAA;AAAA,EACxB,QAAQ,EAAE,GAAG,YAAY,OAAA;AAAA,EACzB,MAAM,EAAE,GAAG,YAAY,KAAA;AAAA,EACvB,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,EAAA;AAAA,EAEN,QAAQ,EAAE,GAAG,YAAY,OAAA;AAAA,EACzB,QAAQ,EAAE,GAAG,YAAY,OAAA;AAAA,EACzB,GAAG,EAAE,GAAG,YAAY,EAAA;AACtB;AAEO,MAAM,gBAA6B;"}
1
+ {"version":3,"file":"tokens.js","sources":["../../src/core/tokens.ts"],"sourcesContent":["/**\n * Design Tokens — camada de personalização do FenixUI.\n *\n * Os componentes consomem apenas `var(--fx-*)`, nunca valores fixos.\n * Isso atravessa o Shadow DOM via CSS Custom Properties e permite\n * trocar o tema em runtime sem recompilar a biblioteca.\n */\n\nexport interface FenixTokens {\n /** Cores semânticas */ color: Record<'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info', string>;\n /** Superfícies */ surface: Record<'background' | 'surface' | 'surface-hover', string>;\n /** Texto */ text: Record<'default' | 'muted' | 'disabled', string>;\n /** Bordas */ border: Record<'default' | 'hover', string>;\n /** Tipografia */ font: Record<'family' | 'size' | 'weight' | 'line-height', string>;\n /** Espaçamento */ space: Record<'xs' | 'sm' | 'md' | 'lg' | 'xl', string>;\n /** Radius */ radius: Record<'none' | 'sm' | 'md' | 'lg' | 'full', string>;\n /** Altura dos controles (button, input, select, switch…). Personalizável por preset. */\n size: Record<'sm' | 'md' | 'lg', string>;\n /** Sombras */ shadow: Record<'sm' | 'md' | 'lg', string>;\n /** Motion */ motion: Record<'duration-fast' | 'duration-normal' | 'easing', string>;\n /** Efeitos visuais configuráveis (ripple, anel de foco dos campos). */\n effect: {\n ripple: string;\n 'focus-ring': string;\n /** Brilho/anel dos estados de validação — acompanha `focus-ring` (ver applyTokens). */\n 'error-ring': string;\n /** Brilho/anel do estado de sucesso — acompanha `focus-ring` (ver applyTokens). */\n 'success-ring': string;\n };\n /** Texto das tabelas (`fx-table`): cor do corpo/cabeçalho e peso do cabeçalho. */\n table: Record<'text' | 'header-text' | 'header-weight', string>;\n /** Z-index */ z: Record<'base' | 'dropdown' | 'modal' | 'toast', string>;\n}\n\n/** Tema claro (padrão). */\nexport const lightTokens: FenixTokens = {\n\tcolor: {\n\t\tprimary: \"#c2410c\",\n\t\tsecondary: \"#991B1B\",\n\t\tsuccess: \"#10b972\",\n\t\twarning: \"#f59e0b\",\n\t\tdanger: \"#f43f5e\",\n\t\tinfo: \"#0ea5e9\",\n\t},\n\tsurface: {\n\t\tbackground: \"#ffffff\",\n\t\tsurface: \"#f8fafc\",\n\t\t\"surface-hover\": \"#eef2f7\",\n\t},\n\ttext: {\n\t\tdefault: \"#10182c\",\n\t\tmuted: \"#64748b\",\n\t\tdisabled: \"#94a3b8\",\n\t},\n\tborder: {\n\t\tdefault: \"#e2e8f0\",\n\t\thover: \"#cbd5e1\",\n\t},\n\tfont: {\n\t\tfamily: \"'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif\",\n\t\tsize: \"14px\",\n\t\tweight: \"500\",\n\t\t\"line-height\": \"1.5\",\n\t},\n\tspace: { xs: \"4px\", sm: \"8px\", md: \"12px\", lg: \"16px\", xl: \"24px\" },\n\tradius: { none: \"0\", sm: \"4px\", md: \"8px\", lg: \"12px\", full: \"9999px\" },\n\tsize: { sm: \"32px\", md: \"40px\", lg: \"48px\" },\n\tshadow: {\n\t\tsm: \"0 1px 2px rgba(15, 23, 42, 0.06)\",\n\t\tmd: \"0 4px 12px rgba(15, 23, 42, 0.10)\",\n\t\tlg: \"0 12px 32px rgba(15, 23, 42, 0.18)\",\n\t},\n\tmotion: {\n\t\t\"duration-fast\": \"120ms\",\n\t\t\"duration-normal\": \"240ms\",\n\t\teasing: \"cubic-bezier(0.25, 0.1, 0.25, 1)\",\n\t},\n\teffect: {\n\t\t/** '0' desativa o efeito ripple do botão. */\n\t\tripple: \"1\",\n\t\t/** Anel de foco dos campos de formulário. Use 'none' para campos sem sobra. */\n\t\t\"focus-ring\":\n\t\t\t\"0 0 0 3px color-mix(in srgb, var(--fx-color-primary) 22%, transparent)\",\n\t\t/**\n\t\t * Brilho dos estados `error`/`invalid`. Acompanha `focus-ring`: quando o\n\t\t * anel de foco está desligado, `applyTokens` força 'none' — o erro segue\n\t\t * visível pela borda (border-color) e pela mensagem, mas sem o anel.\n\t\t */\n\t\t\"error-ring\":\n\t\t\t\"0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent)\",\n\t\t/** Brilho dos estados `success`/`valid` — acompanha `focus-ring` igualmente. */\n\t\t\"success-ring\":\n\t\t\t\"0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent)\",\n\t},\n\ttable: {\n\t\t/** Cor do corpo do fx-table; `var()` mantém claro/escuro automáticos. */\n\t\ttext: \"var(--fx-text-default)\",\n\t\t/** Cabeçalho acompanha o corpo por padrão. */\n\t\t\"header-text\": \"var(--fx-table-text)\",\n\t\t/** O UA aplica bold no <th>; aqui o valor fica explícito e tokenizado. */\n\t\t\"header-weight\": \"bold\",\n\t},\n\tz: { base: \"auto\", dropdown: \"1000\", modal: \"1100\", toast: \"1200\" },\n};\n\n/** Tema escuro — os componentes reagem automaticamente às variáveis CSS. */\nexport const darkTokens: FenixTokens = {\n color: { ...lightTokens.color },\n surface: {\n background: '#0f172a',\n surface: '#1e293b',\n 'surface-hover': '#334155',\n },\n text: {\n default: '#f8fafc',\n muted: '#94a3b8',\n disabled: '#64748b',\n },\n border: { default: '#334155', hover: '#475569' },\n font: { ...lightTokens.font },\n space: { ...lightTokens.space },\n radius: { ...lightTokens.radius },\n size: { ...lightTokens.size },\n shadow: {\n sm: '0 1px 2px rgba(0, 0, 0, 0.4)',\n md: '0 6px 16px rgba(0, 0, 0, 0.5)',\n lg: '0 16px 40px rgba(0, 0, 0, 0.6)',\n },\n motion: { ...lightTokens.motion },\n effect: { ...lightTokens.effect },\n table: { ...lightTokens.table },\n z: { ...lightTokens.z },\n};\n\nexport const defaultTokens: FenixTokens = lightTokens;\n\n/** Override parcial e profundo — permite customizar apenas alguns tokens por vez. */\nexport type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] };"],"names":[],"mappings":"AAmCO,MAAM,cAA2B;AAAA,EACvC,OAAO;AAAA,IACN,SAAS;AAAA,IACT,WAAW;AAAA,IACX,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,EAAA;AAAA,EAEP,SAAS;AAAA,IACR,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,iBAAiB;AAAA,EAAA;AAAA,EAElB,MAAM;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA,IACP,UAAU;AAAA,EAAA;AAAA,EAEX,QAAQ;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,EAAA;AAAA,EAER,MAAM;AAAA,IACL,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,eAAe;AAAA,EAAA;AAAA,EAEhB,OAAO,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,IAAI,OAAA;AAAA,EAC3D,QAAQ,EAAE,MAAM,KAAK,IAAI,OAAO,IAAI,OAAO,IAAI,QAAQ,MAAM,SAAA;AAAA,EAC7D,MAAM,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,OAAA;AAAA,EACpC,QAAQ;AAAA,IACP,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,EAAA;AAAA,EAEL,QAAQ;AAAA,IACP,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,QAAQ;AAAA,EAAA;AAAA,EAET,QAAQ;AAAA;AAAA,IAEP,QAAQ;AAAA;AAAA,IAER,cACC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMD,cACC;AAAA;AAAA,IAED,gBACC;AAAA,EAAA;AAAA,EAEF,OAAO;AAAA;AAAA,IAEN,MAAM;AAAA;AAAA,IAEN,eAAe;AAAA;AAAA,IAEf,iBAAiB;AAAA,EAAA;AAAA,EAElB,GAAG,EAAE,MAAM,QAAQ,UAAU,QAAQ,OAAO,QAAQ,OAAO,OAAA;AAC5D;AAGO,MAAM,aAA0B;AAAA,EACrC,OAAO,EAAE,GAAG,YAAY,MAAA;AAAA,EACxB,SAAS;AAAA,IACP,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,iBAAiB;AAAA,EAAA;AAAA,EAEnB,MAAM;AAAA,IACJ,SAAS;AAAA,IACT,OAAO;AAAA,IACP,UAAU;AAAA,EAAA;AAAA,EAEZ,QAAQ,EAAE,SAAS,WAAW,OAAO,UAAA;AAAA,EACrC,MAAM,EAAE,GAAG,YAAY,KAAA;AAAA,EACvB,OAAO,EAAE,GAAG,YAAY,MAAA;AAAA,EACxB,QAAQ,EAAE,GAAG,YAAY,OAAA;AAAA,EACzB,MAAM,EAAE,GAAG,YAAY,KAAA;AAAA,EACvB,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,EAAA;AAAA,EAEN,QAAQ,EAAE,GAAG,YAAY,OAAA;AAAA,EACzB,QAAQ,EAAE,GAAG,YAAY,OAAA;AAAA,EACzB,OAAO,EAAE,GAAG,YAAY,MAAA;AAAA,EACxB,GAAG,EAAE,GAAG,YAAY,EAAA;AACtB;AAEO,MAAM,gBAA6B;"}
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/docs/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAAiB,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAY5B,OAAO,EAAa,aAAa,EAAgB,MAAM,WAAW,CAAC;AAInE,OAAO,EAAE,aAAa,EAAE,CAAC;AAkEzB,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAmC9C;AA6xED,eAAO,MAAM,iBAAiB,QAAO,OAAO,CAAC,IAAI,CAAgB,CAAC"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/docs/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAAiB,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAmB5B,OAAO,EAAa,aAAa,EAAgB,MAAM,WAAW,CAAC;AAInE,OAAO,EAAE,aAAa,EAAE,CAAC;AAkEzB,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAmC9C;AAygFD,eAAO,MAAM,iBAAiB,QAAO,OAAO,CAAC,IAAI,CAAgB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"floatlabel.doc.d.ts","sourceRoot":"","sources":["../../../src/docs/componentes/floatlabel.doc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,aAAa,EAAE,YA2G3B,CAAC"}
1
+ {"version":3,"file":"floatlabel.doc.d.ts","sourceRoot":"","sources":["../../../src/docs/componentes/floatlabel.doc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,aAAa,EAAE,YAiH3B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"table.doc.d.ts","sourceRoot":"","sources":["../../../src/docs/componentes/table.doc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,eAAO,MAAM,QAAQ,EAAE,YAkUtB,CAAC"}
1
+ {"version":3,"file":"table.doc.d.ts","sourceRoot":"","sources":["../../../src/docs/componentes/table.doc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,eAAO,MAAM,QAAQ,EAAE,YA0VtB,CAAC"}
@@ -14,7 +14,16 @@ export interface ShellDocMeta {
14
14
  }
15
15
  /** Itens do guia (acima dos grupos de componentes) — ordem importa. */
16
16
  export declare const GUIDE_ITEMS: ShellNavItem[];
17
- /** Monta os grupos da sidebar: Guia primeiro, depois os grupos dos docs. */
17
+ /**
18
+ * Grupos estáticos (não são guias nem páginas de componente). Entram logo
19
+ * depois de "Guia" e antes das categorias de componentes.
20
+ */
21
+ export declare const EXTRA_NAV_GROUPS: ShellNavGroup[];
22
+ /** Todos os itens estáticos (guia + extras), com o grupo de origem — busca. */
23
+ export declare const STATIC_NAV_ENTRIES: (ShellNavItem & {
24
+ group: string;
25
+ })[];
26
+ /** Monta os grupos da sidebar: Guia, extras e depois os grupos dos docs. */
18
27
  export declare function buildSidebarGroups(docs: readonly ShellDocMeta[]): ShellNavGroup[];
19
28
  /**
20
29
  * Renderiza a sidebar com `<details>` por grupo (colapsável nativamente:
@@ -1 +1 @@
1
- {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/docs/shell.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,EAAE,CAAC;CACtB;AAED,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd;AAED,uEAAuE;AACvE,eAAO,MAAM,WAAW,EAAE,YAAY,EAUrC,CAAC;AAIF,4EAA4E;AAC5E,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,SAAS,YAAY,EAAE,GAC3B,aAAa,EAAE,CAQjB;AAgCD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAiCpE;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAmClC;AAaD;;;GAGG;AACH,wBAAgB,SAAS,CACxB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAO,GAChC,IAAI,CAuBN;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,IAAI,IAAI,CAG/B;AA2CD;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,IAAI,CA6B/B"}
1
+ {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/docs/shell.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,EAAE,CAAC;CACtB;AAED,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd;AAED,uEAAuE;AACvE,eAAO,MAAM,WAAW,EAAE,YAAY,EAWrC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,aAAa,EAE3C,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,kBAAkB,EAAE,CAAC,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAKlE,CAAC;AAIF,4EAA4E;AAC5E,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,SAAS,YAAY,EAAE,GAC3B,aAAa,EAAE,CAYjB;AAgCD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAiCpE;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAmClC;AAgBD;;;GAGG;AACH,wBAAgB,SAAS,CACxB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAO,GAChC,IAAI,CAwBN;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,IAAI,IAAI,CAG/B;AA2CD;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,IAAI,CA6B/B"}
@@ -0,0 +1,53 @@
1
+ import type { ComponentDoc } from './types';
2
+ export interface TokenGroupInfo {
3
+ /** Rótulo amigável do grupo. */
4
+ label: string;
5
+ /** O que o grupo controla. */
6
+ desc: string;
7
+ }
8
+ /** Metadados de exibição por grupo de token (na ordem de `FenixTokens`). */
9
+ export declare const TOKEN_GROUPS: Record<string, TokenGroupInfo>;
10
+ /**
11
+ * Descrição por `grupo.chave`. Obrigatória para TODO token — o teste
12
+ * `theme-reference.test.ts` falha se um token novo ficar sem descrição.
13
+ */
14
+ export declare const TOKEN_DESCRIPTIONS: Record<string, string>;
15
+ export interface TokenRow {
16
+ /** Nome da CSS Custom Property (`--fx-color-primary`). */
17
+ cssVar: string;
18
+ /** Caminho no preset (`color.primary`). */
19
+ key: string;
20
+ /** Valor no tema claro. */
21
+ light: string;
22
+ /** Valor no tema escuro. */
23
+ dark: string;
24
+ /** O que o token controla (HTML permitido). */
25
+ desc: string;
26
+ }
27
+ export interface TokenGroupRef {
28
+ group: string;
29
+ info: TokenGroupInfo;
30
+ rows: TokenRow[];
31
+ }
32
+ /** Achata os tokens reais em grupos/linhas prontos para a documentação. */
33
+ export declare function buildTokenReference(): TokenGroupRef[];
34
+ /** Tabela-resumo: grupo, chaves disponíveis e o que o grupo controla. */
35
+ export declare function renderTokenGroupsHtml(): string;
36
+ /**
37
+ * Referência completa: um bloco por grupo com a tabela de TODOS os tokens
38
+ * (token, caminho no preset, valor claro/escuro) e o exemplo de `setTokens`.
39
+ */
40
+ export declare function renderTokenReferenceHtml(): string;
41
+ /**
42
+ * `<tbody>` da tabela “Valores ativos”: lê o valor computado de cada token no
43
+ * `:root` (repintada quando o tema muda). Sem DOM/tema aplicado, cai no padrão
44
+ * claro do próprio token.
45
+ */
46
+ export declare function renderLiveTokensTableBody(): string;
47
+ /**
48
+ * Índice de variáveis CSS por componente, gerado de `ComponentDoc.cssVars`
49
+ * (fonte já validada por `docs-quality.test.ts`). Só entram componentes que
50
+ * declaram variáveis.
51
+ */
52
+ export declare function renderComponentVarsHtml(docs: readonly ComponentDoc[]): string;
53
+ //# sourceMappingURL=theme-reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-reference.d.ts","sourceRoot":"","sources":["../../src/docs/theme-reference.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG5C,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,4EAA4E;AAC5E,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAqDvD,CAAC;AAGF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA6DrD,CAAC;AAEF,MAAM,WAAW,QAAQ;IACxB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,QAAQ,EAAE,CAAC;CACjB;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,IAAI,aAAa,EAAE,CAiBrD;AAqCD,yEAAyE;AACzE,wBAAgB,qBAAqB,IAAI,MAAM,CAO9C;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAoBjD;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAgBlD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAkB7E"}
@@ -4,6 +4,8 @@ export interface ApiRow {
4
4
  type?: string;
5
5
  default?: string;
6
6
  desc: string;
7
+ /** Exemplo curto de uso (renderizado em <code> na coluna “Exemplo”). */
8
+ example?: string;
7
9
  }
8
10
  /** Controle do playground ao vivo. */
9
11
  export interface DemoControl {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/docs/types.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sCAAsC;AACtC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,mCAAmC;IACnC,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,aAAa,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClD,aAAa,CAAC,EAAE;QACd,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE;YACV,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,QAAQ,CAAC,EAAE,OAAO,CAAC;YACnB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,WAAW,EAAE,MAAM,CAAC;SACrB,EAAE,CAAC;QACJ,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/docs/types.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,sCAAsC;AACtC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,mCAAmC;IACnC,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,aAAa,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClD,aAAa,CAAC,EAAE;QACd,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE;YACV,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,QAAQ,CAAC,EAAE,OAAO,CAAC;YACnB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,WAAW,EAAE,MAAM,CAAC;SACrB,EAAE,CAAC;QACJ,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH"}
@@ -1390,6 +1390,13 @@
1390
1390
  border-bottom: 2px solid var(--fx-border-default);
1391
1391
  white-space: nowrap;
1392
1392
  user-select: none;
1393
+ /* Cor/peso do cabeçalho por token de TABELA. Sem isso o th herda
1394
+ --fx-text-default do host e o peso vem do UA (bold). */
1395
+ color: var(
1396
+ --fx-table-header-text,
1397
+ var(--fx-table-text, var(--fx-text-default))
1398
+ );
1399
+ font-weight: var(--fx-table-header-weight, bold);
1393
1400
  }
1394
1401
  th.sortable {
1395
1402
  cursor: pointer;
@@ -1434,6 +1441,12 @@
1434
1441
  td {
1435
1442
  padding: var(--fx-space-sm) var(--fx-space-md);
1436
1443
  border-bottom: 1px solid var(--fx-border-default);
1444
+ /* Texto do corpo: token próprio para não depender de
1445
+ --fx-text-default (que afeta TODOS os textos do app) e para
1446
+ não herdar o font-weight do body da página — era isso que
1447
+ fazia a célula parecer mais clara que o cabeçalho. */
1448
+ color: var(--fx-table-text, var(--fx-text-default));
1449
+ font-weight: var(--fx-table-text-weight, inherit);
1437
1450
  }
1438
1451
  tbody tr {
1439
1452
  cursor: pointer;
@@ -1614,13 +1627,17 @@
1614
1627
  </div>
1615
1628
  <div class="error-message" part="error"></div>
1616
1629
  </div>
1617
- `),r){r.classList.add("flabel"),r.setAttribute("part","label");const a=this.root.querySelector(".wrapper"),o=this.root.querySelector(".field-box");a&&o&&(i?a.insertBefore(r,o):o.appendChild(r)),this._labelClickBound||(this._labelClickBound=!0,r.addEventListener("click",s=>{var c,u;s.preventDefault(),(u=(c=this.targetControl)==null?void 0:c.focus)==null||u.call(c)}))}this.attachEvents(),this.syncControlReference(e),this.syncState(),this.applyError(),this.applyFull(),this.applyIcon(),this.syncIconIndent()}disconnectedCallback(){var e,t;super.disconnectedCallback(),(e=this.observer)==null||e.disconnect(),(t=this._labelObserver)==null||t.disconnect(),this._labelObserver=void 0}findControl(){var e;return(e=Array.from(this.children).find(t=>t.tagName.toLowerCase()!=="label"))!=null?e:null}findLabel(){var e;return(e=Array.from(this.children).find(t=>t.tagName.toLowerCase()==="label"))!=null?e:null}attachEvents(){const e=this.root.querySelector("slot");e&&e.addEventListener("slotchange",()=>{const t=this.findControl();this.syncControlReference(t),this.syncState(),this.applyError(),this.applyFull()}),!this.listenersReady&&(this.listenersReady=!0,this.addEventListener("focusin",()=>this.syncState()),this.addEventListener("focusout",()=>setTimeout(()=>this.syncState(),60)),this.addEventListener("input",()=>this.syncState()),this.addEventListener("change",()=>this.syncState()))}syncControlReference(e=this.targetControl){var t;e!==this.targetControl&&((t=this.observer)==null||t.disconnect(),this.observer=null,this.targetControl=e,e&&(this.observer=new MutationObserver(()=>{this.syncState(),this.syncIconIndent()}),this.observer.observe(e,{attributes:!0,attributeFilter:["value","values","start","end","open","icon","icon-pos"]})))}syncState(){var o,s,c;if(this.targetControl&&!this.targetControl.isConnected&&(this.targetControl=null,(o=this.observer)==null||o.disconnect(),this.observer=null),!this.targetControl){this.removeAttribute("active"),this.removeAttribute("focus");return}const e=this.targetControl,t=e.getAttribute?((s=e.getAttribute("value"))!=null?s:"").trim():"",r=e.getAttribute?((c=e.getAttribute("values"))!=null?c:"").trim():"",i=!!(typeof e.value=="string"&&e.value.trim()!==""||Array.isArray(e.values)&&e.values.length>0||t!==""||r!==""||e.hasAttribute&&(e.hasAttribute("open")||e.hasAttribute("start"))),a=this.isFocusedInside();i||a?(this.setAttribute("active",""),a?this.setAttribute("focus",""):this.removeAttribute("focus")):(this.removeAttribute("active"),this.removeAttribute("focus"))}syncIconIndent(){const e=this.root.querySelector(".flabel");if(!e)return;const t=this.targetControl,r=!!(t&&(t.hasAttribute("icon")||t.querySelector(':scope > [slot="icon"]'))&&t.getAttribute("icon-pos")!=="right");e.classList.toggle("icon-indent",r)}applyFull(){this.targetControl&&typeof this.targetControl.setAttribute=="function"&&(this.hasAttr("full")?this.targetControl.setAttribute("full",""):this.targetControl.removeAttribute("full"))}applyIcon(){if(this.targetControl&&typeof this.targetControl.setAttribute=="function"&&this.hasAttr("icon")){const e=this.getAttr("icon"),t=this.getAttr("icon-pos");e!==""?this.targetControl.setAttribute("icon",e):this.targetControl.setAttribute("icon",""),t&&t!=="left"?this.targetControl.setAttribute("icon-pos",t):this.targetControl.removeAttribute("icon-pos")}}applyError(){var r;const e=this.hasAttr("error")||this.hasAttr("invalid"),t=this.root.querySelector(".error-message");if(t&&(t.textContent=e?((r=this.getAttribute("error-text"))==null?void 0:r.trim())||"Valor inválido":""),this.targetControl&&typeof this.targetControl.setAttribute=="function"){const i=this.targetControl;e?(i.setAttribute("error",""),i.removeAttribute("success")):this.hasAttr("success")||this.hasAttr("valid")?(i.setAttribute("success",""),i.removeAttribute("error")):(i.removeAttribute("error"),i.removeAttribute("success"))}}isWithin(e){let t=e;for(;t&&t!==this;)t=t.parentElement;return t===this}isFocusedInside(){const e=document.activeElement;return!!(e&&this.isWithin(e))}};Br.styles=w`
1630
+ `),r){r.classList.add("flabel"),r.setAttribute("part","label");const a=this.root.querySelector(".wrapper"),o=this.root.querySelector(".field-box");a&&o&&(i?a.insertBefore(r,o):o.appendChild(r)),this._labelClickBound||(this._labelClickBound=!0,r.addEventListener("click",s=>{var c,u;s.preventDefault(),(u=(c=this.targetControl)==null?void 0:c.focus)==null||u.call(c)}))}this.attachEvents(),this.syncControlReference(e),this.syncState(),this.applyError(),this.applyFull(),this.applyIcon(),this.syncIconIndent()}disconnectedCallback(){var e,t;super.disconnectedCallback(),this.cancelScheduledSync(),(e=this.observer)==null||e.disconnect(),(t=this._labelObserver)==null||t.disconnect(),this._labelObserver=void 0}findControl(){var e;return(e=Array.from(this.children).find(t=>t.tagName.toLowerCase()!=="label"))!=null?e:null}findLabel(){var e;return(e=Array.from(this.children).find(t=>t.tagName.toLowerCase()==="label"))!=null?e:null}attachEvents(){const e=this.root.querySelector("slot");e&&e.addEventListener("slotchange",()=>{const t=this.findControl();this.syncControlReference(t),this.syncState(),this.applyError(),this.applyFull()}),!this.listenersReady&&(this.listenersReady=!0,this.addEventListener("focusin",()=>{this.cancelScheduledSync(),this.syncState()}),this.addEventListener("focusout",()=>this.scheduleSync()),this.addEventListener("input",()=>this.syncState()),this.addEventListener("change",()=>this.syncState()))}scheduleSync(){this.cancelScheduledSync(),this._syncTimer=setTimeout(()=>{this._syncTimer=void 0,this.syncState()},0)}cancelScheduledSync(){this._syncTimer!==void 0&&(clearTimeout(this._syncTimer),this._syncTimer=void 0)}syncControlReference(e=this.targetControl){var t;e!==this.targetControl&&((t=this.observer)==null||t.disconnect(),this.observer=null,this.targetControl=e,e&&(this.observer=new MutationObserver(()=>{this.syncState(),this.syncIconIndent()}),this.observer.observe(e,{attributes:!0,attributeFilter:["value","values","start","end","open","icon","icon-pos"]})))}syncState(){var c,u,p,h;if(this.targetControl&&!this.targetControl.isConnected&&(this.targetControl=null,(c=this.observer)==null||c.disconnect(),this.observer=null),!this.targetControl){this.removeAttribute("active"),this.removeAttribute("focus");return}const e=this.targetControl,t=e.getAttribute?((u=e.getAttribute("value"))!=null?u:"").trim():"",r=e.getAttribute?((p=e.getAttribute("values"))!=null?p:"").trim():"",i=!!((h=e.hasAttribute)!=null&&h.call(e,"open")),a=!!(typeof e.value=="string"&&e.value.trim()!==""||Array.isArray(e.values)&&e.values.length>0||t!==""||r!==""||e.hasAttribute&&e.hasAttribute("start")),s=this.isFocusedInside()||i;a||s?this.setAttribute("active",""):this.removeAttribute("active"),s?this.setAttribute("focus",""):this.removeAttribute("focus")}syncIconIndent(){const e=this.root.querySelector(".flabel");if(!e)return;const t=this.targetControl,r=!!(t&&(t.hasAttribute("icon")||t.querySelector(':scope > [slot="icon"]'))&&t.getAttribute("icon-pos")!=="right");e.classList.toggle("icon-indent",r)}applyFull(){this.targetControl&&typeof this.targetControl.setAttribute=="function"&&(this.hasAttr("full")?this.targetControl.setAttribute("full",""):this.targetControl.removeAttribute("full"))}applyIcon(){if(this.targetControl&&typeof this.targetControl.setAttribute=="function"&&this.hasAttr("icon")){const e=this.getAttr("icon"),t=this.getAttr("icon-pos");e!==""?this.targetControl.setAttribute("icon",e):this.targetControl.setAttribute("icon",""),t&&t!=="left"?this.targetControl.setAttribute("icon-pos",t):this.targetControl.removeAttribute("icon-pos")}}applyError(){var r;const e=this.hasAttr("error")||this.hasAttr("invalid"),t=this.root.querySelector(".error-message");if(t&&(t.textContent=e?((r=this.getAttribute("error-text"))==null?void 0:r.trim())||"Valor inválido":""),this.targetControl&&typeof this.targetControl.setAttribute=="function"){const i=this.targetControl;e?(i.setAttribute("error",""),i.removeAttribute("success")):this.hasAttr("success")||this.hasAttr("valid")?(i.setAttribute("success",""),i.removeAttribute("error")):(i.removeAttribute("error"),i.removeAttribute("success"))}}isWithin(e){let t=e;for(;t&&t!==this;)t=t.parentElement;return t===this}isFocusedInside(){const e=document.activeElement;return!!(e&&this.isWithin(e))}};Br.styles=w`
1618
1631
  :host {
1619
1632
  display: inline-block;
1620
1633
  position: relative;
1621
1634
  font-family: var(--fx-font-family);
1622
1635
  font-size: var(--fx-font-size);
1623
1636
  vertical-align: middle;
1637
+ /* Cor da label em UM único lugar. active (valor OU foco OU
1638
+ dropdown) só posiciona; focus (foco OU dropdown) só destaca —
1639
+ assim a label acompanha a borda, que só fica primária em foco. */
1640
+ --_flabel-color: var(--fx-text-muted, #94a3b8);
1624
1641
  }
1625
1642
  /* Full width: o host estica até o pai; o controle interno com
1626
1643
  atributo full (ou largura herdada) acompanha. */
@@ -1652,7 +1669,7 @@
1652
1669
  transform: translateY(-50%);
1653
1670
  font-size: calc(var(--fx-font-size, 14px) - 3px);
1654
1671
  font-weight: 600;
1655
- color: var(--fx-text-muted, #94a3b8);
1672
+ color: var(--_flabel-color);
1656
1673
  background-color: var(--fx-surface-background, #fff);
1657
1674
  padding: 0 4px;
1658
1675
  line-height: 1;
@@ -1670,8 +1687,11 @@
1670
1687
  color var(--fx-motion-duration-normal, 180ms)
1671
1688
  var(--fx-motion-easing, ease-in-out);
1672
1689
  }
1673
- :host([active]) .flabel {
1674
- color: var(--fx-color-primary, #4f46e5);
1690
+ /* Destacada = foco (ou dropdown aberto). NÃO usar [active]: ele
1691
+ também é verdadeiro com valor e sem foco — era o que deixava a
1692
+ label primária com a borda neutra. */
1693
+ :host([focus]) {
1694
+ --_flabel-color: var(--fx-color-primary, #4f46e5);
1675
1695
  }
1676
1696
 
1677
1697
  /* ---- variant="in": label como placeholder dentro, sobe ao focar/valor ---- */
@@ -1690,7 +1710,8 @@
1690
1710
  font-weight: 600;
1691
1711
  background-color: var(--fx-surface-background, #fff);
1692
1712
  padding: 0 5px;
1693
- color: var(--fx-color-primary, #4f46e5);
1713
+ /* Sem color aqui: a especificidade desta regra
1714
+ (variant + active) venceria :host([error])/:host([focus]). */
1694
1715
  }
1695
1716
 
1696
1717
  /* ---- variant="in" com ícone dentro do campo (fx-input icon):
@@ -1721,16 +1742,22 @@
1721
1742
  font-size: calc(var(--fx-font-size, 14px) - 3px);
1722
1743
  font-weight: 600;
1723
1744
  white-space: normal;
1745
+ /* Fora do campo, a label pode receber clique: o listener do
1746
+ render() foca o controle (nas variantes on/in o
1747
+ pointer-events: none é intencional, para não bloquear a borda). */
1748
+ pointer-events: auto;
1724
1749
  }
1725
1750
 
1726
- /* ---- estados de erro / sucesso ---- */
1727
- :host([error]) .flabel,
1728
- :host([invalid]) .flabel {
1729
- color: var(--fx-color-danger, #dc2626);
1751
+ /* ---- estados de erro / sucesso ----
1752
+ Declarados DEPOIS de :host([focus]) e com a mesma especificidade:
1753
+ a validação vence o foco pela ordem, sem depender de acidente. */
1754
+ :host([error]),
1755
+ :host([invalid]) {
1756
+ --_flabel-color: var(--fx-color-danger, #dc2626);
1730
1757
  }
1731
- :host([success]) .flabel,
1732
- :host([valid]) .flabel {
1733
- color: var(--fx-color-success, #10b981);
1758
+ :host([success]),
1759
+ :host([valid]) {
1760
+ --_flabel-color: var(--fx-color-success, #10b981);
1734
1761
  }
1735
1762
 
1736
1763
  /* ---- mensagem de erro sob o campo (fonte menor) ---- */