@wrrdev/fenix-ui 1.1.8 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/components/drawer/drawer.d.ts.map +1 -1
- package/dist/components/drawer/drawer.js +4 -0
- package/dist/components/drawer/drawer.js.map +1 -1
- package/dist/components/pagination/pagination.d.ts +1 -0
- package/dist/components/pagination/pagination.d.ts.map +1 -1
- package/dist/components/pagination/pagination.js +18 -2
- package/dist/components/pagination/pagination.js.map +1 -1
- package/dist/components/table/table.d.ts +38 -4
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +90 -18
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/toast/toast.d.ts +46 -1
- package/dist/components/toast/toast.d.ts.map +1 -1
- package/dist/components/toast/toast.js +128 -5
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/core/jsx.d.ts +63 -1
- package/dist/core/jsx.d.ts.map +1 -1
- package/dist/core/jsx.js.map +1 -1
- package/dist/core/vue.d.ts +7 -1
- package/dist/core/vue.d.ts.map +1 -1
- package/dist/core/vue.js.map +1 -1
- package/dist/docs/app.d.ts +2 -6
- package/dist/docs/app.d.ts.map +1 -1
- package/dist/docs/componentes/table.doc.d.ts.map +1 -1
- package/dist/docs/componentes/toast.doc.d.ts.map +1 -1
- package/dist/docs/router.d.ts +17 -0
- package/dist/docs/router.d.ts.map +1 -0
- package/dist/docs/shell.d.ts +43 -0
- package/dist/docs/shell.d.ts.map +1 -0
- package/dist/docs/ui/code.d.ts +12 -0
- package/dist/docs/ui/code.d.ts.map +1 -0
- package/dist/docs/ui/kit.d.ts +18 -0
- package/dist/docs/ui/kit.d.ts.map +1 -0
- package/dist/docs/ui/tabs.d.ts +23 -0
- package/dist/docs/ui/tabs.d.ts.map +1 -0
- package/dist/fenix-ui.umd.min.js +179 -116
- package/dist/fenix-ui2.esm.js.map +1 -1
- package/dist/fenix-ui4.esm.js +237 -26
- package/dist/fenix-ui4.esm.js.map +1 -1
- package/dist/fenix-ui5.esm.js +4 -1
- package/dist/fenix-ui5.esm.js.map +1 -1
- package/dist/plugins/auto-import.d.ts.map +1 -1
- package/dist/plugins/auto-import.js +4 -1
- package/dist/plugins/auto-import.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ import { FenixUI } from '@wrrdev/fenix-ui'; // API de tema/tokens (índice)
|
|
|
18
18
|
FenixUI.theme('dark');
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
> 📦 npm: [`@wrrdev/fenix-ui`](https://www.npmjs.com/package/@wrrdev/fenix-ui) — **v1.1.
|
|
21
|
+
> 📦 npm: [`@wrrdev/fenix-ui`](https://www.npmjs.com/package/@wrrdev/fenix-ui) — **v1.1.9**, acesso público.
|
|
22
22
|
> Subpaths disponíveis: `./theme`, `./tokens`, `./jsx`, `./vue`, `./react`, `./button`, `./badge`, `./spinner`, `./select`, `./input`, `./switch`, `./textarea`, `./checkbox`, `./radio`, `./calendar`, `./datepicker`, `./multiselect`, `./table`, `./floatlabel`, `./dialog`, `./toast`, `./tooltip`, `./tabs`, `./progress`, `./skeleton`, `./alert`, `./drawer`, `./dropdown`, `./icon`, `./icons`, `./pagination`, `./autocomplete`, `./knob`, `./orderlist`, `./picklist`, `./auto-import` e o curinga `./components/*`.
|
|
23
23
|
|
|
24
24
|
|
|
@@ -65,7 +65,7 @@ Carregue o bundle **UMD** único: ele registra todos os componentes e expõe os
|
|
|
65
65
|
<fx-toast></fx-toast>
|
|
66
66
|
|
|
67
67
|
<!--
|
|
68
|
-
🔗 Links diretos para CDN (versão 1.1.
|
|
68
|
+
🔗 Links diretos para CDN (versão 1.1.9):
|
|
69
69
|
• https://cdn.jsdelivr.net/npm/@wrrdev/fenix-ui@latest/dist/fenix-ui.umd.min.js
|
|
70
70
|
• https://unpkg.com/@wrrdev/fenix-ui@latest/dist/fenix-ui.umd.min.js
|
|
71
71
|
-->
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.d.ts","sourceRoot":"","sources":["../../../src/components/drawer/drawer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;GASG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACtC,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"drawer.d.ts","sourceRoot":"","sources":["../../../src/components/drawer/drawer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;GASG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACtC,OAAgB,MAAM,SA0HpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAEtB;IAED,IAAa,KAAK,IAAI,MAAM,CAE3B;IACD,IAAa,KAAK,CAAC,CAAC,EAAE,MAAM,EAE3B;IAED,IAAI,QAAQ,IAAI,MAAM,CAGrB;IACD,IAAI,QAAQ,CAAC,CAAC,EAAE,MAAM,EAErB;cAEkB,MAAM,IAAI,IAAI;cAwBd,iBAAiB,IAAI,IAAI;cAYzB,oBAAoB,IAAI,IAAI;IAM/C,OAAO,CAAC,UAAU,CAAwC;IAC1D,OAAO,CAAC,kBAAkB,CAA4B;IAEtD,OAAO,CAAC,MAAM,CAOZ;IAEF,2DAA2D;IAC3D,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,aAAa;CAIrB;AAED,wBAAgB,cAAc,IAAI,OAAO,QAAQ,CAEhD"}
|
|
@@ -184,6 +184,10 @@ _FxDrawer.styles = css`
|
|
|
184
184
|
position: sticky;
|
|
185
185
|
top: 0;
|
|
186
186
|
background: var(--fx-surface-background);
|
|
187
|
+
/* O conteúdo do drawer (ex.: abas da customização de tema)
|
|
188
|
+
rola por trás do header: sem z-index o texto passava POR CIMA
|
|
189
|
+
e o header parecia transparente. */
|
|
190
|
+
z-index: 1;
|
|
187
191
|
}
|
|
188
192
|
.title {
|
|
189
193
|
font-weight: 700;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.js","sources":["../../../src/components/drawer/drawer.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-drawer> — Painel deslizante (drawer) com overlay.\n *\n * Atributos:\n * - position: 'left' | 'right' | 'top' | 'bottom' (padrão: right);\n * - title: texto do cabeçalho;\n * - open: exibe o drawer.\n * Conteúdo: livre, via slot.\n * Eventos: `open` e `close` (composed). Fecha por ESC ou clique no overlay.\n */\nexport class FxDrawer extends FxElement {\n\tstatic override styles = css`\n\t\t:host {\n\t\t\tdisplay: contents;\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}\n\t\t.overlay {\n\t\t\tdisplay: none;\n\t\t\tposition: fixed;\n\t\t\tinset: 0;\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--fx-text-default, #000) 45%,\n\t\t\t\ttransparent\n\t\t\t);\n\t\t\tbackdrop-filter: blur(3px);\n\t\t\t-webkit-backdrop-filter: blur(3px);\n\t\t\tz-index: var(--fx-z-overlay, 1090);\n\t\t\tanimation: fade var(--fx-motion-duration-fast) var(--fx-motion-easing);\n\t\t}\n\t\t:host([open]) .overlay {\n\t\t\tdisplay: block;\n\t\t}\n\n\t\t.panel {\n\t\t\tposition: fixed;\n\t\t\tbackground: var(--fx-surface-background);\n\t\t\tbox-shadow: var(--fx-shadow-xl);\n\t\t\tz-index: var(--fx-z-modal, 1000);\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\toverflow: auto;\n\t\t}\n\t\t:host(:not([open])) .panel {\n\t\t\tdisplay: none;\n\t\t}\n\t\t:host([open]) .panel {\n\t\t\tdisplay: flex;\n\t\t}\n\t\t/* left / right — altura total, largura mínima fixa e expansível */\n\t\t:host([position=\"left\"]) .panel,\n\t\t:host(:not([position])) .panel,\n\t\t:host([position=\"right\"]) .panel {\n\t\t\ttop: 0;\n\t\t\tbottom: 0;\n\t\t\twidth: var(--fx-drawer-width, 360px);\n\t\t\tmin-width: 300px;\n\t\t\tmax-width: 90vw;\n\t\t}\n\t\t:host([position=\"left\"]) .panel {\n\t\t\tleft: 0;\n\t\t}\n\t\t:host([position=\"right\"]) .panel,\n\t\t:host(:not([position])) .panel {\n\t\t\tright: 0;\n\t\t}\n\n\t\t/* top / bottom — largura total, altura mínima fixa e expansível */\n\t\t:host([position=\"top\"]) .panel,\n\t\t:host([position=\"bottom\"]) .panel {\n\t\t\tleft: 0;\n\t\t\tright: 0;\n\t\t\theight: var(--fx-drawer-height, 280px);\n\t\t\tmin-height: 180px;\n\t\t\tmax-height: 85vh;\n\t\t}\n\t\t:host([position=\"top\"]) .panel {\n\t\t\ttop: 0;\n\t\t}\n\t\t:host([position=\"bottom\"]) .panel {\n\t\t\tbottom: 0;\n\t\t}\n\n\t\t.header {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tmin-height: var(--fx-size-lg);\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\tflex: none;\n\t\t\tposition: sticky;\n\t\t\ttop: 0;\n\t\t\tbackground: var(--fx-surface-background);\n\t\t}\n\t\t.title {\n\t\t\tfont-weight: 700;\n\t\t\tflex: 1;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t\twhite-space: nowrap;\n\t\t}\n\t\t.close {\n\t\t\tborder: none;\n\t\t\tbackground: transparent;\n\t\t\tcursor: pointer;\n\t\t\tcolor: var(--fx-text-muted);\n\t\t\twidth: 28px;\n\t\t\theight: 28px;\n\t\t\tline-height: 1;\n\t\t\tborder-radius: var(--fx-radius-sm);\n\t\t\tfont-size: 16px;\n\t\t\ttransition: background var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\t.close:hover {\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--fx-color-danger) 14%,\n\t\t\t\tvar(--fx-surface-background)\n\t\t\t);\n\t\t\tcolor: var(--fx-color-danger);\n\t\t}\n\t\t.body {\n\t\t\tpadding: var(--fx-space-md);\n\t\t\tflex: 1;\n\t\t}\n\t`;\n\n\tstatic override get observedAttributes(): string[] {\n\t\treturn [\"open\", \"title\", \"position\"];\n\t}\n\n\tget open(): boolean {\n\t\treturn this.hasAttr(\"open\");\n\t}\n\tset open(value: boolean) {\n\t\tthis.toggleAttr(\"open\", value);\n\t}\n\n\toverride get title(): string {\n\t\treturn this.getAttr(\"title\");\n\t}\n\toverride set title(v: string) {\n\t\tthis.setAttribute(\"title\", v);\n\t}\n\n\tget position(): string {\n\t\tconst p = this.getAttr(\"position\");\n\t\treturn [\"left\", \"right\", \"top\", \"bottom\"].includes(p) ? p : \"right\";\n\t}\n\tset position(v: string) {\n\t\tthis.setAttribute(\"position\", v);\n\t}\n\n\tprotected override render(): void {\n\t\tconst title = this.getAttr(\"title\");\n\n\t\tthis.setTemplate(`\n <div class=\"overlay\" part=\"overlay\"></div>\n <div class=\"panel\" part=\"panel\" role=\"dialog\" aria-modal=\"true\" aria-label=\"${esc(title || \"Drawer\")}\" tabindex=\"-1\">\n ${title !== \"\" ? `<div class=\"header\" part=\"header\"><span class=\"title\">${esc(title)}</span><button type=\"button\" class=\"close\" part=\"close\" aria-label=\"Fechar\">✕</button></div>` : \"\"}\n <div class=\"body\" part=\"body\"><slot></slot></div>\n </div>\n `);\n\n\t\tthis.root.querySelector(\".overlay\")?.addEventListener(\"click\", this._close);\n\t\tthis.root.querySelector(\".close\")?.addEventListener(\"click\", this._close);\n\n\t\tif (this.open) {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tif (this._previouslyFocused === null) {\n\t\t\t\t\tthis._previouslyFocused = document.activeElement as HTMLElement | null;\n\t\t\t\t}\n\t\t\t\tthis.root.querySelector<HTMLElement>(\".panel\")?.focus();\n\t\t\t});\n\t\t}\n\t}\n\n\tprotected override connectedCallback(): void {\n\t\tsuper.connectedCallback?.();\n\t\tthis._onKeydown = (e: KeyboardEvent) => {\n\t\t\tif (e.key === \"Escape\" && this.open) {\n\t\t\t\tthis._close();\n\t\t\t} else if (e.key === \"Tab\" && this.open) {\n\t\t\t\tthis._trapFocus(e);\n\t\t\t}\n\t\t};\n\t\tdocument.addEventListener(\"keydown\", this._onKeydown);\n\t}\n\n\tprotected override disconnectedCallback(): void {\n\t\tdocument.removeEventListener(\"keydown\", this._onKeydown);\n\t\tthis._restoreFocus();\n\t\tsuper.disconnectedCallback?.();\n\t}\n\n\tprivate _onKeydown: (e: KeyboardEvent) => void = () => {};\n\tprivate _previouslyFocused: HTMLElement | null = null;\n\n\tprivate _close = (): void => {\n\t\tif (!this.open) return;\n\t\tthis.open = false;\n\t\tthis._restoreFocus();\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent(\"close\", { bubbles: true, composed: true }),\n\t\t);\n\t};\n\n\t/** Mantém o foco dentro do drawer (WCAG 2.4.3 / 2.1.2). */\n\tprivate _trapFocus(e: KeyboardEvent): void {\n\t\tconst panel = this.root.querySelector<HTMLElement>(\".panel\");\n\t\tif (!panel) return;\n\t\tconst focusables = panel.querySelectorAll<HTMLElement>(\n\t\t\t'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n\t\t);\n\t\tif (!focusables.length) return;\n\t\tconst first = focusables[0];\n\t\tconst last = focusables[focusables.length - 1];\n\t\tconst active = this.root.activeElement;\n\t\tif (e.shiftKey && (active === first || active === panel)) {\n\t\t\te.preventDefault();\n\t\t\tlast.focus();\n\t\t} else if (!e.shiftKey && active === last) {\n\t\t\te.preventDefault();\n\t\t\tfirst.focus();\n\t\t}\n\t}\n\n\tprivate _restoreFocus(): void {\n\t\tif (this._previouslyFocused?.isConnected) this._previouslyFocused.focus();\n\t\tthis._previouslyFocused = null;\n\t}\n}\n\nexport function defineFxDrawer(): typeof FxDrawer {\n return defineElement('fx-drawer', FxDrawer);\n}"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"drawer.js","sources":["../../../src/components/drawer/drawer.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-drawer> — Painel deslizante (drawer) com overlay.\n *\n * Atributos:\n * - position: 'left' | 'right' | 'top' | 'bottom' (padrão: right);\n * - title: texto do cabeçalho;\n * - open: exibe o drawer.\n * Conteúdo: livre, via slot.\n * Eventos: `open` e `close` (composed). Fecha por ESC ou clique no overlay.\n */\nexport class FxDrawer extends FxElement {\n\tstatic override styles = css`\n\t\t:host {\n\t\t\tdisplay: contents;\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}\n\t\t.overlay {\n\t\t\tdisplay: none;\n\t\t\tposition: fixed;\n\t\t\tinset: 0;\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--fx-text-default, #000) 45%,\n\t\t\t\ttransparent\n\t\t\t);\n\t\t\tbackdrop-filter: blur(3px);\n\t\t\t-webkit-backdrop-filter: blur(3px);\n\t\t\tz-index: var(--fx-z-overlay, 1090);\n\t\t\tanimation: fade var(--fx-motion-duration-fast) var(--fx-motion-easing);\n\t\t}\n\t\t:host([open]) .overlay {\n\t\t\tdisplay: block;\n\t\t}\n\n\t\t.panel {\n\t\t\tposition: fixed;\n\t\t\tbackground: var(--fx-surface-background);\n\t\t\tbox-shadow: var(--fx-shadow-xl);\n\t\t\tz-index: var(--fx-z-modal, 1000);\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\toverflow: auto;\n\t\t}\n\t\t:host(:not([open])) .panel {\n\t\t\tdisplay: none;\n\t\t}\n\t\t:host([open]) .panel {\n\t\t\tdisplay: flex;\n\t\t}\n\t\t/* left / right — altura total, largura mínima fixa e expansível */\n\t\t:host([position=\"left\"]) .panel,\n\t\t:host(:not([position])) .panel,\n\t\t:host([position=\"right\"]) .panel {\n\t\t\ttop: 0;\n\t\t\tbottom: 0;\n\t\t\twidth: var(--fx-drawer-width, 360px);\n\t\t\tmin-width: 300px;\n\t\t\tmax-width: 90vw;\n\t\t}\n\t\t:host([position=\"left\"]) .panel {\n\t\t\tleft: 0;\n\t\t}\n\t\t:host([position=\"right\"]) .panel,\n\t\t:host(:not([position])) .panel {\n\t\t\tright: 0;\n\t\t}\n\n\t\t/* top / bottom — largura total, altura mínima fixa e expansível */\n\t\t:host([position=\"top\"]) .panel,\n\t\t:host([position=\"bottom\"]) .panel {\n\t\t\tleft: 0;\n\t\t\tright: 0;\n\t\t\theight: var(--fx-drawer-height, 280px);\n\t\t\tmin-height: 180px;\n\t\t\tmax-height: 85vh;\n\t\t}\n\t\t:host([position=\"top\"]) .panel {\n\t\t\ttop: 0;\n\t\t}\n\t\t:host([position=\"bottom\"]) .panel {\n\t\t\tbottom: 0;\n\t\t}\n\n\t\t.header {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: var(--fx-space-sm);\n\t\t\tmin-height: var(--fx-size-lg);\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\tflex: none;\n\t\t\tposition: sticky;\n\t\t\ttop: 0;\n\t\t\tbackground: var(--fx-surface-background);\n\t\t\t/* O conteúdo do drawer (ex.: abas da customização de tema)\n\t\t\t rola por trás do header: sem z-index o texto passava POR CIMA\n\t\t\t e o header parecia transparente. */\n\t\t\tz-index: 1;\n\t\t}\n\t\t.title {\n\t\t\tfont-weight: 700;\n\t\t\tflex: 1;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t\twhite-space: nowrap;\n\t\t}\n\t\t.close {\n\t\t\tborder: none;\n\t\t\tbackground: transparent;\n\t\t\tcursor: pointer;\n\t\t\tcolor: var(--fx-text-muted);\n\t\t\twidth: 28px;\n\t\t\theight: 28px;\n\t\t\tline-height: 1;\n\t\t\tborder-radius: var(--fx-radius-sm);\n\t\t\tfont-size: 16px;\n\t\t\ttransition: background var(--fx-motion-duration-fast)\n\t\t\t\tvar(--fx-motion-easing);\n\t\t}\n\t\t.close:hover {\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--fx-color-danger) 14%,\n\t\t\t\tvar(--fx-surface-background)\n\t\t\t);\n\t\t\tcolor: var(--fx-color-danger);\n\t\t}\n\t\t.body {\n\t\t\tpadding: var(--fx-space-md);\n\t\t\tflex: 1;\n\t\t}\n\t`;\n\n\tstatic override get observedAttributes(): string[] {\n\t\treturn [\"open\", \"title\", \"position\"];\n\t}\n\n\tget open(): boolean {\n\t\treturn this.hasAttr(\"open\");\n\t}\n\tset open(value: boolean) {\n\t\tthis.toggleAttr(\"open\", value);\n\t}\n\n\toverride get title(): string {\n\t\treturn this.getAttr(\"title\");\n\t}\n\toverride set title(v: string) {\n\t\tthis.setAttribute(\"title\", v);\n\t}\n\n\tget position(): string {\n\t\tconst p = this.getAttr(\"position\");\n\t\treturn [\"left\", \"right\", \"top\", \"bottom\"].includes(p) ? p : \"right\";\n\t}\n\tset position(v: string) {\n\t\tthis.setAttribute(\"position\", v);\n\t}\n\n\tprotected override render(): void {\n\t\tconst title = this.getAttr(\"title\");\n\n\t\tthis.setTemplate(`\n <div class=\"overlay\" part=\"overlay\"></div>\n <div class=\"panel\" part=\"panel\" role=\"dialog\" aria-modal=\"true\" aria-label=\"${esc(title || \"Drawer\")}\" tabindex=\"-1\">\n ${title !== \"\" ? `<div class=\"header\" part=\"header\"><span class=\"title\">${esc(title)}</span><button type=\"button\" class=\"close\" part=\"close\" aria-label=\"Fechar\">✕</button></div>` : \"\"}\n <div class=\"body\" part=\"body\"><slot></slot></div>\n </div>\n `);\n\n\t\tthis.root.querySelector(\".overlay\")?.addEventListener(\"click\", this._close);\n\t\tthis.root.querySelector(\".close\")?.addEventListener(\"click\", this._close);\n\n\t\tif (this.open) {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tif (this._previouslyFocused === null) {\n\t\t\t\t\tthis._previouslyFocused = document.activeElement as HTMLElement | null;\n\t\t\t\t}\n\t\t\t\tthis.root.querySelector<HTMLElement>(\".panel\")?.focus();\n\t\t\t});\n\t\t}\n\t}\n\n\tprotected override connectedCallback(): void {\n\t\tsuper.connectedCallback?.();\n\t\tthis._onKeydown = (e: KeyboardEvent) => {\n\t\t\tif (e.key === \"Escape\" && this.open) {\n\t\t\t\tthis._close();\n\t\t\t} else if (e.key === \"Tab\" && this.open) {\n\t\t\t\tthis._trapFocus(e);\n\t\t\t}\n\t\t};\n\t\tdocument.addEventListener(\"keydown\", this._onKeydown);\n\t}\n\n\tprotected override disconnectedCallback(): void {\n\t\tdocument.removeEventListener(\"keydown\", this._onKeydown);\n\t\tthis._restoreFocus();\n\t\tsuper.disconnectedCallback?.();\n\t}\n\n\tprivate _onKeydown: (e: KeyboardEvent) => void = () => {};\n\tprivate _previouslyFocused: HTMLElement | null = null;\n\n\tprivate _close = (): void => {\n\t\tif (!this.open) return;\n\t\tthis.open = false;\n\t\tthis._restoreFocus();\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent(\"close\", { bubbles: true, composed: true }),\n\t\t);\n\t};\n\n\t/** Mantém o foco dentro do drawer (WCAG 2.4.3 / 2.1.2). */\n\tprivate _trapFocus(e: KeyboardEvent): void {\n\t\tconst panel = this.root.querySelector<HTMLElement>(\".panel\");\n\t\tif (!panel) return;\n\t\tconst focusables = panel.querySelectorAll<HTMLElement>(\n\t\t\t'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n\t\t);\n\t\tif (!focusables.length) return;\n\t\tconst first = focusables[0];\n\t\tconst last = focusables[focusables.length - 1];\n\t\tconst active = this.root.activeElement;\n\t\tif (e.shiftKey && (active === first || active === panel)) {\n\t\t\te.preventDefault();\n\t\t\tlast.focus();\n\t\t} else if (!e.shiftKey && active === last) {\n\t\t\te.preventDefault();\n\t\t\tfirst.focus();\n\t\t}\n\t}\n\n\tprivate _restoreFocus(): void {\n\t\tif (this._previouslyFocused?.isConnected) this._previouslyFocused.focus();\n\t\tthis._previouslyFocused = null;\n\t}\n}\n\nexport function defineFxDrawer(): typeof FxDrawer {\n return defineElement('fx-drawer', FxDrawer);\n}"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../src/components/pagination/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../src/components/pagination/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAM5C,OAAO,WAAW,CAAC;AAEnB;;;;;;GAMG;AACH,qBAAa,YAAa,SAAQ,SAAS;IACzC,OAAgB,MAAM,SAmDpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,IAAI,IAAI,IAAI,MAAM,CAAmD;IACrE,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,EAA2C;IAC7D,IAAI,KAAK,IAAI,MAAM,CAAoD;IACvE,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,EAA4C;IAC/D,IAAI,IAAI,IAAI,MAAM,CAAqD;IACvE,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,EAA2C;IAE7D,OAAO,KAAK,KAAK,GAEhB;cAEkB,iBAAiB,IAAI,IAAI;cAOzB,MAAM,IAAI,IAAI;IAkDjC,OAAO,CAAC,IAAI;CAMb;AAED,wBAAgB,kBAAkB,IAAI,OAAO,YAAY,CAExD"}
|
|
@@ -2,6 +2,8 @@ import { FxElement } from "../../core/base.js";
|
|
|
2
2
|
import { css } from "../../core/css.js";
|
|
3
3
|
import { defineElement } from "../../core/define.js";
|
|
4
4
|
import { esc } from "../../core/sanitize.js";
|
|
5
|
+
import { FENIX_ICON_BASE_CSS, fenixIconHtml } from "../../icons/base-css.js";
|
|
6
|
+
import { loadFenixIconsFont } from "../../icons/font.js";
|
|
5
7
|
import "../select/index.js";
|
|
6
8
|
const _FxPagination = class _FxPagination extends FxElement {
|
|
7
9
|
static get observedAttributes() {
|
|
@@ -28,6 +30,10 @@ const _FxPagination = class _FxPagination extends FxElement {
|
|
|
28
30
|
get pages() {
|
|
29
31
|
return Math.max(1, Math.ceil(this.total / this.rows));
|
|
30
32
|
}
|
|
33
|
+
connectedCallback() {
|
|
34
|
+
loadFenixIconsFont();
|
|
35
|
+
super.connectedCallback();
|
|
36
|
+
}
|
|
31
37
|
render() {
|
|
32
38
|
const pages = this.pages;
|
|
33
39
|
const current = Math.min(Math.max(1, this.page), pages);
|
|
@@ -38,11 +44,13 @@ const _FxPagination = class _FxPagination extends FxElement {
|
|
|
38
44
|
const to = Math.min(current * this.rows, this.total);
|
|
39
45
|
this.setTemplate(`
|
|
40
46
|
<span class="info">${this.total ? `${from}-${to} de ${this.total}` : "0 itens"}</span>
|
|
41
|
-
<button type="button" class="nav" part="
|
|
47
|
+
<button type="button" class="nav" part="first" aria-label="Primeira página" data-go="1" ${current <= 1 ? "disabled" : ""}>${fenixIconHtml("first_page")}</button>
|
|
48
|
+
<button type="button" class="nav" part="prev" aria-label="Página anterior" data-go="${current - 1}" ${current <= 1 ? "disabled" : ""}>${fenixIconHtml("navigate_before")}</button>
|
|
42
49
|
${window_[0] !== 1 && pages > 5 ? `<button type="button" class="nav" data-go="1">1</button>` : ""}
|
|
43
50
|
${window_.map((p) => `<button type="button" class="nav ${p === current ? "active" : ""}" data-go="${p}">${p}</button>`).join("")}
|
|
44
51
|
${window_[window_.length - 1] !== pages && pages > 5 ? `<button type="button" class="nav" data-go="${pages}">${pages}</button>` : ""}
|
|
45
|
-
<button type="button" class="nav" part="next" data-go="${current + 1}" ${current >= pages ? "disabled" : ""}
|
|
52
|
+
<button type="button" class="nav" part="next" aria-label="Próxima página" data-go="${current + 1}" ${current >= pages ? "disabled" : ""}>${fenixIconHtml("navigate_next")}</button>
|
|
53
|
+
<button type="button" class="nav" part="last" aria-label="Última página" data-go="${pages}" ${current >= pages ? "disabled" : ""}>${fenixIconHtml("last_page")}</button>
|
|
46
54
|
${opts.length ? `
|
|
47
55
|
<fx-select class="rows-sel" part="rows" size="sm" value="${this.rows}" aria-label="Itens por página">
|
|
48
56
|
${opts.map((o) => `<option value="${esc(o)}">${esc(o)}</option>`).join("")}
|
|
@@ -78,6 +86,7 @@ const _FxPagination = class _FxPagination extends FxElement {
|
|
|
78
86
|
}
|
|
79
87
|
};
|
|
80
88
|
_FxPagination.styles = css`
|
|
89
|
+
${FENIX_ICON_BASE_CSS}
|
|
81
90
|
:host {
|
|
82
91
|
display: flex;
|
|
83
92
|
align-items: center;
|
|
@@ -89,6 +98,9 @@ _FxPagination.styles = css`
|
|
|
89
98
|
:host([position='center']) { justify-content: center; }
|
|
90
99
|
:host([position='right']) { justify-content: flex-end; }
|
|
91
100
|
.nav {
|
|
101
|
+
display: inline-flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: center;
|
|
92
104
|
min-width: var(--fx-size-sm);
|
|
93
105
|
height: var(--fx-size-sm);
|
|
94
106
|
border: 1px solid var(--fx-border-default);
|
|
@@ -103,6 +115,10 @@ _FxPagination.styles = css`
|
|
|
103
115
|
border-color var(--fx-motion-duration-fast) var(--fx-motion-easing),
|
|
104
116
|
background var(--fx-motion-duration-fast) var(--fx-motion-easing);
|
|
105
117
|
}
|
|
118
|
+
/* O glifo precisa de 1px a mais que o dígito: a altura de traço do ícone é
|
|
119
|
+
maior que a do número, então igualar os dois font-size deixa a seta
|
|
120
|
+
visivelmente maior que o texto. */
|
|
121
|
+
.nav .fx-icon { font-size: calc(var(--fx-font-size) - 1px); }
|
|
106
122
|
.nav:hover:not([disabled]):not(.active) { border-color: var(--fx-color-primary); color: var(--fx-color-primary); }
|
|
107
123
|
.nav[disabled] { opacity: 0.5; cursor: not-allowed; }
|
|
108
124
|
.nav.active {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.js","sources":["../../../src/components/pagination/pagination.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\nimport '../select';\n\n/**\n * <fx-pagination> — Paginação standalone.\n *\n * Atributos: page (atual), total (itens), rows (por página), rows-options,\n * position (left|center|right).\n * Evento: `page-change` (composed, detail: { page, rows }).\n */\nexport class FxPagination extends FxElement {\n static override styles = css`\n :host {\n display: flex;\n align-items: center;\n gap: var(--fx-space-sm);\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n color: var(--fx-text-default);\n }\n :host([position='center']) { justify-content: center; }\n :host([position='right']) { justify-content: flex-end; }\n .nav {\n min-width: var(--fx-size-sm);\n height: var(--fx-size-sm);\n border: 1px solid var(--fx-border-default);\n background: var(--fx-surface-background);\n color: var(--fx-text-default);\n font-family: inherit;\n font-size: calc(var(--fx-font-size) - 2px);\n border-radius: var(--fx-radius-sm);\n cursor: pointer;\n padding: 0 var(--fx-space-sm);\n transition:\n border-color var(--fx-motion-duration-fast) var(--fx-motion-easing),\n background var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n .nav:hover:not([disabled]):not(.active) { border-color: var(--fx-color-primary); color: var(--fx-color-primary); }\n .nav[disabled] { opacity: 0.5; cursor: not-allowed; }\n .nav.active {\n background: var(--fx-color-primary);\n border-color: var(--fx-color-primary);\n color: #fff;\n font-weight: 600;\n }\n .info { color: var(--fx-text-muted); font-size: calc(var(--fx-font-size) - 2px); }\n fx-select { vertical-align: middle; }\n fx-select::part(trigger) {\n min-width: 64px !important;\n min-height: var(--fx-size-sm) !important;\n padding: 0 var(--fx-space-sm) !important;\n border-radius: var(--fx-radius-sm) !important;\n font-size: calc(var(--fx-font-size) - 2px) !important;\n }\n `;\n\n static override get observedAttributes(): string[] {\n return ['page', 'total', 'rows', 'rows-options', 'position'];\n }\n\n get page(): number { return Number(this.getAttr('page', '1')) || 1; }\n set page(v: number) { this.setAttribute('page', String(v)); }\n get total(): number { return Number(this.getAttr('total', '0')) || 0; }\n set total(v: number) { this.setAttribute('total', String(v)); }\n get rows(): number { return Number(this.getAttr('rows', '10')) || 10; }\n set rows(v: number) { this.setAttribute('rows', String(v)); }\n\n private get pages(): number {\n return Math.max(1, Math.ceil(this.total / this.rows));\n }\n\n protected override render(): void {\n const pages = this.pages;\n const current = Math.min(Math.max(1, this.page), pages);\n const opts = (this.getAttr('rows-options', '5,10,20,50') || '').split(',').map((s) => s.trim()).filter(Boolean);\n\n // Janela de páginas: mostra até 5 com elipses simplificada\n const window_: number[] = [];\n for (let p = Math.max(1, current - 2); p <= Math.min(pages, current + 2); p++) window_.push(p);\n\n const from = total0(current, this.rows);\n const to = Math.min(current * this.rows, this.total);\n\n this.setTemplate(`\n <span class=\"info\">${this.total ? `${from}-${to} de ${this.total}` : '0 itens'}</span>\n <button type=\"button\" class=\"nav\" part=\"prev\" data-go=\"${current - 1}\" ${current <= 1 ? 'disabled' : ''}
|
|
1
|
+
{"version":3,"file":"pagination.js","sources":["../../../src/components/pagination/pagination.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 '../select';\n\n/**\n * <fx-pagination> — Paginação standalone.\n *\n * Atributos: page (atual), total (itens), rows (por página), rows-options,\n * position (left|center|right).\n * Evento: `page-change` (composed, detail: { page, rows }).\n */\nexport class FxPagination extends FxElement {\n static override styles = css`\n ${FENIX_ICON_BASE_CSS}\n :host {\n display: flex;\n align-items: center;\n gap: var(--fx-space-sm);\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n color: var(--fx-text-default);\n }\n :host([position='center']) { justify-content: center; }\n :host([position='right']) { justify-content: flex-end; }\n .nav {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--fx-size-sm);\n height: var(--fx-size-sm);\n border: 1px solid var(--fx-border-default);\n background: var(--fx-surface-background);\n color: var(--fx-text-default);\n font-family: inherit;\n font-size: calc(var(--fx-font-size) - 2px);\n border-radius: var(--fx-radius-sm);\n cursor: pointer;\n padding: 0 var(--fx-space-sm);\n transition:\n border-color var(--fx-motion-duration-fast) var(--fx-motion-easing),\n background var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n /* O glifo precisa de 1px a mais que o dígito: a altura de traço do ícone é\n maior que a do número, então igualar os dois font-size deixa a seta\n visivelmente maior que o texto. */\n .nav .fx-icon { font-size: calc(var(--fx-font-size) - 1px); }\n .nav:hover:not([disabled]):not(.active) { border-color: var(--fx-color-primary); color: var(--fx-color-primary); }\n .nav[disabled] { opacity: 0.5; cursor: not-allowed; }\n .nav.active {\n background: var(--fx-color-primary);\n border-color: var(--fx-color-primary);\n color: #fff;\n font-weight: 600;\n }\n .info { color: var(--fx-text-muted); font-size: calc(var(--fx-font-size) - 2px); }\n fx-select { vertical-align: middle; }\n fx-select::part(trigger) {\n min-width: 64px !important;\n min-height: var(--fx-size-sm) !important;\n padding: 0 var(--fx-space-sm) !important;\n border-radius: var(--fx-radius-sm) !important;\n font-size: calc(var(--fx-font-size) - 2px) !important;\n }\n `;\n\n static override get observedAttributes(): string[] {\n return ['page', 'total', 'rows', 'rows-options', 'position'];\n }\n\n get page(): number { return Number(this.getAttr('page', '1')) || 1; }\n set page(v: number) { this.setAttribute('page', String(v)); }\n get total(): number { return Number(this.getAttr('total', '0')) || 0; }\n set total(v: number) { this.setAttribute('total', String(v)); }\n get rows(): number { return Number(this.getAttr('rows', '10')) || 10; }\n set rows(v: number) { this.setAttribute('rows', String(v)); }\n\n private get pages(): number {\n return Math.max(1, Math.ceil(this.total / this.rows));\n }\n\n protected override connectedCallback(): void {\n // O pager usa glifos da Fenix Icons: garante o @font-face mesmo quando o\n // app não importou `@wrrdev/fenix-ui/icons` (idempotente).\n loadFenixIconsFont();\n super.connectedCallback();\n }\n\n protected override render(): void {\n const pages = this.pages;\n const current = Math.min(Math.max(1, this.page), pages);\n const opts = (this.getAttr('rows-options', '5,10,20,50') || '').split(',').map((s) => s.trim()).filter(Boolean);\n\n // Janela de páginas: mostra até 5 com elipses simplificada\n const window_: number[] = [];\n for (let p = Math.max(1, current - 2); p <= Math.min(pages, current + 2); p++) window_.push(p);\n\n const from = total0(current, this.rows);\n const to = Math.min(current * this.rows, this.total);\n\n this.setTemplate(`\n <span class=\"info\">${this.total ? `${from}-${to} de ${this.total}` : '0 itens'}</span>\n <button type=\"button\" class=\"nav\" part=\"first\" aria-label=\"Primeira página\" data-go=\"1\" ${current <= 1 ? 'disabled' : ''}>${fenixIconHtml('first_page')}</button>\n <button type=\"button\" class=\"nav\" part=\"prev\" aria-label=\"Página anterior\" data-go=\"${current - 1}\" ${current <= 1 ? 'disabled' : ''}>${fenixIconHtml('navigate_before')}</button>\n ${window_[0] !== 1 && pages > 5 ? `<button type=\"button\" class=\"nav\" data-go=\"1\">1</button>` : ''}\n ${window_.map((p) => `<button type=\"button\" class=\"nav ${p === current ? 'active' : ''}\" data-go=\"${p}\">${p}</button>`).join('')}\n ${window_[window_.length - 1] !== pages && pages > 5 ? `<button type=\"button\" class=\"nav\" data-go=\"${pages}\">${pages}</button>` : ''}\n <button type=\"button\" class=\"nav\" part=\"next\" aria-label=\"Próxima página\" data-go=\"${current + 1}\" ${current >= pages ? 'disabled' : ''}>${fenixIconHtml('navigate_next')}</button>\n <button type=\"button\" class=\"nav\" part=\"last\" aria-label=\"Última página\" data-go=\"${pages}\" ${current >= pages ? 'disabled' : ''}>${fenixIconHtml('last_page')}</button>\n ${opts.length ? `\n <fx-select class=\"rows-sel\" part=\"rows\" size=\"sm\" value=\"${this.rows}\" aria-label=\"Itens por página\">\n ${opts.map((o) => `<option value=\"${esc(o)}\">${esc(o)}</option>`).join('')}\n </fx-select>` : ''}\n `);\n\n function total0(page: number, rows: number): number {\n return (page - 1) * rows + 1;\n }\n\n this.root.querySelectorAll<HTMLButtonElement>('.nav[data-go]').forEach((btn) => {\n btn.addEventListener('click', () => {\n const target = Number(btn.dataset.go);\n if (target >= 1 && target <= pages && target !== current) {\n this.page = target;\n this.emit();\n }\n });\n });\n const sel = this.root.querySelector<HTMLElement>('fx-select.rows-sel');\n sel?.addEventListener('change', (e) => {\n const value = Number((e as CustomEvent).detail?.value);\n if (!value || value === this.rows) return;\n this.rows = value;\n this.page = 1;\n this.emit();\n });\n }\n\n private emit(): void {\n this.dispatchEvent(new CustomEvent('page-change', {\n bubbles: true, composed: true,\n detail: { page: this.page, rows: this.rows },\n }));\n }\n}\n\nexport function defineFxPagination(): typeof FxPagination {\n return defineElement('fx-pagination', FxPagination);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -7,20 +7,29 @@ import "../select";
|
|
|
7
7
|
*
|
|
8
8
|
* Colunas (light DOM):
|
|
9
9
|
* <fx-column field="nome" header="Nome" sortable filterable align="left">
|
|
10
|
-
* opcional: <
|
|
10
|
+
* opcional: <fx-cell>R$ {{value}}</fx-cell> para render customizado
|
|
11
11
|
* ou conteúdo direto (HTML + {{ }}) como template alternativo
|
|
12
12
|
* </fx-column>
|
|
13
13
|
*
|
|
14
|
+
* IMPORTANTE (frameworks): use <fx-cell>, NÃO <template>. A tag nativa
|
|
15
|
+
* <template> é reservada — o Vue 3 a transforma em bloco do compilador e o
|
|
16
|
+
* React gerencia os filhos, então o innerHTML chega vazio. <fx-cell> é uma
|
|
17
|
+
* tag própria, sem significado especial para nenhum framework. A forma
|
|
18
|
+
* antiga com <template> continua funcionando (compatibilidade) e há ainda
|
|
19
|
+
* a opção `template="#id"` apontando para um <template> externo.
|
|
20
|
+
*
|
|
14
21
|
* Toolbar (opcional):
|
|
15
|
-
* <
|
|
22
|
+
* <fx-toolbar>
|
|
16
23
|
* <input data-search-fields="nome,cargo" placeholder="Buscar…">
|
|
17
24
|
* <button>Nova ação</button>
|
|
18
|
-
* </
|
|
25
|
+
* </fx-toolbar>
|
|
26
|
+
* (legado ainda aceito: <template slot="toolbar">…</template>)
|
|
19
27
|
*
|
|
20
28
|
* Dados: propriedade `data` (array de objetos) ou atributo `data` (JSON).
|
|
21
29
|
*
|
|
22
30
|
* Atributos: pagination, rows, rows-options, pagination-position (left|center|right),
|
|
23
|
-
* striped, empty-message, lazy, total, loading, loading-message
|
|
31
|
+
* striped, hover, empty-message, lazy, total, loading, loading-message,
|
|
32
|
+
* sort-field, sort-order.
|
|
24
33
|
* Eventos: page-change, sort-change, row-click, filter-change (todos composed).
|
|
25
34
|
*/
|
|
26
35
|
export declare class FxTable extends FxElement {
|
|
@@ -42,9 +51,34 @@ export declare class FxTable extends FxElement {
|
|
|
42
51
|
set rowsPerPage(value: number);
|
|
43
52
|
get lazy(): boolean;
|
|
44
53
|
private get columns();
|
|
54
|
+
/**
|
|
55
|
+
* Resolve o template de célula de uma `<fx-column>`, em ordem de prioridade:
|
|
56
|
+
*
|
|
57
|
+
* 1. `<fx-cell>` — **recomendado**. Tag própria, sem significado especial
|
|
58
|
+
* para Vue/React/Angular/Svelte: nenhum framework a intercepta, então o
|
|
59
|
+
* `innerHTML` sempre chega intacto ao componente. O light DOM do
|
|
60
|
+
* `<fx-table>` não é exibido (a tabela não usa `<slot>`), então o
|
|
61
|
+
* conteúdo-fonte nunca aparece na tela.
|
|
62
|
+
* 2. `template="#id"` (ou `template="id"`) — aponta para um `<template>`
|
|
63
|
+
* declarado **fora** da tabela, útil em React/Angular, onde é mais
|
|
64
|
+
* simples manter o markup num único lugar.
|
|
65
|
+
* 3. `<template>` dentro da coluna — forma **legada**, mantida por
|
|
66
|
+
* compatibilidade. Em Vue 3 ela vira um bloco do compilador e em React os
|
|
67
|
+
* filhos são gerenciados pelo framework; nos dois casos o `innerHTML`
|
|
68
|
+
* chega vazio e a célula não renderiza. Prefira `<fx-cell>`.
|
|
69
|
+
*/
|
|
70
|
+
private _resolveCellTemplate;
|
|
45
71
|
private get toolbarTemplate();
|
|
72
|
+
/**
|
|
73
|
+
* Resolve `ref` para um elemento: `#id`/`id` ⇒ `getElementById` no
|
|
74
|
+
* documento; qualquer outro valor é tratado como seletor CSS relativo à
|
|
75
|
+
* própria tabela. Valores inválidos devolvem `null` (sem exceção).
|
|
76
|
+
*/
|
|
77
|
+
private _resolveRef;
|
|
46
78
|
protected connectedCallback(): void;
|
|
47
79
|
protected disconnectedCallback(): void;
|
|
80
|
+
/** Ordenação inicial via `sort-field` / `sort-order` (documentados na API). */
|
|
81
|
+
private _parseSortAttributes;
|
|
48
82
|
private _parseDataAttribute;
|
|
49
83
|
private computeRows;
|
|
50
84
|
private _getSearchFields;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/components/table/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/components/table/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAO5C,OAAO,WAAW,CAAC;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACrC,OAAgB,MAAM,SAiNpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAiBjD;IAED,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,eAAe,CAAC,CAAmB;IAE3C,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAEpC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAGxC;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;IAED,IAAI,WAAW,IAAI,MAAM,CAGxB;IACD,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAE5B;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,OAAO,KAAK,OAAO,GAuBlB;IAED;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,oBAAoB;IAW5B,OAAO,KAAK,eAAe,GAU1B;IAED;;;;OAIG;IACH,OAAO,CAAC,WAAW;cAeA,iBAAiB,IAAI,IAAI;cAWzB,oBAAoB,IAAI,IAAI;IAK/C,+EAA+E;IAC/E,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,WAAW;IAiDnB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,QAAQ;cAaG,MAAM,IAAI,IAAI;IAyNjC,OAAO,CAAC,qBAAqB;CAM7B;AAED,wBAAgB,aAAa,IAAI,OAAO,OAAO,CAE9C"}
|
|
@@ -2,6 +2,8 @@ import { FxElement } from "../../core/base.js";
|
|
|
2
2
|
import { css } from "../../core/css.js";
|
|
3
3
|
import { defineElement } from "../../core/define.js";
|
|
4
4
|
import { esc } from "../../core/sanitize.js";
|
|
5
|
+
import { FENIX_ICON_BASE_CSS, fenixIconHtml } from "../../icons/base-css.js";
|
|
6
|
+
import { loadFenixIconsFont } from "../../icons/font.js";
|
|
5
7
|
import { renderCell } from "./expr.js";
|
|
6
8
|
import "../select/index.js";
|
|
7
9
|
const _FxTable = class _FxTable extends FxElement {
|
|
@@ -20,12 +22,16 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
20
22
|
"rows",
|
|
21
23
|
"rows-options",
|
|
22
24
|
"striped",
|
|
25
|
+
"hover",
|
|
26
|
+
"toolbar",
|
|
23
27
|
"empty-message",
|
|
24
28
|
"pagination-position",
|
|
25
29
|
"lazy",
|
|
26
30
|
"total",
|
|
27
31
|
"loading",
|
|
28
|
-
"loading-message"
|
|
32
|
+
"loading-message",
|
|
33
|
+
"sort-field",
|
|
34
|
+
"sort-order"
|
|
29
35
|
];
|
|
30
36
|
}
|
|
31
37
|
get data() {
|
|
@@ -54,7 +60,7 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
54
60
|
}
|
|
55
61
|
get columns() {
|
|
56
62
|
return [...this.querySelectorAll("fx-column")].map((c) => {
|
|
57
|
-
const tpl =
|
|
63
|
+
const tpl = this._resolveCellTemplate(c);
|
|
58
64
|
const direct = tpl ? void 0 : c.innerHTML.trim();
|
|
59
65
|
const align = c.getAttribute("align") ?? "left";
|
|
60
66
|
return {
|
|
@@ -68,13 +74,65 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
68
74
|
};
|
|
69
75
|
});
|
|
70
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolve o template de célula de uma `<fx-column>`, em ordem de prioridade:
|
|
79
|
+
*
|
|
80
|
+
* 1. `<fx-cell>` — **recomendado**. Tag própria, sem significado especial
|
|
81
|
+
* para Vue/React/Angular/Svelte: nenhum framework a intercepta, então o
|
|
82
|
+
* `innerHTML` sempre chega intacto ao componente. O light DOM do
|
|
83
|
+
* `<fx-table>` não é exibido (a tabela não usa `<slot>`), então o
|
|
84
|
+
* conteúdo-fonte nunca aparece na tela.
|
|
85
|
+
* 2. `template="#id"` (ou `template="id"`) — aponta para um `<template>`
|
|
86
|
+
* declarado **fora** da tabela, útil em React/Angular, onde é mais
|
|
87
|
+
* simples manter o markup num único lugar.
|
|
88
|
+
* 3. `<template>` dentro da coluna — forma **legada**, mantida por
|
|
89
|
+
* compatibilidade. Em Vue 3 ela vira um bloco do compilador e em React os
|
|
90
|
+
* filhos são gerenciados pelo framework; nos dois casos o `innerHTML`
|
|
91
|
+
* chega vazio e a célula não renderiza. Prefira `<fx-cell>`.
|
|
92
|
+
*/
|
|
93
|
+
_resolveCellTemplate(c) {
|
|
94
|
+
const cell = c.querySelector(":scope > fx-cell");
|
|
95
|
+
if (cell) return cell;
|
|
96
|
+
const ref = c.getAttribute("template");
|
|
97
|
+
if (ref) {
|
|
98
|
+
const alvo = this._resolveRef(ref);
|
|
99
|
+
if (alvo) return alvo;
|
|
100
|
+
}
|
|
101
|
+
return c.querySelector("template");
|
|
102
|
+
}
|
|
71
103
|
get toolbarTemplate() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
);
|
|
104
|
+
const bar = this.querySelector(":scope > fx-toolbar");
|
|
105
|
+
if (bar) return bar;
|
|
106
|
+
const ref = this.getAttr("toolbar");
|
|
107
|
+
if (ref) {
|
|
108
|
+
const alvo = this._resolveRef(ref);
|
|
109
|
+
if (alvo) return alvo;
|
|
110
|
+
}
|
|
111
|
+
return this.querySelector('template[slot="toolbar"]');
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolve `ref` para um elemento: `#id`/`id` ⇒ `getElementById` no
|
|
115
|
+
* documento; qualquer outro valor é tratado como seletor CSS relativo à
|
|
116
|
+
* própria tabela. Valores inválidos devolvem `null` (sem exceção).
|
|
117
|
+
*/
|
|
118
|
+
_resolveRef(ref) {
|
|
119
|
+
const valor = ref.trim();
|
|
120
|
+
if (!valor) return null;
|
|
121
|
+
const doc = this.ownerDocument;
|
|
122
|
+
if (valor.startsWith("#")) return doc?.getElementById(valor.slice(1)) ?? null;
|
|
123
|
+
if (/^[.#[>]/.test(valor)) {
|
|
124
|
+
try {
|
|
125
|
+
return this.querySelector(valor);
|
|
126
|
+
} catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return doc?.getElementById(valor) ?? null;
|
|
75
131
|
}
|
|
76
132
|
connectedCallback() {
|
|
77
133
|
this._parseDataAttribute();
|
|
134
|
+
this._parseSortAttributes();
|
|
135
|
+
loadFenixIconsFont();
|
|
78
136
|
super.connectedCallback();
|
|
79
137
|
this._columnObserver = new MutationObserver(() => this.render());
|
|
80
138
|
this._columnObserver.observe(this, { childList: true, subtree: true });
|
|
@@ -83,6 +141,13 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
83
141
|
this._columnObserver?.disconnect();
|
|
84
142
|
super.disconnectedCallback();
|
|
85
143
|
}
|
|
144
|
+
/** Ordenação inicial via `sort-field` / `sort-order` (documentados na API). */
|
|
145
|
+
_parseSortAttributes() {
|
|
146
|
+
const field = this.getAttr("sort-field");
|
|
147
|
+
if (field) this.sortField = field;
|
|
148
|
+
const order = this.getAttr("sort-order");
|
|
149
|
+
if (order === "asc" || order === "desc") this.sortDir = order === "asc" ? 1 : -1;
|
|
150
|
+
}
|
|
86
151
|
_parseDataAttribute() {
|
|
87
152
|
if (!this._data.length) {
|
|
88
153
|
try {
|
|
@@ -166,11 +231,11 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
166
231
|
const pagerHtml = !paginated ? "" : `
|
|
167
232
|
<div class="pager" part="pager">
|
|
168
233
|
<span class="info">Página ${this.page + 1} de ${pages} · ${total} registros</span>
|
|
169
|
-
<button type="button" class="pg-btn" data-pg="first" ${this.page === 0 ? "disabled" : ""}
|
|
170
|
-
<button type="button" class="pg-btn" data-pg="prev" ${this.page === 0 ? "disabled" : ""}
|
|
234
|
+
<button type="button" class="pg-btn" part="first" aria-label="Primeira página" data-pg="first" ${this.page === 0 ? "disabled" : ""}>${fenixIconHtml("first_page")}</button>
|
|
235
|
+
<button type="button" class="pg-btn" part="prev" aria-label="Página anterior" data-pg="prev" ${this.page === 0 ? "disabled" : ""}>${fenixIconHtml("navigate_before")}</button>
|
|
171
236
|
${Array.from({ length: pages }, (_, p) => `<button type="button" class="pg-btn" data-pg="${p}" aria-current="${p === this.page}">${p + 1}</button>`).join("")}
|
|
172
|
-
<button type="button" class="pg-btn" data-pg="next" ${this.page >= pages - 1 ? "disabled" : ""}
|
|
173
|
-
<button type="button" class="pg-btn" data-pg="last" ${this.page >= pages - 1 ? "disabled" : ""}
|
|
237
|
+
<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>
|
|
238
|
+
<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>
|
|
174
239
|
<label class="info">${esc("Por página:")}
|
|
175
240
|
<fx-select class="rows-sel" size="sm" value="${this.rowsPerPage}" aria-label="Itens por página">${this.getAttr(
|
|
176
241
|
"rows-options",
|
|
@@ -307,6 +372,7 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
307
372
|
}
|
|
308
373
|
};
|
|
309
374
|
_FxTable.styles = css`
|
|
375
|
+
${FENIX_ICON_BASE_CSS}
|
|
310
376
|
:host {
|
|
311
377
|
display: block;
|
|
312
378
|
font-family: var(--fx-font-family);
|
|
@@ -383,17 +449,14 @@ _FxTable.styles = css`
|
|
|
383
449
|
transition: background-color var(--fx-motion-duration-fast)
|
|
384
450
|
var(--fx-motion-easing);
|
|
385
451
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
var(--fx-color-primary) 8%,
|
|
390
|
-
transparent
|
|
391
|
-
);
|
|
392
|
-
}
|
|
452
|
+
/* Listrado: alterna a superfície (cinza). Antes usava
|
|
453
|
+
--fx-surface-background, que é a MESMA cor do fundo do :host e
|
|
454
|
+
portanto pintava branco sobre branco (listrado invisível). */
|
|
393
455
|
:host([striped]) tbody tr:nth-child(even) {
|
|
394
|
-
background: var(--fx-surface-
|
|
456
|
+
background: var(--fx-surface-surface, #f8fafc);
|
|
395
457
|
}
|
|
396
|
-
|
|
458
|
+
/* O hover é opt-in via atributo (o clique na linha continua sempre ativo). */
|
|
459
|
+
:host([hover]) tbody tr:hover {
|
|
397
460
|
background: color-mix(
|
|
398
461
|
in srgb,
|
|
399
462
|
var(--fx-color-primary) 8%,
|
|
@@ -423,6 +486,9 @@ _FxTable.styles = css`
|
|
|
423
486
|
font-size: calc(var(--fx-font-size) - 2px);
|
|
424
487
|
}
|
|
425
488
|
.pg-btn {
|
|
489
|
+
display: inline-flex;
|
|
490
|
+
align-items: center;
|
|
491
|
+
justify-content: center;
|
|
426
492
|
min-width: var(--fx-size-sm);
|
|
427
493
|
height: var(--fx-size-sm);
|
|
428
494
|
font: inherit;
|
|
@@ -436,6 +502,12 @@ _FxTable.styles = css`
|
|
|
436
502
|
transition: border-color var(--fx-motion-duration-fast)
|
|
437
503
|
var(--fx-motion-easing);
|
|
438
504
|
}
|
|
505
|
+
/* O glifo precisa de 1px a mais que o dígito: a altura de traço do ícone é
|
|
506
|
+
maior que a do número, então igualar os dois font-size deixa a seta
|
|
507
|
+
visivelmente maior que o texto. */
|
|
508
|
+
.pg-btn .fx-icon {
|
|
509
|
+
font-size: calc(var(--fx-font-size) - 1px);
|
|
510
|
+
}
|
|
439
511
|
.pg-btn:hover:not(:disabled):not([aria-current="true"]) {
|
|
440
512
|
border-color: var(--fx-color-primary);
|
|
441
513
|
color: var(--fx-color-primary);
|