@wrrdev/fenix-ui 1.0.13 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +332 -332
  2. package/dist/components/accordion/accordion.d.ts +1 -0
  3. package/dist/components/accordion/accordion.d.ts.map +1 -1
  4. package/dist/components/accordion/accordion.js +18 -13
  5. package/dist/components/accordion/accordion.js.map +1 -1
  6. package/dist/components/accordion/index.js.map +1 -1
  7. package/dist/components/alert/alert.d.ts.map +1 -1
  8. package/dist/components/alert/alert.js +2 -1
  9. package/dist/components/alert/alert.js.map +1 -1
  10. package/dist/components/alert/index.js.map +1 -1
  11. package/dist/components/autocomplete/autocomplete.d.ts +3 -0
  12. package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
  13. package/dist/components/autocomplete/autocomplete.js +28 -4
  14. package/dist/components/autocomplete/autocomplete.js.map +1 -1
  15. package/dist/components/autocomplete/index.js.map +1 -1
  16. package/dist/components/datepicker/datepicker.d.ts.map +1 -1
  17. package/dist/components/datepicker/datepicker.js +7 -6
  18. package/dist/components/datepicker/datepicker.js.map +1 -1
  19. package/dist/components/dialog/dialog.d.ts +4 -0
  20. package/dist/components/dialog/dialog.d.ts.map +1 -1
  21. package/dist/components/dialog/dialog.js +42 -4
  22. package/dist/components/dialog/dialog.js.map +1 -1
  23. package/dist/components/dialog/index.js.map +1 -1
  24. package/dist/components/drawer/drawer.d.ts +5 -0
  25. package/dist/components/drawer/drawer.d.ts.map +1 -1
  26. package/dist/components/drawer/drawer.js +49 -15
  27. package/dist/components/drawer/drawer.js.map +1 -1
  28. package/dist/components/dropdown/dropdown.d.ts +5 -0
  29. package/dist/components/dropdown/dropdown.d.ts.map +1 -1
  30. package/dist/components/dropdown/dropdown.js +34 -10
  31. package/dist/components/dropdown/dropdown.js.map +1 -1
  32. package/dist/components/input/input.d.ts.map +1 -1
  33. package/dist/components/input/input.js +4 -3
  34. package/dist/components/input/input.js.map +1 -1
  35. package/dist/components/knob/index.js.map +1 -1
  36. package/dist/components/knob/knob.d.ts +2 -0
  37. package/dist/components/knob/knob.d.ts.map +1 -1
  38. package/dist/components/knob/knob.js +15 -3
  39. package/dist/components/knob/knob.js.map +1 -1
  40. package/dist/components/multiselect/multiselect.d.ts +2 -0
  41. package/dist/components/multiselect/multiselect.d.ts.map +1 -1
  42. package/dist/components/multiselect/multiselect.js +38 -8
  43. package/dist/components/multiselect/multiselect.js.map +1 -1
  44. package/dist/components/orderlist/orderlist.d.ts.map +1 -1
  45. package/dist/components/orderlist/orderlist.js +1 -3
  46. package/dist/components/orderlist/orderlist.js.map +1 -1
  47. package/dist/components/pagination/index.js.map +1 -1
  48. package/dist/components/pagination/pagination.d.ts +1 -0
  49. package/dist/components/pagination/pagination.d.ts.map +1 -1
  50. package/dist/components/pagination/pagination.js +19 -8
  51. package/dist/components/pagination/pagination.js.map +1 -1
  52. package/dist/components/picklist/picklist.d.ts.map +1 -1
  53. package/dist/components/picklist/picklist.js +1 -3
  54. package/dist/components/picklist/picklist.js.map +1 -1
  55. package/dist/components/progress/index.js.map +1 -1
  56. package/dist/components/progress/progress.d.ts.map +1 -1
  57. package/dist/components/progress/progress.js +2 -1
  58. package/dist/components/progress/progress.js.map +1 -1
  59. package/dist/components/radio/radio.js +1 -1
  60. package/dist/components/radio/radio.js.map +1 -1
  61. package/dist/components/select/select.d.ts +2 -0
  62. package/dist/components/select/select.d.ts.map +1 -1
  63. package/dist/components/select/select.js +36 -5
  64. package/dist/components/select/select.js.map +1 -1
  65. package/dist/components/skeleton/index.js.map +1 -1
  66. package/dist/components/skeleton/skeleton.d.ts +2 -0
  67. package/dist/components/skeleton/skeleton.d.ts.map +1 -1
  68. package/dist/components/skeleton/skeleton.js +7 -2
  69. package/dist/components/skeleton/skeleton.js.map +1 -1
  70. package/dist/components/table/expr.d.ts +38 -0
  71. package/dist/components/table/expr.d.ts.map +1 -0
  72. package/dist/components/table/expr.js +429 -0
  73. package/dist/components/table/expr.js.map +1 -0
  74. package/dist/components/table/table.d.ts +26 -8
  75. package/dist/components/table/table.d.ts.map +1 -1
  76. package/dist/components/table/table.js +402 -140
  77. package/dist/components/table/table.js.map +1 -1
  78. package/dist/components/tabs/tabs.d.ts.map +1 -1
  79. package/dist/components/tabs/tabs.js +3 -2
  80. package/dist/components/tabs/tabs.js.map +1 -1
  81. package/dist/components/textarea/index.js.map +1 -1
  82. package/dist/components/textarea/textarea.d.ts.map +1 -1
  83. package/dist/components/textarea/textarea.js +4 -3
  84. package/dist/components/textarea/textarea.js.map +1 -1
  85. package/dist/components/toast/index.js.map +1 -1
  86. package/dist/components/tooltip/directive.js.map +1 -1
  87. package/dist/components/tooltip/index.js.map +1 -1
  88. package/dist/components/tooltip/tooltip.d.ts.map +1 -1
  89. package/dist/components/tooltip/tooltip.js +2 -1
  90. package/dist/components/tooltip/tooltip.js.map +1 -1
  91. package/dist/core/jsx.js.map +1 -1
  92. package/dist/core/presets.js.map +1 -1
  93. package/dist/core/sanitize.d.ts +36 -0
  94. package/dist/core/sanitize.d.ts.map +1 -0
  95. package/dist/core/sanitize.js +37 -0
  96. package/dist/core/sanitize.js.map +1 -0
  97. package/dist/core/tokens.js.map +1 -1
  98. package/dist/core/vue.js.map +1 -1
  99. package/dist/docs/app.d.ts.map +1 -1
  100. package/dist/docs/componentes/table.doc.d.ts.map +1 -1
  101. package/dist/fenix-ui.esm.js.map +1 -1
  102. package/dist/fenix-ui.umd.min.js +339 -208
  103. package/dist/fenix-ui2.esm.js +1156 -243
  104. package/dist/fenix-ui2.esm.js.map +1 -1
  105. package/dist/fenix-ui3.esm.js +7 -1
  106. package/dist/fenix-ui3.esm.js.map +1 -1
  107. package/dist/index.d.ts +2 -1
  108. package/dist/index.d.ts.map +1 -1
  109. package/dist/index.js +4 -0
  110. package/dist/index.js.map +1 -1
  111. package/dist/plugins/auto-import.d.ts.map +1 -1
  112. package/dist/plugins/auto-import.js +7 -1
  113. package/dist/plugins/auto-import.js.map +1 -1
  114. package/package.json +212 -212
@@ -1,17 +1,46 @@
1
1
  import { FxElement } from "../../core/base.js";
2
2
  import { css } from "../../core/css.js";
3
3
  import { defineElement } from "../../core/define.js";
4
+ import { esc } from "../../core/sanitize.js";
5
+ import { renderCell } from "./expr.js";
6
+ import "../select/index.js";
4
7
  const _FxTable = class _FxTable extends FxElement {
5
8
  constructor() {
6
9
  super(...arguments);
7
- this.data = [];
10
+ this._data = [];
8
11
  this.page = 0;
9
12
  this.sortField = "";
10
13
  this.sortDir = 1;
11
14
  this.filters = {};
15
+ this.globalSearch = "";
12
16
  }
13
17
  static get observedAttributes() {
14
- return ["pagination", "rows", "rows-options", "striped", "empty-message", "pagination-position"];
18
+ return [
19
+ "pagination",
20
+ "rows",
21
+ "rows-options",
22
+ "striped",
23
+ "empty-message",
24
+ "pagination-position",
25
+ "lazy",
26
+ "total",
27
+ "loading",
28
+ "loading-message"
29
+ ];
30
+ }
31
+ get data() {
32
+ return this._data;
33
+ }
34
+ set data(value) {
35
+ this._data = Array.isArray(value) ? value : [];
36
+ this.render();
37
+ }
38
+ get total() {
39
+ return this._total ?? this._data.length;
40
+ }
41
+ set total(value) {
42
+ this._total = value;
43
+ this.setAttribute("total", String(value));
15
44
  }
16
45
  get rowsPerPage() {
17
46
  const r = Number(this.getAttr("rows", "10"));
@@ -20,57 +49,101 @@ const _FxTable = class _FxTable extends FxElement {
20
49
  set rowsPerPage(value) {
21
50
  this.setAttribute("rows", String(value));
22
51
  }
23
- /** Colunas declaradas no light DOM (<fx-column>). */
52
+ get lazy() {
53
+ return this.hasAttr("lazy");
54
+ }
24
55
  get columns() {
25
- return [...this.querySelectorAll("fx-column")].map((c) => ({
26
- field: c.getAttribute("field") ?? "",
27
- header: c.getAttribute("header") ?? c.getAttribute("field") ?? "",
28
- sortable: c.hasAttribute("sortable"),
29
- filterable: c.hasAttribute("filterable"),
30
- align: c.getAttribute("align") ?? "left",
31
- template: c.querySelector("template") ?? void 0
32
- }));
56
+ return [...this.querySelectorAll("fx-column")].map((c) => {
57
+ const tpl = c.querySelector("template");
58
+ const direct = tpl ? void 0 : c.innerHTML.trim();
59
+ const align = c.getAttribute("align") ?? "left";
60
+ return {
61
+ field: c.getAttribute("field") ?? "",
62
+ header: c.getAttribute("header") ?? c.getAttribute("field") ?? "",
63
+ sortable: c.hasAttribute("sortable"),
64
+ filterable: c.hasAttribute("filterable"),
65
+ align: ["left", "center", "right", "justify", "start", "end"].includes(align) ? align : "left",
66
+ template: tpl ?? void 0,
67
+ directTemplate: direct || void 0
68
+ };
69
+ });
70
+ }
71
+ get toolbarTemplate() {
72
+ return this.querySelector(
73
+ 'template[slot="toolbar"]'
74
+ );
33
75
  }
34
76
  connectedCallback() {
77
+ this._parseDataAttribute();
35
78
  super.connectedCallback();
36
- if (!this.data.length) {
79
+ this._columnObserver = new MutationObserver(() => this.render());
80
+ this._columnObserver.observe(this, { childList: true, subtree: true });
81
+ }
82
+ disconnectedCallback() {
83
+ this._columnObserver?.disconnect();
84
+ super.disconnectedCallback();
85
+ }
86
+ _parseDataAttribute() {
87
+ if (!this._data.length) {
37
88
  try {
38
89
  const json = this.getAttr("data");
39
- if (json) this.data = JSON.parse(json);
90
+ if (json) this._data = JSON.parse(json);
40
91
  } catch {
41
92
  }
42
93
  }
43
- new MutationObserver(() => this.render()).observe(this, { childList: true, subtree: true });
94
+ const totalAttr = this.getAttr("total");
95
+ if (totalAttr) {
96
+ const n = Number(totalAttr);
97
+ if (!isNaN(n)) this._total = n;
98
+ }
44
99
  }
45
- /** Pipeline: filtra → ordena → pagina. */
46
100
  computeRows() {
47
- let out = [...this.data];
101
+ let out = [...this._data];
48
102
  for (const [field, term] of Object.entries(this.filters)) {
49
103
  if (!term) continue;
50
104
  const t = term.toLowerCase();
51
- out = out.filter((r) => String(r[field] ?? "").toLowerCase().includes(t));
105
+ out = out.filter(
106
+ (r) => String(r[field] ?? "").toLowerCase().includes(t)
107
+ );
108
+ }
109
+ if (this.globalSearch) {
110
+ const t = this.globalSearch.toLowerCase();
111
+ const searchFields = this._getSearchFields();
112
+ out = out.filter((r) => {
113
+ const fields = searchFields.length ? searchFields : Object.keys(r);
114
+ return fields.some(
115
+ (f) => String(r[f] ?? "").toLowerCase().includes(t)
116
+ );
117
+ });
52
118
  }
53
- const total = out.length;
119
+ const total = this.lazy ? this.total : out.length;
54
120
  if (this.sortField) {
55
121
  out.sort((a, b) => {
56
122
  const av = a[this.sortField], bv = b[this.sortField];
57
- if (typeof av === "number" && typeof bv === "number") return (av - bv) * this.sortDir;
123
+ if (typeof av === "number" && typeof bv === "number")
124
+ return (av - bv) * this.sortDir;
58
125
  return String(av ?? "").localeCompare(String(bv ?? ""), "pt-BR") * this.sortDir;
59
126
  });
60
127
  }
61
- if (this.hasAttr("pagination")) {
128
+ if (this.hasAttr("pagination") && !this.lazy) {
62
129
  const start = this.page * this.rowsPerPage;
63
130
  out = out.slice(start, start + this.rowsPerPage);
64
131
  }
65
132
  return { rows: out, total };
66
133
  }
67
- /** Célula: template customizado ({{value}}, {{campo}}, {{row}}) ou valor direto. */
134
+ _getSearchFields() {
135
+ const input = this.root.querySelector(
136
+ "[data-search-fields]"
137
+ );
138
+ if (!input) return [];
139
+ const attr = input.getAttribute("data-search-fields");
140
+ if (attr === null) return [];
141
+ return attr.split(",").map((s) => s.trim()).filter(Boolean);
142
+ }
68
143
  cellHtml(col, row) {
69
- const value = row[col.field];
70
- if (col.template) {
71
- return col.template.innerHTML.replace(/\{\{(\w+)\}\}/g, (_, k) => k === "value" ? String(value ?? "") : k === "row" ? "" : String(row[k] ?? ""));
72
- }
73
- return String(value ?? "");
144
+ const template = col.template?.innerHTML ?? col.directTemplate;
145
+ if (template) return renderCell(template, row, col.field);
146
+ return esc(String(row[col.field] ?? ""));
74
147
  }
75
148
  render() {
76
149
  const cols = this.columns.filter((c) => c.field);
@@ -78,12 +151,18 @@ const _FxTable = class _FxTable extends FxElement {
78
151
  const paginated = this.hasAttr("pagination");
79
152
  const pages = Math.max(1, Math.ceil(total / this.rowsPerPage));
80
153
  if (this.page >= pages) this.page = pages - 1;
154
+ const toolbar = this.toolbarTemplate;
155
+ const toolbarHtml = toolbar ? `<div class="toolbar" part="toolbar">${toolbar.innerHTML}</div>` : "";
81
156
  const headHtml = cols.map((c) => {
82
- const ind = this.sortField === c.field ? this.sortDir === 1 ? "▲" : "▼" : "";
83
- return `<th part="header" class="${c.sortable ? "sortable" : ""}" style="text-align:${c.align}" data-field="${c.field}">${c.header}${ind ? `<span class="sort-ind">${ind}</span>` : ""}</th>`;
157
+ const isSorted = this.sortField === c.field;
158
+ const ind = isSorted ? this.sortDir === 1 ? "▲" : "▼" : c.sortable ? "⇅" : "";
159
+ const activeClass = isSorted ? "active" : "";
160
+ return `<th part="header" class="${c.sortable ? "sortable" : ""}" style="text-align:${c.align}" data-field="${esc(c.field)}">${esc(c.header)}${ind ? `<span class="sort-ind ${activeClass}">${ind}</span>` : ""}</th>`;
84
161
  }).join("");
85
- const filterRow = cols.some((c) => c.filterable) ? `<tr class="filter-row">${cols.map((c) => `<th style="text-align:${c.align}">${c.filterable ? `<input class="filter" data-field="${c.field}" placeholder="Filtrar…" value="${this.filters[c.field] ?? ""}">` : ""}</th>`).join("")}</tr>` : "";
86
- const bodyHtml = rows.length ? rows.map((row, i) => `<tr part="row" data-index="${i}">${cols.map((c) => `<td part="cell" style="text-align:${c.align}">${this.cellHtml(c, row)}</td>`).join("")}</tr>`).join("") : `<tr><td class="empty" colspan="${cols.length || 1}">${this.getAttr("empty-message", "Nenhum registro encontrado")}</td></tr>`;
162
+ const filterRow = cols.some((c) => c.filterable) ? `<tr class="filter-row">${cols.map((c) => `<th style="text-align:${c.align}">${c.filterable ? `<input class="filter" data-field="${esc(c.field)}" placeholder="${esc("Filtrar…")}" value="${esc(this.filters[c.field] ?? "")}">` : ""}</th>`).join("")}</tr>` : "";
163
+ const bodyHtml = rows.length ? rows.map(
164
+ (row, i) => `<tr part="row" data-index="${i}">${cols.map((c) => `<td part="cell" style="text-align:${c.align}">${this.cellHtml(c, row)}</td>`).join("")}</tr>`
165
+ ).join("") : `<tr><td class="empty" colspan="${cols.length || 1}">${esc(this.getAttr("empty-message", "Nenhum registro encontrado"))}</td></tr>`;
87
166
  const pagerHtml = !paginated ? "" : `
88
167
  <div class="pager" part="pager">
89
168
  <span class="info">Página ${this.page + 1} de ${pages} · ${total} registros</span>
@@ -92,43 +171,69 @@ const _FxTable = class _FxTable extends FxElement {
92
171
  ${Array.from({ length: pages }, (_, p) => `<button type="button" class="pg-btn" data-pg="${p}" aria-current="${p === this.page}">${p + 1}</button>`).join("")}
93
172
  <button type="button" class="pg-btn" data-pg="next" ${this.page >= pages - 1 ? "disabled" : ""}>›</button>
94
173
  <button type="button" class="pg-btn" data-pg="last" ${this.page >= pages - 1 ? "disabled" : ""}>»</button>
95
- <label class="info">Por página:
96
- <select class="rows-sel">${this.getAttr("rows-options", "5,10,20,50").split(",").map((n) => n.trim()).map((n) => `<option value="${n}" ${Number(n) === this.rowsPerPage ? "selected" : ""}>${n}</option>`).join("")}</select>
174
+ <label class="info">${esc("Por página:")}
175
+ <fx-select class="rows-sel" size="sm" value="${this.rowsPerPage}" aria-label="Itens por página">${this.getAttr(
176
+ "rows-options",
177
+ "5,10,20,50"
178
+ ).split(",").map((n) => n.trim()).map((n) => `<option value="${esc(n)}">${esc(n)}</option>`).join("")}</fx-select>
97
179
  </label>
98
180
  </div>`;
181
+ const loading = this.hasAttr("loading");
182
+ const loadingHtml = loading ? `<div class="loading-overlay" part="loading-overlay"><div class="tbl-spinner" part="spinner"></div><span class="loading-text">${esc(this.getAttr("loading-message", "Carregando…"))}</span></div>` : "";
99
183
  this.setTemplate(`
100
- <div class="scroll">
101
- <table part="table">
102
- <thead><tr>${headHtml}</tr>${filterRow}</thead>
103
- <tbody>${bodyHtml}</tbody>
104
- </table>
184
+ ${toolbarHtml}
185
+ <div class="table-wrap">
186
+ <div class="scroll">
187
+ <table part="table">
188
+ <thead><tr>${headHtml}</tr>${filterRow}</thead>
189
+ <tbody>${bodyHtml}</tbody>
190
+ </table>
191
+ </div>
192
+ ${loadingHtml}
105
193
  </div>
106
194
  ${pagerHtml}
107
195
  `);
108
196
  this.root.querySelectorAll("th.sortable").forEach((th) => {
109
197
  th.addEventListener("click", () => {
110
198
  const f = th.dataset.field;
111
- if (this.sortField === f) this.sortDir = this.sortDir === 1 ? -1 : 1;
199
+ if (this.sortField === f)
200
+ this.sortDir = this.sortDir === 1 ? -1 : 1;
112
201
  else {
113
202
  this.sortField = f;
114
203
  this.sortDir = 1;
115
204
  }
116
205
  this.render();
117
- this.dispatchEvent(new CustomEvent("sort-change", {
118
- bubbles: true,
119
- composed: true,
120
- detail: { field: this.sortField, direction: this.sortDir === 1 ? "asc" : "desc" }
121
- }));
206
+ this.dispatchEvent(
207
+ new CustomEvent("sort-change", {
208
+ bubbles: true,
209
+ composed: true,
210
+ detail: {
211
+ field: this.sortField,
212
+ direction: this.sortDir === 1 ? "asc" : "desc",
213
+ lazy: this.lazy
214
+ }
215
+ })
216
+ );
122
217
  });
123
218
  });
124
219
  this.root.querySelectorAll(".filter").forEach((inp) => {
125
220
  inp.addEventListener("input", () => {
126
- this.filters[inp.dataset.field] = inp.value;
127
- this.page = 0;
128
221
  const field = inp.dataset.field;
222
+ const value = inp.value;
129
223
  const pos = inp.selectionStart;
224
+ this.filters[field] = value;
225
+ this.page = 0;
130
226
  this.render();
131
- const again = this.root.querySelector(`.filter[data-field="${field}"]`);
227
+ this.dispatchEvent(
228
+ new CustomEvent("filter-change", {
229
+ bubbles: true,
230
+ composed: true,
231
+ detail: { field, value, lazy: this.lazy }
232
+ })
233
+ );
234
+ const again = this.root.querySelector(
235
+ `.filter[data-field="${esc(field)}"]`
236
+ );
132
237
  if (again) {
133
238
  again.focus();
134
239
  again.setSelectionRange(pos, pos);
@@ -136,117 +241,274 @@ const _FxTable = class _FxTable extends FxElement {
136
241
  });
137
242
  inp.addEventListener("click", (e) => e.stopPropagation());
138
243
  });
244
+ this.root.querySelectorAll("[data-search-fields]").forEach((inp) => {
245
+ inp.addEventListener("input", () => {
246
+ this.globalSearch = inp.value;
247
+ this.page = 0;
248
+ const pos = inp.selectionStart;
249
+ this.render();
250
+ const again = this.root.querySelector(
251
+ "[data-search-fields]"
252
+ );
253
+ if (again) {
254
+ again.focus();
255
+ again.setSelectionRange(pos, pos);
256
+ }
257
+ });
258
+ });
139
259
  this.root.querySelectorAll(".pg-btn").forEach((btn) => {
140
260
  btn.addEventListener("click", () => {
141
261
  const pg = btn.dataset.pg;
142
262
  this.page = pg === "first" ? 0 : pg === "prev" ? Math.max(0, this.page - 1) : pg === "next" ? Math.min(pages - 1, this.page + 1) : pg === "last" ? pages - 1 : Number(pg);
143
263
  this.render();
144
- this.dispatchEvent(new CustomEvent("page-change", {
145
- bubbles: true,
146
- composed: true,
147
- detail: { page: this.page + 1, pages, rowsPerPage: this.rowsPerPage, total }
148
- }));
264
+ this.dispatchEvent(
265
+ new CustomEvent("page-change", {
266
+ bubbles: true,
267
+ composed: true,
268
+ detail: {
269
+ page: this.page + 1,
270
+ pages,
271
+ rowsPerPage: this.rowsPerPage,
272
+ total,
273
+ lazy: this.lazy
274
+ }
275
+ })
276
+ );
149
277
  });
150
278
  });
151
- this.root.querySelector(".rows-sel")?.addEventListener("change", (e) => {
152
- this.rowsPerPage = Number(e.target.value);
279
+ this.root.querySelector("fx-select.rows-sel")?.addEventListener("change", (e) => {
280
+ const value = Number(e.detail?.value);
281
+ if (!value || value === this.rowsPerPage) return;
282
+ this.rowsPerPage = value;
153
283
  this.page = 0;
154
284
  this.render();
155
285
  });
156
286
  this.root.querySelectorAll("tbody tr[data-index]").forEach((tr) => {
157
287
  tr.addEventListener("click", () => {
158
- this.dispatchEvent(new CustomEvent("row-click", {
159
- bubbles: true,
160
- composed: true,
161
- detail: { row: rows[Number(tr.dataset.index)], index: Number(tr.dataset.index) }
162
- }));
288
+ this.dispatchEvent(
289
+ new CustomEvent("row-click", {
290
+ bubbles: true,
291
+ composed: true,
292
+ detail: {
293
+ row: rows[Number(tr.dataset.index)],
294
+ index: Number(tr.dataset.index)
295
+ }
296
+ })
297
+ );
163
298
  });
164
299
  });
300
+ this._restoreToolbarSearch();
301
+ }
302
+ _restoreToolbarSearch() {
303
+ const input = this.root.querySelector(
304
+ "[data-search-fields]"
305
+ );
306
+ if (input && this.globalSearch) input.value = this.globalSearch;
165
307
  }
166
308
  };
167
309
  _FxTable.styles = css`
168
- :host {
169
- display: block;
170
- font-family: var(--fx-font-family);
171
- font-size: var(--fx-font-size);
172
- color: var(--fx-text-default);
173
- }
174
- .scroll { overflow-x: auto; border: 1px solid var(--fx-border-default); border-radius: var(--fx-radius-md); }
175
- table { width: 100%; border-collapse: collapse; }
176
- th {
177
- text-align: left;
178
- padding: var(--fx-space-sm) var(--fx-space-md);
179
- background: var(--fx-surface-background);
180
- border-bottom: 2px solid var(--fx-border-default);
181
- white-space: nowrap;
182
- user-select: none;
183
- }
184
- th.sortable { cursor: pointer; }
185
- th.sortable:hover { color: var(--fx-color-primary); }
186
- .sort-ind { font-size: calc(var(--fx-font-size) - 4px); margin-left: var(--fx-space-3xs, 2px); }
187
- .filter-row th { padding: var(--fx-space-3xs, 4px) var(--fx-space-md) var(--fx-space-sm); border-bottom-width: 1px; }
188
- .filter {
189
- width: 100%;
190
- box-sizing: border-box;
191
- font: inherit;
192
- font-size: calc(var(--fx-font-size) - 2px);
193
- color: var(--fx-text-default);
194
- background: var(--fx-surface-background);
195
- border: 1px solid var(--fx-border-default);
196
- border-radius: var(--fx-radius-sm);
197
- padding: var(--fx-space-3xs, 4px) var(--fx-space-xs);
198
- outline: none;
199
- }
200
- .filter:focus { border-color: var(--fx-color-primary); box-shadow: var(--fx-effect-focus-ring, none); }
201
- td {
202
- padding: var(--fx-space-sm) var(--fx-space-md);
203
- border-bottom: 1px solid var(--fx-border-default);
204
- }
205
- tbody tr { cursor: pointer; transition: background-color var(--fx-motion-duration-fast) var(--fx-motion-easing); }
206
- tbody tr:hover { background: color-mix(in srgb, var(--fx-color-primary) 8%, transparent); }
207
- :host([striped]) tbody tr:nth-child(even) { background: var(--fx-surface-background); }
208
- :host([striped]) tbody tr:hover { background: color-mix(in srgb, var(--fx-color-primary) 8%, transparent); }
209
- .empty { text-align: center; padding: var(--fx-space-xl); color: var(--fx-text-muted); }
210
- .pager {
211
- display: flex;
212
- align-items: center;
213
- gap: var(--fx-space-sm);
214
- flex-wrap: wrap;
215
- padding: var(--fx-space-sm) 0;
216
- }
217
- :host([pagination-position='center']) .pager { justify-content: center; }
218
- :host([pagination-position='right']) .pager { justify-content: flex-end; }
219
- .pager .info { color: var(--fx-text-muted); font-size: calc(var(--fx-font-size) - 2px); }
220
- .pg-btn {
221
- font: inherit;
222
- font-size: calc(var(--fx-font-size) - 2px);
223
- color: var(--fx-text-default);
224
- background: var(--fx-surface-background);
225
- border: 1px solid var(--fx-border-default);
226
- border-radius: var(--fx-radius-sm);
227
- padding: var(--fx-space-3xs, 4px) var(--fx-space-sm);
228
- cursor: pointer;
229
- transition: border-color var(--fx-motion-duration-fast) var(--fx-motion-easing);
230
- }
231
- .pg-btn:hover:not(:disabled) { border-color: var(--fx-color-primary); color: var(--fx-color-primary); }
232
- .pg-btn:disabled { opacity: 0.5; cursor: not-allowed; }
233
- .pg-btn[aria-current='true'] {
234
- background: var(--fx-color-primary);
235
- border-color: var(--fx-color-primary);
236
- color: #fff;
237
- font-weight: var(--fx-font-weight);
238
- }
239
- .rows-sel {
240
- font: inherit;
241
- font-size: calc(var(--fx-font-size) - 2px);
242
- color: var(--fx-text-default);
243
- background: var(--fx-surface-background);
244
- border: 1px solid var(--fx-border-default);
245
- border-radius: var(--fx-radius-sm);
246
- padding: 2px var(--fx-space-xs);
247
- outline: none;
248
- }
249
- `;
310
+ :host {
311
+ display: block;
312
+ font-family: var(--fx-font-family);
313
+ font-size: var(--fx-font-size);
314
+ color: var(--fx-text-default);
315
+ background: var(--fx-surface-background);
316
+ }
317
+ .table-wrap {
318
+ position: relative;
319
+ }
320
+ .scroll {
321
+ overflow-x: auto;
322
+ border: 1px solid var(--fx-border-default);
323
+ border-radius: var(--fx-radius-md);
324
+ }
325
+ table {
326
+ width: 100%;
327
+ border-collapse: collapse;
328
+ }
329
+ th {
330
+ text-align: left;
331
+ padding: var(--fx-space-sm) var(--fx-space-md);
332
+ background: var(--fx-surface-surface);
333
+ border-bottom: 2px solid var(--fx-border-default);
334
+ white-space: nowrap;
335
+ user-select: none;
336
+ }
337
+ th.sortable {
338
+ cursor: pointer;
339
+ }
340
+ th.sortable:hover {
341
+ color: var(--fx-color-primary);
342
+ }
343
+ .sort-ind {
344
+ font-size: calc(var(--fx-font-size) - 4px);
345
+ margin-left: var(--fx-space-3xs, 2px);
346
+ opacity: 0.45;
347
+ transition: opacity var(--fx-motion-duration-fast)
348
+ var(--fx-motion-easing);
349
+ }
350
+ th.sortable:hover .sort-ind {
351
+ opacity: 0.85;
352
+ }
353
+ .sort-ind.active {
354
+ opacity: 1;
355
+ color: var(--fx-color-primary);
356
+ }
357
+ .filter-row th {
358
+ padding: var(--fx-space-3xs, 4px) var(--fx-space-md) var(--fx-space-sm);
359
+ border-bottom-width: 1px;
360
+ }
361
+ .filter {
362
+ width: 100%;
363
+ box-sizing: border-box;
364
+ font: inherit;
365
+ font-size: calc(var(--fx-font-size) - 2px);
366
+ color: var(--fx-text-default);
367
+ background: var(--fx-surface-background);
368
+ border: 1px solid var(--fx-border-default);
369
+ border-radius: var(--fx-radius-sm);
370
+ padding: var(--fx-space-3xs, 4px) var(--fx-space-xs);
371
+ outline: none;
372
+ }
373
+ .filter:focus {
374
+ border-color: var(--fx-color-primary);
375
+ box-shadow: var(--fx-effect-focus-ring, none);
376
+ }
377
+ td {
378
+ padding: var(--fx-space-sm) var(--fx-space-md);
379
+ border-bottom: 1px solid var(--fx-border-default);
380
+ }
381
+ tbody tr {
382
+ cursor: pointer;
383
+ transition: background-color var(--fx-motion-duration-fast)
384
+ var(--fx-motion-easing);
385
+ }
386
+ tbody tr:hover {
387
+ background: color-mix(
388
+ in srgb,
389
+ var(--fx-color-primary) 8%,
390
+ transparent
391
+ );
392
+ }
393
+ :host([striped]) tbody tr:nth-child(even) {
394
+ background: var(--fx-surface-background);
395
+ }
396
+ :host([striped]) tbody tr:hover {
397
+ background: color-mix(
398
+ in srgb,
399
+ var(--fx-color-primary) 8%,
400
+ transparent
401
+ );
402
+ }
403
+ .empty {
404
+ text-align: center;
405
+ padding: var(--fx-space-xl);
406
+ color: var(--fx-text-muted);
407
+ }
408
+ .pager {
409
+ display: flex;
410
+ align-items: center;
411
+ gap: var(--fx-space-sm);
412
+ flex-wrap: wrap;
413
+ padding: var(--fx-space-sm) 0;
414
+ }
415
+ :host([pagination-position="center"]) .pager {
416
+ justify-content: center;
417
+ }
418
+ :host([pagination-position="right"]) .pager {
419
+ justify-content: flex-end;
420
+ }
421
+ .pager .info {
422
+ color: var(--fx-text-muted);
423
+ font-size: calc(var(--fx-font-size) - 2px);
424
+ }
425
+ .pg-btn {
426
+ min-width: var(--fx-size-sm);
427
+ height: var(--fx-size-sm);
428
+ font: inherit;
429
+ font-size: calc(var(--fx-font-size) - 2px);
430
+ color: var(--fx-text-default);
431
+ background: var(--fx-surface-background);
432
+ border: 1px solid var(--fx-border-default);
433
+ border-radius: var(--fx-radius-sm);
434
+ padding: var(--fx-space-3xs, 4px) var(--fx-space-sm);
435
+ cursor: pointer;
436
+ transition: border-color var(--fx-motion-duration-fast)
437
+ var(--fx-motion-easing);
438
+ }
439
+ .pg-btn:hover:not(:disabled):not([aria-current="true"]) {
440
+ border-color: var(--fx-color-primary);
441
+ color: var(--fx-color-primary);
442
+ }
443
+ .pg-btn:disabled {
444
+ opacity: 0.5;
445
+ cursor: not-allowed;
446
+ }
447
+ .pg-btn[aria-current="true"] {
448
+ background: var(--fx-color-primary);
449
+ border-color: var(--fx-color-primary);
450
+ color: #fff;
451
+ font-weight: var(--fx-font-weight);
452
+ }
453
+ fx-select {
454
+ vertical-align: middle;
455
+ }
456
+ fx-select::part(trigger) {
457
+ min-width: 64px !important;
458
+ min-height: var(--fx-size-sm) !important;
459
+ padding: 0 var(--fx-space-sm) !important;
460
+ border-radius: var(--fx-radius-sm) !important;
461
+ font-size: calc(var(--fx-font-size) - 2px) !important;
462
+ }
463
+ .toolbar {
464
+ display: flex;
465
+ align-items: center;
466
+ gap: var(--fx-space-sm);
467
+ flex-wrap: wrap;
468
+ padding: var(--fx-space-sm) var(--fx-space-md);
469
+ border: 1px solid var(--fx-border-default);
470
+ border-bottom: none;
471
+ border-radius: var(--fx-radius-md) var(--fx-radius-md) 0 0;
472
+ background: var(--fx-surface-background);
473
+ }
474
+ .toolbar:empty {
475
+ display: none;
476
+ }
477
+ .loading-overlay {
478
+ position: absolute;
479
+ inset: 0;
480
+ display: flex;
481
+ flex-direction: column;
482
+ align-items: center;
483
+ justify-content: center;
484
+ gap: var(--fx-space-sm);
485
+ background: color-mix(
486
+ in srgb,
487
+ var(--fx-surface-background) 85%,
488
+ transparent
489
+ );
490
+ backdrop-filter: blur(2px);
491
+ z-index: 10;
492
+ border-radius: var(--fx-radius-md);
493
+ }
494
+ .tbl-spinner {
495
+ width: 32px;
496
+ height: 32px;
497
+ border: 3px solid var(--fx-border-default);
498
+ border-top-color: var(--fx-color-primary);
499
+ border-radius: 50%;
500
+ animation: tbl-spin 1s linear infinite;
501
+ }
502
+ @keyframes tbl-spin {
503
+ to {
504
+ transform: rotate(360deg);
505
+ }
506
+ }
507
+ .loading-text {
508
+ color: var(--fx-text-muted);
509
+ font-size: calc(var(--fx-font-size) - 1px);
510
+ }
511
+ `;
250
512
  let FxTable = _FxTable;
251
513
  function defineFxTable() {
252
514
  return defineElement("fx-table", FxTable);