@shival99/z-ui 1.6.10 → 1.7.1
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/assets/css/animations.css +6 -0
- package/assets/css/base.css +0 -6
- package/assets/css/themes/gray.css +8 -0
- package/assets/css/themes/green.css +8 -0
- package/assets/css/themes/hospital.css +8 -0
- package/assets/css/themes/neutral.css +8 -0
- package/assets/css/themes/orange.css +8 -0
- package/assets/css/themes/slate.css +8 -0
- package/assets/css/themes/stone.css +8 -0
- package/assets/css/themes/violet.css +8 -0
- package/assets/css/themes/zinc.css +8 -0
- package/fesm2022/shival99-z-ui-components-z-autocomplete.mjs +19 -17
- package/fesm2022/shival99-z-ui-components-z-autocomplete.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-button-group.mjs +191 -0
- package/fesm2022/shival99-z-ui-components-z-button-group.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-button.mjs +6 -1
- package/fesm2022/shival99-z-ui-components-z-button.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-calendar.mjs +60 -52
- package/fesm2022/shival99-z-ui-components-z-calendar.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-checkbox.mjs +13 -5
- package/fesm2022/shival99-z-ui-components-z-checkbox.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-drawer.mjs +7 -4
- package/fesm2022/shival99-z-ui-components-z-drawer.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-editor.mjs +28 -25
- package/fesm2022/shival99-z-ui-components-z-editor.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-filter.mjs +1 -1
- package/fesm2022/shival99-z-ui-components-z-filter.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-gallery.mjs +1573 -0
- package/fesm2022/shival99-z-ui-components-z-gallery.mjs.map +1 -0
- package/fesm2022/shival99-z-ui-components-z-icon.mjs +3 -1
- package/fesm2022/shival99-z-ui-components-z-icon.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-input.mjs +114 -109
- package/fesm2022/shival99-z-ui-components-z-input.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-modal.mjs +10 -9
- package/fesm2022/shival99-z-ui-components-z-modal.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-pagination.mjs +1 -1
- package/fesm2022/shival99-z-ui-components-z-pagination.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-radio.mjs +13 -5
- package/fesm2022/shival99-z-ui-components-z-radio.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-select.mjs +37 -29
- package/fesm2022/shival99-z-ui-components-z-select.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-switch.mjs +13 -5
- package/fesm2022/shival99-z-ui-components-z-switch.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-table.mjs +1268 -1254
- package/fesm2022/shival99-z-ui-components-z-table.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-upload.mjs +21 -19
- package/fesm2022/shival99-z-ui-components-z-upload.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-i18n.mjs +22 -0
- package/fesm2022/shival99-z-ui-i18n.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-pipes.mjs +32 -1
- package/fesm2022/shival99-z-ui-pipes.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-utils.mjs +9 -1
- package/fesm2022/shival99-z-ui-utils.mjs.map +1 -1
- package/package.json +9 -1
- package/types/shival99-z-ui-components-z-autocomplete.d.ts +11 -9
- package/types/shival99-z-ui-components-z-breadcrumb.d.ts +2 -2
- package/types/shival99-z-ui-components-z-button-group.d.ts +56 -0
- package/types/shival99-z-ui-components-z-button.d.ts +1 -1
- package/types/shival99-z-ui-components-z-calendar.d.ts +5 -4
- package/types/shival99-z-ui-components-z-checkbox.d.ts +5 -1
- package/types/shival99-z-ui-components-z-dropdown-menu.d.ts +1 -1
- package/types/shival99-z-ui-components-z-editor.d.ts +8 -6
- package/types/shival99-z-ui-components-z-empty.d.ts +2 -2
- package/types/shival99-z-ui-components-z-filter.d.ts +1 -1
- package/types/shival99-z-ui-components-z-gallery.d.ts +192 -0
- package/types/shival99-z-ui-components-z-icon.d.ts +3 -1
- package/types/shival99-z-ui-components-z-input.d.ts +40 -27
- package/types/shival99-z-ui-components-z-modal.d.ts +1 -1
- package/types/shival99-z-ui-components-z-radio.d.ts +5 -1
- package/types/shival99-z-ui-components-z-select.d.ts +7 -6
- package/types/shival99-z-ui-components-z-switch.d.ts +5 -1
- package/types/shival99-z-ui-components-z-table.d.ts +25 -22
- package/types/shival99-z-ui-components-z-upload.d.ts +7 -3
- package/types/shival99-z-ui-pipes.d.ts +21 -2
- package/types/shival99-z-ui-utils.d.ts +39 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { moveItemInArray, CdkDropList, CdkDrag } from '@angular/cdk/drag-drop';
|
|
2
2
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
3
|
-
import {
|
|
3
|
+
import { NgStyle, NgClass, NgTemplateOutlet } from '@angular/common';
|
|
4
4
|
import * as i0 from '@angular/core';
|
|
5
|
-
import { input, output, computed, ChangeDetectionStrategy, Component, inject, DestroyRef,
|
|
5
|
+
import { input, output, computed, ChangeDetectionStrategy, Component, inject, DestroyRef, signal, effect, ElementRef, Renderer2, Directive, NgZone, Pipe, TemplateRef, viewChild, viewChildren, untracked, afterNextRender } from '@angular/core';
|
|
6
6
|
import { TranslatePipe } from '@ngx-translate/core';
|
|
7
7
|
import { injectVirtualizer } from '@shival99/angular-virtual';
|
|
8
8
|
import { ZButtonComponent } from '@shival99/z-ui/components/z-button';
|
|
@@ -24,13 +24,13 @@ import { NgScrollbar } from 'ngx-scrollbar';
|
|
|
24
24
|
import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
25
25
|
import { injectDestroy } from 'ngxtension/inject-destroy';
|
|
26
26
|
import { ZDropdownMenuComponent } from '@shival99/z-ui/components/z-dropdown-menu';
|
|
27
|
-
import { takeUntilDestroyed
|
|
27
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
28
28
|
import * as i1 from '@angular/forms';
|
|
29
29
|
import { FormsModule } from '@angular/forms';
|
|
30
30
|
import { ZCalendarComponent } from '@shival99/z-ui/components/z-calendar';
|
|
31
31
|
import { ZSelectComponent } from '@shival99/z-ui/components/z-select';
|
|
32
32
|
import { ZSwitchComponent } from '@shival99/z-ui/components/z-switch';
|
|
33
|
-
import { Subject, debounceTime, distinctUntilChanged
|
|
33
|
+
import { Subject, debounceTime, distinctUntilChanged } from 'rxjs';
|
|
34
34
|
|
|
35
35
|
const Z_DEFAULT_ROW_HEIGHT = 40;
|
|
36
36
|
const Z_DEFAULT_VIRTUAL_OVERSCAN = 5;
|
|
@@ -243,1386 +243,1368 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
243
243
|
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}\n"] }]
|
|
244
244
|
}], propDecorators: { zConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "zConfig", required: true }] }], zRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRow", required: true }] }], zRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRowId", required: true }] }], zDropdownButtonSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "zDropdownButtonSize", required: false }] }], zActionClick: [{ type: i0.Output, args: ["zActionClick"] }] } });
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
246
|
+
const filterVisibleColumns = (columns) => columns
|
|
247
|
+
.filter(col => {
|
|
248
|
+
const { visible } = col;
|
|
249
|
+
if (visible === undefined) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
return typeof visible === 'function' ? visible() : visible;
|
|
253
|
+
})
|
|
254
|
+
.map(col => {
|
|
255
|
+
if (col.columns && col.columns.length > 0) {
|
|
256
|
+
return {
|
|
257
|
+
...col,
|
|
258
|
+
columns: filterVisibleColumns(col.columns),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return col;
|
|
262
|
+
});
|
|
263
|
+
const isObjectConfig = (config) => {
|
|
264
|
+
if (!config || typeof config !== 'object') {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
return config.constructor === Object;
|
|
268
|
+
};
|
|
269
|
+
const isHeaderConfig = (config) => isObjectConfig(config);
|
|
270
|
+
const isBodyConfig = (config) => isObjectConfig(config);
|
|
271
|
+
const isFooterConfig = (config) => isObjectConfig(config);
|
|
272
|
+
const getHeaderOrFooterConfigInternal = (col, type) => {
|
|
273
|
+
const empty = {
|
|
274
|
+
content: undefined,
|
|
275
|
+
class: undefined,
|
|
276
|
+
style: undefined,
|
|
277
|
+
align: undefined,
|
|
278
|
+
tooltip: undefined,
|
|
279
|
+
rowSpan: undefined,
|
|
280
|
+
colSpan: undefined,
|
|
281
|
+
contentClass: undefined,
|
|
282
|
+
contentStyle: undefined,
|
|
283
|
+
};
|
|
284
|
+
if (!col) {
|
|
285
|
+
return empty;
|
|
286
|
+
}
|
|
287
|
+
const config = type === 'header' ? col.header : col.footer;
|
|
288
|
+
const isConfigFn = type === 'header' ? isHeaderConfig : isFooterConfig;
|
|
289
|
+
if (!isConfigFn(config)) {
|
|
290
|
+
return { ...empty, content: config };
|
|
291
|
+
}
|
|
292
|
+
const typedConfig = config;
|
|
293
|
+
return {
|
|
294
|
+
content: typedConfig.content,
|
|
295
|
+
class: typedConfig.class,
|
|
296
|
+
style: typedConfig.style,
|
|
297
|
+
align: typedConfig.align,
|
|
298
|
+
tooltip: typedConfig.tooltip,
|
|
299
|
+
rowSpan: typedConfig.rowSpan,
|
|
300
|
+
colSpan: typedConfig.colSpan,
|
|
301
|
+
contentClass: typedConfig.contentClass,
|
|
302
|
+
contentStyle: typedConfig.contentStyle,
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
const getHeaderConfig = (col) => getHeaderOrFooterConfigInternal(col, 'header');
|
|
306
|
+
const getBodyConfig = (col, ctx) => {
|
|
307
|
+
const empty = {
|
|
308
|
+
content: undefined,
|
|
309
|
+
class: undefined,
|
|
310
|
+
style: undefined,
|
|
311
|
+
align: undefined,
|
|
312
|
+
rowSpan: undefined,
|
|
313
|
+
colSpan: undefined,
|
|
314
|
+
contentClass: undefined,
|
|
315
|
+
contentStyle: undefined,
|
|
316
|
+
tooltip: undefined,
|
|
317
|
+
};
|
|
318
|
+
if (!col) {
|
|
319
|
+
return empty;
|
|
320
|
+
}
|
|
321
|
+
if (!isBodyConfig(col.body)) {
|
|
322
|
+
return { ...empty, content: col.body };
|
|
323
|
+
}
|
|
324
|
+
const { body } = col;
|
|
325
|
+
const rowSpan = typeof body.rowSpan === 'function' && ctx ? body.rowSpan(ctx) : body.rowSpan;
|
|
326
|
+
const colSpan = typeof body.colSpan === 'function' && ctx ? body.colSpan(ctx) : body.colSpan;
|
|
327
|
+
const classValue = typeof body.class === 'function' && ctx ? body.class(ctx) : body.class;
|
|
328
|
+
const styleValue = typeof body.style === 'function' && ctx ? body.style(ctx) : body.style;
|
|
329
|
+
const contentClass = typeof body.contentClass === 'function' && ctx ? body.contentClass(ctx) : body.contentClass;
|
|
330
|
+
const contentStyle = typeof body.contentStyle === 'function' && ctx ? body.contentStyle(ctx) : body.contentStyle;
|
|
331
|
+
const tooltip = typeof body.tooltip === 'function' && ctx ? body.tooltip(ctx) : body.tooltip;
|
|
332
|
+
return {
|
|
333
|
+
content: body.content,
|
|
334
|
+
class: classValue,
|
|
335
|
+
style: styleValue,
|
|
336
|
+
align: body.align,
|
|
337
|
+
rowSpan: typeof rowSpan === 'number' ? rowSpan : undefined,
|
|
338
|
+
colSpan: typeof colSpan === 'number' ? colSpan : undefined,
|
|
339
|
+
contentClass,
|
|
340
|
+
contentStyle: contentStyle,
|
|
341
|
+
tooltip,
|
|
342
|
+
};
|
|
343
|
+
};
|
|
344
|
+
const getFooterConfig = (col) => getHeaderOrFooterConfigInternal(col, 'footer');
|
|
345
|
+
const getHeaderContent = (col) => getHeaderConfig(col).content;
|
|
346
|
+
const getBodyContent = (col) => {
|
|
347
|
+
if (!col?.body) {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
return isBodyConfig(col.body) ? col.body.content : col.body;
|
|
351
|
+
};
|
|
352
|
+
const getFooterContent = (col) => getFooterConfig(col).content;
|
|
353
|
+
const getBodyRowSpan = (col, ctx) => getBodyConfig(col, ctx).rowSpan;
|
|
354
|
+
const getBodyColSpan = (col, ctx) => getBodyConfig(col, ctx).colSpan;
|
|
355
|
+
const getHeaderRowSpan = (col) => getHeaderConfig(col).rowSpan;
|
|
356
|
+
const getHeaderColSpan = (col) => getHeaderConfig(col).colSpan;
|
|
357
|
+
const getFooterRowSpan = (col) => getFooterConfig(col).rowSpan;
|
|
358
|
+
const getFooterColSpan = (col) => getFooterConfig(col).colSpan;
|
|
359
|
+
function parseIconString(content) {
|
|
360
|
+
if (!content || typeof content !== 'string') {
|
|
361
|
+
return [{ type: 'text', value: content || '' }];
|
|
362
|
+
}
|
|
363
|
+
const parts = [];
|
|
364
|
+
const iconRegex = /\[icon:([^\]]+)\]/g;
|
|
365
|
+
let lastIndex = 0;
|
|
366
|
+
let match;
|
|
367
|
+
while ((match = iconRegex.exec(content)) !== null) {
|
|
368
|
+
if (match.index > lastIndex) {
|
|
369
|
+
const textPart = content.slice(lastIndex, match.index);
|
|
370
|
+
if (textPart) {
|
|
371
|
+
parts.push({ type: 'text', value: textPart });
|
|
271
372
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
373
|
+
}
|
|
374
|
+
const iconContent = match[1];
|
|
375
|
+
const attrs = iconContent.split('|');
|
|
376
|
+
const iconName = attrs[0];
|
|
377
|
+
const iconPart = { type: 'icon', value: iconName };
|
|
378
|
+
for (let i = 1; i < attrs.length; i++) {
|
|
379
|
+
const [key, val] = attrs[i].split(':');
|
|
380
|
+
if (key === 'size') {
|
|
381
|
+
iconPart.size = parseInt(val, 10);
|
|
276
382
|
}
|
|
277
|
-
if (
|
|
278
|
-
|
|
383
|
+
if (key === 'class') {
|
|
384
|
+
iconPart.class = val;
|
|
279
385
|
}
|
|
280
|
-
|
|
281
|
-
|
|
386
|
+
if (key === 'strokeWidth') {
|
|
387
|
+
iconPart.strokeWidth = parseFloat(val);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
parts.push(iconPart);
|
|
391
|
+
lastIndex = match.index + match[0].length;
|
|
282
392
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
393
|
+
if (lastIndex < content.length) {
|
|
394
|
+
parts.push({ type: 'text', value: content.slice(lastIndex) });
|
|
395
|
+
}
|
|
396
|
+
return parts.length > 0 ? parts : [{ type: 'text', value: content }];
|
|
397
|
+
}
|
|
398
|
+
function stripIconSyntax(content) {
|
|
399
|
+
if (!content || typeof content !== 'string') {
|
|
400
|
+
return content || '';
|
|
401
|
+
}
|
|
402
|
+
return content.replace(/\[icon:[^\]]+\]/g, '').trim();
|
|
403
|
+
}
|
|
404
|
+
function hasIconSyntax(content) {
|
|
405
|
+
if (!content || typeof content !== 'string') {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
return /\[icon:[^\]]+\]/.test(content);
|
|
409
|
+
}
|
|
410
|
+
const findColumnConfig = (columnId, columns) => {
|
|
411
|
+
for (const col of columns) {
|
|
412
|
+
if (col.id === columnId) {
|
|
413
|
+
return col;
|
|
287
414
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (!typeConfig) {
|
|
294
|
-
return 'text';
|
|
415
|
+
if (col.columns) {
|
|
416
|
+
const found = findColumnConfig(columnId, col.columns);
|
|
417
|
+
if (found) {
|
|
418
|
+
return found;
|
|
419
|
+
}
|
|
295
420
|
}
|
|
296
|
-
|
|
297
|
-
|
|
421
|
+
}
|
|
422
|
+
return undefined;
|
|
423
|
+
};
|
|
424
|
+
const deepestHeader = (header) => {
|
|
425
|
+
let last = header;
|
|
426
|
+
while (true) {
|
|
427
|
+
const next = last.isPlaceholder && last.colSpan === 1 && last.subHeaders.length === 1 ? last.subHeaders[0] : null;
|
|
428
|
+
if (!next) {
|
|
429
|
+
return last === header ? null : last;
|
|
298
430
|
}
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
431
|
+
last = next;
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
const tableHeaderRowSpan = (header) => {
|
|
435
|
+
const deepest = deepestHeader(header);
|
|
436
|
+
const rowSpan = (deepest ? deepest.depth - header.depth : 0) + 1;
|
|
437
|
+
const above = header.depth - header.column.depth;
|
|
438
|
+
return above > 1 ? null : rowSpan;
|
|
439
|
+
};
|
|
440
|
+
const calculateCellRowSpan = (cell, columnConfig, allRows) => {
|
|
441
|
+
if (!columnConfig?.body || !isBodyConfig(columnConfig.body)) {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
const rowSpanValue = columnConfig.body.rowSpan;
|
|
445
|
+
if (!rowSpanValue) {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
if (typeof rowSpanValue === 'function') {
|
|
449
|
+
const rowSpan = rowSpanValue(cell.getContext());
|
|
450
|
+
return rowSpan > 1 ? rowSpan : null;
|
|
451
|
+
}
|
|
452
|
+
if (rowSpanValue > 1) {
|
|
453
|
+
const currentRowId = cell.row.id;
|
|
454
|
+
const filteredIndex = allRows.findIndex(r => r.id === currentRowId);
|
|
455
|
+
if (filteredIndex === -1) {
|
|
456
|
+
return null;
|
|
310
457
|
}
|
|
311
|
-
if (
|
|
312
|
-
return
|
|
458
|
+
if (filteredIndex % rowSpanValue !== 0) {
|
|
459
|
+
return null;
|
|
313
460
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
461
|
+
const remainingRows = allRows.length - filteredIndex;
|
|
462
|
+
const actualRowSpan = Math.min(rowSpanValue, remainingRows);
|
|
463
|
+
return actualRowSpan > 1 ? actualRowSpan : null;
|
|
464
|
+
}
|
|
465
|
+
return null;
|
|
466
|
+
};
|
|
467
|
+
const calculateCellColSpan = (cell, columnConfig) => {
|
|
468
|
+
if (!columnConfig?.body || !isBodyConfig(columnConfig.body)) {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
const colSpanValue = columnConfig.body.colSpan;
|
|
472
|
+
if (!colSpanValue) {
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
const colSpan = typeof colSpanValue === 'function' ? colSpanValue(cell.getContext()) : colSpanValue;
|
|
476
|
+
return colSpan > 1 ? colSpan : null;
|
|
477
|
+
};
|
|
478
|
+
const calculateHeaderRowSpan = (header, columnConfig, columns) => {
|
|
479
|
+
const configRowSpan = getHeaderRowSpan(columnConfig);
|
|
480
|
+
if (configRowSpan !== undefined && configRowSpan !== null) {
|
|
481
|
+
return configRowSpan;
|
|
482
|
+
}
|
|
483
|
+
if (columns && header.column.parent) {
|
|
484
|
+
const parentConfig = findColumnConfig(header.column.parent.id, columns);
|
|
485
|
+
const parentRowSpan = getHeaderRowSpan(parentConfig);
|
|
486
|
+
if (parentRowSpan && parentRowSpan >= 2) {
|
|
487
|
+
return null;
|
|
325
488
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
489
|
+
}
|
|
490
|
+
const deepest = deepestHeader(header);
|
|
491
|
+
const rowSpan = (deepest ? deepest.depth - header.depth : 0) + 1;
|
|
492
|
+
const above = header.depth - header.column.depth;
|
|
493
|
+
return above > 1 ? null : rowSpan;
|
|
494
|
+
};
|
|
495
|
+
const calculateHeaderColSpan = (header, columnConfig) => {
|
|
496
|
+
const configColSpan = getHeaderColSpan(columnConfig);
|
|
497
|
+
const isPinned = header.column.getIsPinned();
|
|
498
|
+
if (isPinned === 'left' && configColSpan !== undefined && configColSpan !== null) {
|
|
499
|
+
const allHeaders = header.headerGroup.headers;
|
|
500
|
+
const currentIndex = allHeaders.findIndex(h => h.id === header.id);
|
|
501
|
+
if (currentIndex === -1) {
|
|
502
|
+
return configColSpan;
|
|
331
503
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
504
|
+
let effectiveColSpan = 0;
|
|
505
|
+
for (let i = 0; i < configColSpan && currentIndex + i < allHeaders.length; i++) {
|
|
506
|
+
const targetHeader = allHeaders[currentIndex + i];
|
|
507
|
+
const targetLeafHeaders = targetHeader.getLeafHeaders();
|
|
508
|
+
const allLeafsPinned = targetLeafHeaders.every(lh => lh.column.getIsPinned() === 'left');
|
|
509
|
+
if (allLeafsPinned) {
|
|
510
|
+
effectiveColSpan++;
|
|
511
|
+
}
|
|
512
|
+
if (!allLeafsPinned) {
|
|
513
|
+
break;
|
|
335
514
|
}
|
|
336
|
-
const obj = opt;
|
|
337
|
-
return {
|
|
338
|
-
label: String(obj[labelKey ?? 'label'] ?? ''),
|
|
339
|
-
value: obj[valueKey ?? 'value'],
|
|
340
|
-
};
|
|
341
|
-
});
|
|
342
|
-
}, ...(ngDevMode ? [{ debugName: "selectOptions" }] : []));
|
|
343
|
-
isDisabled = computed(() => {
|
|
344
|
-
const config = this.editConfig();
|
|
345
|
-
if (!config?.disabled) {
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
if (typeof config.disabled === 'function') {
|
|
349
|
-
return config.disabled(this.zRow());
|
|
350
|
-
}
|
|
351
|
-
return config.disabled;
|
|
352
|
-
}, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
353
|
-
isReadonly = computed(() => {
|
|
354
|
-
const config = this.editConfig();
|
|
355
|
-
if (!config?.readonly) {
|
|
356
|
-
return false;
|
|
357
515
|
}
|
|
358
|
-
|
|
359
|
-
|
|
516
|
+
return effectiveColSpan > 1 ? effectiveColSpan : null;
|
|
517
|
+
}
|
|
518
|
+
if (configColSpan !== undefined && configColSpan !== null) {
|
|
519
|
+
return configColSpan;
|
|
520
|
+
}
|
|
521
|
+
return header.colSpan > 1 ? header.colSpan : null;
|
|
522
|
+
};
|
|
523
|
+
const calculateFooterRowSpan = (header, columnConfig) => {
|
|
524
|
+
const configRowSpan = getFooterRowSpan(columnConfig);
|
|
525
|
+
if (configRowSpan !== undefined && configRowSpan !== null) {
|
|
526
|
+
return configRowSpan;
|
|
527
|
+
}
|
|
528
|
+
const footerContent = getFooterContent(columnConfig);
|
|
529
|
+
if (footerContent !== undefined) {
|
|
530
|
+
return 1;
|
|
531
|
+
}
|
|
532
|
+
if (header.colSpan > 1) {
|
|
533
|
+
return 1;
|
|
534
|
+
}
|
|
535
|
+
return 1;
|
|
536
|
+
};
|
|
537
|
+
const calculateFooterColSpan = (header, columnConfig) => {
|
|
538
|
+
const configColSpan = getFooterColSpan(columnConfig);
|
|
539
|
+
const isPinned = header.column.getIsPinned();
|
|
540
|
+
if (isPinned === 'left' && configColSpan !== undefined && configColSpan !== null) {
|
|
541
|
+
const allHeaders = header.headerGroup.headers;
|
|
542
|
+
const currentIndex = allHeaders.findIndex(h => h.id === header.id);
|
|
543
|
+
if (currentIndex === -1) {
|
|
544
|
+
return configColSpan;
|
|
360
545
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
546
|
+
let effectiveColSpan = 0;
|
|
547
|
+
for (let i = 0; i < configColSpan && currentIndex + i < allHeaders.length; i++) {
|
|
548
|
+
const targetHeader = allHeaders[currentIndex + i];
|
|
549
|
+
const targetLeafHeaders = targetHeader.getLeafHeaders();
|
|
550
|
+
const allLeafsPinned = targetLeafHeaders.every(lh => lh.column.getIsPinned() === 'left');
|
|
551
|
+
if (allLeafsPinned) {
|
|
552
|
+
effectiveColSpan++;
|
|
553
|
+
}
|
|
554
|
+
if (!allLeafsPinned) {
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
368
557
|
}
|
|
369
|
-
return
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
558
|
+
return effectiveColSpan > 1 ? effectiveColSpan : null;
|
|
559
|
+
}
|
|
560
|
+
if (configColSpan !== undefined && configColSpan !== null) {
|
|
561
|
+
return configColSpan;
|
|
562
|
+
}
|
|
563
|
+
return header.colSpan > 1 ? header.colSpan : null;
|
|
564
|
+
};
|
|
565
|
+
const shouldCellRenderUtil = (cell, columnConfig, allRows) => {
|
|
566
|
+
if (!columnConfig?.body || !isBodyConfig(columnConfig.body)) {
|
|
567
|
+
return true;
|
|
568
|
+
}
|
|
569
|
+
const rowSpanValue = columnConfig.body.rowSpan;
|
|
570
|
+
if (!rowSpanValue) {
|
|
571
|
+
return true;
|
|
572
|
+
}
|
|
573
|
+
if (typeof rowSpanValue === 'function') {
|
|
574
|
+
const rowSpan = rowSpanValue(cell.getContext());
|
|
575
|
+
return rowSpan !== 0;
|
|
576
|
+
}
|
|
577
|
+
if (rowSpanValue > 1) {
|
|
578
|
+
const currentRowId = cell.row.id;
|
|
579
|
+
const filteredIndex = allRows.findIndex(r => r.id === currentRowId);
|
|
580
|
+
if (filteredIndex === -1) {
|
|
581
|
+
return true;
|
|
375
582
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
583
|
+
return filteredIndex % rowSpanValue === 0;
|
|
584
|
+
}
|
|
585
|
+
return true;
|
|
586
|
+
};
|
|
587
|
+
const shouldHeaderOrFooterRender = (header, allHeaders, columns, type) => {
|
|
588
|
+
const currentIndex = allHeaders.findIndex(h => h.id === header.id);
|
|
589
|
+
if (currentIndex <= 0) {
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
const getConfigColSpanFn = type === 'header' ? getHeaderColSpan : getFooterColSpan;
|
|
593
|
+
for (let i = 0; i < currentIndex; i++) {
|
|
594
|
+
const prevHeader = allHeaders[i];
|
|
595
|
+
const prevConfig = findColumnConfig(prevHeader.column.id, columns);
|
|
596
|
+
const configColSpan = getConfigColSpanFn(prevConfig);
|
|
597
|
+
if (!configColSpan || configColSpan <= 1) {
|
|
598
|
+
continue;
|
|
387
599
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
600
|
+
const isPinned = prevHeader.column.getIsPinned();
|
|
601
|
+
let effectiveColSpan = configColSpan;
|
|
602
|
+
if (isPinned === 'left') {
|
|
603
|
+
const headersInGroup = prevHeader.headerGroup.headers;
|
|
604
|
+
const prevIdx = headersInGroup.findIndex(h => h.id === prevHeader.id);
|
|
605
|
+
if (prevIdx !== -1) {
|
|
606
|
+
effectiveColSpan = 0;
|
|
607
|
+
for (let j = 0; j < configColSpan && prevIdx + j < headersInGroup.length; j++) {
|
|
608
|
+
const targetHeader = headersInGroup[prevIdx + j];
|
|
609
|
+
const targetLeafHeaders = targetHeader.getLeafHeaders();
|
|
610
|
+
const allLeafsPinned = targetLeafHeaders.every(lh => lh.column.getIsPinned() === 'left');
|
|
611
|
+
if (allLeafsPinned) {
|
|
612
|
+
effectiveColSpan++;
|
|
613
|
+
}
|
|
614
|
+
if (!allLeafsPinned) {
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
391
619
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
onValueChange(newValue) {
|
|
395
|
-
const oldValue = this.zValue();
|
|
396
|
-
this._currentValue.set(newValue);
|
|
397
|
-
const config = this.editConfig();
|
|
398
|
-
if (config?.blurEdit) {
|
|
399
|
-
this._pendingBlurValue = newValue;
|
|
400
|
-
this._hasPendingBlur = true;
|
|
401
|
-
return;
|
|
620
|
+
if (effectiveColSpan > 1 && i + effectiveColSpan > currentIndex) {
|
|
621
|
+
return false;
|
|
402
622
|
}
|
|
403
|
-
this._valueChange$.next({ oldValue, newValue });
|
|
404
623
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
624
|
+
return true;
|
|
625
|
+
};
|
|
626
|
+
const shouldBodyCellRenderColSpan = (cell, allCells, columns) => {
|
|
627
|
+
const currentIndex = allCells.findIndex(c => c.id === cell.id);
|
|
628
|
+
if (currentIndex <= 0) {
|
|
629
|
+
return true;
|
|
408
630
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
631
|
+
for (let i = 0; i < currentIndex; i++) {
|
|
632
|
+
const prevCell = allCells[i];
|
|
633
|
+
const prevConfig = findColumnConfig(prevCell.column.id, columns);
|
|
634
|
+
const prevColSpan = getBodyColSpan(prevConfig, prevCell.getContext());
|
|
635
|
+
if (prevColSpan && prevColSpan > 1 && i + prevColSpan > currentIndex) {
|
|
636
|
+
return false;
|
|
412
637
|
}
|
|
413
|
-
const oldValue = this.zValue();
|
|
414
|
-
this._hasPendingBlur = false;
|
|
415
|
-
this._emitChange(oldValue, this._pendingBlurValue);
|
|
416
638
|
}
|
|
417
|
-
|
|
418
|
-
|
|
639
|
+
return true;
|
|
640
|
+
};
|
|
641
|
+
const shouldFooterRender = (header, allHeaders, columns) => shouldHeaderOrFooterRender(header, allHeaders, columns, 'footer');
|
|
642
|
+
const shouldHeaderRender = (header, allHeaders, columns) => shouldHeaderOrFooterRender(header, allHeaders, columns, 'header');
|
|
643
|
+
function columnConfigToColumnDef(config) {
|
|
644
|
+
const sortConfig = typeof config.sort === 'boolean' ? (config.sort ? { enabled: true } : undefined) : config.sort;
|
|
645
|
+
const filterConfig = typeof config.filter === 'boolean' ? (config.filter ? { enabled: true } : undefined) : config.filter;
|
|
646
|
+
const resolveHeaderOrFooter = (content) => {
|
|
647
|
+
if (!content) {
|
|
648
|
+
return undefined;
|
|
649
|
+
}
|
|
650
|
+
if (typeof content === 'string' || typeof content === 'number') {
|
|
651
|
+
return String(content);
|
|
652
|
+
}
|
|
653
|
+
if (typeof content === 'function') {
|
|
654
|
+
return content;
|
|
655
|
+
}
|
|
656
|
+
return undefined;
|
|
657
|
+
};
|
|
658
|
+
const columnDef = {
|
|
659
|
+
id: config.id,
|
|
660
|
+
size: config.size,
|
|
661
|
+
minSize: config.minSize ?? Z_DEFAULT_COLUMN_MIN_SIZE,
|
|
662
|
+
maxSize: config.maxSize,
|
|
663
|
+
enableSorting: sortConfig?.enabled ?? false,
|
|
664
|
+
enableColumnFilter: filterConfig?.enabled ?? false,
|
|
665
|
+
enableResizing: config.enableResizing,
|
|
666
|
+
enablePinning: config.enablePinning,
|
|
667
|
+
enableHiding: config.enableHiding,
|
|
668
|
+
filterType: filterConfig?.type,
|
|
669
|
+
filterOptions: filterConfig?.options,
|
|
670
|
+
};
|
|
671
|
+
if (config.accessorKey) {
|
|
672
|
+
columnDef.accessorKey = config.accessorKey;
|
|
419
673
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const finalValue = isWrappedObject ? newValue.value : newValue;
|
|
423
|
-
this._handleImmediateChange(finalValue);
|
|
674
|
+
if (config.accessorFn) {
|
|
675
|
+
columnDef.accessorFn = config.accessorFn;
|
|
424
676
|
}
|
|
425
|
-
|
|
426
|
-
|
|
677
|
+
columnDef.header = resolveHeaderOrFooter(getHeaderContent(config));
|
|
678
|
+
const bodyContent = getBodyContent(config);
|
|
679
|
+
if (typeof bodyContent === 'string' || typeof bodyContent === 'number') {
|
|
680
|
+
columnDef.cell = () => bodyContent;
|
|
427
681
|
}
|
|
428
|
-
|
|
429
|
-
|
|
682
|
+
if (typeof bodyContent === 'function') {
|
|
683
|
+
columnDef.cell = bodyContent;
|
|
430
684
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const oldValue = this.zValue();
|
|
437
|
-
this._currentValue.set(newValue);
|
|
438
|
-
this._emitChange(oldValue, newValue);
|
|
685
|
+
columnDef.footer = resolveHeaderOrFooter(getFooterContent(config));
|
|
686
|
+
const isLocalFilterMode = !filterConfig?.mode || filterConfig.mode === 'local';
|
|
687
|
+
const isLocalSortMode = !sortConfig?.mode || sortConfig.mode === 'local';
|
|
688
|
+
if (isLocalSortMode && sortConfig?.sortFn) {
|
|
689
|
+
columnDef.sortingFn = sortConfig.sortFn;
|
|
439
690
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
row: this.zRow(),
|
|
443
|
-
rowId: this.zRowId(),
|
|
444
|
-
rowIndex: this.zRowIndex(),
|
|
445
|
-
columnId: this.zColumnId(),
|
|
446
|
-
oldValue,
|
|
447
|
-
newValue,
|
|
448
|
-
});
|
|
691
|
+
if (!isLocalSortMode) {
|
|
692
|
+
columnDef.sortingFn = () => 0;
|
|
449
693
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const debounceMs = config?.debounceTime ?? 0;
|
|
453
|
-
this._valueChange$
|
|
454
|
-
.pipe(debounceTime(debounceMs), distinctUntilChanged((a, b) => a.newValue === b.newValue), takeUntilDestroyed(this._destroyRef))
|
|
455
|
-
.subscribe(({ oldValue, newValue }) => {
|
|
456
|
-
this._emitChange(oldValue, newValue);
|
|
457
|
-
});
|
|
458
|
-
this._destroyRef.onDestroy(() => {
|
|
459
|
-
this._valueChange$.complete();
|
|
460
|
-
});
|
|
694
|
+
if (isLocalFilterMode && filterConfig?.filterFn) {
|
|
695
|
+
columnDef.filterFn = filterConfig.filterFn;
|
|
461
696
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
toObservable(this._inputControl.state, { injector: this._injector })
|
|
467
|
-
.pipe(map(state => state.blurred), pairwise(), filter(([prev, curr]) => !prev && curr), takeUntilDestroyed(this._destroyRef))
|
|
468
|
-
.subscribe(() => {
|
|
469
|
-
this._handleInputBlur();
|
|
470
|
-
});
|
|
697
|
+
if (!isLocalFilterMode) {
|
|
698
|
+
columnDef.filterFn = () => true;
|
|
471
699
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
700
|
+
if (isLocalFilterMode && !filterConfig?.filterFn && filterConfig?.type) {
|
|
701
|
+
switch (filterConfig.type) {
|
|
702
|
+
case 'select':
|
|
703
|
+
columnDef.filterFn = (row, columnId, filterValue) => {
|
|
704
|
+
if (!filterValue || (Array.isArray(filterValue) && filterValue.length === 0)) {
|
|
705
|
+
return true;
|
|
706
|
+
}
|
|
707
|
+
const cellValue = row.getValue(columnId);
|
|
708
|
+
if (Array.isArray(filterValue)) {
|
|
709
|
+
return filterValue.includes(cellValue);
|
|
710
|
+
}
|
|
711
|
+
return cellValue === filterValue;
|
|
712
|
+
};
|
|
713
|
+
break;
|
|
714
|
+
case 'multi-select':
|
|
715
|
+
case 'tags':
|
|
716
|
+
columnDef.filterFn = (row, columnId, filterValue) => {
|
|
717
|
+
if (!filterValue || (Array.isArray(filterValue) && filterValue.length === 0)) {
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
const cellValue = row.getValue(columnId);
|
|
721
|
+
if (!Array.isArray(filterValue)) {
|
|
722
|
+
return cellValue === filterValue;
|
|
723
|
+
}
|
|
724
|
+
const filterSet = new Set(filterValue);
|
|
725
|
+
if (Array.isArray(cellValue)) {
|
|
726
|
+
return cellValue.some(cv => filterSet.has(cv));
|
|
727
|
+
}
|
|
728
|
+
return filterSet.has(cellValue);
|
|
729
|
+
};
|
|
730
|
+
break;
|
|
731
|
+
case 'date':
|
|
732
|
+
columnDef.filterFn = (row, columnId, filterValue) => {
|
|
733
|
+
if (!filterValue) {
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
const cellValue = row.getValue(columnId);
|
|
737
|
+
if (!cellValue) {
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
const cellDate = cellValue instanceof Date ? cellValue : new Date(cellValue);
|
|
741
|
+
const filterDate = filterValue instanceof Date ? filterValue : new Date(filterValue);
|
|
742
|
+
if (isNaN(cellDate.getTime()) || isNaN(filterDate.getTime())) {
|
|
743
|
+
return false;
|
|
744
|
+
}
|
|
745
|
+
return (cellDate.getFullYear() === filterDate.getFullYear() &&
|
|
746
|
+
cellDate.getMonth() === filterDate.getMonth() &&
|
|
747
|
+
cellDate.getDate() === filterDate.getDate());
|
|
748
|
+
};
|
|
749
|
+
break;
|
|
750
|
+
case 'date-range':
|
|
751
|
+
columnDef.filterFn = (row, columnId, filterValue) => {
|
|
752
|
+
if (!filterValue || typeof filterValue !== 'object') {
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
const { start, end } = filterValue;
|
|
756
|
+
if (!start && !end) {
|
|
757
|
+
return true;
|
|
758
|
+
}
|
|
759
|
+
const cellValue = row.getValue(columnId);
|
|
760
|
+
if (!cellValue) {
|
|
761
|
+
return false;
|
|
762
|
+
}
|
|
763
|
+
const cellDate = cellValue instanceof Date ? cellValue : new Date(cellValue);
|
|
764
|
+
if (isNaN(cellDate.getTime())) {
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
const cellDateOnly = new Date(cellDate.getFullYear(), cellDate.getMonth(), cellDate.getDate());
|
|
768
|
+
if (start) {
|
|
769
|
+
const startDate = start instanceof Date ? start : new Date(start);
|
|
770
|
+
if (!isNaN(startDate.getTime())) {
|
|
771
|
+
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
|
|
772
|
+
if (cellDateOnly < startDateOnly) {
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (end) {
|
|
778
|
+
const endDate = end instanceof Date ? end : new Date(end);
|
|
779
|
+
if (!isNaN(endDate.getTime())) {
|
|
780
|
+
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
|
781
|
+
if (cellDateOnly > endDateOnly) {
|
|
782
|
+
return false;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return true;
|
|
787
|
+
};
|
|
788
|
+
break;
|
|
789
|
+
case 'range':
|
|
790
|
+
columnDef.filterFn = (row, columnId, filterValue) => {
|
|
791
|
+
if (!filterValue || !Array.isArray(filterValue)) {
|
|
792
|
+
return true;
|
|
793
|
+
}
|
|
794
|
+
const [min, max] = filterValue;
|
|
795
|
+
if (min === undefined && max === undefined) {
|
|
796
|
+
return true;
|
|
797
|
+
}
|
|
798
|
+
const cellValue = row.getValue(columnId);
|
|
799
|
+
if (cellValue === null || cellValue === undefined) {
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
const numValue = typeof cellValue === 'number' ? cellValue : Number(cellValue);
|
|
803
|
+
if (isNaN(numValue)) {
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
if (min !== undefined && numValue < min) {
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
if (max !== undefined && numValue > max) {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
return true;
|
|
813
|
+
};
|
|
814
|
+
break;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (config.columns && config.columns.length > 0) {
|
|
818
|
+
columnDef.columns = config.columns.map(subConfig => columnConfigToColumnDef(subConfig));
|
|
569
819
|
}
|
|
570
|
-
|
|
820
|
+
return columnDef;
|
|
571
821
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
@switch (editType()) {
|
|
576
|
-
@case ('checkbox') {
|
|
577
|
-
<div class="flex items-center justify-center">
|
|
578
|
-
<z-checkbox
|
|
579
|
-
[zSize]="size()"
|
|
580
|
-
[zChecked]="booleanValue()"
|
|
581
|
-
[zDisabled]="isDisabled() || isReadonly()"
|
|
582
|
-
(zChange)="onCheckboxChange($event)"
|
|
583
|
-
/>
|
|
584
|
-
</div>
|
|
585
|
-
}
|
|
586
|
-
@case ('toggle') {
|
|
587
|
-
<div class="flex items-center justify-center">
|
|
588
|
-
<z-switch
|
|
589
|
-
[zSize]="size()"
|
|
590
|
-
[zChecked]="booleanValue()"
|
|
591
|
-
[zDisabled]="isDisabled() || isReadonly()"
|
|
592
|
-
(zChange)="onToggleChange($event)"
|
|
593
|
-
/>
|
|
594
|
-
</div>
|
|
595
|
-
}
|
|
596
|
-
@case ('number') {
|
|
597
|
-
<z-input
|
|
598
|
-
zType="number"
|
|
599
|
-
[zSize]="size()"
|
|
600
|
-
[zPlaceholder]="placeholder()"
|
|
601
|
-
[zMin]="editConfig().min"
|
|
602
|
-
[zMax]="editConfig().max"
|
|
603
|
-
[zStep]="editConfig().step ?? 1"
|
|
604
|
-
[zThousandSeparator]="editConfig().thousandSeparator || ','"
|
|
605
|
-
[zDisabled]="isDisabled()"
|
|
606
|
-
[zReadonly]="isReadonly()"
|
|
607
|
-
[ngModel]="numericValue()"
|
|
608
|
-
(ngModelChange)="onValueChange($event)"
|
|
609
|
-
(zControl)="onInputControlReady($event)"
|
|
610
|
-
class="w-full"
|
|
611
|
-
/>
|
|
612
|
-
}
|
|
613
|
-
@case ('select') {
|
|
614
|
-
<z-select
|
|
615
|
-
[zSize]="size()"
|
|
616
|
-
[zWrap]="false"
|
|
617
|
-
[zScrollClose]="true"
|
|
618
|
-
[zOptions]="selectOptions()"
|
|
619
|
-
[zAllowClear]="allowClear()"
|
|
620
|
-
[zDisabled]="isDisabled()"
|
|
621
|
-
[zReadonly]="isReadonly()"
|
|
622
|
-
[ngModel]="value()"
|
|
623
|
-
[zPlaceholder]="placeholder()"
|
|
624
|
-
(ngModelChange)="onSelectChange($event)"
|
|
625
|
-
class="w-full min-w-0"
|
|
626
|
-
/>
|
|
627
|
-
}
|
|
628
|
-
@case ('date') {
|
|
629
|
-
<z-calendar
|
|
630
|
-
zMode="single"
|
|
631
|
-
[zSize]="size()"
|
|
632
|
-
zAllowClear
|
|
633
|
-
zValueType="date"
|
|
634
|
-
[zScrollClose]="true"
|
|
635
|
-
[zDisabled]="isDisabled()"
|
|
636
|
-
[zReadonly]="isReadonly()"
|
|
637
|
-
[zPlaceholder]="placeholder()"
|
|
638
|
-
[ngModel]="dateValue()"
|
|
639
|
-
(ngModelChange)="onDateChange($event)"
|
|
640
|
-
class="w-full"
|
|
641
|
-
/>
|
|
642
|
-
}
|
|
643
|
-
@case ('textarea') {
|
|
644
|
-
<z-input
|
|
645
|
-
zType="textarea"
|
|
646
|
-
[zSize]="size()"
|
|
647
|
-
[zPlaceholder]="placeholder()"
|
|
648
|
-
[zRows]="editConfig().rows ?? 2"
|
|
649
|
-
[zDisabled]="isDisabled()"
|
|
650
|
-
[zReadonly]="isReadonly()"
|
|
651
|
-
[ngModel]="stringValue()"
|
|
652
|
-
(ngModelChange)="onValueChange($event)"
|
|
653
|
-
(zControl)="onInputControlReady($event)"
|
|
654
|
-
class="w-full"
|
|
655
|
-
/>
|
|
656
|
-
}
|
|
657
|
-
@default {
|
|
658
|
-
<z-input
|
|
659
|
-
zType="text"
|
|
660
|
-
[zSize]="size()"
|
|
661
|
-
[zPlaceholder]="placeholder()"
|
|
662
|
-
[zDisabled]="isDisabled()"
|
|
663
|
-
[zReadonly]="isReadonly()"
|
|
664
|
-
[ngModel]="stringValue()"
|
|
665
|
-
(ngModelChange)="onValueChange($event)"
|
|
666
|
-
(zControl)="onInputControlReady($event)"
|
|
667
|
-
class="w-full"
|
|
668
|
-
/>
|
|
669
|
-
}
|
|
822
|
+
const resolveConfigValue = (config, row, defaultValue) => {
|
|
823
|
+
if (config === undefined) {
|
|
824
|
+
return defaultValue;
|
|
670
825
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
}, styles: [":host{display:block;width:100%}\n"] }]
|
|
674
|
-
}], ctorParameters: () => [], propDecorators: { zRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRow", required: true }] }], zRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRowId", required: true }] }], zRowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRowIndex", required: true }] }], zColumnId: [{ type: i0.Input, args: [{ isSignal: true, alias: "zColumnId", required: true }] }], zValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "zValue", required: false }] }], zEditConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "zEditConfig", required: false }] }], zRowUpdate: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRowUpdate", required: false }] }], zChange: [{ type: i0.Output, args: ["zChange"] }] } });
|
|
826
|
+
return typeof config === 'function' ? config(row) : config;
|
|
827
|
+
};
|
|
675
828
|
|
|
676
|
-
class
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
829
|
+
class ZTableEditCellComponent {
|
|
830
|
+
_destroyRef = inject(DestroyRef);
|
|
831
|
+
zRow = input.required(...(ngDevMode ? [{ debugName: "zRow" }] : []));
|
|
832
|
+
zRowId = input.required(...(ngDevMode ? [{ debugName: "zRowId" }] : []));
|
|
833
|
+
zRowIndex = input.required(...(ngDevMode ? [{ debugName: "zRowIndex" }] : []));
|
|
834
|
+
zColumnId = input.required(...(ngDevMode ? [{ debugName: "zColumnId" }] : []));
|
|
835
|
+
zValue = input(...(ngDevMode ? [undefined, { debugName: "zValue" }] : []));
|
|
836
|
+
zEditConfig = input(...(ngDevMode ? [undefined, { debugName: "zEditConfig" }] : []));
|
|
837
|
+
zRowUpdate = input(null, ...(ngDevMode ? [{ debugName: "zRowUpdate" }] : []));
|
|
838
|
+
zChange = output();
|
|
839
|
+
_currentValue = signal(undefined, ...(ngDevMode ? [{ debugName: "_currentValue" }] : []));
|
|
840
|
+
_valueChange$ = new Subject();
|
|
841
|
+
_pendingBlurValue = undefined;
|
|
842
|
+
_hasPendingBlur = false;
|
|
843
|
+
constructor() {
|
|
844
|
+
effect(() => {
|
|
845
|
+
const externalValue = this.zValue();
|
|
846
|
+
this._currentValue.set(externalValue);
|
|
847
|
+
});
|
|
848
|
+
effect(() => {
|
|
849
|
+
const update = this.zRowUpdate();
|
|
850
|
+
if (!update) {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
const myRowId = this.zRowId();
|
|
854
|
+
const myColumnId = this.zColumnId();
|
|
855
|
+
if (update.rowId !== myRowId) {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
if (!(myColumnId in update.updates)) {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
this._currentValue.set(update.updates[myColumnId]);
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
editConfig = computed(() => {
|
|
865
|
+
const config = this.zEditConfig();
|
|
866
|
+
if (!config || typeof config === 'boolean') {
|
|
867
|
+
return { enabled: true, type: 'text' };
|
|
690
868
|
}
|
|
691
|
-
return
|
|
692
|
-
}, ...(ngDevMode ? [{ debugName: "
|
|
693
|
-
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
869
|
+
return config;
|
|
870
|
+
}, ...(ngDevMode ? [{ debugName: "editConfig" }] : []));
|
|
871
|
+
cfg = computed(() => {
|
|
872
|
+
const config = this.editConfig();
|
|
873
|
+
const row = this.zRow();
|
|
874
|
+
return {
|
|
875
|
+
type: resolveConfigValue(config?.type, row, 'text'),
|
|
876
|
+
size: config?.size ?? 'default',
|
|
877
|
+
placeholder: resolveConfigValue(config?.placeholder, row, ''),
|
|
878
|
+
allowClear: config?.allowClear ?? false,
|
|
879
|
+
class: resolveConfigValue(config?.class, row, ''),
|
|
880
|
+
style: resolveConfigValue(config?.style, row, {}),
|
|
881
|
+
disabled: resolveConfigValue(config?.disabled, row, false),
|
|
882
|
+
readonly: resolveConfigValue(config?.readonly, row, false),
|
|
883
|
+
};
|
|
884
|
+
}, ...(ngDevMode ? [{ debugName: "cfg" }] : []));
|
|
885
|
+
selectOptions = computed(() => {
|
|
886
|
+
const config = this.editConfig();
|
|
887
|
+
const optionsConfig = config?.options;
|
|
888
|
+
if (!optionsConfig) {
|
|
889
|
+
return [];
|
|
697
890
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
return value;
|
|
891
|
+
const rawOptions = typeof optionsConfig === 'function' ? optionsConfig(this.zRow()) : optionsConfig;
|
|
892
|
+
const labelKey = config?.optionLabelKey;
|
|
893
|
+
const valueKey = config?.optionValueKey;
|
|
894
|
+
if (!labelKey && !valueKey) {
|
|
895
|
+
return rawOptions;
|
|
704
896
|
}
|
|
705
|
-
return
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
return
|
|
897
|
+
return rawOptions.map((opt) => {
|
|
898
|
+
if (typeof opt !== 'object' || opt === null) {
|
|
899
|
+
return { label: String(opt), value: opt };
|
|
900
|
+
}
|
|
901
|
+
const obj = opt;
|
|
902
|
+
return {
|
|
903
|
+
label: String(obj[labelKey ?? 'label'] ?? ''),
|
|
904
|
+
value: obj[valueKey ?? 'value'],
|
|
905
|
+
};
|
|
906
|
+
});
|
|
907
|
+
}, ...(ngDevMode ? [{ debugName: "selectOptions" }] : []));
|
|
908
|
+
value = this._currentValue.asReadonly();
|
|
909
|
+
stringValue = computed(() => {
|
|
910
|
+
const val = this.value();
|
|
911
|
+
if (val === null || val === undefined) {
|
|
912
|
+
return '';
|
|
711
913
|
}
|
|
712
|
-
return
|
|
713
|
-
}, ...(ngDevMode ? [{ debugName: "
|
|
714
|
-
|
|
715
|
-
const
|
|
716
|
-
if (
|
|
914
|
+
return String(val);
|
|
915
|
+
}, ...(ngDevMode ? [{ debugName: "stringValue" }] : []));
|
|
916
|
+
numericValue = computed(() => {
|
|
917
|
+
const val = this.value();
|
|
918
|
+
if (val === null || val === undefined || val === '') {
|
|
717
919
|
return null;
|
|
718
920
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
if (columnDef.filterOptions && columnDef.filterOptions.length > 0) {
|
|
731
|
-
return columnDef.filterOptions;
|
|
732
|
-
}
|
|
733
|
-
const facetedValues = this.zColumn().getFacetedUniqueValues();
|
|
734
|
-
if (facetedValues && facetedValues.size > 0) {
|
|
735
|
-
const uniqueValues = Array.from(facetedValues.keys()).sort().slice(0, 5000);
|
|
736
|
-
return uniqueValues.map(v => ({ label: String(v), value: String(v) }));
|
|
737
|
-
}
|
|
738
|
-
const table = this.zTable();
|
|
739
|
-
const column = this.zColumn();
|
|
740
|
-
const { rows } = table.getRowModel();
|
|
741
|
-
if (rows.length === 0) {
|
|
742
|
-
return [];
|
|
921
|
+
const num = Number(val);
|
|
922
|
+
return isNaN(num) ? null : num;
|
|
923
|
+
}, ...(ngDevMode ? [{ debugName: "numericValue" }] : []));
|
|
924
|
+
booleanValue = computed(() => {
|
|
925
|
+
const val = this.value();
|
|
926
|
+
return Boolean(val);
|
|
927
|
+
}, ...(ngDevMode ? [{ debugName: "booleanValue" }] : []));
|
|
928
|
+
dateValue = computed(() => {
|
|
929
|
+
const val = this.value();
|
|
930
|
+
if (val instanceof Date) {
|
|
931
|
+
return val;
|
|
743
932
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
if (value !== null && value !== undefined && value !== '') {
|
|
748
|
-
uniqueValues.add(String(value));
|
|
749
|
-
}
|
|
933
|
+
if (typeof val === 'string' && val) {
|
|
934
|
+
const date = new Date(val);
|
|
935
|
+
return isNaN(date.getTime()) ? null : date;
|
|
750
936
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
937
|
+
return null;
|
|
938
|
+
}, ...(ngDevMode ? [{ debugName: "dateValue" }] : []));
|
|
939
|
+
onValueChange(newValue) {
|
|
940
|
+
const oldValue = this.zValue();
|
|
941
|
+
this._currentValue.set(newValue);
|
|
942
|
+
const config = this.editConfig();
|
|
943
|
+
if (config?.blurEdit) {
|
|
944
|
+
this._pendingBlurValue = newValue;
|
|
945
|
+
this._hasPendingBlur = true;
|
|
946
|
+
return;
|
|
757
947
|
}
|
|
948
|
+
this._valueChange$.next({ oldValue, newValue });
|
|
758
949
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
this.zColumn().setFilterValue((old) => [numValue, old?.[1]]);
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
onMaxChangeDebounced(value) {
|
|
766
|
-
this._debounce(() => {
|
|
767
|
-
const numValue = value === null || value === '' ? undefined : Number(value);
|
|
768
|
-
this.zColumn().setFilterValue((old) => [old?.[0], numValue]);
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
onTextChangeDebounced(value) {
|
|
772
|
-
this._debounce(() => {
|
|
773
|
-
this.zColumn().setFilterValue(value);
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
onSelectChange(value) {
|
|
777
|
-
if (typeof value === 'object' && value !== null && 'value' in value) {
|
|
778
|
-
this.zColumn().setFilterValue(value.value || undefined);
|
|
779
|
-
return;
|
|
950
|
+
onInputEvent(event) {
|
|
951
|
+
if (event.type === 'blur') {
|
|
952
|
+
this._handleInputBlur();
|
|
780
953
|
}
|
|
781
|
-
this.zColumn().setFilterValue(value || undefined);
|
|
782
954
|
}
|
|
783
|
-
|
|
784
|
-
if (!
|
|
785
|
-
this.zColumn().setFilterValue(undefined);
|
|
955
|
+
_handleInputBlur() {
|
|
956
|
+
if (!this._hasPendingBlur) {
|
|
786
957
|
return;
|
|
787
958
|
}
|
|
788
|
-
this.
|
|
959
|
+
const oldValue = this.zValue();
|
|
960
|
+
this._hasPendingBlur = false;
|
|
961
|
+
this._emitChange(oldValue, this._pendingBlurValue);
|
|
789
962
|
}
|
|
790
|
-
|
|
791
|
-
this.
|
|
963
|
+
onImmediateChange(newValue) {
|
|
964
|
+
const oldValue = this.zValue();
|
|
965
|
+
this._currentValue.set(newValue);
|
|
966
|
+
this._emitChange(oldValue, newValue);
|
|
792
967
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
}
|
|
798
|
-
this.zColumn().setFilterValue(undefined);
|
|
968
|
+
onSelectChange(newValue) {
|
|
969
|
+
const isWrappedObject = typeof newValue === 'object' && newValue !== null && 'value' in newValue;
|
|
970
|
+
const finalValue = isWrappedObject ? newValue.value : newValue;
|
|
971
|
+
this.onImmediateChange(finalValue);
|
|
799
972
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
973
|
+
_emitChange(oldValue, newValue) {
|
|
974
|
+
this.zChange.emit({
|
|
975
|
+
row: this.zRow(),
|
|
976
|
+
rowId: this.zRowId(),
|
|
977
|
+
rowIndex: this.zRowIndex(),
|
|
978
|
+
columnId: this.zColumnId(),
|
|
979
|
+
oldValue,
|
|
980
|
+
newValue,
|
|
981
|
+
});
|
|
805
982
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
class="
|
|
983
|
+
ngOnInit() {
|
|
984
|
+
const config = this.editConfig();
|
|
985
|
+
const debounceMs = config?.debounceTime ?? 0;
|
|
986
|
+
this._valueChange$
|
|
987
|
+
.pipe(debounceTime(debounceMs), distinctUntilChanged((a, b) => a.newValue === b.newValue), takeUntilDestroyed(this._destroyRef))
|
|
988
|
+
.subscribe(({ oldValue, newValue }) => {
|
|
989
|
+
this._emitChange(oldValue, newValue);
|
|
990
|
+
});
|
|
991
|
+
this._destroyRef.onDestroy(() => {
|
|
992
|
+
this._valueChange$.complete();
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableEditCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
996
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableEditCellComponent, isStandalone: true, selector: "z-table-edit-cell", inputs: { zRow: { classPropertyName: "zRow", publicName: "zRow", isSignal: true, isRequired: true, transformFunction: null }, zRowId: { classPropertyName: "zRowId", publicName: "zRowId", isSignal: true, isRequired: true, transformFunction: null }, zRowIndex: { classPropertyName: "zRowIndex", publicName: "zRowIndex", isSignal: true, isRequired: true, transformFunction: null }, zColumnId: { classPropertyName: "zColumnId", publicName: "zColumnId", isSignal: true, isRequired: true, transformFunction: null }, zValue: { classPropertyName: "zValue", publicName: "zValue", isSignal: true, isRequired: false, transformFunction: null }, zEditConfig: { classPropertyName: "zEditConfig", publicName: "zEditConfig", isSignal: true, isRequired: false, transformFunction: null }, zRowUpdate: { classPropertyName: "zRowUpdate", publicName: "zRowUpdate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zChange: "zChange" }, host: { classAttribute: "z-table-edit-cell block" }, ngImport: i0, template: `
|
|
997
|
+
@switch (cfg().type) {
|
|
998
|
+
@case ('checkbox') {
|
|
999
|
+
<div class="flex items-center justify-center">
|
|
1000
|
+
<z-checkbox
|
|
1001
|
+
[zSize]="cfg().size"
|
|
1002
|
+
[class]="cfg().class"
|
|
1003
|
+
[ngStyle]="cfg().style"
|
|
1004
|
+
[zChecked]="booleanValue()"
|
|
1005
|
+
[zDisabled]="cfg().disabled || cfg().readonly"
|
|
1006
|
+
(zChange)="onImmediateChange($event)"
|
|
826
1007
|
/>
|
|
827
1008
|
</div>
|
|
828
1009
|
}
|
|
829
|
-
@case ('
|
|
830
|
-
<
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
/>
|
|
1010
|
+
@case ('toggle') {
|
|
1011
|
+
<div class="flex items-center justify-center">
|
|
1012
|
+
<z-switch
|
|
1013
|
+
[zSize]="cfg().size"
|
|
1014
|
+
[class]="cfg().class"
|
|
1015
|
+
[ngStyle]="cfg().style"
|
|
1016
|
+
[zChecked]="booleanValue()"
|
|
1017
|
+
[zDisabled]="cfg().disabled || cfg().readonly"
|
|
1018
|
+
(zChange)="onImmediateChange($event)"
|
|
1019
|
+
/>
|
|
1020
|
+
</div>
|
|
841
1021
|
}
|
|
842
|
-
@case ('
|
|
843
|
-
<z-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
[
|
|
849
|
-
[
|
|
850
|
-
[
|
|
851
|
-
|
|
1022
|
+
@case ('number') {
|
|
1023
|
+
<z-input
|
|
1024
|
+
zType="number"
|
|
1025
|
+
[class]="cfg().class"
|
|
1026
|
+
[ngStyle]="cfg().style"
|
|
1027
|
+
[zSize]="cfg().size"
|
|
1028
|
+
[zPlaceholder]="cfg().placeholder"
|
|
1029
|
+
[zMin]="editConfig().min"
|
|
1030
|
+
[zMax]="editConfig().max"
|
|
1031
|
+
[zStep]="editConfig().step ?? 1"
|
|
1032
|
+
[zThousandSeparator]="editConfig().thousandSeparator || ','"
|
|
1033
|
+
[zDisabled]="cfg().disabled"
|
|
1034
|
+
[zReadonly]="cfg().readonly"
|
|
1035
|
+
[ngModel]="numericValue()"
|
|
1036
|
+
(ngModelChange)="onValueChange($event)"
|
|
1037
|
+
(zEvent)="onInputEvent($event)"
|
|
852
1038
|
class="w-full"
|
|
853
1039
|
/>
|
|
854
1040
|
}
|
|
855
1041
|
@case ('select') {
|
|
856
1042
|
<z-select
|
|
857
|
-
|
|
858
|
-
|
|
1043
|
+
[class]="cfg().class"
|
|
1044
|
+
[ngStyle]="cfg().style"
|
|
1045
|
+
[zSize]="cfg().size"
|
|
859
1046
|
[zWrap]="false"
|
|
860
1047
|
[zScrollClose]="true"
|
|
861
1048
|
[zOptions]="selectOptions()"
|
|
862
|
-
[
|
|
863
|
-
[
|
|
864
|
-
[
|
|
1049
|
+
[zAllowClear]="cfg().allowClear"
|
|
1050
|
+
[zDisabled]="cfg().disabled"
|
|
1051
|
+
[zReadonly]="cfg().readonly"
|
|
1052
|
+
[ngModel]="value()"
|
|
1053
|
+
[zPlaceholder]="cfg().placeholder"
|
|
865
1054
|
(ngModelChange)="onSelectChange($event)"
|
|
866
1055
|
class="w-full min-w-0"
|
|
867
1056
|
/>
|
|
868
1057
|
}
|
|
869
|
-
@case ('
|
|
870
|
-
<z-
|
|
871
|
-
zMode="
|
|
872
|
-
|
|
1058
|
+
@case ('date') {
|
|
1059
|
+
<z-calendar
|
|
1060
|
+
zMode="single"
|
|
1061
|
+
[class]="cfg().class"
|
|
1062
|
+
[ngStyle]="cfg().style"
|
|
1063
|
+
[zSize]="cfg().size"
|
|
873
1064
|
zAllowClear
|
|
874
|
-
|
|
875
|
-
[zScrollClose]="true"
|
|
876
|
-
[zOptions]="selectOptions()"
|
|
877
|
-
[zMaxTagCount]="1"
|
|
878
|
-
[zMaxVisible]="30"
|
|
879
|
-
[ngModel]="multiSelectValue()"
|
|
880
|
-
[zPlaceholder]="'i18n_z_ui_common_select' | translate"
|
|
881
|
-
(ngModelChange)="onMultiSelectChange($event)"
|
|
882
|
-
class="w-full min-w-0"
|
|
883
|
-
/>
|
|
884
|
-
}
|
|
885
|
-
@case ('tags') {
|
|
886
|
-
<z-select
|
|
887
|
-
zMode="tags"
|
|
888
|
-
zSize="sm"
|
|
889
|
-
[zOptions]="selectOptions()"
|
|
890
|
-
[zWrap]="false"
|
|
1065
|
+
zValueType="date"
|
|
891
1066
|
[zScrollClose]="true"
|
|
892
|
-
[
|
|
893
|
-
[
|
|
894
|
-
[zPlaceholder]="
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
[ngModel]="multiSelectValue()"
|
|
1067
|
+
[zDisabled]="cfg().disabled"
|
|
1068
|
+
[zReadonly]="cfg().readonly"
|
|
1069
|
+
[zPlaceholder]="cfg().placeholder"
|
|
1070
|
+
[ngModel]="dateValue()"
|
|
1071
|
+
(ngModelChange)="onImmediateChange($event)"
|
|
1072
|
+
class="w-full"
|
|
899
1073
|
/>
|
|
900
1074
|
}
|
|
901
|
-
@case ('
|
|
1075
|
+
@case ('textarea') {
|
|
902
1076
|
<z-input
|
|
903
|
-
zType="
|
|
904
|
-
zSize="
|
|
905
|
-
|
|
906
|
-
[
|
|
907
|
-
[
|
|
908
|
-
|
|
1077
|
+
zType="textarea"
|
|
1078
|
+
[zSize]="cfg().size"
|
|
1079
|
+
[class]="cfg().class"
|
|
1080
|
+
[ngStyle]="cfg().style"
|
|
1081
|
+
[zPlaceholder]="cfg().placeholder"
|
|
1082
|
+
[zRows]="editConfig().rows ?? 2"
|
|
1083
|
+
[zDisabled]="cfg().disabled"
|
|
1084
|
+
[zReadonly]="cfg().readonly"
|
|
1085
|
+
[ngModel]="stringValue()"
|
|
1086
|
+
(ngModelChange)="onValueChange($event)"
|
|
1087
|
+
(zEvent)="onInputEvent($event)"
|
|
909
1088
|
class="w-full"
|
|
910
1089
|
/>
|
|
911
1090
|
}
|
|
912
1091
|
@default {
|
|
913
1092
|
<z-input
|
|
914
1093
|
zType="text"
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
[
|
|
918
|
-
[
|
|
919
|
-
|
|
1094
|
+
[class]="cfg().class"
|
|
1095
|
+
[ngStyle]="cfg().style"
|
|
1096
|
+
[zSize]="cfg().size"
|
|
1097
|
+
[zPlaceholder]="cfg().placeholder"
|
|
1098
|
+
[zDisabled]="cfg().disabled"
|
|
1099
|
+
[zReadonly]="cfg().readonly"
|
|
1100
|
+
[ngModel]="stringValue()"
|
|
1101
|
+
(ngModelChange)="onValueChange($event)"
|
|
1102
|
+
(zEvent)="onInputEvent($event)"
|
|
920
1103
|
class="w-full"
|
|
921
1104
|
/>
|
|
922
1105
|
}
|
|
923
1106
|
}
|
|
924
|
-
`, isInline: true, styles: [":host{
|
|
1107
|
+
`, isInline: true, styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ZInputComponent, selector: "z-input", inputs: ["class", "zType", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zPrefix", "zSuffix", "zMin", "zMax", "zStep", "zShowArrows", "zMask", "zDecimalPlaces", "zAllowNegative", "zThousandSeparator", "zDecimalMarker", "zValidators", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zShowPasswordToggle", "zSearch", "zDebounce", "zAutofocus", "zAutoComplete", "zAllowClear", "zAutoSizeContent", "zRows", "zResize", "zMaxLength", "zAutoSuggest"], outputs: ["zOnSearch", "zOnChange", "zControl", "zEvent"], exportAs: ["zInput"] }, { kind: "component", type: ZSelectComponent, selector: "z-select", inputs: ["class", "zMode", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zLoading", "zPrefix", "zAllowClear", "zWrap", "zShowSearch", "zPlaceholderSearch", "zDebounce", "zNotFoundText", "zEmptyText", "zEmptyIcon", "zMaxTagCount", "zDropdownMaxHeight", "zOptionHeight", "zVirtualScroll", "zShowAction", "zOptions", "zTranslateLabels", "zKey", "zSearchServer", "zLoadingMore", "zEnableLoadMore", "zScrollDistance", "zMaxVisible", "zScrollClose", "zSelectedTemplate", "zOptionTemplate", "zActionTemplate", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zValidators"], outputs: ["zOnSearch", "zOnLoadMore", "zControl", "zEvent"], exportAs: ["zSelect"] }, { kind: "component", type: ZCalendarComponent, selector: "z-calendar", inputs: ["class", "zMode", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zShowTime", "zTimeFormat", "zShowHour", "zShowMinute", "zShowSecond", "zQuickSelect", "zAllowClear", "zFormat", "zMinDate", "zMaxDate", "zValueType", "zValidators", "zShowOk", "zOkText", "zShowCancel", "zCancelText", "zDisabledDate", "zScrollClose", "zDefaultTime", "zRangeDefaultTime"], outputs: ["zControl", "zChange", "zEvent"], exportAs: ["zCalendar"] }, { kind: "component", type: ZCheckboxComponent, selector: "z-checkbox", inputs: ["class", "zType", "zSize", "zLabel", "zText", "zDisabled", "zIndeterminate", "zValue", "zOptions", "zOrientation", "zCheckAll", "zCheckAllText", "zChecked", "zGroupValue"], outputs: ["zChange", "zGroupChange", "zControl", "zEvent", "zCheckedChange", "zGroupValueChange"] }, { kind: "component", type: ZSwitchComponent, selector: "z-switch", inputs: ["class", "zSize", "zLabel", "zText", "zDisabled", "zLoading", "zTextPosition", "zChecked"], outputs: ["zChange", "zEvent", "zCheckedChange"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
925
1108
|
}
|
|
926
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type:
|
|
1109
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableEditCellComponent, decorators: [{
|
|
927
1110
|
type: Component,
|
|
928
|
-
args: [{ selector: 'z-table-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
<z-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
[
|
|
944
|
-
[
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
/>
|
|
948
|
-
</div>
|
|
949
|
-
}
|
|
950
|
-
@case ('date') {
|
|
951
|
-
<z-calendar
|
|
952
|
-
zMode="single"
|
|
953
|
-
zSize="sm"
|
|
954
|
-
zAllowClear
|
|
955
|
-
zValueType="date"
|
|
956
|
-
[zScrollClose]="true"
|
|
957
|
-
[zPlaceholder]="'i18n_z_ui_table_filter_date' | translate"
|
|
958
|
-
[ngModel]="dateValue()"
|
|
959
|
-
(ngModelChange)="onDateChange($event)"
|
|
960
|
-
class="w-full"
|
|
961
|
-
/>
|
|
1111
|
+
args: [{ selector: 'z-table-edit-cell', imports: [
|
|
1112
|
+
FormsModule,
|
|
1113
|
+
ZInputComponent,
|
|
1114
|
+
ZSelectComponent,
|
|
1115
|
+
ZCalendarComponent,
|
|
1116
|
+
ZCheckboxComponent,
|
|
1117
|
+
ZSwitchComponent,
|
|
1118
|
+
NgStyle,
|
|
1119
|
+
], standalone: true, template: `
|
|
1120
|
+
@switch (cfg().type) {
|
|
1121
|
+
@case ('checkbox') {
|
|
1122
|
+
<div class="flex items-center justify-center">
|
|
1123
|
+
<z-checkbox
|
|
1124
|
+
[zSize]="cfg().size"
|
|
1125
|
+
[class]="cfg().class"
|
|
1126
|
+
[ngStyle]="cfg().style"
|
|
1127
|
+
[zChecked]="booleanValue()"
|
|
1128
|
+
[zDisabled]="cfg().disabled || cfg().readonly"
|
|
1129
|
+
(zChange)="onImmediateChange($event)"
|
|
1130
|
+
/>
|
|
1131
|
+
</div>
|
|
962
1132
|
}
|
|
963
|
-
@case ('
|
|
964
|
-
<
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1133
|
+
@case ('toggle') {
|
|
1134
|
+
<div class="flex items-center justify-center">
|
|
1135
|
+
<z-switch
|
|
1136
|
+
[zSize]="cfg().size"
|
|
1137
|
+
[class]="cfg().class"
|
|
1138
|
+
[ngStyle]="cfg().style"
|
|
1139
|
+
[zChecked]="booleanValue()"
|
|
1140
|
+
[zDisabled]="cfg().disabled || cfg().readonly"
|
|
1141
|
+
(zChange)="onImmediateChange($event)"
|
|
1142
|
+
/>
|
|
1143
|
+
</div>
|
|
1144
|
+
}
|
|
1145
|
+
@case ('number') {
|
|
1146
|
+
<z-input
|
|
1147
|
+
zType="number"
|
|
1148
|
+
[class]="cfg().class"
|
|
1149
|
+
[ngStyle]="cfg().style"
|
|
1150
|
+
[zSize]="cfg().size"
|
|
1151
|
+
[zPlaceholder]="cfg().placeholder"
|
|
1152
|
+
[zMin]="editConfig().min"
|
|
1153
|
+
[zMax]="editConfig().max"
|
|
1154
|
+
[zStep]="editConfig().step ?? 1"
|
|
1155
|
+
[zThousandSeparator]="editConfig().thousandSeparator || ','"
|
|
1156
|
+
[zDisabled]="cfg().disabled"
|
|
1157
|
+
[zReadonly]="cfg().readonly"
|
|
1158
|
+
[ngModel]="numericValue()"
|
|
1159
|
+
(ngModelChange)="onValueChange($event)"
|
|
1160
|
+
(zEvent)="onInputEvent($event)"
|
|
973
1161
|
class="w-full"
|
|
974
1162
|
/>
|
|
975
1163
|
}
|
|
976
1164
|
@case ('select') {
|
|
977
1165
|
<z-select
|
|
978
|
-
|
|
979
|
-
|
|
1166
|
+
[class]="cfg().class"
|
|
1167
|
+
[ngStyle]="cfg().style"
|
|
1168
|
+
[zSize]="cfg().size"
|
|
980
1169
|
[zWrap]="false"
|
|
981
1170
|
[zScrollClose]="true"
|
|
982
1171
|
[zOptions]="selectOptions()"
|
|
983
|
-
[
|
|
984
|
-
[
|
|
985
|
-
[
|
|
1172
|
+
[zAllowClear]="cfg().allowClear"
|
|
1173
|
+
[zDisabled]="cfg().disabled"
|
|
1174
|
+
[zReadonly]="cfg().readonly"
|
|
1175
|
+
[ngModel]="value()"
|
|
1176
|
+
[zPlaceholder]="cfg().placeholder"
|
|
986
1177
|
(ngModelChange)="onSelectChange($event)"
|
|
987
1178
|
class="w-full min-w-0"
|
|
988
1179
|
/>
|
|
989
1180
|
}
|
|
990
|
-
@case ('
|
|
991
|
-
<z-
|
|
992
|
-
zMode="
|
|
993
|
-
|
|
1181
|
+
@case ('date') {
|
|
1182
|
+
<z-calendar
|
|
1183
|
+
zMode="single"
|
|
1184
|
+
[class]="cfg().class"
|
|
1185
|
+
[ngStyle]="cfg().style"
|
|
1186
|
+
[zSize]="cfg().size"
|
|
994
1187
|
zAllowClear
|
|
995
|
-
|
|
996
|
-
[zScrollClose]="true"
|
|
997
|
-
[zOptions]="selectOptions()"
|
|
998
|
-
[zMaxTagCount]="1"
|
|
999
|
-
[zMaxVisible]="30"
|
|
1000
|
-
[ngModel]="multiSelectValue()"
|
|
1001
|
-
[zPlaceholder]="'i18n_z_ui_common_select' | translate"
|
|
1002
|
-
(ngModelChange)="onMultiSelectChange($event)"
|
|
1003
|
-
class="w-full min-w-0"
|
|
1004
|
-
/>
|
|
1005
|
-
}
|
|
1006
|
-
@case ('tags') {
|
|
1007
|
-
<z-select
|
|
1008
|
-
zMode="tags"
|
|
1009
|
-
zSize="sm"
|
|
1010
|
-
[zOptions]="selectOptions()"
|
|
1011
|
-
[zWrap]="false"
|
|
1188
|
+
zValueType="date"
|
|
1012
1189
|
[zScrollClose]="true"
|
|
1013
|
-
[
|
|
1014
|
-
[
|
|
1015
|
-
[zPlaceholder]="
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
(ngModelChange)="onMultiSelectChange($event)"
|
|
1019
|
-
[ngModel]="multiSelectValue()"
|
|
1020
|
-
/>
|
|
1021
|
-
}
|
|
1022
|
-
@case ('number') {
|
|
1023
|
-
<z-input
|
|
1024
|
-
zType="number"
|
|
1025
|
-
zSize="sm"
|
|
1026
|
-
zAllowClear
|
|
1027
|
-
[zPlaceholder]="'i18n_z_ui_table_filter_search' | translate"
|
|
1028
|
-
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
1029
|
-
(ngModelChange)="onTextChangeDebounced($event)"
|
|
1190
|
+
[zDisabled]="cfg().disabled"
|
|
1191
|
+
[zReadonly]="cfg().readonly"
|
|
1192
|
+
[zPlaceholder]="cfg().placeholder"
|
|
1193
|
+
[ngModel]="dateValue()"
|
|
1194
|
+
(ngModelChange)="onImmediateChange($event)"
|
|
1030
1195
|
class="w-full"
|
|
1031
1196
|
/>
|
|
1032
1197
|
}
|
|
1033
|
-
@
|
|
1198
|
+
@case ('textarea') {
|
|
1034
1199
|
<z-input
|
|
1035
|
-
zType="
|
|
1036
|
-
zSize="
|
|
1037
|
-
|
|
1038
|
-
[
|
|
1039
|
-
[
|
|
1040
|
-
|
|
1200
|
+
zType="textarea"
|
|
1201
|
+
[zSize]="cfg().size"
|
|
1202
|
+
[class]="cfg().class"
|
|
1203
|
+
[ngStyle]="cfg().style"
|
|
1204
|
+
[zPlaceholder]="cfg().placeholder"
|
|
1205
|
+
[zRows]="editConfig().rows ?? 2"
|
|
1206
|
+
[zDisabled]="cfg().disabled"
|
|
1207
|
+
[zReadonly]="cfg().readonly"
|
|
1208
|
+
[ngModel]="stringValue()"
|
|
1209
|
+
(ngModelChange)="onValueChange($event)"
|
|
1210
|
+
(zEvent)="onInputEvent($event)"
|
|
1211
|
+
class="w-full"
|
|
1212
|
+
/>
|
|
1213
|
+
}
|
|
1214
|
+
@default {
|
|
1215
|
+
<z-input
|
|
1216
|
+
zType="text"
|
|
1217
|
+
[class]="cfg().class"
|
|
1218
|
+
[ngStyle]="cfg().style"
|
|
1219
|
+
[zSize]="cfg().size"
|
|
1220
|
+
[zPlaceholder]="cfg().placeholder"
|
|
1221
|
+
[zDisabled]="cfg().disabled"
|
|
1222
|
+
[zReadonly]="cfg().readonly"
|
|
1223
|
+
[ngModel]="stringValue()"
|
|
1224
|
+
(ngModelChange)="onValueChange($event)"
|
|
1225
|
+
(zEvent)="onInputEvent($event)"
|
|
1041
1226
|
class="w-full"
|
|
1042
1227
|
/>
|
|
1043
1228
|
}
|
|
1044
1229
|
}
|
|
1045
|
-
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
1046
|
-
class: 'z-table-
|
|
1047
|
-
}, styles: [":host{
|
|
1048
|
-
}], propDecorators: {
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
.
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
})
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
}
|
|
1065
|
-
return col;
|
|
1066
|
-
});
|
|
1067
|
-
const isObjectConfig = (config) => {
|
|
1068
|
-
if (!config || typeof config !== 'object') {
|
|
1069
|
-
return false;
|
|
1070
|
-
}
|
|
1071
|
-
return config.constructor === Object;
|
|
1072
|
-
};
|
|
1073
|
-
const isHeaderConfig = (config) => isObjectConfig(config);
|
|
1074
|
-
const isBodyConfig = (config) => isObjectConfig(config);
|
|
1075
|
-
const isFooterConfig = (config) => isObjectConfig(config);
|
|
1076
|
-
const getHeaderOrFooterConfigInternal = (col, type) => {
|
|
1077
|
-
const empty = {
|
|
1078
|
-
content: undefined,
|
|
1079
|
-
class: undefined,
|
|
1080
|
-
style: undefined,
|
|
1081
|
-
align: undefined,
|
|
1082
|
-
tooltip: undefined,
|
|
1083
|
-
rowSpan: undefined,
|
|
1084
|
-
colSpan: undefined,
|
|
1085
|
-
contentClass: undefined,
|
|
1086
|
-
contentStyle: undefined,
|
|
1087
|
-
};
|
|
1088
|
-
if (!col) {
|
|
1089
|
-
return empty;
|
|
1090
|
-
}
|
|
1091
|
-
const config = type === 'header' ? col.header : col.footer;
|
|
1092
|
-
const isConfigFn = type === 'header' ? isHeaderConfig : isFooterConfig;
|
|
1093
|
-
if (!isConfigFn(config)) {
|
|
1094
|
-
return { ...empty, content: config };
|
|
1095
|
-
}
|
|
1096
|
-
const typedConfig = config;
|
|
1097
|
-
return {
|
|
1098
|
-
content: typedConfig.content,
|
|
1099
|
-
class: typedConfig.class,
|
|
1100
|
-
style: typedConfig.style,
|
|
1101
|
-
align: typedConfig.align,
|
|
1102
|
-
tooltip: typedConfig.tooltip,
|
|
1103
|
-
rowSpan: typedConfig.rowSpan,
|
|
1104
|
-
colSpan: typedConfig.colSpan,
|
|
1105
|
-
contentClass: typedConfig.contentClass,
|
|
1106
|
-
contentStyle: typedConfig.contentStyle,
|
|
1107
|
-
};
|
|
1108
|
-
};
|
|
1109
|
-
const getHeaderConfig = (col) => getHeaderOrFooterConfigInternal(col, 'header');
|
|
1110
|
-
const getBodyConfig = (col, ctx) => {
|
|
1111
|
-
const empty = {
|
|
1112
|
-
content: undefined,
|
|
1113
|
-
class: undefined,
|
|
1114
|
-
style: undefined,
|
|
1115
|
-
align: undefined,
|
|
1116
|
-
rowSpan: undefined,
|
|
1117
|
-
colSpan: undefined,
|
|
1118
|
-
contentClass: undefined,
|
|
1119
|
-
contentStyle: undefined,
|
|
1120
|
-
tooltip: undefined,
|
|
1121
|
-
};
|
|
1122
|
-
if (!col) {
|
|
1123
|
-
return empty;
|
|
1124
|
-
}
|
|
1125
|
-
if (!isBodyConfig(col.body)) {
|
|
1126
|
-
return { ...empty, content: col.body };
|
|
1127
|
-
}
|
|
1128
|
-
const { body } = col;
|
|
1129
|
-
const rowSpan = typeof body.rowSpan === 'function' && ctx ? body.rowSpan(ctx) : body.rowSpan;
|
|
1130
|
-
const colSpan = typeof body.colSpan === 'function' && ctx ? body.colSpan(ctx) : body.colSpan;
|
|
1131
|
-
const classValue = typeof body.class === 'function' && ctx ? body.class(ctx) : body.class;
|
|
1132
|
-
const styleValue = typeof body.style === 'function' && ctx ? body.style(ctx) : body.style;
|
|
1133
|
-
const contentClass = typeof body.contentClass === 'function' && ctx ? body.contentClass(ctx) : body.contentClass;
|
|
1134
|
-
const contentStyle = typeof body.contentStyle === 'function' && ctx ? body.contentStyle(ctx) : body.contentStyle;
|
|
1135
|
-
const tooltip = typeof body.tooltip === 'function' && ctx ? body.tooltip(ctx) : body.tooltip;
|
|
1136
|
-
return {
|
|
1137
|
-
content: body.content,
|
|
1138
|
-
class: classValue,
|
|
1139
|
-
style: styleValue,
|
|
1140
|
-
align: body.align,
|
|
1141
|
-
rowSpan: typeof rowSpan === 'number' ? rowSpan : undefined,
|
|
1142
|
-
colSpan: typeof colSpan === 'number' ? colSpan : undefined,
|
|
1143
|
-
contentClass,
|
|
1144
|
-
contentStyle: contentStyle,
|
|
1145
|
-
tooltip,
|
|
1146
|
-
};
|
|
1147
|
-
};
|
|
1148
|
-
const getFooterConfig = (col) => getHeaderOrFooterConfigInternal(col, 'footer');
|
|
1149
|
-
const getHeaderContent = (col) => getHeaderConfig(col).content;
|
|
1150
|
-
const getBodyContent = (col) => {
|
|
1151
|
-
if (!col?.body) {
|
|
1152
|
-
return undefined;
|
|
1153
|
-
}
|
|
1154
|
-
return isBodyConfig(col.body) ? col.body.content : col.body;
|
|
1155
|
-
};
|
|
1156
|
-
const getFooterContent = (col) => getFooterConfig(col).content;
|
|
1157
|
-
const getBodyRowSpan = (col, ctx) => getBodyConfig(col, ctx).rowSpan;
|
|
1158
|
-
const getBodyColSpan = (col, ctx) => getBodyConfig(col, ctx).colSpan;
|
|
1159
|
-
const getHeaderRowSpan = (col) => getHeaderConfig(col).rowSpan;
|
|
1160
|
-
const getHeaderColSpan = (col) => getHeaderConfig(col).colSpan;
|
|
1161
|
-
const getFooterRowSpan = (col) => getFooterConfig(col).rowSpan;
|
|
1162
|
-
const getFooterColSpan = (col) => getFooterConfig(col).colSpan;
|
|
1163
|
-
function parseIconString(content) {
|
|
1164
|
-
if (!content || typeof content !== 'string') {
|
|
1165
|
-
return [{ type: 'text', value: content || '' }];
|
|
1166
|
-
}
|
|
1167
|
-
const parts = [];
|
|
1168
|
-
const iconRegex = /\[icon:([^\]]+)\]/g;
|
|
1169
|
-
let lastIndex = 0;
|
|
1170
|
-
let match;
|
|
1171
|
-
while ((match = iconRegex.exec(content)) !== null) {
|
|
1172
|
-
if (match.index > lastIndex) {
|
|
1173
|
-
const textPart = content.slice(lastIndex, match.index);
|
|
1174
|
-
if (textPart) {
|
|
1175
|
-
parts.push({ type: 'text', value: textPart });
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
const iconContent = match[1];
|
|
1179
|
-
const attrs = iconContent.split('|');
|
|
1180
|
-
const iconName = attrs[0];
|
|
1181
|
-
const iconPart = { type: 'icon', value: iconName };
|
|
1182
|
-
for (let i = 1; i < attrs.length; i++) {
|
|
1183
|
-
const [key, val] = attrs[i].split(':');
|
|
1184
|
-
if (key === 'size') {
|
|
1185
|
-
iconPart.size = parseInt(val, 10);
|
|
1186
|
-
}
|
|
1187
|
-
if (key === 'class') {
|
|
1188
|
-
iconPart.class = val;
|
|
1189
|
-
}
|
|
1190
|
-
if (key === 'strokeWidth') {
|
|
1191
|
-
iconPart.strokeWidth = parseFloat(val);
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
parts.push(iconPart);
|
|
1195
|
-
lastIndex = match.index + match[0].length;
|
|
1196
|
-
}
|
|
1197
|
-
if (lastIndex < content.length) {
|
|
1198
|
-
parts.push({ type: 'text', value: content.slice(lastIndex) });
|
|
1199
|
-
}
|
|
1200
|
-
return parts.length > 0 ? parts : [{ type: 'text', value: content }];
|
|
1201
|
-
}
|
|
1202
|
-
function stripIconSyntax(content) {
|
|
1203
|
-
if (!content || typeof content !== 'string') {
|
|
1204
|
-
return content || '';
|
|
1205
|
-
}
|
|
1206
|
-
return content.replace(/\[icon:[^\]]+\]/g, '').trim();
|
|
1207
|
-
}
|
|
1208
|
-
function hasIconSyntax(content) {
|
|
1209
|
-
if (!content || typeof content !== 'string') {
|
|
1210
|
-
return false;
|
|
1211
|
-
}
|
|
1212
|
-
return /\[icon:[^\]]+\]/.test(content);
|
|
1213
|
-
}
|
|
1214
|
-
const findColumnConfig = (columnId, columns) => {
|
|
1215
|
-
for (const col of columns) {
|
|
1216
|
-
if (col.id === columnId) {
|
|
1217
|
-
return col;
|
|
1230
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
1231
|
+
class: 'z-table-edit-cell block',
|
|
1232
|
+
}, styles: [":host{display:block;width:100%}\n"] }]
|
|
1233
|
+
}], ctorParameters: () => [], propDecorators: { zRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRow", required: true }] }], zRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRowId", required: true }] }], zRowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRowIndex", required: true }] }], zColumnId: [{ type: i0.Input, args: [{ isSignal: true, alias: "zColumnId", required: true }] }], zValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "zValue", required: false }] }], zEditConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "zEditConfig", required: false }] }], zRowUpdate: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRowUpdate", required: false }] }], zChange: [{ type: i0.Output, args: ["zChange"] }] } });
|
|
1234
|
+
|
|
1235
|
+
class ZTableFilterComponent {
|
|
1236
|
+
zColumn = input.required(...(ngDevMode ? [{ debugName: "zColumn" }] : []));
|
|
1237
|
+
zTable = input.required(...(ngDevMode ? [{ debugName: "zTable" }] : []));
|
|
1238
|
+
_debounceTimeout = null;
|
|
1239
|
+
_debounceMs = 300;
|
|
1240
|
+
_columnDef = computed(() => this.zColumn().columnDef, ...(ngDevMode ? [{ debugName: "_columnDef" }] : []));
|
|
1241
|
+
filterType = computed(() => this._columnDef().filterType, ...(ngDevMode ? [{ debugName: "filterType" }] : []));
|
|
1242
|
+
columnFilterValue = computed(() => this.zColumn().getFilterValue(), ...(ngDevMode ? [{ debugName: "columnFilterValue" }] : []));
|
|
1243
|
+
minValue = computed(() => this.zColumn().getFacetedMinMaxValues()?.[0] ?? '', ...(ngDevMode ? [{ debugName: "minValue" }] : []));
|
|
1244
|
+
maxValue = computed(() => this.zColumn().getFacetedMinMaxValues()?.[1] ?? '', ...(ngDevMode ? [{ debugName: "maxValue" }] : []));
|
|
1245
|
+
rangeMinValue = computed(() => {
|
|
1246
|
+
const value = this.columnFilterValue();
|
|
1247
|
+
if (!Array.isArray(value)) {
|
|
1248
|
+
return '';
|
|
1218
1249
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1250
|
+
return value[0] ?? '';
|
|
1251
|
+
}, ...(ngDevMode ? [{ debugName: "rangeMinValue" }] : []));
|
|
1252
|
+
rangeMaxValue = computed(() => {
|
|
1253
|
+
const value = this.columnFilterValue();
|
|
1254
|
+
if (!Array.isArray(value)) {
|
|
1255
|
+
return '';
|
|
1224
1256
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
const next = last.isPlaceholder && last.colSpan === 1 && last.subHeaders.length === 1 ? last.subHeaders[0] : null;
|
|
1232
|
-
if (!next) {
|
|
1233
|
-
return last === header ? null : last;
|
|
1257
|
+
return value[1] ?? '';
|
|
1258
|
+
}, ...(ngDevMode ? [{ debugName: "rangeMaxValue" }] : []));
|
|
1259
|
+
dateValue = computed(() => {
|
|
1260
|
+
const value = this.columnFilterValue();
|
|
1261
|
+
if (value instanceof Date) {
|
|
1262
|
+
return value;
|
|
1234
1263
|
}
|
|
1235
|
-
last = next;
|
|
1236
|
-
}
|
|
1237
|
-
};
|
|
1238
|
-
const tableHeaderRowSpan = (header) => {
|
|
1239
|
-
const deepest = deepestHeader(header);
|
|
1240
|
-
const rowSpan = (deepest ? deepest.depth - header.depth : 0) + 1;
|
|
1241
|
-
const above = header.depth - header.column.depth;
|
|
1242
|
-
return above > 1 ? null : rowSpan;
|
|
1243
|
-
};
|
|
1244
|
-
const calculateCellRowSpan = (cell, columnConfig, allRows) => {
|
|
1245
|
-
if (!columnConfig?.body || !isBodyConfig(columnConfig.body)) {
|
|
1246
|
-
return null;
|
|
1247
|
-
}
|
|
1248
|
-
const rowSpanValue = columnConfig.body.rowSpan;
|
|
1249
|
-
if (!rowSpanValue) {
|
|
1250
1264
|
return null;
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
if (rowSpanValue > 1) {
|
|
1257
|
-
const currentRowId = cell.row.id;
|
|
1258
|
-
const filteredIndex = allRows.findIndex(r => r.id === currentRowId);
|
|
1259
|
-
if (filteredIndex === -1) {
|
|
1260
|
-
return null;
|
|
1261
|
-
}
|
|
1262
|
-
if (filteredIndex % rowSpanValue !== 0) {
|
|
1263
|
-
return null;
|
|
1265
|
+
}, ...(ngDevMode ? [{ debugName: "dateValue" }] : []));
|
|
1266
|
+
dateRangeValue = computed(() => {
|
|
1267
|
+
const value = this.columnFilterValue();
|
|
1268
|
+
if (value && typeof value === 'object' && 'start' in value && 'end' in value) {
|
|
1269
|
+
return value;
|
|
1264
1270
|
}
|
|
1265
|
-
const remainingRows = allRows.length - filteredIndex;
|
|
1266
|
-
const actualRowSpan = Math.min(rowSpanValue, remainingRows);
|
|
1267
|
-
return actualRowSpan > 1 ? actualRowSpan : null;
|
|
1268
|
-
}
|
|
1269
|
-
return null;
|
|
1270
|
-
};
|
|
1271
|
-
const calculateCellColSpan = (cell, columnConfig) => {
|
|
1272
|
-
if (!columnConfig?.body || !isBodyConfig(columnConfig.body)) {
|
|
1273
|
-
return null;
|
|
1274
|
-
}
|
|
1275
|
-
const colSpanValue = columnConfig.body.colSpan;
|
|
1276
|
-
if (!colSpanValue) {
|
|
1277
1271
|
return null;
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
const calculateHeaderRowSpan = (header, columnConfig, columns) => {
|
|
1283
|
-
const configRowSpan = getHeaderRowSpan(columnConfig);
|
|
1284
|
-
if (configRowSpan !== undefined && configRowSpan !== null) {
|
|
1285
|
-
return configRowSpan;
|
|
1286
|
-
}
|
|
1287
|
-
if (columns && header.column.parent) {
|
|
1288
|
-
const parentConfig = findColumnConfig(header.column.parent.id, columns);
|
|
1289
|
-
const parentRowSpan = getHeaderRowSpan(parentConfig);
|
|
1290
|
-
if (parentRowSpan && parentRowSpan >= 2) {
|
|
1272
|
+
}, ...(ngDevMode ? [{ debugName: "dateRangeValue" }] : []));
|
|
1273
|
+
selectValue = computed(() => {
|
|
1274
|
+
const value = this.columnFilterValue();
|
|
1275
|
+
if (value === null || value === undefined || value === '') {
|
|
1291
1276
|
return null;
|
|
1292
1277
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
const calculateHeaderColSpan = (header, columnConfig) => {
|
|
1300
|
-
const configColSpan = getHeaderColSpan(columnConfig);
|
|
1301
|
-
const isPinned = header.column.getIsPinned();
|
|
1302
|
-
if (isPinned === 'left' && configColSpan !== undefined && configColSpan !== null) {
|
|
1303
|
-
const allHeaders = header.headerGroup.headers;
|
|
1304
|
-
const currentIndex = allHeaders.findIndex(h => h.id === header.id);
|
|
1305
|
-
if (currentIndex === -1) {
|
|
1306
|
-
return configColSpan;
|
|
1307
|
-
}
|
|
1308
|
-
let effectiveColSpan = 0;
|
|
1309
|
-
for (let i = 0; i < configColSpan && currentIndex + i < allHeaders.length; i++) {
|
|
1310
|
-
const targetHeader = allHeaders[currentIndex + i];
|
|
1311
|
-
const targetLeafHeaders = targetHeader.getLeafHeaders();
|
|
1312
|
-
const allLeafsPinned = targetLeafHeaders.every(lh => lh.column.getIsPinned() === 'left');
|
|
1313
|
-
if (allLeafsPinned) {
|
|
1314
|
-
effectiveColSpan++;
|
|
1315
|
-
}
|
|
1316
|
-
if (!allLeafsPinned) {
|
|
1317
|
-
break;
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
return effectiveColSpan > 1 ? effectiveColSpan : null;
|
|
1321
|
-
}
|
|
1322
|
-
if (configColSpan !== undefined && configColSpan !== null) {
|
|
1323
|
-
return configColSpan;
|
|
1324
|
-
}
|
|
1325
|
-
return header.colSpan > 1 ? header.colSpan : null;
|
|
1326
|
-
};
|
|
1327
|
-
const calculateFooterRowSpan = (header, columnConfig) => {
|
|
1328
|
-
const configRowSpan = getFooterRowSpan(columnConfig);
|
|
1329
|
-
if (configRowSpan !== undefined && configRowSpan !== null) {
|
|
1330
|
-
return configRowSpan;
|
|
1331
|
-
}
|
|
1332
|
-
const footerContent = getFooterContent(columnConfig);
|
|
1333
|
-
if (footerContent !== undefined) {
|
|
1334
|
-
return 1;
|
|
1335
|
-
}
|
|
1336
|
-
if (header.colSpan > 1) {
|
|
1337
|
-
return 1;
|
|
1338
|
-
}
|
|
1339
|
-
return 1;
|
|
1340
|
-
};
|
|
1341
|
-
const calculateFooterColSpan = (header, columnConfig) => {
|
|
1342
|
-
const configColSpan = getFooterColSpan(columnConfig);
|
|
1343
|
-
const isPinned = header.column.getIsPinned();
|
|
1344
|
-
if (isPinned === 'left' && configColSpan !== undefined && configColSpan !== null) {
|
|
1345
|
-
const allHeaders = header.headerGroup.headers;
|
|
1346
|
-
const currentIndex = allHeaders.findIndex(h => h.id === header.id);
|
|
1347
|
-
if (currentIndex === -1) {
|
|
1348
|
-
return configColSpan;
|
|
1349
|
-
}
|
|
1350
|
-
let effectiveColSpan = 0;
|
|
1351
|
-
for (let i = 0; i < configColSpan && currentIndex + i < allHeaders.length; i++) {
|
|
1352
|
-
const targetHeader = allHeaders[currentIndex + i];
|
|
1353
|
-
const targetLeafHeaders = targetHeader.getLeafHeaders();
|
|
1354
|
-
const allLeafsPinned = targetLeafHeaders.every(lh => lh.column.getIsPinned() === 'left');
|
|
1355
|
-
if (allLeafsPinned) {
|
|
1356
|
-
effectiveColSpan++;
|
|
1357
|
-
}
|
|
1358
|
-
if (!allLeafsPinned) {
|
|
1359
|
-
break;
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
return effectiveColSpan > 1 ? effectiveColSpan : null;
|
|
1363
|
-
}
|
|
1364
|
-
if (configColSpan !== undefined && configColSpan !== null) {
|
|
1365
|
-
return configColSpan;
|
|
1366
|
-
}
|
|
1367
|
-
return header.colSpan > 1 ? header.colSpan : null;
|
|
1368
|
-
};
|
|
1369
|
-
const shouldCellRenderUtil = (cell, columnConfig, allRows) => {
|
|
1370
|
-
if (!columnConfig?.body || !isBodyConfig(columnConfig.body)) {
|
|
1371
|
-
return true;
|
|
1372
|
-
}
|
|
1373
|
-
const rowSpanValue = columnConfig.body.rowSpan;
|
|
1374
|
-
if (!rowSpanValue) {
|
|
1375
|
-
return true;
|
|
1376
|
-
}
|
|
1377
|
-
if (typeof rowSpanValue === 'function') {
|
|
1378
|
-
const rowSpan = rowSpanValue(cell.getContext());
|
|
1379
|
-
return rowSpan !== 0;
|
|
1380
|
-
}
|
|
1381
|
-
if (rowSpanValue > 1) {
|
|
1382
|
-
const currentRowId = cell.row.id;
|
|
1383
|
-
const filteredIndex = allRows.findIndex(r => r.id === currentRowId);
|
|
1384
|
-
if (filteredIndex === -1) {
|
|
1385
|
-
return true;
|
|
1386
|
-
}
|
|
1387
|
-
return filteredIndex % rowSpanValue === 0;
|
|
1388
|
-
}
|
|
1389
|
-
return true;
|
|
1390
|
-
};
|
|
1391
|
-
const shouldHeaderOrFooterRender = (header, allHeaders, columns, type) => {
|
|
1392
|
-
const currentIndex = allHeaders.findIndex(h => h.id === header.id);
|
|
1393
|
-
if (currentIndex <= 0) {
|
|
1394
|
-
return true;
|
|
1395
|
-
}
|
|
1396
|
-
const getConfigColSpanFn = type === 'header' ? getHeaderColSpan : getFooterColSpan;
|
|
1397
|
-
for (let i = 0; i < currentIndex; i++) {
|
|
1398
|
-
const prevHeader = allHeaders[i];
|
|
1399
|
-
const prevConfig = findColumnConfig(prevHeader.column.id, columns);
|
|
1400
|
-
const configColSpan = getConfigColSpanFn(prevConfig);
|
|
1401
|
-
if (!configColSpan || configColSpan <= 1) {
|
|
1402
|
-
continue;
|
|
1403
|
-
}
|
|
1404
|
-
const isPinned = prevHeader.column.getIsPinned();
|
|
1405
|
-
let effectiveColSpan = configColSpan;
|
|
1406
|
-
if (isPinned === 'left') {
|
|
1407
|
-
const headersInGroup = prevHeader.headerGroup.headers;
|
|
1408
|
-
const prevIdx = headersInGroup.findIndex(h => h.id === prevHeader.id);
|
|
1409
|
-
if (prevIdx !== -1) {
|
|
1410
|
-
effectiveColSpan = 0;
|
|
1411
|
-
for (let j = 0; j < configColSpan && prevIdx + j < headersInGroup.length; j++) {
|
|
1412
|
-
const targetHeader = headersInGroup[prevIdx + j];
|
|
1413
|
-
const targetLeafHeaders = targetHeader.getLeafHeaders();
|
|
1414
|
-
const allLeafsPinned = targetLeafHeaders.every(lh => lh.column.getIsPinned() === 'left');
|
|
1415
|
-
if (allLeafsPinned) {
|
|
1416
|
-
effectiveColSpan++;
|
|
1417
|
-
}
|
|
1418
|
-
if (!allLeafsPinned) {
|
|
1419
|
-
break;
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1278
|
+
return value;
|
|
1279
|
+
}, ...(ngDevMode ? [{ debugName: "selectValue" }] : []));
|
|
1280
|
+
multiSelectValue = computed(() => {
|
|
1281
|
+
const value = this.columnFilterValue();
|
|
1282
|
+
if (Array.isArray(value)) {
|
|
1283
|
+
return value;
|
|
1423
1284
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1285
|
+
return [];
|
|
1286
|
+
}, ...(ngDevMode ? [{ debugName: "multiSelectValue" }] : []));
|
|
1287
|
+
selectOptions = computed(() => {
|
|
1288
|
+
const columnDef = this._columnDef();
|
|
1289
|
+
if (columnDef.filterOptions && columnDef.filterOptions.length > 0) {
|
|
1290
|
+
return columnDef.filterOptions;
|
|
1426
1291
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
const currentIndex = allCells.findIndex(c => c.id === cell.id);
|
|
1432
|
-
if (currentIndex <= 0) {
|
|
1433
|
-
return true;
|
|
1434
|
-
}
|
|
1435
|
-
for (let i = 0; i < currentIndex; i++) {
|
|
1436
|
-
const prevCell = allCells[i];
|
|
1437
|
-
const prevConfig = findColumnConfig(prevCell.column.id, columns);
|
|
1438
|
-
const prevColSpan = getBodyColSpan(prevConfig, prevCell.getContext());
|
|
1439
|
-
if (prevColSpan && prevColSpan > 1 && i + prevColSpan > currentIndex) {
|
|
1440
|
-
return false;
|
|
1292
|
+
const facetedValues = this.zColumn().getFacetedUniqueValues();
|
|
1293
|
+
if (facetedValues && facetedValues.size > 0) {
|
|
1294
|
+
const uniqueValues = Array.from(facetedValues.keys()).sort().slice(0, 5000);
|
|
1295
|
+
return uniqueValues.map(v => ({ label: String(v), value: String(v) }));
|
|
1441
1296
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
};
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
function columnConfigToColumnDef(config) {
|
|
1448
|
-
const sortConfig = typeof config.sort === 'boolean' ? (config.sort ? { enabled: true } : undefined) : config.sort;
|
|
1449
|
-
const filterConfig = typeof config.filter === 'boolean' ? (config.filter ? { enabled: true } : undefined) : config.filter;
|
|
1450
|
-
const resolveHeaderOrFooter = (content) => {
|
|
1451
|
-
if (!content) {
|
|
1452
|
-
return undefined;
|
|
1297
|
+
const table = this.zTable();
|
|
1298
|
+
const column = this.zColumn();
|
|
1299
|
+
const { rows } = table.getRowModel();
|
|
1300
|
+
if (rows.length === 0) {
|
|
1301
|
+
return [];
|
|
1453
1302
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1303
|
+
const uniqueValues = new Set();
|
|
1304
|
+
for (const row of rows) {
|
|
1305
|
+
const value = row.getValue(column.id);
|
|
1306
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
1307
|
+
uniqueValues.add(String(value));
|
|
1308
|
+
}
|
|
1456
1309
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1310
|
+
const sortedValues = Array.from(uniqueValues).sort();
|
|
1311
|
+
return sortedValues.map(v => ({ label: String(v), value: String(v) }));
|
|
1312
|
+
}, ...(ngDevMode ? [{ debugName: "selectOptions" }] : []));
|
|
1313
|
+
ngOnDestroy() {
|
|
1314
|
+
if (this._debounceTimeout) {
|
|
1315
|
+
clearTimeout(this._debounceTimeout);
|
|
1459
1316
|
}
|
|
1460
|
-
return undefined;
|
|
1461
|
-
};
|
|
1462
|
-
const columnDef = {
|
|
1463
|
-
id: config.id,
|
|
1464
|
-
size: config.size,
|
|
1465
|
-
minSize: config.minSize ?? Z_DEFAULT_COLUMN_MIN_SIZE,
|
|
1466
|
-
maxSize: config.maxSize,
|
|
1467
|
-
enableSorting: sortConfig?.enabled ?? false,
|
|
1468
|
-
enableColumnFilter: filterConfig?.enabled ?? false,
|
|
1469
|
-
enableResizing: config.enableResizing,
|
|
1470
|
-
enablePinning: config.enablePinning,
|
|
1471
|
-
enableHiding: config.enableHiding,
|
|
1472
|
-
filterType: filterConfig?.type,
|
|
1473
|
-
filterOptions: filterConfig?.options,
|
|
1474
|
-
};
|
|
1475
|
-
if (config.accessorKey) {
|
|
1476
|
-
columnDef.accessorKey = config.accessorKey;
|
|
1477
1317
|
}
|
|
1478
|
-
|
|
1479
|
-
|
|
1318
|
+
onMinChangeDebounced(value) {
|
|
1319
|
+
this._debounce(() => {
|
|
1320
|
+
const numValue = value === null || value === '' ? undefined : Number(value);
|
|
1321
|
+
this.zColumn().setFilterValue((old) => [numValue, old?.[1]]);
|
|
1322
|
+
});
|
|
1480
1323
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1324
|
+
onMaxChangeDebounced(value) {
|
|
1325
|
+
this._debounce(() => {
|
|
1326
|
+
const numValue = value === null || value === '' ? undefined : Number(value);
|
|
1327
|
+
this.zColumn().setFilterValue((old) => [old?.[0], numValue]);
|
|
1328
|
+
});
|
|
1485
1329
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1330
|
+
onTextChangeDebounced(value) {
|
|
1331
|
+
this._debounce(() => {
|
|
1332
|
+
this.zColumn().setFilterValue(value);
|
|
1333
|
+
});
|
|
1488
1334
|
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1335
|
+
onSelectChange(value) {
|
|
1336
|
+
if (typeof value === 'object' && value !== null && 'value' in value) {
|
|
1337
|
+
this.zColumn().setFilterValue(value.value || undefined);
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
this.zColumn().setFilterValue(value || undefined);
|
|
1494
1341
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1342
|
+
onMultiSelectChange(value) {
|
|
1343
|
+
if (!value || (Array.isArray(value) && value.length === 0)) {
|
|
1344
|
+
this.zColumn().setFilterValue(undefined);
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
this.zColumn().setFilterValue(value);
|
|
1497
1348
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1349
|
+
onDateChange(value) {
|
|
1350
|
+
this.zColumn().setFilterValue(value || undefined);
|
|
1500
1351
|
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1352
|
+
onDateRangeChange(value) {
|
|
1353
|
+
if (value && (value.start || value.end)) {
|
|
1354
|
+
this.zColumn().setFilterValue(value);
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
this.zColumn().setFilterValue(undefined);
|
|
1503
1358
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
columnDef.filterFn = (row, columnId, filterValue) => {
|
|
1508
|
-
if (!filterValue || (Array.isArray(filterValue) && filterValue.length === 0)) {
|
|
1509
|
-
return true;
|
|
1510
|
-
}
|
|
1511
|
-
const cellValue = row.getValue(columnId);
|
|
1512
|
-
if (Array.isArray(filterValue)) {
|
|
1513
|
-
return filterValue.includes(cellValue);
|
|
1514
|
-
}
|
|
1515
|
-
return cellValue === filterValue;
|
|
1516
|
-
};
|
|
1517
|
-
break;
|
|
1518
|
-
case 'multi-select':
|
|
1519
|
-
case 'tags':
|
|
1520
|
-
columnDef.filterFn = (row, columnId, filterValue) => {
|
|
1521
|
-
if (!filterValue || (Array.isArray(filterValue) && filterValue.length === 0)) {
|
|
1522
|
-
return true;
|
|
1523
|
-
}
|
|
1524
|
-
const cellValue = row.getValue(columnId);
|
|
1525
|
-
if (!Array.isArray(filterValue)) {
|
|
1526
|
-
return cellValue === filterValue;
|
|
1527
|
-
}
|
|
1528
|
-
const filterSet = new Set(filterValue);
|
|
1529
|
-
if (Array.isArray(cellValue)) {
|
|
1530
|
-
return cellValue.some(cv => filterSet.has(cv));
|
|
1531
|
-
}
|
|
1532
|
-
return filterSet.has(cellValue);
|
|
1533
|
-
};
|
|
1534
|
-
break;
|
|
1535
|
-
case 'date':
|
|
1536
|
-
columnDef.filterFn = (row, columnId, filterValue) => {
|
|
1537
|
-
if (!filterValue) {
|
|
1538
|
-
return true;
|
|
1539
|
-
}
|
|
1540
|
-
const cellValue = row.getValue(columnId);
|
|
1541
|
-
if (!cellValue) {
|
|
1542
|
-
return false;
|
|
1543
|
-
}
|
|
1544
|
-
const cellDate = cellValue instanceof Date ? cellValue : new Date(cellValue);
|
|
1545
|
-
const filterDate = filterValue instanceof Date ? filterValue : new Date(filterValue);
|
|
1546
|
-
if (isNaN(cellDate.getTime()) || isNaN(filterDate.getTime())) {
|
|
1547
|
-
return false;
|
|
1548
|
-
}
|
|
1549
|
-
return (cellDate.getFullYear() === filterDate.getFullYear() &&
|
|
1550
|
-
cellDate.getMonth() === filterDate.getMonth() &&
|
|
1551
|
-
cellDate.getDate() === filterDate.getDate());
|
|
1552
|
-
};
|
|
1553
|
-
break;
|
|
1554
|
-
case 'date-range':
|
|
1555
|
-
columnDef.filterFn = (row, columnId, filterValue) => {
|
|
1556
|
-
if (!filterValue || typeof filterValue !== 'object') {
|
|
1557
|
-
return true;
|
|
1558
|
-
}
|
|
1559
|
-
const { start, end } = filterValue;
|
|
1560
|
-
if (!start && !end) {
|
|
1561
|
-
return true;
|
|
1562
|
-
}
|
|
1563
|
-
const cellValue = row.getValue(columnId);
|
|
1564
|
-
if (!cellValue) {
|
|
1565
|
-
return false;
|
|
1566
|
-
}
|
|
1567
|
-
const cellDate = cellValue instanceof Date ? cellValue : new Date(cellValue);
|
|
1568
|
-
if (isNaN(cellDate.getTime())) {
|
|
1569
|
-
return false;
|
|
1570
|
-
}
|
|
1571
|
-
const cellDateOnly = new Date(cellDate.getFullYear(), cellDate.getMonth(), cellDate.getDate());
|
|
1572
|
-
if (start) {
|
|
1573
|
-
const startDate = start instanceof Date ? start : new Date(start);
|
|
1574
|
-
if (!isNaN(startDate.getTime())) {
|
|
1575
|
-
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
|
|
1576
|
-
if (cellDateOnly < startDateOnly) {
|
|
1577
|
-
return false;
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
if (end) {
|
|
1582
|
-
const endDate = end instanceof Date ? end : new Date(end);
|
|
1583
|
-
if (!isNaN(endDate.getTime())) {
|
|
1584
|
-
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
|
1585
|
-
if (cellDateOnly > endDateOnly) {
|
|
1586
|
-
return false;
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
return true;
|
|
1591
|
-
};
|
|
1592
|
-
break;
|
|
1593
|
-
case 'range':
|
|
1594
|
-
columnDef.filterFn = (row, columnId, filterValue) => {
|
|
1595
|
-
if (!filterValue || !Array.isArray(filterValue)) {
|
|
1596
|
-
return true;
|
|
1597
|
-
}
|
|
1598
|
-
const [min, max] = filterValue;
|
|
1599
|
-
if (min === undefined && max === undefined) {
|
|
1600
|
-
return true;
|
|
1601
|
-
}
|
|
1602
|
-
const cellValue = row.getValue(columnId);
|
|
1603
|
-
if (cellValue === null || cellValue === undefined) {
|
|
1604
|
-
return false;
|
|
1605
|
-
}
|
|
1606
|
-
const numValue = typeof cellValue === 'number' ? cellValue : Number(cellValue);
|
|
1607
|
-
if (isNaN(numValue)) {
|
|
1608
|
-
return false;
|
|
1609
|
-
}
|
|
1610
|
-
if (min !== undefined && numValue < min) {
|
|
1611
|
-
return false;
|
|
1612
|
-
}
|
|
1613
|
-
if (max !== undefined && numValue > max) {
|
|
1614
|
-
return false;
|
|
1615
|
-
}
|
|
1616
|
-
return true;
|
|
1617
|
-
};
|
|
1618
|
-
break;
|
|
1359
|
+
_debounce(fn) {
|
|
1360
|
+
if (this._debounceTimeout) {
|
|
1361
|
+
clearTimeout(this._debounceTimeout);
|
|
1619
1362
|
}
|
|
1363
|
+
this._debounceTimeout = setTimeout(fn, this._debounceMs);
|
|
1620
1364
|
}
|
|
1621
|
-
|
|
1622
|
-
|
|
1365
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1366
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableFilterComponent, isStandalone: true, selector: "z-table-filter", inputs: { zColumn: { classPropertyName: "zColumn", publicName: "zColumn", isSignal: true, isRequired: true, transformFunction: null }, zTable: { classPropertyName: "zTable", publicName: "zTable", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "z-table-filter block" }, ngImport: i0, template: `
|
|
1367
|
+
@switch (filterType()) {
|
|
1368
|
+
@case ('range') {
|
|
1369
|
+
<div class="flex gap-1">
|
|
1370
|
+
<z-input
|
|
1371
|
+
zType="number"
|
|
1372
|
+
zSize="sm"
|
|
1373
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_min' | translate"
|
|
1374
|
+
[ngModel]="rangeMinValue()"
|
|
1375
|
+
(ngModelChange)="onMinChangeDebounced($event)"
|
|
1376
|
+
class="min-w-0 flex-1"
|
|
1377
|
+
/>
|
|
1378
|
+
<z-input
|
|
1379
|
+
zType="number"
|
|
1380
|
+
zSize="sm"
|
|
1381
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_max' | translate"
|
|
1382
|
+
[ngModel]="rangeMaxValue()"
|
|
1383
|
+
(ngModelChange)="onMaxChangeDebounced($event)"
|
|
1384
|
+
class="min-w-0 flex-1"
|
|
1385
|
+
/>
|
|
1386
|
+
</div>
|
|
1387
|
+
}
|
|
1388
|
+
@case ('date') {
|
|
1389
|
+
<z-calendar
|
|
1390
|
+
zMode="single"
|
|
1391
|
+
zSize="sm"
|
|
1392
|
+
zAllowClear
|
|
1393
|
+
zValueType="date"
|
|
1394
|
+
[zScrollClose]="true"
|
|
1395
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_date' | translate"
|
|
1396
|
+
[ngModel]="dateValue()"
|
|
1397
|
+
(ngModelChange)="onDateChange($event)"
|
|
1398
|
+
class="w-full"
|
|
1399
|
+
/>
|
|
1400
|
+
}
|
|
1401
|
+
@case ('date-range') {
|
|
1402
|
+
<z-calendar
|
|
1403
|
+
zMode="range"
|
|
1404
|
+
zSize="sm"
|
|
1405
|
+
zAllowClear
|
|
1406
|
+
zValueType="date"
|
|
1407
|
+
[zScrollClose]="true"
|
|
1408
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_date_range' | translate"
|
|
1409
|
+
[ngModel]="dateRangeValue()"
|
|
1410
|
+
(ngModelChange)="onDateRangeChange($event)"
|
|
1411
|
+
class="w-full"
|
|
1412
|
+
/>
|
|
1413
|
+
}
|
|
1414
|
+
@case ('select') {
|
|
1415
|
+
<z-select
|
|
1416
|
+
zSize="sm"
|
|
1417
|
+
zAllowClear
|
|
1418
|
+
[zWrap]="false"
|
|
1419
|
+
[zScrollClose]="true"
|
|
1420
|
+
[zOptions]="selectOptions()"
|
|
1421
|
+
[ngModel]="selectValue()"
|
|
1422
|
+
[zMaxVisible]="30"
|
|
1423
|
+
[zPlaceholder]="'i18n_z_ui_common_select' | translate"
|
|
1424
|
+
(ngModelChange)="onSelectChange($event)"
|
|
1425
|
+
class="w-full min-w-0"
|
|
1426
|
+
/>
|
|
1427
|
+
}
|
|
1428
|
+
@case ('multi-select') {
|
|
1429
|
+
<z-select
|
|
1430
|
+
zMode="multiple"
|
|
1431
|
+
zSize="sm"
|
|
1432
|
+
zAllowClear
|
|
1433
|
+
[zWrap]="false"
|
|
1434
|
+
[zScrollClose]="true"
|
|
1435
|
+
[zOptions]="selectOptions()"
|
|
1436
|
+
[zMaxTagCount]="1"
|
|
1437
|
+
[zMaxVisible]="30"
|
|
1438
|
+
[ngModel]="multiSelectValue()"
|
|
1439
|
+
[zPlaceholder]="'i18n_z_ui_common_select' | translate"
|
|
1440
|
+
(ngModelChange)="onMultiSelectChange($event)"
|
|
1441
|
+
class="w-full min-w-0"
|
|
1442
|
+
/>
|
|
1443
|
+
}
|
|
1444
|
+
@case ('tags') {
|
|
1445
|
+
<z-select
|
|
1446
|
+
zMode="tags"
|
|
1447
|
+
zSize="sm"
|
|
1448
|
+
[zOptions]="selectOptions()"
|
|
1449
|
+
[zWrap]="false"
|
|
1450
|
+
[zScrollClose]="true"
|
|
1451
|
+
[zMaxTagCount]="1"
|
|
1452
|
+
[zMaxVisible]="30"
|
|
1453
|
+
[zPlaceholder]="'i18n_z_ui_common_select' | translate"
|
|
1454
|
+
class="w-full min-w-0"
|
|
1455
|
+
[zAllowClear]="true"
|
|
1456
|
+
(ngModelChange)="onMultiSelectChange($event)"
|
|
1457
|
+
[ngModel]="multiSelectValue()"
|
|
1458
|
+
/>
|
|
1459
|
+
}
|
|
1460
|
+
@case ('number') {
|
|
1461
|
+
<z-input
|
|
1462
|
+
zType="number"
|
|
1463
|
+
zSize="sm"
|
|
1464
|
+
zAllowClear
|
|
1465
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_search' | translate"
|
|
1466
|
+
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
1467
|
+
(ngModelChange)="onTextChangeDebounced($event)"
|
|
1468
|
+
class="w-full"
|
|
1469
|
+
/>
|
|
1470
|
+
}
|
|
1471
|
+
@default {
|
|
1472
|
+
<z-input
|
|
1473
|
+
zType="text"
|
|
1474
|
+
zSize="sm"
|
|
1475
|
+
zAllowClear
|
|
1476
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_search' | translate"
|
|
1477
|
+
[ngModel]="(columnFilterValue() ?? '').toString()"
|
|
1478
|
+
(ngModelChange)="onTextChangeDebounced($event)"
|
|
1479
|
+
class="w-full"
|
|
1480
|
+
/>
|
|
1481
|
+
}
|
|
1623
1482
|
}
|
|
1624
|
-
|
|
1483
|
+
`, isInline: true, styles: [":host{color:var(--foreground);font-weight:450;font-style:normal;text-decoration:none;text-transform:none;letter-spacing:normal}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ZInputComponent, selector: "z-input", inputs: ["class", "zType", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zPrefix", "zSuffix", "zMin", "zMax", "zStep", "zShowArrows", "zMask", "zDecimalPlaces", "zAllowNegative", "zThousandSeparator", "zDecimalMarker", "zValidators", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zShowPasswordToggle", "zSearch", "zDebounce", "zAutofocus", "zAutoComplete", "zAllowClear", "zAutoSizeContent", "zRows", "zResize", "zMaxLength", "zAutoSuggest"], outputs: ["zOnSearch", "zOnChange", "zControl", "zEvent"], exportAs: ["zInput"] }, { kind: "component", type: ZSelectComponent, selector: "z-select", inputs: ["class", "zMode", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zLoading", "zPrefix", "zAllowClear", "zWrap", "zShowSearch", "zPlaceholderSearch", "zDebounce", "zNotFoundText", "zEmptyText", "zEmptyIcon", "zMaxTagCount", "zDropdownMaxHeight", "zOptionHeight", "zVirtualScroll", "zShowAction", "zOptions", "zTranslateLabels", "zKey", "zSearchServer", "zLoadingMore", "zEnableLoadMore", "zScrollDistance", "zMaxVisible", "zScrollClose", "zSelectedTemplate", "zOptionTemplate", "zActionTemplate", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zValidators"], outputs: ["zOnSearch", "zOnLoadMore", "zControl", "zEvent"], exportAs: ["zSelect"] }, { kind: "component", type: ZCalendarComponent, selector: "z-calendar", inputs: ["class", "zMode", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zShowTime", "zTimeFormat", "zShowHour", "zShowMinute", "zShowSecond", "zQuickSelect", "zAllowClear", "zFormat", "zMinDate", "zMaxDate", "zValueType", "zValidators", "zShowOk", "zOkText", "zShowCancel", "zCancelText", "zDisabledDate", "zScrollClose", "zDefaultTime", "zRangeDefaultTime"], outputs: ["zControl", "zChange", "zEvent"], exportAs: ["zCalendar"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1625
1484
|
}
|
|
1485
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableFilterComponent, decorators: [{
|
|
1486
|
+
type: Component,
|
|
1487
|
+
args: [{ selector: 'z-table-filter', imports: [FormsModule, ZInputComponent, ZSelectComponent, ZCalendarComponent, TranslatePipe], standalone: true, template: `
|
|
1488
|
+
@switch (filterType()) {
|
|
1489
|
+
@case ('range') {
|
|
1490
|
+
<div class="flex gap-1">
|
|
1491
|
+
<z-input
|
|
1492
|
+
zType="number"
|
|
1493
|
+
zSize="sm"
|
|
1494
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_min' | translate"
|
|
1495
|
+
[ngModel]="rangeMinValue()"
|
|
1496
|
+
(ngModelChange)="onMinChangeDebounced($event)"
|
|
1497
|
+
class="min-w-0 flex-1"
|
|
1498
|
+
/>
|
|
1499
|
+
<z-input
|
|
1500
|
+
zType="number"
|
|
1501
|
+
zSize="sm"
|
|
1502
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_max' | translate"
|
|
1503
|
+
[ngModel]="rangeMaxValue()"
|
|
1504
|
+
(ngModelChange)="onMaxChangeDebounced($event)"
|
|
1505
|
+
class="min-w-0 flex-1"
|
|
1506
|
+
/>
|
|
1507
|
+
</div>
|
|
1508
|
+
}
|
|
1509
|
+
@case ('date') {
|
|
1510
|
+
<z-calendar
|
|
1511
|
+
zMode="single"
|
|
1512
|
+
zSize="sm"
|
|
1513
|
+
zAllowClear
|
|
1514
|
+
zValueType="date"
|
|
1515
|
+
[zScrollClose]="true"
|
|
1516
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_date' | translate"
|
|
1517
|
+
[ngModel]="dateValue()"
|
|
1518
|
+
(ngModelChange)="onDateChange($event)"
|
|
1519
|
+
class="w-full"
|
|
1520
|
+
/>
|
|
1521
|
+
}
|
|
1522
|
+
@case ('date-range') {
|
|
1523
|
+
<z-calendar
|
|
1524
|
+
zMode="range"
|
|
1525
|
+
zSize="sm"
|
|
1526
|
+
zAllowClear
|
|
1527
|
+
zValueType="date"
|
|
1528
|
+
[zScrollClose]="true"
|
|
1529
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_date_range' | translate"
|
|
1530
|
+
[ngModel]="dateRangeValue()"
|
|
1531
|
+
(ngModelChange)="onDateRangeChange($event)"
|
|
1532
|
+
class="w-full"
|
|
1533
|
+
/>
|
|
1534
|
+
}
|
|
1535
|
+
@case ('select') {
|
|
1536
|
+
<z-select
|
|
1537
|
+
zSize="sm"
|
|
1538
|
+
zAllowClear
|
|
1539
|
+
[zWrap]="false"
|
|
1540
|
+
[zScrollClose]="true"
|
|
1541
|
+
[zOptions]="selectOptions()"
|
|
1542
|
+
[ngModel]="selectValue()"
|
|
1543
|
+
[zMaxVisible]="30"
|
|
1544
|
+
[zPlaceholder]="'i18n_z_ui_common_select' | translate"
|
|
1545
|
+
(ngModelChange)="onSelectChange($event)"
|
|
1546
|
+
class="w-full min-w-0"
|
|
1547
|
+
/>
|
|
1548
|
+
}
|
|
1549
|
+
@case ('multi-select') {
|
|
1550
|
+
<z-select
|
|
1551
|
+
zMode="multiple"
|
|
1552
|
+
zSize="sm"
|
|
1553
|
+
zAllowClear
|
|
1554
|
+
[zWrap]="false"
|
|
1555
|
+
[zScrollClose]="true"
|
|
1556
|
+
[zOptions]="selectOptions()"
|
|
1557
|
+
[zMaxTagCount]="1"
|
|
1558
|
+
[zMaxVisible]="30"
|
|
1559
|
+
[ngModel]="multiSelectValue()"
|
|
1560
|
+
[zPlaceholder]="'i18n_z_ui_common_select' | translate"
|
|
1561
|
+
(ngModelChange)="onMultiSelectChange($event)"
|
|
1562
|
+
class="w-full min-w-0"
|
|
1563
|
+
/>
|
|
1564
|
+
}
|
|
1565
|
+
@case ('tags') {
|
|
1566
|
+
<z-select
|
|
1567
|
+
zMode="tags"
|
|
1568
|
+
zSize="sm"
|
|
1569
|
+
[zOptions]="selectOptions()"
|
|
1570
|
+
[zWrap]="false"
|
|
1571
|
+
[zScrollClose]="true"
|
|
1572
|
+
[zMaxTagCount]="1"
|
|
1573
|
+
[zMaxVisible]="30"
|
|
1574
|
+
[zPlaceholder]="'i18n_z_ui_common_select' | translate"
|
|
1575
|
+
class="w-full min-w-0"
|
|
1576
|
+
[zAllowClear]="true"
|
|
1577
|
+
(ngModelChange)="onMultiSelectChange($event)"
|
|
1578
|
+
[ngModel]="multiSelectValue()"
|
|
1579
|
+
/>
|
|
1580
|
+
}
|
|
1581
|
+
@case ('number') {
|
|
1582
|
+
<z-input
|
|
1583
|
+
zType="number"
|
|
1584
|
+
zSize="sm"
|
|
1585
|
+
zAllowClear
|
|
1586
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_search' | translate"
|
|
1587
|
+
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
1588
|
+
(ngModelChange)="onTextChangeDebounced($event)"
|
|
1589
|
+
class="w-full"
|
|
1590
|
+
/>
|
|
1591
|
+
}
|
|
1592
|
+
@default {
|
|
1593
|
+
<z-input
|
|
1594
|
+
zType="text"
|
|
1595
|
+
zSize="sm"
|
|
1596
|
+
zAllowClear
|
|
1597
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_search' | translate"
|
|
1598
|
+
[ngModel]="(columnFilterValue() ?? '').toString()"
|
|
1599
|
+
(ngModelChange)="onTextChangeDebounced($event)"
|
|
1600
|
+
class="w-full"
|
|
1601
|
+
/>
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
1605
|
+
class: 'z-table-filter block',
|
|
1606
|
+
}, styles: [":host{color:var(--foreground);font-weight:450;font-style:normal;text-decoration:none;text-transform:none;letter-spacing:normal}\n"] }]
|
|
1607
|
+
}], propDecorators: { zColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "zColumn", required: true }] }], zTable: [{ type: i0.Input, args: [{ isSignal: true, alias: "zTable", required: true }] }] } });
|
|
1626
1608
|
|
|
1627
1609
|
class ZTableIconTextComponent {
|
|
1628
1610
|
zText = input('', ...(ngDevMode ? [{ debugName: "zText" }] : []));
|
|
@@ -2160,6 +2142,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
2160
2142
|
}]
|
|
2161
2143
|
}] });
|
|
2162
2144
|
|
|
2145
|
+
class ZTableCellVisiblePipe {
|
|
2146
|
+
transform(cell, columns) {
|
|
2147
|
+
const columnConfig = findColumnConfig(cell.column.id, columns);
|
|
2148
|
+
if (!columnConfig) {
|
|
2149
|
+
return true;
|
|
2150
|
+
}
|
|
2151
|
+
const { body } = columnConfig;
|
|
2152
|
+
if (!isBodyConfig(body)) {
|
|
2153
|
+
return true;
|
|
2154
|
+
}
|
|
2155
|
+
const { visible } = body;
|
|
2156
|
+
if (visible === undefined) {
|
|
2157
|
+
return true;
|
|
2158
|
+
}
|
|
2159
|
+
if (typeof visible === 'function') {
|
|
2160
|
+
return visible(cell.getContext());
|
|
2161
|
+
}
|
|
2162
|
+
return visible;
|
|
2163
|
+
}
|
|
2164
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableCellVisiblePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
2165
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableCellVisiblePipe, isStandalone: true, name: "zTableCellVisible" });
|
|
2166
|
+
}
|
|
2167
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableCellVisiblePipe, decorators: [{
|
|
2168
|
+
type: Pipe,
|
|
2169
|
+
args: [{
|
|
2170
|
+
name: 'zTableCellVisible',
|
|
2171
|
+
standalone: true,
|
|
2172
|
+
pure: true,
|
|
2173
|
+
}]
|
|
2174
|
+
}] });
|
|
2175
|
+
|
|
2163
2176
|
const Z_TABLE_ALIGN_MAP = {
|
|
2164
2177
|
left: 'text-left',
|
|
2165
2178
|
center: 'text-center',
|
|
@@ -2804,7 +2817,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
2804
2817
|
class ZTableComponent {
|
|
2805
2818
|
zChange = output();
|
|
2806
2819
|
zControl = output();
|
|
2807
|
-
|
|
2820
|
+
zClass = input('', ...(ngDevMode ? [{ debugName: "zClass" }] : []));
|
|
2808
2821
|
zConfig = input({ data: [], columns: [], mode: 'local' }, ...(ngDevMode ? [{ debugName: "zConfig" }] : []));
|
|
2809
2822
|
zLoading = input(false, ...(ngDevMode ? [{ debugName: "zLoading" }] : []));
|
|
2810
2823
|
zKey = input('', ...(ngDevMode ? [{ debugName: "zKey" }] : []));
|
|
@@ -2834,7 +2847,7 @@ class ZTableComponent {
|
|
|
2834
2847
|
globalFilter = signal('', ...(ngDevMode ? [{ debugName: "globalFilter" }] : []));
|
|
2835
2848
|
pagination = signal({ pageIndex: 1, pageSize: 10 }, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
|
|
2836
2849
|
sorting = signal([], ...(ngDevMode ? [{ debugName: "sorting" }] : []));
|
|
2837
|
-
classTable = computed(() => zMergeClasses('z-table-container shadow-xs', this.
|
|
2850
|
+
classTable = computed(() => zMergeClasses('z-table-container shadow-xs', this.zClass()), ...(ngDevMode ? [{ debugName: "classTable" }] : []));
|
|
2838
2851
|
_tanstackPagination = computed(() => ({
|
|
2839
2852
|
...this.pagination(),
|
|
2840
2853
|
pageIndex: Math.max(0, this.pagination().pageIndex - 1),
|
|
@@ -4406,7 +4419,7 @@ class ZTableComponent {
|
|
|
4406
4419
|
return result;
|
|
4407
4420
|
}
|
|
4408
4421
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4409
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableComponent, isStandalone: true, selector: "z-table", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, zConfig: { classPropertyName: "zConfig", publicName: "zConfig", isSignal: true, isRequired: false, transformFunction: null }, zLoading: { classPropertyName: "zLoading", publicName: "zLoading", isSignal: true, isRequired: false, transformFunction: null }, zKey: { classPropertyName: "zKey", publicName: "zKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zChange: "zChange", zControl: "zControl" }, host: { classAttribute: "z-table block relative py-1" }, providers: [TranslatePipe], viewQueries: [{ propertyName: "theadWrapper", first: true, predicate: ["theadWrapper"], descendants: true, isSignal: true }, { propertyName: "tbodyContainer", first: true, predicate: ["tbodyContainer"], descendants: true, isSignal: true }, { propertyName: "tbodyWrapper", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tbodyScrollbar", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tfootWrapper", first: true, predicate: ["tfootWrapper"], descendants: true, isSignal: true }, { propertyName: "expandedRowTemplate", first: true, predicate: ["expandedRowTemplate"], descendants: true, isSignal: true }, { propertyName: "virtualRowElements", predicate: ["virtualRow"], descendants: true, isSignal: true }], exportAs: ["zTable"], ngImport: i0, template: "<!-- Toolbar: Search & Settings -->\n@if (isSearchEnabled() || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (isSearchEnabled()) {\n @let config = searchConfig();\n <z-input\n [class]=\"config?.width ?? 'w-64'\"\n [zSize]=\"config?.size ?? 'sm'\"\n [zPlaceholder]=\"config?.placeholder ?? 'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"config?.debounceTime ?? 300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">\n {{ 'i18n_z_ui_table_settings' | translate }}\n </z-button>\n }\n </div>\n}\n\n<div\n [class]=\"classTable()\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\">\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-xs\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: headerGroup.headers : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\"\n (zChange)=\"table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div class=\"flex items-center justify-center\">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <!-- Column Options Popover Template -->\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucidePin\" zSize=\"12\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucidePin\" zSize=\"12\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n </div>\n </ng-template>\n\n <!-- Header Text with Popover Trigger -->\n @let hasColumnOptions =\n (header.column.getCanPin() && effectiveEnablePinning) || effectiveEnableOrdering;\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n <!-- Dropdown indicator when has options (between text and sort icon) -->\n @if (hasColumnOptions) {\n <z-icon\n zType=\"lucideChevronDown\"\n zSize=\"12\"\n class=\"text-muted-foreground shrink-0 opacity-50\" />\n }\n </div>\n <!-- Sort Icon (outside wrapper, no hover background) -->\n @if (header.column.getCanSort() && !hasBodyRowSpan()) {\n <span\n class=\"z-sort-icon shrink-0 cursor-pointer text-gray-500 hover:text-gray-700\"\n (click)=\"handleSort($event, header.column.getToggleSortingHandler())\">\n @if (header.column.getIsSorted() === 'asc') {\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n } @else if (header.column.getIsSorted() === 'desc') {\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n } @else {\n <z-icon zType=\"lucideArrowDownUp\" zSize=\"12\" class=\"opacity-30\" />\n }\n </span>\n }\n </div>\n }\n <!-- Column Filter -->\n @if (header.column.getCanFilter() && hasFiltering()) {\n <div class=\"mt-1\">\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n </div>\n }\n <!-- Column Resizer -->\n @if (\n header.column.id !== 'select' &&\n header.column.id !== 'expand' &&\n zConfig().enableColumnResizing !== false\n ) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (isLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"animate-in fade-in flex h-full flex-col duration-200\">\n @for (i of skeletonRows(); track $index; let last = $last) {\n <div\n class=\"border-border box-border flex flex-1 flex-col items-start justify-center gap-1.5 px-2\"\n [class.border-b]=\"!last\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" zHeight=\"22px\" zRadius=\"4px\" />\n <z-skeleton zType=\"bar\" zWidth=\"50%\" zHeight=\"14px\" zRadius=\"4px\" />\n </div>\n }\n </div>\n } @else {\n <!-- Spinner Loading -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" [zText]=\"'i18n_z_ui_table_loading' | translate\" />\n </div>\n }\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <z-empty zIcon=\"lucideSearchX\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_results' | translate\" />\n } @else {\n <z-empty zIcon=\"lucidePackageOpen\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_data' | translate\" />\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper track=\"all\" (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n <div\n #virtualRow\n class=\"z-virtual-row\"\n [attr.data-index]=\"virtualItem.index\"\n [style.height.px]=\"\n dynamicSize() ? null : (dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight())\n \"\n [style.transform]=\"'translate3d(0,' + virtualItem.start + 'px,0)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n @for (row of groupRows; track row.id) {\n <tr\n z-table-row-hover\n [style.height.px]=\"dynamicSize() ? null : virtualRowHeight()\"\n [style.min-height.px]=\"dynamicSize() ? virtualRowHeight() : null\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableCellRender: row.getVisibleCells() : zConfig().columns : 'body';\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : row.getVisibleCells()\n : zConfig().columns\n : columnSizingInfo()\n \"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === actionColumnInfo()?.columnId\"\n [class.z-at-bottom]=\"\n cell | zTableCellBottom: zConfig().columns : table.getRowModel().rows\n \"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let editInfoVirtual = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfoVirtual.enabled) {\n <!-- Editable Cell (Virtual) -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfoVirtual.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickable = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickable && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIcon = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableIcon && onCellClick(row, cell.column.id, cell.getValue())\n \" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefault = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableDefault && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row>\n <tr\n z-table-row-hover\n [attr.data-row-id]=\"row.id\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\"\n [class.z-pinned-top]=\"row.getIsPinned() === 'top'\"\n [class.z-pinned-bottom]=\"row.getIsPinned() === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'top' &&\n (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableCellRender: row.getVisibleCells() : zConfig().columns : 'body';\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : row.getVisibleCells()\n : zConfig().columns\n : columnSizingInfo()\n \"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"\n cell.column.id === 'actions' || cell.column.id === actionColumnInfo()?.columnId\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : table.getRowModel().rows\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'actions') {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let editInfo = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfo.enabled) {\n <!-- Editable Cell -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfo.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickableTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickableTpl && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIconTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickableIconTpl && onCellClick(row, cell.column.id, cell.getValue())\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefaultTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableDefaultTpl && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n </td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length && zConfig().expandedRowTemplate) {\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"row.getVisibleCells().length\" class=\"p-0\">\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (row of table.getCenterRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableCellRender: footerGroup.headers : zConfig().columns : 'footer';\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"\n footer.column\n | zTablePinningStyles: footer : 'footer' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n footer | zTableCellPin: 'isLastLeftPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-first]=\"\n footer | zTableCellPin: 'isFirstRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-last]=\"\n footer | zTableCellPin: 'isLastRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n </div>\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\"\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \"\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n {{ footerContent | translate }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \"\n [ngClass]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\"\n [ngStyle]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\">\n {{ $any(configFooterContent) | translate }}\n </span>\n </div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().pagination?.enabled !== false) {\n @let totalRows = table.getFilteredRowModel().rows.length;\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"truncate text-sm text-gray-500\">\n {{ 'i18n_z_ui_table_total_rows' | translate: { total: (zConfig().totalCount ?? totalRows | zFormatNum) } }}\n </div>\n <z-pagination\n [zTotal]=\"zConfig().totalCount ?? totalRows\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || isLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n [zTitle]=\"'i18n_z_ui_table_settings_title' | translate\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n [zCancelText]=\"'i18n_z_ui_drawer_close' | translate\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_display_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_display_settings_desc' | translate }}</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n [zChecked]=\"showHeaderFooterShadow()\"\n [zText]=\"'i18n_z_ui_table_header_footer_shadow' | translate\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n @if (zConfig().enableSettings) {\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_column_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_column_settings_desc' | translate }}</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div\n cdkDropList\n #columnDropList=\"cdkDropList\"\n (cdkDropListDropped)=\"onPendingColumnDrop($event)\"\n class=\"z-column-drop-list space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n cdkDragBoundary=\".z-column-drop-list\"\n cdkDragPreviewClass=\"z-drag-preview\"\n class=\"z-drag-item border-border bg-card hover:border-primary flex cursor-grab items-center gap-2 rounded border px-2 py-1.5 text-sm active:cursor-grabbing\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon\n cdkDragHandle\n zType=\"lucideGripVertical\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0 cursor-grab active:cursor-grabbing\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n (mousedown)=\"$event.stopPropagation()\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents | translate }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\" (mousedown)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n [disabled]=\"!isVisible\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n [disabled]=\"!isVisible\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">\n {{ 'i18n_z_ui_table_pinned_columns' | translate }}\n </h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">\n ({{ pinnedParents | translate }})\n </span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium\">\n {{\n isPinned === 'left'\n ? ('i18n_z_ui_table_left' | translate)\n : ('i18n_z_ui_table_right' | translate)\n }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n</z-drawer>\n", styles: [".z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}:host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -30px;--z-shadow-left-width: 30px;--z-shadow-left-opacity: 0;--z-shadow-right-left: -30px;--z-shadow-right-width: 30px;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border)}.z-table-container{display:flex;flex-direction:column;position:relative;width:100%;height:100%;overflow:hidden;border-radius:8px;border:thin solid var(--border);background-color:var(--card)}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:14px}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;-webkit-user-select:text;user-select:text}.z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-thead-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;min-height:100px;display:flex;flex-direction:column}.z-tbody-scrollbar{flex:1;height:100%}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:100px;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:8px 12px;height:40px;vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper tbody.z-has-vertical-scroll td.z-at-bottom,.z-tbody-wrapper tbody.z-last-row-touches-bottom td.z-at-bottom{border-bottom:none}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:12px}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:8px 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{padding-left:12px!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,var(--background))!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,var(--background))!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td,tr.z-expanded-row td{background-color:var(--muted)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:8px;background:transparent;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:5}thead th .z-resizer:after{content:\"\";position:absolute;right:0;top:0;height:100%;width:3px;background:#0000001a;opacity:0;transition:opacity .2s ease}thead th .z-resizer:after:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover:after{opacity:1;background:var(--primary);width:3px}thead th .z-resizer.z-is-resizing:after{opacity:1;background:var(--primary);width:3px}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th .z-resizer.z-resizer-left:after{right:auto;left:0}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible;border-right:thin solid var(--z-sticky-left-border-color)}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:var(--z-shadow-left-right);width:var(--z-shadow-left-width);pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-left-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-thead-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-left-last:after{box-shadow:inset 10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-left,.z-tbody-wrapper.z-scroll-left,.z-tfoot-wrapper.z-scroll-left{--z-shadow-left-opacity: 1}.z-thead-wrapper.z-scroll-left:where(.dark,.dark *),.z-tbody-wrapper.z-scroll-left:where(.dark,.dark *),.z-tfoot-wrapper.z-scroll-left:where(.dark,.dark *){--z-sticky-left-border-color: var(--border)}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible;border-left:thin solid var(--z-sticky-right-border-color)}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:var(--z-shadow-right-left);width:var(--z-shadow-right-width);pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-right-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-thead-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-right-first:before{box-shadow:inset -10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-right,.z-tbody-wrapper.z-scroll-right,.z-tfoot-wrapper.z-scroll-right{--z-shadow-right-opacity: 1}.z-thead-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tbody-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tfoot-wrapper.z-scroll-right:not(:where(.dark,.dark *)){--z-sticky-right-border-color: transparent}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-tfoot-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:12px;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:2px 4px;border-radius:4px;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}th .z-header-text-wrapper{transition:background-color .15s ease;border-radius:4px;min-width:0;overflow:hidden;flex-shrink:1}th .z-header-text-wrapper.z-has-options:hover{background-color:color-mix(in srgb,var(--foreground) 8%,transparent)}th .z-header-text-wrapper.z-has-options:active{background-color:color-mix(in srgb,var(--foreground) 12%,transparent)}.cdk-drag-preview,.z-drag-preview{box-shadow:0 5px 20px #0003;border-radius:6px;background-color:var(--card);border:1px solid var(--primary);z-index:10000!important;pointer-events:none}.cdk-drag-preview:where(.dark,.dark *),.z-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.cdk-drag-placeholder{background-color:color-mix(in srgb,var(--primary) 10%,var(--background));border:2px dashed var(--primary);border-radius:6px}.cdk-drag-animating{transition:transform .1s cubic-bezier(0,0,.2,1)}.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .1s cubic-bezier(0,0,.2,1)}.z-drag-item.cdk-drag-dragging{transition:none!important}.z-column-drop-list{min-height:50px}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "component", type: NgScrollbar, selector: "ng-scrollbar:not([externalViewport]), [ngScrollbar]", exportAs: ["ngScrollbar"] }, { kind: "directive", type: FlexRenderDirective, selector: "[flexRender]", inputs: ["flexRender", "flexRenderProps", "flexRenderInjector"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: ZCheckboxComponent, selector: "z-checkbox", inputs: ["class", "zType", "zSize", "zLabel", "zText", "zDisabled", "zIndeterminate", "zValue", "zOptions", "zOrientation", "zCheckAll", "zCheckAllText", "zChecked", "zGroupValue"], outputs: ["zChange", "zGroupChange", "zControl", "zCheckedChange", "zGroupValueChange"] }, { kind: "component", type: ZEmptyComponent, selector: "z-empty", inputs: ["class", "zType", "zIcon", "zIconSize", "zSize", "zMessage", "zDescription"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "component", type: ZInputComponent, selector: "z-input", inputs: ["class", "zType", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zPrefix", "zSuffix", "zMin", "zMax", "zStep", "zShowArrows", "zMask", "zDecimalPlaces", "zAllowNegative", "zThousandSeparator", "zDecimalMarker", "zValidators", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zShowPasswordToggle", "zSearch", "zDebounce", "zAutofocus", "zAutoComplete", "zAllowClear", "zAutoSizeContent", "zRows", "zResize", "zMaxLength", "zAutoSuggest"], outputs: ["zOnSearch", "zOnChange", "zControl"], exportAs: ["zInput"] }, { kind: "component", type: ZLoadingComponent, selector: "z-loading", inputs: ["class", "zSpinner", "zSize", "zColor", "zText", "zOverlay", "zOverlayType", "zFullscreen", "zLoading"] }, { kind: "component", type: ZSkeletonComponent, selector: "z-skeleton", inputs: ["class", "zType", "zSize", "zWidth", "zHeight", "zRows", "zGap", "zAnimated", "zRadius"] }, { kind: "component", type: ZDrawerComponent, selector: "z-drawer", inputs: ["class", "zVisible", "zTitle", "zDescription", "zWidth", "zHeight", "zPlacement", "zClosable", "zMaskClosable", "zHideFooter", "zOkText", "zCancelText", "zOkDestructive", "zOkDisabled", "zLoading", "zOverlay", "zShadow", "zShape", "zContentLoading", "zSkeletonRows"], outputs: ["zOnOk", "zOnCancel", "zScrollbar", "zVisibleChange"], exportAs: ["zDrawer"] }, { kind: "component", type: ZPaginationComponent, selector: "z-pagination", inputs: ["zPageIndex", "zPageSize", "zTotal", "zPageSizeOptions", "zShowSizeChanger", "zShowQuickJumper", "zShowTotal", "zSimple", "zSize", "zDisabled", "zTotalLabel", "zPerPageLabel", "zGoToLabel"], outputs: ["zOnPageChange", "zPageIndexChange", "zPageSizeChange"] }, { kind: "component", type: ZTableFilterComponent, selector: "z-table-filter", inputs: ["zColumn", "zTable"] }, { kind: "component", type: ZTableEditCellComponent, selector: "z-table-edit-cell", inputs: ["zRow", "zRowId", "zRowIndex", "zColumnId", "zValue", "zEditConfig", "zRowUpdate"], outputs: ["zChange"] }, { kind: "directive", type: ZPopoverDirective, selector: "[z-popover]", inputs: ["zPopoverContent", "zPosition", "zTrigger", "zClass", "zShowDelay", "zHideDelay", "zDisabled", "zOffset", "zPopoverWidth", "zManualClose", "zScrollClose", "zShowArrow"], outputs: ["zShow", "zHide", "zHideStart", "zControl", "zPositionChange"], exportAs: ["zPopover"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTrigger", "zTooltipType", "zTooltipSize", "zClass", "zShowDelay", "zHideDelay", "zArrow", "zDisabled", "zOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "directive", type: ZTableResizeDirective, selector: "[z-table-resize],[zTableResize]", inputs: ["zTableResize"] }, { kind: "directive", type: ZTableRowHoverDirective, selector: "[z-table-row-hover], [zTableRowHover]", inputs: ["zTableRowHover"] }, { kind: "component", type: ZTableIconTextComponent, selector: "z-table-icon-text", inputs: ["zText", "zTooltip", "zTriggerElement"] }, { kind: "component", type: ZTableActionsComponent, selector: "z-table-actions", inputs: ["zConfig", "zRow", "zRowId", "zDropdownButtonSize"], outputs: ["zActionClick"] }, { kind: "pipe", type: ZTableIsTemplateRefPipe, name: "zTableIsTemplateRef" }, { kind: "pipe", type: ZTableHasIconPipe, name: "zTableHasIcon" }, { kind: "pipe", type: ZTableCellBottomPipe, name: "zTableCellBottom" }, { kind: "pipe", type: ZTableCellClickablePipe, name: "zTableCellClickable" }, { kind: "pipe", type: ZTableCellConfigPipe, name: "zTableCellConfig" }, { kind: "pipe", type: ZTableCellEditPipe, name: "zTableCellEdit" }, { kind: "pipe", type: ZTableCellOffsetPipe, name: "zTableCellOffset" }, { kind: "pipe", type: ZTableCellPinPipe, name: "zTableCellPin" }, { kind: "pipe", type: ZTableColumnConfigPipe, name: "zTableColumnConfig" }, { kind: "pipe", type: ZTableColumnHeaderPipe, name: "zTableColumnHeader" }, { kind: "pipe", type: ZTableColumnParentsPipe, name: "zTableColumnParents" }, { kind: "pipe", type: ZTableFooterContentPipe, name: "zTableFooterContent" }, { kind: "pipe", type: ZTablePinningStylesPipe, name: "zTablePinningStyles" }, { kind: "pipe", type: ZTableRowPipe, name: "zTableRow" }, { kind: "pipe", type: ZTableSpanPipe, name: "zTableSpan" }, { kind: "pipe", type: ZTableCellRenderPipe, name: "zTableCellRender" }, { kind: "pipe", type: ZFormatNumPipe, name: "zFormatNum" }, { kind: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4422
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableComponent, isStandalone: true, selector: "z-table", inputs: { zClass: { classPropertyName: "zClass", publicName: "zClass", isSignal: true, isRequired: false, transformFunction: null }, zConfig: { classPropertyName: "zConfig", publicName: "zConfig", isSignal: true, isRequired: false, transformFunction: null }, zLoading: { classPropertyName: "zLoading", publicName: "zLoading", isSignal: true, isRequired: false, transformFunction: null }, zKey: { classPropertyName: "zKey", publicName: "zKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zChange: "zChange", zControl: "zControl" }, host: { classAttribute: "z-table block relative py-1" }, providers: [TranslatePipe], viewQueries: [{ propertyName: "theadWrapper", first: true, predicate: ["theadWrapper"], descendants: true, isSignal: true }, { propertyName: "tbodyContainer", first: true, predicate: ["tbodyContainer"], descendants: true, isSignal: true }, { propertyName: "tbodyWrapper", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tbodyScrollbar", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tfootWrapper", first: true, predicate: ["tfootWrapper"], descendants: true, isSignal: true }, { propertyName: "expandedRowTemplate", first: true, predicate: ["expandedRowTemplate"], descendants: true, isSignal: true }, { propertyName: "virtualRowElements", predicate: ["virtualRow"], descendants: true, isSignal: true }], exportAs: ["zTable"], ngImport: i0, template: "<!-- Toolbar: Search & Settings -->\n@if (isSearchEnabled() || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (isSearchEnabled()) {\n @let config = searchConfig();\n <z-input\n [class]=\"config?.width ?? 'w-64'\"\n [zSize]=\"config?.size ?? 'sm'\"\n [zPlaceholder]=\"config?.placeholder ?? 'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"config?.debounceTime ?? 300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">\n {{ 'i18n_z_ui_table_settings' | translate }}\n </z-button>\n }\n </div>\n}\n\n<div\n [class]=\"classTable()\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\">\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-xs\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: headerGroup.headers : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\"\n (zChange)=\"table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div class=\"flex items-center justify-center\">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <!-- Column Options Popover Template -->\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucidePin\" zSize=\"12\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucidePin\" zSize=\"12\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n </div>\n </ng-template>\n\n <!-- Header Text with Popover Trigger -->\n @let hasColumnOptions =\n (header.column.getCanPin() && effectiveEnablePinning) || effectiveEnableOrdering;\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n <!-- Dropdown indicator when has options (between text and sort icon) -->\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n <!-- Sort Icon (outside wrapper, no hover background) -->\n @if (header.column.getCanSort() && !hasBodyRowSpan()) {\n <span\n class=\"z-sort-icon shrink-0 cursor-pointer text-gray-500 hover:text-gray-700\"\n (click)=\"handleSort($event, header.column.getToggleSortingHandler())\">\n @if (header.column.getIsSorted() === 'asc') {\n <z-icon zType=\"lucideArrowUp\" zSize=\"15\" />\n } @else if (header.column.getIsSorted() === 'desc') {\n <z-icon zType=\"lucideArrowDown\" zSize=\"15\" />\n } @else {\n <z-icon zType=\"lucideArrowDownUp\" zSize=\"15\" class=\"opacity-60\" />\n }\n </span>\n }\n </div>\n }\n <!-- Column Filter -->\n @if (header.column.getCanFilter() && hasFiltering()) {\n <div class=\"mt-1\">\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n </div>\n }\n <!-- Column Resizer -->\n @if (\n header.column.id !== 'select' &&\n header.column.id !== 'expand' &&\n zConfig().enableColumnResizing !== false\n ) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (isLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"animate-in fade-in flex h-full flex-col duration-200\">\n @for (i of skeletonRows(); track $index; let last = $last) {\n <div\n class=\"border-border box-border flex flex-1 flex-col items-start justify-center gap-1.5 px-2\"\n [class.border-b]=\"!last\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" zHeight=\"22px\" zRadius=\"4px\" />\n <z-skeleton zType=\"bar\" zWidth=\"50%\" zHeight=\"14px\" zRadius=\"4px\" />\n </div>\n }\n </div>\n } @else {\n <!-- Spinner Loading -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" [zText]=\"'i18n_z_ui_table_loading' | translate\" />\n </div>\n }\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <z-empty zIcon=\"lucideSearchX\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_results' | translate\" />\n } @else {\n <z-empty zIcon=\"lucidePackageOpen\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_data' | translate\" />\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper track=\"all\" (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n <div\n #virtualRow\n class=\"z-virtual-row\"\n [attr.data-index]=\"virtualItem.index\"\n [style.height.px]=\"\n dynamicSize() ? null : (dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight())\n \"\n [style.transform]=\"'translate3d(0,' + virtualItem.start + 'px,0)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n @for (row of groupRows; track row.id) {\n <tr\n z-table-row-hover\n [style.height.px]=\"dynamicSize() ? null : virtualRowHeight()\"\n [style.min-height.px]=\"dynamicSize() ? virtualRowHeight() : null\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableCellRender: row.getVisibleCells() : zConfig().columns : 'body';\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : row.getVisibleCells()\n : zConfig().columns\n : columnSizingInfo()\n \"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === actionColumnInfo()?.columnId\"\n [class.z-at-bottom]=\"\n cell | zTableCellBottom: zConfig().columns : table.getRowModel().rows\n \"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisible = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisible) {\n @let editInfoVirtual = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfoVirtual.enabled) {\n <!-- Editable Cell (Virtual) -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfoVirtual.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else {\n <ng-container\n *flexRender=\"\n cell.column.columnDef.cell;\n props: cell.getContext();\n let cellContent\n \">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickable = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickable && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"\n cellContent;\n context: { $implicit: cell.getContext() }\n \" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIcon = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableIcon && onCellClick(row, cell.column.id, cell.getValue())\n \" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefault =\n cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') ||\n cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableDefault && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row>\n <tr\n z-table-row-hover\n [attr.data-row-id]=\"row.id\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\"\n [class.z-pinned-top]=\"row.getIsPinned() === 'top'\"\n [class.z-pinned-bottom]=\"row.getIsPinned() === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'top' &&\n (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableCellRender: row.getVisibleCells() : zConfig().columns : 'body';\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : row.getVisibleCells()\n : zConfig().columns\n : columnSizingInfo()\n \"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"\n cell.column.id === 'actions' || cell.column.id === actionColumnInfo()?.columnId\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : table.getRowModel().rows\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'actions') {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisibleNormal = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisibleNormal) {\n @let editInfo = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfo.enabled) {\n <!-- Editable Cell -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfo.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickableTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickableTpl && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIconTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickableIconTpl && onCellClick(row, cell.column.id, cell.getValue())\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefaultTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableDefaultTpl && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length && zConfig().expandedRowTemplate) {\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"row.getVisibleCells().length\" class=\"p-0\">\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (row of table.getCenterRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableCellRender: footerGroup.headers : zConfig().columns : 'footer';\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"\n footer.column\n | zTablePinningStyles: footer : 'footer' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n footer | zTableCellPin: 'isLastLeftPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-first]=\"\n footer | zTableCellPin: 'isFirstRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-last]=\"\n footer | zTableCellPin: 'isLastRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n </div>\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\"\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \"\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n {{ footerContent | translate }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \"\n [ngClass]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\"\n [ngStyle]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\">\n {{ $any(configFooterContent) | translate }}\n </span>\n </div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().pagination?.enabled !== false) {\n @let totalRows = table.getFilteredRowModel().rows.length;\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"truncate text-sm text-gray-500\">\n {{ 'i18n_z_ui_table_total_rows' | translate: { total: (zConfig().totalCount ?? totalRows | zFormatNum) } }}\n </div>\n <z-pagination\n [zTotal]=\"zConfig().totalCount ?? totalRows\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || isLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n [zTitle]=\"'i18n_z_ui_table_settings_title' | translate\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n [zCancelText]=\"'i18n_z_ui_drawer_close' | translate\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_display_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_display_settings_desc' | translate }}</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n [zChecked]=\"showHeaderFooterShadow()\"\n [zText]=\"'i18n_z_ui_table_header_footer_shadow' | translate\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n @if (zConfig().enableSettings) {\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_column_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_column_settings_desc' | translate }}</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div\n cdkDropList\n #columnDropList=\"cdkDropList\"\n (cdkDropListDropped)=\"onPendingColumnDrop($event)\"\n class=\"z-column-drop-list space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n cdkDragBoundary=\".z-column-drop-list\"\n cdkDragPreviewClass=\"z-drag-preview\"\n class=\"z-drag-item border-border bg-card hover:border-primary flex cursor-grab items-center gap-2 rounded border px-2 py-1.5 text-sm active:cursor-grabbing\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon\n cdkDragHandle\n zType=\"lucideGripVertical\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0 cursor-grab active:cursor-grabbing\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n (mousedown)=\"$event.stopPropagation()\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents | translate }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\" (mousedown)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n [disabled]=\"!isVisible\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n [disabled]=\"!isVisible\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">\n {{ 'i18n_z_ui_table_pinned_columns' | translate }}\n </h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">\n ({{ pinnedParents | translate }})\n </span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium\">\n {{\n isPinned === 'left'\n ? ('i18n_z_ui_table_left' | translate)\n : ('i18n_z_ui_table_right' | translate)\n }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n</z-drawer>\n", styles: [".z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}:host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -30px;--z-shadow-left-width: 30px;--z-shadow-left-opacity: 0;--z-shadow-right-left: -30px;--z-shadow-right-width: 30px;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border)}.z-table-container{display:flex;flex-direction:column;position:relative;width:100%;height:100%;overflow:hidden;border-radius:8px;border:thin solid var(--border);background-color:var(--card)}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:14px}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;-webkit-user-select:text;user-select:text}.z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-thead-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;min-height:100px;display:flex;flex-direction:column}.z-tbody-scrollbar{flex:1;height:100%}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:100px;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:8px 12px;height:40px;vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper tbody.z-has-vertical-scroll td.z-at-bottom,.z-tbody-wrapper tbody.z-last-row-touches-bottom td.z-at-bottom{border-bottom:none}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:12px}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:8px 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{padding-left:12px!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,var(--background))!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,var(--background))!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td,tr.z-expanded-row td{background-color:var(--muted)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:8px;background:transparent;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:5}thead th .z-resizer:after{content:\"\";position:absolute;right:0;top:0;height:100%;width:3px;background:#0000001a;opacity:0;transition:opacity .2s ease}thead th .z-resizer:after:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover:after{opacity:1;background:var(--primary);width:3px}thead th .z-resizer.z-is-resizing:after{opacity:1;background:var(--primary);width:3px}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th .z-resizer.z-resizer-left:after{right:auto;left:0}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible;border-right:thin solid var(--z-sticky-left-border-color)}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:var(--z-shadow-left-right);width:var(--z-shadow-left-width);pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-left-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-thead-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-left-last:after{box-shadow:inset 10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-left,.z-tbody-wrapper.z-scroll-left,.z-tfoot-wrapper.z-scroll-left{--z-shadow-left-opacity: 1}.z-thead-wrapper.z-scroll-left:where(.dark,.dark *),.z-tbody-wrapper.z-scroll-left:where(.dark,.dark *),.z-tfoot-wrapper.z-scroll-left:where(.dark,.dark *){--z-sticky-left-border-color: var(--border)}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible;border-left:thin solid var(--z-sticky-right-border-color)}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:var(--z-shadow-right-left);width:var(--z-shadow-right-width);pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-right-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-thead-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-right-first:before{box-shadow:inset -10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-right,.z-tbody-wrapper.z-scroll-right,.z-tfoot-wrapper.z-scroll-right{--z-shadow-right-opacity: 1}.z-thead-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tbody-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tfoot-wrapper.z-scroll-right:not(:where(.dark,.dark *)){--z-sticky-right-border-color: transparent}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-tfoot-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:12px;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:2px 4px;border-radius:4px;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}th .z-header-text-wrapper{transition:background-color .15s ease;border-radius:4px;min-width:0;overflow:hidden;flex-shrink:1}th .z-header-text-wrapper.z-has-options:hover{background-color:color-mix(in srgb,var(--foreground) 8%,transparent)}th .z-header-text-wrapper.z-has-options:active{background-color:color-mix(in srgb,var(--foreground) 12%,transparent)}.cdk-drag-preview,.z-drag-preview{box-shadow:0 5px 20px #0003;border-radius:6px;background-color:var(--card);border:1px solid var(--primary);z-index:10000!important;pointer-events:none}.cdk-drag-preview:where(.dark,.dark *),.z-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.cdk-drag-placeholder{background-color:color-mix(in srgb,var(--primary) 10%,var(--background));border:2px dashed var(--primary);border-radius:6px}.cdk-drag-animating{transition:transform .1s cubic-bezier(0,0,.2,1)}.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .1s cubic-bezier(0,0,.2,1)}.z-drag-item.cdk-drag-dragging{transition:none!important}.z-column-drop-list{min-height:50px}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "component", type: NgScrollbar, selector: "ng-scrollbar:not([externalViewport]), [ngScrollbar]", exportAs: ["ngScrollbar"] }, { kind: "directive", type: FlexRenderDirective, selector: "[flexRender]", inputs: ["flexRender", "flexRenderProps", "flexRenderInjector"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: ZCheckboxComponent, selector: "z-checkbox", inputs: ["class", "zType", "zSize", "zLabel", "zText", "zDisabled", "zIndeterminate", "zValue", "zOptions", "zOrientation", "zCheckAll", "zCheckAllText", "zChecked", "zGroupValue"], outputs: ["zChange", "zGroupChange", "zControl", "zEvent", "zCheckedChange", "zGroupValueChange"] }, { kind: "component", type: ZEmptyComponent, selector: "z-empty", inputs: ["class", "zType", "zIcon", "zIconSize", "zSize", "zMessage", "zDescription"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "component", type: ZInputComponent, selector: "z-input", inputs: ["class", "zType", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zPrefix", "zSuffix", "zMin", "zMax", "zStep", "zShowArrows", "zMask", "zDecimalPlaces", "zAllowNegative", "zThousandSeparator", "zDecimalMarker", "zValidators", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zShowPasswordToggle", "zSearch", "zDebounce", "zAutofocus", "zAutoComplete", "zAllowClear", "zAutoSizeContent", "zRows", "zResize", "zMaxLength", "zAutoSuggest"], outputs: ["zOnSearch", "zOnChange", "zControl", "zEvent"], exportAs: ["zInput"] }, { kind: "component", type: ZLoadingComponent, selector: "z-loading", inputs: ["class", "zSpinner", "zSize", "zColor", "zText", "zOverlay", "zOverlayType", "zFullscreen", "zLoading"] }, { kind: "component", type: ZSkeletonComponent, selector: "z-skeleton", inputs: ["class", "zType", "zSize", "zWidth", "zHeight", "zRows", "zGap", "zAnimated", "zRadius"] }, { kind: "component", type: ZDrawerComponent, selector: "z-drawer", inputs: ["class", "zVisible", "zTitle", "zDescription", "zWidth", "zHeight", "zPlacement", "zClosable", "zMaskClosable", "zHideFooter", "zOkText", "zCancelText", "zOkDestructive", "zOkDisabled", "zLoading", "zOverlay", "zShadow", "zShape", "zContentLoading", "zSkeletonRows"], outputs: ["zOnOk", "zOnCancel", "zScrollbar", "zVisibleChange"], exportAs: ["zDrawer"] }, { kind: "component", type: ZPaginationComponent, selector: "z-pagination", inputs: ["zPageIndex", "zPageSize", "zTotal", "zPageSizeOptions", "zShowSizeChanger", "zShowQuickJumper", "zShowTotal", "zSimple", "zSize", "zDisabled", "zTotalLabel", "zPerPageLabel", "zGoToLabel"], outputs: ["zOnPageChange", "zPageIndexChange", "zPageSizeChange"] }, { kind: "component", type: ZTableFilterComponent, selector: "z-table-filter", inputs: ["zColumn", "zTable"] }, { kind: "component", type: ZTableEditCellComponent, selector: "z-table-edit-cell", inputs: ["zRow", "zRowId", "zRowIndex", "zColumnId", "zValue", "zEditConfig", "zRowUpdate"], outputs: ["zChange"] }, { kind: "directive", type: ZPopoverDirective, selector: "[z-popover]", inputs: ["zPopoverContent", "zPosition", "zTrigger", "zClass", "zShowDelay", "zHideDelay", "zDisabled", "zOffset", "zPopoverWidth", "zManualClose", "zScrollClose", "zShowArrow"], outputs: ["zShow", "zHide", "zHideStart", "zControl", "zPositionChange"], exportAs: ["zPopover"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTrigger", "zTooltipType", "zTooltipSize", "zClass", "zShowDelay", "zHideDelay", "zArrow", "zDisabled", "zOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "directive", type: ZTableResizeDirective, selector: "[z-table-resize],[zTableResize]", inputs: ["zTableResize"] }, { kind: "directive", type: ZTableRowHoverDirective, selector: "[z-table-row-hover], [zTableRowHover]", inputs: ["zTableRowHover"] }, { kind: "component", type: ZTableIconTextComponent, selector: "z-table-icon-text", inputs: ["zText", "zTooltip", "zTriggerElement"] }, { kind: "component", type: ZTableActionsComponent, selector: "z-table-actions", inputs: ["zConfig", "zRow", "zRowId", "zDropdownButtonSize"], outputs: ["zActionClick"] }, { kind: "pipe", type: ZTableIsTemplateRefPipe, name: "zTableIsTemplateRef" }, { kind: "pipe", type: ZTableHasIconPipe, name: "zTableHasIcon" }, { kind: "pipe", type: ZTableCellBottomPipe, name: "zTableCellBottom" }, { kind: "pipe", type: ZTableCellClickablePipe, name: "zTableCellClickable" }, { kind: "pipe", type: ZTableCellConfigPipe, name: "zTableCellConfig" }, { kind: "pipe", type: ZTableCellEditPipe, name: "zTableCellEdit" }, { kind: "pipe", type: ZTableCellOffsetPipe, name: "zTableCellOffset" }, { kind: "pipe", type: ZTableCellPinPipe, name: "zTableCellPin" }, { kind: "pipe", type: ZTableCellVisiblePipe, name: "zTableCellVisible" }, { kind: "pipe", type: ZTableColumnConfigPipe, name: "zTableColumnConfig" }, { kind: "pipe", type: ZTableColumnHeaderPipe, name: "zTableColumnHeader" }, { kind: "pipe", type: ZTableColumnParentsPipe, name: "zTableColumnParents" }, { kind: "pipe", type: ZTableFooterContentPipe, name: "zTableFooterContent" }, { kind: "pipe", type: ZTablePinningStylesPipe, name: "zTablePinningStyles" }, { kind: "pipe", type: ZTableRowPipe, name: "zTableRow" }, { kind: "pipe", type: ZTableSpanPipe, name: "zTableSpan" }, { kind: "pipe", type: ZTableCellRenderPipe, name: "zTableCellRender" }, { kind: "pipe", type: ZFormatNumPipe, name: "zFormatNum" }, { kind: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4410
4423
|
}
|
|
4411
4424
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableComponent, decorators: [{
|
|
4412
4425
|
type: Component,
|
|
@@ -4442,6 +4455,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
4442
4455
|
ZTableCellEditPipe,
|
|
4443
4456
|
ZTableCellOffsetPipe,
|
|
4444
4457
|
ZTableCellPinPipe,
|
|
4458
|
+
ZTableCellVisiblePipe,
|
|
4445
4459
|
ZTableColumnConfigPipe,
|
|
4446
4460
|
ZTableColumnHeaderPipe,
|
|
4447
4461
|
ZTableColumnParentsPipe,
|
|
@@ -4457,8 +4471,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
4457
4471
|
TranslatePipe,
|
|
4458
4472
|
], standalone: true, providers: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
4459
4473
|
class: 'z-table block relative py-1',
|
|
4460
|
-
}, exportAs: 'zTable', template: "<!-- Toolbar: Search & Settings -->\n@if (isSearchEnabled() || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (isSearchEnabled()) {\n @let config = searchConfig();\n <z-input\n [class]=\"config?.width ?? 'w-64'\"\n [zSize]=\"config?.size ?? 'sm'\"\n [zPlaceholder]=\"config?.placeholder ?? 'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"config?.debounceTime ?? 300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">\n {{ 'i18n_z_ui_table_settings' | translate }}\n </z-button>\n }\n </div>\n}\n\n<div\n [class]=\"classTable()\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\">\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-xs\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: headerGroup.headers : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\"\n (zChange)=\"table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div class=\"flex items-center justify-center\">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <!-- Column Options Popover Template -->\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucidePin\" zSize=\"12\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucidePin\" zSize=\"12\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n </div>\n </ng-template>\n\n <!-- Header Text with Popover Trigger -->\n @let hasColumnOptions =\n (header.column.getCanPin() && effectiveEnablePinning) || effectiveEnableOrdering;\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n <!-- Dropdown indicator when has options (between text and sort icon) -->\n @if (hasColumnOptions) {\n <z-icon\n zType=\"lucideChevronDown\"\n zSize=\"12\"\n class=\"text-muted-foreground shrink-0 opacity-50\" />\n }\n </div>\n <!-- Sort Icon (outside wrapper, no hover background) -->\n @if (header.column.getCanSort() && !hasBodyRowSpan()) {\n <span\n class=\"z-sort-icon shrink-0 cursor-pointer text-gray-500 hover:text-gray-700\"\n (click)=\"handleSort($event, header.column.getToggleSortingHandler())\">\n @if (header.column.getIsSorted() === 'asc') {\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n } @else if (header.column.getIsSorted() === 'desc') {\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n } @else {\n <z-icon zType=\"lucideArrowDownUp\" zSize=\"12\" class=\"opacity-30\" />\n }\n </span>\n }\n </div>\n }\n <!-- Column Filter -->\n @if (header.column.getCanFilter() && hasFiltering()) {\n <div class=\"mt-1\">\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n </div>\n }\n <!-- Column Resizer -->\n @if (\n header.column.id !== 'select' &&\n header.column.id !== 'expand' &&\n zConfig().enableColumnResizing !== false\n ) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (isLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"animate-in fade-in flex h-full flex-col duration-200\">\n @for (i of skeletonRows(); track $index; let last = $last) {\n <div\n class=\"border-border box-border flex flex-1 flex-col items-start justify-center gap-1.5 px-2\"\n [class.border-b]=\"!last\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" zHeight=\"22px\" zRadius=\"4px\" />\n <z-skeleton zType=\"bar\" zWidth=\"50%\" zHeight=\"14px\" zRadius=\"4px\" />\n </div>\n }\n </div>\n } @else {\n <!-- Spinner Loading -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" [zText]=\"'i18n_z_ui_table_loading' | translate\" />\n </div>\n }\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <z-empty zIcon=\"lucideSearchX\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_results' | translate\" />\n } @else {\n <z-empty zIcon=\"lucidePackageOpen\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_data' | translate\" />\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper track=\"all\" (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n <div\n #virtualRow\n class=\"z-virtual-row\"\n [attr.data-index]=\"virtualItem.index\"\n [style.height.px]=\"\n dynamicSize() ? null : (dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight())\n \"\n [style.transform]=\"'translate3d(0,' + virtualItem.start + 'px,0)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n @for (row of groupRows; track row.id) {\n <tr\n z-table-row-hover\n [style.height.px]=\"dynamicSize() ? null : virtualRowHeight()\"\n [style.min-height.px]=\"dynamicSize() ? virtualRowHeight() : null\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableCellRender: row.getVisibleCells() : zConfig().columns : 'body';\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : row.getVisibleCells()\n : zConfig().columns\n : columnSizingInfo()\n \"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === actionColumnInfo()?.columnId\"\n [class.z-at-bottom]=\"\n cell | zTableCellBottom: zConfig().columns : table.getRowModel().rows\n \"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let editInfoVirtual = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfoVirtual.enabled) {\n <!-- Editable Cell (Virtual) -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfoVirtual.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickable = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickable && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIcon = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableIcon && onCellClick(row, cell.column.id, cell.getValue())\n \" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefault = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableDefault && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row>\n <tr\n z-table-row-hover\n [attr.data-row-id]=\"row.id\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\"\n [class.z-pinned-top]=\"row.getIsPinned() === 'top'\"\n [class.z-pinned-bottom]=\"row.getIsPinned() === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'top' &&\n (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableCellRender: row.getVisibleCells() : zConfig().columns : 'body';\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : row.getVisibleCells()\n : zConfig().columns\n : columnSizingInfo()\n \"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"\n cell.column.id === 'actions' || cell.column.id === actionColumnInfo()?.columnId\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : table.getRowModel().rows\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'actions') {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let editInfo = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfo.enabled) {\n <!-- Editable Cell -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfo.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickableTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickableTpl && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIconTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickableIconTpl && onCellClick(row, cell.column.id, cell.getValue())\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefaultTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableDefaultTpl && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n </td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length && zConfig().expandedRowTemplate) {\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"row.getVisibleCells().length\" class=\"p-0\">\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (row of table.getCenterRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableCellRender: footerGroup.headers : zConfig().columns : 'footer';\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"\n footer.column\n | zTablePinningStyles: footer : 'footer' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n footer | zTableCellPin: 'isLastLeftPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-first]=\"\n footer | zTableCellPin: 'isFirstRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-last]=\"\n footer | zTableCellPin: 'isLastRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n </div>\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\"\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \"\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n {{ footerContent | translate }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \"\n [ngClass]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\"\n [ngStyle]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\">\n {{ $any(configFooterContent) | translate }}\n </span>\n </div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().pagination?.enabled !== false) {\n @let totalRows = table.getFilteredRowModel().rows.length;\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"truncate text-sm text-gray-500\">\n {{ 'i18n_z_ui_table_total_rows' | translate: { total: (zConfig().totalCount ?? totalRows | zFormatNum) } }}\n </div>\n <z-pagination\n [zTotal]=\"zConfig().totalCount ?? totalRows\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || isLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n [zTitle]=\"'i18n_z_ui_table_settings_title' | translate\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n [zCancelText]=\"'i18n_z_ui_drawer_close' | translate\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_display_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_display_settings_desc' | translate }}</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n [zChecked]=\"showHeaderFooterShadow()\"\n [zText]=\"'i18n_z_ui_table_header_footer_shadow' | translate\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n @if (zConfig().enableSettings) {\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_column_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_column_settings_desc' | translate }}</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div\n cdkDropList\n #columnDropList=\"cdkDropList\"\n (cdkDropListDropped)=\"onPendingColumnDrop($event)\"\n class=\"z-column-drop-list space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n cdkDragBoundary=\".z-column-drop-list\"\n cdkDragPreviewClass=\"z-drag-preview\"\n class=\"z-drag-item border-border bg-card hover:border-primary flex cursor-grab items-center gap-2 rounded border px-2 py-1.5 text-sm active:cursor-grabbing\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon\n cdkDragHandle\n zType=\"lucideGripVertical\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0 cursor-grab active:cursor-grabbing\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n (mousedown)=\"$event.stopPropagation()\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents | translate }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\" (mousedown)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n [disabled]=\"!isVisible\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n [disabled]=\"!isVisible\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">\n {{ 'i18n_z_ui_table_pinned_columns' | translate }}\n </h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">\n ({{ pinnedParents | translate }})\n </span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium\">\n {{\n isPinned === 'left'\n ? ('i18n_z_ui_table_left' | translate)\n : ('i18n_z_ui_table_right' | translate)\n }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n</z-drawer>\n", styles: [".z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}:host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -30px;--z-shadow-left-width: 30px;--z-shadow-left-opacity: 0;--z-shadow-right-left: -30px;--z-shadow-right-width: 30px;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border)}.z-table-container{display:flex;flex-direction:column;position:relative;width:100%;height:100%;overflow:hidden;border-radius:8px;border:thin solid var(--border);background-color:var(--card)}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:14px}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;-webkit-user-select:text;user-select:text}.z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-thead-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;min-height:100px;display:flex;flex-direction:column}.z-tbody-scrollbar{flex:1;height:100%}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:100px;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:8px 12px;height:40px;vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper tbody.z-has-vertical-scroll td.z-at-bottom,.z-tbody-wrapper tbody.z-last-row-touches-bottom td.z-at-bottom{border-bottom:none}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:12px}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:8px 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{padding-left:12px!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,var(--background))!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,var(--background))!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td,tr.z-expanded-row td{background-color:var(--muted)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:8px;background:transparent;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:5}thead th .z-resizer:after{content:\"\";position:absolute;right:0;top:0;height:100%;width:3px;background:#0000001a;opacity:0;transition:opacity .2s ease}thead th .z-resizer:after:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover:after{opacity:1;background:var(--primary);width:3px}thead th .z-resizer.z-is-resizing:after{opacity:1;background:var(--primary);width:3px}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th .z-resizer.z-resizer-left:after{right:auto;left:0}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible;border-right:thin solid var(--z-sticky-left-border-color)}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:var(--z-shadow-left-right);width:var(--z-shadow-left-width);pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-left-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-thead-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-left-last:after{box-shadow:inset 10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-left,.z-tbody-wrapper.z-scroll-left,.z-tfoot-wrapper.z-scroll-left{--z-shadow-left-opacity: 1}.z-thead-wrapper.z-scroll-left:where(.dark,.dark *),.z-tbody-wrapper.z-scroll-left:where(.dark,.dark *),.z-tfoot-wrapper.z-scroll-left:where(.dark,.dark *){--z-sticky-left-border-color: var(--border)}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible;border-left:thin solid var(--z-sticky-right-border-color)}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:var(--z-shadow-right-left);width:var(--z-shadow-right-width);pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-right-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-thead-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-right-first:before{box-shadow:inset -10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-right,.z-tbody-wrapper.z-scroll-right,.z-tfoot-wrapper.z-scroll-right{--z-shadow-right-opacity: 1}.z-thead-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tbody-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tfoot-wrapper.z-scroll-right:not(:where(.dark,.dark *)){--z-sticky-right-border-color: transparent}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-tfoot-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:12px;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:2px 4px;border-radius:4px;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}th .z-header-text-wrapper{transition:background-color .15s ease;border-radius:4px;min-width:0;overflow:hidden;flex-shrink:1}th .z-header-text-wrapper.z-has-options:hover{background-color:color-mix(in srgb,var(--foreground) 8%,transparent)}th .z-header-text-wrapper.z-has-options:active{background-color:color-mix(in srgb,var(--foreground) 12%,transparent)}.cdk-drag-preview,.z-drag-preview{box-shadow:0 5px 20px #0003;border-radius:6px;background-color:var(--card);border:1px solid var(--primary);z-index:10000!important;pointer-events:none}.cdk-drag-preview:where(.dark,.dark *),.z-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.cdk-drag-placeholder{background-color:color-mix(in srgb,var(--primary) 10%,var(--background));border:2px dashed var(--primary);border-radius:6px}.cdk-drag-animating{transition:transform .1s cubic-bezier(0,0,.2,1)}.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .1s cubic-bezier(0,0,.2,1)}.z-drag-item.cdk-drag-dragging{transition:none!important}.z-column-drop-list{min-height:50px}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"] }]
|
|
4461
|
-
}], ctorParameters: () => [], propDecorators: { zChange: [{ type: i0.Output, args: ["zChange"] }], zControl: [{ type: i0.Output, args: ["zControl"] }],
|
|
4474
|
+
}, exportAs: 'zTable', template: "<!-- Toolbar: Search & Settings -->\n@if (isSearchEnabled() || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (isSearchEnabled()) {\n @let config = searchConfig();\n <z-input\n [class]=\"config?.width ?? 'w-64'\"\n [zSize]=\"config?.size ?? 'sm'\"\n [zPlaceholder]=\"config?.placeholder ?? 'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"config?.debounceTime ?? 300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">\n {{ 'i18n_z_ui_table_settings' | translate }}\n </z-button>\n }\n </div>\n}\n\n<div\n [class]=\"classTable()\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\">\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-xs\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: headerGroup.headers : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\"\n (zChange)=\"table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div class=\"flex items-center justify-center\">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <!-- Column Options Popover Template -->\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucidePin\" zSize=\"12\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucidePin\" zSize=\"12\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n </div>\n </ng-template>\n\n <!-- Header Text with Popover Trigger -->\n @let hasColumnOptions =\n (header.column.getCanPin() && effectiveEnablePinning) || effectiveEnableOrdering;\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [ngClass]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n <!-- Dropdown indicator when has options (between text and sort icon) -->\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n <!-- Sort Icon (outside wrapper, no hover background) -->\n @if (header.column.getCanSort() && !hasBodyRowSpan()) {\n <span\n class=\"z-sort-icon shrink-0 cursor-pointer text-gray-500 hover:text-gray-700\"\n (click)=\"handleSort($event, header.column.getToggleSortingHandler())\">\n @if (header.column.getIsSorted() === 'asc') {\n <z-icon zType=\"lucideArrowUp\" zSize=\"15\" />\n } @else if (header.column.getIsSorted() === 'desc') {\n <z-icon zType=\"lucideArrowDown\" zSize=\"15\" />\n } @else {\n <z-icon zType=\"lucideArrowDownUp\" zSize=\"15\" class=\"opacity-60\" />\n }\n </span>\n }\n </div>\n }\n <!-- Column Filter -->\n @if (header.column.getCanFilter() && hasFiltering()) {\n <div class=\"mt-1\">\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n </div>\n }\n <!-- Column Resizer -->\n @if (\n header.column.id !== 'select' &&\n header.column.id !== 'expand' &&\n zConfig().enableColumnResizing !== false\n ) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (isLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"animate-in fade-in flex h-full flex-col duration-200\">\n @for (i of skeletonRows(); track $index; let last = $last) {\n <div\n class=\"border-border box-border flex flex-1 flex-col items-start justify-center gap-1.5 px-2\"\n [class.border-b]=\"!last\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" zHeight=\"22px\" zRadius=\"4px\" />\n <z-skeleton zType=\"bar\" zWidth=\"50%\" zHeight=\"14px\" zRadius=\"4px\" />\n </div>\n }\n </div>\n } @else {\n <!-- Spinner Loading -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" [zText]=\"'i18n_z_ui_table_loading' | translate\" />\n </div>\n }\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <z-empty zIcon=\"lucideSearchX\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_results' | translate\" />\n } @else {\n <z-empty zIcon=\"lucidePackageOpen\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_data' | translate\" />\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper track=\"all\" (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n <div\n #virtualRow\n class=\"z-virtual-row\"\n [attr.data-index]=\"virtualItem.index\"\n [style.height.px]=\"\n dynamicSize() ? null : (dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight())\n \"\n [style.transform]=\"'translate3d(0,' + virtualItem.start + 'px,0)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n @for (row of groupRows; track row.id) {\n <tr\n z-table-row-hover\n [style.height.px]=\"dynamicSize() ? null : virtualRowHeight()\"\n [style.min-height.px]=\"dynamicSize() ? virtualRowHeight() : null\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableCellRender: row.getVisibleCells() : zConfig().columns : 'body';\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : row.getVisibleCells()\n : zConfig().columns\n : columnSizingInfo()\n \"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === actionColumnInfo()?.columnId\"\n [class.z-at-bottom]=\"\n cell | zTableCellBottom: zConfig().columns : table.getRowModel().rows\n \"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisible = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisible) {\n @let editInfoVirtual = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfoVirtual.enabled) {\n <!-- Editable Cell (Virtual) -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfoVirtual.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else {\n <ng-container\n *flexRender=\"\n cell.column.columnDef.cell;\n props: cell.getContext();\n let cellContent\n \">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickable = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickable && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"\n cellContent;\n context: { $implicit: cell.getContext() }\n \" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIcon = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableIcon && onCellClick(row, cell.column.id, cell.getValue())\n \" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefault =\n cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') ||\n cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableDefault && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row>\n <tr\n z-table-row-hover\n [attr.data-row-id]=\"row.id\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\"\n [class.z-pinned-top]=\"row.getIsPinned() === 'top'\"\n [class.z-pinned-bottom]=\"row.getIsPinned() === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'top' &&\n (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableCellRender: row.getVisibleCells() : zConfig().columns : 'body';\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : row.getVisibleCells()\n : zConfig().columns\n : columnSizingInfo()\n \"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"\n cell.column.id === 'actions' || cell.column.id === actionColumnInfo()?.columnId\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : table.getRowModel().rows\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'actions') {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisibleNormal = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisibleNormal) {\n @let editInfo = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfo.enabled) {\n <!-- Editable Cell -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfo.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickableTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickableTpl && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIconTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"isClickableIconTpl && onCellClick(row, cell.column.id, cell.getValue())\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefaultTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"\n (click)=\"\n isClickableDefaultTpl && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length && zConfig().expandedRowTemplate) {\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"row.getVisibleCells().length\" class=\"p-0\">\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (row of table.getCenterRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableCellRender: footerGroup.headers : zConfig().columns : 'footer';\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"\n footer.column\n | zTablePinningStyles: footer : 'footer' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n footer | zTableCellPin: 'isLastLeftPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-first]=\"\n footer | zTableCellPin: 'isFirstRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-last]=\"\n footer | zTableCellPin: 'isLastRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n </div>\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\"\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \"\n [ngClass]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n {{ footerContent | translate }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \"\n [ngClass]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\"\n [ngStyle]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\">\n {{ $any(configFooterContent) | translate }}\n </span>\n </div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().pagination?.enabled !== false) {\n @let totalRows = table.getFilteredRowModel().rows.length;\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"truncate text-sm text-gray-500\">\n {{ 'i18n_z_ui_table_total_rows' | translate: { total: (zConfig().totalCount ?? totalRows | zFormatNum) } }}\n </div>\n <z-pagination\n [zTotal]=\"zConfig().totalCount ?? totalRows\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || isLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n [zTitle]=\"'i18n_z_ui_table_settings_title' | translate\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n [zCancelText]=\"'i18n_z_ui_drawer_close' | translate\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_display_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_display_settings_desc' | translate }}</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n [zChecked]=\"showHeaderFooterShadow()\"\n [zText]=\"'i18n_z_ui_table_header_footer_shadow' | translate\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n @if (zConfig().enableSettings) {\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_column_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_column_settings_desc' | translate }}</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div\n cdkDropList\n #columnDropList=\"cdkDropList\"\n (cdkDropListDropped)=\"onPendingColumnDrop($event)\"\n class=\"z-column-drop-list space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n cdkDragBoundary=\".z-column-drop-list\"\n cdkDragPreviewClass=\"z-drag-preview\"\n class=\"z-drag-item border-border bg-card hover:border-primary flex cursor-grab items-center gap-2 rounded border px-2 py-1.5 text-sm active:cursor-grabbing\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon\n cdkDragHandle\n zType=\"lucideGripVertical\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0 cursor-grab active:cursor-grabbing\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n (mousedown)=\"$event.stopPropagation()\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents | translate }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\" (mousedown)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n [disabled]=\"!isVisible\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n [disabled]=\"!isVisible\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">\n {{ 'i18n_z_ui_table_pinned_columns' | translate }}\n </h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">\n ({{ pinnedParents | translate }})\n </span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium\">\n {{\n isPinned === 'left'\n ? ('i18n_z_ui_table_left' | translate)\n : ('i18n_z_ui_table_right' | translate)\n }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n</z-drawer>\n", styles: [".z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}:host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -30px;--z-shadow-left-width: 30px;--z-shadow-left-opacity: 0;--z-shadow-right-left: -30px;--z-shadow-right-width: 30px;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border)}.z-table-container{display:flex;flex-direction:column;position:relative;width:100%;height:100%;overflow:hidden;border-radius:8px;border:thin solid var(--border);background-color:var(--card)}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:14px}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;-webkit-user-select:text;user-select:text}.z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-thead-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;min-height:100px;display:flex;flex-direction:column}.z-tbody-scrollbar{flex:1;height:100%}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:100px;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:8px 12px;height:40px;vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper tbody.z-has-vertical-scroll td.z-at-bottom,.z-tbody-wrapper tbody.z-last-row-touches-bottom td.z-at-bottom{border-bottom:none}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:12px}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:8px 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{padding-left:12px!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,var(--background))!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,var(--background))!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td,tr.z-expanded-row td{background-color:var(--muted)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:8px;background:transparent;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:5}thead th .z-resizer:after{content:\"\";position:absolute;right:0;top:0;height:100%;width:3px;background:#0000001a;opacity:0;transition:opacity .2s ease}thead th .z-resizer:after:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover:after{opacity:1;background:var(--primary);width:3px}thead th .z-resizer.z-is-resizing:after{opacity:1;background:var(--primary);width:3px}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th .z-resizer.z-resizer-left:after{right:auto;left:0}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible;border-right:thin solid var(--z-sticky-left-border-color)}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:var(--z-shadow-left-right);width:var(--z-shadow-left-width);pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-left-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-thead-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-left-last:after{box-shadow:inset 10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-left,.z-tbody-wrapper.z-scroll-left,.z-tfoot-wrapper.z-scroll-left{--z-shadow-left-opacity: 1}.z-thead-wrapper.z-scroll-left:where(.dark,.dark *),.z-tbody-wrapper.z-scroll-left:where(.dark,.dark *),.z-tfoot-wrapper.z-scroll-left:where(.dark,.dark *){--z-sticky-left-border-color: var(--border)}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible;border-left:thin solid var(--z-sticky-right-border-color)}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:var(--z-shadow-right-left);width:var(--z-shadow-right-width);pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-right-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-thead-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-right-first:before{box-shadow:inset -10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-right,.z-tbody-wrapper.z-scroll-right,.z-tfoot-wrapper.z-scroll-right{--z-shadow-right-opacity: 1}.z-thead-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tbody-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tfoot-wrapper.z-scroll-right:not(:where(.dark,.dark *)){--z-sticky-right-border-color: transparent}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-tfoot-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:12px;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:2px 4px;border-radius:4px;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}th .z-header-text-wrapper{transition:background-color .15s ease;border-radius:4px;min-width:0;overflow:hidden;flex-shrink:1}th .z-header-text-wrapper.z-has-options:hover{background-color:color-mix(in srgb,var(--foreground) 8%,transparent)}th .z-header-text-wrapper.z-has-options:active{background-color:color-mix(in srgb,var(--foreground) 12%,transparent)}.cdk-drag-preview,.z-drag-preview{box-shadow:0 5px 20px #0003;border-radius:6px;background-color:var(--card);border:1px solid var(--primary);z-index:10000!important;pointer-events:none}.cdk-drag-preview:where(.dark,.dark *),.z-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.cdk-drag-placeholder{background-color:color-mix(in srgb,var(--primary) 10%,var(--background));border:2px dashed var(--primary);border-radius:6px}.cdk-drag-animating{transition:transform .1s cubic-bezier(0,0,.2,1)}.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .1s cubic-bezier(0,0,.2,1)}.z-drag-item.cdk-drag-dragging{transition:none!important}.z-column-drop-list{min-height:50px}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"] }]
|
|
4475
|
+
}], ctorParameters: () => [], propDecorators: { zChange: [{ type: i0.Output, args: ["zChange"] }], zControl: [{ type: i0.Output, args: ["zControl"] }], zClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "zClass", required: false }] }], zConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "zConfig", required: false }] }], zLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLoading", required: false }] }], zKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "zKey", required: false }] }], theadWrapper: [{ type: i0.ViewChild, args: ['theadWrapper', { isSignal: true }] }], tbodyContainer: [{ type: i0.ViewChild, args: ['tbodyContainer', { isSignal: true }] }], tbodyWrapper: [{ type: i0.ViewChild, args: ['tbodyWrapper', { isSignal: true }] }], tbodyScrollbar: [{ type: i0.ViewChild, args: ['tbodyWrapper', { isSignal: true }] }], tfootWrapper: [{ type: i0.ViewChild, args: ['tfootWrapper', { isSignal: true }] }], expandedRowTemplate: [{ type: i0.ViewChild, args: ['expandedRowTemplate', { isSignal: true }] }], virtualRowElements: [{ type: i0.ViewChildren, args: ['virtualRow', { isSignal: true }] }] } });
|
|
4462
4476
|
|
|
4463
4477
|
/**
|
|
4464
4478
|
* Generated bundle index. Do not edit.
|