@wrrdev/fenix-ui 1.1.10 → 1.1.12

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.
Files changed (39) hide show
  1. package/README.md +20 -2
  2. package/dist/assets/{font-CF0TXsAP.js → font-DPeWTz-n.js} +58 -13
  3. package/dist/assets/font-DPeWTz-n.js.map +1 -0
  4. package/dist/components/breadcrumb/breadcrumb.d.ts +4 -0
  5. package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -1
  6. package/dist/components/breadcrumb/breadcrumb.js +12 -2
  7. package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
  8. package/dist/components/table/table.d.ts +18 -1
  9. package/dist/components/table/table.d.ts.map +1 -1
  10. package/dist/components/table/table.js +120 -23
  11. package/dist/components/table/table.js.map +1 -1
  12. package/dist/components/toast/toast.d.ts +8 -0
  13. package/dist/components/toast/toast.d.ts.map +1 -1
  14. package/dist/components/toast/toast.js +13 -0
  15. package/dist/components/toast/toast.js.map +1 -1
  16. package/dist/components/tooltip/directive.d.ts +8 -0
  17. package/dist/components/tooltip/directive.d.ts.map +1 -1
  18. package/dist/components/tooltip/directive.js +59 -18
  19. package/dist/components/tooltip/directive.js.map +1 -1
  20. package/dist/core/jsx.d.ts +19 -0
  21. package/dist/core/jsx.d.ts.map +1 -1
  22. package/dist/core/jsx.js.map +1 -1
  23. package/dist/docs/app.d.ts.map +1 -1
  24. package/dist/docs/componentes/table.doc.d.ts.map +1 -1
  25. package/dist/fenix-ui.umd.min.js +237 -170
  26. package/dist/fenix-ui3.esm.js +8 -13
  27. package/dist/fenix-ui3.esm.js.map +1 -1
  28. package/dist/fenix-ui4.esm.js +275 -114
  29. package/dist/fenix-ui4.esm.js.map +1 -1
  30. package/dist/icons/font.d.ts +23 -0
  31. package/dist/icons/font.d.ts.map +1 -1
  32. package/dist/icons/font.js +54 -8
  33. package/dist/icons/font.js.map +1 -1
  34. package/dist/icons/index.d.ts +7 -2
  35. package/dist/icons/index.d.ts.map +1 -1
  36. package/dist/icons/index.js +6 -11
  37. package/dist/icons/index.js.map +1 -1
  38. package/package.json +1 -1
  39. package/dist/assets/font-CF0TXsAP.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"table.js","sources":["../../../src/components/table/table.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from \"../../core/sanitize\";\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\nimport { loadFenixIconsFont } from '../../icons/font';\nimport { renderCell } from \"./expr\";\nimport \"../select\";\n\n/**\n * <fx-table> — Tabela de dados estilo DataTable (dados + colunas configuráveis,\n * ordenação, filtros por coluna, paginação personalizável, busca global na toolbar\n * e modo lazy para busca no servidor).\n *\n * Colunas (light DOM):\n * <fx-column field=\"nome\" header=\"Nome\" sortable filterable align=\"left\">\n * opcional: <fx-cell>R$ {{value}}</fx-cell> para render customizado\n * ou conteúdo direto (HTML + {{ }}) como template alternativo\n * </fx-column>\n *\n * IMPORTANTE (frameworks): use <fx-cell>, NÃO <template>. A tag nativa\n * <template> é reservada — o Vue 3 a transforma em bloco do compilador e o\n * React gerencia os filhos, então o innerHTML chega vazio. <fx-cell> é uma\n * tag própria, sem significado especial para nenhum framework. A forma\n * antiga com <template> continua funcionando (compatibilidade) e há ainda\n * a opção `template=\"#id\"` apontando para um <template> externo.\n *\n * ⚠️ REGRA DO v-pre (Vue) — o conteúdo do <fx-cell> é TEXTO CRU:\n * {{ }} é avaliado pelo motor de expressões do <fx-table> (./expr.ts) e\n * NÃO pelo Vue. Sem `v-pre`, o compilador do Vue consome a interpolação\n * antes: emite `_toDisplayString(_ctx.value | _ctx.number)`, o conteúdo\n * chega sem as chaves no innerHTML e o vue-tsc acusa `value`/`number`\n * inexistentes no escopo. → SEMPRE use <fx-cell v-pre> no Vue.\n *\n * Cada framework tem a sua neutralização (todas verificadas por compilador):\n * - Vue 3 / Nuxt .... v-pre <fx-cell v-pre>{{ value }}</fx-cell>\n * - React / JSX ..... string JS <fx-cell>{'{{ value }}'}</fx-cell>\n * (o `{{ }}` solto é ERRO de sintaxe no parser JSX)\n * - Angular ......... ngNonBindable <fx-cell ngNonBindable>…</fx-cell>\n * - Svelte .......... string JS ou entidades HTML\n * {'{{ value }}'} ou &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.\n * Eventos: page-change, sort-change, row-click, filter-change (todos composed).\n */\nexport class FxTable extends FxElement {\n\tstatic override styles = css`\n\t\t${FENIX_ICON_BASE_CSS}\n\t\t:host {\n\t\t\tdisplay: block;\n\t\t\tfont-family: var(--fx-font-family);\n\t\t\tfont-size: var(--fx-font-size);\n\t\t\tcolor: var(--fx-text-default);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t}\n\t\t.table-wrap {\n\t\t\tposition: relative;\n\t\t}\n\t\t.scroll {\n\t\t\toverflow-x: auto;\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-radius-md);\n\t\t}\n\t\ttable {\n\t\t\twidth: 100%;\n\t\t\tborder-collapse: collapse;\n\t\t}\n\t\tth {\n\t\t\ttext-align: left;\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tbackground: var(--fx-surface-surface);\n\t\t\tborder-bottom: 2px solid var(--fx-border-default);\n\t\t\twhite-space: nowrap;\n\t\t\tuser-select: none;\n\t\t}\n\t\tth.sortable {\n\t\t\tcursor: pointer;\n\t\t}\n\t\tth.sortable:hover {\n\t\t\tcolor: var(--fx-color-primary);\n\t\t}\n\t\t.sort-ind {\n\t\t\tfont-size: calc(var(--fx-font-size) - 4px);\n\t\t\tmargin-left: var(--fx-space-3xs, 2px);\n\t\t\topacity: 0.45;\n\t\t\ttransition: opacity var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\tth.sortable:hover .sort-ind {\n\t\t\topacity: 0.85;\n\t\t}\n\t\t.sort-ind.active {\n\t\t\topacity: 1;\n\t\t\tcolor: var(--fx-color-primary);\n\t\t}\n\t\t.filter-row th {\n\t\t\tpadding: var(--fx-space-3xs, 4px) var(--fx-space-md) var(--fx-space-sm);\n\t\t\tborder-bottom-width: 1px;\n\t\t}\n\t\t.filter {\n\t\t\twidth: 100%;\n\t\t\tbox-sizing: border-box;\n\t\t\tfont: inherit;\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t\tcolor: var(--fx-text-default);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-radius-sm);\n\t\t\tpadding: var(--fx-space-3xs, 4px) var(--fx-space-xs);\n\t\t\toutline: none;\n\t\t}\n\t\t.filter:focus {\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tbox-shadow: var(--fx-effect-focus-ring, none);\n\t\t}\n\t\ttd {\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tborder-bottom: 1px solid var(--fx-border-default);\n\t\t}\n\t\ttbody tr {\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\t/* Listrado: alterna a superfície (cinza). Antes usava\n\t\t --fx-surface-background, que é a MESMA cor do fundo do :host e\n\t\t portanto pintava branco sobre branco (listrado invisível). */\n\t\t:host([striped]) tbody tr:nth-child(even) {\n\t\t\tbackground: var(--fx-surface-surface, #f8fafc);\n\t\t}\n\t\t/* O hover é opt-in via atributo (o clique na linha continua sempre ativo). */\n\t\t:host([hover]) tbody tr:hover {\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--fx-color-primary) 8%,\n\t\t\t\ttransparent\n\t\t\t);\n\t\t}\n\t\t.empty {\n\t\t\ttext-align: center;\n\t\t\tpadding: var(--fx-space-xl);\n\t\t\tcolor: var(--fx-text-muted);\n\t\t}\n\t\t.pager {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tflex-wrap: wrap;\n\t\t\tpadding: var(--fx-space-sm) 0;\n\t\t}\n\t\t:host([pagination-position=\"center\"]) .pager {\n\t\t\tjustify-content: center;\n\t\t}\n\t\t:host([pagination-position=\"right\"]) .pager {\n\t\t\tjustify-content: flex-end;\n\t\t}\n\t\t.pager .info {\n\t\t\tcolor: var(--fx-text-muted);\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t}\n\t\t.pg-btn {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tmin-width: var(--fx-size-sm);\n\t\t\theight: var(--fx-size-sm);\n\t\t\tfont: inherit;\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px);\n\t\t\tcolor: var(--fx-text-default);\n\t\t\tbackground: var(--fx-surface-background);\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-radius: var(--fx-radius-sm);\n\t\t\tpadding: var(--fx-space-3xs, 4px) var(--fx-space-sm);\n\t\t\tcursor: pointer;\n\t\t\ttransition: border-color var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\t/* O glifo precisa de 1px a mais que o dígito: a altura de traço do ícone é\n\t\t maior que a do número, então igualar os dois font-size deixa a seta\n\t\t visivelmente maior que o texto. */\n\t\t.pg-btn .fx-icon {\n\t\t\tfont-size: calc(var(--fx-font-size) - 1px);\n\t\t}\n\t\t.pg-btn:hover:not(:disabled):not([aria-current=\"true\"]) {\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tcolor: var(--fx-color-primary);\n\t\t}\n\t\t.pg-btn:disabled {\n\t\t\topacity: 0.5;\n\t\t\tcursor: not-allowed;\n\t\t}\n\t\t.pg-btn[aria-current=\"true\"] {\n\t\t\tbackground: var(--fx-color-primary);\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tcolor: #fff;\n\t\t\tfont-weight: var(--fx-font-weight);\n\t\t}\n\t\tfx-select {\n\t\t\tvertical-align: middle;\n\t\t}\n\t\tfx-select::part(trigger) {\n\t\t\tmin-width: 64px !important;\n\t\t\tmin-height: var(--fx-size-sm) !important;\n\t\t\tpadding: 0 var(--fx-space-sm) !important;\n\t\t\tborder-radius: var(--fx-radius-sm) !important;\n\t\t\tfont-size: calc(var(--fx-font-size) - 2px) !important;\n\t\t}\n\t\t.toolbar {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tflex-wrap: wrap;\n\t\t\tpadding: var(--fx-space-sm) var(--fx-space-md);\n\t\t\tborder: 1px solid var(--fx-border-default);\n\t\t\tborder-bottom: none;\n\t\t\tborder-radius: var(--fx-radius-md) var(--fx-radius-md) 0 0;\n\t\t\tbackground: var(--fx-surface-background);\n\t\t}\n\t\t.toolbar:empty {\n\t\t\tdisplay: none;\n\t\t}\n\t\t.loading-overlay {\n\t\t\tposition: absolute;\n\t\t\tinset: 0;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--fx-surface-background) 85%,\n\t\t\t\ttransparent\n\t\t\t);\n\t\t\tbackdrop-filter: blur(2px);\n\t\t\tz-index: 10;\n\t\t\tborder-radius: var(--fx-radius-md);\n\t\t}\n\t\t.tbl-spinner {\n\t\t\twidth: 32px;\n\t\t\theight: 32px;\n\t\t\tborder: 3px solid var(--fx-border-default);\n\t\t\tborder-top-color: var(--fx-color-primary);\n\t\t\tborder-radius: 50%;\n\t\t\tanimation: tbl-spin 1s linear infinite;\n\t\t}\n\t\t@keyframes tbl-spin {\n\t\t\tto {\n\t\t\t\ttransform: rotate(360deg);\n\t\t\t}\n\t\t}\n\t\t.loading-text {\n\t\t\tcolor: var(--fx-text-muted);\n\t\t\tfont-size: calc(var(--fx-font-size) - 1px);\n\t\t}\n\t`;\n\n\tstatic override get observedAttributes(): string[] {\n\t\treturn [\n\t\t\t\"pagination\",\n\t\t\t\"rows\",\n\t\t\t\"rows-options\",\n\t\t\t\"striped\",\n\t\t\t\"hover\",\n\t\t\t\"toolbar\",\n\t\t\t\"empty-message\",\n\t\t\t\"pagination-position\",\n\t\t\t\"lazy\",\n\t\t\t\"total\",\n\t\t\t\"loading\",\n\t\t\t\"loading-message\",\n\t\t\t\"sort-field\",\n\t\t\t\"sort-order\",\n\t\t];\n\t}\n\n\tprivate _data: Record<string, unknown>[] = [];\n\tprivate _total?: number;\n\tprivate page = 0;\n\tprivate sortField = \"\";\n\tprivate sortDir: 1 | -1 = 1;\n\tprivate filters: Record<string, string> = {};\n\tprivate globalSearch = \"\";\n\tprivate _columnObserver?: MutationObserver;\n\n\tget data(): Record<string, unknown>[] {\n\t\treturn this._data;\n\t}\n\tset data(value: Record<string, unknown>[]) {\n\t\tthis._data = Array.isArray(value) ? value : [];\n\t\tthis.render();\n\t}\n\n\tget total(): number {\n\t\treturn this._total ?? this._data.length;\n\t}\n\tset total(value: number) {\n\t\tthis._total = value;\n\t\tthis.setAttribute(\"total\", String(value));\n\t}\n\n\tget rowsPerPage(): number {\n\t\tconst r = Number(this.getAttr(\"rows\", \"10\"));\n\t\treturn r > 0 ? r : 10;\n\t}\n\tset rowsPerPage(value: number) {\n\t\tthis.setAttribute(\"rows\", String(value));\n\t}\n\n\tget lazy(): boolean {\n\t\treturn this.hasAttr(\"lazy\");\n\t}\n\n\tprivate get columns(): {\n\t\tfield: string;\n\t\theader: string;\n\t\tsortable: boolean;\n\t\tfilterable: boolean;\n\t\talign: string;\n\t\ttemplate?: HTMLElement;\n\t\tdirectTemplate?: string;\n\t}[] {\n\t\treturn [...this.querySelectorAll(\"fx-column\")].map((c) => {\n\t\t\tconst tpl = this._resolveCellTemplate(c);\n\t\t\tconst direct = tpl ? undefined : c.innerHTML.trim();\n\t\t\tconst align = c.getAttribute(\"align\") ?? \"left\";\n\t\t\treturn {\n\t\t\t\tfield: c.getAttribute(\"field\") ?? \"\",\n\t\t\t\theader: c.getAttribute(\"header\") ?? c.getAttribute(\"field\") ?? \"\",\n\t\t\t\tsortable: c.hasAttribute(\"sortable\"),\n\t\t\t\tfilterable: c.hasAttribute(\"filterable\"),\n\t\t\t\talign: [\"left\", \"center\", \"right\", \"justify\", \"start\", \"end\"].includes(align) ? align : \"left\",\n\t\t\t\ttemplate: tpl ?? undefined,\n\t\t\t\tdirectTemplate: direct || undefined,\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Resolve o template de célula de uma `<fx-column>`, em ordem de prioridade:\n\t *\n\t * 1. `<fx-cell>` — **recomendado**. Tag própria, sem significado especial\n\t * para Vue/React/Angular/Svelte: nenhum framework a intercepta, então o\n\t * `innerHTML` sempre chega intacto ao componente. O light DOM do\n\t * `<fx-table>` não é exibido (a tabela não usa `<slot>`), então o\n\t * conteúdo-fonte nunca aparece na tela.\n\t *\n\t * ⚠️ O conteúdo é TEXTO CRU para o motor de expressões: o `{{ }}` NÃO é\n\t * interpretado pelo framework hospedeiro. Cada um precisa neutralizar a\n\t * própria sintaxe antes (ver o cabeçalho do arquivo): `v-pre` no Vue,\n\t * `{'{{ }}'}` no JSX, `ngNonBindable` no Angular, string/entidades no\n\t * Svelte. Sem isso o template não chega ao `renderCell`.\n\t * 2. `template=\"#id\"` (ou `template=\"id\"`) — aponta para um `<template>`\n\t * declarado **fora** da tabela, útil em React/Angular, onde é mais\n\t * simples manter o markup num único lugar.\n\t * 3. `<template>` dentro da coluna — forma **legada**, mantida por\n\t * compatibilidade. Em Vue 3 ela vira um bloco do compilador e em React os\n\t * filhos são gerenciados pelo framework; nos dois casos o `innerHTML`\n\t * chega vazio e a célula não renderiza. Prefira `<fx-cell>`.\n\t */\n\tprivate _resolveCellTemplate(c: Element): HTMLElement | null {\n\t\tconst cell = c.querySelector<HTMLElement>(\":scope > fx-cell\");\n\t\tif (cell) return cell;\n\t\tconst ref = c.getAttribute(\"template\");\n\t\tif (ref) {\n\t\t\tconst alvo = this._resolveRef(ref);\n\t\t\tif (alvo) return alvo;\n\t\t}\n\t\treturn c.querySelector<HTMLElement>(\"template\");\n\t}\n\n\tprivate get toolbarTemplate(): HTMLElement | null {\n\t\t// Mesma lógica da célula: `<fx-toolbar>` > `toolbar=\"#id\"` > legado.\n\t\tconst bar = this.querySelector<HTMLElement>(\":scope > fx-toolbar\");\n\t\tif (bar) return bar;\n\t\tconst ref = this.getAttr(\"toolbar\");\n\t\tif (ref) {\n\t\t\tconst alvo = this._resolveRef(ref);\n\t\t\tif (alvo) return alvo;\n\t\t}\n\t\treturn this.querySelector<HTMLElement>('template[slot=\"toolbar\"]');\n\t}\n\n\t/**\n\t * Resolve `ref` para um elemento: `#id`/`id` ⇒ `getElementById` no\n\t * documento; qualquer outro valor é tratado como seletor CSS relativo à\n\t * própria tabela. Valores inválidos devolvem `null` (sem exceção).\n\t */\n\tprivate _resolveRef(ref: string): HTMLElement | null {\n\t\tconst valor = ref.trim();\n\t\tif (!valor) return null;\n\t\tconst doc = this.ownerDocument;\n\t\tif (valor.startsWith('#')) return doc?.getElementById(valor.slice(1)) ?? null;\n\t\tif (/^[.#[>]/.test(valor)) {\n\t\t\ttry {\n\t\t\t\treturn this.querySelector<HTMLElement>(valor);\n\t\t\t} catch {\n\t\t\t\treturn null; // seletor inválido — degrada para o próximo fallback\n\t\t\t}\n\t\t}\n\t\treturn doc?.getElementById(valor) ?? null;\n\t}\n\n\tprotected override connectedCallback(): void {\n\t\tthis._parseDataAttribute();\n\t\tthis._parseSortAttributes();\n\t\t// O pager usa glifos da Fenix Icons: garante o @font-face mesmo quando o\n\t\t// app não importou `@wrrdev/fenix-ui/icons` (idempotente).\n\t\tloadFenixIconsFont();\n\t\tsuper.connectedCallback();\n\t\tthis._columnObserver = new MutationObserver(() => this.render());\n\t\tthis._columnObserver.observe(this, { childList: true, subtree: true });\n\t}\n\n\tprotected override disconnectedCallback(): void {\n\t\tthis._columnObserver?.disconnect();\n\t\tsuper.disconnectedCallback();\n\t}\n\n\t/** Ordenação inicial via `sort-field` / `sort-order` (documentados na API). */\n\tprivate _parseSortAttributes(): void {\n\t\tconst field = this.getAttr(\"sort-field\");\n\t\tif (field) this.sortField = field;\n\t\tconst order = this.getAttr(\"sort-order\");\n\t\tif (order === \"asc\" || order === \"desc\") this.sortDir = order === \"asc\" ? 1 : -1;\n\t}\n\n\tprivate _parseDataAttribute(): void {\n\t\tif (!this._data.length) {\n\t\t\ttry {\n\t\t\t\tconst json = this.getAttr(\"data\");\n\t\t\t\tif (json) this._data = JSON.parse(json);\n\t\t\t} catch {\n\t\t\t\t/* atributo invalido - ignora */\n\t\t\t}\n\t\t}\n\t\tconst totalAttr = this.getAttr(\"total\");\n\t\tif (totalAttr) {\n\t\t\tconst n = Number(totalAttr);\n\t\t\tif (!isNaN(n)) this._total = n;\n\t\t}\n\t}\n\n\tprivate computeRows(): { rows: Record<string, unknown>[]; total: number } {\n\t\tlet out = [...this._data];\n\n\t\tfor (const [field, term] of Object.entries(this.filters)) {\n\t\t\tif (!term) continue;\n\t\t\tconst t = term.toLowerCase();\n\t\t\tout = out.filter((r) =>\n\t\t\t\tString(r[field] ?? \"\")\n\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t.includes(t),\n\t\t\t);\n\t\t}\n\n\t\tif (this.globalSearch) {\n\t\t\tconst t = this.globalSearch.toLowerCase();\n\t\t\tconst searchFields = this._getSearchFields();\n\t\t\tout = out.filter((r) => {\n\t\t\t\tconst fields = searchFields.length ? searchFields : Object.keys(r);\n\t\t\t\treturn fields.some((f) =>\n\t\t\t\t\tString(r[f] ?? \"\")\n\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t.includes(t),\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\n\t\tconst total = this.lazy ? this.total : out.length;\n\n\t\tif (this.sortField) {\n\t\t\tout.sort((a, b) => {\n\t\t\t\tconst av = a[this.sortField],\n\t\t\t\t\tbv = b[this.sortField];\n\t\t\t\tif (typeof av === \"number\" && typeof bv === \"number\")\n\t\t\t\t\treturn (av - bv) * this.sortDir;\n\t\t\t\treturn (\n\t\t\t\t\tString(av ?? \"\").localeCompare(String(bv ?? \"\"), \"pt-BR\") *\n\t\t\t\t\tthis.sortDir\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\n\t\tif (this.hasAttr(\"pagination\") && !this.lazy) {\n\t\t\tconst start = this.page * this.rowsPerPage;\n\t\t\tout = out.slice(start, start + this.rowsPerPage);\n\t\t}\n\n\t\treturn { rows: out, total };\n\t}\n\n\tprivate _getSearchFields(): string[] {\n\t\tconst input = this.root.querySelector<HTMLInputElement>(\n\t\t\t\"[data-search-fields]\",\n\t\t);\n\t\tif (!input) return [];\n\t\tconst attr = input.getAttribute(\"data-search-fields\");\n\t\tif (attr === null) return [];\n\t\treturn attr\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean);\n\t}\n\n\tprivate cellHtml(\n\t\tcol: {\n\t\t\tfield: string;\n\t\t\ttemplate?: HTMLElement;\n\t\t\tdirectTemplate?: string;\n\t\t},\n\t\trow: Record<string, unknown>,\n\t): string {\n\t\tconst template = col.template?.innerHTML ?? col.directTemplate;\n\t\tif (template) return renderCell(template, row, col.field);\n\t\treturn esc(String(row[col.field] ?? \"\"));\n\t}\n\n\tprotected override render(): void {\n\t\tconst cols = this.columns.filter((c) => c.field);\n\t\tconst { rows, total } = this.computeRows();\n\t\tconst paginated = this.hasAttr(\"pagination\");\n\t\tconst pages = Math.max(1, Math.ceil(total / this.rowsPerPage));\n\t\tif (this.page >= pages) this.page = pages - 1;\n\n\t\tconst toolbar = this.toolbarTemplate;\n\t\tconst toolbarHtml = toolbar\n\t\t\t? `<div class=\"toolbar\" part=\"toolbar\">${toolbar.innerHTML}</div>`\n\t\t\t: \"\";\n\n\t\tconst headHtml = cols\n\t\t\t.map((c) => {\n\t\t\t\tconst isSorted = this.sortField === c.field;\n\t\t\t\tconst ind = isSorted\n\t\t\t\t\t? this.sortDir === 1\n\t\t\t\t\t\t? \"▲\"\n\t\t\t\t\t\t: \"▼\"\n\t\t\t\t\t: c.sortable\n\t\t\t\t\t\t? \"⇅\"\n\t\t\t\t\t\t: \"\";\n\t\t\t\tconst activeClass = isSorted ? \"active\" : \"\";\n\t\t\t\treturn `<th part=\"header\" class=\"${c.sortable ? \"sortable\" : \"\"}\" style=\"text-align:${c.align}\" data-field=\"${esc(c.field)}\">${esc(c.header)}${ind ? `<span class=\"sort-ind ${activeClass}\">${ind}</span>` : \"\"}</th>`;\n\t\t\t})\n\t\t\t.join(\"\");\n\n\t\tconst filterRow = cols.some((c) => c.filterable)\n\t\t\t? `<tr class=\"filter-row\">${cols.map((c) => `<th style=\"text-align:${c.align}\">${c.filterable ? `<input class=\"filter\" data-field=\"${esc(c.field)}\" placeholder=\"${esc(\"Filtrar…\")}\" value=\"${esc(this.filters[c.field] ?? \"\")}\">` : \"\"}</th>`).join(\"\")}</tr>`\n\t\t\t: \"\";\n\n\t\tconst bodyHtml = rows.length\n\t\t\t? rows\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(row, i) =>\n\t\t\t\t\t\t\t`<tr part=\"row\" data-index=\"${i}\">${cols.map((c) => `<td part=\"cell\" style=\"text-align:${c.align}\">${this.cellHtml(c, row)}</td>`).join(\"\")}</tr>`,\n\t\t\t\t\t)\n\t\t\t\t\t.join(\"\")\n\t\t\t: `<tr><td class=\"empty\" colspan=\"${cols.length || 1}\">${esc(this.getAttr(\"empty-message\", \"Nenhum registro encontrado\"))}</td></tr>`;\n\n\t\tconst pagerHtml = !paginated\n\t\t\t? \"\"\n\t\t\t: `\n <div class=\"pager\" part=\"pager\">\n <span class=\"info\">Página ${this.page + 1} de ${pages} · ${total} registros</span>\n <button type=\"button\" class=\"pg-btn\" part=\"first\" aria-label=\"Primeira página\" data-pg=\"first\" ${this.page === 0 ? \"disabled\" : \"\"}>${fenixIconHtml(\"first_page\")}</button>\n <button type=\"button\" class=\"pg-btn\" part=\"prev\" aria-label=\"Página anterior\" data-pg=\"prev\" ${this.page === 0 ? \"disabled\" : \"\"}>${fenixIconHtml(\"navigate_before\")}</button>\n ${Array.from({ length: pages }, (_, p) => `<button type=\"button\" class=\"pg-btn\" data-pg=\"${p}\" aria-current=\"${p === this.page}\">${p + 1}</button>`).join(\"\")}\n <button type=\"button\" class=\"pg-btn\" part=\"next\" aria-label=\"Próxima página\" data-pg=\"next\" ${this.page >= pages - 1 ? \"disabled\" : \"\"}>${fenixIconHtml(\"navigate_next\")}</button>\n <button type=\"button\" class=\"pg-btn\" part=\"last\" aria-label=\"Última página\" data-pg=\"last\" ${this.page >= pages - 1 ? \"disabled\" : \"\"}>${fenixIconHtml(\"last_page\")}</button>\n <label class=\"info\">${esc(\"Por página:\")}\n <fx-select class=\"rows-sel\" size=\"sm\" value=\"${this.rowsPerPage}\" aria-label=\"Itens por página\">${this.getAttr(\n\t\t\t\t\t\"rows-options\",\n\t\t\t\t\t\"5,10,20,50\",\n\t\t\t\t)\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((n) => n.trim())\n\t\t\t\t\t.map((n) => `<option value=\"${esc(n)}\">${esc(n)}</option>`)\n\t\t\t\t\t.join(\"\")}</fx-select>\n </label>\n </div>`;\n\n\t\tconst loading = this.hasAttr(\"loading\");\n\t\tconst loadingHtml = loading\n\t\t\t? `<div class=\"loading-overlay\" part=\"loading-overlay\"><div class=\"tbl-spinner\" part=\"spinner\"></div><span class=\"loading-text\">${esc(this.getAttr(\"loading-message\", \"Carregando…\"))}</span></div>`\n\t\t\t: \"\";\n\n\t\tthis.setTemplate(`\n ${toolbarHtml}\n <div class=\"table-wrap\">\n <div class=\"scroll\">\n <table part=\"table\">\n <thead><tr>${headHtml}</tr>${filterRow}</thead>\n <tbody>${bodyHtml}</tbody>\n </table>\n </div>\n ${loadingHtml}\n </div>\n ${pagerHtml}\n `);\n\n\t\t// Ordenação.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLTableCellElement>(\"th.sortable\")\n\t\t\t.forEach((th) => {\n\t\t\t\tth.addEventListener(\"click\", () => {\n\t\t\t\t\tconst f = th.dataset.field!;\n\t\t\t\t\tif (this.sortField === f)\n\t\t\t\t\t\tthis.sortDir = this.sortDir === 1 ? -1 : 1;\n\t\t\t\t\telse {\n\t\t\t\t\t\tthis.sortField = f;\n\t\t\t\t\t\tthis.sortDir = 1;\n\t\t\t\t\t}\n\t\t\t\t\tthis.render();\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"sort-change\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\tfield: this.sortField,\n\t\t\t\t\t\t\t\tdirection: this.sortDir === 1 ? \"asc\" : \"desc\",\n\t\t\t\t\t\t\t\tlazy: this.lazy,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\n\t\t// Filtros (preserva foco/caret ao digitar).\n\t\tthis.root.querySelectorAll<HTMLInputElement>(\".filter\").forEach((inp) => {\n\t\t\tinp.addEventListener(\"input\", () => {\n\t\t\t\tconst field = inp.dataset.field!;\n\t\t\t\tconst value = inp.value;\n\t\t\t\tconst pos = inp.selectionStart;\n\t\t\t\tthis.filters[field] = value;\n\t\t\t\tthis.page = 0;\n\t\t\t\tthis.render();\n\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\tnew CustomEvent(\"filter-change\", {\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\tdetail: { field, value, lazy: this.lazy },\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tconst again = this.root.querySelector<HTMLInputElement>(\n\t\t\t\t\t`.filter[data-field=\"${esc(field)}\"]`,\n\t\t\t\t);\n\t\t\t\tif (again) {\n\t\t\t\t\tagain.focus();\n\t\t\t\t\tagain.setSelectionRange(pos, pos);\n\t\t\t\t}\n\t\t\t});\n\t\t\tinp.addEventListener(\"click\", (e) => e.stopPropagation());\n\t\t});\n\n\t\t// Busca global da toolbar.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLInputElement>(\"[data-search-fields]\")\n\t\t\t.forEach((inp) => {\n\t\t\t\tinp.addEventListener(\"input\", () => {\n\t\t\t\t\tthis.globalSearch = inp.value;\n\t\t\t\t\tthis.page = 0;\n\t\t\t\t\tconst pos = inp.selectionStart;\n\t\t\t\t\tthis.render();\n\t\t\t\t\tconst again = this.root.querySelector<HTMLInputElement>(\n\t\t\t\t\t\t\"[data-search-fields]\",\n\t\t\t\t\t);\n\t\t\t\t\tif (again) {\n\t\t\t\t\t\tagain.focus();\n\t\t\t\t\t\tagain.setSelectionRange(pos, pos);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\n\t\t// Paginação.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLButtonElement>(\".pg-btn\")\n\t\t\t.forEach((btn) => {\n\t\t\t\tbtn.addEventListener(\"click\", () => {\n\t\t\t\t\tconst pg = btn.dataset.pg!;\n\t\t\t\t\tthis.page =\n\t\t\t\t\t\tpg === \"first\"\n\t\t\t\t\t\t\t? 0\n\t\t\t\t\t\t\t: pg === \"prev\"\n\t\t\t\t\t\t\t\t? Math.max(0, this.page - 1)\n\t\t\t\t\t\t\t\t: pg === \"next\"\n\t\t\t\t\t\t\t\t\t? Math.min(pages - 1, this.page + 1)\n\t\t\t\t\t\t\t\t\t: pg === \"last\"\n\t\t\t\t\t\t\t\t\t\t? pages - 1\n\t\t\t\t\t\t\t\t\t\t: Number(pg);\n\t\t\t\t\tthis.render();\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"page-change\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\tpage: this.page + 1,\n\t\t\t\t\t\t\t\tpages,\n\t\t\t\t\t\t\t\trowsPerPage: this.rowsPerPage,\n\t\t\t\t\t\t\t\ttotal,\n\t\t\t\t\t\t\t\tlazy: this.lazy,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\t\tthis.root\n\t\t\t.querySelector<HTMLElement>(\"fx-select.rows-sel\")\n\t\t\t?.addEventListener(\"change\", (e) => {\n\t\t\t\tconst value = Number((e as CustomEvent).detail?.value);\n\t\t\t\tif (!value || value === this.rowsPerPage) return;\n\t\t\t\tthis.rowsPerPage = value;\n\t\t\t\tthis.page = 0;\n\t\t\t\tthis.render();\n\t\t\t});\n\n\t\t// Clique na linha.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLTableRowElement>(\"tbody tr[data-index]\")\n\t\t\t.forEach((tr) => {\n\t\t\t\ttr.addEventListener(\"click\", () => {\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"row-click\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\trow: rows[Number(tr.dataset.index)],\n\t\t\t\t\t\t\t\tindex: Number(tr.dataset.index),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\n\t\tthis._restoreToolbarSearch();\n\t}\n\n\tprivate _restoreToolbarSearch(): void {\n\t\tconst input = this.root.querySelector<HTMLInputElement>(\n\t\t\t\"[data-search-fields]\",\n\t\t);\n\t\tif (input && this.globalSearch) input.value = this.globalSearch;\n\t}\n}\n\nexport function defineFxTable(): typeof FxTable {\n return defineElement('fx-table', FxTable);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"table.js","sources":["../../../src/components/table/table.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from \"../../core/sanitize\";\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\nimport { loadFenixIconsFont } from '../../icons/font';\nimport { renderCell } from \"./expr\";\nimport \"../select\";\n\n/**\n * <fx-table> — Tabela de dados estilo DataTable (dados + colunas configuráveis,\n * ordenação, filtros por coluna, paginação personalizável, busca global na toolbar\n * e modo lazy para busca no servidor).\n *\n * Colunas (light DOM):\n * <fx-column field=\"nome\" header=\"Nome\" sortable filterable align=\"left\">\n * opcional: <fx-cell>R$ {{value}}</fx-cell> para render customizado\n * ou conteúdo direto (HTML + {{ }}) como template alternativo\n * </fx-column>\n *\n * IMPORTANTE (frameworks): use <fx-cell>, NÃO <template>. A tag nativa\n * <template> é reservada — o Vue 3 a transforma em bloco do compilador e o\n * React gerencia os filhos, então o innerHTML chega vazio. <fx-cell> é uma\n * tag própria, sem significado especial para nenhum framework. A forma\n * antiga com <template> continua funcionando (compatibilidade) e há ainda\n * a opção `template=\"#id\"` apontando para um <template> externo.\n *\n * ⚠️ REGRA DO v-pre (Vue) — o conteúdo do <fx-cell> é TEXTO CRU:\n * {{ }} é avaliado pelo motor de expressões do <fx-table> (./expr.ts) e\n * NÃO pelo Vue. Sem `v-pre`, o compilador do Vue consome a interpolação\n * antes: emite `_toDisplayString(_ctx.value | _ctx.number)`, o conteúdo\n * chega sem as chaves no innerHTML e o vue-tsc acusa `value`/`number`\n * inexistentes no escopo. → SEMPRE use <fx-cell v-pre> no Vue.\n *\n * Cada framework tem a sua neutralização (todas verificadas por compilador):\n * - Vue 3 / Nuxt .... v-pre <fx-cell v-pre>{{ value }}</fx-cell>\n * - React / JSX ..... string JS <fx-cell>{'{{ value }}'}</fx-cell>\n * (o `{{ }}` solto é ERRO de sintaxe no parser JSX)\n * - Angular ......... ngNonBindable <fx-cell ngNonBindable>…</fx-cell>\n * - Svelte .......... string JS ou entidades HTML\n * {'{{ value }}'} ou &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=\"true\"]) {\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tcolor: var(--fx-color-primary);\n\t\t}\n\t\t.pg-btn:disabled {\n\t\t\topacity: 0.5;\n\t\t\tcursor: not-allowed;\n\t\t}\n\t\t.pg-btn[aria-current=\"true\"] {\n\t\t\tbackground: var(--fx-color-primary);\n\t\t\tborder-color: var(--fx-color-primary);\n\t\t\tcolor: #fff;\n\t\t\tfont-weight: var(--fx-font-weight);\n\t\t}\n\t\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.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\n\tget data(): Record<string, unknown>[] {\n\t\treturn this._data;\n\t}\n\tset data(value: Record<string, unknown>[]) {\n\t\tthis._data = Array.isArray(value) ? value : [];\n\t\tthis.render();\n\t}\n\n\tget total(): number {\n\t\treturn this._total ?? this._data.length;\n\t}\n\tset total(value: number) {\n\t\tthis._total = value;\n\t\tthis.setAttribute(\"total\", String(value));\n\t}\n\n\tget rowsPerPage(): number {\n\t\tconst r = Number(this.getAttr(\"rows\", \"10\"));\n\t\treturn r > 0 ? r : 10;\n\t}\n\tset rowsPerPage(value: number) {\n\t\tthis.setAttribute(\"rows\", String(value));\n\t}\n\n\tget lazy(): boolean {\n\t\treturn this.hasAttr(\"lazy\");\n\t}\n\n\tprivate get columns(): {\n\t\tfield: string;\n\t\theader: string;\n\t\tsortable: boolean;\n\t\tfilterable: boolean;\n\t\talign: string;\n\t\ttemplate?: HTMLElement;\n\t\tdirectTemplate?: string;\n\t}[] {\n\t\treturn [...this.querySelectorAll(\"fx-column\")].map((c) => {\n\t\t\tconst tpl = this._resolveCellTemplate(c);\n\t\t\tconst direct = tpl ? undefined : c.innerHTML.trim();\n\t\t\tconst align = c.getAttribute(\"align\") ?? \"left\";\n\t\t\treturn {\n\t\t\t\tfield: c.getAttribute(\"field\") ?? \"\",\n\t\t\t\theader: c.getAttribute(\"header\") ?? c.getAttribute(\"field\") ?? \"\",\n\t\t\t\tsortable: c.hasAttribute(\"sortable\"),\n\t\t\t\tfilterable: c.hasAttribute(\"filterable\"),\n\t\t\t\talign: [\"left\", \"center\", \"right\", \"justify\", \"start\", \"end\"].includes(align) ? align : \"left\",\n\t\t\t\ttemplate: tpl ?? undefined,\n\t\t\t\tdirectTemplate: direct || undefined,\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Resolve o template de célula de uma `<fx-column>`, em ordem de prioridade:\n\t *\n\t * 1. `<fx-cell>` — **recomendado**. Tag própria, sem significado especial\n\t * para Vue/React/Angular/Svelte: nenhum framework a intercepta, então o\n\t * `innerHTML` sempre chega intacto ao componente. O light DOM do\n\t * `<fx-table>` não é exibido (a tabela não usa `<slot>`), então o\n\t * conteúdo-fonte nunca aparece na tela.\n\t *\n\t * ⚠️ O conteúdo é TEXTO CRU para o motor de expressões: o `{{ }}` NÃO é\n\t * interpretado pelo framework hospedeiro. Cada um precisa neutralizar a\n\t * própria sintaxe antes (ver o cabeçalho do arquivo): `v-pre` no Vue,\n\t * `{'{{ }}'}` no JSX, `ngNonBindable` no Angular, string/entidades no\n\t * Svelte. Sem isso o template não chega ao `renderCell`.\n\t * 2. `template=\"#id\"` (ou `template=\"id\"`) — aponta para um `<template>`\n\t * declarado **fora** da tabela, útil em React/Angular, onde é mais\n\t * simples manter o markup num único lugar.\n\t * 3. `<template>` dentro da coluna — forma **legada**, mantida por\n\t * compatibilidade. Em Vue 3 ela vira um bloco do compilador e em React os\n\t * filhos são gerenciados pelo framework; nos dois casos o `innerHTML`\n\t * chega vazio e a célula não renderiza. Prefira `<fx-cell>`.\n\t */\n\tprivate _resolveCellTemplate(c: Element): HTMLElement | null {\n\t\tconst cell = c.querySelector<HTMLElement>(\":scope > fx-cell\");\n\t\tif (cell) return cell;\n\t\tconst ref = c.getAttribute(\"template\");\n\t\tif (ref) {\n\t\t\tconst alvo = this._resolveRef(ref);\n\t\t\tif (alvo) return alvo;\n\t\t}\n\t\treturn c.querySelector<HTMLElement>(\"template\");\n\t}\n\n\tprivate get toolbarTemplate(): HTMLElement | null {\n\t\t// Mesma lógica da célula: `<fx-toolbar>` > `toolbar=\"#id\"` > legado.\n\t\tconst bar = this.querySelector<HTMLElement>(\":scope > fx-toolbar\");\n\t\tif (bar) return bar;\n\t\tconst ref = this.getAttr(\"toolbar\");\n\t\tif (ref) {\n\t\t\tconst alvo = this._resolveRef(ref);\n\t\t\tif (alvo) return alvo;\n\t\t}\n\t\treturn this.querySelector<HTMLElement>('template[slot=\"toolbar\"]');\n\t}\n\n\t/**\n\t * Resolve `ref` para um elemento: `#id`/`id` ⇒ `getElementById` no\n\t * documento; qualquer outro valor é tratado como seletor CSS relativo à\n\t * própria tabela. Valores inválidos devolvem `null` (sem exceção).\n\t */\n\tprivate _resolveRef(ref: string): HTMLElement | null {\n\t\tconst valor = ref.trim();\n\t\tif (!valor) return null;\n\t\tconst doc = this.ownerDocument;\n\t\tif (valor.startsWith('#')) return doc?.getElementById(valor.slice(1)) ?? null;\n\t\tif (/^[.#[>]/.test(valor)) {\n\t\t\ttry {\n\t\t\t\treturn this.querySelector<HTMLElement>(valor);\n\t\t\t} catch {\n\t\t\t\treturn null; // seletor inválido — degrada para o próximo fallback\n\t\t\t}\n\t\t}\n\t\treturn doc?.getElementById(valor) ?? null;\n\t}\n\n\tprotected override connectedCallback(): void {\n\t\tthis._parseDataAttribute();\n\t\tthis._parseSortAttributes();\n\t\t// O pager usa glifos da Fenix Icons: garante o @font-face mesmo quando o\n\t\t// app não importou `@wrrdev/fenix-ui/icons` (idempotente).\n\t\tloadFenixIconsFont();\n\t\tsuper.connectedCallback();\n\t\tthis._columnObserver = new MutationObserver(() => this.render());\n\t\tthis._columnObserver.observe(this, { childList: true, subtree: true });\n\t}\n\n\tprotected override disconnectedCallback(): void {\n\t\tthis._columnObserver?.disconnect();\n\t\tsuper.disconnectedCallback();\n\t}\n\n\t/** Ordenação inicial via `sort-field` / `sort-order` (documentados na API). */\n\tprivate _parseSortAttributes(): void {\n\t\tconst field = this.getAttr(\"sort-field\");\n\t\tif (field) this.sortField = field;\n\t\tconst order = this.getAttr(\"sort-order\");\n\t\tif (order === \"asc\" || order === \"desc\") this.sortDir = order === \"asc\" ? 1 : -1;\n\t}\n\n\tprivate _parseDataAttribute(): void {\n\t\tif (!this._data.length) {\n\t\t\ttry {\n\t\t\t\tconst json = this.getAttr(\"data\");\n\t\t\t\tif (json) this._data = JSON.parse(json);\n\t\t\t} catch {\n\t\t\t\t/* atributo invalido - ignora */\n\t\t\t}\n\t\t}\n\t\tconst totalAttr = this.getAttr(\"total\");\n\t\tif (totalAttr) {\n\t\t\tconst n = Number(totalAttr);\n\t\t\tif (!isNaN(n)) this._total = n;\n\t\t}\n\t}\n\n\tprivate computeRows(): { rows: Record<string, unknown>[]; total: number } {\n\t\tlet out = [...this._data];\n\n\t\tfor (const [field, term] of Object.entries(this.filters)) {\n\t\t\tif (!term) continue;\n\t\t\tconst t = term.toLowerCase();\n\t\t\tout = out.filter((r) =>\n\t\t\t\tString(r[field] ?? \"\")\n\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t.includes(t),\n\t\t\t);\n\t\t}\n\n\t\tif (this.globalSearch) {\n\t\t\tconst t = this.globalSearch.toLowerCase();\n\t\t\tconst searchFields = this._getSearchFields();\n\t\t\tout = out.filter((r) => {\n\t\t\t\tconst fields = searchFields.length ? searchFields : Object.keys(r);\n\t\t\t\treturn fields.some((f) =>\n\t\t\t\t\tString(r[f] ?? \"\")\n\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t.includes(t),\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\n\t\tconst total = this.lazy ? this.total : out.length;\n\n\t\tif (this.sortField) {\n\t\t\tout.sort((a, b) => {\n\t\t\t\tconst av = a[this.sortField],\n\t\t\t\t\tbv = b[this.sortField];\n\t\t\t\tif (typeof av === \"number\" && typeof bv === \"number\")\n\t\t\t\t\treturn (av - bv) * this.sortDir;\n\t\t\t\treturn (\n\t\t\t\t\tString(av ?? \"\").localeCompare(String(bv ?? \"\"), \"pt-BR\") *\n\t\t\t\t\tthis.sortDir\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\n\t\tif (this.hasAttr(\"pagination\") && !this.lazy) {\n\t\t\tconst start = this.page * this.rowsPerPage;\n\t\t\tout = out.slice(start, start + this.rowsPerPage);\n\t\t}\n\n\t\treturn { rows: out, total };\n\t}\n\n\tprivate _getSearchFields(): string[] {\n\t\tconst input = this.root.querySelector<HTMLInputElement>(\n\t\t\t\"[data-search-fields]\",\n\t\t);\n\t\tif (!input) return [];\n\t\tconst attr = input.getAttribute(\"data-search-fields\");\n\t\tif (attr === null) return [];\n\t\treturn attr\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean);\n\t}\n\n\tprivate cellHtml(\n\t\tcol: {\n\t\t\tfield: string;\n\t\t\ttemplate?: HTMLElement;\n\t\t\tdirectTemplate?: string;\n\t\t},\n\t\trow: Record<string, unknown>,\n\t): string {\n\t\tconst template = col.template?.innerHTML ?? col.directTemplate;\n\t\tif (template) return renderCell(template, row, col.field);\n\t\treturn esc(String(row[col.field] ?? \"\"));\n\t}\n\n\t/**\n\t * Assinatura da \"casca\" da tabela: tudo que NÃO depende das linhas\n\t * (colunas, toolbar, filtros, paginação, loading).\n\t *\n\t * Serve para o caminho rápido: quando só `data` muda, a casca é idêntica e\n\t * podemos trocar apenas o `<tbody>`, sem recriar `<style>`, `<thead>`,\n\t * toolbar e paginação — nem religar todos os listeners.\n\t */\n\tprivate _shellKey = '';\n\n\tprotected override render(): void {\n\t\tconst cols = this.columns.filter((c) => c.field);\n\t\tconst { rows, total } = this.computeRows();\n\t\tconst paginated = this.hasAttr(\"pagination\");\n\t\tconst pages = Math.max(1, Math.ceil(total / this.rowsPerPage));\n\t\tif (this.page >= pages) this.page = pages - 1;\n\n\t\tconst toolbar = this.toolbarTemplate;\n\t\tconst toolbarHtml = toolbar\n\t\t\t? `<div class=\"toolbar\" part=\"toolbar\">${toolbar.innerHTML}</div>`\n\t\t\t: \"\";\n\n\t\tconst headHtml = cols\n\t\t\t.map((c) => {\n\t\t\t\tconst isSorted = this.sortField === c.field;\n\t\t\t\tconst ind = isSorted\n\t\t\t\t\t? this.sortDir === 1\n\t\t\t\t\t\t? \"▲\"\n\t\t\t\t\t\t: \"▼\"\n\t\t\t\t\t: c.sortable\n\t\t\t\t\t\t? \"⇅\"\n\t\t\t\t\t\t: \"\";\n\t\t\t\tconst activeClass = isSorted ? \"active\" : \"\";\n\t\t\t\treturn `<th part=\"header\" class=\"${c.sortable ? \"sortable\" : \"\"}\" style=\"text-align:${c.align}\" data-field=\"${esc(c.field)}\">${esc(c.header)}${ind ? `<span class=\"sort-ind ${activeClass}\">${ind}</span>` : \"\"}</th>`;\n\t\t\t})\n\t\t\t.join(\"\");\n\n\t\tconst filterRow = cols.some((c) => c.filterable)\n\t\t\t? `<tr class=\"filter-row\">${cols.map((c) => `<th style=\"text-align:${c.align}\">${c.filterable ? `<input class=\"filter\" data-field=\"${esc(c.field)}\" placeholder=\"${esc(\"Filtrar…\")}\" value=\"${esc(this.filters[c.field] ?? \"\")}\">` : \"\"}</th>`).join(\"\")}</tr>`\n\t\t\t: \"\";\n\n\t\tconst bodyHtml = rows.length\n\t\t\t? rows\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(row, i) =>\n\t\t\t\t\t\t\t`<tr part=\"row\" data-index=\"${i}\">${cols.map((c) => `<td part=\"cell\" style=\"text-align:${c.align}\">${this.cellHtml(c, row)}</td>`).join(\"\")}</tr>`,\n\t\t\t\t\t)\n\t\t\t\t\t.join(\"\")\n\t\t\t: `<tr><td class=\"empty\" colspan=\"${cols.length || 1}\">${esc(this.getAttr(\"empty-message\", \"Nenhum registro encontrado\"))}</td></tr>`;\n\n\t\tconst pagerHtml = !paginated\n\t\t\t? \"\"\n\t\t\t: `\n <div class=\"pager\" part=\"pager\">\n <span class=\"info\">Página ${this.page + 1} de ${pages} · ${total} registros</span>\n <button type=\"button\" class=\"pg-btn\" part=\"first\" aria-label=\"Primeira página\" data-pg=\"first\" ${this.page === 0 ? \"disabled\" : \"\"}>${fenixIconHtml(\"first_page\")}</button>\n <button type=\"button\" class=\"pg-btn\" part=\"prev\" aria-label=\"Página anterior\" data-pg=\"prev\" ${this.page === 0 ? \"disabled\" : \"\"}>${fenixIconHtml(\"navigate_before\")}</button>\n ${Array.from({ length: pages }, (_, p) => `<button type=\"button\" class=\"pg-btn\" data-pg=\"${p}\" aria-current=\"${p === this.page}\">${p + 1}</button>`).join(\"\")}\n <button type=\"button\" class=\"pg-btn\" part=\"next\" aria-label=\"Próxima página\" data-pg=\"next\" ${this.page >= pages - 1 ? \"disabled\" : \"\"}>${fenixIconHtml(\"navigate_next\")}</button>\n <button type=\"button\" class=\"pg-btn\" part=\"last\" aria-label=\"Última página\" data-pg=\"last\" ${this.page >= pages - 1 ? \"disabled\" : \"\"}>${fenixIconHtml(\"last_page\")}</button>\n <label class=\"info\">${esc(\"Por página:\")}\n <fx-select class=\"rows-sel\" size=\"sm\" value=\"${this.rowsPerPage}\" aria-label=\"Itens por página\">${this.getAttr(\n\t\t\t\t\t\"rows-options\",\n\t\t\t\t\t\"5,10,20,50\",\n\t\t\t\t)\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((n) => n.trim())\n\t\t\t\t\t.map((n) => `<option value=\"${esc(n)}\">${esc(n)}</option>`)\n\t\t\t\t\t.join(\"\")}</fx-select>\n </label>\n </div>`;\n\n\t\tconst loading = this.hasAttr(\"loading\");\n\t\tconst loadingHtml = loading\n\t\t\t? `<div class=\"loading-overlay\" part=\"loading-overlay\"><div class=\"tbl-spinner\" part=\"spinner\"></div><span class=\"loading-text\">${esc(this.getAttr(\"loading-message\", \"Carregando…\"))}</span></div>`\n\t\t\t: \"\";\n\n\t\t// Caminho rápido: se a casca não mudou (só `data` foi substituído),\n\t\t// troca APENAS o <tbody>. Evita recriar <style>/<thead>/toolbar/pager e\n\t\t// religar todos os listeners a cada atribuição de `data` — que é o\n\t\t// gargalo em tabelas grandes (busca por tecla, paginação, reload).\n\t\tconst shellKey = [\n\t\t\ttoolbarHtml,\n\t\t\theadHtml,\n\t\t\tfilterRow,\n\t\t\tpagerHtml,\n\t\t\tloadingHtml,\n\t\t\tcols.length,\n\t\t].join(\"|\");\n\t\tif (shellKey === this._shellKey) {\n\t\t\tconst tbody = this.root.querySelector(\"tbody\");\n\t\t\tif (tbody) {\n\t\t\t\ttbody.innerHTML = bodyHtml;\n\t\t\t\tthis._bindRowClicks(rows);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthis._shellKey = shellKey;\n\n\t\tthis.setTemplate(`\n ${toolbarHtml}\n <div class=\"frame${toolbar ? \" 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.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLInputElement>(\"[data-search-fields]\")\n\t\t\t.forEach((inp) => {\n\t\t\t\tinp.addEventListener(\"input\", () => {\n\t\t\t\t\tthis.globalSearch = inp.value;\n\t\t\t\t\tthis.page = 0;\n\t\t\t\t\tconst pos = inp.selectionStart;\n\t\t\t\t\tthis.render();\n\t\t\t\t\tconst again = this.root.querySelector<HTMLInputElement>(\n\t\t\t\t\t\t\"[data-search-fields]\",\n\t\t\t\t\t);\n\t\t\t\t\tif (again) {\n\t\t\t\t\t\tagain.focus();\n\t\t\t\t\t\tagain.setSelectionRange(pos, pos);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\n\t\t// Paginação.\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLButtonElement>(\".pg-btn\")\n\t\t\t.forEach((btn) => {\n\t\t\t\tbtn.addEventListener(\"click\", () => {\n\t\t\t\t\tconst pg = btn.dataset.pg!;\n\t\t\t\t\tthis.page =\n\t\t\t\t\t\tpg === \"first\"\n\t\t\t\t\t\t\t? 0\n\t\t\t\t\t\t\t: pg === \"prev\"\n\t\t\t\t\t\t\t\t? Math.max(0, this.page - 1)\n\t\t\t\t\t\t\t\t: pg === \"next\"\n\t\t\t\t\t\t\t\t\t? Math.min(pages - 1, this.page + 1)\n\t\t\t\t\t\t\t\t\t: pg === \"last\"\n\t\t\t\t\t\t\t\t\t\t? pages - 1\n\t\t\t\t\t\t\t\t\t\t: Number(pg);\n\t\t\t\t\tthis.render();\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"page-change\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\tpage: this.page + 1,\n\t\t\t\t\t\t\t\tpages,\n\t\t\t\t\t\t\t\trowsPerPage: this.rowsPerPage,\n\t\t\t\t\t\t\t\ttotal,\n\t\t\t\t\t\t\t\tlazy: this.lazy,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\t\tthis.root\n\t\t\t.querySelector<HTMLElement>(\"fx-select.rows-sel\")\n\t\t\t?.addEventListener(\"change\", (e) => {\n\t\t\t\tconst value = Number((e as CustomEvent).detail?.value);\n\t\t\t\tif (!value || value === this.rowsPerPage) return;\n\t\t\t\tthis.rowsPerPage = value;\n\t\t\t\tthis.page = 0;\n\t\t\t\tthis.render();\n\t\t\t});\n\n\t\t// Clique na linha.\n\t\tthis._bindRowClicks(rows);\n\n\t\tthis._restoreToolbarSearch();\n\t}\n\n\t/**\n\t * Liga o clique das linhas ao evento `row-click`.\n\t *\n\t * Extraído do render para ser reaproveitado no caminho rápido (troca só do\n\t * <tbody>): os <tr> são novos, então precisam ser rebound.\n\t */\n\tprivate _bindRowClicks(rows: Record<string, unknown>[]): void {\n\t\tthis.root\n\t\t\t.querySelectorAll<HTMLTableRowElement>(\"tbody tr[data-index]\")\n\t\t\t.forEach((tr) => {\n\t\t\t\ttr.addEventListener(\"click\", () => {\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent(\"row-click\", {\n\t\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t\t\tdetail: {\n\t\t\t\t\t\t\t\trow: rows[Number(tr.dataset.index)],\n\t\t\t\t\t\t\t\tindex: Number(tr.dataset.index),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\t}\n\n\tprivate _restoreToolbarSearch(): void {\n\t\tconst input = this.root.querySelector<HTMLInputElement>(\n\t\t\t\"[data-search-fields]\",\n\t\t);\n\t\tif (input && this.globalSearch) input.value = this.globalSearch;\n\t}\n}\n\nexport function defineFxTable(): typeof FxTable {\n return defineElement('fx-table', FxTable);\n}\n"],"names":[],"mappings":";;;;;;;;AA0DO,MAAM,WAAN,MAAM,iBAAgB,UAAU;AAAA,EAAhC,cAAA;AAAA,UAAA,GAAA,SAAA;AA2SN,SAAQ,QAAmC,CAAA;AAE3C,SAAQ,OAAO;AACf,SAAQ,YAAY;AACpB,SAAQ,UAAkB;AAC1B,SAAQ,UAAkC,CAAA;AAC1C,SAAQ,eAAe;AAoPvB,SAAQ,YAAY;AAAA,EAAA;AAAA,EAhRpB,WAAoB,qBAA+B;AAClD,WAAO;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEF;AAAA,EAWA,IAAI,OAAkC;AACrC,WAAO,KAAK;AAAA,EACb;AAAA,EACA,IAAI,KAAK,OAAkC;AAC1C,SAAK,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAA;AAC5C,SAAK,OAAA;AAAA,EACN;AAAA,EAEA,IAAI,QAAgB;AACnB,WAAO,KAAK,UAAU,KAAK,MAAM;AAAA,EAClC;AAAA,EACA,IAAI,MAAM,OAAe;AACxB,SAAK,SAAS;AACd,SAAK,aAAa,SAAS,OAAO,KAAK,CAAC;AAAA,EACzC;AAAA,EAEA,IAAI,cAAsB;AACzB,UAAM,IAAI,OAAO,KAAK,QAAQ,QAAQ,IAAI,CAAC;AAC3C,WAAO,IAAI,IAAI,IAAI;AAAA,EACpB;AAAA,EACA,IAAI,YAAY,OAAe;AAC9B,SAAK,aAAa,QAAQ,OAAO,KAAK,CAAC;AAAA,EACxC;AAAA,EAEA,IAAI,OAAgB;AACnB,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC3B;AAAA,EAEA,IAAY,UAQR;AACH,WAAO,CAAC,GAAG,KAAK,iBAAiB,WAAW,CAAC,EAAE,IAAI,CAAC,MAAM;AACzD,YAAM,MAAM,KAAK,qBAAqB,CAAC;AACvC,YAAM,SAAS,MAAM,SAAY,EAAE,UAAU,KAAA;AAC7C,YAAM,QAAQ,EAAE,aAAa,OAAO,KAAK;AACzC,aAAO;AAAA,QACN,OAAO,EAAE,aAAa,OAAO,KAAK;AAAA,QAClC,QAAQ,EAAE,aAAa,QAAQ,KAAK,EAAE,aAAa,OAAO,KAAK;AAAA,QAC/D,UAAU,EAAE,aAAa,UAAU;AAAA,QACnC,YAAY,EAAE,aAAa,YAAY;AAAA,QACvC,OAAO,CAAC,QAAQ,UAAU,SAAS,WAAW,SAAS,KAAK,EAAE,SAAS,KAAK,IAAI,QAAQ;AAAA,QACxF,UAAU,OAAO;AAAA,QACjB,gBAAgB,UAAU;AAAA,MAAA;AAAA,IAE5B,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBQ,qBAAqB,GAAgC;AAC5D,UAAM,OAAO,EAAE,cAA2B,kBAAkB;AAC5D,QAAI,KAAM,QAAO;AACjB,UAAM,MAAM,EAAE,aAAa,UAAU;AACrC,QAAI,KAAK;AACR,YAAM,OAAO,KAAK,YAAY,GAAG;AACjC,UAAI,KAAM,QAAO;AAAA,IAClB;AACA,WAAO,EAAE,cAA2B,UAAU;AAAA,EAC/C;AAAA,EAEA,IAAY,kBAAsC;AAEjD,UAAM,MAAM,KAAK,cAA2B,qBAAqB;AACjE,QAAI,IAAK,QAAO;AAChB,UAAM,MAAM,KAAK,QAAQ,SAAS;AAClC,QAAI,KAAK;AACR,YAAM,OAAO,KAAK,YAAY,GAAG;AACjC,UAAI,KAAM,QAAO;AAAA,IAClB;AACA,WAAO,KAAK,cAA2B,0BAA0B;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,YAAY,KAAiC;AACpD,UAAM,QAAQ,IAAI,KAAA;AAClB,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,MAAM,KAAK;AACjB,QAAI,MAAM,WAAW,GAAG,EAAG,QAAO,KAAK,eAAe,MAAM,MAAM,CAAC,CAAC,KAAK;AACzE,QAAI,UAAU,KAAK,KAAK,GAAG;AAC1B,UAAI;AACH,eAAO,KAAK,cAA2B,KAAK;AAAA,MAC7C,QAAQ;AACP,eAAO;AAAA,MACR;AAAA,IACD;AACA,WAAO,KAAK,eAAe,KAAK,KAAK;AAAA,EACtC;AAAA,EAEmB,oBAA0B;AAC5C,SAAK,oBAAA;AACL,SAAK,qBAAA;AAGL,uBAAA;AACA,UAAM,kBAAA;AACN,SAAK,kBAAkB,IAAI,iBAAiB,MAAM,KAAK,QAAQ;AAC/D,SAAK,gBAAgB,QAAQ,MAAM,EAAE,WAAW,MAAM,SAAS,MAAM;AAAA,EACtE;AAAA,EAEmB,uBAA6B;AAC/C,SAAK,iBAAiB,WAAA;AACtB,UAAM,qBAAA;AAAA,EACP;AAAA;AAAA,EAGQ,uBAA6B;AACpC,UAAM,QAAQ,KAAK,QAAQ,YAAY;AACvC,QAAI,YAAY,YAAY;AAC5B,UAAM,QAAQ,KAAK,QAAQ,YAAY;AACvC,QAAI,UAAU,SAAS,UAAU,aAAa,UAAU,UAAU,QAAQ,IAAI;AAAA,EAC/E;AAAA,EAEQ,sBAA4B;AACnC,QAAI,CAAC,KAAK,MAAM,QAAQ;AACvB,UAAI;AACH,cAAM,OAAO,KAAK,QAAQ,MAAM;AAChC,YAAI,KAAM,MAAK,QAAQ,KAAK,MAAM,IAAI;AAAA,MACvC,QAAQ;AAAA,MAER;AAAA,IACD;AACA,UAAM,YAAY,KAAK,QAAQ,OAAO;AACtC,QAAI,WAAW;AACd,YAAM,IAAI,OAAO,SAAS;AAC1B,UAAI,CAAC,MAAM,CAAC,QAAQ,SAAS;AAAA,IAC9B;AAAA,EACD;AAAA,EAEQ,cAAkE;AACzE,QAAI,MAAM,CAAC,GAAG,KAAK,KAAK;AAExB,eAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,KAAK,OAAO,GAAG;AACzD,UAAI,CAAC,KAAM;AACX,YAAM,IAAI,KAAK,YAAA;AACf,YAAM,IAAI;AAAA,QAAO,CAAC,MACjB,OAAO,EAAE,KAAK,KAAK,EAAE,EACnB,cACA,SAAS,CAAC;AAAA,MAAA;AAAA,IAEd;AAEA,QAAI,KAAK,cAAc;AACtB,YAAM,IAAI,KAAK,aAAa,YAAA;AAC5B,YAAM,eAAe,KAAK,iBAAA;AAC1B,YAAM,IAAI,OAAO,CAAC,MAAM;AACvB,cAAM,SAAS,aAAa,SAAS,eAAe,OAAO,KAAK,CAAC;AACjE,eAAO,OAAO;AAAA,UAAK,CAAC,MACnB,OAAO,EAAE,CAAC,KAAK,EAAE,EACf,cACA,SAAS,CAAC;AAAA,QAAA;AAAA,MAEd,CAAC;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,OAAO,KAAK,QAAQ,IAAI;AAE3C,QAAI,KAAK,WAAW;AACnB,UAAI,KAAK,CAAC,GAAG,MAAM;AAClB,cAAM,KAAK,EAAE,KAAK,SAAS,GAC1B,KAAK,EAAE,KAAK,SAAS;AACtB,YAAI,OAAO,OAAO,YAAY,OAAO,OAAO;AAC3C,kBAAQ,KAAK,MAAM,KAAK;AACzB,eACC,OAAO,MAAM,EAAE,EAAE,cAAc,OAAO,MAAM,EAAE,GAAG,OAAO,IACxD,KAAK;AAAA,MAEP,CAAC;AAAA,IACF;AAEA,QAAI,KAAK,QAAQ,YAAY,KAAK,CAAC,KAAK,MAAM;AAC7C,YAAM,QAAQ,KAAK,OAAO,KAAK;AAC/B,YAAM,IAAI,MAAM,OAAO,QAAQ,KAAK,WAAW;AAAA,IAChD;AAEA,WAAO,EAAE,MAAM,KAAK,MAAA;AAAA,EACrB;AAAA,EAEQ,mBAA6B;AACpC,UAAM,QAAQ,KAAK,KAAK;AAAA,MACvB;AAAA,IAAA;AAED,QAAI,CAAC,MAAO,QAAO,CAAA;AACnB,UAAM,OAAO,MAAM,aAAa,oBAAoB;AACpD,QAAI,SAAS,KAAM,QAAO,CAAA;AAC1B,WAAO,KACL,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAA,CAAM,EACnB,OAAO,OAAO;AAAA,EACjB;AAAA,EAEQ,SACP,KAKA,KACS;AACT,UAAM,WAAW,IAAI,UAAU,aAAa,IAAI;AAChD,QAAI,SAAU,QAAO,WAAW,UAAU,KAAK,IAAI,KAAK;AACxD,WAAO,IAAI,OAAO,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;AAAA,EACxC;AAAA,EAYmB,SAAe;AACjC,UAAM,OAAO,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,KAAK;AAC/C,UAAM,EAAE,MAAM,UAAU,KAAK,YAAA;AAC7B,UAAM,YAAY,KAAK,QAAQ,YAAY;AAC3C,UAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,KAAK,WAAW,CAAC;AAC7D,QAAI,KAAK,QAAQ,MAAO,MAAK,OAAO,QAAQ;AAE5C,UAAM,UAAU,KAAK;AACrB,UAAM,cAAc,UACjB,uCAAuC,QAAQ,SAAS,WACxD;AAEH,UAAM,WAAW,KACf,IAAI,CAAC,MAAM;AACX,YAAM,WAAW,KAAK,cAAc,EAAE;AACtC,YAAM,MAAM,WACT,KAAK,YAAY,IAChB,MACA,MACD,EAAE,WACD,MACA;AACJ,YAAM,cAAc,WAAW,WAAW;AAC1C,aAAO,4BAA4B,EAAE,WAAW,aAAa,EAAE,uBAAuB,EAAE,KAAK,iBAAiB,IAAI,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,MAAM,CAAC,GAAG,MAAM,yBAAyB,WAAW,KAAK,GAAG,YAAY,EAAE;AAAA,IAChN,CAAC,EACA,KAAK,EAAE;AAET,UAAM,YAAY,KAAK,KAAK,CAAC,MAAM,EAAE,UAAU,IAC5C,0BAA0B,KAAK,IAAI,CAAC,MAAM,yBAAyB,EAAE,KAAK,KAAK,EAAE,aAAa,qCAAqC,IAAI,EAAE,KAAK,CAAC,kBAAkB,IAAI,UAAU,CAAC,YAAY,IAAI,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,UACtP;AAEH,UAAM,WAAW,KAAK,SACnB,KACC;AAAA,MACA,CAAC,KAAK,MACL,8BAA8B,CAAC,KAAK,KAAK,IAAI,CAAC,MAAM,qCAAqC,EAAE,KAAK,KAAK,KAAK,SAAS,GAAG,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,IAAA,EAE5I,KAAK,EAAE,IACR,kCAAkC,KAAK,UAAU,CAAC,KAAK,IAAI,KAAK,QAAQ,iBAAiB,4BAA4B,CAAC,CAAC;AAE1H,UAAM,YAAY,CAAC,YAChB,KACA;AAAA;AAAA,oCAE+B,KAAK,OAAO,CAAC,OAAO,KAAK,MAAM,KAAK;AAAA,yGACiC,KAAK,SAAS,IAAI,aAAa,EAAE,IAAI,cAAc,YAAY,CAAC;AAAA,uGAClE,KAAK,SAAS,IAAI,aAAa,EAAE,IAAI,cAAc,iBAAiB,CAAC;AAAA,UAClK,MAAM,KAAK,EAAE,QAAQ,SAAS,CAAC,GAAG,MAAM,iDAAiD,CAAC,mBAAmB,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC;AAAA,sGAC/D,KAAK,QAAQ,QAAQ,IAAI,aAAa,EAAE,IAAI,cAAc,eAAe,CAAC;AAAA,qGAC3E,KAAK,QAAQ,QAAQ,IAAI,aAAa,EAAE,IAAI,cAAc,WAAW,CAAC;AAAA,8BAC7I,IAAI,aAAa,CAAC;AAAA,yDACS,KAAK,WAAW,mCAAmC,KAAK;AAAA,MAC5G;AAAA,MACA;AAAA,IAAA,EAEC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAA,CAAM,EACnB,IAAI,CAAC,MAAM,kBAAkB,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,WAAW,EACzD,KAAK,EAAE,CAAC;AAAA;AAAA;AAIZ,UAAM,UAAU,KAAK,QAAQ,SAAS;AACtC,UAAM,cAAc,UACjB,gIAAgI,IAAI,KAAK,QAAQ,mBAAmB,aAAa,CAAC,CAAC,kBACnL;AAMH,UAAM,WAAW;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK;AAAA,IAAA,EACJ,KAAK,GAAG;AACV,QAAI,aAAa,KAAK,WAAW;AAChC,YAAM,QAAQ,KAAK,KAAK,cAAc,OAAO;AAC7C,UAAI,OAAO;AACV,cAAM,YAAY;AAClB,aAAK,eAAe,IAAI;AACxB;AAAA,MACD;AAAA,IACD;AACA,SAAK,YAAY;AAEjB,SAAK,YAAY;AAAA,QACX,WAAW;AAAA,yBACM,UAAU,iBAAiB,EAAE;AAAA;AAAA;AAAA;AAAA,2BAI3B,QAAQ,QAAQ,SAAS;AAAA,uBAC7B,QAAQ;AAAA;AAAA;AAAA,YAGnB,WAAW;AAAA;AAAA,UAEb,SAAS;AAAA;AAAA,KAEd;AAGH,SAAK,KACH,iBAAuC,aAAa,EACpD,QAAQ,CAAC,OAAO;AAChB,SAAG,iBAAiB,SAAS,MAAM;AAClC,cAAM,IAAI,GAAG,QAAQ;AACrB,YAAI,KAAK,cAAc;AACtB,eAAK,UAAU,KAAK,YAAY,IAAI,KAAK;AAAA,aACrC;AACJ,eAAK,YAAY;AACjB,eAAK,UAAU;AAAA,QAChB;AACA,aAAK,OAAA;AACL,aAAK;AAAA,UACJ,IAAI,YAAY,eAAe;AAAA,YAC9B,SAAS;AAAA,YACT,UAAU;AAAA,YACV,QAAQ;AAAA,cACP,OAAO,KAAK;AAAA,cACZ,WAAW,KAAK,YAAY,IAAI,QAAQ;AAAA,cACxC,MAAM,KAAK;AAAA,YAAA;AAAA,UACZ,CACA;AAAA,QAAA;AAAA,MAEH,CAAC;AAAA,IACF,CAAC;AAGF,SAAK,KAAK,iBAAmC,SAAS,EAAE,QAAQ,CAAC,QAAQ;AACxE,UAAI,iBAAiB,SAAS,MAAM;AACnC,cAAM,QAAQ,IAAI,QAAQ;AAC1B,cAAM,QAAQ,IAAI;AAClB,cAAM,MAAM,IAAI;AAChB,aAAK,QAAQ,KAAK,IAAI;AACtB,aAAK,OAAO;AACZ,aAAK,OAAA;AACL,aAAK;AAAA,UACJ,IAAI,YAAY,iBAAiB;AAAA,YAChC,SAAS;AAAA,YACT,UAAU;AAAA,YACV,QAAQ,EAAE,OAAO,OAAO,MAAM,KAAK,KAAA;AAAA,UAAK,CACxC;AAAA,QAAA;AAEF,cAAM,QAAQ,KAAK,KAAK;AAAA,UACvB,uBAAuB,IAAI,KAAK,CAAC;AAAA,QAAA;AAElC,YAAI,OAAO;AACV,gBAAM,MAAA;AACN,gBAAM,kBAAkB,KAAK,GAAG;AAAA,QACjC;AAAA,MACD,CAAC;AACD,UAAI,iBAAiB,SAAS,CAAC,MAAM,EAAE,iBAAiB;AAAA,IACzD,CAAC;AAGD,SAAK,KACH,iBAAmC,sBAAsB,EACzD,QAAQ,CAAC,QAAQ;AACjB,UAAI,iBAAiB,SAAS,MAAM;AACnC,aAAK,eAAe,IAAI;AACxB,aAAK,OAAO;AACZ,cAAM,MAAM,IAAI;AAChB,aAAK,OAAA;AACL,cAAM,QAAQ,KAAK,KAAK;AAAA,UACvB;AAAA,QAAA;AAED,YAAI,OAAO;AACV,gBAAM,MAAA;AACN,gBAAM,kBAAkB,KAAK,GAAG;AAAA,QACjC;AAAA,MACD,CAAC;AAAA,IACF,CAAC;AAGF,SAAK,KACH,iBAAoC,SAAS,EAC7C,QAAQ,CAAC,QAAQ;AACjB,UAAI,iBAAiB,SAAS,MAAM;AACnC,cAAM,KAAK,IAAI,QAAQ;AACvB,aAAK,OACJ,OAAO,UACJ,IACA,OAAO,SACN,KAAK,IAAI,GAAG,KAAK,OAAO,CAAC,IACzB,OAAO,SACN,KAAK,IAAI,QAAQ,GAAG,KAAK,OAAO,CAAC,IACjC,OAAO,SACN,QAAQ,IACR,OAAO,EAAE;AAChB,aAAK,OAAA;AACL,aAAK;AAAA,UACJ,IAAI,YAAY,eAAe;AAAA,YAC9B,SAAS;AAAA,YACT,UAAU;AAAA,YACV,QAAQ;AAAA,cACP,MAAM,KAAK,OAAO;AAAA,cAClB;AAAA,cACA,aAAa,KAAK;AAAA,cAClB;AAAA,cACA,MAAM,KAAK;AAAA,YAAA;AAAA,UACZ,CACA;AAAA,QAAA;AAAA,MAEH,CAAC;AAAA,IACF,CAAC;AACF,SAAK,KACH,cAA2B,oBAAoB,GAC9C,iBAAiB,UAAU,CAAC,MAAM;AACnC,YAAM,QAAQ,OAAQ,EAAkB,QAAQ,KAAK;AACrD,UAAI,CAAC,SAAS,UAAU,KAAK,YAAa;AAC1C,WAAK,cAAc;AACnB,WAAK,OAAO;AACZ,WAAK,OAAA;AAAA,IACN,CAAC;AAGF,SAAK,eAAe,IAAI;AAExB,SAAK,sBAAA;AAAA,EACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,eAAe,MAAuC;AAC7D,SAAK,KACH,iBAAsC,sBAAsB,EAC5D,QAAQ,CAAC,OAAO;AAChB,SAAG,iBAAiB,SAAS,MAAM;AAClC,aAAK;AAAA,UACJ,IAAI,YAAY,aAAa;AAAA,YAC5B,SAAS;AAAA,YACT,UAAU;AAAA,YACV,QAAQ;AAAA,cACP,KAAK,KAAK,OAAO,GAAG,QAAQ,KAAK,CAAC;AAAA,cAClC,OAAO,OAAO,GAAG,QAAQ,KAAK;AAAA,YAAA;AAAA,UAC/B,CACA;AAAA,QAAA;AAAA,MAEH,CAAC;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,wBAA8B;AACrC,UAAM,QAAQ,KAAK,KAAK;AAAA,MACvB;AAAA,IAAA;AAED,QAAI,SAAS,KAAK,aAAc,OAAM,QAAQ,KAAK;AAAA,EACpD;AACD;AAvyBC,SAAgB,SAAS;AAAA,IACtB,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAFhB,IAAM,UAAN;AA0yBA,SAAS,gBAAgC;AAC9C,SAAO,cAAc,YAAY,OAAO;AAC1C;"}
@@ -49,6 +49,14 @@ export declare class FxToast extends HTMLElement {
49
49
  private timer;
50
50
  constructor();
51
51
  connectedCallback(): void;
52
+ /**
53
+ * Limpa recursos ao sair do DOM e avisa a API imperativa (`FenixToast`).
54
+ *
55
+ * O aviso é essencial: `dismiss()` (timer ou botão de fechar) tira o
56
+ * elemento do DOM mas, sem isto, `ToastApi.map` continuaria segurando uma
57
+ * referência forte para cada toast já dispensado — crescimento de memória
58
+ * sem limite em uso prolongado.
59
+ */
52
60
  disconnectedCallback(): void;
53
61
  attributeChangedCallback(name: string): void;
54
62
  dismiss(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../../src/components/toast/toast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,QAAA,MAAM,SAAS,kGAGL,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AACvD,KAAK,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AA6H1D,qBAAa,OAAQ,SAAQ,WAAW;;IACtC,MAAM,KAAK,kBAAkB,aAE5B;IAED;;;;;;;;;;;OAWG;IACH,IAAI,QAAQ,IAAI,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAIxD;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,EAG5D;IAED;;;;;;;;;;OAUG;IACH,IAAI,cAAc,IAAI,MAAM,GAAG,OAAO,CAErC;IACD,IAAI,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAEzC;IAED,oEAAoE;IACpE,OAAO,KAAK,YAAY,GAIvB;IAED,qFAAqF;IACrF,IAAI,IAAI,IAAI,OAAO,GAAG,MAAM,GAAG,SAAS,CAGvC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,EAG3C;IAED,OAAO,CAAC,KAAK,CAA8C;;IAO3D,iBAAiB;IAWjB,oBAAoB;IAKpB,wBAAwB,CAAC,IAAI,EAAE,MAAM;IA6CrC,OAAO;CAqCR;AA8BD,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;IACzC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,0EAA0E;IAC1E,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACzB;AAED,cAAM,QAAQ;IACZ,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,GAAG,CAA8B;IAEzC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,MAAM;IAqBxG,KAAK,CAAC,EAAE,EAAE,MAAM;IAKhB,OAAO,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAmC;IACrF,KAAK,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAiC;IACjF,IAAI,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAgC;IAC/E,OAAO,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAmC;CACtF;AAED,eAAO,MAAM,UAAU,UAAiB,CAAC;AAIzC,wBAAgB,aAAa,SAE5B"}
1
+ {"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../../src/components/toast/toast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,QAAA,MAAM,SAAS,kGAGL,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AACvD,KAAK,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAoI1D,qBAAa,OAAQ,SAAQ,WAAW;;IACtC,MAAM,KAAK,kBAAkB,aAE5B;IAED;;;;;;;;;;;OAWG;IACH,IAAI,QAAQ,IAAI,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAIxD;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,EAG5D;IAED;;;;;;;;;;OAUG;IACH,IAAI,cAAc,IAAI,MAAM,GAAG,OAAO,CAErC;IACD,IAAI,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAEzC;IAED,oEAAoE;IACpE,OAAO,KAAK,YAAY,GAIvB;IAED,qFAAqF;IACrF,IAAI,IAAI,IAAI,OAAO,GAAG,MAAM,GAAG,SAAS,CAGvC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,EAG3C;IAED,OAAO,CAAC,KAAK,CAA8C;;IAO3D,iBAAiB;IAWjB;;;;;;;OAOG;IACH,oBAAoB;IAMpB,wBAAwB,CAAC,IAAI,EAAE,MAAM;IA6CrC,OAAO;CAqCR;AA8BD,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;IACzC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,0EAA0E;IAC1E,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACzB;AAED,cAAM,QAAQ;IACZ,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,GAAG,CAA8B;IAEzC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,MAAM;IA0BxG,KAAK,CAAC,EAAE,EAAE,MAAM;IAKhB,OAAO,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAmC;IACrF,KAAK,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAiC;IACjF,IAAI,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAgC;IAC/E,OAAO,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,YAAY,YAAmC;CACtF;AAED,eAAO,MAAM,UAAU,UAAiB,CAAC;AAIzC,wBAAgB,aAAa,SAE5B"}
@@ -6,6 +6,7 @@ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot
6
6
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
7
7
  var _FxToast_instances, syncProgress_fn, render_fn;
8
8
  import { __fenixPreload } from "../../_virtual/preload-helper.js";
9
+ const REMOVED_EVENT = "fx-toast:removed";
9
10
  const KIND_COLOR = {
10
11
  success: "var(--fx-color-success, #10b981)",
11
12
  error: "var(--fx-color-danger, #f43f5e)",
@@ -196,9 +197,18 @@ class FxToast extends HTMLElement {
196
197
  }
197
198
  __privateMethod(this, _FxToast_instances, syncProgress_fn).call(this);
198
199
  }
200
+ /**
201
+ * Limpa recursos ao sair do DOM e avisa a API imperativa (`FenixToast`).
202
+ *
203
+ * O aviso é essencial: `dismiss()` (timer ou botão de fechar) tira o
204
+ * elemento do DOM mas, sem isto, `ToastApi.map` continuaria segurando uma
205
+ * referência forte para cada toast já dispensado — crescimento de memória
206
+ * sem limite em uso prolongado.
207
+ */
199
208
  disconnectedCallback() {
200
209
  if (this.timer) clearTimeout(this.timer);
201
210
  this.timer = null;
211
+ this.dispatchEvent(new CustomEvent(REMOVED_EVENT, { bubbles: false, composed: false }));
202
212
  }
203
213
  attributeChangedCallback(name) {
204
214
  if (name === "duration" || name === "progress" || name === "progress-origin" || name === "kind") {
@@ -320,6 +330,9 @@ class ToastApi {
320
330
  if (o.progress) el.setAttribute("progress", o.progress);
321
331
  if (o.progressOrigin) el.setAttribute("progress-origin", o.progressOrigin);
322
332
  regionFor(o.position ?? "top-right").appendChild(el);
333
+ el.addEventListener(REMOVED_EVENT, () => {
334
+ if (this.map.get(id) === el) this.map.delete(id);
335
+ });
323
336
  this.map.set(id, el);
324
337
  return id;
325
338
  }
@@ -1 +1 @@
1
- {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../src/components/toast/toast.ts"],"sourcesContent":["/**\n * <fx-toast> — notificações imperativas com posição e duração configuráveis.\n *\n * Atributos: kind (success|error|info|warning), title, message,\n * position (top-left|top-center|top-right|bottom-left|bottom-center|bottom-right),\n * duration (ms; 0 = fixo até fechar), mode ('light'|'dark' — força o esquema de\n * cores do card independentemente do tema global), progress (booleano ou\n * 'always'|'hover'|'paused' — barra de contagem regressiva na cor do kind),\n * progress-origin (left|right — lado pelo qual a barra esvazia; padrão left).\n */\nconst POSITIONS = [\n 'top-left', 'top-center', 'top-right',\n 'bottom-left', 'bottom-center', 'bottom-right',\n] as const;\nexport type ToastPosition = (typeof POSITIONS)[number];\ntype ToastKind = 'success' | 'error' | 'info' | 'warning';\n\nconst KIND_COLOR: Record<ToastKind, string> = {\n success: 'var(--fx-color-success, #10b981)',\n error: 'var(--fx-color-danger, #f43f5e)',\n info: 'var(--fx-color-info, #0ea5e9)',\n warning: 'var(--fx-color-warning, #f59e0b)',\n};\n\n/** Ícone exibido no lugar da bolinha, conforme a variante. */\nconst KIND_ICON: Record<ToastKind, string> = {\n success: '✓',\n error: '✕',\n warning: '!',\n info: 'i',\n};\n\nconst CARD_CSS = `\n:host { display: contents; pointer-events: auto; }\n/* Modo explícito: sobrepõe os tokens herdados do tema global SOMENTE neste card. */\n:host([mode='dark']) {\n --fx-surface-background: #1e293b;\n --fx-text-default: #f1f5f9;\n --fx-text-muted: #94a3b8;\n --fx-border-default: #334155;\n --fx-shadow-lg: 0 10px 30px rgba(0, 0, 0, .5);\n --fx-surface-surface-hover: rgba(255, 255, 255, .08);\n}\n:host([mode='light']) {\n --fx-surface-background: #ffffff;\n --fx-text-default: #1e293b;\n --fx-text-muted: #64748b;\n --fx-border-default: #e2e8f0;\n --fx-shadow-lg: 0 10px 30px rgba(0, 0, 0, .14);\n --fx-surface-surface-hover: rgba(0, 0, 0, .05);\n}\n.fx-icon {\n font-family: var(--fx-icon-font, 'Fenix Icons');\n font-weight: normal;\n font-style: normal;\n line-height: 1;\n letter-spacing: normal;\n display: inline-block;\n white-space: nowrap;\n direction: ltr;\n font-variation-settings: 'FILL' 0, 'wght' 500, 'GRAD' 0, 'opsz' 24;\n -webkit-font-smoothing: antialiased;\n user-select: none;\n}\n.card {\n display: flex; align-items: flex-start; gap: 10px;\n min-width: 260px; max-width: 360px;\n background: var(--fx-surface-background, #fff);\n color: var(--fx-text-default, #1e293b);\n border: 1px solid var(--fx-border-default, #e2e8f0);\n border-left: 4px solid var(--kind);\n border-radius: var(--fx-radius-md, 8px);\n box-shadow: var(--fx-shadow-lg, 0 10px 30px rgba(0,0,0,.14));\n padding: var(--fx-space-sm, 8px) var(--fx-space-md, 12px);\n font-family: inherit;\n animation: fx-toast-in .25s ease;\n}\n@keyframes fx-toast-in { from { opacity: 0; transform: translateY(-6px); } }\n.card.leaving { opacity: 0; transition: opacity .2s ease; }\n.icon {\n width: 22px; height: 22px; border-radius: 50%;\n background: var(--kind);\n color: #fff;\n display: flex; align-items: center; justify-content: center;\n font-size: 12px; font-weight: 700;\n flex: none; margin-top: 2px;\n user-select: none;\n}\n.body { flex: 1; min-width: 0; }\n.title { font-weight: 600; font-size: 14px; }\n.msg { font-size: calc(var(--fx-font-size, 14px) - 2px); color: var(--fx-text-muted, #64748b); margin-top: 2px; word-break: break-word; white-space: pre-line; }\n.close {\n all: unset; cursor: pointer; flex: none; line-height: 1;\n color: var(--fx-text-muted, #64748b); font-size: 15px; padding: 2px 4px; border-radius: 4px;\n}\n.close:hover { color: var(--fx-color-danger, #f43f5e); background: var(--fx-surface-surface-hover, rgba(0,0,0,.05)); }\n\n/* --- Barra de contagem regressiva (atributo progress) ---\n Fica na base do card e esvazia durante a duração, na cor do kind (--kind) —\n ou seja, acompanha success/error/warning/info sem configuração extra.\n\n O lado é configurável pelo atributo progress-origin (padrão: left):\n left → a barra drena da direita para a esquerda (ancorada à esquerda)\n right → a barra drena da esquerda para a direita (ancorada à direita)\n A origem entra como custom property, então os keyframes são compartilhados.\n\n Usa escalaX em vez de width: a animação escreve só na compositor, sem\n reflow por frame. */\n.card { position: relative; overflow: hidden; }\n.progress {\n position: absolute;\n left: 0;\n bottom: 0;\n width: 100%;\n height: 3px;\n background: var(--kind);\n transform-origin: var(--toast-progress-origin, left center);\n transform: scaleX(1);\n border-radius: 0 0 var(--fx-radius-md, 8px) var(--fx-radius-md, 8px);\n pointer-events: none;\n will-change: transform;\n}\n.progress.run { animation: fx-toast-drain var(--toast-duration, 4000ms) linear forwards; }\n/* No modo hover a animação fica parada até o cursor/foco entrar no card. */\n.card:hover .progress.run,\n.card:focus-within .progress.run { animation-play-state: running; }\n@keyframes fx-toast-drain {\n from { transform: scaleX(1); }\n to { transform: scaleX(0); }\n}\n/* A barra só começa a correr no 2º frame: no 1º ela já apareceria zerada\n (o elemento acabou de ser inserido e a animação aplica o estado final). */\n.card.entering .progress { animation: none; }\n/* Ao dispensar, congela a barra: ela some junto com o card em .2s. */\n.card.leaving .progress { animation-play-state: paused; }\n@media (prefers-reduced-motion: reduce) {\n .progress.run { animation-duration: 0.01ms; }\n}\n`;\n\nexport class FxToast extends HTMLElement {\n static get observedAttributes() {\n return ['kind', 'title', 'message', 'duration', 'icon', 'mode', 'progress', 'progress-origin'];\n }\n\n /**\n * Exibe a barra de contagem regressiva na base do card.\n *\n * A barra esvazia da esquerda para a direita durante exatamente `duration`\n * e usa a cor do `kind`. Com `duration=\"0\"` (toast fixo) a barra é\n * omitida — não há tempo restante a representar.\n *\n * - `'always'` (ou atributo vazio): barra sempre correndo.\n * - `'hover'`: a barra só corre enquanto o cursor estiver sobre o toast;\n * ao sair, a animação pausa, então dá tempo de ler a mensagem.\n * - `'paused'`: barra visível, porém parada.\n */\n get progress(): 'always' | 'hover' | 'paused' | undefined {\n if (!this.hasAttribute('progress')) return undefined;\n const v = this.getAttribute('progress');\n return v === 'hover' || v === 'paused' ? v : 'always';\n }\n set progress(value: 'always' | 'hover' | 'paused' | undefined) {\n if (value) this.setAttribute('progress', value);\n else this.removeAttribute('progress');\n }\n\n /**\n * Lado a partir do qual a barra esvazia.\n *\n * - `'left'` (padrão): a barra fica ancorada à esquerda e drena da direita\n * para a esquerda.\n * - `'right'`: a barra fica ancorada à direita e drena da esquerda para a\n * direita (espelhado).\n *\n * Só tem efeito junto com `progress`; qualquer valor fora da lista cai em\n * `'left'`.\n */\n get progressOrigin(): 'left' | 'right' {\n return this.getAttribute('progress-origin') === 'right' ? 'right' : 'left';\n }\n set progressOrigin(value: 'left' | 'right') {\n this.setAttribute('progress-origin', value);\n }\n\n /** Duração efetiva em ms (0 = fixo). Mesma regra usada no timer. */\n private get safeDuration(): number {\n const dur = Number(this.getAttribute('duration') ?? '4000');\n if (dur === 0) return 0;\n return Math.max(Number.isNaN(dur) ? 4000 : dur, 1000);\n }\n\n /** Esquema de cores forçado do card: 'light' | 'dark' | undefined (segue o tema). */\n get mode(): 'light' | 'dark' | undefined {\n const m = this.getAttribute('mode');\n return m === 'light' || m === 'dark' ? m : undefined;\n }\n set mode(value: 'light' | 'dark' | undefined) {\n if (value) this.setAttribute('mode', value);\n else this.removeAttribute('mode');\n }\n\n private timer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n if (!this.shadowRoot!.firstChild) this.#render();\n // Durações mínimas: valores muito baixos (ex.: digitação parcial no\n // playground) são elevados para 1s; 0 permanece fixo até fechar.\n const safe = this.safeDuration;\n if (safe > 0 && !this.timer) {\n this.timer = setTimeout(() => this.dismiss(), safe);\n }\n this.#syncProgress();\n }\n\n disconnectedCallback() {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n }\n\n attributeChangedCallback(name: string) {\n // A duração é lida em dois lugares (timer + barra): se mudar com o toast\n // na tela, ambos precisam se realinhar.\n if (name === 'duration' || name === 'progress' || name === 'progress-origin' || name === 'kind') {\n this.#syncProgress();\n }\n }\n\n /**\n * Aplica (ou remove) a barra de contagem regressiva.\n *\n * A barra só existe com `progress` e `duration > 0`. O tempo da animação\n * vem de `--toast-duration`, sempre igual ao do `setTimeout` que fecha o\n * toast — assim a barra e o desaparecimento nunca desalinham.\n */\n #syncProgress(): void {\n const card = this.shadowRoot?.querySelector<HTMLElement>('.card');\n if (!card) return;\n const bar = this.shadowRoot!.querySelector<HTMLElement>('.progress');\n const mode = this.progress;\n const dur = this.safeDuration;\n const shouldShow = !!mode && dur > 0;\n\n if (!shouldShow) {\n bar?.remove();\n card.classList.remove('entering');\n return;\n }\n if (!bar) {\n this.shadowRoot!.querySelector('.card')!.insertAdjacentHTML('beforeend', '<span class=\"progress\" part=\"progress\" aria-hidden=\"true\"></span>');\n }\n const el = this.shadowRoot!.querySelector<HTMLElement>('.progress')!;\n el.style.setProperty('--toast-duration', `${dur}ms`);\n // `left` (padrão) => origem à esquerda; `right` espelha a drenagem.\n el.style.setProperty('--toast-progress-origin', `${this.progressOrigin} center`);\n el.classList.remove('run');\n // `paused` mantém a barra cheia e imóvel; `always` e `hover` deixam a\n // animação correr (no `hover`, o CSS só a dispara sob :hover do host).\n if (mode !== 'paused') el.classList.add('run');\n // 1 frame sem animação para o browser registrar o estado inicial cheio\n // antes de a barra começar a esvaziar.\n card.classList.add('entering');\n requestAnimationFrame(() => requestAnimationFrame(() => card.classList.remove('entering')));\n }\n\n dismiss() {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n const card = this.shadowRoot!.querySelector('.card');\n if (card) {\n card.classList.add('leaving');\n setTimeout(() => this.remove(), 200);\n } else {\n this.remove();\n }\n }\n\n #render() {\n const kind = (this.getAttribute('kind') || 'info') as ToastKind;\n const title = this.getAttribute('title') ?? '';\n const msg = this.getAttribute('message') ?? '';\n const color = KIND_COLOR[kind] ?? KIND_COLOR.info;\n const iconName = this.getAttribute('icon');\n const isFxIcon = !!iconName && /^[a-z][a-z0-9_]*$/.test(iconName);\n if (isFxIcon) void import('../../icons');\n const iconContent = isFxIcon ? iconName : KIND_ICON[kind] ?? KIND_ICON.info;\n this.shadowRoot!.innerHTML = `\n <style>${CARD_CSS}</style>\n <div class=\"card\" style=\"--kind:${color}\" role=\"status\" part=\"card\">\n <span class=\"icon${isFxIcon ? ' fx-icon' : ''}\">${iconContent}</span>\n <div class=\"body\">\n ${title ? '<div class=\"title\"></div>' : ''}\n ${msg ? '<div class=\"msg\"></div>' : ''}\n </div>\n <button class=\"close\" aria-label=\"Fechar\">✕</button>\n </div>`;\n const t = this.shadowRoot!.querySelector<HTMLElement>('.title');\n const m = this.shadowRoot!.querySelector<HTMLElement>('.msg');\n if (t) t.textContent = title;\n if (m) m.textContent = msg;\n this.shadowRoot!.querySelector('.close')!.addEventListener('click', () => this.dismiss());\n }\n}\n\n/* ---------- Regiões por posição na tela ---------- */\nfunction regionFor(position: ToastPosition): HTMLElement {\n let region = document.querySelector<HTMLElement>(`[data-fx-toast-region=\"${position}\"]`);\n if (!region) {\n region = document.createElement('div');\n region.setAttribute('data-fx-toast-region', position);\n const [vertical, horizontal] = position.split('-') as [string, string];\n const style = region.style as CSSStyleDeclaration & Record<string, string>;\n style.position = 'fixed';\n style[vertical] = '16px';\n if (horizontal === 'center') {\n style.left = '50%';\n style.transform = 'translateX(-50%)';\n } else {\n style[horizontal] = '16px';\n }\n style.display = 'flex';\n style.flexDirection = 'column';\n style.gap = '8px';\n style.zIndex = '1100';\n style.pointerEvents = 'none';\n // Os cards reabilitam pointer-events via `:host { pointer-events: auto }` (herança),\n // sem necessidade de MutationObserver.\n document.body.appendChild(region);\n }\n return region;\n}\n\nexport interface ToastOptions {\n /** Texto secundário (também aceito como 2º argumento dos métodos). */\n message?: string;\n position?: ToastPosition;\n /** Tempo em ms até sumir. 0 = fixo até fechar. Padrão: 4000 */\n duration?: number;\n /**\n * Barra de contagem regressiva na base do card, na cor do `kind`.\n * `'always'` (padrão) corre sempre; `'hover'` só enquanto o cursor estiver\n * sobre o toast; `'paused'` exibe a barra cheia, porém imóvel.\n * Sem efeito com `duration: 0` (não há tempo restante a representar).\n */\n progress?: 'always' | 'hover' | 'paused';\n /**\n * Lado a partir do qual a barra esvazia. `'left'` (padrão) deixa a barra\n * ancorada à esquerda; `'right'` a ancora à direita (espelhado).\n * Só tem efeito junto com `progress`.\n */\n progressOrigin?: 'left' | 'right';\n /** Força o esquema de cores do card, independentemente do tema global. */\n mode?: 'light' | 'dark';\n}\n\nclass ToastApi {\n private seq = 0;\n private map = new Map<number, FxToast>();\n\n push(kind: ToastKind, title: string, messageOrOpts?: string | ToastOptions, opts?: ToastOptions): number {\n const id = ++this.seq;\n // Assinaturas suportadas:\n // push(kind, title, options)\n // push(kind, title, message, options) <- usada pela doc e mais intuitiva\n const message = typeof messageOrOpts === 'string' ? messageOrOpts : messageOrOpts?.message;\n const o: ToastOptions = { ...(typeof messageOrOpts === 'object' && messageOrOpts ? messageOrOpts : undefined), ...opts };\n const el = document.createElement('fx-toast') as FxToast;\n el.setAttribute('kind', kind);\n el.setAttribute('title', title);\n if (message) el.setAttribute('message', message);\n el.setAttribute('position', o.position ?? 'top-right');\n el.setAttribute('duration', String(o.duration ?? 4000));\n if (o.mode) el.setAttribute('mode', o.mode);\n if (o.progress) el.setAttribute('progress', o.progress);\n if (o.progressOrigin) el.setAttribute('progress-origin', o.progressOrigin);\n regionFor(o.position ?? 'top-right').appendChild(el);\n this.map.set(id, el);\n return id;\n }\n\n close(id: number) {\n this.map.get(id)?.dismiss();\n this.map.delete(id);\n }\n\n success = (t: string, m?: string, o?: ToastOptions) => this.push('success', t, m, o);\n error = (t: string, m?: string, o?: ToastOptions) => this.push('error', t, m, o);\n info = (t: string, m?: string, o?: ToastOptions) => this.push('info', t, m, o);\n warning = (t: string, m?: string, o?: ToastOptions) => this.push('warning', t, m, o);\n}\n\nexport const FenixToast = new ToastApi();\n// Exposto globalmente para uso em HTML puro (onclick=\"FenixToast.success(...)\")\n(globalThis as unknown as Record<string, unknown>).FenixToast = FenixToast;\n\nexport function defineFxToast() {\n if (!customElements.get('fx-toast')) customElements.define('fx-toast', FxToast);\n}\n\n"],"file":"components/toast/toast.js"}
1
+ {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../src/components/toast/toast.ts"],"sourcesContent":["/**\n * <fx-toast> — notificações imperativas com posição e duração configuráveis.\n *\n * Atributos: kind (success|error|info|warning), title, message,\n * position (top-left|top-center|top-right|bottom-left|bottom-center|bottom-right),\n * duration (ms; 0 = fixo até fechar), mode ('light'|'dark' — força o esquema de\n * cores do card independentemente do tema global), progress (booleano ou\n * 'always'|'hover'|'paused' — barra de contagem regressiva na cor do kind),\n * progress-origin (left|right — lado pelo qual a barra esvazia; padrão left).\n */\nconst POSITIONS = [\n 'top-left', 'top-center', 'top-right',\n 'bottom-left', 'bottom-center', 'bottom-right',\n] as const;\nexport type ToastPosition = (typeof POSITIONS)[number];\ntype ToastKind = 'success' | 'error' | 'info' | 'warning';\n\n/**\n * Evento interno disparado quando o <fx-toast> sai do DOM.\n * A API `FenixToast` o escuta para remover a entrada do mapa interno —\n * é o que impede o acúmulo de toasts já dispensados.\n */\nconst REMOVED_EVENT = 'fx-toast:removed';\n\nconst KIND_COLOR: Record<ToastKind, string> = {\n success: 'var(--fx-color-success, #10b981)',\n error: 'var(--fx-color-danger, #f43f5e)',\n info: 'var(--fx-color-info, #0ea5e9)',\n warning: 'var(--fx-color-warning, #f59e0b)',\n};\n\n/** Ícone exibido no lugar da bolinha, conforme a variante. */\nconst KIND_ICON: Record<ToastKind, string> = {\n success: '✓',\n error: '✕',\n warning: '!',\n info: 'i',\n};\n\nconst CARD_CSS = `\n:host { display: contents; pointer-events: auto; }\n/* Modo explícito: sobrepõe os tokens herdados do tema global SOMENTE neste card. */\n:host([mode='dark']) {\n --fx-surface-background: #1e293b;\n --fx-text-default: #f1f5f9;\n --fx-text-muted: #94a3b8;\n --fx-border-default: #334155;\n --fx-shadow-lg: 0 10px 30px rgba(0, 0, 0, .5);\n --fx-surface-surface-hover: rgba(255, 255, 255, .08);\n}\n:host([mode='light']) {\n --fx-surface-background: #ffffff;\n --fx-text-default: #1e293b;\n --fx-text-muted: #64748b;\n --fx-border-default: #e2e8f0;\n --fx-shadow-lg: 0 10px 30px rgba(0, 0, 0, .14);\n --fx-surface-surface-hover: rgba(0, 0, 0, .05);\n}\n.fx-icon {\n font-family: var(--fx-icon-font, 'Fenix Icons');\n font-weight: normal;\n font-style: normal;\n line-height: 1;\n letter-spacing: normal;\n display: inline-block;\n white-space: nowrap;\n direction: ltr;\n font-variation-settings: 'FILL' 0, 'wght' 500, 'GRAD' 0, 'opsz' 24;\n -webkit-font-smoothing: antialiased;\n user-select: none;\n}\n.card {\n display: flex; align-items: flex-start; gap: 10px;\n min-width: 260px; max-width: 360px;\n background: var(--fx-surface-background, #fff);\n color: var(--fx-text-default, #1e293b);\n border: 1px solid var(--fx-border-default, #e2e8f0);\n border-left: 4px solid var(--kind);\n border-radius: var(--fx-radius-md, 8px);\n box-shadow: var(--fx-shadow-lg, 0 10px 30px rgba(0,0,0,.14));\n padding: var(--fx-space-sm, 8px) var(--fx-space-md, 12px);\n font-family: inherit;\n animation: fx-toast-in .25s ease;\n}\n@keyframes fx-toast-in { from { opacity: 0; transform: translateY(-6px); } }\n.card.leaving { opacity: 0; transition: opacity .2s ease; }\n.icon {\n width: 22px; height: 22px; border-radius: 50%;\n background: var(--kind);\n color: #fff;\n display: flex; align-items: center; justify-content: center;\n font-size: 12px; font-weight: 700;\n flex: none; margin-top: 2px;\n user-select: none;\n}\n.body { flex: 1; min-width: 0; }\n.title { font-weight: 600; font-size: 14px; }\n.msg { font-size: calc(var(--fx-font-size, 14px) - 2px); color: var(--fx-text-muted, #64748b); margin-top: 2px; word-break: break-word; white-space: pre-line; }\n.close {\n all: unset; cursor: pointer; flex: none; line-height: 1;\n color: var(--fx-text-muted, #64748b); font-size: 15px; padding: 2px 4px; border-radius: 4px;\n}\n.close:hover { color: var(--fx-color-danger, #f43f5e); background: var(--fx-surface-surface-hover, rgba(0,0,0,.05)); }\n\n/* --- Barra de contagem regressiva (atributo progress) ---\n Fica na base do card e esvazia durante a duração, na cor do kind (--kind) —\n ou seja, acompanha success/error/warning/info sem configuração extra.\n\n O lado é configurável pelo atributo progress-origin (padrão: left):\n left → a barra drena da direita para a esquerda (ancorada à esquerda)\n right → a barra drena da esquerda para a direita (ancorada à direita)\n A origem entra como custom property, então os keyframes são compartilhados.\n\n Usa escalaX em vez de width: a animação escreve só na compositor, sem\n reflow por frame. */\n.card { position: relative; overflow: hidden; }\n.progress {\n position: absolute;\n left: 0;\n bottom: 0;\n width: 100%;\n height: 3px;\n background: var(--kind);\n transform-origin: var(--toast-progress-origin, left center);\n transform: scaleX(1);\n border-radius: 0 0 var(--fx-radius-md, 8px) var(--fx-radius-md, 8px);\n pointer-events: none;\n will-change: transform;\n}\n.progress.run { animation: fx-toast-drain var(--toast-duration, 4000ms) linear forwards; }\n/* No modo hover a animação fica parada até o cursor/foco entrar no card. */\n.card:hover .progress.run,\n.card:focus-within .progress.run { animation-play-state: running; }\n@keyframes fx-toast-drain {\n from { transform: scaleX(1); }\n to { transform: scaleX(0); }\n}\n/* A barra só começa a correr no 2º frame: no 1º ela já apareceria zerada\n (o elemento acabou de ser inserido e a animação aplica o estado final). */\n.card.entering .progress { animation: none; }\n/* Ao dispensar, congela a barra: ela some junto com o card em .2s. */\n.card.leaving .progress { animation-play-state: paused; }\n@media (prefers-reduced-motion: reduce) {\n .progress.run { animation-duration: 0.01ms; }\n}\n`;\n\nexport class FxToast extends HTMLElement {\n static get observedAttributes() {\n return ['kind', 'title', 'message', 'duration', 'icon', 'mode', 'progress', 'progress-origin'];\n }\n\n /**\n * Exibe a barra de contagem regressiva na base do card.\n *\n * A barra esvazia da esquerda para a direita durante exatamente `duration`\n * e usa a cor do `kind`. Com `duration=\"0\"` (toast fixo) a barra é\n * omitida — não há tempo restante a representar.\n *\n * - `'always'` (ou atributo vazio): barra sempre correndo.\n * - `'hover'`: a barra só corre enquanto o cursor estiver sobre o toast;\n * ao sair, a animação pausa, então dá tempo de ler a mensagem.\n * - `'paused'`: barra visível, porém parada.\n */\n get progress(): 'always' | 'hover' | 'paused' | undefined {\n if (!this.hasAttribute('progress')) return undefined;\n const v = this.getAttribute('progress');\n return v === 'hover' || v === 'paused' ? v : 'always';\n }\n set progress(value: 'always' | 'hover' | 'paused' | undefined) {\n if (value) this.setAttribute('progress', value);\n else this.removeAttribute('progress');\n }\n\n /**\n * Lado a partir do qual a barra esvazia.\n *\n * - `'left'` (padrão): a barra fica ancorada à esquerda e drena da direita\n * para a esquerda.\n * - `'right'`: a barra fica ancorada à direita e drena da esquerda para a\n * direita (espelhado).\n *\n * Só tem efeito junto com `progress`; qualquer valor fora da lista cai em\n * `'left'`.\n */\n get progressOrigin(): 'left' | 'right' {\n return this.getAttribute('progress-origin') === 'right' ? 'right' : 'left';\n }\n set progressOrigin(value: 'left' | 'right') {\n this.setAttribute('progress-origin', value);\n }\n\n /** Duração efetiva em ms (0 = fixo). Mesma regra usada no timer. */\n private get safeDuration(): number {\n const dur = Number(this.getAttribute('duration') ?? '4000');\n if (dur === 0) return 0;\n return Math.max(Number.isNaN(dur) ? 4000 : dur, 1000);\n }\n\n /** Esquema de cores forçado do card: 'light' | 'dark' | undefined (segue o tema). */\n get mode(): 'light' | 'dark' | undefined {\n const m = this.getAttribute('mode');\n return m === 'light' || m === 'dark' ? m : undefined;\n }\n set mode(value: 'light' | 'dark' | undefined) {\n if (value) this.setAttribute('mode', value);\n else this.removeAttribute('mode');\n }\n\n private timer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n if (!this.shadowRoot!.firstChild) this.#render();\n // Durações mínimas: valores muito baixos (ex.: digitação parcial no\n // playground) são elevados para 1s; 0 permanece fixo até fechar.\n const safe = this.safeDuration;\n if (safe > 0 && !this.timer) {\n this.timer = setTimeout(() => this.dismiss(), safe);\n }\n this.#syncProgress();\n }\n\n /**\n * Limpa recursos ao sair do DOM e avisa a API imperativa (`FenixToast`).\n *\n * O aviso é essencial: `dismiss()` (timer ou botão de fechar) tira o\n * elemento do DOM mas, sem isto, `ToastApi.map` continuaria segurando uma\n * referência forte para cada toast já dispensado — crescimento de memória\n * sem limite em uso prolongado.\n */\n disconnectedCallback() {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n this.dispatchEvent(new CustomEvent(REMOVED_EVENT, { bubbles: false, composed: false }));\n }\n\n attributeChangedCallback(name: string) {\n // A duração é lida em dois lugares (timer + barra): se mudar com o toast\n // na tela, ambos precisam se realinhar.\n if (name === 'duration' || name === 'progress' || name === 'progress-origin' || name === 'kind') {\n this.#syncProgress();\n }\n }\n\n /**\n * Aplica (ou remove) a barra de contagem regressiva.\n *\n * A barra só existe com `progress` e `duration > 0`. O tempo da animação\n * vem de `--toast-duration`, sempre igual ao do `setTimeout` que fecha o\n * toast — assim a barra e o desaparecimento nunca desalinham.\n */\n #syncProgress(): void {\n const card = this.shadowRoot?.querySelector<HTMLElement>('.card');\n if (!card) return;\n const bar = this.shadowRoot!.querySelector<HTMLElement>('.progress');\n const mode = this.progress;\n const dur = this.safeDuration;\n const shouldShow = !!mode && dur > 0;\n\n if (!shouldShow) {\n bar?.remove();\n card.classList.remove('entering');\n return;\n }\n if (!bar) {\n this.shadowRoot!.querySelector('.card')!.insertAdjacentHTML('beforeend', '<span class=\"progress\" part=\"progress\" aria-hidden=\"true\"></span>');\n }\n const el = this.shadowRoot!.querySelector<HTMLElement>('.progress')!;\n el.style.setProperty('--toast-duration', `${dur}ms`);\n // `left` (padrão) => origem à esquerda; `right` espelha a drenagem.\n el.style.setProperty('--toast-progress-origin', `${this.progressOrigin} center`);\n el.classList.remove('run');\n // `paused` mantém a barra cheia e imóvel; `always` e `hover` deixam a\n // animação correr (no `hover`, o CSS só a dispara sob :hover do host).\n if (mode !== 'paused') el.classList.add('run');\n // 1 frame sem animação para o browser registrar o estado inicial cheio\n // antes de a barra começar a esvaziar.\n card.classList.add('entering');\n requestAnimationFrame(() => requestAnimationFrame(() => card.classList.remove('entering')));\n }\n\n dismiss() {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n const card = this.shadowRoot!.querySelector('.card');\n if (card) {\n card.classList.add('leaving');\n setTimeout(() => this.remove(), 200);\n } else {\n this.remove();\n }\n }\n\n #render() {\n const kind = (this.getAttribute('kind') || 'info') as ToastKind;\n const title = this.getAttribute('title') ?? '';\n const msg = this.getAttribute('message') ?? '';\n const color = KIND_COLOR[kind] ?? KIND_COLOR.info;\n const iconName = this.getAttribute('icon');\n const isFxIcon = !!iconName && /^[a-z][a-z0-9_]*$/.test(iconName);\n if (isFxIcon) void import('../../icons');\n const iconContent = isFxIcon ? iconName : KIND_ICON[kind] ?? KIND_ICON.info;\n this.shadowRoot!.innerHTML = `\n <style>${CARD_CSS}</style>\n <div class=\"card\" style=\"--kind:${color}\" role=\"status\" part=\"card\">\n <span class=\"icon${isFxIcon ? ' fx-icon' : ''}\">${iconContent}</span>\n <div class=\"body\">\n ${title ? '<div class=\"title\"></div>' : ''}\n ${msg ? '<div class=\"msg\"></div>' : ''}\n </div>\n <button class=\"close\" aria-label=\"Fechar\">✕</button>\n </div>`;\n const t = this.shadowRoot!.querySelector<HTMLElement>('.title');\n const m = this.shadowRoot!.querySelector<HTMLElement>('.msg');\n if (t) t.textContent = title;\n if (m) m.textContent = msg;\n this.shadowRoot!.querySelector('.close')!.addEventListener('click', () => this.dismiss());\n }\n}\n\n/* ---------- Regiões por posição na tela ---------- */\nfunction regionFor(position: ToastPosition): HTMLElement {\n let region = document.querySelector<HTMLElement>(`[data-fx-toast-region=\"${position}\"]`);\n if (!region) {\n region = document.createElement('div');\n region.setAttribute('data-fx-toast-region', position);\n const [vertical, horizontal] = position.split('-') as [string, string];\n const style = region.style as CSSStyleDeclaration & Record<string, string>;\n style.position = 'fixed';\n style[vertical] = '16px';\n if (horizontal === 'center') {\n style.left = '50%';\n style.transform = 'translateX(-50%)';\n } else {\n style[horizontal] = '16px';\n }\n style.display = 'flex';\n style.flexDirection = 'column';\n style.gap = '8px';\n style.zIndex = '1100';\n style.pointerEvents = 'none';\n // Os cards reabilitam pointer-events via `:host { pointer-events: auto }` (herança),\n // sem necessidade de MutationObserver.\n document.body.appendChild(region);\n }\n return region;\n}\n\nexport interface ToastOptions {\n /** Texto secundário (também aceito como 2º argumento dos métodos). */\n message?: string;\n position?: ToastPosition;\n /** Tempo em ms até sumir. 0 = fixo até fechar. Padrão: 4000 */\n duration?: number;\n /**\n * Barra de contagem regressiva na base do card, na cor do `kind`.\n * `'always'` (padrão) corre sempre; `'hover'` só enquanto o cursor estiver\n * sobre o toast; `'paused'` exibe a barra cheia, porém imóvel.\n * Sem efeito com `duration: 0` (não há tempo restante a representar).\n */\n progress?: 'always' | 'hover' | 'paused';\n /**\n * Lado a partir do qual a barra esvazia. `'left'` (padrão) deixa a barra\n * ancorada à esquerda; `'right'` a ancora à direita (espelhado).\n * Só tem efeito junto com `progress`.\n */\n progressOrigin?: 'left' | 'right';\n /** Força o esquema de cores do card, independentemente do tema global. */\n mode?: 'light' | 'dark';\n}\n\nclass ToastApi {\n private seq = 0;\n private map = new Map<number, FxToast>();\n\n push(kind: ToastKind, title: string, messageOrOpts?: string | ToastOptions, opts?: ToastOptions): number {\n const id = ++this.seq;\n // Assinaturas suportadas:\n // push(kind, title, options)\n // push(kind, title, message, options) <- usada pela doc e mais intuitiva\n const message = typeof messageOrOpts === 'string' ? messageOrOpts : messageOrOpts?.message;\n const o: ToastOptions = { ...(typeof messageOrOpts === 'object' && messageOrOpts ? messageOrOpts : undefined), ...opts };\n const el = document.createElement('fx-toast') as FxToast;\n el.setAttribute('kind', kind);\n el.setAttribute('title', title);\n if (message) el.setAttribute('message', message);\n el.setAttribute('position', o.position ?? 'top-right');\n el.setAttribute('duration', String(o.duration ?? 4000));\n if (o.mode) el.setAttribute('mode', o.mode);\n if (o.progress) el.setAttribute('progress', o.progress);\n if (o.progressOrigin) el.setAttribute('progress-origin', o.progressOrigin);\n regionFor(o.position ?? 'top-right').appendChild(el);\n // O toast avisa quando sai do DOM (timer esgotado, botão de fechar ou\n // remoção externa). Sem este sync o mapa retém cada toast da sessão.\n el.addEventListener(REMOVED_EVENT, () => {\n if (this.map.get(id) === el) this.map.delete(id);\n });\n this.map.set(id, el);\n return id;\n }\n\n close(id: number) {\n this.map.get(id)?.dismiss();\n this.map.delete(id);\n }\n\n success = (t: string, m?: string, o?: ToastOptions) => this.push('success', t, m, o);\n error = (t: string, m?: string, o?: ToastOptions) => this.push('error', t, m, o);\n info = (t: string, m?: string, o?: ToastOptions) => this.push('info', t, m, o);\n warning = (t: string, m?: string, o?: ToastOptions) => this.push('warning', t, m, o);\n}\n\nexport const FenixToast = new ToastApi();\n// Exposto globalmente para uso em HTML puro (onclick=\"FenixToast.success(...)\")\n(globalThis as unknown as Record<string, unknown>).FenixToast = FenixToast;\n\nexport function defineFxToast() {\n if (!customElements.get('fx-toast')) customElements.define('fx-toast', FxToast);\n}\n\n"],"file":"components/toast/toast.js"}
@@ -24,6 +24,14 @@ export interface FxTooltipConfig {
24
24
  * defineFxTooltipDirective();
25
25
  */
26
26
  export declare function defineFxTooltipDirective(): void;
27
+ /**
28
+ * Quantos tooltips a diretiva mantém vivos agora.
29
+ *
30
+ * Deve igualar o nº de elementos `[fx-tooltip]` no documento. Se crescer após
31
+ * trocar de tela, há comportamento retido (vazamento). Pensado para diagnóstico
32
+ * em produção e para os testes de regressão.
33
+ */
34
+ export declare function fxTooltipActiveCount(): number;
27
35
  /**
28
36
  * Destrói a diretiva fx-tooltip e limpa todos os recursos.
29
37
  */
@@ -1 +1 @@
1
- {"version":3,"file":"directive.d.ts","sourceRoot":"","sources":["../../../src/components/tooltip/directive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC9C,IAAI,EAAE,OAAO,CAAC;CACf;AAkTD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAS/C;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
1
+ {"version":3,"file":"directive.d.ts","sourceRoot":"","sources":["../../../src/components/tooltip/directive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC9C,IAAI,EAAE,OAAO,CAAC;CACf;AAkXD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAS/C;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
@@ -90,6 +90,8 @@ const _FxTooltipManager = class _FxTooltipManager {
90
90
  this.observer = null;
91
91
  this.behaviors = /* @__PURE__ */ new Map();
92
92
  this.injectedStyles = null;
93
+ this._pending = { added: [] };
94
+ this._flushScheduled = false;
93
95
  this.injectStyles();
94
96
  this.observe();
95
97
  this.processExistingElements();
@@ -100,6 +102,20 @@ const _FxTooltipManager = class _FxTooltipManager {
100
102
  }
101
103
  return _FxTooltipManager.instance;
102
104
  }
105
+ /** Quantos tooltips estão ativos agora (0 se a diretiva nunca foi iniciada). */
106
+ static peekActiveCount() {
107
+ return _FxTooltipManager.instance?.behaviors.size ?? 0;
108
+ }
109
+ /**
110
+ * Quantos tooltips estão ativamente gerenciados.
111
+ *
112
+ * Deve acompanhar o número de elementos `[fx-tooltip]` no documento: um
113
+ * valor crescente depois de trocar de tela significa comportamento retido
114
+ * (vazamento). Usado nos testes de regressão e no diagnóstico em produção.
115
+ */
116
+ get activeCount() {
117
+ return this.behaviors.size;
118
+ }
103
119
  injectStyles() {
104
120
  if (document.getElementById("fx-tooltip-directive-styles")) return;
105
121
  this.injectedStyles = document.createElement("style");
@@ -109,32 +125,55 @@ const _FxTooltipManager = class _FxTooltipManager {
109
125
  }
110
126
  observe() {
111
127
  this.observer = new MutationObserver((mutations) => {
112
- for (const mutation of mutations) {
113
- if (mutation.type === "childList") {
114
- mutation.addedNodes.forEach((node) => {
115
- if (node instanceof HTMLElement) {
116
- this.processElement(node);
117
- node.querySelectorAll("[fx-tooltip]").forEach((el) => this.processElement(el));
118
- }
119
- });
120
- }
121
- if (mutation.type === "attributes" && mutation.target instanceof HTMLElement) {
122
- const target = mutation.target;
123
- if (target.hasAttribute("fx-tooltip")) {
124
- this.processElement(target);
125
- } else if (this.behaviors.has(target)) {
126
- this.removeBehavior(target);
127
- }
128
- }
129
- }
128
+ this._pending.added.push(...mutations);
129
+ if (this._flushScheduled) return;
130
+ this._flushScheduled = true;
131
+ const run = () => {
132
+ this._flushScheduled = false;
133
+ const batch = this._pending;
134
+ this._pending = { added: [] };
135
+ this._flush(batch.added);
136
+ };
137
+ if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
138
+ else setTimeout(run, 0);
130
139
  });
131
140
  this.observer.observe(document.body, {
132
141
  childList: true,
133
142
  subtree: true,
134
143
  attributes: true,
144
+ // `attributeFilter` é o que mantém o custo baixo: o browser só notifica
145
+ // mudanças nestes 3 atributos, e não em qualquer atributo da página.
135
146
  attributeFilter: ["fx-tooltip", "fx-tooltip-position", "fx-tooltip-html"]
136
147
  });
137
148
  }
149
+ /** Um passe de verificação sobre todos os registros acumulados. */
150
+ _flush(records) {
151
+ const toProcess = /* @__PURE__ */ new Set();
152
+ const toRemove = /* @__PURE__ */ new Set();
153
+ for (const mutation of records) {
154
+ if (mutation.type === "childList") {
155
+ mutation.addedNodes.forEach((node) => {
156
+ if (node instanceof HTMLElement) {
157
+ toProcess.add(node);
158
+ node.querySelectorAll("[fx-tooltip]").forEach((el) => toProcess.add(el));
159
+ }
160
+ });
161
+ mutation.removedNodes.forEach((node) => {
162
+ if (node instanceof HTMLElement) {
163
+ toRemove.add(node);
164
+ node.querySelectorAll("[fx-tooltip]").forEach((el) => toRemove.add(el));
165
+ }
166
+ });
167
+ }
168
+ if (mutation.type === "attributes" && mutation.target instanceof HTMLElement) {
169
+ const target = mutation.target;
170
+ if (target.hasAttribute("fx-tooltip")) toProcess.add(target);
171
+ else toRemove.add(target);
172
+ }
173
+ }
174
+ toRemove.forEach((el) => this.removeBehavior(el));
175
+ toProcess.forEach((el) => this.processElement(el));
176
+ }
138
177
  processExistingElements() {
139
178
  document.querySelectorAll("[fx-tooltip]").forEach((el) => this.processElement(el));
140
179
  }
@@ -157,6 +196,8 @@ const _FxTooltipManager = class _FxTooltipManager {
157
196
  }
158
197
  }
159
198
  destroy() {
199
+ this._pending.added = [];
200
+ this._flushScheduled = false;
160
201
  if (this.observer) {
161
202
  this.observer.disconnect();
162
203
  this.observer = null;
@@ -1 +1 @@
1
- {"version":3,"file":"directive.js","sources":["../../../src/components/tooltip/directive.ts"],"sourcesContent":["/**\n * Diretiva fx-tooltip — Aplica tooltip em qualquer elemento HTML.\n *\n * Uso:\n * <div fx-tooltip=\"Texto simples\">Div com tooltip</div>\n * <button fx-tooltip=\"Clique\" fx-tooltip-position=\"bottom\">Botão</button>\n * <span fx-tooltip=\"<strong>HTML</strong>\" fx-tooltip-html>Texto</span>\n *\n * Atributos:\n * fx-tooltip — Conteúdo do tooltip (texto ou HTML)\n * fx-tooltip-position — Posição: top | bottom | left | right (padrão: top)\n * fx-tooltip-html — Se presente, trata o conteúdo como HTML\n */\n\nexport interface FxTooltipConfig {\n position: 'top' | 'bottom' | 'left' | 'right';\n html: boolean;\n}\n\nconst DEFAULT_CONFIG: FxTooltipConfig = {\n position: 'top',\n html: false,\n};\n\n/**\n * Classe que gerencia o comportamento de tooltip em um elemento.\n */\nclass TooltipBehavior {\n private element: HTMLElement;\n private tooltipElement: HTMLSpanElement | null = null;\n private config: FxTooltipConfig;\n private showTimeout: number | null = null;\n private hideTimeout: number | null = null;\n\n constructor(element: HTMLElement) {\n this.element = element;\n this.config = this.parseConfig();\n this.init();\n }\n\n private parseConfig(): FxTooltipConfig {\n const position = this.element.getAttribute('fx-tooltip-position') as FxTooltipConfig['position'];\n return {\n position: position && ['top', 'bottom', 'left', 'right'].includes(position) ? position : DEFAULT_CONFIG.position,\n html: this.element.hasAttribute('fx-tooltip-html'),\n };\n }\n\n private init(): void {\n const computedStyle = window.getComputedStyle(this.element);\n if (computedStyle.position === 'static') {\n this.element.style.position = 'relative';\n }\n this.createTooltipElement();\n this.element.addEventListener('mouseenter', this.show);\n this.element.addEventListener('mouseleave', this.hide);\n this.element.addEventListener('focus', this.show);\n this.element.addEventListener('blur', this.hide);\n }\n\n private createTooltipElement(): void {\n const content = this.element.getAttribute('fx-tooltip');\n if (!content) return;\n this.tooltipElement = document.createElement('span');\n this.tooltipElement.setAttribute('role', 'tooltip');\n this.tooltipElement.setAttribute('data-fx-tooltip', '');\n this.tooltipElement.className = `fx-tooltip-bubble fx-tooltip-${this.config.position}`;\n if (this.config.html) {\n this.tooltipElement.innerHTML = content;\n } else {\n this.tooltipElement.textContent = content;\n }\n this.element.appendChild(this.tooltipElement);\n }\n\n private show = (): void => {\n if (this.hideTimeout) {\n clearTimeout(this.hideTimeout);\n this.hideTimeout = null;\n }\n this.showTimeout = window.setTimeout(() => {\n if (this.tooltipElement) {\n this.tooltipElement.classList.add('fx-tooltip-visible');\n }\n }, 100);\n };\n\n private hide = (): void => {\n if (this.showTimeout) {\n clearTimeout(this.showTimeout);\n this.showTimeout = null;\n }\n this.hideTimeout = window.setTimeout(() => {\n if (this.tooltipElement) {\n this.tooltipElement.classList.remove('fx-tooltip-visible');\n }\n }, 50);\n };\n\n public update(): void {\n this.config = this.parseConfig();\n if (this.tooltipElement) {\n this.tooltipElement.remove();\n this.tooltipElement = null;\n }\n this.createTooltipElement();\n }\n\n public destroy(): void {\n this.element.removeEventListener('mouseenter', this.show);\n this.element.removeEventListener('mouseleave', this.hide);\n this.element.removeEventListener('focus', this.show);\n this.element.removeEventListener('blur', this.hide);\n if (this.showTimeout) clearTimeout(this.showTimeout);\n if (this.hideTimeout) clearTimeout(this.hideTimeout);\n if (this.tooltipElement) {\n this.tooltipElement.remove();\n this.tooltipElement = null;\n }\n }\n}\n\n/**\n * Gerencia todas as instâncias de tooltip no documento.\n */\nclass FxTooltipManager {\n private static instance: FxTooltipManager | null = null;\n private observer: MutationObserver | null = null;\n private behaviors: Map<HTMLElement, TooltipBehavior> = new Map();\n private injectedStyles: HTMLStyleElement | null = null;\n\n private constructor() {\n this.injectStyles();\n this.observe();\n this.processExistingElements();\n }\n\n public static getInstance(): FxTooltipManager {\n if (!FxTooltipManager.instance) {\n FxTooltipManager.instance = new FxTooltipManager();\n }\n return FxTooltipManager.instance;\n }\n\n private injectStyles(): void {\n if (document.getElementById('fx-tooltip-directive-styles')) return;\n this.injectedStyles = document.createElement('style');\n this.injectedStyles.id = 'fx-tooltip-directive-styles';\n this.injectedStyles.textContent = getTooltipStyles();\n document.head.appendChild(this.injectedStyles);\n }\n\n private observe(): void {\n this.observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n mutation.addedNodes.forEach((node) => {\n if (node instanceof HTMLElement) {\n this.processElement(node);\n node.querySelectorAll<HTMLElement>('[fx-tooltip]').forEach((el) => this.processElement(el));\n }\n });\n }\n if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {\n const target = mutation.target;\n if (target.hasAttribute('fx-tooltip')) {\n this.processElement(target);\n } else if (this.behaviors.has(target)) {\n this.removeBehavior(target);\n }\n }\n }\n });\n this.observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['fx-tooltip', 'fx-tooltip-position', 'fx-tooltip-html'],\n });\n }\n\n private processExistingElements(): void {\n document.querySelectorAll<HTMLElement>('[fx-tooltip]').forEach((el) => this.processElement(el));\n }\n\n private processElement(element: HTMLElement): void {\n if (!element.hasAttribute('fx-tooltip')) {\n this.removeBehavior(element);\n return;\n }\n if (this.behaviors.has(element)) {\n this.behaviors.get(element)!.update();\n } else {\n this.behaviors.set(element, new TooltipBehavior(element));\n }\n }\n\n private removeBehavior(element: HTMLElement): void {\n const behavior = this.behaviors.get(element);\n if (behavior) {\n behavior.destroy();\n this.behaviors.delete(element);\n }\n }\n\n public destroy(): void {\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n this.behaviors.forEach((behavior) => behavior.destroy());\n this.behaviors.clear();\n if (this.injectedStyles) {\n this.injectedStyles.remove();\n this.injectedStyles = null;\n }\n FxTooltipManager.instance = null;\n }\n}\n\n/**\n * Retorna os estilos CSS para os tooltips.\n */\nfunction getTooltipStyles(): string {\n return /* css */ `\n [fx-tooltip] {\n position: relative;\n }\n\n .fx-tooltip-bubble {\n position: absolute;\n background: var(--fx-text-default, #1e293b);\n color: var(--fx-surface-background, #fff);\n font-family: var(--fx-font-family, system-ui, -apple-system, sans-serif);\n font-size: calc(var(--fx-font-size, 14px) - 2px);\n line-height: 1.45;\n padding: var(--fx-space-xs, 4px) var(--fx-space-md, 12px);\n border-radius: var(--fx-radius-sm, 4px);\n box-shadow: var(--fx-shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.1));\n width: max-content;\n min-width: 60px;\n max-width: var(--fx-tooltip-max-width, 320px);\n white-space: normal;\n text-align: center;\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--fx-motion-duration-fast, 150ms) var(--fx-motion-easing, ease);\n z-index: var(--fx-z-tooltip, 1050);\n pointer-events: none;\n }\n\n .fx-tooltip-bubble.fx-tooltip-visible {\n opacity: 1;\n visibility: visible;\n }\n\n .fx-tooltip-top {\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n }\n .fx-tooltip-top::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-bottom {\n top: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n bottom: auto;\n }\n .fx-tooltip-bottom::after {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-bottom-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-left {\n right: calc(100% + 6px);\n top: 50%;\n transform: translateY(-50%);\n left: auto;\n bottom: auto;\n }\n .fx-tooltip-left::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 100%;\n transform: translateY(-50%);\n border: 5px solid transparent;\n border-left-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-right {\n left: calc(100% + 6px);\n top: 50%;\n transform: translateY(-50%);\n right: auto;\n bottom: auto;\n }\n .fx-tooltip-right::after {\n content: '';\n position: absolute;\n top: 50%;\n right: 100%;\n transform: translateY(-50%);\n border: 5px solid transparent;\n border-right-color: var(--fx-text-default, #1e293b);\n }\n `;\n}\n\n/**\n * Inicializa a diretiva fx-tooltip.\n * Deve ser chamado uma vez na aplicação.\n *\n * @example\n * import { defineFxTooltipDirective } from '@wrrdev/fenix-ui/tooltip';\n * defineFxTooltipDirective();\n */\nexport function defineFxTooltipDirective(): void {\n if (typeof window === 'undefined') return;\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n FxTooltipManager.getInstance();\n });\n } else {\n FxTooltipManager.getInstance();\n }\n}\n\n/**\n * Destrói a diretiva fx-tooltip e limpa todos os recursos.\n */\nexport function destroyFxTooltipDirective(): void {\n FxTooltipManager.getInstance().destroy();\n}\n\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"directive.js","sources":["../../../src/components/tooltip/directive.ts"],"sourcesContent":["/**\n * Diretiva fx-tooltip — Aplica tooltip em qualquer elemento HTML.\n *\n * Uso:\n * <div fx-tooltip=\"Texto simples\">Div com tooltip</div>\n * <button fx-tooltip=\"Clique\" fx-tooltip-position=\"bottom\">Botão</button>\n * <span fx-tooltip=\"<strong>HTML</strong>\" fx-tooltip-html>Texto</span>\n *\n * Atributos:\n * fx-tooltip — Conteúdo do tooltip (texto ou HTML)\n * fx-tooltip-position — Posição: top | bottom | left | right (padrão: top)\n * fx-tooltip-html — Se presente, trata o conteúdo como HTML\n */\n\nexport interface FxTooltipConfig {\n position: 'top' | 'bottom' | 'left' | 'right';\n html: boolean;\n}\n\nconst DEFAULT_CONFIG: FxTooltipConfig = {\n position: 'top',\n html: false,\n};\n\n/**\n * Classe que gerencia o comportamento de tooltip em um elemento.\n */\nclass TooltipBehavior {\n private element: HTMLElement;\n private tooltipElement: HTMLSpanElement | null = null;\n private config: FxTooltipConfig;\n private showTimeout: number | null = null;\n private hideTimeout: number | null = null;\n\n constructor(element: HTMLElement) {\n this.element = element;\n this.config = this.parseConfig();\n this.init();\n }\n\n private parseConfig(): FxTooltipConfig {\n const position = this.element.getAttribute('fx-tooltip-position') as FxTooltipConfig['position'];\n return {\n position: position && ['top', 'bottom', 'left', 'right'].includes(position) ? position : DEFAULT_CONFIG.position,\n html: this.element.hasAttribute('fx-tooltip-html'),\n };\n }\n\n private init(): void {\n const computedStyle = window.getComputedStyle(this.element);\n if (computedStyle.position === 'static') {\n this.element.style.position = 'relative';\n }\n this.createTooltipElement();\n this.element.addEventListener('mouseenter', this.show);\n this.element.addEventListener('mouseleave', this.hide);\n this.element.addEventListener('focus', this.show);\n this.element.addEventListener('blur', this.hide);\n }\n\n private createTooltipElement(): void {\n const content = this.element.getAttribute('fx-tooltip');\n if (!content) return;\n this.tooltipElement = document.createElement('span');\n this.tooltipElement.setAttribute('role', 'tooltip');\n this.tooltipElement.setAttribute('data-fx-tooltip', '');\n this.tooltipElement.className = `fx-tooltip-bubble fx-tooltip-${this.config.position}`;\n if (this.config.html) {\n this.tooltipElement.innerHTML = content;\n } else {\n this.tooltipElement.textContent = content;\n }\n this.element.appendChild(this.tooltipElement);\n }\n\n private show = (): void => {\n if (this.hideTimeout) {\n clearTimeout(this.hideTimeout);\n this.hideTimeout = null;\n }\n this.showTimeout = window.setTimeout(() => {\n if (this.tooltipElement) {\n this.tooltipElement.classList.add('fx-tooltip-visible');\n }\n }, 100);\n };\n\n private hide = (): void => {\n if (this.showTimeout) {\n clearTimeout(this.showTimeout);\n this.showTimeout = null;\n }\n this.hideTimeout = window.setTimeout(() => {\n if (this.tooltipElement) {\n this.tooltipElement.classList.remove('fx-tooltip-visible');\n }\n }, 50);\n };\n\n public update(): void {\n this.config = this.parseConfig();\n if (this.tooltipElement) {\n this.tooltipElement.remove();\n this.tooltipElement = null;\n }\n this.createTooltipElement();\n }\n\n public destroy(): void {\n this.element.removeEventListener('mouseenter', this.show);\n this.element.removeEventListener('mouseleave', this.hide);\n this.element.removeEventListener('focus', this.show);\n this.element.removeEventListener('blur', this.hide);\n if (this.showTimeout) clearTimeout(this.showTimeout);\n if (this.hideTimeout) clearTimeout(this.hideTimeout);\n if (this.tooltipElement) {\n this.tooltipElement.remove();\n this.tooltipElement = null;\n }\n }\n}\n\n/**\n * Gerencia todas as instâncias de tooltip no documento.\n */\nclass FxTooltipManager {\n private static instance: FxTooltipManager | null = null;\n private observer: MutationObserver | null = null;\n private behaviors: Map<HTMLElement, TooltipBehavior> = new Map();\n private injectedStyles: HTMLStyleElement | null = null;\n\n private constructor() {\n this.injectStyles();\n this.observe();\n this.processExistingElements();\n }\n\n public static getInstance(): FxTooltipManager {\n if (!FxTooltipManager.instance) {\n FxTooltipManager.instance = new FxTooltipManager();\n }\n return FxTooltipManager.instance;\n }\n\n /** Quantos tooltips estão ativos agora (0 se a diretiva nunca foi iniciada). */\n public static peekActiveCount(): number {\n return FxTooltipManager.instance?.behaviors.size ?? 0;\n }\n\n /**\n * Quantos tooltips estão ativamente gerenciados.\n *\n * Deve acompanhar o número de elementos `[fx-tooltip]` no documento: um\n * valor crescente depois de trocar de tela significa comportamento retido\n * (vazamento). Usado nos testes de regressão e no diagnóstico em produção.\n */\n public get activeCount(): number {\n return this.behaviors.size;\n }\n\n private injectStyles(): void {\n if (document.getElementById('fx-tooltip-directive-styles')) return;\n this.injectedStyles = document.createElement('style');\n this.injectedStyles.id = 'fx-tooltip-directive-styles';\n this.injectedStyles.textContent = getTooltipStyles();\n document.head.appendChild(this.injectedStyles);\n }\n\n private observe(): void {\n this.observer = new MutationObserver((mutations) => {\n // NÃO processa aqui: enfileira e faz UM passe por frame.\n //\n // O observer escuta `childList + subtree` no <body> inteiro, então numa\n // tela grande (tabela, lista virtualizada) um único gesto do usuário pode\n // gerar dezenas de registros. Processar cada record na hora repete\n // `querySelectorAll` sobre a mesma subárvore várias vezes e trava o\n // main thread. O lote garante no máximo 1 varredura por frame.\n this._pending.added.push(...mutations);\n if (this._flushScheduled) return;\n this._flushScheduled = true;\n const run = () => {\n this._flushScheduled = false;\n const batch = this._pending;\n this._pending = { added: [] };\n this._flush(batch.added);\n };\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(run);\n else setTimeout(run, 0);\n });\n this.observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n // `attributeFilter` é o que mantém o custo baixo: o browser só notifica\n // mudanças nestes 3 atributos, e não em qualquer atributo da página.\n attributeFilter: ['fx-tooltip', 'fx-tooltip-position', 'fx-tooltip-html'],\n });\n }\n\n private _pending: { added: MutationRecord[] } = { added: [] };\n private _flushScheduled = false;\n\n /** Um passe de verificação sobre todos os registros acumulados. */\n private _flush(records: MutationRecord[]): void {\n // Sets evitam processar o mesmo elemento várias vezes quando ele aparece\n // em mais de um registro (comum com inserções aninhadas).\n const toProcess = new Set<HTMLElement>();\n const toRemove = new Set<HTMLElement>();\n\n for (const mutation of records) {\n if (mutation.type === 'childList') {\n mutation.addedNodes.forEach((node) => {\n if (node instanceof HTMLElement) {\n toProcess.add(node);\n node.querySelectorAll<HTMLElement>('[fx-tooltip]').forEach((el) => toProcess.add(el));\n }\n });\n // SEMPRE trate as remoções: sem isso, cada elemento que já teve\n // `fx-tooltip` fica preso no Map (elemento + listeners + bubble)\n // mesmo depois de desmontado. Em app grande/uso prolongado isso\n // cresce sem limite.\n mutation.removedNodes.forEach((node) => {\n if (node instanceof HTMLElement) {\n toRemove.add(node);\n node.querySelectorAll<HTMLElement>('[fx-tooltip]').forEach((el) => toRemove.add(el));\n }\n });\n }\n if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {\n const target = mutation.target;\n if (target.hasAttribute('fx-tooltip')) toProcess.add(target);\n else toRemove.add(target);\n }\n }\n\n // Remoções primeiro: um elemento que saiu e voltou no mesmo frame deve\n // acabar com o comportamento novo, não ser destruído por engano.\n toRemove.forEach((el) => this.removeBehavior(el));\n toProcess.forEach((el) => this.processElement(el));\n }\n\n private processExistingElements(): void {\n document.querySelectorAll<HTMLElement>('[fx-tooltip]').forEach((el) => this.processElement(el));\n }\n\n private processElement(element: HTMLElement): void {\n if (!element.hasAttribute('fx-tooltip')) {\n this.removeBehavior(element);\n return;\n }\n if (this.behaviors.has(element)) {\n this.behaviors.get(element)!.update();\n } else {\n this.behaviors.set(element, new TooltipBehavior(element));\n }\n }\n\n private removeBehavior(element: HTMLElement): void {\n const behavior = this.behaviors.get(element);\n if (behavior) {\n behavior.destroy();\n this.behaviors.delete(element);\n }\n }\n\n public destroy(): void {\n // Descarta o lote pendente: um flush depois do destroy recriaria\n // comportamentos em elementos já desmontados.\n this._pending.added = [];\n this._flushScheduled = false;\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n this.behaviors.forEach((behavior) => behavior.destroy());\n this.behaviors.clear();\n if (this.injectedStyles) {\n this.injectedStyles.remove();\n this.injectedStyles = null;\n }\n FxTooltipManager.instance = null;\n }\n}\n\n/**\n * Retorna os estilos CSS para os tooltips.\n */\nfunction getTooltipStyles(): string {\n return /* css */ `\n [fx-tooltip] {\n position: relative;\n }\n\n .fx-tooltip-bubble {\n position: absolute;\n background: var(--fx-text-default, #1e293b);\n color: var(--fx-surface-background, #fff);\n font-family: var(--fx-font-family, system-ui, -apple-system, sans-serif);\n font-size: calc(var(--fx-font-size, 14px) - 2px);\n line-height: 1.45;\n padding: var(--fx-space-xs, 4px) var(--fx-space-md, 12px);\n border-radius: var(--fx-radius-sm, 4px);\n box-shadow: var(--fx-shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.1));\n width: max-content;\n min-width: 60px;\n max-width: var(--fx-tooltip-max-width, 320px);\n white-space: normal;\n text-align: center;\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--fx-motion-duration-fast, 150ms) var(--fx-motion-easing, ease);\n z-index: var(--fx-z-tooltip, 1050);\n pointer-events: none;\n }\n\n .fx-tooltip-bubble.fx-tooltip-visible {\n opacity: 1;\n visibility: visible;\n }\n\n .fx-tooltip-top {\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n }\n .fx-tooltip-top::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-bottom {\n top: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n bottom: auto;\n }\n .fx-tooltip-bottom::after {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-bottom-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-left {\n right: calc(100% + 6px);\n top: 50%;\n transform: translateY(-50%);\n left: auto;\n bottom: auto;\n }\n .fx-tooltip-left::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 100%;\n transform: translateY(-50%);\n border: 5px solid transparent;\n border-left-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-right {\n left: calc(100% + 6px);\n top: 50%;\n transform: translateY(-50%);\n right: auto;\n bottom: auto;\n }\n .fx-tooltip-right::after {\n content: '';\n position: absolute;\n top: 50%;\n right: 100%;\n transform: translateY(-50%);\n border: 5px solid transparent;\n border-right-color: var(--fx-text-default, #1e293b);\n }\n `;\n}\n\n/**\n * Inicializa a diretiva fx-tooltip.\n * Deve ser chamado uma vez na aplicação.\n *\n * @example\n * import { defineFxTooltipDirective } from '@wrrdev/fenix-ui/tooltip';\n * defineFxTooltipDirective();\n */\nexport function defineFxTooltipDirective(): void {\n if (typeof window === 'undefined') return;\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n FxTooltipManager.getInstance();\n });\n } else {\n FxTooltipManager.getInstance();\n }\n}\n\n/**\n * Quantos tooltips a diretiva mantém vivos agora.\n *\n * Deve igualar o nº de elementos `[fx-tooltip]` no documento. Se crescer após\n * trocar de tela, há comportamento retido (vazamento). Pensado para diagnóstico\n * em produção e para os testes de regressão.\n */\nexport function fxTooltipActiveCount(): number {\n return FxTooltipManager.peekActiveCount();\n}\n\n/**\n * Destrói a diretiva fx-tooltip e limpa todos os recursos.\n */\nexport function destroyFxTooltipDirective(): void {\n FxTooltipManager.getInstance().destroy();\n}\n\n"],"names":[],"mappings":"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"}