@wrrdev/fenix-ui 1.1.8 → 1.1.10
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 +60 -4
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +96 -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 +84 -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-cell-rules.d.ts +3 -0
- package/dist/docs/componentes/table-cell-rules.d.ts.map +1 -0
- 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/types.d.ts +9 -0
- package/dist/docs/types.d.ts.map +1 -1
- 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 +243 -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.10**, 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.10):
|
|
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,45 @@ 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
|
+
*
|
|
21
|
+
* ⚠️ REGRA DO v-pre (Vue) — o conteúdo do <fx-cell> é TEXTO CRU:
|
|
22
|
+
* {{ }} é avaliado pelo motor de expressões do <fx-table> (./expr.ts) e
|
|
23
|
+
* NÃO pelo Vue. Sem `v-pre`, o compilador do Vue consome a interpolação
|
|
24
|
+
* antes: emite `_toDisplayString(_ctx.value | _ctx.number)`, o conteúdo
|
|
25
|
+
* chega sem as chaves no innerHTML e o vue-tsc acusa `value`/`number`
|
|
26
|
+
* inexistentes no escopo. → SEMPRE use <fx-cell v-pre> no Vue.
|
|
27
|
+
*
|
|
28
|
+
* Cada framework tem a sua neutralização (todas verificadas por compilador):
|
|
29
|
+
* - Vue 3 / Nuxt .... v-pre <fx-cell v-pre>{{ value }}</fx-cell>
|
|
30
|
+
* - React / JSX ..... string JS <fx-cell>{'{{ value }}'}</fx-cell>
|
|
31
|
+
* (o `{{ }}` solto é ERRO de sintaxe no parser JSX)
|
|
32
|
+
* - Angular ......... ngNonBindable <fx-cell ngNonBindable>…</fx-cell>
|
|
33
|
+
* - Svelte .......... string JS ou entidades HTML
|
|
34
|
+
* {'{{ value }}'} ou {{ value }}
|
|
35
|
+
* - HTML/CDN/JSF/JSP nada <fx-cell>{{ value }}</fx-cell>
|
|
36
|
+
*
|
|
14
37
|
* Toolbar (opcional):
|
|
15
|
-
* <
|
|
38
|
+
* <fx-toolbar>
|
|
16
39
|
* <input data-search-fields="nome,cargo" placeholder="Buscar…">
|
|
17
40
|
* <button>Nova ação</button>
|
|
18
|
-
* </
|
|
41
|
+
* </fx-toolbar>
|
|
42
|
+
* (legado ainda aceito: <template slot="toolbar">…</template>)
|
|
19
43
|
*
|
|
20
44
|
* Dados: propriedade `data` (array de objetos) ou atributo `data` (JSON).
|
|
21
45
|
*
|
|
22
46
|
* Atributos: pagination, rows, rows-options, pagination-position (left|center|right),
|
|
23
|
-
* striped, empty-message, lazy, total, loading, loading-message
|
|
47
|
+
* striped, hover, empty-message, lazy, total, loading, loading-message,
|
|
48
|
+
* sort-field, sort-order.
|
|
24
49
|
* Eventos: page-change, sort-change, row-click, filter-change (todos composed).
|
|
25
50
|
*/
|
|
26
51
|
export declare class FxTable extends FxElement {
|
|
@@ -42,9 +67,40 @@ export declare class FxTable extends FxElement {
|
|
|
42
67
|
set rowsPerPage(value: number);
|
|
43
68
|
get lazy(): boolean;
|
|
44
69
|
private get columns();
|
|
70
|
+
/**
|
|
71
|
+
* Resolve o template de célula de uma `<fx-column>`, em ordem de prioridade:
|
|
72
|
+
*
|
|
73
|
+
* 1. `<fx-cell>` — **recomendado**. Tag própria, sem significado especial
|
|
74
|
+
* para Vue/React/Angular/Svelte: nenhum framework a intercepta, então o
|
|
75
|
+
* `innerHTML` sempre chega intacto ao componente. O light DOM do
|
|
76
|
+
* `<fx-table>` não é exibido (a tabela não usa `<slot>`), então o
|
|
77
|
+
* conteúdo-fonte nunca aparece na tela.
|
|
78
|
+
*
|
|
79
|
+
* ⚠️ O conteúdo é TEXTO CRU para o motor de expressões: o `{{ }}` NÃO é
|
|
80
|
+
* interpretado pelo framework hospedeiro. Cada um precisa neutralizar a
|
|
81
|
+
* própria sintaxe antes (ver o cabeçalho do arquivo): `v-pre` no Vue,
|
|
82
|
+
* `{'{{ }}'}` no JSX, `ngNonBindable` no Angular, string/entidades no
|
|
83
|
+
* Svelte. Sem isso o template não chega ao `renderCell`.
|
|
84
|
+
* 2. `template="#id"` (ou `template="id"`) — aponta para um `<template>`
|
|
85
|
+
* declarado **fora** da tabela, útil em React/Angular, onde é mais
|
|
86
|
+
* simples manter o markup num único lugar.
|
|
87
|
+
* 3. `<template>` dentro da coluna — forma **legada**, mantida por
|
|
88
|
+
* compatibilidade. Em Vue 3 ela vira um bloco do compilador e em React os
|
|
89
|
+
* filhos são gerenciados pelo framework; nos dois casos o `innerHTML`
|
|
90
|
+
* chega vazio e a célula não renderiza. Prefira `<fx-cell>`.
|
|
91
|
+
*/
|
|
92
|
+
private _resolveCellTemplate;
|
|
45
93
|
private get toolbarTemplate();
|
|
94
|
+
/**
|
|
95
|
+
* Resolve `ref` para um elemento: `#id`/`id` ⇒ `getElementById` no
|
|
96
|
+
* documento; qualquer outro valor é tratado como seletor CSS relativo à
|
|
97
|
+
* própria tabela. Valores inválidos devolvem `null` (sem exceção).
|
|
98
|
+
*/
|
|
99
|
+
private _resolveRef;
|
|
46
100
|
protected connectedCallback(): void;
|
|
47
101
|
protected disconnectedCallback(): void;
|
|
102
|
+
/** Ordenação inicial via `sort-field` / `sort-order` (documentados na API). */
|
|
103
|
+
private _parseSortAttributes;
|
|
48
104
|
private _parseDataAttribute;
|
|
49
105
|
private computeRows;
|
|
50
106
|
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+CG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACrC,OAAgB,MAAM,SAiNpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAiBjD;IAED,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,eAAe,CAAC,CAAmB;IAE3C,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAEpC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAGxC;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;IAED,IAAI,WAAW,IAAI,MAAM,CAGxB;IACD,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAE5B;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,OAAO,KAAK,OAAO,GAuBlB;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,oBAAoB;IAW5B,OAAO,KAAK,eAAe,GAU1B;IAED;;;;OAIG;IACH,OAAO,CAAC,WAAW;cAeA,iBAAiB,IAAI,IAAI;cAWzB,oBAAoB,IAAI,IAAI;IAK/C,+EAA+E;IAC/E,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,WAAW;IAiDnB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,QAAQ;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,71 @@ 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
|
+
*
|
|
86
|
+
* ⚠️ O conteúdo é TEXTO CRU para o motor de expressões: o `{{ }}` NÃO é
|
|
87
|
+
* interpretado pelo framework hospedeiro. Cada um precisa neutralizar a
|
|
88
|
+
* própria sintaxe antes (ver o cabeçalho do arquivo): `v-pre` no Vue,
|
|
89
|
+
* `{'{{ }}'}` no JSX, `ngNonBindable` no Angular, string/entidades no
|
|
90
|
+
* Svelte. Sem isso o template não chega ao `renderCell`.
|
|
91
|
+
* 2. `template="#id"` (ou `template="id"`) — aponta para um `<template>`
|
|
92
|
+
* declarado **fora** da tabela, útil em React/Angular, onde é mais
|
|
93
|
+
* simples manter o markup num único lugar.
|
|
94
|
+
* 3. `<template>` dentro da coluna — forma **legada**, mantida por
|
|
95
|
+
* compatibilidade. Em Vue 3 ela vira um bloco do compilador e em React os
|
|
96
|
+
* filhos são gerenciados pelo framework; nos dois casos o `innerHTML`
|
|
97
|
+
* chega vazio e a célula não renderiza. Prefira `<fx-cell>`.
|
|
98
|
+
*/
|
|
99
|
+
_resolveCellTemplate(c) {
|
|
100
|
+
const cell = c.querySelector(":scope > fx-cell");
|
|
101
|
+
if (cell) return cell;
|
|
102
|
+
const ref = c.getAttribute("template");
|
|
103
|
+
if (ref) {
|
|
104
|
+
const alvo = this._resolveRef(ref);
|
|
105
|
+
if (alvo) return alvo;
|
|
106
|
+
}
|
|
107
|
+
return c.querySelector("template");
|
|
108
|
+
}
|
|
71
109
|
get toolbarTemplate() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
);
|
|
110
|
+
const bar = this.querySelector(":scope > fx-toolbar");
|
|
111
|
+
if (bar) return bar;
|
|
112
|
+
const ref = this.getAttr("toolbar");
|
|
113
|
+
if (ref) {
|
|
114
|
+
const alvo = this._resolveRef(ref);
|
|
115
|
+
if (alvo) return alvo;
|
|
116
|
+
}
|
|
117
|
+
return this.querySelector('template[slot="toolbar"]');
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolve `ref` para um elemento: `#id`/`id` ⇒ `getElementById` no
|
|
121
|
+
* documento; qualquer outro valor é tratado como seletor CSS relativo à
|
|
122
|
+
* própria tabela. Valores inválidos devolvem `null` (sem exceção).
|
|
123
|
+
*/
|
|
124
|
+
_resolveRef(ref) {
|
|
125
|
+
const valor = ref.trim();
|
|
126
|
+
if (!valor) return null;
|
|
127
|
+
const doc = this.ownerDocument;
|
|
128
|
+
if (valor.startsWith("#")) return doc?.getElementById(valor.slice(1)) ?? null;
|
|
129
|
+
if (/^[.#[>]/.test(valor)) {
|
|
130
|
+
try {
|
|
131
|
+
return this.querySelector(valor);
|
|
132
|
+
} catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return doc?.getElementById(valor) ?? null;
|
|
75
137
|
}
|
|
76
138
|
connectedCallback() {
|
|
77
139
|
this._parseDataAttribute();
|
|
140
|
+
this._parseSortAttributes();
|
|
141
|
+
loadFenixIconsFont();
|
|
78
142
|
super.connectedCallback();
|
|
79
143
|
this._columnObserver = new MutationObserver(() => this.render());
|
|
80
144
|
this._columnObserver.observe(this, { childList: true, subtree: true });
|
|
@@ -83,6 +147,13 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
83
147
|
this._columnObserver?.disconnect();
|
|
84
148
|
super.disconnectedCallback();
|
|
85
149
|
}
|
|
150
|
+
/** Ordenação inicial via `sort-field` / `sort-order` (documentados na API). */
|
|
151
|
+
_parseSortAttributes() {
|
|
152
|
+
const field = this.getAttr("sort-field");
|
|
153
|
+
if (field) this.sortField = field;
|
|
154
|
+
const order = this.getAttr("sort-order");
|
|
155
|
+
if (order === "asc" || order === "desc") this.sortDir = order === "asc" ? 1 : -1;
|
|
156
|
+
}
|
|
86
157
|
_parseDataAttribute() {
|
|
87
158
|
if (!this._data.length) {
|
|
88
159
|
try {
|
|
@@ -166,11 +237,11 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
166
237
|
const pagerHtml = !paginated ? "" : `
|
|
167
238
|
<div class="pager" part="pager">
|
|
168
239
|
<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" : ""}
|
|
240
|
+
<button type="button" class="pg-btn" part="first" aria-label="Primeira página" data-pg="first" ${this.page === 0 ? "disabled" : ""}>${fenixIconHtml("first_page")}</button>
|
|
241
|
+
<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
242
|
${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" : ""}
|
|
243
|
+
<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>
|
|
244
|
+
<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
245
|
<label class="info">${esc("Por página:")}
|
|
175
246
|
<fx-select class="rows-sel" size="sm" value="${this.rowsPerPage}" aria-label="Itens por página">${this.getAttr(
|
|
176
247
|
"rows-options",
|
|
@@ -307,6 +378,7 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
307
378
|
}
|
|
308
379
|
};
|
|
309
380
|
_FxTable.styles = css`
|
|
381
|
+
${FENIX_ICON_BASE_CSS}
|
|
310
382
|
:host {
|
|
311
383
|
display: block;
|
|
312
384
|
font-family: var(--fx-font-family);
|
|
@@ -383,17 +455,14 @@ _FxTable.styles = css`
|
|
|
383
455
|
transition: background-color var(--fx-motion-duration-fast)
|
|
384
456
|
var(--fx-motion-easing);
|
|
385
457
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
var(--fx-color-primary) 8%,
|
|
390
|
-
transparent
|
|
391
|
-
);
|
|
392
|
-
}
|
|
458
|
+
/* Listrado: alterna a superfície (cinza). Antes usava
|
|
459
|
+
--fx-surface-background, que é a MESMA cor do fundo do :host e
|
|
460
|
+
portanto pintava branco sobre branco (listrado invisível). */
|
|
393
461
|
:host([striped]) tbody tr:nth-child(even) {
|
|
394
|
-
background: var(--fx-surface-
|
|
462
|
+
background: var(--fx-surface-surface, #f8fafc);
|
|
395
463
|
}
|
|
396
|
-
|
|
464
|
+
/* O hover é opt-in via atributo (o clique na linha continua sempre ativo). */
|
|
465
|
+
:host([hover]) tbody tr:hover {
|
|
397
466
|
background: color-mix(
|
|
398
467
|
in srgb,
|
|
399
468
|
var(--fx-color-primary) 8%,
|
|
@@ -423,6 +492,9 @@ _FxTable.styles = css`
|
|
|
423
492
|
font-size: calc(var(--fx-font-size) - 2px);
|
|
424
493
|
}
|
|
425
494
|
.pg-btn {
|
|
495
|
+
display: inline-flex;
|
|
496
|
+
align-items: center;
|
|
497
|
+
justify-content: center;
|
|
426
498
|
min-width: var(--fx-size-sm);
|
|
427
499
|
height: var(--fx-size-sm);
|
|
428
500
|
font: inherit;
|
|
@@ -436,6 +508,12 @@ _FxTable.styles = css`
|
|
|
436
508
|
transition: border-color var(--fx-motion-duration-fast)
|
|
437
509
|
var(--fx-motion-easing);
|
|
438
510
|
}
|
|
511
|
+
/* O glifo precisa de 1px a mais que o dígito: a altura de traço do ícone é
|
|
512
|
+
maior que a do número, então igualar os dois font-size deixa a seta
|
|
513
|
+
visivelmente maior que o texto. */
|
|
514
|
+
.pg-btn .fx-icon {
|
|
515
|
+
font-size: calc(var(--fx-font-size) - 1px);
|
|
516
|
+
}
|
|
439
517
|
.pg-btn:hover:not(:disabled):not([aria-current="true"]) {
|
|
440
518
|
border-color: var(--fx-color-primary);
|
|
441
519
|
color: var(--fx-color-primary);
|